[Smartupload-svn] [78] CakePHP

Back to archive index

svnno****@sourc***** svnno****@sourc*****
2012年 3月 31日 (土) 21:37:43 JST


Revision: 78
          http://sourceforge.jp/projects/smartupload/svn/view?view=rev&revision=78
Author:   hryksbt
Date:     2012-03-31 21:37:43 +0900 (Sat, 31 Mar 2012)
Log Message:
-----------
CakePHP

Modified Paths:
--------------
    trunk/src/Web/.settings/org.eclipse.php.core.prefs

Added Paths:
-----------
    trunk/src/Web/.gitignore
    trunk/src/Web/.htaccess
    trunk/src/Web/.travis.yml
    trunk/src/Web/README
    trunk/src/Web/app/
    trunk/src/Web/app/.htaccess
    trunk/src/Web/app/config/
    trunk/src/Web/app/config/acl.ini.php
    trunk/src/Web/app/config/bootstrap.php
    trunk/src/Web/app/config/core.php
    trunk/src/Web/app/config/database.php.default
    trunk/src/Web/app/config/routes.php
    trunk/src/Web/app/config/schema/
    trunk/src/Web/app/config/schema/db_acl.php
    trunk/src/Web/app/config/schema/i18n.php
    trunk/src/Web/app/config/schema/sessions.php
    trunk/src/Web/app/controllers/
    trunk/src/Web/app/controllers/components/
    trunk/src/Web/app/controllers/components/empty
    trunk/src/Web/app/index.php
    trunk/src/Web/app/libs/
    trunk/src/Web/app/libs/empty
    trunk/src/Web/app/locale/
    trunk/src/Web/app/locale/eng/
    trunk/src/Web/app/locale/eng/LC_MESSAGES/
    trunk/src/Web/app/locale/eng/LC_MESSAGES/empty
    trunk/src/Web/app/models/
    trunk/src/Web/app/models/behaviors/
    trunk/src/Web/app/models/behaviors/empty
    trunk/src/Web/app/models/datasources/
    trunk/src/Web/app/models/datasources/empty
    trunk/src/Web/app/plugins/
    trunk/src/Web/app/plugins/empty
    trunk/src/Web/app/tests/
    trunk/src/Web/app/tests/cases/
    trunk/src/Web/app/tests/cases/behaviors/
    trunk/src/Web/app/tests/cases/behaviors/empty
    trunk/src/Web/app/tests/cases/components/
    trunk/src/Web/app/tests/cases/components/empty
    trunk/src/Web/app/tests/cases/controllers/
    trunk/src/Web/app/tests/cases/controllers/empty
    trunk/src/Web/app/tests/cases/helpers/
    trunk/src/Web/app/tests/cases/helpers/empty
    trunk/src/Web/app/tests/cases/models/
    trunk/src/Web/app/tests/cases/models/empty
    trunk/src/Web/app/tests/fixtures/
    trunk/src/Web/app/tests/fixtures/empty
    trunk/src/Web/app/tests/groups/
    trunk/src/Web/app/tests/groups/empty
    trunk/src/Web/app/tmp/
    trunk/src/Web/app/tmp/cache/
    trunk/src/Web/app/tmp/cache/models/
    trunk/src/Web/app/tmp/cache/models/empty
    trunk/src/Web/app/tmp/cache/persistent/
    trunk/src/Web/app/tmp/cache/persistent/empty
    trunk/src/Web/app/tmp/cache/views/
    trunk/src/Web/app/tmp/cache/views/empty
    trunk/src/Web/app/tmp/logs/
    trunk/src/Web/app/tmp/logs/empty
    trunk/src/Web/app/tmp/sessions/
    trunk/src/Web/app/tmp/sessions/empty
    trunk/src/Web/app/tmp/tests/
    trunk/src/Web/app/tmp/tests/empty
    trunk/src/Web/app/vendors/
    trunk/src/Web/app/vendors/shells/
    trunk/src/Web/app/vendors/shells/tasks/
    trunk/src/Web/app/vendors/shells/tasks/empty
    trunk/src/Web/app/vendors/shells/templates/
    trunk/src/Web/app/vendors/shells/templates/empty
    trunk/src/Web/app/views/
    trunk/src/Web/app/views/elements/
    trunk/src/Web/app/views/elements/email/
    trunk/src/Web/app/views/elements/email/html/
    trunk/src/Web/app/views/elements/email/html/empty
    trunk/src/Web/app/views/elements/email/text/
    trunk/src/Web/app/views/elements/email/text/empty
    trunk/src/Web/app/views/elements/empty
    trunk/src/Web/app/views/errors/
    trunk/src/Web/app/views/errors/empty
    trunk/src/Web/app/views/helpers/
    trunk/src/Web/app/views/helpers/empty
    trunk/src/Web/app/views/layouts/
    trunk/src/Web/app/views/layouts/email/
    trunk/src/Web/app/views/layouts/email/html/
    trunk/src/Web/app/views/layouts/email/html/empty
    trunk/src/Web/app/views/layouts/email/text/
    trunk/src/Web/app/views/layouts/email/text/empty
    trunk/src/Web/app/views/layouts/js/
    trunk/src/Web/app/views/layouts/js/empty
    trunk/src/Web/app/views/layouts/rss/
    trunk/src/Web/app/views/layouts/rss/empty
    trunk/src/Web/app/views/layouts/xml/
    trunk/src/Web/app/views/layouts/xml/empty
    trunk/src/Web/app/views/pages/
    trunk/src/Web/app/views/pages/empty
    trunk/src/Web/app/views/scaffolds/
    trunk/src/Web/app/views/scaffolds/empty
    trunk/src/Web/app/webroot/
    trunk/src/Web/app/webroot/.htaccess
    trunk/src/Web/app/webroot/css/
    trunk/src/Web/app/webroot/css/cake.generic.css
    trunk/src/Web/app/webroot/css.php
    trunk/src/Web/app/webroot/favicon.ico
    trunk/src/Web/app/webroot/files/
    trunk/src/Web/app/webroot/files/empty
    trunk/src/Web/app/webroot/img/
    trunk/src/Web/app/webroot/img/cake.icon.png
    trunk/src/Web/app/webroot/img/cake.power.gif
    trunk/src/Web/app/webroot/img/test-error-icon.png
    trunk/src/Web/app/webroot/img/test-fail-icon.png
    trunk/src/Web/app/webroot/img/test-pass-icon.png
    trunk/src/Web/app/webroot/img/test-skip-icon.png
    trunk/src/Web/app/webroot/index.php
    trunk/src/Web/app/webroot/js/
    trunk/src/Web/app/webroot/js/empty
    trunk/src/Web/app/webroot/test.php
    trunk/src/Web/cake/
    trunk/src/Web/cake/LICENSE.txt
    trunk/src/Web/cake/VERSION.txt
    trunk/src/Web/cake/basics.php
    trunk/src/Web/cake/bootstrap.php
    trunk/src/Web/cake/config/
    trunk/src/Web/cake/config/config.php
    trunk/src/Web/cake/config/paths.php
    trunk/src/Web/cake/config/unicode/
    trunk/src/Web/cake/config/unicode/casefolding/
    trunk/src/Web/cake/config/unicode/casefolding/0080_00ff.php
    trunk/src/Web/cake/config/unicode/casefolding/0100_017f.php
    trunk/src/Web/cake/config/unicode/casefolding/0180_024F.php
    trunk/src/Web/cake/config/unicode/casefolding/0250_02af.php
    trunk/src/Web/cake/config/unicode/casefolding/0370_03ff.php
    trunk/src/Web/cake/config/unicode/casefolding/0400_04ff.php
    trunk/src/Web/cake/config/unicode/casefolding/0500_052f.php
    trunk/src/Web/cake/config/unicode/casefolding/0530_058f.php
    trunk/src/Web/cake/config/unicode/casefolding/1e00_1eff.php
    trunk/src/Web/cake/config/unicode/casefolding/1f00_1fff.php
    trunk/src/Web/cake/config/unicode/casefolding/2100_214f.php
    trunk/src/Web/cake/config/unicode/casefolding/2150_218f.php
    trunk/src/Web/cake/config/unicode/casefolding/2460_24ff.php
    trunk/src/Web/cake/config/unicode/casefolding/2c00_2c5f.php
    trunk/src/Web/cake/config/unicode/casefolding/2c60_2c7f.php
    trunk/src/Web/cake/config/unicode/casefolding/2c80_2cff.php
    trunk/src/Web/cake/config/unicode/casefolding/ff00_ffef.php
    trunk/src/Web/cake/console/
    trunk/src/Web/cake/console/cake
    trunk/src/Web/cake/console/cake.bat
    trunk/src/Web/cake/console/cake.php
    trunk/src/Web/cake/console/error.php
    trunk/src/Web/cake/console/libs/
    trunk/src/Web/cake/console/libs/acl.php
    trunk/src/Web/cake/console/libs/api.php
    trunk/src/Web/cake/console/libs/bake.php
    trunk/src/Web/cake/console/libs/console.php
    trunk/src/Web/cake/console/libs/i18n.php
    trunk/src/Web/cake/console/libs/schema.php
    trunk/src/Web/cake/console/libs/shell.php
    trunk/src/Web/cake/console/libs/tasks/
    trunk/src/Web/cake/console/libs/tasks/bake.php
    trunk/src/Web/cake/console/libs/tasks/controller.php
    trunk/src/Web/cake/console/libs/tasks/db_config.php
    trunk/src/Web/cake/console/libs/tasks/extract.php
    trunk/src/Web/cake/console/libs/tasks/fixture.php
    trunk/src/Web/cake/console/libs/tasks/model.php
    trunk/src/Web/cake/console/libs/tasks/plugin.php
    trunk/src/Web/cake/console/libs/tasks/project.php
    trunk/src/Web/cake/console/libs/tasks/template.php
    trunk/src/Web/cake/console/libs/tasks/test.php
    trunk/src/Web/cake/console/libs/tasks/view.php
    trunk/src/Web/cake/console/libs/testsuite.php
    trunk/src/Web/cake/console/templates/
    trunk/src/Web/cake/console/templates/default/
    trunk/src/Web/cake/console/templates/default/actions/
    trunk/src/Web/cake/console/templates/default/actions/controller_actions.ctp
    trunk/src/Web/cake/console/templates/default/classes/
    trunk/src/Web/cake/console/templates/default/classes/controller.ctp
    trunk/src/Web/cake/console/templates/default/classes/fixture.ctp
    trunk/src/Web/cake/console/templates/default/classes/model.ctp
    trunk/src/Web/cake/console/templates/default/classes/test.ctp
    trunk/src/Web/cake/console/templates/default/views/
    trunk/src/Web/cake/console/templates/default/views/form.ctp
    trunk/src/Web/cake/console/templates/default/views/home.ctp
    trunk/src/Web/cake/console/templates/default/views/index.ctp
    trunk/src/Web/cake/console/templates/default/views/view.ctp
    trunk/src/Web/cake/console/templates/skel/
    trunk/src/Web/cake/console/templates/skel/.htaccess
    trunk/src/Web/cake/console/templates/skel/app_controller.php
    trunk/src/Web/cake/console/templates/skel/app_helper.php
    trunk/src/Web/cake/console/templates/skel/app_model.php
    trunk/src/Web/cake/console/templates/skel/config/
    trunk/src/Web/cake/console/templates/skel/config/acl.ini.php
    trunk/src/Web/cake/console/templates/skel/config/bootstrap.php
    trunk/src/Web/cake/console/templates/skel/config/core.php
    trunk/src/Web/cake/console/templates/skel/config/database.php.default
    trunk/src/Web/cake/console/templates/skel/config/routes.php
    trunk/src/Web/cake/console/templates/skel/config/schema/
    trunk/src/Web/cake/console/templates/skel/config/schema/db_acl.php
    trunk/src/Web/cake/console/templates/skel/config/schema/db_acl.sql
    trunk/src/Web/cake/console/templates/skel/config/schema/i18n.php
    trunk/src/Web/cake/console/templates/skel/config/schema/i18n.sql
    trunk/src/Web/cake/console/templates/skel/config/schema/sessions.php
    trunk/src/Web/cake/console/templates/skel/config/schema/sessions.sql
    trunk/src/Web/cake/console/templates/skel/controllers/
    trunk/src/Web/cake/console/templates/skel/controllers/components/
    trunk/src/Web/cake/console/templates/skel/controllers/components/empty
    trunk/src/Web/cake/console/templates/skel/controllers/pages_controller.php
    trunk/src/Web/cake/console/templates/skel/index.php
    trunk/src/Web/cake/console/templates/skel/libs/
    trunk/src/Web/cake/console/templates/skel/libs/empty
    trunk/src/Web/cake/console/templates/skel/locale/
    trunk/src/Web/cake/console/templates/skel/locale/eng/
    trunk/src/Web/cake/console/templates/skel/locale/eng/LC_MESSAGES/
    trunk/src/Web/cake/console/templates/skel/locale/eng/LC_MESSAGES/empty
    trunk/src/Web/cake/console/templates/skel/models/
    trunk/src/Web/cake/console/templates/skel/models/behaviors/
    trunk/src/Web/cake/console/templates/skel/models/behaviors/empty
    trunk/src/Web/cake/console/templates/skel/models/datasources/
    trunk/src/Web/cake/console/templates/skel/models/datasources/empty
    trunk/src/Web/cake/console/templates/skel/plugins/
    trunk/src/Web/cake/console/templates/skel/plugins/empty
    trunk/src/Web/cake/console/templates/skel/tests/
    trunk/src/Web/cake/console/templates/skel/tests/cases/
    trunk/src/Web/cake/console/templates/skel/tests/cases/behaviors/
    trunk/src/Web/cake/console/templates/skel/tests/cases/behaviors/empty
    trunk/src/Web/cake/console/templates/skel/tests/cases/components/
    trunk/src/Web/cake/console/templates/skel/tests/cases/components/empty
    trunk/src/Web/cake/console/templates/skel/tests/cases/controllers/
    trunk/src/Web/cake/console/templates/skel/tests/cases/controllers/empty
    trunk/src/Web/cake/console/templates/skel/tests/cases/datasources/
    trunk/src/Web/cake/console/templates/skel/tests/cases/datasources/empty
    trunk/src/Web/cake/console/templates/skel/tests/cases/helpers/
    trunk/src/Web/cake/console/templates/skel/tests/cases/helpers/empty
    trunk/src/Web/cake/console/templates/skel/tests/cases/models/
    trunk/src/Web/cake/console/templates/skel/tests/cases/models/empty
    trunk/src/Web/cake/console/templates/skel/tests/cases/shells/
    trunk/src/Web/cake/console/templates/skel/tests/cases/shells/empty
    trunk/src/Web/cake/console/templates/skel/tests/fixtures/
    trunk/src/Web/cake/console/templates/skel/tests/fixtures/empty
    trunk/src/Web/cake/console/templates/skel/tests/groups/
    trunk/src/Web/cake/console/templates/skel/tests/groups/empty
    trunk/src/Web/cake/console/templates/skel/tmp/
    trunk/src/Web/cake/console/templates/skel/tmp/cache/
    trunk/src/Web/cake/console/templates/skel/tmp/cache/models/
    trunk/src/Web/cake/console/templates/skel/tmp/cache/models/empty
    trunk/src/Web/cake/console/templates/skel/tmp/cache/persistent/
    trunk/src/Web/cake/console/templates/skel/tmp/cache/persistent/empty
    trunk/src/Web/cake/console/templates/skel/tmp/cache/views/
    trunk/src/Web/cake/console/templates/skel/tmp/cache/views/empty
    trunk/src/Web/cake/console/templates/skel/tmp/logs/
    trunk/src/Web/cake/console/templates/skel/tmp/logs/empty
    trunk/src/Web/cake/console/templates/skel/tmp/sessions/
    trunk/src/Web/cake/console/templates/skel/tmp/sessions/empty
    trunk/src/Web/cake/console/templates/skel/tmp/tests/
    trunk/src/Web/cake/console/templates/skel/tmp/tests/empty
    trunk/src/Web/cake/console/templates/skel/vendors/
    trunk/src/Web/cake/console/templates/skel/vendors/shells/
    trunk/src/Web/cake/console/templates/skel/vendors/shells/tasks/
    trunk/src/Web/cake/console/templates/skel/vendors/shells/tasks/empty
    trunk/src/Web/cake/console/templates/skel/views/
    trunk/src/Web/cake/console/templates/skel/views/elements/
    trunk/src/Web/cake/console/templates/skel/views/elements/email/
    trunk/src/Web/cake/console/templates/skel/views/elements/email/html/
    trunk/src/Web/cake/console/templates/skel/views/elements/email/html/default.ctp
    trunk/src/Web/cake/console/templates/skel/views/elements/email/text/
    trunk/src/Web/cake/console/templates/skel/views/elements/email/text/default.ctp
    trunk/src/Web/cake/console/templates/skel/views/elements/empty
    trunk/src/Web/cake/console/templates/skel/views/errors/
    trunk/src/Web/cake/console/templates/skel/views/errors/empty
    trunk/src/Web/cake/console/templates/skel/views/helpers/
    trunk/src/Web/cake/console/templates/skel/views/helpers/empty
    trunk/src/Web/cake/console/templates/skel/views/layouts/
    trunk/src/Web/cake/console/templates/skel/views/layouts/ajax.ctp
    trunk/src/Web/cake/console/templates/skel/views/layouts/default.ctp
    trunk/src/Web/cake/console/templates/skel/views/layouts/email/
    trunk/src/Web/cake/console/templates/skel/views/layouts/email/html/
    trunk/src/Web/cake/console/templates/skel/views/layouts/email/html/default.ctp
    trunk/src/Web/cake/console/templates/skel/views/layouts/email/text/
    trunk/src/Web/cake/console/templates/skel/views/layouts/email/text/default.ctp
    trunk/src/Web/cake/console/templates/skel/views/layouts/flash.ctp
    trunk/src/Web/cake/console/templates/skel/views/layouts/js/
    trunk/src/Web/cake/console/templates/skel/views/layouts/js/default.ctp
    trunk/src/Web/cake/console/templates/skel/views/layouts/rss/
    trunk/src/Web/cake/console/templates/skel/views/layouts/rss/default.ctp
    trunk/src/Web/cake/console/templates/skel/views/layouts/xml/
    trunk/src/Web/cake/console/templates/skel/views/layouts/xml/default.ctp
    trunk/src/Web/cake/console/templates/skel/views/pages/
    trunk/src/Web/cake/console/templates/skel/views/pages/empty
    trunk/src/Web/cake/console/templates/skel/views/scaffolds/
    trunk/src/Web/cake/console/templates/skel/views/scaffolds/empty
    trunk/src/Web/cake/console/templates/skel/webroot/
    trunk/src/Web/cake/console/templates/skel/webroot/.htaccess
    trunk/src/Web/cake/console/templates/skel/webroot/css/
    trunk/src/Web/cake/console/templates/skel/webroot/css/cake.generic.css
    trunk/src/Web/cake/console/templates/skel/webroot/css.php
    trunk/src/Web/cake/console/templates/skel/webroot/favicon.ico
    trunk/src/Web/cake/console/templates/skel/webroot/img/
    trunk/src/Web/cake/console/templates/skel/webroot/img/cake.icon.png
    trunk/src/Web/cake/console/templates/skel/webroot/img/cake.power.gif
    trunk/src/Web/cake/console/templates/skel/webroot/img/test-error-icon.png
    trunk/src/Web/cake/console/templates/skel/webroot/img/test-fail-icon.png
    trunk/src/Web/cake/console/templates/skel/webroot/img/test-pass-icon.png
    trunk/src/Web/cake/console/templates/skel/webroot/img/test-skip-icon.png
    trunk/src/Web/cake/console/templates/skel/webroot/index.php
    trunk/src/Web/cake/console/templates/skel/webroot/js/
    trunk/src/Web/cake/console/templates/skel/webroot/js/empty
    trunk/src/Web/cake/console/templates/skel/webroot/test.php
    trunk/src/Web/cake/dispatcher.php
    trunk/src/Web/cake/libs/
    trunk/src/Web/cake/libs/cache/
    trunk/src/Web/cake/libs/cache/apc.php
    trunk/src/Web/cake/libs/cache/file.php
    trunk/src/Web/cake/libs/cache/memcache.php
    trunk/src/Web/cake/libs/cache/xcache.php
    trunk/src/Web/cake/libs/cache.php
    trunk/src/Web/cake/libs/cake_log.php
    trunk/src/Web/cake/libs/cake_session.php
    trunk/src/Web/cake/libs/cake_socket.php
    trunk/src/Web/cake/libs/class_registry.php
    trunk/src/Web/cake/libs/configure.php
    trunk/src/Web/cake/libs/controller/
    trunk/src/Web/cake/libs/controller/app_controller.php
    trunk/src/Web/cake/libs/controller/component.php
    trunk/src/Web/cake/libs/controller/components/
    trunk/src/Web/cake/libs/controller/components/acl.php
    trunk/src/Web/cake/libs/controller/components/auth.php
    trunk/src/Web/cake/libs/controller/components/cookie.php
    trunk/src/Web/cake/libs/controller/components/email.php
    trunk/src/Web/cake/libs/controller/components/request_handler.php
    trunk/src/Web/cake/libs/controller/components/security.php
    trunk/src/Web/cake/libs/controller/components/session.php
    trunk/src/Web/cake/libs/controller/controller.php
    trunk/src/Web/cake/libs/controller/pages_controller.php
    trunk/src/Web/cake/libs/controller/scaffold.php
    trunk/src/Web/cake/libs/debugger.php
    trunk/src/Web/cake/libs/error.php
    trunk/src/Web/cake/libs/file.php
    trunk/src/Web/cake/libs/folder.php
    trunk/src/Web/cake/libs/http_socket.php
    trunk/src/Web/cake/libs/i18n.php
    trunk/src/Web/cake/libs/inflector.php
    trunk/src/Web/cake/libs/l10n.php
    trunk/src/Web/cake/libs/log/
    trunk/src/Web/cake/libs/log/file_log.php
    trunk/src/Web/cake/libs/magic_db.php
    trunk/src/Web/cake/libs/model/
    trunk/src/Web/cake/libs/model/app_model.php
    trunk/src/Web/cake/libs/model/behaviors/
    trunk/src/Web/cake/libs/model/behaviors/acl.php
    trunk/src/Web/cake/libs/model/behaviors/containable.php
    trunk/src/Web/cake/libs/model/behaviors/translate.php
    trunk/src/Web/cake/libs/model/behaviors/tree.php
    trunk/src/Web/cake/libs/model/cake_schema.php
    trunk/src/Web/cake/libs/model/connection_manager.php
    trunk/src/Web/cake/libs/model/datasources/
    trunk/src/Web/cake/libs/model/datasources/datasource.php
    trunk/src/Web/cake/libs/model/datasources/dbo/
    trunk/src/Web/cake/libs/model/datasources/dbo/dbo_mssql.php
    trunk/src/Web/cake/libs/model/datasources/dbo/dbo_mysql.php
    trunk/src/Web/cake/libs/model/datasources/dbo/dbo_mysqli.php
    trunk/src/Web/cake/libs/model/datasources/dbo/dbo_oracle.php
    trunk/src/Web/cake/libs/model/datasources/dbo/dbo_postgres.php
    trunk/src/Web/cake/libs/model/datasources/dbo/dbo_sqlite.php
    trunk/src/Web/cake/libs/model/datasources/dbo_source.php
    trunk/src/Web/cake/libs/model/db_acl.php
    trunk/src/Web/cake/libs/model/model.php
    trunk/src/Web/cake/libs/model/model_behavior.php
    trunk/src/Web/cake/libs/multibyte.php
    trunk/src/Web/cake/libs/object.php
    trunk/src/Web/cake/libs/overloadable.php
    trunk/src/Web/cake/libs/overloadable_php4.php
    trunk/src/Web/cake/libs/overloadable_php5.php
    trunk/src/Web/cake/libs/router.php
    trunk/src/Web/cake/libs/sanitize.php
    trunk/src/Web/cake/libs/security.php
    trunk/src/Web/cake/libs/set.php
    trunk/src/Web/cake/libs/string.php
    trunk/src/Web/cake/libs/validation.php
    trunk/src/Web/cake/libs/view/
    trunk/src/Web/cake/libs/view/elements/
    trunk/src/Web/cake/libs/view/elements/email/
    trunk/src/Web/cake/libs/view/elements/email/html/
    trunk/src/Web/cake/libs/view/elements/email/html/default.ctp
    trunk/src/Web/cake/libs/view/elements/email/text/
    trunk/src/Web/cake/libs/view/elements/email/text/default.ctp
    trunk/src/Web/cake/libs/view/elements/sql_dump.ctp
    trunk/src/Web/cake/libs/view/errors/
    trunk/src/Web/cake/libs/view/errors/error404.ctp
    trunk/src/Web/cake/libs/view/errors/error500.ctp
    trunk/src/Web/cake/libs/view/errors/missing_action.ctp
    trunk/src/Web/cake/libs/view/errors/missing_behavior_class.ctp
    trunk/src/Web/cake/libs/view/errors/missing_behavior_file.ctp
    trunk/src/Web/cake/libs/view/errors/missing_component_class.ctp
    trunk/src/Web/cake/libs/view/errors/missing_component_file.ctp
    trunk/src/Web/cake/libs/view/errors/missing_connection.ctp
    trunk/src/Web/cake/libs/view/errors/missing_controller.ctp
    trunk/src/Web/cake/libs/view/errors/missing_helper_class.ctp
    trunk/src/Web/cake/libs/view/errors/missing_helper_file.ctp
    trunk/src/Web/cake/libs/view/errors/missing_layout.ctp
    trunk/src/Web/cake/libs/view/errors/missing_model.ctp
    trunk/src/Web/cake/libs/view/errors/missing_scaffolddb.ctp
    trunk/src/Web/cake/libs/view/errors/missing_table.ctp
    trunk/src/Web/cake/libs/view/errors/missing_view.ctp
    trunk/src/Web/cake/libs/view/errors/private_action.ctp
    trunk/src/Web/cake/libs/view/errors/scaffold_error.ctp
    trunk/src/Web/cake/libs/view/helper.php
    trunk/src/Web/cake/libs/view/helpers/
    trunk/src/Web/cake/libs/view/helpers/ajax.php
    trunk/src/Web/cake/libs/view/helpers/app_helper.php
    trunk/src/Web/cake/libs/view/helpers/cache.php
    trunk/src/Web/cake/libs/view/helpers/form.php
    trunk/src/Web/cake/libs/view/helpers/html.php
    trunk/src/Web/cake/libs/view/helpers/javascript.php
    trunk/src/Web/cake/libs/view/helpers/jquery_engine.php
    trunk/src/Web/cake/libs/view/helpers/js.php
    trunk/src/Web/cake/libs/view/helpers/mootools_engine.php
    trunk/src/Web/cake/libs/view/helpers/number.php
    trunk/src/Web/cake/libs/view/helpers/paginator.php
    trunk/src/Web/cake/libs/view/helpers/prototype_engine.php
    trunk/src/Web/cake/libs/view/helpers/rss.php
    trunk/src/Web/cake/libs/view/helpers/session.php
    trunk/src/Web/cake/libs/view/helpers/text.php
    trunk/src/Web/cake/libs/view/helpers/time.php
    trunk/src/Web/cake/libs/view/helpers/xml.php
    trunk/src/Web/cake/libs/view/layouts/
    trunk/src/Web/cake/libs/view/layouts/ajax.ctp
    trunk/src/Web/cake/libs/view/layouts/default.ctp
    trunk/src/Web/cake/libs/view/layouts/email/
    trunk/src/Web/cake/libs/view/layouts/email/html/
    trunk/src/Web/cake/libs/view/layouts/email/html/default.ctp
    trunk/src/Web/cake/libs/view/layouts/email/text/
    trunk/src/Web/cake/libs/view/layouts/email/text/default.ctp
    trunk/src/Web/cake/libs/view/layouts/flash.ctp
    trunk/src/Web/cake/libs/view/layouts/js/
    trunk/src/Web/cake/libs/view/layouts/js/default.ctp
    trunk/src/Web/cake/libs/view/layouts/rss/
    trunk/src/Web/cake/libs/view/layouts/rss/default.ctp
    trunk/src/Web/cake/libs/view/layouts/xml/
    trunk/src/Web/cake/libs/view/layouts/xml/default.ctp
    trunk/src/Web/cake/libs/view/media.php
    trunk/src/Web/cake/libs/view/pages/
    trunk/src/Web/cake/libs/view/pages/home.ctp
    trunk/src/Web/cake/libs/view/scaffolds/
    trunk/src/Web/cake/libs/view/scaffolds/edit.ctp
    trunk/src/Web/cake/libs/view/scaffolds/index.ctp
    trunk/src/Web/cake/libs/view/scaffolds/view.ctp
    trunk/src/Web/cake/libs/view/theme.php
    trunk/src/Web/cake/libs/view/view.php
    trunk/src/Web/cake/libs/xml.php
    trunk/src/Web/cake/tests/
    trunk/src/Web/cake/tests/cases/
    trunk/src/Web/cake/tests/cases/basics.test.php
    trunk/src/Web/cake/tests/cases/console/
    trunk/src/Web/cake/tests/cases/console/cake.test.php
    trunk/src/Web/cake/tests/cases/console/libs/
    trunk/src/Web/cake/tests/cases/console/libs/acl.test.php
    trunk/src/Web/cake/tests/cases/console/libs/api.test.php
    trunk/src/Web/cake/tests/cases/console/libs/bake.test.php
    trunk/src/Web/cake/tests/cases/console/libs/schema.test.php
    trunk/src/Web/cake/tests/cases/console/libs/shell.test.php
    trunk/src/Web/cake/tests/cases/console/libs/tasks/
    trunk/src/Web/cake/tests/cases/console/libs/tasks/controller.test.php
    trunk/src/Web/cake/tests/cases/console/libs/tasks/db_config.test.php
    trunk/src/Web/cake/tests/cases/console/libs/tasks/extract.test.php
    trunk/src/Web/cake/tests/cases/console/libs/tasks/fixture.test.php
    trunk/src/Web/cake/tests/cases/console/libs/tasks/model.test.php
    trunk/src/Web/cake/tests/cases/console/libs/tasks/plugin.test.php
    trunk/src/Web/cake/tests/cases/console/libs/tasks/project.test.php
    trunk/src/Web/cake/tests/cases/console/libs/tasks/template.test.php
    trunk/src/Web/cake/tests/cases/console/libs/tasks/test.test.php
    trunk/src/Web/cake/tests/cases/console/libs/tasks/view.test.php
    trunk/src/Web/cake/tests/cases/dispatcher.test.php
    trunk/src/Web/cake/tests/cases/libs/
    trunk/src/Web/cake/tests/cases/libs/cache/
    trunk/src/Web/cake/tests/cases/libs/cache/apc.test.php
    trunk/src/Web/cake/tests/cases/libs/cache/file.test.php
    trunk/src/Web/cake/tests/cases/libs/cache/memcache.test.php
    trunk/src/Web/cake/tests/cases/libs/cache/xcache.test.php
    trunk/src/Web/cake/tests/cases/libs/cache.test.php
    trunk/src/Web/cake/tests/cases/libs/cake_log.test.php
    trunk/src/Web/cake/tests/cases/libs/cake_session.test.php
    trunk/src/Web/cake/tests/cases/libs/cake_socket.test.php
    trunk/src/Web/cake/tests/cases/libs/cake_test_case.test.php
    trunk/src/Web/cake/tests/cases/libs/cake_test_fixture.test.php
    trunk/src/Web/cake/tests/cases/libs/class_registry.test.php
    trunk/src/Web/cake/tests/cases/libs/code_coverage_manager.test.php
    trunk/src/Web/cake/tests/cases/libs/configure.test.php
    trunk/src/Web/cake/tests/cases/libs/controller/
    trunk/src/Web/cake/tests/cases/libs/controller/component.test.php
    trunk/src/Web/cake/tests/cases/libs/controller/components/
    trunk/src/Web/cake/tests/cases/libs/controller/components/acl.test.php
    trunk/src/Web/cake/tests/cases/libs/controller/components/auth.test.php
    trunk/src/Web/cake/tests/cases/libs/controller/components/cookie.test.php
    trunk/src/Web/cake/tests/cases/libs/controller/components/email.test.php
    trunk/src/Web/cake/tests/cases/libs/controller/components/request_handler.test.php
    trunk/src/Web/cake/tests/cases/libs/controller/components/security.test.php
    trunk/src/Web/cake/tests/cases/libs/controller/components/session.test.php
    trunk/src/Web/cake/tests/cases/libs/controller/controller.test.php
    trunk/src/Web/cake/tests/cases/libs/controller/controller_merge_vars.test.php
    trunk/src/Web/cake/tests/cases/libs/controller/pages_controller.test.php
    trunk/src/Web/cake/tests/cases/libs/controller/scaffold.test.php
    trunk/src/Web/cake/tests/cases/libs/debugger.test.php
    trunk/src/Web/cake/tests/cases/libs/error.test.php
    trunk/src/Web/cake/tests/cases/libs/file.test.php
    trunk/src/Web/cake/tests/cases/libs/folder.test.php
    trunk/src/Web/cake/tests/cases/libs/http_socket.test.php
    trunk/src/Web/cake/tests/cases/libs/i18n.test.php
    trunk/src/Web/cake/tests/cases/libs/inflector.test.php
    trunk/src/Web/cake/tests/cases/libs/l10n.test.php
    trunk/src/Web/cake/tests/cases/libs/log/
    trunk/src/Web/cake/tests/cases/libs/log/file_log.test.php
    trunk/src/Web/cake/tests/cases/libs/magic_db.test.php
    trunk/src/Web/cake/tests/cases/libs/model/
    trunk/src/Web/cake/tests/cases/libs/model/behaviors/
    trunk/src/Web/cake/tests/cases/libs/model/behaviors/acl.test.php
    trunk/src/Web/cake/tests/cases/libs/model/behaviors/containable.test.php
    trunk/src/Web/cake/tests/cases/libs/model/behaviors/translate.test.php
    trunk/src/Web/cake/tests/cases/libs/model/behaviors/tree.test.php
    trunk/src/Web/cake/tests/cases/libs/model/cake_schema.test.php
    trunk/src/Web/cake/tests/cases/libs/model/connection_manager.test.php
    trunk/src/Web/cake/tests/cases/libs/model/datasources/
    trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo/
    trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo/dbo_mssql.test.php
    trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo/dbo_mysql.test.php
    trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo/dbo_mysqli.test.php
    trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo/dbo_oracle.test.php
    trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo/dbo_postgres.test.php
    trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo/dbo_sqlite.test.php
    trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo_source.test.php
    trunk/src/Web/cake/tests/cases/libs/model/db_acl.test.php
    trunk/src/Web/cake/tests/cases/libs/model/model.test.php
    trunk/src/Web/cake/tests/cases/libs/model/model_behavior.test.php
    trunk/src/Web/cake/tests/cases/libs/model/model_delete.test.php
    trunk/src/Web/cake/tests/cases/libs/model/model_integration.test.php
    trunk/src/Web/cake/tests/cases/libs/model/model_read.test.php
    trunk/src/Web/cake/tests/cases/libs/model/model_validation.test.php
    trunk/src/Web/cake/tests/cases/libs/model/model_write.test.php
    trunk/src/Web/cake/tests/cases/libs/model/models.php
    trunk/src/Web/cake/tests/cases/libs/multibyte.test.php
    trunk/src/Web/cake/tests/cases/libs/object.test.php
    trunk/src/Web/cake/tests/cases/libs/overloadable.test.php
    trunk/src/Web/cake/tests/cases/libs/router.test.php
    trunk/src/Web/cake/tests/cases/libs/sanitize.test.php
    trunk/src/Web/cake/tests/cases/libs/security.test.php
    trunk/src/Web/cake/tests/cases/libs/set.test.php
    trunk/src/Web/cake/tests/cases/libs/string.test.php
    trunk/src/Web/cake/tests/cases/libs/test_manager.test.php
    trunk/src/Web/cake/tests/cases/libs/validation.test.php
    trunk/src/Web/cake/tests/cases/libs/view/
    trunk/src/Web/cake/tests/cases/libs/view/helper.test.php
    trunk/src/Web/cake/tests/cases/libs/view/helpers/
    trunk/src/Web/cake/tests/cases/libs/view/helpers/ajax.test.php
    trunk/src/Web/cake/tests/cases/libs/view/helpers/cache.test.php
    trunk/src/Web/cake/tests/cases/libs/view/helpers/form.test.php
    trunk/src/Web/cake/tests/cases/libs/view/helpers/html.test.php
    trunk/src/Web/cake/tests/cases/libs/view/helpers/javascript.test.php
    trunk/src/Web/cake/tests/cases/libs/view/helpers/jquery_engine.test.php
    trunk/src/Web/cake/tests/cases/libs/view/helpers/js.test.php
    trunk/src/Web/cake/tests/cases/libs/view/helpers/mootools_engine.test.php
    trunk/src/Web/cake/tests/cases/libs/view/helpers/number.test.php
    trunk/src/Web/cake/tests/cases/libs/view/helpers/paginator.test.php
    trunk/src/Web/cake/tests/cases/libs/view/helpers/prototype_engine.test.php
    trunk/src/Web/cake/tests/cases/libs/view/helpers/rss.test.php
    trunk/src/Web/cake/tests/cases/libs/view/helpers/session.test.php
    trunk/src/Web/cake/tests/cases/libs/view/helpers/text.test.php
    trunk/src/Web/cake/tests/cases/libs/view/helpers/time.test.php
    trunk/src/Web/cake/tests/cases/libs/view/helpers/xml.test.php
    trunk/src/Web/cake/tests/cases/libs/view/media.test.php
    trunk/src/Web/cake/tests/cases/libs/view/theme.test.php
    trunk/src/Web/cake/tests/cases/libs/view/view.test.php
    trunk/src/Web/cake/tests/cases/libs/xml.test.php
    trunk/src/Web/cake/tests/fixtures/
    trunk/src/Web/cake/tests/fixtures/account_fixture.php
    trunk/src/Web/cake/tests/fixtures/aco_action_fixture.php
    trunk/src/Web/cake/tests/fixtures/aco_fixture.php
    trunk/src/Web/cake/tests/fixtures/aco_two_fixture.php
    trunk/src/Web/cake/tests/fixtures/ad_fixture.php
    trunk/src/Web/cake/tests/fixtures/advertisement_fixture.php
    trunk/src/Web/cake/tests/fixtures/after_tree_fixture.php
    trunk/src/Web/cake/tests/fixtures/another_article_fixture.php
    trunk/src/Web/cake/tests/fixtures/apple_fixture.php
    trunk/src/Web/cake/tests/fixtures/aro_fixture.php
    trunk/src/Web/cake/tests/fixtures/aro_two_fixture.php
    trunk/src/Web/cake/tests/fixtures/aros_aco_fixture.php
    trunk/src/Web/cake/tests/fixtures/aros_aco_two_fixture.php
    trunk/src/Web/cake/tests/fixtures/article_featured_fixture.php
    trunk/src/Web/cake/tests/fixtures/article_featureds_tags_fixture.php
    trunk/src/Web/cake/tests/fixtures/article_fixture.php
    trunk/src/Web/cake/tests/fixtures/articles_tag_fixture.php
    trunk/src/Web/cake/tests/fixtures/attachment_fixture.php
    trunk/src/Web/cake/tests/fixtures/auth_user_custom_field_fixture.php
    trunk/src/Web/cake/tests/fixtures/auth_user_fixture.php
    trunk/src/Web/cake/tests/fixtures/author_fixture.php
    trunk/src/Web/cake/tests/fixtures/basket_fixture.php
    trunk/src/Web/cake/tests/fixtures/bid_fixture.php
    trunk/src/Web/cake/tests/fixtures/bidding_fixture.php
    trunk/src/Web/cake/tests/fixtures/bidding_message_fixture.php
    trunk/src/Web/cake/tests/fixtures/binary_test_fixture.php
    trunk/src/Web/cake/tests/fixtures/book_fixture.php
    trunk/src/Web/cake/tests/fixtures/cache_test_model_fixture.php
    trunk/src/Web/cake/tests/fixtures/callback_fixture.php
    trunk/src/Web/cake/tests/fixtures/campaign_fixture.php
    trunk/src/Web/cake/tests/fixtures/category_fixture.php
    trunk/src/Web/cake/tests/fixtures/category_thread_fixture.php
    trunk/src/Web/cake/tests/fixtures/cd_fixture.php
    trunk/src/Web/cake/tests/fixtures/comment_fixture.php
    trunk/src/Web/cake/tests/fixtures/content_account_fixture.php
    trunk/src/Web/cake/tests/fixtures/content_fixture.php
    trunk/src/Web/cake/tests/fixtures/counter_cache_post_fixture.php
    trunk/src/Web/cake/tests/fixtures/counter_cache_post_nonstandard_primary_key_fixture.php
    trunk/src/Web/cake/tests/fixtures/counter_cache_user_fixture.php
    trunk/src/Web/cake/tests/fixtures/counter_cache_user_nonstandard_primary_key_fixture.php
    trunk/src/Web/cake/tests/fixtures/data_test_fixture.php
    trunk/src/Web/cake/tests/fixtures/datatype_fixture.php
    trunk/src/Web/cake/tests/fixtures/dependency_fixture.php
    trunk/src/Web/cake/tests/fixtures/device_fixture.php
    trunk/src/Web/cake/tests/fixtures/device_type_category_fixture.php
    trunk/src/Web/cake/tests/fixtures/device_type_fixture.php
    trunk/src/Web/cake/tests/fixtures/document_directory_fixture.php
    trunk/src/Web/cake/tests/fixtures/document_fixture.php
    trunk/src/Web/cake/tests/fixtures/exterior_type_category_fixture.php
    trunk/src/Web/cake/tests/fixtures/feature_set_fixture.php
    trunk/src/Web/cake/tests/fixtures/featured_fixture.php
    trunk/src/Web/cake/tests/fixtures/film_file_fixture.php
    trunk/src/Web/cake/tests/fixtures/flag_tree_fixture.php
    trunk/src/Web/cake/tests/fixtures/fruit_fixture.php
    trunk/src/Web/cake/tests/fixtures/fruits_uuid_tag_fixture.php
    trunk/src/Web/cake/tests/fixtures/group_update_all_fixture.php
    trunk/src/Web/cake/tests/fixtures/home_fixture.php
    trunk/src/Web/cake/tests/fixtures/image_fixture.php
    trunk/src/Web/cake/tests/fixtures/item_fixture.php
    trunk/src/Web/cake/tests/fixtures/items_portfolio_fixture.php
    trunk/src/Web/cake/tests/fixtures/join_a_b_fixture.php
    trunk/src/Web/cake/tests/fixtures/join_a_c_fixture.php
    trunk/src/Web/cake/tests/fixtures/join_a_fixture.php
    trunk/src/Web/cake/tests/fixtures/join_b_fixture.php
    trunk/src/Web/cake/tests/fixtures/join_c_fixture.php
    trunk/src/Web/cake/tests/fixtures/join_thing_fixture.php
    trunk/src/Web/cake/tests/fixtures/message_fixture.php
    trunk/src/Web/cake/tests/fixtures/my_categories_my_products_fixture.php
    trunk/src/Web/cake/tests/fixtures/my_categories_my_users_fixture.php
    trunk/src/Web/cake/tests/fixtures/my_category_fixture.php
    trunk/src/Web/cake/tests/fixtures/my_product_fixture.php
    trunk/src/Web/cake/tests/fixtures/my_user_fixture.php
    trunk/src/Web/cake/tests/fixtures/node_fixture.php
    trunk/src/Web/cake/tests/fixtures/number_tree_fixture.php
    trunk/src/Web/cake/tests/fixtures/number_tree_two_fixture.php
    trunk/src/Web/cake/tests/fixtures/numeric_article_fixture.php
    trunk/src/Web/cake/tests/fixtures/overall_favorite_fixture.php
    trunk/src/Web/cake/tests/fixtures/person_fixture.php
    trunk/src/Web/cake/tests/fixtures/portfolio_fixture.php
    trunk/src/Web/cake/tests/fixtures/post_fixture.php
    trunk/src/Web/cake/tests/fixtures/posts_tag_fixture.php
    trunk/src/Web/cake/tests/fixtures/prefix_test_fixture.php
    trunk/src/Web/cake/tests/fixtures/primary_model_fixture.php
    trunk/src/Web/cake/tests/fixtures/product_fixture.php
    trunk/src/Web/cake/tests/fixtures/product_update_all_fixture.php
    trunk/src/Web/cake/tests/fixtures/project_fixture.php
    trunk/src/Web/cake/tests/fixtures/sample_fixture.php
    trunk/src/Web/cake/tests/fixtures/secondary_model_fixture.php
    trunk/src/Web/cake/tests/fixtures/session_fixture.php
    trunk/src/Web/cake/tests/fixtures/something_else_fixture.php
    trunk/src/Web/cake/tests/fixtures/something_fixture.php
    trunk/src/Web/cake/tests/fixtures/stories_tag_fixture.php
    trunk/src/Web/cake/tests/fixtures/story_fixture.php
    trunk/src/Web/cake/tests/fixtures/syfile_fixture.php
    trunk/src/Web/cake/tests/fixtures/tag_fixture.php
    trunk/src/Web/cake/tests/fixtures/test_plugin_article_fixture.php
    trunk/src/Web/cake/tests/fixtures/test_plugin_comment_fixture.php
    trunk/src/Web/cake/tests/fixtures/the_paper_monkies_fixture.php
    trunk/src/Web/cake/tests/fixtures/thread_fixture.php
    trunk/src/Web/cake/tests/fixtures/translate_article_fixture.php
    trunk/src/Web/cake/tests/fixtures/translate_fixture.php
    trunk/src/Web/cake/tests/fixtures/translate_table_fixture.php
    trunk/src/Web/cake/tests/fixtures/translate_with_prefix_fixture.php
    trunk/src/Web/cake/tests/fixtures/translated_article_fixture.php
    trunk/src/Web/cake/tests/fixtures/translated_item_fixture.php
    trunk/src/Web/cake/tests/fixtures/unconventional_tree_fixture.php
    trunk/src/Web/cake/tests/fixtures/underscore_field_fixture.php
    trunk/src/Web/cake/tests/fixtures/user_fixture.php
    trunk/src/Web/cake/tests/fixtures/uuid_fixture.php
    trunk/src/Web/cake/tests/fixtures/uuid_tag_fixture.php
    trunk/src/Web/cake/tests/fixtures/uuid_tree_fixture.php
    trunk/src/Web/cake/tests/fixtures/uuiditem_fixture.php
    trunk/src/Web/cake/tests/fixtures/uuiditems_uuidportfolio_fixture.php
    trunk/src/Web/cake/tests/fixtures/uuiditems_uuidportfolio_numericid_fixture.php
    trunk/src/Web/cake/tests/fixtures/uuidportfolio_fixture.php
    trunk/src/Web/cake/tests/groups/
    trunk/src/Web/cake/tests/groups/acl.group.php
    trunk/src/Web/cake/tests/groups/bake.group.php
    trunk/src/Web/cake/tests/groups/behaviors.group.php
    trunk/src/Web/cake/tests/groups/cache.group.php
    trunk/src/Web/cake/tests/groups/components.group.php
    trunk/src/Web/cake/tests/groups/configure.group.php
    trunk/src/Web/cake/tests/groups/console.group.php
    trunk/src/Web/cake/tests/groups/controller.group.php
    trunk/src/Web/cake/tests/groups/database.group.php
    trunk/src/Web/cake/tests/groups/helpers.group.php
    trunk/src/Web/cake/tests/groups/i18n.group.php
    trunk/src/Web/cake/tests/groups/javascript.group.php
    trunk/src/Web/cake/tests/groups/lib.group.php
    trunk/src/Web/cake/tests/groups/model.group.php
    trunk/src/Web/cake/tests/groups/no_cross_contamination.group.php
    trunk/src/Web/cake/tests/groups/routing_system.group.php
    trunk/src/Web/cake/tests/groups/socket.group.php
    trunk/src/Web/cake/tests/groups/test_suite.group.php
    trunk/src/Web/cake/tests/groups/view.group.php
    trunk/src/Web/cake/tests/groups/xml.group.php
    trunk/src/Web/cake/tests/lib/
    trunk/src/Web/cake/tests/lib/cake_test_case.php
    trunk/src/Web/cake/tests/lib/cake_test_fixture.php
    trunk/src/Web/cake/tests/lib/cake_test_model.php
    trunk/src/Web/cake/tests/lib/cake_test_suite_dispatcher.php
    trunk/src/Web/cake/tests/lib/cake_web_test_case.php
    trunk/src/Web/cake/tests/lib/code_coverage_manager.php
    trunk/src/Web/cake/tests/lib/reporter/
    trunk/src/Web/cake/tests/lib/reporter/cake_base_reporter.php
    trunk/src/Web/cake/tests/lib/reporter/cake_cli_reporter.php
    trunk/src/Web/cake/tests/lib/reporter/cake_html_reporter.php
    trunk/src/Web/cake/tests/lib/reporter/cake_text_reporter.php
    trunk/src/Web/cake/tests/lib/templates/
    trunk/src/Web/cake/tests/lib/templates/footer.php
    trunk/src/Web/cake/tests/lib/templates/header.php
    trunk/src/Web/cake/tests/lib/templates/menu.php
    trunk/src/Web/cake/tests/lib/templates/simpletest.php
    trunk/src/Web/cake/tests/lib/templates/xdebug.php
    trunk/src/Web/cake/tests/lib/test_manager.php
    trunk/src/Web/cake/tests/test_app/
    trunk/src/Web/cake/tests/test_app/config/
    trunk/src/Web/cake/tests/test_app/config/acl.ini.php
    trunk/src/Web/cake/tests/test_app/controllers/
    trunk/src/Web/cake/tests/test_app/controllers/components/
    trunk/src/Web/cake/tests/test_app/controllers/components/empty
    trunk/src/Web/cake/tests/test_app/controllers/tests_apps_controller.php
    trunk/src/Web/cake/tests/test_app/controllers/tests_apps_posts_controller.php
    trunk/src/Web/cake/tests/test_app/libs/
    trunk/src/Web/cake/tests/test_app/libs/cache/
    trunk/src/Web/cake/tests/test_app/libs/cache/test_app_cache.php
    trunk/src/Web/cake/tests/test_app/libs/library.php
    trunk/src/Web/cake/tests/test_app/libs/log/
    trunk/src/Web/cake/tests/test_app/libs/log/test_app_log.php
    trunk/src/Web/cake/tests/test_app/locale/
    trunk/src/Web/cake/tests/test_app/locale/cache_test_po/
    trunk/src/Web/cake/tests/test_app/locale/cache_test_po/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/cache_test_po/LC_MESSAGES/default.po
    trunk/src/Web/cake/tests/test_app/locale/cache_test_po/LC_MESSAGES/dom1.po
    trunk/src/Web/cake/tests/test_app/locale/cache_test_po/LC_MESSAGES/dom2.po
    trunk/src/Web/cake/tests/test_app/locale/ja_jp/
    trunk/src/Web/cake/tests/test_app/locale/ja_jp/LC_TIME
    trunk/src/Web/cake/tests/test_app/locale/po/
    trunk/src/Web/cake/tests/test_app/locale/po/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/po/LC_MESSAGES/default.po
    trunk/src/Web/cake/tests/test_app/locale/po/LC_MONETARY/
    trunk/src/Web/cake/tests/test_app/locale/po/LC_MONETARY/default.po
    trunk/src/Web/cake/tests/test_app/locale/po/LC_TIME
    trunk/src/Web/cake/tests/test_app/locale/rule_0_mo/
    trunk/src/Web/cake/tests/test_app/locale/rule_0_mo/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_0_mo/LC_MESSAGES/core.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_0_mo/LC_MESSAGES/default.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_0_po/
    trunk/src/Web/cake/tests/test_app/locale/rule_0_po/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_0_po/LC_MESSAGES/core.po
    trunk/src/Web/cake/tests/test_app/locale/rule_0_po/LC_MESSAGES/default.po
    trunk/src/Web/cake/tests/test_app/locale/rule_10_mo/
    trunk/src/Web/cake/tests/test_app/locale/rule_10_mo/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_10_mo/LC_MESSAGES/core.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_10_mo/LC_MESSAGES/default.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_10_po/
    trunk/src/Web/cake/tests/test_app/locale/rule_10_po/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_10_po/LC_MESSAGES/core.po
    trunk/src/Web/cake/tests/test_app/locale/rule_10_po/LC_MESSAGES/default.po
    trunk/src/Web/cake/tests/test_app/locale/rule_11_mo/
    trunk/src/Web/cake/tests/test_app/locale/rule_11_mo/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_11_mo/LC_MESSAGES/core.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_11_mo/LC_MESSAGES/default.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_11_po/
    trunk/src/Web/cake/tests/test_app/locale/rule_11_po/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_11_po/LC_MESSAGES/core.po
    trunk/src/Web/cake/tests/test_app/locale/rule_11_po/LC_MESSAGES/default.po
    trunk/src/Web/cake/tests/test_app/locale/rule_12_mo/
    trunk/src/Web/cake/tests/test_app/locale/rule_12_mo/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_12_mo/LC_MESSAGES/core.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_12_mo/LC_MESSAGES/default.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_12_po/
    trunk/src/Web/cake/tests/test_app/locale/rule_12_po/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_12_po/LC_MESSAGES/core.po
    trunk/src/Web/cake/tests/test_app/locale/rule_12_po/LC_MESSAGES/default.po
    trunk/src/Web/cake/tests/test_app/locale/rule_13_mo/
    trunk/src/Web/cake/tests/test_app/locale/rule_13_mo/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_13_mo/LC_MESSAGES/core.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_13_mo/LC_MESSAGES/default.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_13_po/
    trunk/src/Web/cake/tests/test_app/locale/rule_13_po/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_13_po/LC_MESSAGES/core.po
    trunk/src/Web/cake/tests/test_app/locale/rule_13_po/LC_MESSAGES/default.po
    trunk/src/Web/cake/tests/test_app/locale/rule_14_mo/
    trunk/src/Web/cake/tests/test_app/locale/rule_14_mo/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_14_mo/LC_MESSAGES/core.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_14_mo/LC_MESSAGES/default.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_14_po/
    trunk/src/Web/cake/tests/test_app/locale/rule_14_po/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_14_po/LC_MESSAGES/core.po
    trunk/src/Web/cake/tests/test_app/locale/rule_14_po/LC_MESSAGES/default.po
    trunk/src/Web/cake/tests/test_app/locale/rule_1_mo/
    trunk/src/Web/cake/tests/test_app/locale/rule_1_mo/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_1_mo/LC_MESSAGES/core.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_1_mo/LC_MESSAGES/default.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_1_po/
    trunk/src/Web/cake/tests/test_app/locale/rule_1_po/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_1_po/LC_MESSAGES/core.po
    trunk/src/Web/cake/tests/test_app/locale/rule_1_po/LC_MESSAGES/default.po
    trunk/src/Web/cake/tests/test_app/locale/rule_2_mo/
    trunk/src/Web/cake/tests/test_app/locale/rule_2_mo/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_2_mo/LC_MESSAGES/core.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_2_mo/LC_MESSAGES/default.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_2_po/
    trunk/src/Web/cake/tests/test_app/locale/rule_2_po/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_2_po/LC_MESSAGES/core.po
    trunk/src/Web/cake/tests/test_app/locale/rule_2_po/LC_MESSAGES/default.po
    trunk/src/Web/cake/tests/test_app/locale/rule_3_mo/
    trunk/src/Web/cake/tests/test_app/locale/rule_3_mo/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_3_mo/LC_MESSAGES/core.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_3_mo/LC_MESSAGES/default.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_3_po/
    trunk/src/Web/cake/tests/test_app/locale/rule_3_po/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_3_po/LC_MESSAGES/core.po
    trunk/src/Web/cake/tests/test_app/locale/rule_3_po/LC_MESSAGES/default.po
    trunk/src/Web/cake/tests/test_app/locale/rule_4_mo/
    trunk/src/Web/cake/tests/test_app/locale/rule_4_mo/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_4_mo/LC_MESSAGES/core.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_4_mo/LC_MESSAGES/default.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_4_po/
    trunk/src/Web/cake/tests/test_app/locale/rule_4_po/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_4_po/LC_MESSAGES/core.po
    trunk/src/Web/cake/tests/test_app/locale/rule_4_po/LC_MESSAGES/default.po
    trunk/src/Web/cake/tests/test_app/locale/rule_5_mo/
    trunk/src/Web/cake/tests/test_app/locale/rule_5_mo/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_5_mo/LC_MESSAGES/core.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_5_mo/LC_MESSAGES/default.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_5_po/
    trunk/src/Web/cake/tests/test_app/locale/rule_5_po/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_5_po/LC_MESSAGES/core.po
    trunk/src/Web/cake/tests/test_app/locale/rule_5_po/LC_MESSAGES/default.po
    trunk/src/Web/cake/tests/test_app/locale/rule_6_mo/
    trunk/src/Web/cake/tests/test_app/locale/rule_6_mo/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_6_mo/LC_MESSAGES/core.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_6_mo/LC_MESSAGES/default.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_6_po/
    trunk/src/Web/cake/tests/test_app/locale/rule_6_po/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_6_po/LC_MESSAGES/core.po
    trunk/src/Web/cake/tests/test_app/locale/rule_6_po/LC_MESSAGES/default.po
    trunk/src/Web/cake/tests/test_app/locale/rule_7_mo/
    trunk/src/Web/cake/tests/test_app/locale/rule_7_mo/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_7_mo/LC_MESSAGES/core.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_7_mo/LC_MESSAGES/default.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_7_po/
    trunk/src/Web/cake/tests/test_app/locale/rule_7_po/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_7_po/LC_MESSAGES/core.po
    trunk/src/Web/cake/tests/test_app/locale/rule_7_po/LC_MESSAGES/default.po
    trunk/src/Web/cake/tests/test_app/locale/rule_8_mo/
    trunk/src/Web/cake/tests/test_app/locale/rule_8_mo/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_8_mo/LC_MESSAGES/core.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_8_mo/LC_MESSAGES/default.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_8_po/
    trunk/src/Web/cake/tests/test_app/locale/rule_8_po/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_8_po/LC_MESSAGES/core.po
    trunk/src/Web/cake/tests/test_app/locale/rule_8_po/LC_MESSAGES/default.po
    trunk/src/Web/cake/tests/test_app/locale/rule_9_mo/
    trunk/src/Web/cake/tests/test_app/locale/rule_9_mo/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_9_mo/LC_MESSAGES/core.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_9_mo/LC_MESSAGES/default.mo
    trunk/src/Web/cake/tests/test_app/locale/rule_9_po/
    trunk/src/Web/cake/tests/test_app/locale/rule_9_po/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/locale/rule_9_po/LC_MESSAGES/core.po
    trunk/src/Web/cake/tests/test_app/locale/rule_9_po/LC_MESSAGES/default.po
    trunk/src/Web/cake/tests/test_app/locale/time_test/
    trunk/src/Web/cake/tests/test_app/locale/time_test/LC_TIME
    trunk/src/Web/cake/tests/test_app/models/
    trunk/src/Web/cake/tests/test_app/models/behaviors/
    trunk/src/Web/cake/tests/test_app/models/behaviors/empty
    trunk/src/Web/cake/tests/test_app/models/behaviors/persister_one_behavior.php
    trunk/src/Web/cake/tests/test_app/models/behaviors/persister_two_behavior.php
    trunk/src/Web/cake/tests/test_app/models/comment.php
    trunk/src/Web/cake/tests/test_app/models/datasources/
    trunk/src/Web/cake/tests/test_app/models/datasources/test/
    trunk/src/Web/cake/tests/test_app/models/datasources/test/test_local_driver.php
    trunk/src/Web/cake/tests/test_app/models/datasources/test2_other_source.php
    trunk/src/Web/cake/tests/test_app/models/datasources/test2_source.php
    trunk/src/Web/cake/tests/test_app/models/persister_one.php
    trunk/src/Web/cake/tests/test_app/models/persister_two.php
    trunk/src/Web/cake/tests/test_app/models/post.php
    trunk/src/Web/cake/tests/test_app/plugins/
    trunk/src/Web/cake/tests/test_app/plugins/plugin_js/
    trunk/src/Web/cake/tests/test_app/plugins/plugin_js/webroot/
    trunk/src/Web/cake/tests/test_app/plugins/plugin_js/webroot/js/
    trunk/src/Web/cake/tests/test_app/plugins/plugin_js/webroot/js/one/
    trunk/src/Web/cake/tests/test_app/plugins/plugin_js/webroot/js/one/plugin_one.js
    trunk/src/Web/cake/tests/test_app/plugins/plugin_js/webroot/js/plugin_js.js
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/config/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/config/load.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/config/more.load.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/config/schema/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/config/schema/schema.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/controllers/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/controllers/components/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/controllers/components/other_component.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/controllers/components/plugins_component.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/controllers/components/test_plugin_component.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/controllers/components/test_plugin_other_component.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/controllers/test_plugin_controller.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/controllers/tests_controller.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/libs/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/libs/cache/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/libs/cache/test_plugin_cache.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/libs/log/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/libs/log/test_plugin_log.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/libs/test_plugin_library.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/locale/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/locale/po/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/locale/po/LC_MESSAGES/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/locale/po/LC_MESSAGES/test_plugin.po
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/locale/po/LC_MONETARY/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/locale/po/LC_MONETARY/test_plugin.po
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/behaviors/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/behaviors/test_plugin_persister_one.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/behaviors/test_plugin_persister_two.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/datasources/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/datasources/dbo/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/datasources/dbo/dbo_dummy.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/datasources/test/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/datasources/test/test_driver.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/datasources/test_other_source.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/datasources/test_source.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/test_plugin_auth_user.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/test_plugin_authors.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/test_plugin_comment.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/test_plugin_post.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/test_plugin_app_controller.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/test_plugin_app_model.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/vendors/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/vendors/sample/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/vendors/sample/sample_plugin.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/vendors/shells/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/vendors/shells/example.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/vendors/shells/tasks/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/vendors/shells/tasks/empty
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/vendors/shells/templates/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/vendors/shells/templates/empty
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/vendors/welcome.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/elements/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/elements/plugin_element.ctp
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/elements/test_plugin_element.ctp
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/helpers/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/helpers/other_helper.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/helpers/plugged_helper.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/helpers/test_plugin_app.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/layouts/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/layouts/default.ctp
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/tests/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/tests/index.ctp
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/tests/scaffold.edit.ctp
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/css/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/css/test_plugin_asset.css
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/css/theme_one.htc
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/css/unknown.extension
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/flash/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/flash/plugin_test.swf
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/img/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/img/cake.icon.gif
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/js/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/js/test_plugin/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/js/test_plugin/test.js
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/pdfs/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/pdfs/plugin_test.pdf
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/root.js
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin_two/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin_two/vendors/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin_two/vendors/shells/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin_two/vendors/shells/example.php
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin_two/vendors/shells/tasks/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin_two/vendors/shells/tasks/empty
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin_two/vendors/shells/templates/
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin_two/vendors/shells/templates/empty
    trunk/src/Web/cake/tests/test_app/plugins/test_plugin_two/vendors/shells/welcome.php
    trunk/src/Web/cake/tests/test_app/tmp/
    trunk/src/Web/cake/tests/test_app/tmp/dir_map
    trunk/src/Web/cake/tests/test_app/vendors/
    trunk/src/Web/cake/tests/test_app/vendors/Test/
    trunk/src/Web/cake/tests/test_app/vendors/Test/MyTest.php
    trunk/src/Web/cake/tests/test_app/vendors/Test/hello.php
    trunk/src/Web/cake/tests/test_app/vendors/css/
    trunk/src/Web/cake/tests/test_app/vendors/css/test_asset.css
    trunk/src/Web/cake/tests/test_app/vendors/img/
    trunk/src/Web/cake/tests/test_app/vendors/img/test.jpg
    trunk/src/Web/cake/tests/test_app/vendors/img/test_2.JPG
    trunk/src/Web/cake/tests/test_app/vendors/sample/
    trunk/src/Web/cake/tests/test_app/vendors/sample/configure_test_vendor_sample.php
    trunk/src/Web/cake/tests/test_app/vendors/shells/
    trunk/src/Web/cake/tests/test_app/vendors/shells/sample.php
    trunk/src/Web/cake/tests/test_app/vendors/shells/tasks/
    trunk/src/Web/cake/tests/test_app/vendors/shells/tasks/empty
    trunk/src/Web/cake/tests/test_app/vendors/shells/templates/
    trunk/src/Web/cake/tests/test_app/vendors/shells/templates/test/
    trunk/src/Web/cake/tests/test_app/vendors/shells/templates/test/classes/
    trunk/src/Web/cake/tests/test_app/vendors/shells/templates/test/classes/test_object.ctp
    trunk/src/Web/cake/tests/test_app/vendors/somename/
    trunk/src/Web/cake/tests/test_app/vendors/somename/some.name.php
    trunk/src/Web/cake/tests/test_app/vendors/welcome.php
    trunk/src/Web/cake/tests/test_app/views/
    trunk/src/Web/cake/tests/test_app/views/elements/
    trunk/src/Web/cake/tests/test_app/views/elements/email/
    trunk/src/Web/cake/tests/test_app/views/elements/email/html/
    trunk/src/Web/cake/tests/test_app/views/elements/email/html/custom.ctp
    trunk/src/Web/cake/tests/test_app/views/elements/email/html/default.ctp
    trunk/src/Web/cake/tests/test_app/views/elements/email/html/nested_element.ctp
    trunk/src/Web/cake/tests/test_app/views/elements/email/text/
    trunk/src/Web/cake/tests/test_app/views/elements/email/text/custom.ctp
    trunk/src/Web/cake/tests/test_app/views/elements/email/text/default.ctp
    trunk/src/Web/cake/tests/test_app/views/elements/email/text/wide.ctp
    trunk/src/Web/cake/tests/test_app/views/elements/empty
    trunk/src/Web/cake/tests/test_app/views/elements/html_call.ctp
    trunk/src/Web/cake/tests/test_app/views/elements/nocache/
    trunk/src/Web/cake/tests/test_app/views/elements/nocache/contains_nocache.ctp
    trunk/src/Web/cake/tests/test_app/views/elements/nocache/plain.ctp
    trunk/src/Web/cake/tests/test_app/views/elements/nocache/sub1.ctp
    trunk/src/Web/cake/tests/test_app/views/elements/nocache/sub2.ctp
    trunk/src/Web/cake/tests/test_app/views/elements/session_helper.ctp
    trunk/src/Web/cake/tests/test_app/views/elements/test_element.ctp
    trunk/src/Web/cake/tests/test_app/views/elements/test_element.xml
    trunk/src/Web/cake/tests/test_app/views/elements/type_check.ctp
    trunk/src/Web/cake/tests/test_app/views/errors/
    trunk/src/Web/cake/tests/test_app/views/errors/empty
    trunk/src/Web/cake/tests/test_app/views/helpers/
    trunk/src/Web/cake/tests/test_app/views/helpers/banana.php
    trunk/src/Web/cake/tests/test_app/views/helpers/empty
    trunk/src/Web/cake/tests/test_app/views/layouts/
    trunk/src/Web/cake/tests/test_app/views/layouts/ajax.ctp
    trunk/src/Web/cake/tests/test_app/views/layouts/ajax2.ctp
    trunk/src/Web/cake/tests/test_app/views/layouts/cache_empty_sections.ctp
    trunk/src/Web/cake/tests/test_app/views/layouts/cache_layout.ctp
    trunk/src/Web/cake/tests/test_app/views/layouts/default.ctp
    trunk/src/Web/cake/tests/test_app/views/layouts/email/
    trunk/src/Web/cake/tests/test_app/views/layouts/email/html/
    trunk/src/Web/cake/tests/test_app/views/layouts/email/html/default.ctp
    trunk/src/Web/cake/tests/test_app/views/layouts/email/html/thin.ctp
    trunk/src/Web/cake/tests/test_app/views/layouts/email/text/
    trunk/src/Web/cake/tests/test_app/views/layouts/email/text/default.ctp
    trunk/src/Web/cake/tests/test_app/views/layouts/flash.ctp
    trunk/src/Web/cake/tests/test_app/views/layouts/js/
    trunk/src/Web/cake/tests/test_app/views/layouts/js/default.ctp
    trunk/src/Web/cake/tests/test_app/views/layouts/multi_cache.ctp
    trunk/src/Web/cake/tests/test_app/views/layouts/rss/
    trunk/src/Web/cake/tests/test_app/views/layouts/rss/default.ctp
    trunk/src/Web/cake/tests/test_app/views/layouts/xml/
    trunk/src/Web/cake/tests/test_app/views/layouts/xml/default.ctp
    trunk/src/Web/cake/tests/test_app/views/pages/
    trunk/src/Web/cake/tests/test_app/views/pages/empty
    trunk/src/Web/cake/tests/test_app/views/pages/extract.ctp
    trunk/src/Web/cake/tests/test_app/views/pages/home.ctp
    trunk/src/Web/cake/tests/test_app/views/posts/
    trunk/src/Web/cake/tests/test_app/views/posts/cache_empty_sections.ctp
    trunk/src/Web/cake/tests/test_app/views/posts/cache_form.ctp
    trunk/src/Web/cake/tests/test_app/views/posts/helper_overwrite.ctp
    trunk/src/Web/cake/tests/test_app/views/posts/index.ctp
    trunk/src/Web/cake/tests/test_app/views/posts/multiple_nocache.ctp
    trunk/src/Web/cake/tests/test_app/views/posts/nocache_multiple_element.ctp
    trunk/src/Web/cake/tests/test_app/views/posts/scaffold.edit.ctp
    trunk/src/Web/cake/tests/test_app/views/posts/sequencial_nocache.ctp
    trunk/src/Web/cake/tests/test_app/views/posts/test_nocache_tags.ctp
    trunk/src/Web/cake/tests/test_app/views/scaffolds/
    trunk/src/Web/cake/tests/test_app/views/scaffolds/empty
    trunk/src/Web/cake/tests/test_app/views/tests_apps/
    trunk/src/Web/cake/tests/test_app/views/tests_apps/index.ctp
    trunk/src/Web/cake/tests/test_app/views/themed/
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/elements/
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/elements/test_element.ctp
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/layouts/
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/layouts/default.ctp
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/plugins/
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/plugins/test_plugin/
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/plugins/test_plugin/layouts/
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/plugins/test_plugin/layouts/plugin_default.ctp
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/plugins/test_plugin/tests/
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/plugins/test_plugin/tests/index.ctp
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/posts/
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/posts/index.ctp
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/posts/scaffold.index.ctp
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/css/
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/css/test_asset.css
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/css/theme_webroot.css
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/flash/
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/flash/theme_test.swf
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/img/
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/img/cake.power.gif
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/img/test.jpg
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/js/
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/js/one/
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/js/one/theme_one.js
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/js/theme.js
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/pdfs/
    trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/pdfs/theme_test.pdf
    trunk/src/Web/cake/tests/test_app/webroot/
    trunk/src/Web/cake/tests/test_app/webroot/theme/
    trunk/src/Web/cake/tests/test_app/webroot/theme/test_theme/
    trunk/src/Web/cake/tests/test_app/webroot/theme/test_theme/css/
    trunk/src/Web/cake/tests/test_app/webroot/theme/test_theme/css/theme_webroot.css
    trunk/src/Web/cake/tests/test_app/webroot/theme/test_theme/css/webroot_test.css
    trunk/src/Web/cake/tests/test_app/webroot/theme/test_theme/img/
    trunk/src/Web/cake/tests/test_app/webroot/theme/test_theme/img/cake.power.gif
    trunk/src/Web/cake/tests/test_app/webroot/theme/test_theme/img/test.jpg
    trunk/src/Web/index.php
    trunk/src/Web/plugins/
    trunk/src/Web/plugins/empty
    trunk/src/Web/vendors/
    trunk/src/Web/vendors/shells/
    trunk/src/Web/vendors/shells/tasks/
    trunk/src/Web/vendors/shells/tasks/empty
    trunk/src/Web/vendors/shells/templates/
    trunk/src/Web/vendors/shells/templates/empty

-------------- next part --------------
Added: trunk/src/Web/.gitignore
===================================================================
--- trunk/src/Web/.gitignore	                        (rev 0)
+++ trunk/src/Web/.gitignore	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,4 @@
+/app/config
+/app/tmp
+/plugins
+/vendors
\ No newline at end of file

Added: trunk/src/Web/.htaccess
===================================================================
--- trunk/src/Web/.htaccess	                        (rev 0)
+++ trunk/src/Web/.htaccess	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,5 @@
+<IfModule mod_rewrite.c>
+   RewriteEngine on
+   RewriteRule    ^$ app/webroot/    [L]
+   RewriteRule    (.*) app/webroot/$1 [L]
+</IfModule>
\ No newline at end of file

Modified: trunk/src/Web/.settings/org.eclipse.php.core.prefs
===================================================================
--- trunk/src/Web/.settings/org.eclipse.php.core.prefs	2012-03-31 12:26:27 UTC (rev 77)
+++ trunk/src/Web/.settings/org.eclipse.php.core.prefs	2012-03-31 12:37:43 UTC (rev 78)
@@ -1,3 +1,3 @@
-#Sat Mar 31 21:24:16 JST 2012
+#Sat Mar 31 21:29:07 JST 2012
 eclipse.preferences.version=1
 include_path=0;/SmartUpload-Web

Added: trunk/src/Web/.travis.yml
===================================================================
--- trunk/src/Web/.travis.yml	                        (rev 0)
+++ trunk/src/Web/.travis.yml	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,5 @@
+language: php
+notifications:
+  email:false
+branches:
+  only: master
\ No newline at end of file

Added: trunk/src/Web/README
===================================================================
--- trunk/src/Web/README	                        (rev 0)
+++ trunk/src/Web/README	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,28 @@
+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://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 TV - screen casts from events and video tutorials
+http://tv.cakephp.org
+
+CakePHP Google Group - community mailing list and forum
+http://groups.google.com/group/cake-php
+
+#cakephp on irc.freenode.net - chat with CakePHP developers
+irc://irc.freenode.net/cakephp

Added: trunk/src/Web/app/.htaccess
===================================================================
--- trunk/src/Web/app/.htaccess	                        (rev 0)
+++ trunk/src/Web/app/.htaccess	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,5 @@
+<IfModule mod_rewrite.c>
+    RewriteEngine on
+    RewriteRule    ^$    webroot/    [L]
+    RewriteRule    (.*) webroot/$1    [L]
+ </IfModule>
\ No newline at end of file

Added: trunk/src/Web/app/config/acl.ini.php
===================================================================
--- trunk/src/Web/app/config/acl.ini.php	                        (rev 0)
+++ trunk/src/Web/app/config/acl.ini.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,70 @@
+;<?php die() ?>
+; SVN FILE: $Id$
+;/**
+; * ACL configuration
+; *
+; *
+; * PHP versions 4 and 5
+; *
+; * CakePHP(tm) :  Rapid Development Framework http://cakephp.org
+; * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+; *
+; *  Licensed under The MIT License
+; *  Redistributions of files must retain the above copyright notice.
+; *
+; * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+; * @link          http://cakephp.org CakePHP(tm) Project
+; * @package       cake
+; * @subpackage    cake.app.config
+; * @since         CakePHP(tm) v 0.10.0.1076
+; * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+; */
+
+; 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

Added: trunk/src/Web/app/config/bootstrap.php
===================================================================
--- trunk/src/Web/app/config/bootstrap.php	                        (rev 0)
+++ trunk/src/Web/app/config/bootstrap.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,50 @@
+<?php
+/**
+ * This file is loaded automatically by the app/webroot/index.php file after the core bootstrap.php
+ *
+ * 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.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config
+ * @since         CakePHP(tm) v 0.10.8.2117
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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)
+ *
+ * App::build(array(
+ *     'plugins' => array('/full/path/to/plugins/', '/next/full/path/to/plugins/'),
+ *     'models' =>  array('/full/path/to/models/', '/next/full/path/to/models/'),
+ *     'views' => array('/full/path/to/views/', '/next/full/path/to/views/'),
+ *     'controllers' => array('/full/path/to/controllers/', '/next/full/path/to/controllers/'),
+ *     'datasources' => array('/full/path/to/datasources/', '/next/full/path/to/datasources/'),
+ *     'behaviors' => array('/full/path/to/behaviors/', '/next/full/path/to/behaviors/'),
+ *     'components' => array('/full/path/to/components/', '/next/full/path/to/components/'),
+ *     'helpers' => array('/full/path/to/helpers/', '/next/full/path/to/helpers/'),
+ *     'vendors' => array('/full/path/to/vendors/', '/next/full/path/to/vendors/'),
+ *     'shells' => array('/full/path/to/shells/', '/next/full/path/to/shells/'),
+ *     'locales' => array('/full/path/to/locale/', '/next/full/path/to/locale/')
+ * ));
+ *
+ */
+
+/**
+ * As of 1.3, additional rules for the inflector are added below
+ *
+ * Inflector::rules('singular', array('rules' => array(), 'irregular' => array(), 'uninflected' => array()));
+ * Inflector::rules('plural', array('rules' => array(), 'irregular' => array(), 'uninflected' => array()));
+ *
+ */

Added: trunk/src/Web/app/config/core.php
===================================================================
--- trunk/src/Web/app/config/core.php	                        (rev 0)
+++ trunk/src/Web/app/config/core.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,304 @@
+<?php
+/**
+ * This is core configuration file.
+ *
+ * Use it to configure core behavior of Cake.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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.
+ *
+ * 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);
+
+/**
+ * CakePHP Log Level:
+ *
+ * In case of Production Mode CakePHP gives you the possibility to continue logging errors.
+ *
+ * The following parameters can be used:
+ *  Boolean: Set true/false to activate/deactivate logging
+ *    Configure::write('log', true);
+ *
+ *  Integer: Use built-in PHP constants to set the error level (see error_reporting)
+ *    Configure::write('log', E_ERROR | E_WARNING);
+ *    Configure::write('log', E_ALL ^ E_NOTICE);
+ */
+	Configure::write('log', true);
+
+/**
+ * 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 prefix routes.
+ *
+ * The value of the define determines the names of the routes
+ * and their associated controller actions:
+ *
+ * Set to an array of prefixes you want to use in your application. Use for
+ * admin or other prefixed routes.
+ *
+ * 	Routing.prefixes = array('admin', 'manager');
+ *
+ * Enables:
+ *	`admin_index()` and `/admin/controller/index`
+ *	`manager_index()` and `/manager/controller/index`
+ *
+ * [Note Routing.admin is deprecated in 1.3.  Use Routing.prefixes instead]
+ */
+	//Configure::write('Routing.prefixes', array('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, run the app/config/schema/sessions.php schema using
+ * the cake shell command: cake schema create Sessions
+ *
+ */
+	Configure::write('Session.save', 'php');
+
+/**
+ * The model name to be used for the session model.
+ *
+ * 'Session.save' must be set to 'database' in order to utilize this constant.
+ *
+ * The model name set here should *not* be used elsewhere in your application.
+ */
+	//Configure::write('Session.model', 'Session');
+
+/**
+ * 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.
+ *
+ * Please note that if you set a value for Session.model (above), any value set for
+ * Session.table will be ignored.
+ *
+ * [Note: Session.table is deprecated as of CakePHP 1.3]
+ */
+	//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.
+ *
+ * Note the guidelines for Session names states: "The session name references
+ * the session id in cookies and URLs. It should contain only alphanumeric
+ * characters."
+ * @link http://php.net/session_name
+ */
+	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. You might want to set the value to false, when dealing with
+ * older versions of IE, Chrome Frame or certain web-browsing devices and AJAX
+ */
+	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', 'medium');
+
+/**
+ * A random string used in security hashing methods.
+ */
+	Configure::write('Security.salt', 'DYhG93b0qyJfIxfs2guVoUubWwvniR2G0FgaC9mi');
+
+/**
+ * A random numeric string (digits only) used to encrypt/decrypt strings.
+ */
+	Configure::write('Security.cipherSeed', '76859309657453542496749683645');
+
+/**
+ * Apply timestamps with the last modified time to static assets (js, css, images).
+ * Will append a querystring parameter containing the time the file was modified. This is
+ * useful for invalidating browser caches.
+ *
+ * Set to `true` to apply timestamps when debug > 0. Set to 'force' to always enable
+ * timestamping regardless of debug value.
+ */
+	//Configure::write('Asset.timestamp', true);
+/**
+ * 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');
+
+/**
+ * If you are on PHP 5.3 uncomment this line and correct your server timezone
+ * to fix the date & time related errors.
+ */
+	//date_default_timezone_set('UTC');
+
+/**
+ *
+ * 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)
+ * 		'persistent' => true, // [optional] set this to false for non-persistent connections
+ *	));
+ *
+ */
+	Cache::config('default', array('engine' => 'File'));

Added: trunk/src/Web/app/config/database.php.default
===================================================================
--- trunk/src/Web/app/config/database.php.default	                        (rev 0)
+++ trunk/src/Web/app/config/database.php.default	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,97 @@
+<?php
+/**
+ * This is core configuration file.
+ *
+ * Use it to configure core behaviour ofCake.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+/**
+ * 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' => '',
+		//'encoding' => 'utf8',
+	);
+
+	var $test = array(
+		'driver' => 'mysql',
+		'persistent' => false,
+		'host' => 'localhost',
+		'login' => 'user',
+		'password' => 'password',
+		'database' => 'test_database_name',
+		'prefix' => '',
+		//'encoding' => 'utf8',
+	);
+}

Added: trunk/src/Web/app/config/routes.php
===================================================================
--- trunk/src/Web/app/config/routes.php	                        (rev 0)
+++ trunk/src/Web/app/config/routes.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Routes configuration
+ *
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+/**
+ * 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'));

Added: trunk/src/Web/app/config/schema/db_acl.php
===================================================================
--- trunk/src/Web/app/config/schema/db_acl.php	                        (rev 0)
+++ trunk/src/Web/app/config/schema/db_acl.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,73 @@
+<?php
+/*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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config.sql
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+/*
+ *
+ * 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))
+		);
+
+}

Added: trunk/src/Web/app/config/schema/i18n.php
===================================================================
--- trunk/src/Web/app/config/schema/i18n.php	                        (rev 0)
+++ trunk/src/Web/app/config/schema/i18n.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,50 @@
+<?php
+/*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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config.sql
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+/*
+ *
+ * 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))
+		);
+
+}

Added: trunk/src/Web/app/config/schema/sessions.php
===================================================================
--- trunk/src/Web/app/config/schema/sessions.php	                        (rev 0)
+++ trunk/src/Web/app/config/schema/sessions.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,47 @@
+<?php
+/*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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config.sql
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+/*
+ *
+ * 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))
+		);
+
+}

Added: trunk/src/Web/app/controllers/components/empty
===================================================================
Added: trunk/src/Web/app/index.php
===================================================================
--- trunk/src/Web/app/index.php	                        (rev 0)
+++ trunk/src/Web/app/index.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,18 @@
+<?php
+/**
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+require 'webroot' . DIRECTORY_SEPARATOR . 'index.php';

Added: trunk/src/Web/app/libs/empty
===================================================================
Added: trunk/src/Web/app/locale/eng/LC_MESSAGES/empty
===================================================================
Added: trunk/src/Web/app/models/behaviors/empty
===================================================================
Added: trunk/src/Web/app/models/datasources/empty
===================================================================
Added: trunk/src/Web/app/plugins/empty
===================================================================
Added: trunk/src/Web/app/tests/cases/behaviors/empty
===================================================================
Added: trunk/src/Web/app/tests/cases/components/empty
===================================================================
Added: trunk/src/Web/app/tests/cases/controllers/empty
===================================================================
Added: trunk/src/Web/app/tests/cases/helpers/empty
===================================================================
Added: trunk/src/Web/app/tests/cases/models/empty
===================================================================
Added: trunk/src/Web/app/tests/fixtures/empty
===================================================================
Added: trunk/src/Web/app/tests/groups/empty
===================================================================
Added: trunk/src/Web/app/tmp/cache/models/empty
===================================================================
Added: trunk/src/Web/app/tmp/cache/persistent/empty
===================================================================
Added: trunk/src/Web/app/tmp/cache/views/empty
===================================================================
Added: trunk/src/Web/app/tmp/logs/empty
===================================================================
Added: trunk/src/Web/app/tmp/sessions/empty
===================================================================
Added: trunk/src/Web/app/tmp/tests/empty
===================================================================
Added: trunk/src/Web/app/vendors/shells/tasks/empty
===================================================================
Added: trunk/src/Web/app/vendors/shells/templates/empty
===================================================================
Added: trunk/src/Web/app/views/elements/email/html/empty
===================================================================
Added: trunk/src/Web/app/views/elements/email/text/empty
===================================================================
Added: trunk/src/Web/app/views/elements/empty
===================================================================
Added: trunk/src/Web/app/views/errors/empty
===================================================================
Added: trunk/src/Web/app/views/helpers/empty
===================================================================
Added: trunk/src/Web/app/views/layouts/email/html/empty
===================================================================
Added: trunk/src/Web/app/views/layouts/email/text/empty
===================================================================
Added: trunk/src/Web/app/views/layouts/js/empty
===================================================================
Added: trunk/src/Web/app/views/layouts/rss/empty
===================================================================
Added: trunk/src/Web/app/views/layouts/xml/empty
===================================================================
Added: trunk/src/Web/app/views/pages/empty
===================================================================
Added: trunk/src/Web/app/views/scaffolds/empty
===================================================================
Added: trunk/src/Web/app/webroot/.htaccess
===================================================================
--- trunk/src/Web/app/webroot/.htaccess	                        (rev 0)
+++ trunk/src/Web/app/webroot/.htaccess	2012-03-31 12:37:43 UTC (rev 78)
@@ -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

Added: trunk/src/Web/app/webroot/css/cake.generic.css
===================================================================
--- trunk/src/Web/app/webroot/css/cake.generic.css	                        (rev 0)
+++ trunk/src/Web/app/webroot/css/cake.generic.css	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,541 @@
+/**
+ *
+ * Generic CSS for CakePHP
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.webroot.css
+ * @since         CakePHP(tm)
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+* {
+	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 {
+	color: #003d4c;
+	text-decoration: underline;
+	font-weight: bold;
+}
+a:hover {
+	color: #367889;
+	text-decoration:none;
+}
+a img {
+	border:none;
+}
+h1, h2, h3, h4 {
+	font-weight: normal;
+	margin-bottom:0.5em;
+}
+h1 {
+	background:#fff;
+	color: #003d4c;
+	font-size: 100%;
+}
+h2 {
+	background:#fff;
+	color: #e32;
+	font-family:'Gill Sans','lucida grande', helvetica, arial, sans-serif;
+	font-size: 190%;
+}
+h3 {
+	color: #993;
+	font-family:'Gill Sans','lucida grande', helvetica, arial, sans-serif;
+	font-size: 165%;
+}
+h4 {
+	color: #993;
+	font-weight: normal;
+}
+ul, li {
+	margin: 0 12px;
+}
+
+/** Layout **/
+#container {
+	text-align: left;
+}
+
+#header{
+	padding: 10px 20px;
+}
+#header h1 {
+	line-height:20px;
+	background: #003d4c url('../img/cake.icon.png') 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;
+}
+
+/** containers **/
+div.form,
+div.index,
+div.view {
+	float:right;
+	width:76%;
+	border-left:1px solid #666;
+	padding:10px 2%;
+}
+div.actions {
+	float:left;
+	width:16%;
+	padding:10px 1.5%;
+}
+div.actions h3 {
+	padding-top:0;
+	color:#777;
+}
+
+
+/** Tables **/
+table {
+	background: #fff;
+	border-right:0;
+	clear: both;
+	color: #333;
+	margin-bottom: 10px;
+	width: 100%;
+}
+th {
+	border:0;
+	border-bottom:2px solid #555;
+	text-align: left;
+	padding:4px;
+}
+th a {
+	display: block;
+	padding: 2px 4px;
+	text-decoration: none;
+}
+th a.asc:after {
+	content: ' ⇣';
+}
+th a.desc:after {
+	content: ' ⇡';
+}
+table tr td {
+	background: #fff;
+	padding: 6px;
+	text-align: left;
+	vertical-align: top;
+	border-bottom:1px solid #ddd;
+}
+table tr:nth-child(2n) td {
+	background: #f5f5f5;
+}
+table .altrow td {
+	background: #f5f5f5;
+}
+td.actions {
+	text-align: center;
+	white-space: nowrap;
+}
+table td.actions a {
+	margin: 0px 6px;
+	padding:2px 5px;
+}
+.cake-sql-log table {
+	background: #f4f4f4;
+}
+.cake-sql-log td {
+	padding: 4px 8px;
+	text-align: left;
+	font-family: Monaco, Consolas, "Courier New", monospaced;
+}
+.cake-sql-log caption {
+	color:#fff;
+}
+
+/** Paging **/
+div.paging {
+	background:#fff;
+	color: #ccc;
+	margin-top: 1em;
+	clear:both;
+}
+div.paging span.disabled {
+	color: #ddd;
+	display: inline;
+}
+div.paging span.current {
+	color: #c73e14;
+}
+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;
+	width: 10em;
+}
+dd {
+	margin-left: 10em;
+	margin-top: -2em;
+	vertical-align: top;
+}
+
+/** Forms **/
+form {
+	clear: both;
+	margin-right: 20px;
+	padding: 0;
+	width: 95%;
+}
+fieldset {
+	border: 1px solid #ccc;
+	margin-bottom: 1em;
+	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 .input {
+	color: #444;
+}
+form .required {
+	font-weight: bold;
+}
+form .required label:after {
+	color: #e32;
+	content: '*';
+	display:inline;
+}
+form div.submit {
+	border: 0;
+	clear: both;
+	margin-top: 10px;
+}
+label {
+	display: block;
+	font-size: 110%;
+	margin-bottom:3px;
+}
+input, textarea {
+	clear: both;
+	font-size: 140%;
+	font-family: "frutiger linotype", "lucida grande", "verdana", sans-serif;
+	padding: 1%;
+	width:98%;
+}
+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;
+}
+div.checkbox label {
+	display: inline;
+}
+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%;
+	width: auto;
+}
+form .submit input[type=submit] {
+	background:#62af56;
+	background: -webkit-gradient(linear, left top, left bottom, from(#a8ea9c), to(#62af56));
+	background-image: -moz-linear-gradient(top, #a8ea9c, #62af56);
+	border-color: #2d6324;
+	color: #000;
+	text-shadow: #8cee7c 0px 1px 0px;
+}
+form .submit input[type=submit]:hover {
+	background:#4ca83d;
+	background: -webkit-gradient(linear, left top, left bottom, from(#85e573), to(#4ca83d));
+	background-image: -moz-linear-gradient(top, #85e573, #4ca83d);
+}
+
+/** Notices and Errors **/
+div.message {
+	clear: both;
+	color: #fff;
+	font-size: 140%;
+	font-weight: bold;
+	margin: 0 0 1em 0;
+	background: #c73e14;
+	padding: 5px;
+}
+div.error-message {
+	clear: both;
+	color: #fff;
+	font-weight: bold;
+	background: #c73e14;
+}
+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: 0;
+	padding: 0;
+}
+div.actions li {
+	margin:0 0 0.5em 0;
+	list-style-type: none;
+	white-space: nowrap;
+	padding: 0;
+}
+div.actions ul li a {
+	font-weight: normal;
+	display: block;
+	clear: both;
+}
+div.actions ul li a:hover {
+	text-decoration: underline;
+}
+
+input[type=submit],
+div.actions ul li a,
+td.actions a {
+	font-weight:normal;
+	padding: 4px 8px;
+	background:#e6e49f;
+	background: -webkit-gradient(linear, left top, left bottom, from(#f1f1d4), to(#e6e49f));
+	background-image: -moz-linear-gradient(top, #f1f1d4, #e6e49f);
+	color:#333;
+	border:1px solid #aaac62;
+	-webkit-border-radius:8px;
+	-moz-border-radius:8px;
+	border-radius:8px;
+	text-decoration:none;
+	text-shadow: #fff 0px 1px 0px;
+	min-width: 0;
+}
+input[type=submit]:hover,
+div.actions ul li a:hover,
+td.actions a:hover {
+	background: #f0f09a;
+	background: -webkit-gradient(linear, left top, left bottom, from(#f7f7e1), to(#eeeca9));
+}
+
+/** 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;
+	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;
+}
+
+/** Elements **/
+#url-rewriting-warning {
+	display: none;
+}


Property changes on: trunk/src/Web/app/webroot/css/cake.generic.css
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Added: trunk/src/Web/app/webroot/css.php
===================================================================
--- trunk/src/Web/app/webroot/css.php	                        (rev 0)
+++ trunk/src/Web/app/webroot/css.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,96 @@
+<?php
+/**
+ * CSS helping functions
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.webroot
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+if (!defined('CAKE_CORE_INCLUDE_PATH')) {
+	header('HTTP/1.1 404 Not Found');
+	exit('File Not Found');
+}
+
+/**
+ * Ensure required classes are available.
+ */
+if (!class_exists('File')) {
+	uses('file');
+}
+
+/**
+ * Make clean CSS
+ *
+ * @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;
+	}
+/**
+ * Write CSS cache
+ *
+ * @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, d 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;

Added: trunk/src/Web/app/webroot/favicon.ico
===================================================================
(Binary files differ)


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

Added: trunk/src/Web/app/webroot/files/empty
===================================================================
Added: trunk/src/Web/app/webroot/img/cake.icon.png
===================================================================
(Binary files differ)


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

Added: trunk/src/Web/app/webroot/img/cake.power.gif
===================================================================
(Binary files differ)


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

Added: trunk/src/Web/app/webroot/img/test-error-icon.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/app/webroot/img/test-error-icon.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/app/webroot/img/test-fail-icon.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/app/webroot/img/test-fail-icon.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/app/webroot/img/test-pass-icon.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/app/webroot/img/test-pass-icon.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/app/webroot/img/test-skip-icon.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/app/webroot/img/test-skip-icon.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/app/webroot/index.php
===================================================================
--- trunk/src/Web/app/webroot/index.php	                        (rev 0)
+++ trunk/src/Web/app/webroot/index.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,87 @@
+<?php
+/**
+ * Index
+ *
+ * The Front Controller for handling every request
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.webroot
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+/**
+ * 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 (php_sapi_name() == 'cli-server') {
+		$_SERVER['PHP_SELF'] = '/'.basename(__FILE__);
+	}
+	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();
+	}

Added: trunk/src/Web/app/webroot/js/empty
===================================================================
Added: trunk/src/Web/app/webroot/test.php
===================================================================
--- trunk/src/Web/app/webroot/test.php	                        (rev 0)
+++ trunk/src/Web/app/webroot/test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,94 @@
+<?php
+/**
+ * Web Access Frontend for TestSuite
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing
+ * @package       cake
+ * @subpackage    cake.app.webroot
+ * @since         CakePHP(tm) v 1.2.0.4433
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+set_time_limit(0);
+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 = App::core('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);
+}
+
+if (Configure::read('debug') < 1) {
+	die(__('Debug setting does not allow access to this url.', true));
+}
+
+require_once CAKE_TESTS_LIB . 'cake_test_suite_dispatcher.php';
+
+$Dispatcher = new CakeTestSuiteDispatcher();
+$Dispatcher->dispatch();

Added: trunk/src/Web/cake/LICENSE.txt
===================================================================
--- trunk/src/Web/cake/LICENSE.txt	                        (rev 0)
+++ trunk/src/Web/cake/LICENSE.txt	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,22 @@
+The MIT License
+
+CakePHP(tm) : The Rapid Development PHP Framework (http://cakephp.org)
+Copyright 2005-2012, 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


Property changes on: trunk/src/Web/cake/LICENSE.txt
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Added: trunk/src/Web/cake/VERSION.txt
===================================================================
--- trunk/src/Web/cake/VERSION.txt	                        (rev 0)
+++ trunk/src/Web/cake/VERSION.txt	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,28 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// +--------------------------------------------------------------------------------------------+ //
+// CakePHP Version
+//
+// Holds a static string representing the current version of CakePHP
+//
+// CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+// Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+//
+// Licensed under The MIT License
+// Redistributions of files must retain the above copyright notice.
+//
+// @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+// @link          http://cakephp.org
+// @package       cake
+// @subpackage    cake.cake.libs
+// @since         CakePHP(tm) v 0.2.9
+// @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+// +--------------------------------------------------------------------------------------------+ //
+////////////////////////////////////////////////////////////////////////////////////////////////////
+1.3.14
+
+
+
+
+
+
+


Property changes on: trunk/src/Web/cake/VERSION.txt
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Added: trunk/src/Web/cake/basics.php
===================================================================
--- trunk/src/Web/cake/basics.php	                        (rev 0)
+++ trunk/src/Web/cake/basics.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,1046 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Basic defines for timing functions.
+ */
+	define('SECOND', 1);
+	define('MINUTE', 60);
+	define('HOUR', 3600);
+	define('DAY', 86400);
+	define('WEEK', 604800);
+	define('MONTH', 2592000);
+	define('YEAR', 31536000);
+
+/**
+ * Patch old versions of PHP4.
+ */
+if (!defined('PHP_EOL')) {
+	switch (strtoupper(substr(PHP_OS, 0, 3))) {
+		case 'WIN':
+			define('PHP_EOL', "\r\n");
+			break;
+		default:
+			define('PHP_EOL', "\n");
+	}
+}
+
+/**
+ * Patch PHP4 and PHP5.0
+ */
+if (!defined('DATE_RFC2822')) {
+	define('DATE_RFC2822', 'D, d M Y H:i:s O');
+}
+
+/**
+ * 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:
+ *
+ * `config('config1', 'config2');`
+ *
+ * @return boolean Success
+ * @link http://book.cakephp.org/view/1125/config
+ */
+	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:
+ *
+ * `uses('flay', 'time');`
+ *
+ * @param string $name Filename without the .php part
+ * @deprecated Will be removed in 2.0
+ * @link http://book.cakephp.org/view/1140/uses
+ */
+	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/1190/Basic-Debugging
+ * @link http://book.cakephp.org/view/1128/debug
+ */
+	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.
+ * @deprecated Will be removed in 2.0
+ */
+	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/1132/h
+ */
+	function h($text, $charset = null) {
+		if (is_array($text)) {
+			return array_map('h', $text);
+		}
+
+		static $defaultCharset = false;
+		if ($defaultCharset === false) {
+			$defaultCharset = Configure::read('App.encoding');
+			if ($defaultCharset === null) {
+				$defaultCharset = 'UTF-8';
+			}
+		}
+		if ($charset) {
+			return htmlspecialchars($text, ENT_QUOTES, $charset);
+		} else {
+			return htmlspecialchars($text, ENT_QUOTES, $defaultCharset);
+		}
+	}
+
+/**
+ * Splits a dot syntax plugin name into its plugin and classname.
+ * If $name does not have a dot, then index 0 will be null.
+ *
+ * Commonly used like `list($plugin, $name) = pluginSplit($name);`
+ *
+ * @param string $name The name you want to plugin split.
+ * @param boolean $dotAppend Set to true if you want the plugin to have a '.' appended to it.
+ * @param string $plugin Optional default plugin to use if no plugin is found. Defaults to null.
+ * @return array Array with 2 indexes.  0 => plugin name, 1 => classname
+ */
+	function pluginSplit($name, $dotAppend = false, $plugin = null) {
+		if (strpos($name, '.') !== false) {
+			$parts = explode('.', $name, 2);
+			if ($dotAppend) {
+				$parts[0] .= '.';
+			}
+			return $parts;
+		}
+		return array($plugin, $name);
+	}
+
+/**
+ * Returns an array of all the given parameters.
+ *
+ * Example:
+ *
+ * `a('a', 'b')`
+ *
+ * Would return:
+ *
+ * `array('a', 'b')`
+ *
+ * @return array Array of given parameters
+ * @link http://book.cakephp.org/view/1122/a
+ * @deprecated Will be removed in 2.0
+ */
+	function a() {
+		$args = func_get_args();
+		return $args;
+	}
+
+/**
+ * Constructs associative array from pairs of arguments.
+ *
+ * Example:
+ *
+ * `aa('a','b')`
+ *
+ * Would return:
+ *
+ * `array('a'=>'b')`
+ *
+ * @return array Associative array
+ * @link http://book.cakephp.org/view/1123/aa
+ * @deprecated Will be removed in 2.0
+ */
+	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/1129/e
+ * @deprecated Will be removed in 2.0
+ */
+	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/1134/low
+ * @deprecated Will be removed in 2.0
+ */
+	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/1139/up
+ * @deprecated Will be removed in 2.0
+ */
+	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/1137/r
+ * @deprecated Will be removed in 2.0
+ */
+	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
+ * @link http://book.cakephp.org/view/1136/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
+ * @deprecated Will be removed in 2.0
+ */
+	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/1124/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/1130/env
+ */
+	function env($key) {
+		if ($key == 'HTTPS') {
+			if (isset($_SERVER['HTTPS'])) {
+				return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off');
+			}
+			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');
+				$parts = explode('.', $host);
+				$count = count($parts);
+
+				if ($count === 1) {
+					return '.' . $host;
+				} elseif ($count === 2) {
+					return '.' . $host;
+				} elseif ($count === 3) {
+					$gTLD = array('aero', 'asia', 'biz', 'cat', 'com', 'coop', 'edu', 'gov', 'info', 'int', 'jobs', 'mil', 'mobi', 'museum', 'name', 'net', 'org', 'pro', 'tel', 'travel', 'xxx');
+					if (in_array($parts[1], $gTLD)) {
+						return '.' . $host;
+					}
+				}
+				array_shift($parts);
+				return '.' . implode('.', $parts);
+				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 implode()ed with an empty string.
+ *
+ * @param string $fileName File name.
+ * @param mixed  $data String or array.
+ * @return boolean Success
+ * @deprecated Will be removed in 2.0
+ */
+	function file_put_contents($fileName, $data) {
+		if (is_array($data)) {
+			$data = implode('', $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 (strtolower($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) && strrpos($file, DS . 'empty') !== strlen($file) - 6) {
+						@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) && strrpos($file, DS . 'empty') !== strlen($file) - 6) {
+						@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/1138/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/1121/__
+ */
+	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, 6, $count);
+		} else {
+			return I18n::translate($singular, $plural, null, 6, $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, 6, $count);
+		} else {
+			return I18n::translate($singular, $plural, $domain, 6, $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_ALL       0
+ * - LC_COLLATE   1
+ * - LC_CTYPE     2
+ * - LC_MONETARY  3
+ * - LC_NUMERIC   4
+ * - LC_TIME      5
+ * - LC_MESSAGES  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_ALL       0
+ * - LC_COLLATE   1
+ * - LC_CTYPE     2
+ * - LC_MONETARY  3
+ * - LC_NUMERIC   4
+ * - LC_TIME      5
+ * - LC_MESSAGES  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_ALL       0
+ * - LC_COLLATE   1
+ * - LC_CTYPE     2
+ * - LC_MONETARY  3
+ * - LC_NUMERIC   4
+ * - LC_TIME      5
+ * - LC_MESSAGES  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
+ * @deprecated Will be removed in 2.0
+ */
+	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 (array_key_exists($valueKey, $args[$i])) {
+						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
+ * @deprecated Will be removed in 2.0
+ */
+	if (!function_exists('array_intersect_key')) {
+		function array_intersect_key($arr1, $arr2) {
+			$res = array();
+			foreach ($arr1 as $key => $value) {
+				if (array_key_exists($key, $arr2)) {
+					$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/1131/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/1126/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
+ * @deprecated Will be removed in 2.0
+ */
+	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:
+ *
+ * `ife(isset($variable), @$variable, 'default');`
+ *
+ * @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/1133/ife
+ * @deprecated Will be removed in 2.0
+ */
+	function ife($condition, $val1 = null, $val2 = null) {
+		if (!empty($condition)) {
+			return $val1;
+		}
+		return $val2;
+	}

Added: trunk/src/Web/cake/bootstrap.php
===================================================================
--- trunk/src/Web/cake/bootstrap.php	                        (rev 0)
+++ trunk/src/Web/cake/bootstrap.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Basic Cake functionality.
+ *
+ * Handles loading of core files needed on every request
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+if (!defined('PHP5')) {
+	define('PHP5', (PHP_VERSION >= 5));
+}
+if (!defined('E_DEPRECATED')) {
+	define('E_DEPRECATED', 8192);
+}
+error_reporting(E_ALL & ~E_DEPRECATED & ~E_STRICT);
+
+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 . 'set.php';
+require LIBS . 'cache.php';
+Configure::getInstance();
+require CAKE . 'dispatcher.php';

Added: trunk/src/Web/cake/config/config.php
===================================================================
--- trunk/src/Web/cake/config/config.php	                        (rev 0)
+++ trunk/src/Web/cake/config/config.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,21 @@
+<?php
+/**
+ * Core Configurations.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config
+ * @since         CakePHP(tm) v 1.1.11.4062
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+$config['Cake.version'] = '1.3.14';
+return $config;

Added: trunk/src/Web/cake/config/paths.php
===================================================================
--- trunk/src/Web/cake/config/paths.php	                        (rev 0)
+++ trunk/src/Web/cake/config/paths.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,231 @@
+<?php
+/**
+ * Path configuration
+ *
+ * In this file you set paths to different directories used by Cake.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.app.config
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 libs directory.
+ */
+	define('APPLIBS', APP.'libs'.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('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);
+}
+
+/**
+ *  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/');
+}

Added: trunk/src/Web/cake/config/unicode/casefolding/0080_00ff.php
===================================================================
--- trunk/src/Web/cake/config/unicode/casefolding/0080_00ff.php	                        (rev 0)
+++ trunk/src/Web/cake/config/unicode/casefolding/0080_00ff.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,74 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 */

Added: trunk/src/Web/cake/config/unicode/casefolding/0100_017f.php
===================================================================
--- trunk/src/Web/cake/config/unicode/casefolding/0100_017f.php	                        (rev 0)
+++ trunk/src/Web/cake/config/unicode/casefolding/0100_017f.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,107 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 */

Added: trunk/src/Web/cake/config/unicode/casefolding/0180_024F.php
===================================================================
--- trunk/src/Web/cake/config/unicode/casefolding/0180_024F.php	                        (rev 0)
+++ trunk/src/Web/cake/config/unicode/casefolding/0180_024F.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,149 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 */

Added: trunk/src/Web/cake/config/unicode/casefolding/0250_02af.php
===================================================================
--- trunk/src/Web/cake/config/unicode/casefolding/0250_02af.php	                        (rev 0)
+++ trunk/src/Web/cake/config/unicode/casefolding/0250_02af.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,42 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.6833
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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));

Added: trunk/src/Web/cake/config/unicode/casefolding/0370_03ff.php
===================================================================
--- trunk/src/Web/cake/config/unicode/casefolding/0370_03ff.php	                        (rev 0)
+++ trunk/src/Web/cake/config/unicode/casefolding/0370_03ff.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,103 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 */

Added: trunk/src/Web/cake/config/unicode/casefolding/0400_04ff.php
===================================================================
--- trunk/src/Web/cake/config/unicode/casefolding/0400_04ff.php	                        (rev 0)
+++ trunk/src/Web/cake/config/unicode/casefolding/0400_04ff.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,165 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 */

Added: trunk/src/Web/cake/config/unicode/casefolding/0500_052f.php
===================================================================
--- trunk/src/Web/cake/config/unicode/casefolding/0500_052f.php	                        (rev 0)
+++ trunk/src/Web/cake/config/unicode/casefolding/0500_052f.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,49 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 */

Added: trunk/src/Web/cake/config/unicode/casefolding/0530_058f.php
===================================================================
--- trunk/src/Web/cake/config/unicode/casefolding/0530_058f.php	                        (rev 0)
+++ trunk/src/Web/cake/config/unicode/casefolding/0530_058f.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,79 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 */

Added: trunk/src/Web/cake/config/unicode/casefolding/1e00_1eff.php
===================================================================
--- trunk/src/Web/cake/config/unicode/casefolding/1e00_1eff.php	                        (rev 0)
+++ trunk/src/Web/cake/config/unicode/casefolding/1e00_1eff.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,169 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 */

Added: trunk/src/Web/cake/config/unicode/casefolding/1f00_1fff.php
===================================================================
--- trunk/src/Web/cake/config/unicode/casefolding/1f00_1fff.php	                        (rev 0)
+++ trunk/src/Web/cake/config/unicode/casefolding/1f00_1fff.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,217 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 */

Added: trunk/src/Web/cake/config/unicode/casefolding/2100_214f.php
===================================================================
--- trunk/src/Web/cake/config/unicode/casefolding/2100_214f.php	                        (rev 0)
+++ trunk/src/Web/cake/config/unicode/casefolding/2100_214f.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,45 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 */

Added: trunk/src/Web/cake/config/unicode/casefolding/2150_218f.php
===================================================================
--- trunk/src/Web/cake/config/unicode/casefolding/2150_218f.php	                        (rev 0)
+++ trunk/src/Web/cake/config/unicode/casefolding/2150_218f.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,58 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 */

Added: trunk/src/Web/cake/config/unicode/casefolding/2460_24ff.php
===================================================================
--- trunk/src/Web/cake/config/unicode/casefolding/2460_24ff.php	                        (rev 0)
+++ trunk/src/Web/cake/config/unicode/casefolding/2460_24ff.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,67 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 */

Added: trunk/src/Web/cake/config/unicode/casefolding/2c00_2c5f.php
===================================================================
--- trunk/src/Web/cake/config/unicode/casefolding/2c00_2c5f.php	                        (rev 0)
+++ trunk/src/Web/cake/config/unicode/casefolding/2c00_2c5f.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,88 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 */

Added: trunk/src/Web/cake/config/unicode/casefolding/2c60_2c7f.php
===================================================================
--- trunk/src/Web/cake/config/unicode/casefolding/2c60_2c7f.php	                        (rev 0)
+++ trunk/src/Web/cake/config/unicode/casefolding/2c60_2c7f.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,49 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 */

Added: trunk/src/Web/cake/config/unicode/casefolding/2c80_2cff.php
===================================================================
--- trunk/src/Web/cake/config/unicode/casefolding/2c80_2cff.php	                        (rev 0)
+++ trunk/src/Web/cake/config/unicode/casefolding/2c80_2cff.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,91 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 */

Added: trunk/src/Web/cake/config/unicode/casefolding/ff00_ffef.php
===================================================================
--- trunk/src/Web/cake/config/unicode/casefolding/ff00_ffef.php	                        (rev 0)
+++ trunk/src/Web/cake/config/unicode/casefolding/ff00_ffef.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,67 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.config.unicode.casefolding
+ * @since         CakePHP(tm) v 1.2.0.5691
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 */

Added: trunk/src/Web/cake/console/cake
===================================================================
--- trunk/src/Web/cake/console/cake	                        (rev 0)
+++ trunk/src/Web/cake/console/cake	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,34 @@
+#!/bin/bash
+################################################################################
+#
+# Bake is a shell script for running CakePHP bake script
+# PHP versions 4 and 5
+#
+# CakePHP(tm) :  Rapid Development Framework (http://cakephp.org)
+# Copyright 2005-2012, Cake Software Foundation, Inc.
+#
+# Licensed under The MIT License
+# Redistributions of files must retain the above copyright notice.
+#
+# @copyright            Copyright 2005-2012, Cake Software Foundation, Inc.
+# @link                 http://cakephp.org CakePHP(tm) Project
+# @package              Cake
+# @subpackage           Cake.Console
+# @since                CakePHP(tm) v 1.2.0.5012
+# @license              MIT License (http://www.opensource.org/licenses/mit-license.php)
+#
+################################################################################
+LIB=$(cd -P -- "$(dirname -- "$0")" && pwd -P) && LIB=$LIB/$(basename -- "$0")
+
+while [ -h $LIB ]; do
+    DIR=$(dirname -- "$LIB")
+    SYM=$(readlink $LIB)
+    LIB=$(cd $DIR && cd $(dirname -- "$SYM") && pwd)/$(basename -- "$SYM")
+done
+
+LIB=$(dirname -- "$LIB")/
+APP=`pwd`
+
+exec php -q ${LIB}cake.php -working "${APP}" "$@"
+
+exit;
\ No newline at end of file

Added: trunk/src/Web/cake/console/cake.bat
===================================================================
--- trunk/src/Web/cake/console/cake.bat	                        (rev 0)
+++ trunk/src/Web/cake/console/cake.bat	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,33 @@
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::
+:: Bake is a shell script for running CakePHP bake script
+:: PHP versions 4 and 5
+::
+:: CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+:: Copyright 2005-2012, Cake Software Foundation, Inc.
+::
+:: Licensed under The MIT License
+:: Redistributions of files must retain the above copyright notice.
+::
+:: @copyright		Copyright 2005-2012, Cake Software Foundation, Inc.
+:: @link				http://cakephp.org CakePHP(tm) Project
+:: @package			cake
+:: @subpackage		cake.cake.console
+:: @since			CakePHP(tm) v 1.2.0.5012
+:: @license			MIT License (http://www.opensource.org/licenses/mit-license.php)
+::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+
+:: 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.
+
+exit /B %ERRORLEVEL%
\ No newline at end of file

Added: trunk/src/Web/cake/console/cake.php
===================================================================
--- trunk/src/Web/cake/console/cake.php	                        (rev 0)
+++ trunk/src/Web/cake/console/cake.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,666 @@
+#!/usr/bin/php -q
+<?php
+/**
+ * Command-line code generation utility to automate programmer chores.
+ *
+ * Shell dispatcher class
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console
+ * @since         CakePHP(tm) v 1.2.0.5012
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+if (!defined('E_DEPRECATED')) {
+	define('E_DEPRECATED', 8192);
+}
+/**
+ * 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;
+
+/**
+ * Constructor
+ *
+ * The execution of the script is stopped after dispatching the request with
+ * a status code of either 0 or 1 according to the result of the dispatch.
+ *
+ * @param array $args the argv
+ * @return void
+ * @access public
+ */
+	function ShellDispatcher($args = array()) {
+		set_time_limit(0);
+
+		$this->__initConstants();
+		$this->parseParams($args);
+		$this->_initEnvironment();
+		$this->__buildPaths();
+		$this->_stop($this->dispatch() === false ? 1 : 0);
+	}
+
+/**
+ * Defines core configuration.
+ *
+ * @access private
+ */
+	function __initConstants() {
+		if (function_exists('ini_set')) {
+			ini_set('display_errors', '1');
+			ini_set('error_reporting', E_ALL & ~E_DEPRECATED & ~E_STRICT);
+			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();
+		if (!class_exists('Folder')) {
+			require LIBS . 'folder.php';
+		}
+		$plugins = App::objects('plugin', null, false);
+		foreach ((array)$plugins as $plugin) {
+			$pluginPath = App::pluginPath($plugin);
+			$path = $pluginPath . 'vendors' . DS . 'shells' . DS;
+			if (file_exists($path)) {
+				$paths[] = $path;
+			}
+		}
+
+		$vendorPaths = array_values(App::path('vendors'));
+		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, App::path('shells'))));
+	}
+
+/**
+ * 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);
+		if (!is_dir(ROOT . DS . APP_DIR . DS . 'tmp')) {
+			define('TMP', CORE_PATH . 'cake' . DS . 'console' . DS . 'templates' . DS . 'skel' . DS . 'tmp' . 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 . 'templates' . DS . 'skel' . DS . 'config' . DS . 'core.php';
+			App::build();
+		}
+
+		return true;
+	}
+
+/**
+ * Clear the console
+ *
+ * @return void
+ * @access public
+ */
+	function clear() {
+		if (empty($this->params['noclear'])) {
+			if ( DS === '/') {
+				passthru('clear');
+			} else {
+				passthru('cls');
+			}
+		}
+	}
+
+/**
+ * Dispatches a CLI request
+ *
+ * @return boolean
+ * @access public
+ */
+	function dispatch() {
+		$arg = $this->shiftArgs();
+
+		if (!$arg) {
+			$this->help();
+			return false;
+		}
+		if ($arg == 'help') {
+			$this->help();
+			return true;
+		}
+		
+		list($plugin, $shell) = pluginSplit($arg);
+		$this->shell = $shell;
+		$this->shellName = Inflector::camelize($shell);
+		$this->shellClass = $this->shellName . 'Shell';
+
+		$arg = null;
+
+		if (isset($this->args[0])) {
+			$arg = $this->args[0];
+			$this->shellCommand = Inflector::variable($arg);
+		}
+
+		$Shell = $this->_getShell($plugin);
+
+		if (!$Shell) {
+			$title = sprintf(__('Error: Class %s could not be loaded.', true), $this->shellClass);
+			$this->stderr($title . "\n");
+			return false;
+		}
+
+		$methods = array();
+
+		if (is_a($Shell, 'Shell')) {
+			$Shell->initialize();
+			$Shell->loadTasks();
+
+			foreach ($Shell->taskNames as $task) {
+				if (is_a($Shell->{$task}, 'Shell')) {
+					$Shell->{$task}->initialize();
+					$Shell->{$task}->loadTasks();
+				}
+			}
+
+			$task = Inflector::camelize($arg);
+
+			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();
+					} else {
+						$this->help();
+					}
+					return true;
+				}
+				return $Shell->{$task}->execute();
+			}
+			$methods = array_diff(get_class_methods('Shell'), array('help'));
+		}
+		$methods = array_diff(get_class_methods($Shell), $methods);
+		$added = in_array(strtolower($arg), array_map('strtolower', $methods));
+		$private = $arg[0] == '_' && method_exists($Shell, $arg);
+
+		if (!$private) {
+			if ($added) {
+				$this->shiftArgs();
+				$Shell->startup();
+				return $Shell->{$arg}();
+			}
+			if (method_exists($Shell, 'main')) {
+				$Shell->startup();
+				return $Shell->main();
+			}
+		}
+
+		$title = sprintf(__('Error: Unknown %1$s command %2$s.', true), $this->shellName, $arg);
+		$message = sprintf(__('For usage try `cake %s help`', true), $this->shell);
+		$this->stderr($title . "\n" . $message . "\n");
+		return false;
+	}
+
+/**
+ * Get shell to use, either plugin shell or application shell
+ *
+ * All paths in the shellPaths property are searched.
+ * shell, shellPath and shellClass properties are taken into account.
+ *
+ * @param string $plugin Optionally the name of a plugin
+ * @return mixed False if no shell could be found or an object on success
+ * @access protected
+ */
+	function _getShell($plugin = null) {
+		foreach ($this->shellPaths as $path) {
+			$this->shellPath = $path . $this->shell . '.php';
+			$pluginShellPath =  DS . $plugin . DS . 'vendors' . DS . 'shells' . DS;
+
+			if ((strpos($path, $pluginShellPath) !== false || !$plugin) && file_exists($this->shellPath)) {
+				$loaded = true;
+				break;
+			}
+		}
+		if (!isset($loaded)) {
+			return false;
+		}
+
+		if (!class_exists('Shell')) {
+			require CONSOLE_LIBS . 'shell.php';
+		}
+
+		if (!class_exists($this->shellClass)) {
+			require $this->shellPath;
+		}
+		if (!class_exists($this->shellClass)) {
+			return false;
+		}
+		$Shell = new $this->shellClass($this);
+		return $Shell;
+	}
+
+/**
+ * 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 && ($result === '' || $result === null)) {
+			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.
+ * @return integer Returns the number of bytes output to stdout.
+ * @access public
+ */
+	function stdout($string, $newline = true) {
+		if ($newline) {
+			return fwrite($this->stdout, $string . "\n");
+		} else {
+			return fwrite($this->stdout, $string);
+		}
+	}
+/**
+ * Outputs to the stderr filehandle.
+ *
+ * @param string $string Error text to output.
+ * @access public
+ */
+	function stderr($string) {
+		fwrite($this->stderr, $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 = false;
+		foreach ($defaults as $default => $value) {
+			if (strpos($params[$default], '\\') !== false) {
+				$isWin = true;
+				break;
+			}
+		}
+		$params = str_replace('\\', '/', $params);
+
+		if (isset($params['working'])) {
+			$params['working'] = trim($params['working']);
+		}
+		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-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'], '/');
+		if (!$isWin || !preg_match('/^[A-Z]:$/i', $params['app'])) {
+			$params['working'] .= '/' . $params['app'];
+		}
+
+		if (!empty($matches[0]) || !empty($isWin)) {
+			$params = str_replace('/', '\\', $params);
+		}
+
+		$this->params = array_merge($this->params, $params);
+	}
+
+/**
+ * Helper for recursively parsing 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 mixed Null if there are no arguments otherwise the shifted argument
+ * @access public
+ */
+	function shiftArgs() {
+		return array_shift($this->args);
+	}
+
+/**
+ * Shows console help
+ *
+ * @access public
+ */
+	function help() {
+		$this->clear();
+		$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:");
+		$shellList = array();
+		foreach ($this->shellPaths as $path) {
+			if (!is_dir($path)) {
+				continue;
+			}
+ 			$shells = App::objects('file', $path);
+			if (empty($shells)) {
+				continue;
+			}
+			if (preg_match('@plugins[\\\/]([^\\\/]*)@', $path, $matches)) {
+				$type = Inflector::camelize($matches[1]);
+			} elseif (preg_match('@([^\\\/]*)[\\\/]vendors[\\\/]@', $path, $matches)) {
+				$type = $matches[1];
+			} elseif (strpos($path, CAKE_CORE_INCLUDE_PATH . DS . 'cake') === 0) {
+				$type = 'CORE';
+			} else {
+				$type = 'app';
+			}
+			foreach ($shells as $shell) {
+				if ($shell !== 'shell.php') {
+					$shell = str_replace('.php', '', $shell);
+					$shellList[$shell][$type] = $type;
+				}
+			}
+		}
+		if ($shellList) {
+			ksort($shellList);
+			if (DS === '/') {
+				$width = exec('tput cols') - 2;
+			}
+			if (empty($width)) {
+				$width = 80;
+			}
+			$columns = max(1, floor($width / 30));
+			$rows = ceil(count($shellList) / $columns);
+
+			foreach ($shellList as $shell => $types) {
+				sort($types);
+				$shellList[$shell] = str_pad($shell . ' [' . implode ($types, ', ') . ']', $width / $columns);
+			}
+			$out = array_chunk($shellList, $rows);
+			for ($i = 0; $i < $rows; $i++) {
+				$row = '';
+				for ($j = 0; $j < $columns; $j++) {
+					if (!isset($out[$j][$i])) {
+						continue;
+ 					}
+					$row .= $out[$j][$i];
+ 				}
+				$this->stdout(" " . $row);
+			}
+		}
+		$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'");
+	}
+
+/**
+ * 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);
+}

Added: trunk/src/Web/cake/console/error.php
===================================================================
--- trunk/src/Web/cake/console/error.php	                        (rev 0)
+++ trunk/src/Web/cake/console/error.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,265 @@
+<?php
+/**
+ * ErrorHandler for Console Shells
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console
+ * @since         CakePHP(tm) v 1.2.0.5074
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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');
+		call_user_func_array(array(&$this, $method), $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(1);
+	}
+
+/**
+ * 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(1);
+	}
+
+/**
+ * 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(1);
+	}
+
+/**
+ * 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(1);
+	}
+
+/**
+ * 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(1);
+	}
+
+/**
+ * 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(1);
+	}
+
+/**
+ * Renders the Missing Database web page.
+ *
+ * @param array $params Parameters
+ * @access public
+ */
+	function missingDatabase($params = array()) {
+		$this->stderr(__("Missing Database", true));
+		$this->_stop(1);
+	}
+
+/**
+ * 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(1);
+	}
+
+/**
+ * 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(1);
+	}
+
+/**
+ * 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(1);
+	}
+
+/**
+ * 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(1);
+	}
+
+/**
+ * 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(1);
+	}
+
+/**
+ * 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(1);
+	}
+
+/**
+ * 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(1);
+	}
+
+/**
+ * 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(1);
+	}
+
+/**
+ * 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");
+	}
+}

Added: trunk/src/Web/cake/console/libs/acl.php
===================================================================
--- trunk/src/Web/cake/console/libs/acl.php	                        (rev 0)
+++ trunk/src/Web/cake/console/libs/acl.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,602 @@
+<?php
+/**
+ * Acl Shell provides Acl access in the CLI environment
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ * @since         CakePHP(tm) v 1.2.0.5012
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Component', 'Acl');
+App::import('Model', 'DbAcl');
+
+/**
+ * Shell for ACL management.  This console is known to have issues with zend.ze1_compatibility_mode 
+ * being enabled.  Be sure to turn it off when using this shell.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ */
+class AclShell extends Shell {
+
+/**
+ * Contains instance of AclComponent
+ *
+ * @var AclComponent
+ * @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 $connection = 'default';
+
+/**
+ * Contains tasks to load and instantiate
+ *
+ * @var array
+ * @access public
+ */
+	var $tasks = array('DbConfig');
+
+/**
+ * Override startup of the Shell
+ *
+ * @access public
+ */
+	function startup() {
+		if (isset($this->params['connection'])) {
+			$this->connection = $this->params['connection'];
+		}
+
+		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]);
+		$parent = $this->parseIdentifier($this->args[1]);
+
+		if (!empty($parent) && $parent != '/' && $parent != 'root') {
+			$parent = $this->_getNodeId($class, $parent);
+		} else {
+			$parent = null;
+		}
+
+		$data = $this->parseIdentifier($this->args[2]);
+		if (is_string($data) && $data != '/') {
+			$data = array('alias' => $data);
+		} elseif (is_string($data)) {
+			$this->error(__('/ can not be used as an alias!', true), __("\t/ is the root, please supply a sub alias", true));
+		}
+
+		$data['parent_id'] = $parent;
+		$this->Acl->{$class}->create();
+		if ($this->Acl->{$class}->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());
+
+		$identifier = $this->parseIdentifier($this->args[1]);
+		$nodeId = $this->_getNodeId($class, $identifier);
+
+		if (!$this->Acl->{$class}->delete($nodeId)) {
+			$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());
+		$target = $this->parseIdentifier($this->args[1]);
+		$parent = $this->parseIdentifier($this->args[2]);
+
+		$data = array(
+			$class => array(
+				'id' => $this->_getNodeId($class, $target),
+				'parent_id' => $this->_getNodeId($class, $parent)
+			)
+		);
+		$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());
+		$identifier = $this->parseIdentifier($this->args[1]);
+
+		$id = $this->_getNodeId($class, $identifier);
+		$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)
+			);
+		}
+		$this->out(__('Path:', true));
+		$this->hr();
+		for ($i = 0; $i < count($nodes); $i++) {
+			$this->_outputNode($class, $nodes[$i], $i);
+		}
+	}
+
+/**
+ * Outputs a single node, Either using the alias or Model.key
+ *
+ * @param string $class Class name that is being used.
+ * @param array $node Array of node information.
+ * @param integer $indent indent level.
+ * @return void
+ * @access protected
+ */
+	function _outputNode($class, $node, $indent) {
+		$indent = str_repeat('  ', $indent);
+		$data = $node[$class];
+		if ($data['alias']) {
+			$this->out($indent . "[" . $data['id'] . "] " . $data['alias']);
+		 } else {
+			$this->out($indent . "[" . $data['id'] . "] " . $data['model'] . '.' . $data['foreign_key']);
+		}
+	}
+
+/**
+ * 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), $aroName), true);
+		} else {
+			$this->out(sprintf(__("%s is not allowed.", true), $aroName), 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])) {
+			$identity = $this->parseIdentifier($this->args[1]);
+
+			$topNode = $this->Acl->{$class}->find('first', array(
+				'conditions' => array($class . '.id' => $this->_getNodeId($class, $identity))
+			));
+
+			$nodes = $this->Acl->{$class}->find('all', array(
+				'conditions' => array(
+					$class . '.lft >=' => $topNode[$class]['lft'],
+					$class . '.lft <=' => $topNode[$class]['rght']
+				),
+				'order' => $class . '.lft ASC'
+			));
+		} else {
+			$nodes = $this->Acl->{$class}->find('all', array('order' => $class . '.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->_outputNode($class, $n, $count);
+		}
+		$this->hr();
+	}
+
+/**
+ * Initialize ACL database.
+ *
+ * @access public
+ */
+	function initdb() {
+		$this->Dispatch->args = array('schema', 'create', 'DbAcl');
+		$this->Dispatch->dispatch();
+	}
+
+/**
+ * Show help screen.
+ *
+ * @access public
+ */
+	function help() {
+		$head = "-----------------------------------------------\n";
+		$head .= __("Usage: cake acl <command> <arg1> <arg2>...", true) . "\n";
+		$head .= "-----------------------------------------------\n";
+		$head .= __("Commands:", true) . "\n";
+
+		$commands = array(
+			'create' => "create aro|aco <parent> <node>\n" .
+				"\t" . __("Creates a new ACL object <node> under the parent", true) . "\n" .
+				"\t" . __("specified by <parent>, an id/alias.", true) . "\n" .
+				"\t" . __("The <parent> and <node> references can be", true) . "\n" .
+				"\t" . __("in one of the following formats:", true) . "\n\n" .
+				"\t\t- " . __("<model>.<id> - The node will be bound to a", true) . "\n" .
+				"\t\t" . __("specific record of the given model.", true) . "\n\n" .
+				"\t\t- " . __("<alias> - The node will be given a string alias,", true) . "\n" .
+				"\t\t" . __(" (or path, in the case of <parent>)", true) . "\n" .
+				"\t\t  " . __("i.e. 'John'.  When used with <parent>,", true) . "\n" .
+				"\t\t" . __("this takes the form of an alias path,", true) . "\n" .
+				"\t\t  " . __("i.e. <group>/<subgroup>/<parent>.", true) . "\n\n" .
+				"\t" . __("To add a node at the root level,", true) . "\n" .
+				"\t" . __("enter 'root' or '/' as the <parent> parameter.", true) . "\n",
+
+			'delete' => "delete aro|aco <node>\n" .
+				"\t" . __("Deletes the ACL object with the given <node> reference", true) . "\n" .
+				"\t" . __("For more detailed parameter usage info,", true) . "\n" .
+				"\t" . __("see help for the 'create' command.", true),
+
+			'setparent' => "setParent aro|aco <node> <parent node>\n" .
+				"\t" . __("Moves the ACL object specified by <node> beneath", true) . "\n" .
+				"\t" . __("the parent ACL object specified by <parent>.", true) . "\n" .
+				"\t" . __("For more detailed parameter usage info,", true) . "\n" .
+				"\t" . __("see help for the 'create' command.", true),
+
+			'getpath' => "getPath aro|aco <node>\n" .
+				"\t" . __("Returns the path to the ACL object specified by <node>. This command", true) . "\n" .
+				"\t" . __("is useful in determining the inhertiance of permissions for a certain", true) . "\n" .
+				"\t" . __("object in the tree.", true) . "\n" .
+				"\t" . __("For more detailed parameter usage info,", true) . "\n" .
+				"\t" . __("see help for the 'create' command.", true),
+
+			'check' => "check <node> <node> [<aco_action>] " . __("or", true) . " all\n" .
+				"\t" . __("Use this command to check ACL permissions.", true) . "\n" .
+				"\t" . __("For more detailed parameter usage info,", true) . "\n" .
+				"\t" . __("see help for the 'create' command.", true),
+
+			'grant' => "grant <aronode> <aconode> [<aco_action>] " . __("or", true) . " all\n" .
+				"\t" . __("Use this command to grant ACL permissions. Once executed, the ARO", true) . "\n" .
+				"\t" . __("specified (and its children, if any) will have ALLOW access to the", true) . "\n" .
+				"\t" . __("specified ACO action (and the ACO's children, if any).", true) . "\n" .
+				"\t" . __("For more detailed parameter usage info,", true) . "\n" .
+				"\t" . __("see help for the 'create' command.", true),
+
+			'deny' => "deny <aronode> <aconode> [<aco_action>]" . __("or", true) . " all\n" .
+				"\t" . __("Use this command to deny ACL permissions. Once executed, the ARO", true) . "\n" .
+				"\t" . __("specified (and its children, if any) will have DENY access to the", true) . "\n" .
+				"\t" . __("specified ACO action (and the ACO's children, if any).", true) . "\n" .
+				"\t" . __("For more detailed parameter usage info,", true) . "\n" .
+				"\t" . __("see help for the 'create' command.", true),
+
+			'inherit' => "inherit <aronode> <aconode> [<aco_action>]" . __("or", true) . " all\n" .
+				"\t" . __("Use this command to force a child ARO object to inherit its", true) . "\n" .
+				"\t" . __("permissions settings from its parent.", true) . "\n" .
+				"\t" . __("For more detailed parameter usage info,", true) . "\n" .
+				"\t" . __("see help for the 'create' command.", true),
+
+			'view' => "view aro|aco [<node>]\n" .
+				"\t" . __("The view command will return the ARO or ACO tree.", true) . "\n" .
+				"\t" . __("The optional node parameter allows you to return", true) . "\n" .
+				"\t" . __("only a portion of the requested tree.", true) . "\n" .
+				"\t" . __("For more detailed parameter usage info,", true) . "\n" .
+				"\t" . __("see help for the 'create' command.", true),
+
+			'initdb' => "initdb\n".
+				"\t" . __("Uses this command : cake schema run create DbAcl", true),
+
+			'help' => "help [<command>]\n" .
+				"\t" . __("Displays this help message, or a message on a specific command.", true)
+		);
+
+		$this->out($head);
+		if (!isset($this->args[0])) {
+			foreach ($commands as $cmd) {
+				$this->out("{$cmd}\n\n");
+			}
+		} elseif (isset($commands[strtolower($this->args[0])])) {
+			$this->out($commands[strtolower($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[0]), __('Please specify which ACL object type you wish to create. Either "aro" or "aco"', 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 = 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;
+	}
+
+/**
+ * Parse an identifier into Model.foriegnKey or an alias.
+ * Takes an identifier determines its type and returns the result as used by other methods.
+ *
+ * @param string $identifier Identifier to parse
+ * @return mixed a string for aliases, and an array for model.foreignKey
+ */
+	function parseIdentifier($identifier) {
+		if (preg_match('/^([\w]+)\.(.*)$/', $identifier, $matches)) {
+			return array(
+				'model' => $matches[1],
+				'foreign_key' => $matches[2],
+			);
+		}
+		return $identifier;
+	}
+
+/**
+ * Get the node for a given identifier. $identifier can either be a string alias
+ * or an array of properties to use in AcoNode::node()
+ *
+ * @param string $class Class type you want (Aro/Aco)
+ * @param mixed $identifier A mixed identifier for finding the node.
+ * @return int Integer of NodeId. Will trigger an error if nothing is found.
+ */
+	function _getNodeId($class, $identifier) {
+		$node = $this->Acl->{$class}->node($identifier);
+		if (empty($node)) {
+			if (is_array($identifier)) {
+				$identifier = var_export($identifier, true);
+			}
+			$this->error(sprintf(__('Could not find node using reference "%s"', true), $identifier));
+		}
+		return Set::extract($node, "0.{$class}.id");
+	}
+
+/**
+ * get params for standard Acl methods
+ *
+ * @return array aro, aco, action
+ * @access private
+ */
+	function __getParams() {
+		$aro = is_numeric($this->args[0]) ? intval($this->args[0]) : $this->args[0];
+		$aco = is_numeric($this->args[1]) ? intval($this->args[1]) : $this->args[1];
+		$aroName = $aro;
+		$acoName = $aco;
+
+		if (is_string($aro)) {
+			$aro = $this->parseIdentifier($aro);
+		}
+		if (is_string($aco)) {
+			$aco = $this->parseIdentifier($aco);
+		}
+		$action = null;
+		if (isset($this->args[2])) {
+			$action = $this->args[2];
+			if ($action == '' || $action == 'all') {
+				$action = '*';
+			}
+		}
+		return compact('aro', 'aco', 'action', 'aroName', 'acoName');
+	}
+
+/**
+ * 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'] = (strtolower($class) == 'aro') ? 'foreign_key' : 'object_id';
+		$vars['data_name'] = $type;
+		$vars['table_name'] = $type . 's';
+		$vars['class'] = $class;
+		return $vars;
+	}
+}

Added: trunk/src/Web/cake/console/libs/api.php
===================================================================
--- trunk/src/Web/cake/console/libs/api.php	                        (rev 0)
+++ trunk/src/Web/cake/console/libs/api.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,213 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ * @since         CakePHP(tm) v 1.2.0.5012
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 = strtolower($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 = App::objects('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 = strtolower($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[strtolower($this->args[1])])) {
+			$this->out($commands[strtolower($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' => str_replace(array('/*', '*/', '*'), '', trim($result[1][$key])),
+						'method' => $method,
+						'parameters' => trim($result[3][$key])
+					);
+				}
+			}
+		}
+		ksort($parsed);
+		return $parsed;
+	}
+}

Added: trunk/src/Web/cake/console/libs/bake.php
===================================================================
--- trunk/src/Web/cake/console/libs/bake.php	                        (rev 0)
+++ trunk/src/Web/cake/console/libs/bake.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,232 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ * @since         CakePHP(tm) v 1.2.0.5012
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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/1522/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', 'Fixture', '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'))) {
+			if (isset($this->params['connection'])) {
+				$this->{$task}->connection = $this->params['connection'];
+			}
+			foreach($this->args as $i => $arg) {
+				if (strpos($arg, '.')) {
+					list($this->params['plugin'], $this->args[$i]) = pluginSplit($arg);
+					break;
+				}
+			}
+			if (isset($this->params['plugin'])) {
+				$this->{$task}->plugin = $this->params['plugin'];
+			}
+		}
+	}
+
+/**
+ * Override main() to handle action
+ *
+ * @access public
+ */
+	function main() {
+		Configure::write('Cache.disable', 1);
+
+		if (!is_dir($this->DbConfig->path)) {
+			if ($this->Project->execute()) {
+				$this->DbConfig->path = $this->params['working'] . DS . 'config' . DS;
+			} else {
+				return false;
+			}
+		}
+
+		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('[F]ixture');
+		$this->out('[T]est case');
+		$this->out('[Q]uit');
+
+		$classToBake = strtoupper($this->in(__('What would you like to Bake?', true), array('D', 'M', 'V', 'C', 'P', 'F', 'T', '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 'F':
+				$this->Fixture->execute();
+				break;
+			case 'T':
+				$this->Test->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, F, T, or C.', true));
+		}
+		$this->hr();
+		$this->main();
+	}
+
+/**
+ * Quickly bake the MVC
+ *
+ * @access public
+ */
+	function all() {
+		$this->hr();
+		$this->out('Bake All');
+		$this->hr();
+
+		if (!isset($this->params['connection']) && empty($this->connection)) {
+			$this->connection = $this->DbConfig->getConfig();
+		}
+
+		if (empty($this->args)) {
+			$this->Model->interactive = true;
+			$name = $this->Model->getName($this->connection);
+		}
+
+		foreach (array('Model', 'Controller', 'View') as $task) {
+			$this->{$task}->connection = $this->connection;
+			$this->{$task}->interactive = false;
+		}
+
+		if (!empty($this->args[0])) {
+			$name = $this->args[0];
+		}
+
+		$modelExists = false;
+		$model = $this->_modelName($name);
+		if (App::import('Model', $model)) {
+			$object = new $model();
+			$modelExists = true;
+		} else {
+			App::import('Model', 'Model', false);
+			$object = new Model(array('name' => $name, 'ds' => $this->connection));
+		}
+
+		$modelBaked = $this->Model->bake($object, false);
+
+		if ($modelBaked && $modelExists === false) {
+			$this->out(sprintf(__('%s Model was baked.', true), $model));
+			if ($this->_checkUnitTest()) {
+				$this->Model->bakeFixture($model);
+				$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(sprintf(__('%s Views were baked.', true), $name));
+			}
+			$this->out(__('Bake All complete', true));
+			array_shift($this->args);
+		} else {
+			$this->err(__('Bake All could not continue without a valid model', true));
+		}
+		$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("\n\tbake fixture\n\t\tbakes fixtures. run 'bake fixture help' for more info.");
+		$this->out("\n\tbake test\n\t\tbakes unit tests. run 'bake test help' for more info.");
+		$this->out();
+
+	}
+}

Added: trunk/src/Web/cake/console/libs/console.php
===================================================================
--- trunk/src/Web/cake/console/libs/console.php	                        (rev 0)
+++ trunk/src/Web/cake/console/libs/console.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,361 @@
+<?php
+/**
+ * CakePHP Console Shell
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ * @since         CakePHP(tm) v 1.2.0.5012
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * @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 = App::objects('model');
+		App::import('Model', $this->models);
+
+		foreach ($this->models as $model) {
+			$class = Inflector::camelize(str_replace('.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() {
+		$out  = 'Console help:';
+		$out .= '-------------';
+		$out .= 'The interactive console is a tool for testing parts of your app before you';
+		$out .= 'write code.';
+		$out .= "\n";
+		$out .= 'Model testing:';
+		$out .= 'To test model results, use the name of your model without a leading $';
+		$out .= 'e.g. Foo->find("all")';
+		$out .= "\n";
+		$out .= 'To dynamically set associations, you can do the following:';
+		$out .= "\tModelA bind <association> ModelB";
+		$out .= "where the supported assocations are hasOne, hasMany, belongsTo, hasAndBelongsToMany";
+		$out .= "\n";
+		$out .= 'To dynamically remove associations, you can do the following:';
+		$out .= "\t ModelA unbind <association> ModelB";
+		$out .= "where the supported associations are the same as above";
+		$out .= "\n";
+		$out .= "To save a new field in a model, you can do the following:";
+		$out .= "\tModelA->save(array('foo' => 'bar', 'baz' => 0))";
+		$out .= "where you are passing a hash of data to be saved in the format";
+		$out .= "of field => value pairs";
+		$out .= "\n";
+		$out .= "To get column information for a model, use the following:";
+		$out .= "\tModelA columns";
+		$out .= "which returns a list of columns and their type";
+		$out .= "\n";
+		$out .= "\n";
+		$out .= 'Route testing:';
+		$out .= "\n";
+		$out .= 'To test URLs against your app\'s route configuration, type:';
+		$out .= "\n";
+		$out .= "\tRoute <url>";
+		$out .= "\n";
+		$out .= "where url is the path to your your action plus any query parameters,";
+		$out .= "minus the application's base path.  For example:";
+		$out .= "\n";
+		$out .= "\tRoute /posts/view/1";
+		$out .= "\n";
+		$out .= "will return something like the following:";
+		$out .= "\n";
+		$out .= "\tarray (";
+		$out .= "\t  [...]";
+		$out .= "\t  'controller' => 'posts',";
+		$out .= "\t  'action' => 'view',";
+		$out .= "\t  [...]";
+		$out .= "\t)";
+		$out .= "\n";
+		$out .= 'Alternatively, you can use simple array syntax to test reverse';
+		$out .= 'To reload your routes config (config/routes.php), do the following:';
+		$out .= "\n";
+		$out .= "\tRoutes reload";
+		$out .= "\n";
+		$out .= 'To show all connected routes, do the following:';
+		$out .= "\tRoutes show";
+		$this->out($out);
+	}
+
+/**
+ * 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->help();
+				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);
+						$data = preg_replace('/^\(*(array)?\(*(.+?)\)*$/i', '\\2', $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(implode("\n", Set::extract($router->routes, '{n}.0')));
+				break;
+				case (preg_match("/^route\s+(\(.*\))$/i", $command, $tmp) == true):
+					if ($url = eval('return array' . $tmp[1] . ';')) {
+						$this->out(Router::url($url));
+					}
+				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 protected
+ */
+	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 protected
+ */
+	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, $len = count($router->routes); $i < $len; $i++) {
+			$router->routes[$i]->compile();
+		}
+		return true;
+	}
+}

Added: trunk/src/Web/cake/console/libs/i18n.php
===================================================================
--- trunk/src/Web/cake/console/libs/i18n.php	                        (rev 0)
+++ trunk/src/Web/cake/console/libs/i18n.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,128 @@
+<?php
+/**
+ * Internationalization Management Shell
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ * @since         CakePHP(tm) v 1.2.0.5669
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 = strtolower($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', '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();
+	}
+}

Added: trunk/src/Web/cake/console/libs/schema.php
===================================================================
--- trunk/src/Web/cake/console/libs/schema.php	                        (rev 0)
+++ trunk/src/Web/cake/console/libs/schema.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,512 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ * @since         CakePHP(tm) v 1.2.0.5550
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Core', 'File', false);
+App::import('Model', 'CakeSchema', false);
+
+/**
+ * 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/1523/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 = $file = $path = $connection = $plugin = null;
+		if (!empty($this->params['name'])) {
+			$name = $this->params['name'];
+		} elseif (!empty($this->args[0])) {
+			$name = $this->params['name'] = $this->args[0];
+		}
+
+		if (strpos($name, '.')) {
+			list($this->params['plugin'], $splitName) = pluginSplit($name);
+			$name = $this->params['name'] = $splitName;
+		}
+
+		if ($name) {
+			$this->params['file'] = Inflector::underscore($name);
+		}
+
+		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'];
+
+		if (!empty($this->params['path'])) {
+			$path = $this->params['path'];
+		}
+
+		if (!empty($this->params['connection'])) {
+			$connection = $this->params['connection'];
+		}
+		if (!empty($this->params['plugin'])) {
+			$plugin = $this->params['plugin'];
+			if (empty($name)) {
+				$name = $plugin;
+			}
+		}
+		$this->Schema =& new CakeSchema(compact('name', 'path', 'file', 'connection', 'plugin'));
+	}
+
+/**
+ * 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 {
+			$file = $this->Schema->path . DS . $this->params['file'];
+			$this->err(sprintf(__('Schema file (%s) could not be found.', true), $file));
+			$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...', true));
+		$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 = strtolower($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') {
+				return $this->_stop();
+			}
+			if ($result === 'o') {
+				$snapshot = false;
+			}
+		}
+
+		$cacheDisable = Configure::read('Cache.disable');
+		Configure::write('Cache.disable', true);
+
+		$content = $this->Schema->read($options);
+		$content['file'] = $this->params['file'];
+		
+		Configure::write('Cache.disable', $cacheDisable);
+
+		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
+ * Use the `write` param to enable and control SQL file output location.
+ * Simply using -write will write the sql file to the same dir as the schema file.
+ * If -write contains a full path name the file will be saved there. If -write only
+ * contains no DS, that will be used as the file name, in the same dir as the schema file.
+ *
+ * @access public
+ */
+	function dump() {
+		$write = false;
+		$Schema = $this->Schema->load();
+		if (!$Schema) {
+			$this->err(__('Schema could not be loaded', true));
+			$this->_stop();
+		}
+		if (isset($this->params['write'])) {
+			if ($this->params['write'] == 1) {
+				$write = Inflector::underscore($this->Schema->name);
+			} else {
+				$write = $this->params['write'];
+			}
+		}
+		$db =& ConnectionManager::getDataSource($this->Schema->connection);
+		$contents = "#" . $Schema->name . " sql generated on: " . date('Y-m-d H:i:s') . " : " . time() . "\n\n";
+		$contents .= $db->dropSchema($Schema) . "\n\n". $db->createSchema($Schema);
+
+		if ($write) {
+			if (strpos($write, '.sql') === false) {
+				$write .= '.sql';
+			}
+			if (strpos($write, DS) !== false) {
+				$File =& new File($write, true);
+			} else {
+				$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 create commands.  Alias for run create.
+ *
+ * @return void
+ */
+	function create() {
+		list($Schema, $table) = $this->_loadSchema();
+		$this->__create($Schema, $table);
+	}
+
+/**
+ * Run database create commands.  Alias for run create.
+ *
+ * @return void
+ */
+	function update() {
+		list($Schema, $table) = $this->_loadSchema();
+		$this->__update($Schema, $table);
+	}
+
+/**
+ * Prepares the Schema objects for database operations.
+ *
+ * @return void
+ */
+	function _loadSchema() {
+		$name = $plugin = null;
+		if (isset($this->params['name'])) {
+			$name = $this->params['name'];
+		}
+		if (isset($this->params['plugin'])) {
+			$plugin = $this->params['plugin'];
+		}
+		
+		if (isset($this->params['dry'])) {
+			$this->__dry = true;
+			$this->out(__('Performing a dry run.', true));
+		}
+
+		$options = array('name' => $name, 'plugin' => $plugin);
+		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->path . DS . $this->Schema->file));
+			$this->_stop();
+		}
+		$table = null;
+		if (isset($this->args[1])) {
+			$table = $this->args[1];
+		}
+		return array(&$Schema, $table);
+	}
+
+/**
+ * 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).', true));
+			$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).', true));
+			$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...', true));
+		$options = array();
+		if (isset($this->params['f'])) {
+			$options['models'] = false;
+		}
+		$Old = $this->Schema->read($options);
+		$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);
+
+		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;
+					}
+					$error = null;
+					if (!$db->execute($sql)) {
+						$error = $table . ': '  . $db->lastError();
+					}
+
+					$Schema->after(array($event => $table, 'errors' => $error));
+
+					if (!empty($error)) {
+						$this->out($error);
+					} else {
+						$this->out(sprintf(__('%s updated.', true), $table));
+					}
+				}
+			}
+		}
+	}
+
+/**
+ * Displays help contents
+ *
+ * @access public
+ */
+	function help() {
+		$help = <<<TEXT
+The Schema Shell generates a schema object from
+the database and updates the database from the schema.
+---------------------------------------------------------------
+Usage: cake schema <command> <arg1> <arg2>...
+---------------------------------------------------------------
+Params:
+	-connection <config>
+		set db config <config>. uses 'default' if none is specified
+
+	-path <dir>
+		path <dir> to read and write schema.php.
+		default path: {$this->Schema->path}
+
+	-name <name>
+		Classname to use. If <name> is Plugin.className, it will
+		set the plugin and name params.
+
+	-file <name>
+		file <name> to read and write.
+		default file: {$this->Schema->file}
+
+	-s <number>
+		snapshot <number> to use for run.
+
+	-dry
+		Perform a dry run on create + update commands.
+		Queries will be output to window instead of executed.
+
+	-f
+		force 'generate' to create a new schema.
+
+	-plugin
+		Indicate the plugin to use.
+
+Commands:
+
+	schema help
+		shows this help message.
+
+	schema view <name>
+		read and output contents of schema file.
+
+	schema generate
+		reads from 'connection' writes to 'path'
+		To force generation of all tables into the schema, use the -f param.
+		Use 'schema generate snapshot <number>' to generate snapshots
+		which you can use with the -s parameter in the other operations.
+
+	schema dump <name>
+		Dump database sql based on schema file to stdout.
+		If you use the `-write` param is used a .sql will be generated.
+		If `-write` is a filename, then that file name will be generate.
+		If `-write` is a full path, the schema will be written there.
+
+	schema create <name> <table>
+		Drop and create tables based on schema file
+		optional <table> argument can be used to create only a single 
+		table in the schema. Pass the -s param with a number to use a snapshot.
+		Use the `-dry` param to preview the changes.
+
+	schema update <name> <table>
+		Alter the tables based on schema file. Optional <table>
+		parameter will only update one table. 
+		To use a snapshot pass the `-s` param with the snapshot number.
+		To preview the changes that will be done use `-dry`.
+		To force update of all tables into the schema, use the -f param.
+TEXT;
+		$this->out($help);
+		$this->_stop();
+	}
+}

Added: trunk/src/Web/cake/console/libs/shell.php
===================================================================
--- trunk/src/Web/cake/console/libs/shell.php	                        (rev 0)
+++ trunk/src/Web/cake/console/libs/shell.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,645 @@
+<?php
+/**
+ * Base class for Shells
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ * @since         CakePHP(tm) v 1.2.0.5012
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 ShellDispatcher
+ * @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 DATABASE_CONFIG
+ * @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, strtolower(Inflector::camelize($this->args[0]))) !== false) {
+				$dispatch->shiftArgs();
+			}
+			if (strtolower($this->command) == strtolower(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 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->Dispatch->clear();
+		$this->out();
+		$this->out('Welcome to CakePHP v' . Configure::version() . ' Console');
+		$this->hr();
+		$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) {
+				list($plugin, $modelClass) = pluginSplit($modelClass, true);
+				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;
+					}
+				}
+			}
+			$taskClassCheck = $taskClass;
+			if (!PHP5) {
+				$taskClassCheck = strtolower($taskClass);
+			}
+			if (ClassRegistry::isKeySet($taskClassCheck)) {
+				$this->taskNames[] = $taskName;
+				if (!PHP5) {
+					$this->{$taskName} =& ClassRegistry::getObject($taskClassCheck);
+				} else {
+					$this->{$taskName} = ClassRegistry::getObject($taskClassCheck);
+				}
+			} 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(strtolower($in), $options) && !in_array(strtoupper($in), $options)) && !in_array($in, $options))) {
+				$in = $this->Dispatch->getInput($prompt, $options, $default);
+			}
+		}
+		return $in;
+	}
+
+/**
+ * Outputs a single or multiple messages to stdout. If no parameters
+ * are passed outputs just a newline.
+ *
+ * @param mixed $message A string or a an array of strings to output
+ * @param integer $newlines Number of newlines to append
+ * @return integer Returns the number of bytes returned from writing to stdout.
+ * @access public
+ */
+	function out($message = null, $newlines = 1) {
+		if (is_array($message)) {
+			$message = implode($this->nl(), $message);
+		}
+		return $this->Dispatch->stdout($message . $this->nl($newlines), false);
+	}
+
+/**
+ * Outputs a single or multiple error messages to stderr. If no parameters
+ * are passed outputs just a newline.
+ *
+ * @param mixed $message A string or a an array of strings to output
+ * @param integer $newlines Number of newlines to append
+ * @access public
+ */
+	function err($message = null, $newlines = 1) {
+		if (is_array($message)) {
+			$message = implode($this->nl(), $message);
+		}
+		$this->Dispatch->stderr($message . $this->nl($newlines));
+	}
+
+/**
+ * Returns a single or multiple linefeeds sequences.
+ *
+ * @param integer $multiplier Number of times the linefeed sequence should be repeated
+ * @access public
+ * @return string
+ */
+	function nl($multiplier = 1) {
+		return str_repeat("\n", $multiplier);
+	}
+
+/**
+ * Outputs a series of minus characters to the standard output, acts as a visual separator.
+ *
+ * @param integer $newlines Number of newlines to pre- and append
+ * @access public
+ */
+	function hr($newlines = 0) {
+		$this->out(null, $newlines);
+		$this->out('---------------------------------------------------------------');
+		$this->out(null, $newlines);
+	}
+
+/**
+ * Displays a formatted error message
+ * and exits the application with status code 1
+ *
+ * @param string $title Title of the error
+ * @param string $message An optional error message
+ * @access public
+ */
+	function error($title, $message = null) {
+		$this->err(sprintf(__('Error: %s', true), $title));
+
+		if (!empty($message)) {
+			$this->err($message);
+		}
+		$this->_stop(1);
+	}
+
+/**
+ * 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) {
+			$message[] = "Got: " . count($this->args);
+			$message[] = "Expected: {$expectedNum}";
+			$message[] = "Please type `cake {$this->shell} help` for help";
+			$message[] = "on usage of the {$this->name} {$command}.";
+			$this->error('Wrong number of parameters', $message);
+		}
+	}
+
+/**
+ * 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();
+		$this->out(sprintf(__("Creating file %s", true), $path));
+
+		if (is_file($path) && $this->interactive === true) {
+			$prompt = sprintf(__('File `%s` exists, overwrite?', true), $path);
+			$key = $this->in($prompt,  array('y', 'n', 'q'), 'n');
+
+			if (strtolower($key) == 'q') {
+				$this->out(__('Quitting.', true), 2);
+				$this->_stop();
+			} elseif (strtolower($key) != 'y') {
+				$this->out(sprintf(__('Skip `%s`', true), $path), 2);
+				return false;
+			}
+		}
+		if (!class_exists('File')) {
+			require LIBS . 'file.php';
+		}
+
+		if ($File = new File($path, true)) {
+			$data = $File->prepare($contents);
+			$File->write($data);
+			$this->out(sprintf(__('Wrote `%s`', true), $path));
+			return true;
+		} else {
+			$this->err(sprintf(__('Could not write to `%s`.', true), $path), 2);
+			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}`.");
+			$this->err("For usage, try `cake {$this->shell} help`.", 2);
+		} 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;
+		}
+		$prompt = 'SimpleTest is not installed. Do you want to bake unit test files anyway?';
+		$unitTest = $this->in($prompt, array('y','n'), 'y');
+		$result = strtolower($unitTest) == 'y' || strtolower($unitTest) == 'yes';
+
+		if ($result) {
+			$this->out();
+			$this->out('You can download SimpleTest from http://simpletest.org');
+		}
+		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 str_replace(DS . DS, DS, $shortPath);
+	}
+
+/**
+ * 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 strtolower(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 underscored model key for associations
+ *
+ * @param string $name Model class name
+ * @return string Singular model key
+ * @access protected
+ */
+	function _modelKey($name) {
+		return Inflector::underscore($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) {
+		return Inflector::camelize(str_replace('_id', '', $key));
+	}
+
+/**
+ * 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($name));
+	}
+
+/**
+ * Find the correct path for a plugin. Scans $pluginPaths for the plugin you want.
+ *
+ * @param string $pluginName Name of the plugin you want ie. DebugKit
+ * @return string $path path to the correct plugin.
+ */
+	function _pluginPath($pluginName) {
+		return App::pluginPath($pluginName);
+	}
+}

Added: trunk/src/Web/cake/console/libs/tasks/bake.php
===================================================================
--- trunk/src/Web/cake/console/libs/tasks/bake.php	                        (rev 0)
+++ trunk/src/Web/cake/console/libs/tasks/bake.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,71 @@
+<?php
+/**
+ * Base class for Bake Tasks.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.tasks
+ * @since         CakePHP(tm) v 1.3
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+class BakeTask extends Shell {
+
+/**
+ * Name of plugin
+ *
+ * @var string
+ * @access public
+ */
+	var $plugin = null;
+
+/**
+ * The db connection being used for baking
+ *
+ * @var string
+ * @access public
+ */
+	var $connection = null;
+
+/**
+ * Flag for interactive mode
+ *
+ * @var boolean
+ */
+	var $interactive = false;
+
+/**
+ * Disable caching for baking.
+ * This forces the most current database schema to be used.
+ *
+ * @return void
+ */
+	function startup() {
+		Configure::write('Cache.disable', 1);
+		parent::startup();
+	}
+
+/**
+ * Gets the path for output.  Checks the plugin property
+ * and returns the correct path.
+ *
+ * @return string Path to output.
+ * @access public
+ */
+	function getPath() {
+		$path = $this->path;
+		if (isset($this->plugin)) {
+			$name = substr($this->name, 0, strlen($this->name) - 4);
+			$path = $this->_pluginPath($this->plugin) . Inflector::pluralize(Inflector::underscore($name)) . DS;
+		}
+		return $path;
+	}
+}

Added: trunk/src/Web/cake/console/libs/tasks/controller.php
===================================================================
--- trunk/src/Web/cake/console/libs/tasks/controller.php	                        (rev 0)
+++ trunk/src/Web/cake/console/libs/tasks/controller.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,486 @@
+<?php
+/**
+ * The ControllerTask handles creating and updating controller files.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.tasks
+ * @since         CakePHP(tm) v 1.2
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+include_once dirname(__FILE__) . DS . 'bake.php';
+
+/**
+ * Task class for creating and updating controller files.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.tasks
+ */
+class ControllerTask extends BakeTask {
+
+/**
+ * Tasks to be loaded by this Task
+ *
+ * @var array
+ * @access public
+ */
+	var $tasks = array('Model', 'Test', 'Template', 'DbConfig', '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)) {
+			return $this->__interactive();
+		}
+
+		if (isset($this->args[0])) {
+			if (!isset($this->connection)) {
+				$this->connection = 'default';
+			}
+			if (strtolower($this->args[0]) == 'all') {
+				return $this->all();
+			}
+
+			$controller = $this->_controllerName($this->args[0]);
+			$actions = 'scaffold';
+
+			if (!empty($this->args[1]) && ($this->args[1] == 'public' || $this->args[1] == 'scaffold')) {
+				$this->out(__('Baking basic crud methods for ', true) . $controller);
+				$actions = $this->bakeActions($controller);
+			} elseif (!empty($this->args[1]) && $this->args[1] == 'admin') {
+				$admin = $this->Project->getPrefix();
+				if ($admin) {
+					$this->out(sprintf(__('Adding %s methods', true), $admin));
+					$actions = $this->bakeActions($controller, $admin);
+				}
+			}
+
+			if (!empty($this->args[2]) && $this->args[2] == 'admin') {
+				$admin = $this->Project->getPrefix();
+				if ($admin) {
+					$this->out(sprintf(__('Adding %s methods', true), $admin));
+					$actions .= "\n" . $this->bakeActions($controller, $admin);
+				}
+			}
+
+			if ($this->bake($controller, $actions)) {
+				if ($this->_checkUnitTest()) {
+					$this->bakeTest($controller);
+				}
+			}
+		}
+	}
+
+/**
+ * Bake All the controllers at once.  Will only bake controllers for models that exist.
+ *
+ * @access public
+ * @return void
+ */
+	function all() {
+		$this->interactive = false;
+		$this->listAll($this->connection, false);
+		ClassRegistry::config('Model', array('ds' => $this->connection));
+		$unitTestExists = $this->_checkUnitTest();
+		foreach ($this->__tables as $table) {
+			$model = $this->_modelName($table);
+			$controller = $this->_controllerName($model);
+			if (App::import('Model', $model)) {
+				$actions = $this->bakeActions($controller);
+				if ($this->bake($controller, $actions) && $unitTestExists) {
+					$this->bakeTest($controller);
+				}
+			}
+		}
+	}
+
+/**
+ * Interactive
+ *
+ * @access private
+ */
+	function __interactive() {
+		$this->interactive = true;
+		$this->hr();
+		$this->out(sprintf(__("Bake Controller\nPath: %s", true), $this->path));
+		$this->hr();
+
+		if (empty($this->connection)) {
+			$this->connection = $this->DbConfig->getConfig();
+		}
+
+		$controllerName = $this->getName();
+		$this->hr();
+		$this->out(sprintf(__('Baking %sController', true), $controllerName));
+		$this->hr();
+
+		$helpers = $components = array();
+		$actions = '';
+		$wannaUseSession = 'y';
+		$wannaBakeAdminCrud = 'n';
+		$useDynamicScaffold = 'n';
+		$wannaBakeCrud = 'y';
+
+		$controllerFile = strtolower(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(implode("\n", $question), array('y','n'), 'y');
+
+		if (strtolower($doItInteractive) == 'y') {
+			$this->interactive = true;
+			$useDynamicScaffold = $this->in(
+				__("Would you like to use dynamic scaffolding?", true), array('y','n'), 'n'
+			);
+
+			if (strtolower($useDynamicScaffold) == 'y') {
+				$wannaBakeCrud = 'n';
+				$actions = 'scaffold';
+			} else {
+				list($wannaBakeCrud, $wannaBakeAdminCrud) = $this->_askAboutMethods();
+
+				$helpers = $this->doHelpers();
+				$components = $this->doComponents();
+
+				$wannaUseSession = $this->in(
+					__("Would you like to use Session flash messages?", true), array('y','n'), 'y'
+				);
+			}
+		} else {
+			list($wannaBakeCrud, $wannaBakeAdminCrud) = $this->_askAboutMethods();
+		}
+
+		if (strtolower($wannaBakeCrud) == 'y') {
+			$actions = $this->bakeActions($controllerName, null, strtolower($wannaUseSession) == 'y');
+		}
+		if (strtolower($wannaBakeAdminCrud) == 'y') {
+			$admin = $this->Project->getPrefix();
+			$actions .= $this->bakeActions($controllerName, $admin, strtolower($wannaUseSession) == 'y');
+		}
+
+		$baked = false;
+		if ($this->interactive === true) {
+			$this->confirmController($controllerName, $useDynamicScaffold, $helpers, $components);
+			$looksGood = $this->in(__('Look okay?', true), array('y','n'), 'y');
+
+			if (strtolower($looksGood) == 'y') {
+				$baked = $this->bake($controllerName, $actions, $helpers, $components);
+				if ($baked && $this->_checkUnitTest()) {
+					$this->bakeTest($controllerName);
+				}
+			}
+		} else {
+			$baked = $this->bake($controllerName, $actions, $helpers, $components);
+			if ($baked && $this->_checkUnitTest()) {
+				$this->bakeTest($controllerName);
+			}
+		}
+		return $baked;
+	}
+
+/**
+ * Confirm a to be baked controller with the user
+ *
+ * @return void
+ */
+	function confirmController($controllerName, $useDynamicScaffold, $helpers, $components) {
+		$this->out();
+		$this->hr();
+		$this->out(__('The following controller will be created:', true));
+		$this->hr();
+		$this->out(sprintf(__("Controller Name:\n\t%s", true), $controllerName));
+
+		if (strtolower($useDynamicScaffold) == 'y') {
+			$this->out("var \$scaffold;");
+		}
+
+		$properties = array(
+			'helpers' => __("Helpers:", true),
+			'components' => __('Components:', true),
+		);
+
+		foreach ($properties as $var => $title) {
+			if (count($$var)) {
+				$output = '';
+				$length = count($$var);
+				foreach ($$var as $i => $propElement) {
+					if ($i != $length -1) {
+						$output .= ucfirst($propElement) . ', ';
+					} else {
+						$output .= ucfirst($propElement);
+					}
+				}
+				$this->out($title . "\n\t" . $output);
+			}
+		}
+		$this->hr();
+	}
+
+/**
+ * Interact with the user and ask about which methods (admin or regular they want to bake)
+ *
+ * @return array Array containing (bakeRegular, bakeAdmin) answers
+ */
+	function _askAboutMethods() {
+		$wannaBakeCrud = $this->in(
+			__("Would you like to create some basic class methods \n(index(), add(), view(), edit())?", true),
+			array('y','n'), 'n'
+		);
+		$wannaBakeAdminCrud = $this->in(
+			__("Would you like to create the basic class methods for admin routing?", true),
+			array('y','n'), 'n'
+		);
+		return array($wannaBakeCrud, $wannaBakeAdminCrud);
+	}
+
+/**
+ * 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 = $modelImport = $this->_modelName($controllerName);
+		$plugin = $this->plugin;
+		if ($plugin) {
+			$modelImport = $plugin . '.' . $modelImport;
+		}
+		if (!App::import('Model', $modelImport)) {
+			$this->err(__('You must have a model for this class to build basic methods. Please try again.', true));
+			$this->_stop();
+		}
+
+		$modelObj =& ClassRegistry::init($currentModelName);
+		$controllerPath = $this->_controllerPath($controllerName);
+		$pluralName = $this->_pluralName($currentModelName);
+		$singularName = Inflector::variable($currentModelName);
+		$singularHumanName = $this->_singularHumanName($controllerName);
+		$pluralHumanName = $this->_pluralName($controllerName);
+
+		$this->Template->set(compact('plugin', 'admin', 'controllerPath', 'pluralName', 'singularName', 'singularHumanName',
+			'pluralHumanName', 'modelObj', 'wannaUseSession', 'currentModelName'));
+		$actions = $this->Template->generate('actions', 'controller_actions');
+		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) {
+		$isScaffold = ($actions === 'scaffold') ? true : false;
+
+		$this->Template->set('plugin', Inflector::camelize($this->plugin));
+		$this->Template->set(compact('controllerName', 'actions', 'helpers', 'components', 'isScaffold'));
+		$contents = $this->Template->generate('classes', 'controller');
+
+		$path = $this->getPath();
+		$filename = $path . $this->_controllerPath($controllerName) . '_controller.php';
+		if ($this->createFile($filename, $contents)) {
+			return $contents;
+		}
+		return false;
+	}
+
+/**
+ * Assembles and writes a unit test file
+ *
+ * @param string $className Controller class name
+ * @return string Baked test
+ * @access private
+ */
+	function bakeTest($className) {
+		$this->Test->plugin = $this->plugin;
+		$this->Test->connection = $this->connection;
+		$this->Test->interactive = $this->interactive;
+		return $this->Test->bake('Controller', $className);
+	}
+
+/**
+ * Interact with the user and get a list of additional helpers
+ *
+ * @return array Helpers that the user wants to use.
+ */
+	function doHelpers() {
+		return $this->_doPropertyChoices(
+			__("Would you like this controller to use other helpers\nbesides HtmlHelper and FormHelper?", true),
+			__("Please provide a comma separated list of the other\nhelper names you'd like to use.\nExample: 'Ajax, Javascript, Time'", true)
+		);
+	}
+
+/**
+ * Interact with the user and get a list of additional components
+ *
+ * @return array Components the user wants to use.
+ */
+	function doComponents() {
+		return $this->_doPropertyChoices(
+			__("Would you like this controller to use any components?", true),
+			__("Please provide a comma separated list of the component names you'd like to use.\nExample: 'Acl, Security, RequestHandler'", true)
+		);
+	}
+
+/**
+ * Common code for property choice handling.
+ *
+ * @param string $prompt A yes/no question to precede the list
+ * @param sting $example A question for a comma separated list, with examples.
+ * @return array Array of values for property.
+ */
+	function _doPropertyChoices($prompt, $example) {
+		$proceed = $this->in($prompt, array('y','n'), 'n');
+		$property = array();
+		if (strtolower($proceed) == 'y') {
+			$propertyList = $this->in($example);
+			$propertyListTrimmed = str_replace(' ', '', $propertyList);
+			$property = explode(',', $propertyListTrimmed);
+		}
+		return array_filter($property);
+	}
+
+/**
+ * Outputs and gets the list of possible controllers from database
+ *
+ * @param string $useDbConfig Database configuration name
+ * @param boolean $interactive Whether you are using listAll interactively and want options output.
+ * @return array Set of controllers
+ * @access public
+ */
+	function listAll($useDbConfig = null) {
+		if (is_null($useDbConfig)) {
+			$useDbConfig = $this->connection;
+		}
+		$this->__tables = $this->Model->getAllTables($useDbConfig);
+
+		if ($this->interactive == true) {
+			$this->out(__('Possible Controllers based on your current database:', true));
+			$this->_controllerNames = array();
+			$count = count($this->__tables);
+			for ($i = 0; $i < $count; $i++) {
+				$this->_controllerNames[] = $this->_controllerName($this->_modelName($this->__tables[$i]));
+				$this->out($i + 1 . ". " . $this->_controllerNames[$i]);
+			}
+			return $this->_controllerNames;
+		}
+		return $this->__tables;
+	}
+
+/**
+ * Forces the user to specify the controller he wants to bake, and returns the selected controller name.
+ *
+ * @param string $useDbConfig Connection name to get a controller name for.
+ * @return string Controller name
+ * @access public
+ */
+	function getName($useDbConfig = null) {
+		$controllers = $this->listAll($useDbConfig);
+		$enteredController = '';
+
+		while ($enteredController == '') {
+			$enteredController = $this->in(__("Enter a number from the list above,\ntype 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->err(__("The Controller name you supplied was empty,\nor the number you 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('Arguments:');
+		$this->out();
+		$this->out("<name>");
+		$this->out("\tName of the controller to bake. Can use Plugin.name");
+		$this->out("\tas a shortcut for plugin baking.");
+		$this->out();
+		$this->out('Params:');
+		$this->out();
+		$this->out('-connection <config>');
+		$this->out("\tset db config <config>. uses 'default' if none is specified");
+		$this->out();
+		$this->out('Commands:');
+		$this->out();
+		$this->out("controller <name>");
+		$this->out("\tbakes controller with var \$scaffold");
+		$this->out();
+		$this->out("controller <name> public");
+		$this->out("\tbakes controller with basic crud actions");
+		$this->out("\t(index, view, add, edit, delete)");
+		$this->out();
+		$this->out("controller <name> admin");
+		$this->out("\tbakes a controller with basic crud actions for one of the");
+		$this->out("\tConfigure::read('Routing.prefixes') methods.");
+		$this->out();
+		$this->out("controller <name> public admin");
+		$this->out("\tbakes a controller with basic crud actions for one");
+		$this->out("\tConfigure::read('Routing.prefixes') and non admin methods.");
+		$this->out("\t(index, view, add, edit, delete,");
+		$this->out("\tadmin_index, admin_view, admin_edit, admin_add, admin_delete)");
+		$this->out();
+		$this->out("controller all");
+		$this->out("\tbakes all controllers with CRUD methods.");
+		$this->out();
+		$this->_stop();
+	}
+}

Added: trunk/src/Web/cake/console/libs/tasks/db_config.php
===================================================================
--- trunk/src/Web/cake/console/libs/tasks/db_config.php	                        (rev 0)
+++ trunk/src/Web/cake/console/libs/tasks/db_config.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,368 @@
+<?php
+/**
+ * The DbConfig Task handles creating and updating the database.php
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.tasks
+ * @since         CakePHP(tm) v 1.2
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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
+	);
+
+/**
+ * String name of the database config class name.
+ * Used for testing.
+ *
+ * @var string
+ */
+	var $databaseClassName = 'DATABASE_CONFIG';
+
+/**
+ * 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 = $this->in('Driver:', array('mssql', 'mysql', 'mysqli', 'oracle', 'postgres', 'sqlite'), 'mysql');
+
+			$persistent = $this->in('Persistent Connection?', array('y', 'n'), 'n');
+			if (strtolower($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 (strtolower($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 (strtolower($prefix) == 'n') {
+				$prefix = null;
+			}
+
+			$encoding = '';
+			while ($encoding == '') {
+				$encoding = $this->in('Table encoding?', null, 'n');
+			}
+			if (strtolower($encoding) == 'n') {
+				$encoding = null;
+			}
+
+			$schema = '';
+			if ($driver == 'postgres') {
+				while ($schema == '') {
+					$schema = $this->in('Table schema?', null, 'n');
+				}
+			}
+			if (strtolower($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 (strtolower($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 (strtolower($looksGood) == 'y') {
+			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)) {
+			config('database');
+			$db = new $this->databaseClassName;
+			$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'] = ($info['persistent'] == true) ? 'true' : '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);
+	}
+
+/**
+ * Get a user specified Connection name
+ *
+ * @return void
+ */
+	function getConfig() {
+		App::import('Model', 'ConnectionManager', false);
+
+		$useDbConfig = 'default';
+		$configs = get_class_vars($this->databaseClassName);
+		if (!is_array($configs)) {
+			return $this->execute();
+		}
+
+		$connections = array_keys($configs);
+		if (count($connections) > 1) {
+			$useDbConfig = $this->in(__('Use Database Config', true) .':', $connections, 'default');
+		}
+		return $useDbConfig;
+	}
+}

Added: trunk/src/Web/cake/console/libs/tasks/extract.php
===================================================================
--- trunk/src/Web/cake/console/libs/tasks/extract.php	                        (rev 0)
+++ trunk/src/Web/cake/console/libs/tasks/extract.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,514 @@
+<?php
+/**
+ * Language string extractor
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ * @since         CakePHP(tm) v 1.2.0.5012
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Language string extractor
+ *
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.tasks
+ */
+class ExtractTask extends Shell {
+
+/**
+ * Paths to use when looking for strings
+ *
+ * @var string
+ * @access private
+ */
+	var $__paths = array();
+
+/**
+ * Files from where to extract
+ *
+ * @var array
+ * @access private
+ */
+	var $__files = array();
+
+/**
+ * Merge all domains string into the default.pot file
+ *
+ * @var boolean
+ * @access private
+ */
+	var $__merge = false;
+
+/**
+ * Current file being processed
+ *
+ * @var string
+ * @access private
+ */
+	var $__file = null;
+
+/**
+ * Contains all content waiting to be write
+ *
+ * @var string
+ * @access private
+ */
+	var $__storage = array();
+
+/**
+ * Extracted tokens
+ *
+ * @var array
+ * @access private
+ */
+	var $__tokens = array();
+
+/**
+ * Extracted strings
+ *
+ * @var array
+ * @access private
+ */
+	var $__strings = array();
+
+/**
+ * Destination path
+ *
+ * @var string
+ * @access private
+ */
+	var $__output = null;
+
+/**
+ * Execution method always used for tasks
+ *
+ * @return void
+ * @access private
+ */
+	function execute() {
+		if (isset($this->params['files']) && !is_array($this->params['files'])) {
+			$this->__files = explode(',', $this->params['files']);
+		}
+		if (isset($this->params['paths'])) {
+			$this->__paths = explode(',', $this->params['paths']);
+		} else {
+			$defaultPath = $this->params['working'];
+			$message = sprintf(__("What is the full path you would like to extract?\nExample: %s\n[Q]uit [D]one", true), $this->params['root'] . DS . 'myapp');
+			while (true) {
+				$response = $this->in($message, null, $defaultPath);
+				if (strtoupper($response) === 'Q') {
+					$this->out(__('Extract Aborted', true));
+					$this->_stop();
+				} elseif (strtoupper($response) === 'D') {
+					$this->out();
+					break;
+				} elseif (is_dir($response)) {
+					$this->__paths[] = $response;
+					$defaultPath = 'D';
+				} else {
+					$this->err(__('The directory path you supplied was not found. Please try again.', true));
+				}
+				$this->out();
+			}
+		}
+
+		if (isset($this->params['output'])) {
+			$this->__output = $this->params['output'];
+		} else {
+			$message = sprintf(__("What is the full path you would like to output?\nExample: %s\n[Q]uit", true), $this->__paths[0] . DS . 'locale');
+			while (true) {
+				$response = $this->in($message, null, $this->__paths[0] . DS . 'locale');
+				if (strtoupper($response) === 'Q') {
+					$this->out(__('Extract Aborted', true));
+					$this->_stop();
+				} elseif (is_dir($response)) {
+					$this->__output = $response . DS;
+					break;
+				} else {
+					$this->err(__('The directory path you supplied was not found. Please try again.', true));
+				}
+				$this->out();
+			}
+		}
+
+		if (isset($this->params['merge'])) {
+			$this->__merge = !(strtolower($this->params['merge']) === 'no');
+		} else {
+			$this->out();
+			$response = $this->in(sprintf(__('Would you like to merge all domains strings into the default.pot file?', true)), array('y', 'n'), 'n');
+			$this->__merge = strtolower($response) === 'y';
+		}
+
+		if (empty($this->__files)) {
+			$this->__searchFiles();
+		}
+		$this->__extract();
+	}
+
+/**
+ * Extract text
+ *
+ * @return void
+ * @access private
+ */
+	function __extract() {
+		$this->out();
+		$this->out();
+		$this->out(__('Extracting...', true));
+		$this->hr();
+		$this->out(__('Paths:', true));
+		foreach ($this->__paths as $path) {
+			$this->out('   ' . $path);
+		}
+		$this->out(__('Output Directory: ', true) . $this->__output);
+		$this->hr();
+		$this->__extractTokens();
+		$this->__buildFiles();
+		$this->__writeFiles();
+		$this->__paths = $this->__files = $this->__storage = array();
+		$this->__strings = $this->__tokens = array();
+		$this->out();
+		$this->out(__('Done.', true));
+	}
+
+/**
+ * Show help options
+ *
+ * @return void
+ * @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> <param1> <param2>...', true));
+		$this->out();
+		$this->out(__('Params:', 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(__('   -paths [comma separated list of paths, full path is needed]', true));
+		$this->out(__('   -merge [yes|no]: Merge all domains strings into the default.pot file', 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();
+		$this->out(__('Commands:', true));
+		$this->out(__('   cake i18n extract help: Shows this help message.', true));
+		$this->out();
+	}
+
+/**
+ * Extract tokens out of all files to be processed
+ *
+ * @return void
+ * @access private
+ */
+	function __extractTokens() {
+		foreach ($this->__files as $file) {
+			$this->__file = $file;
+			$this->out(sprintf(__('Processing %s...', true), $file));
+
+			$code = file_get_contents($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(explode("\n", $token[1])) - 1;
+				} else {
+					$lineNumber += count(explode("\n", $token)) - 1;
+				}
+			}
+			unset($allTokens);
+			$this->__parse('__', array('singular'));
+			$this->__parse('__n', array('singular', 'plural'));
+			$this->__parse('__d', array('domain', 'singular'));
+			$this->__parse('__c', array('singular'));
+			$this->__parse('__dc', array('domain', 'singular'));
+			$this->__parse('__dn', array('domain', 'singular', 'plural'));
+			$this->__parse('__dcn', array('domain', 'singular', 'plural'));
+		}
+	}
+
+/**
+ * Parse tokens
+ *
+ * @param string $functionName Function name that indicates translatable string (e.g: '__')
+ * @param array $map Array containing what variables it will find (e.g: domain, singular, plural)
+ * @return void
+ * @access private
+ */
+	function __parse($functionName, $map) {
+		$count = 0;
+		$tokenCount = count($this->__tokens);
+
+		while (($tokenCount - $count) > 1) {
+			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++;
+				}
+
+				$mapCount = count($map);
+				$strings = $this->__getStrings($position, $mapCount);
+
+				if ($mapCount == count($strings)) {
+					extract(array_combine($map, $strings));
+					$domain = isset($domain) ? $domain : 'default';
+					$string = isset($plural) ? $singular . "\0" . $plural : $singular;
+					$this->__strings[$domain][$string][$this->__file][] = $line;
+				} else {
+					$this->__markerError($this->__file, $line, $functionName, $count);
+				}
+			}
+			$count++;
+		}
+	}
+
+/**
+* Get the strings from the position forward
+*
+* @param integer $position Actual position on tokens array
+* @param integer $target Number of strings to extract
+* @return array Strings extracted
+*/
+	function __getStrings($position, $target) {
+		$strings = array();
+		while (count($strings) < $target && ($this->__tokens[$position] == ',' || $this->__tokens[$position][0] == T_CONSTANT_ENCAPSED_STRING)) {
+			$condition1 = ($this->__tokens[$position][0] == T_CONSTANT_ENCAPSED_STRING && $this->__tokens[$position+1] == '.');
+			$condition2 = ($this->__tokens[$position][0] == T_CONSTANT_ENCAPSED_STRING && $this->__tokens[$position+1][0] == T_COMMENT);
+			if ($condition1	|| $condition2) {
+				$string = '';
+				while ($this->__tokens[$position][0] == T_CONSTANT_ENCAPSED_STRING || $this->__tokens[$position][0] == T_COMMENT || $this->__tokens[$position] == '.') {
+					if ($this->__tokens[$position][0] == T_CONSTANT_ENCAPSED_STRING) {
+						$string .= $this->__formatString($this->__tokens[$position][1]);
+					}
+					$position++;
+				}
+				if ($this->__tokens[$position][0] == T_COMMENT || $this->__tokens[$position] == ',' || $this->__tokens[$position] == ')') {
+					$strings[] = $string;
+				}
+			} else if ($this->__tokens[$position][0] == T_CONSTANT_ENCAPSED_STRING) {
+				$strings[] = $this->__formatString($this->__tokens[$position][1]);
+			}
+			$position++;
+		}
+		return $strings;
+	}
+	
+/**
+ * Build the translate template file contents out of obtained strings
+ *
+ * @return void
+ * @access private
+ */
+	function __buildFiles() {
+		foreach ($this->__strings as $domain => $strings) {
+			foreach ($strings as $string => $files) {
+				$occurrences = array();
+				foreach ($files as $file => $lines) {
+					$occurrences[] = $file . ':' . implode(';', $lines);
+				}
+				$occurrences = implode("\n#: ", $occurrences);
+				$header = '#: ' . str_replace($this->__paths, '', $occurrences) . "\n";
+
+				if (strpos($string, "\0") === false) {
+					$sentence = "msgid \"{$string}\"\n";
+					$sentence .= "msgstr \"\"\n\n";
+				} else {
+					list($singular, $plural) = explode("\0", $string);
+					$sentence = "msgid \"{$singular}\"\n";
+					$sentence .= "msgid_plural \"{$plural}\"\n";
+					$sentence .= "msgstr[0] \"\"\n";
+					$sentence .= "msgstr[1] \"\"\n\n";
+				}
+
+				$this->__store($domain, $header, $sentence);
+				if ($domain != 'default' && $this->__merge) {
+					$this->__store('default', $header, $sentence);
+				}
+			}
+		}
+	}
+
+/**
+ * Prepare a file to be stored
+ *
+ * @return void
+ * @access private
+ */
+	function __store($domain, $header, $sentence) {
+		if (!isset($this->__storage[$domain])) {
+			$this->__storage[$domain] = array();
+		}
+		if (!isset($this->__storage[$domain][$sentence])) {
+			$this->__storage[$domain][$sentence] = $header;
+		} else {
+			$this->__storage[$domain][$sentence] .= $header;
+		}
+	}
+
+/**
+ * Write the files that need to be stored
+ *
+ * @return void
+ * @access private
+ */
+	function __writeFiles() {
+		$overwriteAll = false;
+		foreach ($this->__storage as $domain => $sentences) {
+			$output = $this->__writeHeader();
+			foreach ($sentences as $sentence => $header) {
+				$output .= $header . $sentence;
+			}
+
+			$filename = $domain . '.pot';
+			$File = new File($this->__output . $filename);
+			$response = '';
+			while ($overwriteAll === false && $File->exists() && strtoupper($response) !== 'Y') {
+				$this->out();
+				$response = $this->in(sprintf(__('Error: %s already exists in this location. Overwrite? [Y]es, [N]o, [A]ll', true), $filename), array('y', 'n', 'a'), 'y');
+				if (strtoupper($response) === 'N') {
+					$response = '';
+					while ($response == '') {
+						$response = $this->in(sprintf(__("What would you like to name this file?\nExample: %s", true), 'new_' . $filename), null, 'new_' . $filename);
+						$File = new File($this->__output . $response);
+						$filename = $response;
+					}
+				} elseif (strtoupper($response) === 'A') {
+					$overwriteAll = true;
+				}
+			}
+			$File->write($output);
+			$File->close();
+		}
+	}
+
+/**
+ * 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 .= "#\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;
+	}
+
+/**
+ * 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
+ * @return void
+ * @access private
+ */
+	function __markerError($file, $line, $marker, $count) {
+		$this->out(sprintf(__("Invalid marker content in %s:%s\n* %s(", true), $file, $line, $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 files that may contain translateable strings
+ *
+ * @return void
+ * @access private
+ */
+	function __searchFiles() {
+		foreach ($this->__paths as $path) {
+			$Folder = new Folder($path);
+			$files = $Folder->findRecursive('.*\.(php|ctp|thtml|inc|tpl)', true);
+			$this->__files = array_merge($this->__files, $files);
+		}
+	}
+}

Added: trunk/src/Web/cake/console/libs/tasks/fixture.php
===================================================================
--- trunk/src/Web/cake/console/libs/tasks/fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/console/libs/tasks/fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,424 @@
+<?php
+/**
+ * The FixtureTask handles creating and updating fixture files.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.tasks
+ * @since         CakePHP(tm) v 1.3
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+include_once dirname(__FILE__) . DS . 'bake.php';
+/**
+ * Task class for creating and updating fixtures files.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.tasks
+ */
+class FixtureTask extends BakeTask {
+
+/**
+ * Tasks to be loaded by this Task
+ *
+ * @var array
+ * @access public
+ */
+	var $tasks = array('DbConfig', 'Model', 'Template');
+
+/**
+ * path to fixtures directory
+ *
+ * @var string
+ * @access public
+ */
+	var $path = null;
+
+/**
+ * Schema instance
+ *
+ * @var object
+ * @access protected
+ */
+	var $_Schema = null;
+
+/**
+ * Override initialize
+ *
+ * @access public
+ */
+	function __construct(&$dispatch) {
+		parent::__construct($dispatch);
+		$this->path = $this->params['working'] . DS . 'tests' . DS . 'fixtures' . DS;
+	}
+
+/**
+ * Execution method always used for tasks
+ * Handles dispatching to interactive, named, or all processess.
+ *
+ * @access public
+ */
+	function execute() {
+		if (empty($this->args)) {
+			$this->__interactive();
+		}
+
+		if (isset($this->args[0])) {
+			$this->interactive = false;
+			if (!isset($this->connection)) {
+				$this->connection = 'default';
+			}
+			if (strtolower($this->args[0]) == 'all') {
+				return $this->all();
+			}
+			$model = $this->_modelName($this->args[0]);
+			$this->bake($model);
+		}
+	}
+
+/**
+ * Bake All the Fixtures at once.  Will only bake fixtures for models that exist.
+ *
+ * @access public
+ * @return void
+ */
+	function all() {
+		$this->interactive = false;
+		$this->Model->interactive = false;
+		$tables = $this->Model->listAll($this->connection, false);
+		foreach ($tables as $table) {
+			$model = $this->_modelName($table);
+			$this->bake($model);
+		}
+	}
+
+/**
+ * Interactive baking function
+ *
+ * @access private
+ */
+	function __interactive() {
+		$this->DbConfig->interactive = $this->Model->interactive = $this->interactive = true;
+		$this->hr();
+		$this->out(sprintf("Bake Fixture\nPath: %s", $this->path));
+		$this->hr();
+
+		$useDbConfig = $this->connection;
+		if (!isset($this->connection)) {
+			$this->connection = $this->DbConfig->getConfig();
+		}
+		$modelName = $this->Model->getName($this->connection);
+		$useTable = $this->Model->getTable($modelName, $this->connection);
+		$importOptions = $this->importOptions($modelName);
+		$this->bake($modelName, $useTable, $importOptions);
+	}
+
+/**
+ * Interacts with the User to setup an array of import options. For a fixture.
+ *
+ * @param string $modelName Name of model you are dealing with.
+ * @return array Array of import options.
+ * @access public
+ */
+	function importOptions($modelName) {
+		$options = array();
+		$doSchema = $this->in(__('Would you like to import schema for this fixture?', true), array('y', 'n'), 'n');
+		if ($doSchema == 'y') {
+			$options['schema'] = $modelName;
+		}
+		$doRecords = $this->in(__('Would you like to use record importing for this fixture?', true), array('y', 'n'), 'n');
+		if ($doRecords == 'y') {
+			$options['records'] = true;
+		}
+		if ($doRecords == 'n') {
+			$prompt = sprintf(__("Would you like to build this fixture with data from %s's table?", true), $modelName);
+			$fromTable = $this->in($prompt, array('y', 'n'), 'n');
+			if (strtolower($fromTable) == 'y') {
+				$options['fromTable'] = true;
+			}
+		}
+		return $options;
+	}
+
+/**
+ * Assembles and writes a Fixture file
+ *
+ * @param string $model Name of model to bake.
+ * @param string $useTable Name of table to use.
+ * @param array $importOptions Options for var $import
+ * @return string Baked fixture content
+ * @access public
+ */
+	function bake($model, $useTable = false, $importOptions = array()) {
+		if (!class_exists('CakeSchema')) {
+			App::import('Model', 'CakeSchema', false);
+		}
+		$table = $schema = $records = $import = $modelImport = null;
+		$importBits = array();
+
+		if (!$useTable) {
+			$useTable = Inflector::tableize($model);
+		} elseif ($useTable != Inflector::tableize($model)) {
+			$table = $useTable;
+		}
+
+		if (!empty($importOptions)) {
+			if (isset($importOptions['schema'])) {
+				$modelImport = true;
+				$importBits[] = "'model' => '{$importOptions['schema']}'";
+			}
+			if (isset($importOptions['records'])) {
+				$importBits[] = "'records' => true";
+			}
+			if ($this->connection != 'default') {
+				$importBits[] .= "'connection' => '{$this->connection}'";
+			}
+			if (!empty($importBits)) {
+				$import = sprintf("array(%s)", implode(', ', $importBits));
+			}
+		}
+
+		$this->_Schema = new CakeSchema();
+		$data = $this->_Schema->read(array('models' => false, 'connection' => $this->connection));
+
+		if (!isset($data['tables'][$useTable])) {
+			$this->err('Could not find your selected table ' . $useTable);
+			return false;
+		}
+
+		$tableInfo = $data['tables'][$useTable];
+		if (is_null($modelImport)) {
+			$schema = $this->_generateSchema($tableInfo);
+		}
+
+		if (!isset($importOptions['records']) && !isset($importOptions['fromTable'])) {
+			$recordCount = 1;
+			if (isset($this->params['count'])) {
+				$recordCount = $this->params['count'];
+			}
+			$records = $this->_makeRecordString($this->_generateRecords($tableInfo, $recordCount));
+		}
+		if (isset($this->params['records']) || isset($importOptions['fromTable'])) {
+			$records = $this->_makeRecordString($this->_getRecordsFromTable($model, $useTable));
+		}
+		$out = $this->generateFixtureFile($model, compact('records', 'table', 'schema', 'import', 'fields'));
+		return $out;
+	}
+
+/**
+ * Generate the fixture file, and write to disk
+ *
+ * @param string $model name of the model being generated
+ * @param string $fixture Contents of the fixture file.
+ * @return string Content saved into fixture file.
+ * @access public
+ */
+	function generateFixtureFile($model, $otherVars) {
+		$defaults = array('table' => null, 'schema' => null, 'records' => null, 'import' => null, 'fields' => null);
+		$vars = array_merge($defaults, $otherVars);
+
+		$path = $this->getPath();
+		$filename = Inflector::underscore($model) . '_fixture.php';
+
+		$this->Template->set('model', $model);
+		$this->Template->set($vars);
+		$content = $this->Template->generate('classes', 'fixture');
+
+		$this->out("\nBaking test fixture for $model...");
+		$this->createFile($path . $filename, $content);
+		return $content;
+	}
+
+/**
+ * Get the path to the fixtures.
+ *
+ * @return void
+ */
+	function getPath() {
+		$path = $this->path;
+		if (isset($this->plugin)) {
+			$path = $this->_pluginPath($this->plugin) . 'tests' . DS . 'fixtures' . DS;
+		}
+		return $path;
+	}
+
+/**
+ * Generates a string representation of a schema.
+ *
+ * @param array $table Table schema array
+ * @return string fields definitions
+ * @access protected
+ */
+	function _generateSchema($tableInfo) {
+		$schema = $this->_Schema->generateTable('f', $tableInfo);
+		return substr($schema, 10, -2);
+	}
+
+/**
+ * Generate String representation of Records
+ *
+ * @param array $table Table schema array
+ * @return array Array of records to use in the fixture.
+ * @access protected
+ */
+	function _generateRecords($tableInfo, $recordCount = 1) {
+		$records = array();
+		for ($i = 0; $i < $recordCount; $i++) {
+			$record = array();
+			foreach ($tableInfo as $field => $fieldInfo) {
+				if (empty($fieldInfo['type'])) {
+					continue;
+				}
+				switch ($fieldInfo['type']) {
+					case 'integer':
+					case 'float':
+						$insert = $i + 1;
+					break;
+					case 'string':
+					case 'binary':
+						$isPrimaryUuid = (
+							isset($fieldInfo['key']) && strtolower($fieldInfo['key']) == 'primary' &&
+							isset($fieldInfo['length']) && $fieldInfo['length'] == 36
+						);
+						if ($isPrimaryUuid) {
+							$insert = String::uuid();
+						} else {
+							$insert = "Lorem ipsum dolor sit amet";
+							if (!empty($fieldInfo['length'])) {
+								 $insert = substr($insert, 0, (int)$fieldInfo['length'] - 2);
+							}
+						}
+					break;
+					case 'timestamp':
+						$insert = time();
+					break;
+					case 'datetime':
+						$insert = date('Y-m-d H:i:s');
+					break;
+					case 'date':
+						$insert = date('Y-m-d');
+					break;
+					case 'time':
+						$insert = date('H:i:s');
+					break;
+					case 'boolean':
+						$insert = 1;
+					break;
+					case 'text':
+						$insert = "Lorem ipsum dolor sit amet, aliquet feugiat.";
+						$insert .= " Convallis morbi fringilla gravida,";
+						$insert .= " phasellus feugiat dapibus velit nunc, pulvinar eget sollicitudin";
+						$insert .= " venenatis cum nullam, vivamus ut a sed, mollitia lectus. Nulla";
+						$insert .= " vestibulum massa neque ut et, id hendrerit sit,";
+						$insert .= " feugiat in taciti enim proin nibh, tempor dignissim, rhoncus";
+						$insert .= " duis vestibulum nunc mattis convallis.";
+					break;
+				}
+				$record[$field] = $insert;
+			}
+			$records[] = $record;
+		}
+		return $records;
+	}
+
+/**
+ * Convert a $records array into a a string.
+ *
+ * @param array $records Array of records to be converted to string
+ * @return string A string value of the $records array.
+ * @access protected
+ */
+	function _makeRecordString($records) {
+		$out = "array(\n";
+		foreach ($records as $record) {
+			$values = array();
+			foreach ($record as $field => $value) {
+				$val = var_export($value, true);
+				$values[] = "\t\t\t'$field' => $val";
+			}
+			$out .= "\t\tarray(\n";
+			$out .= implode(",\n", $values);
+			$out .= "\n\t\t),\n";
+		}
+		$out .= "\t)";
+		return $out;
+	}
+
+/**
+ * Interact with the user to get a custom SQL condition and use that to extract data
+ * to build a fixture.
+ *
+ * @param string $modelName name of the model to take records from.
+ * @param string $useTable Name of table to use.
+ * @return array Array of records.
+ * @access protected
+ */
+	function _getRecordsFromTable($modelName, $useTable = null) {
+		if ($this->interactive) {
+			$condition = null;
+			$prompt = __("Please provide a SQL fragment to use as conditions\nExample: WHERE 1=1 LIMIT 10", true);
+			while (!$condition) {
+				$condition = $this->in($prompt, null, 'WHERE 1=1 LIMIT 10');
+			}
+		} else {
+			$condition = 'WHERE 1=1 LIMIT ' . (isset($this->params['count']) ? $this->params['count'] : 10);
+		}
+		App::import('Model', 'Model', false);
+		$modelObject =& new Model(array('name' => $modelName, 'table' => $useTable, 'ds' => $this->connection));
+		$records = $modelObject->find('all', array(
+			'conditions' => $condition,
+			'recursive' => -1
+		));
+		$db =& ConnectionManager::getDataSource($modelObject->useDbConfig);
+		$schema = $modelObject->schema(true);
+		$out = array();
+		foreach ($records as $record) {
+			$row = array();
+			foreach ($record[$modelObject->alias] as $field => $value) {
+				if ($schema[$field]['type'] === 'boolean') {
+					$value = (int)(bool)$value;
+				}
+				$row[$field] = $value;
+			}
+			$out[] = $row;
+		}
+		return $out;
+	}
+
+/**
+ * Displays help contents
+ *
+ * @access public
+ */
+	function help() {
+		$this->hr();
+		$this->out("Usage: cake bake fixture <arg1> <params>");
+		$this->hr();
+		$this->out('Arguments:');
+		$this->out();
+		$this->out("<name>");
+		$this->out("\tName of the fixture to bake. Can use Plugin.name");
+		$this->out("\tas a shortcut for plugin baking.");
+		$this->out();
+		$this->out('Commands:');
+		$this->out("\nfixture <name>\n\tbakes fixture with specified name.");
+		$this->out("\nfixture all\n\tbakes all fixtures.");
+		$this->out();
+		$this->out('Parameters:');
+		$this->out("\t-count       When using generated data, the number of records to include in the fixture(s).");
+		$this->out("\t-connection  Which database configuration to use for baking.");
+		$this->out("\t-plugin      CamelCased name of plugin to bake fixtures for.");
+		$this->out("\t-records     Used with -count and <name>/all commands to pull [n] records from the live tables");
+		$this->out("\t             Where [n] is either -count or the default of 10.");
+		$this->out();
+		$this->_stop();
+	}
+}

Added: trunk/src/Web/cake/console/libs/tasks/model.php
===================================================================
--- trunk/src/Web/cake/console/libs/tasks/model.php	                        (rev 0)
+++ trunk/src/Web/cake/console/libs/tasks/model.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,938 @@
+<?php
+/**
+ * The ModelTask handles creating and updating models files.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.tasks
+ * @since         CakePHP(tm) v 1.2
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+include_once dirname(__FILE__) . DS . 'bake.php';
+
+/**
+ * Task class for creating and updating model files.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.tasks
+ */
+class ModelTask extends BakeTask {
+
+/**
+ * path to MODELS directory
+ *
+ * @var string
+ * @access public
+ */
+	var $path = MODELS;
+
+/**
+ * tasks
+ *
+ * @var array
+ * @access public
+ */
+	var $tasks = array('DbConfig', 'Fixture', 'Test', 'Template');
+
+/**
+ * Tables to skip when running all()
+ *
+ * @var array
+ * @access protected
+ */
+	var $skipTables = array('i18n');
+
+/**
+ * Holds tables found on connection.
+ *
+ * @var array
+ * @access protected
+ */
+	var $_tables = array();
+
+/**
+ * Holds validation method map.
+ *
+ * @var array
+ * @access protected
+ */
+	var $_validations = array();
+
+/**
+ * Execution method always used for tasks
+ *
+ * @access public
+ */
+	function execute() {
+		App::import('Model', 'Model', false);
+
+		if (empty($this->args)) {
+			$this->__interactive();
+		}
+
+		if (!empty($this->args[0])) {
+			$this->interactive = false;
+			if (!isset($this->connection)) {
+				$this->connection = 'default';
+			}
+			if (strtolower($this->args[0]) == 'all') {
+				return $this->all();
+			}
+			$model = $this->_modelName($this->args[0]);
+			$object = $this->_getModelObject($model);
+			if ($this->bake($object, false)) {
+				if ($this->_checkUnitTest()) {
+					$this->bakeFixture($model);
+					$this->bakeTest($model);
+				}
+			}
+		}
+	}
+
+/**
+ * Bake all models at once.
+ *
+ * @return void
+ */
+	function all() {
+		$this->listAll($this->connection, false);
+		$unitTestExists = $this->_checkUnitTest();
+		foreach ($this->_tables as $table) {
+			if (in_array($table, $this->skipTables)) {
+				continue;
+			}
+			$modelClass = Inflector::classify($table);
+			$this->out(sprintf(__('Baking %s', true), $modelClass));
+			$object = $this->_getModelObject($modelClass);
+			if ($this->bake($object, false) && $unitTestExists) {
+				$this->bakeFixture($modelClass);
+				$this->bakeTest($modelClass);
+			}
+		}
+	}
+
+/**
+ * Get a model object for a class name.
+ *
+ * @param string $className Name of class you want model to be.
+ * @return object Model instance
+ */
+	function &_getModelObject($className, $table = null) {
+		if (!$table) {
+			$table = Inflector::tableize($className);
+		}
+		$object =& new Model(array('name' => $className, 'table' => $table, 'ds' => $this->connection));
+		return $object;
+	}
+
+/**
+ * Generate a key value list of options and a prompt.
+ *
+ * @param array $options Array of options to use for the selections. indexes must start at 0
+ * @param string $prompt Prompt to use for options list.
+ * @param integer $default The default option for the given prompt.
+ * @return result of user choice.
+ */
+	function inOptions($options, $prompt = null, $default = null) {
+		$valid = false;
+		$max = count($options);
+		while (!$valid) {
+			foreach ($options as $i => $option) {
+				$this->out($i + 1 .'. ' . $option);
+			}
+			if (empty($prompt)) {
+				$prompt = __('Make a selection from the choices above', true);
+			}
+			$choice = $this->in($prompt, null, $default);
+			if (intval($choice) > 0 && intval($choice) <= $max) {
+				$valid = true;
+			}
+		}
+		return $choice - 1;
+	}
+
+/**
+ * Handles interactive baking
+ *
+ * @access private
+ */
+	function __interactive() {
+		$this->hr();
+		$this->out(sprintf("Bake Model\nPath: %s", $this->path));
+		$this->hr();
+		$this->interactive = true;
+
+		$primaryKey = 'id';
+		$validate = $associations = array();
+
+		if (empty($this->connection)) {
+			$this->connection = $this->DbConfig->getConfig();
+		}
+		$currentModelName = $this->getName();
+		$useTable = $this->getTable($currentModelName);
+		$db =& ConnectionManager::getDataSource($this->connection);
+		$fullTableName = $db->fullTableName($useTable);
+
+		if (in_array($useTable, $this->_tables)) {
+			$tempModel = new Model(array('name' => $currentModelName, 'table' => $useTable, 'ds' => $this->connection));
+			$fields = $tempModel->schema(true);
+			if (!array_key_exists('id', $fields)) {
+				$primaryKey = $this->findPrimaryKey($fields);
+			}
+		} else {
+			$this->err(sprintf(__('Table %s does not exist, cannot bake a model without a table.', true), $useTable));
+			$this->_stop();
+			return false;
+		}
+		$displayField = $tempModel->hasField(array('name', 'title'));
+		if (!$displayField) {
+			$displayField = $this->findDisplayField($tempModel->schema());
+		}
+
+		$prompt = __("Would you like to supply validation criteria \nfor the fields in your model?", true);
+		$wannaDoValidation = $this->in($prompt, array('y','n'), 'y');
+		if (array_search($useTable, $this->_tables) !== false && strtolower($wannaDoValidation) == 'y') {
+			$validate = $this->doValidation($tempModel);
+		}
+
+		$prompt = __("Would you like to define model associations\n(hasMany, hasOne, belongsTo, etc.)?", true);
+		$wannaDoAssoc = $this->in($prompt, array('y','n'), 'y');
+		if (strtolower($wannaDoAssoc) == 'y') {
+			$associations = $this->doAssociations($tempModel);
+		}
+
+		$this->out();
+		$this->hr();
+		$this->out(__('The following Model will be created:', true));
+		$this->hr();
+		$this->out("Name:       " . $currentModelName);
+
+		if ($this->connection !== 'default') {
+			$this->out(sprintf(__("DB Config:  %s", true), $this->connection));
+		}
+		if ($fullTableName !== Inflector::tableize($currentModelName)) {
+			$this->out(sprintf(__("DB Table:   %s", true), $fullTableName));
+		}
+		if ($primaryKey != 'id') {
+			$this->out(sprintf(__("Primary Key: %s", true), $primaryKey));
+		}
+		if (!empty($validate)) {
+			$this->out(sprintf(__("Validation: %s", true), print_r($validate, true)));
+		}
+		if (!empty($associations)) {
+			$this->out(__("Associations:", true));
+			$assocKeys = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');
+			foreach ($assocKeys as $assocKey) {
+				$this->_printAssociation($currentModelName, $assocKey, $associations);
+			}
+		}
+
+		$this->hr();
+		$looksGood = $this->in(__('Look okay?', true), array('y','n'), 'y');
+
+		if (strtolower($looksGood) == 'y') {
+			$vars = compact('associations', 'validate', 'primaryKey', 'useTable', 'displayField');
+			$vars['useDbConfig'] = $this->connection;
+			if ($this->bake($currentModelName, $vars)) {
+				if ($this->_checkUnitTest()) {
+					$this->bakeFixture($currentModelName, $useTable);
+					$this->bakeTest($currentModelName, $useTable, $associations);
+				}
+			}
+		} else {
+			return false;
+		}
+	}
+
+/**
+ * Print out all the associations of a particular type
+ *
+ * @param string $modelName Name of the model relations belong to.
+ * @param string $type Name of association you want to see. i.e. 'belongsTo'
+ * @param string $associations Collection of associations.
+ * @access protected
+ * @return void
+ */
+	function _printAssociation($modelName, $type, $associations) {
+		if (!empty($associations[$type])) {
+			for ($i = 0; $i < count($associations[$type]); $i++) {
+				$out = "\t" . $modelName . ' ' . $type . ' ' . $associations[$type][$i]['alias'];
+				$this->out($out);
+			}
+		}
+	}
+
+/**
+ * Finds a primary Key in a list of fields.
+ *
+ * @param array $fields Array of fields that might have a primary key.
+ * @return string Name of field that is a primary key.
+ * @access public
+ */
+	function findPrimaryKey($fields) {
+		foreach ($fields as $name => $field) {
+			if (isset($field['key']) && $field['key'] == 'primary') {
+				break;
+			}
+		}
+		return $this->in(__('What is the primaryKey?', true), null, $name);
+	}
+
+/**
+ * interact with the user to find the displayField value for a model.
+ *
+ * @param array $fields Array of fields to look for and choose as a displayField
+ * @return mixed Name of field to use for displayField or false if the user declines to choose
+ */
+	function findDisplayField($fields) {
+		$fieldNames = array_keys($fields);
+		$prompt = __("A displayField could not be automatically detected\nwould you like to choose one?", true);
+		$continue = $this->in($prompt, array('y', 'n'));
+		if (strtolower($continue) == 'n') {
+			return false;
+		}
+		$prompt = __('Choose a field from the options above:', true);
+		$choice = $this->inOptions($fieldNames, $prompt);
+		return $fieldNames[$choice];
+	}
+
+/**
+ * Handles Generation and user interaction for creating validation.
+ *
+ * @param object $model Model to have validations generated for.
+ * @return array $validate Array of user selected validations.
+ * @access public
+ */
+	function doValidation(&$model) {
+		if (!is_object($model)) {
+			return false;
+		}
+		$fields = $model->schema();
+
+		if (empty($fields)) {
+			return false;
+		}
+		$validate = array();
+		$this->initValidations();
+		foreach ($fields as $fieldName => $field) {
+			$validation = $this->fieldValidation($fieldName, $field, $model->primaryKey);
+			if (!empty($validation)) {
+				$validate[$fieldName] = $validation;
+			}
+		}
+		return $validate;
+	}
+
+/**
+ * Populate the _validations array
+ *
+ * @return void
+ */
+	function initValidations() {
+		$options = $choices = array();
+		if (class_exists('Validation')) {
+			$parent = get_class_methods(get_parent_class('Validation'));
+			$options = get_class_methods('Validation');
+			$options = array_diff($options, $parent);
+		}
+		sort($options);
+		$default = 1;
+		foreach ($options as $key => $option) {
+			if ($option{0} != '_' && strtolower($option) != 'getinstance') {
+				$choices[$default] = strtolower($option);
+				$default++;
+			}
+		}
+		$choices[$default] = 'none'; // Needed since index starts at 1
+		$this->_validations = $choices;
+		return $choices;
+	}
+
+/**
+ * Does individual field validation handling.
+ *
+ * @param string $fieldName Name of field to be validated.
+ * @param array $metaData metadata for field
+ * @return array Array of validation for the field.
+ */
+	function fieldValidation($fieldName, $metaData, $primaryKey = 'id') {
+		$defaultChoice = count($this->_validations);
+		$validate = $alreadyChosen = array();
+
+		$anotherValidator = 'y';
+		while ($anotherValidator == 'y') {
+			if ($this->interactive) {
+				$this->out();
+				$this->out(sprintf(__('Field: %s', true), $fieldName));
+				$this->out(sprintf(__('Type: %s', true), $metaData['type']));
+				$this->hr();
+				$this->out(__('Please select one of the following validation options:', true));
+				$this->hr();
+			}
+
+			$prompt = '';
+			for ($i = 1; $i < $defaultChoice; $i++) {
+				$prompt .= $i . ' - ' . $this->_validations[$i] . "\n";
+			}
+			$prompt .=  sprintf(__("%s - Do not do any validation on this field.\n", true), $defaultChoice);
+			$prompt .= __("... or enter in a valid regex validation string.\n", true);
+
+			$methods = array_flip($this->_validations);
+			$guess = $defaultChoice;
+			if ($metaData['null'] != 1 && !in_array($fieldName, array($primaryKey, 'created', 'modified', 'updated'))) {
+				if ($fieldName == 'email') {
+					$guess = $methods['email'];
+				} elseif ($metaData['type'] == 'string' && $metaData['length'] == 36) {
+					$guess = $methods['uuid'];
+				} elseif ($metaData['type'] == 'string') {
+					$guess = $methods['notempty'];
+				} elseif ($metaData['type'] == 'integer') {
+					$guess = $methods['numeric'];
+				} elseif ($metaData['type'] == 'boolean') {
+					$guess = $methods['boolean'];
+				} elseif ($metaData['type'] == 'date') {
+					$guess = $methods['date'];
+				} elseif ($metaData['type'] == 'time') {
+					$guess = $methods['time'];
+				}
+			}
+
+			if ($this->interactive === true) {
+				$choice = $this->in($prompt, null, $guess);
+				if (in_array($choice, $alreadyChosen)) {
+					$this->out(__("You have already chosen that validation rule,\nplease choose again", true));
+					continue;
+				}
+				if (!isset($this->_validations[$choice]) && is_numeric($choice)) {
+					$this->out(__('Please make a valid selection.', true));
+					continue;
+				}
+				$alreadyChosen[] = $choice;
+			} else {
+				$choice = $guess;
+			}
+
+			if (isset($this->_validations[$choice])) {
+				$validatorName = $this->_validations[$choice];
+			} else {
+				$validatorName = Inflector::slug($choice);
+			}
+
+			if ($choice != $defaultChoice) {
+				if (is_numeric($choice) && isset($this->_validations[$choice])) {
+					$validate[$validatorName] = $this->_validations[$choice];
+				} else {
+					$validate[$validatorName] = $choice;
+				}
+			}
+			if ($this->interactive == true && $choice != $defaultChoice) {
+				$anotherValidator = $this->in(__('Would you like to add another validation rule?', true), array('y', 'n'), 'n');
+			} else {
+				$anotherValidator = 'n';
+			}
+		}
+		return $validate;
+	}
+
+/**
+ * Handles associations
+ *
+ * @param object $model
+ * @return array $assocaitons
+ * @access public
+ */
+	function doAssociations(&$model) {
+		if (!is_object($model)) {
+			return false;
+		}
+		if ($this->interactive === true) {
+			$this->out(__('One moment while the associations are detected.', true));
+		}
+
+		$fields = $model->schema(true);
+		if (empty($fields)) {
+			return false;
+		}
+
+		if (empty($this->_tables)) {
+			$this->_tables = $this->getAllTables();
+		}
+
+		$associations = array(
+			'belongsTo' => array(), 'hasMany' => array(), 'hasOne'=> array(), 'hasAndBelongsToMany' => array()
+		);
+		$possibleKeys = array();
+
+		$associations = $this->findBelongsTo($model, $associations);
+		$associations = $this->findHasOneAndMany($model, $associations);
+		$associations = $this->findHasAndBelongsToMany($model, $associations);
+
+		if ($this->interactive !== true) {
+			unset($associations['hasOne']);
+		}
+
+		if ($this->interactive === true) {
+			$this->hr();
+			if (empty($associations)) {
+				$this->out(__('None found.', true));
+			} else {
+				$this->out(__('Please confirm the following associations:', true));
+				$this->hr();
+				$associations = $this->confirmAssociations($model, $associations);
+			}
+			$associations = $this->doMoreAssociations($model, $associations);
+		}
+		return $associations;
+	}
+
+/**
+ * Find belongsTo relations and add them to the associations list.
+ *
+ * @param object $model Model instance of model being generated.
+ * @param array $associations Array of inprogress associations
+ * @return array $associations with belongsTo added in.
+ */
+	function findBelongsTo(&$model, $associations) {
+		$fields = $model->schema(true);
+		foreach ($fields as $fieldName => $field) {
+			$offset = strpos($fieldName, '_id');
+			if ($fieldName != $model->primaryKey && $fieldName != 'parent_id' && $offset !== false) {
+				$tmpModelName = $this->_modelNameFromKey($fieldName);
+				$associations['belongsTo'][] = array(
+					'alias' => $tmpModelName,
+					'className' => $tmpModelName,
+					'foreignKey' => $fieldName,
+				);
+			} elseif ($fieldName == 'parent_id') {
+				$associations['belongsTo'][] = array(
+					'alias' => 'Parent' . $model->name,
+					'className' => $model->name,
+					'foreignKey' => $fieldName,
+				);
+			}
+		}
+		return $associations;
+	}
+
+/**
+ * Find the hasOne and HasMany relations and add them to associations list
+ *
+ * @param object $model Model instance being generated
+ * @param array $associations Array of inprogress associations
+ * @return array $associations with hasOne and hasMany added in.
+ */
+	function findHasOneAndMany(&$model, $associations) {
+		$foreignKey = $this->_modelKey($model->name);
+		foreach ($this->_tables as $otherTable) {
+			$tempOtherModel = $this->_getModelObject($this->_modelName($otherTable), $otherTable);
+			$modelFieldsTemp = $tempOtherModel->schema(true);
+
+			$pattern = '/_' . preg_quote($model->table, '/') . '|' . preg_quote($model->table, '/') . '_/';
+			$possibleJoinTable = preg_match($pattern , $otherTable);
+			if ($possibleJoinTable == true) {
+				continue;
+			}
+			foreach ($modelFieldsTemp as $fieldName => $field) {
+				$assoc = false;
+				if ($fieldName != $model->primaryKey && $fieldName == $foreignKey) {
+					$assoc = array(
+						'alias' => $tempOtherModel->name,
+						'className' => $tempOtherModel->name,
+						'foreignKey' => $fieldName
+					);
+				} elseif ($otherTable == $model->table && $fieldName == 'parent_id') {
+					$assoc = array(
+						'alias' => 'Child' . $model->name,
+						'className' => $model->name,
+						'foreignKey' => $fieldName
+					);
+				}
+				if ($assoc) {
+					$associations['hasOne'][] = $assoc;
+					$associations['hasMany'][] = $assoc;
+				}
+
+			}
+		}
+		return $associations;
+	}
+
+/**
+ * Find the hasAndBelongsToMany relations and add them to associations list
+ *
+ * @param object $model Model instance being generated
+ * @param array $associations Array of inprogress associations
+ * @return array $associations with hasAndBelongsToMany added in.
+ */
+	function findHasAndBelongsToMany(&$model, $associations) {
+		$foreignKey = $this->_modelKey($model->name);
+		foreach ($this->_tables as $otherTable) {
+			$tempOtherModel = $this->_getModelObject($this->_modelName($otherTable), $otherTable);
+			$modelFieldsTemp = $tempOtherModel->schema(true);
+
+			$offset = strpos($otherTable, $model->table . '_');
+			$otherOffset = strpos($otherTable, '_' . $model->table);
+
+			if ($offset !== false) {
+				$offset = strlen($model->table . '_');
+				$habtmName = $this->_modelName(substr($otherTable, $offset));
+				$associations['hasAndBelongsToMany'][] = array(
+					'alias' => $habtmName,
+					'className' => $habtmName,
+					'foreignKey' => $foreignKey,
+					'associationForeignKey' => $this->_modelKey($habtmName),
+					'joinTable' => $otherTable
+				);
+			} elseif ($otherOffset !== false) {
+				$habtmName = $this->_modelName(substr($otherTable, 0, $otherOffset));
+				$associations['hasAndBelongsToMany'][] = array(
+					'alias' => $habtmName,
+					'className' => $habtmName,
+					'foreignKey' => $foreignKey,
+					'associationForeignKey' => $this->_modelKey($habtmName),
+					'joinTable' => $otherTable
+				);
+			}
+		}
+		return $associations;
+	}
+
+/**
+ * Interact with the user and confirm associations.
+ *
+ * @param array $model Temporary Model instance.
+ * @param array $associations Array of associations to be confirmed.
+ * @return array Array of confirmed associations
+ */
+	function confirmAssociations(&$model, $associations) {
+		foreach ($associations as $type => $settings) {
+			if (!empty($associations[$type])) {
+				$count = count($associations[$type]);
+				$response = 'y';
+				foreach ($associations[$type] as $i => $assoc) {
+					$prompt = "{$model->name} {$type} {$assoc['alias']}?";
+					$response = $this->in($prompt, array('y','n'), 'y');
+
+					if ('n' == strtolower($response)) {
+						unset($associations[$type][$i]);
+					} elseif ($type == 'hasMany') {
+						unset($associations['hasOne'][$i]);
+					}
+				}
+				$associations[$type] = array_merge($associations[$type]);
+			}
+		}
+		return $associations;
+	}
+
+/**
+ * Interact with the user and generate additional non-conventional associations
+ *
+ * @param object $model Temporary model instance
+ * @param array $associations Array of associations.
+ * @return array Array of associations.
+ */
+	function doMoreAssociations($model, $associations) {
+		$prompt = __('Would you like to define some additional model associations?', true);
+		$wannaDoMoreAssoc = $this->in($prompt, array('y','n'), 'n');
+		$possibleKeys = $this->_generatePossibleKeys();
+		while (strtolower($wannaDoMoreAssoc) == 'y') {
+			$assocs = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');
+			$this->out(__('What is the association type?', true));
+			$assocType = intval($this->inOptions($assocs, __('Enter a number',true)));
+
+			$this->out(__("For the following options be very careful to match your setup exactly.\nAny 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 == 0) {
+				$showKeys = $possibleKeys[$model->table];
+				$suggestedForeignKey = $this->_modelKey($alias);
+			} else {
+				$otherTable = Inflector::tableize($className);
+				if (in_array($otherTable, $this->_tables)) {
+					if ($assocType < 3) {
+						$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));
+				$foreignKey = $this->inOptions($showKeys, __('What is the foreignKey?', true));
+				$foreignKey = $showKeys[intval($foreignKey)];
+			}
+			if (!isset($foreignKey)) {
+				$foreignKey = $this->in(__('What is the foreignKey? Specify your own.', true), null, $suggestedForeignKey);
+			}
+			if ($assocType == 3) {
+				$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 == 3) {
+				$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;
+	}
+
+/**
+ * Finds all possible keys to use on custom associations.
+ *
+ * @return array array of tables and possible keys
+ */
+	function _generatePossibleKeys() {
+		$possible = array();
+		foreach ($this->_tables as $otherTable) {
+			$tempOtherModel = & new Model(array('table' => $otherTable, 'ds' => $this->connection));
+			$modelFieldsTemp = $tempOtherModel->schema(true);
+			foreach ($modelFieldsTemp as $fieldName => $field) {
+				if ($field['type'] == 'integer' || $field['type'] == 'string') {
+					$possible[$otherTable][] = $fieldName;
+				}
+			}
+		}
+		return $possible;
+	}
+
+/**
+ * Assembles and writes a Model file.
+ *
+ * @param mixed $name Model name or object
+ * @param mixed $data if array and $name is not an object assume bake data, otherwise boolean.
+ * @access private
+ */
+	function bake($name, $data = array()) {
+		if (is_object($name)) {
+			if ($data == false) {
+				$data = $associations = array();
+				$data['associations'] = $this->doAssociations($name, $associations);
+				$data['validate'] = $this->doValidation($name);
+			}
+			$data['primaryKey'] = $name->primaryKey;
+			$data['useTable'] = $name->table;
+			$data['useDbConfig'] = $name->useDbConfig;
+			$data['name'] = $name = $name->name;
+		} else {
+			$data['name'] = $name;
+		}
+		$defaults = array('associations' => array(), 'validate' => array(), 'primaryKey' => 'id',
+			'useTable' => null, 'useDbConfig' => 'default', 'displayField' => null);
+		$data = array_merge($defaults, $data);
+
+		$this->Template->set($data);
+		$this->Template->set('plugin', Inflector::camelize($this->plugin));
+		$out = $this->Template->generate('classes', 'model');
+
+		$path = $this->getPath();
+		$filename = $path . Inflector::underscore($name) . '.php';
+		$this->out("\nBaking model class for $name...");
+		$this->createFile($filename, $out);
+		ClassRegistry::flush();
+		return $out;
+	}
+
+/**
+ * Assembles and writes a unit test file
+ *
+ * @param string $className Model class name
+ * @access private
+ */
+	function bakeTest($className) {
+		$this->Test->interactive = $this->interactive;
+		$this->Test->plugin = $this->plugin;
+		$this->Test->connection = $this->connection;
+		return $this->Test->bake('Model', $className);
+	}
+
+/**
+ * outputs the a list of possible models or controllers from database
+ *
+ * @param string $useDbConfig Database configuration name
+ * @access public
+ */
+	function listAll($useDbConfig = null) {
+		$this->_tables = $this->getAllTables($useDbConfig);
+
+		if ($this->interactive === true) {
+			$this->out(__('Possible Models based on your current database:', true));
+			$this->_modelNames = array();
+			$count = count($this->_tables);
+			for ($i = 0; $i < $count; $i++) {
+				$this->_modelNames[] = $this->_modelName($this->_tables[$i]);
+				$this->out($i + 1 . ". " . $this->_modelNames[$i]);
+			}
+		}
+		return $this->_tables;
+	}
+
+/**
+ * Interact with the user to determine the table name of a particular model
+ *
+ * @param string $modelName Name of the model you want a table for.
+ * @param string $useDbConfig Name of the database config you want to get tables from.
+ * @return void
+ */
+	function getTable($modelName, $useDbConfig = null) {
+		if (!isset($useDbConfig)) {
+			$useDbConfig = $this->connection;
+		}
+		App::import('Model', 'ConnectionManager', false);
+
+		$db =& ConnectionManager::getDataSource($useDbConfig);
+		$useTable = Inflector::tableize($modelName);
+		$fullTableName = $db->fullTableName($useTable, false);
+		$tableIsGood = false;
+
+		if (array_search($useTable, $this->_tables) === false) {
+			$this->out();
+			$this->out(sprintf(__("Given your model named '%s',\nCake would expect a database table named '%s'", true), $modelName, $fullTableName));
+			$tableIsGood = $this->in(__('Do you want to use this table?', true), array('y','n'), 'y');
+		}
+		if (strtolower($tableIsGood) == 'n') {
+			$useTable = $this->in(__('What is the name of the table?', true));
+		}
+		return $useTable;
+	}
+
+/**
+ * Get an Array of all the tables in the supplied connection
+ * will halt the script if no tables are found.
+ *
+ * @param string $useDbConfig Connection name to scan.
+ * @return array Array of tables in the database.
+ */
+	function getAllTables($useDbConfig = null) {
+		if (!isset($useDbConfig)) {
+			$useDbConfig = $this->connection;
+		}
+		App::import('Model', 'ConnectionManager', false);
+
+		$tables = array();
+		$db =& ConnectionManager::getDataSource($useDbConfig);
+		$db->cacheSources = false;
+		$usePrefix = empty($db->config['prefix']) ? '' : $db->config['prefix'];
+		if ($usePrefix) {
+			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();
+		}
+		return $tables;
+	}
+
+/**
+ * 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 = null) {
+		$this->listAll($useDbConfig);
+
+		$enteredModel = '';
+
+		while ($enteredModel == '') {
+			$enteredModel = $this->in(__("Enter a number from the list above,\ntype 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,\nor 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('Arguments:');
+		$this->out();
+		$this->out("<name>");
+		$this->out("\tName of the model to bake. Can use Plugin.name");
+		$this->out("\tas a shortcut for plugin baking.");
+		$this->out();
+		$this->out('Params:');
+		$this->out();
+		$this->out('-connection <config>');
+		$this->out("\tset db config <config>. uses 'default' if none is specified");
+		$this->out();
+		$this->out('Commands:');
+		$this->out();
+		$this->out("model");
+		$this->out("\tbakes model in interactive mode.");
+		$this->out();
+		$this->out("model <name>");
+		$this->out("\tbakes model file with no associations or validation");
+		$this->out();
+		$this->out("model all");
+		$this->out("\tbakes all model files with associations and validation");
+		$this->out();
+		$this->_stop();
+	}
+
+/**
+ * Interact with FixtureTask to automatically bake fixtures when baking models.
+ *
+ * @param string $className Name of class to bake fixture for
+ * @param string $useTable Optional table name for fixture to use.
+ * @access public
+ * @return void
+ * @see FixtureTask::bake
+ */
+	function bakeFixture($className, $useTable = null) {
+		$this->Fixture->interactive = $this->interactive;
+		$this->Fixture->connection = $this->connection;
+		$this->Fixture->plugin = $this->plugin;
+		$this->Fixture->bake($className, $useTable);
+	}
+}

Added: trunk/src/Web/cake/console/libs/tasks/plugin.php
===================================================================
--- trunk/src/Web/cake/console/libs/tasks/plugin.php	                        (rev 0)
+++ trunk/src/Web/cake/console/libs/tasks/plugin.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,247 @@
+<?php
+/**
+ * The Plugin Task handles creating an empty plugin, ready to be used
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.tasks
+ * @since         CakePHP(tm) v 1.2
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 . 'console' . DS . 'templates' . DS . 'skel') === true) {
+				$this->params['skel'] = CAKE_CORE_INCLUDE_PATH . DS . CAKE . 'console' . DS . 'templates' . DS . 'skel';
+			}
+		}
+		$plugin = null;
+
+		if (isset($this->args[0])) {
+			$plugin = Inflector::camelize($this->args[0]);
+			$pluginPath = $this->_pluginPath($plugin);
+			$this->Dispatch->shiftArgs();
+			if (is_dir($pluginPath)) {
+				$this->out(sprintf(__('Plugin: %s', true), $plugin));
+				$this->out(sprintf(__('Path: %s', true), $pluginPath));
+			} elseif (isset($this->args[0])) {
+				$this->err(sprintf(__('%s in path %s not found.', true), $plugin, $pluginPath));
+				$this->_stop();
+			} else {
+				$this->__interactive($plugin);
+			}
+		} else {
+			return $this->__interactive();
+		}
+
+		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 = $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();
+				return $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 . Inflector::underscore($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);
+
+		$pathOptions = App::path('plugins');
+		if (count($pathOptions) > 1) {
+			$this->findPath($pathOptions);
+		}
+		$this->hr();
+		$this->out(sprintf(__("Plugin Name: %s", true),  $plugin));
+		$this->out(sprintf(__("Plugin Directory: %s", true), $this->path . $pluginPath));
+		$this->hr();
+
+		$looksGood = $this->in(__('Look okay?', true), array('y', 'n', 'q'), 'y');
+
+		if (strtolower($looksGood) == 'y') {
+			$verbose = $this->in(__('Do you want verbose output?', true), array('y', 'n'), 'n');
+
+			$Folder =& new Folder($this->path . $pluginPath);
+			$directories = array(
+				'config' . DS . 'schema',
+				'models' . DS . 'behaviors',
+				'models' . DS . 'datasources',
+				'controllers' . DS . 'components',
+				'libs',
+				'views' . DS . 'helpers',
+				'tests' . DS . 'cases' . DS . 'components',
+				'tests' . DS . 'cases' . DS . 'helpers',
+				'tests' . DS . 'cases' . DS . 'behaviors',
+				'tests' . DS . 'cases' . DS . 'controllers',
+				'tests' . DS . 'cases' . DS . 'models',
+				'tests' . DS . 'groups',
+				'tests' . DS . 'fixtures',
+				'vendors',
+				'vendors' . DS . 'shells' . DS . 'tasks',
+				'webroot'
+			);
+
+			foreach ($directories as $directory) {
+				$dirPath = $this->path . $pluginPath . DS . $directory;
+				$Folder->create($dirPath);
+				$File =& new File($dirPath . DS . 'empty', true);
+			}
+
+			if (strtolower($verbose) == 'y') {
+				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;
+	}
+
+/**
+ * find and change $this->path to the user selection
+ *
+ * @return void
+ */
+	function findPath($pathOptions) {
+		$valid = false;
+		$max = count($pathOptions);
+		while (!$valid) {
+			foreach ($pathOptions as $i => $option) {
+				$this->out($i + 1 .'. ' . $option);
+			}
+			$prompt = __('Choose a plugin path from the paths above.', true);
+			$choice = $this->in($prompt);
+			if (intval($choice) > 0 && intval($choice) <= $max) {
+				$valid = true;
+			}
+		}
+		$this->path = $pathOptions[$choice - 1];
+	}
+
+/**
+ * Help
+ *
+ * @return void
+ * @access public
+ */
+	function help() {
+		$this->hr();
+		$this->out("Usage: cake bake plugin <arg1> <arg2>...");
+		$this->hr();
+		$this->out('Commands:');
+		$this->out();
+		$this->out("plugin <name>");
+		$this->out("\tbakes plugin directory structure");
+		$this->out();
+		$this->out("plugin <name> model");
+		$this->out("\tbakes model. Run 'cake bake model help' for more info.");
+		$this->out();
+		$this->out("plugin <name> controller");
+		$this->out("\tbakes controller. Run 'cake bake controller help' for more info.");
+		$this->out();
+		$this->out("plugin <name> view");
+		$this->out("\tbakes view. Run 'cake bake view help' for more info.");
+		$this->out();
+		$this->_stop();
+	}
+}

Added: trunk/src/Web/cake/console/libs/tasks/project.php
===================================================================
--- trunk/src/Web/cake/console/libs/tasks/project.php	                        (rev 0)
+++ trunk/src/Web/cake/console/libs/tasks/project.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,371 @@
+<?php
+/**
+ * The Project Task handles creating the base application
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.bake
+ * @since         CakePHP(tm) v 1.2
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+/**
+ * Task class for creating new project apps and plugins
+ *
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.tasks
+ */
+class ProjectTask extends Shell {
+
+/**
+ * configs path (used in testing).
+ *
+ * @var string
+ */
+	var $configPath = null;
+
+/**
+ * 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];
+			}
+		}
+
+		if ($project) {
+			$this->Dispatch->parseParams(array('-app', $project));
+			$project = $this->params['working'];
+		}
+
+		if (empty($this->params['skel'])) {
+			$this->params['skel'] = '';
+			if (is_dir(CAKE . 'console' . DS . 'templates' . DS . 'skel') === true) {
+				$this->params['skel'] = CAKE . 'console' . DS . 'templates' . DS . 'skel';
+			}
+		}
+
+		while (!$project) {
+			$prompt = __("What is the full path for this app including the app directory name?\n Example:", true);
+			$default = $this->params['working'] . DS . 'myapp';
+			$project = $this->in($prompt . $default, null, $default);
+		}
+
+		if ($project) {
+			$response = false;
+			while ($response == false && is_dir($project) === true && file_exists($project . 'config' . 'core.php')) {
+				$prompt = sprintf(__('A project already exists in this location: %s Overwrite?', true), $project);
+				$response = $this->in($prompt, 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'));
+			}
+
+			if ($this->securityCipherSeed($path) === true ) {
+				$this->out(__('Random seed created for \'Security.cipherSeed\'', true));
+			} else {
+				$this->err(sprintf(__('Unable to generate random seed for \'Security.cipherSeed\', 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', true));
+		$this->out(__("Skel Directory: ", true) . $skel);
+		$this->out(__("Will be copied to: ", true) . $path);
+		$this->hr();
+
+		$looksGood = $this->in(__('Look okay?', true), array('y', 'n', 'q'), 'y');
+
+		if (strtolower($looksGood) == 'y') {
+			$verbose = $this->in(__('Do you want verbose output?', true), array('y', 'n'), 'n');
+
+			$Folder = new Folder($skel);
+			if (!empty($this->params['empty'])) {
+				$skip = array();
+			}
+			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(sprintf(__(" '%s' could not be created properly", true), $app));
+				return false;
+			}
+
+			if (strtolower($verbose) == 'y') {
+				foreach ($Folder->messages() as $message) {
+					$this->out($message);
+				}
+			}
+
+			return true;
+		} elseif (strtolower($looksGood) == 'q') {
+			$this->out(__('Bake Aborted.', true));
+		} 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;
+		$source = CAKE . 'console' . DS . 'templates' . DS .'default' . DS . 'views' . DS . 'home.ctp';
+		include($source);
+		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')) {
+				require LIBS . 'security.php';
+			}
+			$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 'Security.cipherSeed'
+	 *
+	 * @param string $path Project path
+	 * @return boolean Success
+	 * @access public
+	 */
+		function securityCipherSeed($path) {
+			$File =& new File($path . 'config' . DS . 'core.php');
+			$contents = $File->read();
+			if (preg_match('/([\\t\\x20]*Configure::write\\(\\\'Security.cipherSeed\\\',[\\t\\x20\'A-z0-9]*\\);)/', $contents, $match)) {
+				if (!class_exists('Security')) {
+					require LIBS . 'security.php';
+				}
+				$string = substr(bin2hex(Security::generateAuthKey()), 0, 30);
+				$result = str_replace($match[0], "\t" . 'Configure::write(\'Security.cipherSeed\', \''.$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)) {
+				$root = strpos(CAKE_CORE_INCLUDE_PATH, '/') === 0 ? " DS . '" : "'";
+				$result = str_replace($match[0], "\t\tdefine('CAKE_CORE_INCLUDE_PATH', " . $root . str_replace(DS, "' . DS . '", trim(CAKE_CORE_INCLUDE_PATH, DS)) . "');", $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', " . $root . str_replace(DS, "' . DS . '", trim(CAKE_CORE_INCLUDE_PATH, DS)) . "');", $contents);
+				if (!$File->write($result)) {
+					return false;
+				}
+			} else {
+				return false;
+			}
+			return true;
+		}
+	}
+
+/**
+ * Enables Configure::read('Routing.prefixes') in /app/config/core.php
+ *
+ * @param string $name Name to use as admin routing
+ * @return boolean Success
+ * @access public
+ */
+	function cakeAdmin($name) {
+		$path = (empty($this->configPath)) ? CONFIGS : $this->configPath;
+		$File =& new File($path . 'core.php');
+		$contents = $File->read();
+		if (preg_match('%([/\\t\\x20]*Configure::write\(\'Routing.prefixes\',[\\t\\x20\'a-z,\)\(]*\\);)%', $contents, $match)) {
+			$result = str_replace($match[0], "\t" . 'Configure::write(\'Routing.prefixes\', array(\''.$name.'\'));', $contents);
+			if ($File->write($result)) {
+				Configure::write('Routing.prefixes', array($name));
+				return true;
+			} else {
+				return false;
+			}
+		} else {
+			return false;
+		}
+	}
+
+/**
+ * Checks for Configure::read('Routing.prefixes') and forces user to input it if not enabled
+ *
+ * @return string Admin route to use
+ * @access public
+ */
+	function getPrefix() {
+		$admin = '';
+		$prefixes = Configure::read('Routing.prefixes');
+		if (!empty($prefixes)) {
+			if (count($prefixes) == 1) {
+				return $prefixes[0] . '_';
+			}
+			if ($this->interactive) {
+				$this->out();
+				$this->out(__('You have more than one routing prefix configured', true));
+			}
+			$options = array();
+			foreach ($prefixes as $i => $prefix) {
+				$options[] = $i + 1;
+				if ($this->interactive) {
+					$this->out($i + 1 . '. ' . $prefix);
+				}
+			}
+			$selection = $this->in(__('Please choose a prefix to bake with.', true), $options, 1);
+			return $prefixes[$selection - 1] . '_';
+		}
+		if ($this->interactive) {
+			$this->hr();
+			$this->out('You need to enable Configure::write(\'Routing.prefixes\',array(\'admin\')) in /app/config/core.php to use prefix routing.');
+			$this->out(__('What would you like the prefix route to be?', true));
+			$this->out(__('Example: www.example.com/admin/controller', true));
+			while ($admin == '') {
+				$admin = $this->in(__("Enter a routing prefix:", true), null, 'admin');
+			}
+			if ($this->cakeAdmin($admin) !== true) {
+				$this->out(__('Unable to write to /app/config/core.php.', true));
+				$this->out('You need to enable Configure::write(\'Routing.prefixes\',array(\'admin\')) in /app/config/core.php to use prefix routing.');
+				$this->_stop();
+			}
+			return $admin . '_';
+		}
+		return '';
+	}
+
+/**
+ * Help
+ *
+ * @return void
+ * @access public
+ */
+	function help() {
+		$this->hr();
+		$this->out("Usage: cake bake project <arg1>");
+		$this->hr();
+		$this->out('Commands:');
+		$this->out();
+		$this->out("project <name>");
+		$this->out("\tbakes app directory structure.");
+		$this->out("\tif <name> begins with '/' path is absolute.");
+		$this->out();
+		$this->_stop();
+	}
+
+}

Added: trunk/src/Web/cake/console/libs/tasks/template.php
===================================================================
--- trunk/src/Web/cake/console/libs/tasks/template.php	                        (rev 0)
+++ trunk/src/Web/cake/console/libs/tasks/template.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,210 @@
+<?php
+/**
+ * Template Task can generate templated output Used in other Tasks
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.console.libs.tasks
+ * @since         CakePHP(tm) v 1.3
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+class TemplateTask extends Shell {
+
+/**
+ * variables to add to template scope
+ *
+ * @var array
+ */
+	var $templateVars = array();
+
+/**
+ * Paths to look for templates on.
+ * Contains a list of $theme => $path
+ *
+ * @var array
+ */
+	var $templatePaths = array();
+
+/**
+ * Initialize callback.  Setup paths for the template task.
+ *
+ * @access public
+ * @return void
+ */
+	function initialize() {
+		$this->templatePaths = $this->_findThemes();
+	}
+
+/**
+ * Find the paths to all the installed shell themes in the app.
+ *
+ * Bake themes are directories not named `skel` inside a `vendors/shells/templates` path.
+ *
+ * @return array Array of bake themes that are installed.
+ */
+	function _findThemes() {
+		$paths = App::path('shells');
+		$core = array_pop($paths);
+		$separator = DS === '/' ? '/' : '\\\\';
+		$core = preg_replace('#libs' . $separator . '$#', '', $core);
+
+		$Folder =& new Folder($core . 'templates' . DS . 'default');
+		$contents = $Folder->read();
+		$themeFolders = $contents[0];
+
+		$plugins = App::objects('plugin');
+		foreach ($plugins as $plugin) {
+			$paths[] = $this->_pluginPath($plugin) . 'vendors' . DS . 'shells' . DS;
+		}
+		$paths[] = $core;
+
+		// TEMPORARY TODO remove when all paths are DS terminated
+		foreach ($paths as $i => $path) {
+			$paths[$i] = rtrim($path, DS) . DS;
+		}
+
+		$themes = array();
+		foreach ($paths as $path) {
+			$Folder =& new Folder($path . 'templates', false);
+			$contents = $Folder->read();
+			$subDirs = $contents[0];
+			foreach ($subDirs as $dir) {
+				if (empty($dir) || preg_match('@^skel$|_skel$@', $dir)) {
+					continue;
+				}
+				$Folder =& new Folder($path . 'templates' . DS . $dir);
+				$contents = $Folder->read();
+				$subDirs = $contents[0];
+				if (array_intersect($contents[0], $themeFolders)) {
+					$templateDir = $path . 'templates' . DS . $dir . DS;
+					$themes[$dir] = $templateDir;
+				}
+			}
+		}
+		return $themes;
+	}
+
+/**
+ * Set variable values to the template scope
+ *
+ * @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
+ */
+	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;
+		}
+		$this->templateVars = $data + $this->templateVars;
+	}
+
+/**
+ * Runs the template
+ *
+ * @param string $directory directory / type of thing you want
+ * @param string $filename template name
+ * @param string $vars Additional vars to set to template scope.
+ * @access public
+ * @return contents of generated code template
+ */
+	function generate($directory, $filename, $vars = null) {
+		if ($vars !== null) {
+			$this->set($vars);
+		}
+		if (empty($this->templatePaths)) {
+			$this->initialize();
+		}
+		$themePath = $this->getThemePath();
+		$templateFile = $this->_findTemplate($themePath, $directory, $filename);
+		if ($templateFile) {
+			extract($this->templateVars);
+			ob_start();
+			ob_implicit_flush(0);
+			include($templateFile);
+			$content = ob_get_clean();
+			return $content;
+		}
+		return '';
+	}
+
+/**
+ * Find the theme name for the current operation.
+ * If there is only one theme in $templatePaths it will be used.
+ * If there is a -theme param in the cli args, it will be used.
+ * If there is more than one installed theme user interaction will happen
+ *
+ * @return string returns the path to the selected theme.
+ */
+	function getThemePath() {
+		if (count($this->templatePaths) == 1) {
+			$paths = array_values($this->templatePaths);
+			return $paths[0];
+		}
+		if (!empty($this->params['theme']) && isset($this->templatePaths[$this->params['theme']])) {
+			return $this->templatePaths[$this->params['theme']];
+		}
+
+		$this->hr();
+		$this->out(__('You have more than one set of templates installed.', true));
+		$this->out(__('Please choose the template set you wish to use:', true));
+		$this->hr();
+
+		$i = 1;
+		$indexedPaths = array();
+		foreach ($this->templatePaths as $key => $path) {
+			$this->out($i . '. ' . $key);
+			$indexedPaths[$i] = $path;
+			$i++;
+		}
+		$index = $this->in(__('Which bake theme would you like to use?', true), range(1, $i - 1), 1);
+		$themeNames = array_keys($this->templatePaths);
+		$this->Dispatch->params['theme'] = $themeNames[$index - 1];
+		return $indexedPaths[$index];
+	}
+
+/**
+ * Find a template inside a directory inside a path.
+ * Will scan all other theme dirs if the template is not found in the first directory.
+ *
+ * @param string $path The initial path to look for the file on. If it is not found fallbacks will be used.
+ * @param string $directory Subdirectory to look for ie. 'views', 'objects'
+ * @param string $filename lower_case_underscored filename you want.
+ * @access public
+ * @return string filename will exit program if template is not found.
+ */
+	function _findTemplate($path, $directory, $filename) {
+		$themeFile = $path . $directory . DS . $filename . '.ctp';
+		if (file_exists($themeFile)) {
+			return $themeFile;
+		}
+		foreach ($this->templatePaths as $path) {
+			$templatePath = $path . $directory . DS . $filename . '.ctp';
+			if (file_exists($templatePath)) {
+				return $templatePath;
+			}
+		}
+		$this->err(sprintf(__('Could not find template for %s', true), $filename));
+		return false;
+	}
+}

Added: trunk/src/Web/cake/console/libs/tasks/test.php
===================================================================
--- trunk/src/Web/cake/console/libs/tasks/test.php	                        (rev 0)
+++ trunk/src/Web/cake/console/libs/tasks/test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,469 @@
+<?php
+/**
+ * The TestTask handles creating and updating test files.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.tasks
+ * @since         CakePHP(tm) v 1.3
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+include_once dirname(__FILE__) . DS . 'bake.php';
+
+/**
+ * Task class for creating and updating test files.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.tasks
+ */
+class TestTask extends BakeTask {
+
+/**
+ * path to TESTS directory
+ *
+ * @var string
+ * @access public
+ */
+	var $path = TESTS;
+
+/**
+ * Tasks used.
+ *
+ * @var array
+ * @access public
+ */
+	var $tasks = array('Template');
+
+/**
+ * class types that methods can be generated for
+ *
+ * @var array
+ * @access public
+ */
+	var $classTypes =  array('Model', 'Controller', 'Component', 'Behavior', 'Helper');
+
+/**
+ * Internal list of fixtures that have been added so far.
+ *
+ * @var string
+ * @access protected
+ */
+	var $_fixtures = array();
+
+
+/**
+ * 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) {
+			$type = Inflector::underscore($this->args[0]);
+			if ($this->bake($type, $this->args[1])) {
+				$this->out('done');
+			}
+		}
+	}
+
+/**
+ * Handles interactive baking
+ *
+ * @access private
+ */
+	function __interactive($type = null) {
+		$this->interactive = true;
+		$this->hr();
+		$this->out(__('Bake Tests', true));
+		$this->out(sprintf(__("Path: %s", true), $this->path));
+		$this->hr();
+
+		if ($type) {
+			$type = Inflector::camelize($type);
+			if (!in_array($type, $this->classTypes)) {
+				$this->error(sprintf('Incorrect type provided.  Please choose one of %s', implode(', ', $this->classTypes)));
+			}
+		} else {
+			$type = $this->getObjectType();
+		}
+		$className = $this->getClassName($type);
+		return $this->bake($type, $className);
+	}
+
+/**
+ * Completes final steps for generating data to create test case.
+ *
+ * @param string $type Type of object to bake test case for ie. Model, Controller
+ * @param string $className the 'cake name' for the class ie. Posts for the PostsController
+ * @access public
+ */
+	function bake($type, $className) {
+		if ($this->typeCanDetectFixtures($type) && $this->isLoadableClass($type, $className)) {
+			$this->out(__('Bake is detecting possible fixtures..', true));
+			$testSubject =& $this->buildTestSubject($type, $className);
+			$this->generateFixtureList($testSubject);
+		} elseif ($this->interactive) {
+			$this->getUserFixtures();
+		}
+		$fullClassName = $this->getRealClassName($type, $className);
+
+		$methods = array();
+		if (class_exists($fullClassName)) {
+			$methods = $this->getTestableMethods($fullClassName);
+		}
+		$mock = $this->hasMockClass($type, $fullClassName);
+		$construction = $this->generateConstructor($type, $fullClassName);
+
+		$plugin = null;
+		if ($this->plugin) {
+			$plugin = $this->plugin . '.';
+		}
+
+		$this->Template->set('fixtures', $this->_fixtures);
+		$this->Template->set('plugin', $plugin);
+		$this->Template->set(compact('className', 'methods', 'type', 'fullClassName', 'mock', 'construction'));
+		$out = $this->Template->generate('classes', 'test');
+
+		$filename = $this->testCaseFileName($type, $className);
+		$made = $this->createFile($filename, $out);
+		if ($made) {
+			return $out;
+		}
+		return false;
+	}
+
+/**
+ * Interact with the user and get their chosen type. Can exit the script.
+ *
+ * @return string Users chosen type.
+ * @access public
+ */
+	function getObjectType() {
+		$this->hr();
+		$this->out(__("Select an object type:", true));
+		$this->hr();
+
+		$keys = array();
+		foreach ($this->classTypes as $key => $option) {
+			$this->out(++$key . '. ' . $option);
+			$keys[] = $key;
+		}
+		$keys[] = 'q';
+		$selection = $this->in(__("Enter the type of object to bake a test for or (q)uit", true), $keys, 'q');
+		if ($selection == 'q') {
+			return $this->_stop();
+		}
+		return $this->classTypes[$selection - 1];
+	}
+
+/**
+ * Get the user chosen Class name for the chosen type
+ *
+ * @param string $objectType Type of object to list classes for i.e. Model, Controller.
+ * @return string Class name the user chose.
+ * @access public
+ */
+	function getClassName($objectType) {
+		$type = strtolower($objectType);
+		if ($this->plugin) {
+			$path = Inflector::pluralize($type);
+			if ($type === 'helper') {
+				$path = 'views' . DS . $path;
+			} elseif ($type === 'component') {
+				$path = 'controllers' . DS . $path;
+			} elseif ($type === 'behavior') {
+				$path = 'models' . DS . $path;
+			}
+			$options = App::objects($type, App::pluginPath($this->plugin) . $path, false);
+		} else {
+			$options = App::objects($type);
+		}
+		$this->out(sprintf(__('Choose a %s class', true), $objectType));
+		$keys = array();
+		foreach ($options as $key => $option) {
+			$this->out(++$key . '. ' . $option);
+			$keys[] = $key;
+		}
+		$selection = $this->in(__('Choose an existing class, or enter the name of a class that does not exist', true));
+		if (isset($options[$selection - 1])) {
+			return $options[$selection - 1];
+		}
+		return $selection;
+	}
+
+/**
+ * Checks whether the chosen type can find its own fixtures.
+ * Currently only model, and controller are supported
+ * 
+ * @param string $type The Type of object you are generating tests for eg. controller
+ * @param string $className the Classname of the class the test is being generated for.
+ * @return boolean
+ * @access public
+ */
+	function typeCanDetectFixtures($type) {
+		$type = strtolower($type);
+		return ($type == 'controller' || $type == 'model');
+	}
+
+/**
+ * Check if a class with the given type is loaded or can be loaded.
+ *
+ * @param string $type The Type of object you are generating tests for eg. controller
+ * @param string $className the Classname of the class the test is being generated for.
+ * @return boolean
+ * @access public
+ */
+	function isLoadableClass($type, $class) {
+		return App::import($type, $class);
+	}
+
+/**
+ * Construct an instance of the class to be tested.
+ * So that fixtures can be detected
+ *
+ * @param string $type The Type of object you are generating tests for eg. controller
+ * @param string $class the Classname of the class the test is being generated for.
+ * @return object And instance of the class that is going to be tested.
+ * @access public
+ */
+	function &buildTestSubject($type, $class) {
+		ClassRegistry::flush();
+		App::import($type, $class);
+		$class = $this->getRealClassName($type, $class);
+		if (strtolower($type) == 'model') {
+			$instance =& ClassRegistry::init($class);
+		} else {
+			$instance =& new $class();
+		}
+		return $instance;
+	}
+
+/**
+ * Gets the real class name from the cake short form.
+ *
+ * @param string $type The Type of object you are generating tests for eg. controller
+ * @param string $class the Classname of the class the test is being generated for.
+ * @return string Real classname
+ * @access public
+ */
+	function getRealClassName($type, $class) {
+		if (strtolower($type) == 'model') {
+			return $class;
+		}
+		return $class . $type;
+	}
+
+/**
+ * Get methods declared in the class given.
+ * No parent methods will be returned
+ *
+ * @param string $className Name of class to look at.
+ * @return array Array of method names.
+ * @access public
+ */
+	function getTestableMethods($className) {
+		$classMethods = get_class_methods($className);
+		$parentMethods = get_class_methods(get_parent_class($className));
+		$thisMethods = array_diff($classMethods, $parentMethods);
+		$out = array();
+		foreach ($thisMethods as $method) {
+			if (substr($method, 0, 1) != '_' && $method != strtolower($className)) {
+				$out[] = $method;
+			}
+		}
+		return $out;
+	}
+
+/**
+ * Generate the list of fixtures that will be required to run this test based on
+ * loaded models.
+ *
+ * @param object $subject The object you want to generate fixtures for.
+ * @return array Array of fixtures to be included in the test.
+ * @access public
+ */
+	function generateFixtureList(&$subject) {
+		$this->_fixtures = array();
+		if (is_a($subject, 'Model')) {
+			$this->_processModel($subject);
+		} elseif (is_a($subject, 'Controller')) {
+			$this->_processController($subject);
+		}
+		return array_values($this->_fixtures);
+	}
+
+/**
+ * Process a model recursively and pull out all the
+ * model names converting them to fixture names.
+ *
+ * @param Model $subject A Model class to scan for associations and pull fixtures off of.
+ * @return void
+ * @access protected
+ */
+	function _processModel(&$subject) {
+		$this->_addFixture($subject->name);
+		$associated = $subject->getAssociated();
+		foreach ($associated as $alias => $type) {
+			$className = $subject->{$alias}->name;
+			if (!isset($this->_fixtures[$className])) {
+				$this->_processModel($subject->{$alias});
+			}
+			if ($type == 'hasAndBelongsToMany') {
+				$joinModel = Inflector::classify($subject->hasAndBelongsToMany[$alias]['joinTable']);
+				if (!isset($this->_fixtures[$joinModel])) {
+					$this->_processModel($subject->{$joinModel});
+				}
+			}
+		}
+	}
+
+/**
+ * Process all the models attached to a controller
+ * and generate a fixture list.
+ *
+ * @param Controller $subject A controller to pull model names off of.
+ * @return void
+ * @access protected
+ */
+	function _processController(&$subject) {
+		$subject->constructClasses();
+		$models = array(Inflector::classify($subject->name));
+		if (!empty($subject->uses)) {
+			$models = $subject->uses;
+		}
+		foreach ($models as $model) {
+			$this->_processModel($subject->{$model});
+		}
+	}
+
+/**
+ * Add classname to the fixture list.
+ * Sets the app. or plugin.plugin_name. prefix.
+ *
+ * @param string $name Name of the Model class that a fixture might be required for.
+ * @return void
+ * @access protected
+ */
+	function _addFixture($name) {
+		$parent = get_parent_class($name);
+		$prefix = 'app.';
+		if (strtolower($parent) != 'appmodel' && strtolower(substr($parent, -8)) == 'appmodel') {
+			$pluginName = substr($parent, 0, strlen($parent) -8);
+			$prefix = 'plugin.' . Inflector::underscore($pluginName) . '.';
+		}
+		$fixture = $prefix . Inflector::underscore($name);
+		$this->_fixtures[$name] = $fixture;
+	}
+
+/**
+ * Interact with the user to get additional fixtures they want to use.
+ *
+ * @return array Array of fixtures the user wants to add.
+ * @access public
+ */
+	function getUserFixtures() {
+		$proceed = $this->in(__('Bake could not detect fixtures, would you like to add some?', true), array('y','n'), 'n');
+		$fixtures = array();
+		if (strtolower($proceed) == 'y') {
+			$fixtureList = $this->in(__("Please provide a comma separated list of the fixtures names you'd like to use.\nExample: 'app.comment, app.post, plugin.forums.post'", true));
+			$fixtureListTrimmed = str_replace(' ', '', $fixtureList);
+			$fixtures = explode(',', $fixtureListTrimmed);
+		}
+		$this->_fixtures = array_merge($this->_fixtures, $fixtures);
+		return $fixtures;
+	}
+
+/**
+ * Is a mock class required for this type of test?
+ * Controllers require a mock class.
+ *
+ * @param string $type The type of object tests are being generated for eg. controller.
+ * @return boolean
+ * @access public
+ */
+	function hasMockClass($type) {
+		$type = strtolower($type);
+		return $type == 'controller';
+	}
+
+/**
+ * Generate a constructor code snippet for the type and classname
+ *
+ * @param string $type The Type of object you are generating tests for eg. controller
+ * @param string $className the Classname of the class the test is being generated for.
+ * @return string Constructor snippet for the thing you are building.
+ * @access public
+ */
+	function generateConstructor($type, $fullClassName) {
+		$type = strtolower($type);
+		if ($type == 'model') {
+			return "ClassRegistry::init('$fullClassName');\n";
+		}
+		if ($type == 'controller') {
+			$className = substr($fullClassName, 0, strlen($fullClassName) - 10);
+			return "new Test$fullClassName();\n\t\t\$this->{$className}->constructClasses();\n";
+		}
+		return "new $fullClassName();\n";
+	}
+
+/**
+ * Make the filename for the test case. resolve the suffixes for controllers
+ * and get the plugin path if needed.
+ *
+ * @param string $type The Type of object you are generating tests for eg. controller
+ * @param string $className the Classname of the class the test is being generated for.
+ * @return string filename the test should be created on.
+ * @access public
+ */
+	function testCaseFileName($type, $className) {
+		$path = $this->getPath();;
+		$path .= 'cases' . DS . strtolower($type) . 's' . DS;
+		if (strtolower($type) == 'controller') {
+			$className = $this->getRealClassName($type, $className);
+		}
+		return $path . Inflector::underscore($className) . '.test.php';
+	}
+
+/**
+ * Show help file.
+ *
+ * @return void
+ * @access public
+ */
+	function help() {
+		$this->hr();
+		$this->out("Usage: cake bake test <type> <class>");
+		$this->hr();
+		$this->out('Commands:');
+		$this->out("");
+		$this->out("test model post\n\tbakes a test case for the post model.");
+		$this->out("");
+		$this->out("test controller comments\n\tbakes a test case for the comments controller.");
+		$this->out("");
+		$this->out('Arguments:');
+		$this->out("\t<type>   Can be any of the following 'controller', 'model', 'helper',\n\t'component', 'behavior'.");
+		$this->out("\t<class>  Any existing class for the chosen type.");
+		$this->out("");
+		$this->out("Parameters:");
+		$this->out("\t-plugin  CamelCased name of plugin to bake tests for.");
+		$this->out("");
+		$this->_stop();
+	}
+}

Added: trunk/src/Web/cake/console/libs/tasks/view.php
===================================================================
--- trunk/src/Web/cake/console/libs/tasks/view.php	                        (rev 0)
+++ trunk/src/Web/cake/console/libs/tasks/view.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,491 @@
+<?php
+/**
+ * The View Tasks handles creating and updating view files.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.tasks
+ * @since         CakePHP(tm) v 1.2
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Controller', 'Controller', false);
+include_once dirname(__FILE__) . DS . 'bake.php';
+
+/**
+ * Task class for creating and updating view files.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.tasks
+ */
+class ViewTask extends BakeTask {
+
+/**
+ * Tasks to be loaded by this Task
+ *
+ * @var array
+ * @access public
+ */
+	var $tasks = array('Project', 'Controller', 'DbConfig', 'Template');
+
+/**
+ * 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');
+
+/**
+ * An array of action names that don't require templates.  These
+ * actions will not emit errors when doing bakeActions()
+ *
+ * @var array
+ * @access public
+ */
+	var $noTemplateActions = array('delete');
+
+/**
+ * Override initialize
+ *
+ * @access public
+ */
+	function initialize() {
+	}
+
+/**
+ * Execution method always used for tasks
+ *
+ * @access public
+ */
+	function execute() {
+		if (empty($this->args)) {
+			$this->__interactive();
+		}
+		if (empty($this->args[0])) {
+			return;
+		}
+		if (!isset($this->connection)) {
+			$this->connection = 'default';
+		}
+		$controller = $action = $alias = null;
+		$this->controllerName = $this->_controllerName($this->args[0]);
+		$this->controllerPath = $this->_controllerPath($this->controllerName);
+
+		$this->Project->interactive = false;
+		if (strtolower($this->args[0]) == 'all') {
+			return $this->all();
+		}
+
+		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 ($action) {
+			return $this->bake($action, true);
+		}
+
+		$vars = $this->__loadController();
+		$methods = $this->_methodsToBake();
+
+		foreach ($methods as $method) {
+			$content = $this->getContent($method, $vars);
+			if ($content) {
+				$this->bake($method, $content);
+			}
+		}
+	}
+
+/**
+ * Get a list of actions that can / should have views baked for them.
+ *
+ * @return array Array of action names that should be baked
+ */
+	function _methodsToBake() {
+		$methods =  array_diff(
+			array_map('strtolower', get_class_methods($this->controllerName . 'Controller')),
+			array_map('strtolower', get_class_methods('appcontroller'))
+		);
+		$scaffoldActions = false;
+		if (empty($methods)) {
+			$scaffoldActions = true;
+			$methods = $this->scaffoldActions;
+		}
+		$adminRoute = $this->Project->getPrefix();
+		foreach ($methods as $i => $method) {
+			if ($adminRoute && isset($this->params['admin'])) {
+				if ($scaffoldActions) {
+					$methods[$i] = $adminRoute . $method;
+					continue;
+				} elseif (strpos($method, $adminRoute) === false) {
+					unset($methods[$i]);
+				}
+			}
+			if ($method[0] === '_' || $method == strtolower($this->controllerName . 'Controller')) {
+				unset($methods[$i]);
+			}
+		}
+		return $methods;
+	}
+
+/**
+ * Bake All views for All controllers.
+ *
+ * @return void
+ */
+	function all() {
+		$this->Controller->interactive = false;
+		$tables = $this->Controller->listAll($this->connection, false);
+
+		$actions = null;
+		if (isset($this->args[1])) {
+			$actions = array($this->args[1]);
+		}
+		$this->interactive = false;
+		foreach ($tables as $table) {
+			$model = $this->_modelName($table);
+			$this->controllerName = $this->_controllerName($model);
+			$this->controllerPath = Inflector::underscore($this->controllerName);
+			if (App::import('Model', $model)) {
+				$vars = $this->__loadController();
+				if (!$actions) {
+					$actions = $this->_methodsToBake();
+				}
+				$this->bakeActions($actions, $vars);
+				$actions = null;
+			}
+		}
+	}
+
+/**
+ * Handles interactive baking
+ *
+ * @access private
+ */
+	function __interactive() {
+		$this->hr();
+		$this->out(sprintf("Bake View\nPath: %s", $this->path));
+		$this->hr();
+
+		$this->DbConfig->interactive = $this->Controller->interactive = $this->interactive = true;
+
+		if (empty($this->connection)) {
+			$this->connection = $this->DbConfig->getConfig();
+		}
+
+		$this->Controller->connection = $this->connection;
+		$this->controllerName = $this->Controller->getName();
+
+		$this->controllerPath = strtolower(Inflector::underscore($this->controllerName));
+
+		$prompt = sprintf(__("Would you like bake to build your views interactively?\nWarning: Choosing no will overwrite %s views if it exist.", true),  $this->controllerName);
+		$interactive = $this->in($prompt, array('y', 'n'), 'n');
+
+		if (strtolower($interactive) == 'n') {
+			$this->interactive = false;
+		}
+
+		$prompt = __("Would you like to create some CRUD views\n(index, add, view, edit) for this controller?\nNOTE: Before doing so, you'll need to create your controller\nand model classes (including associated models).", true);
+		$wannaDoScaffold = $this->in($prompt, array('y','n'), 'y');
+
+		$wannaDoAdmin = $this->in(__("Would you like to create the views for admin routing?", true), array('y','n'), 'n');
+
+		if (strtolower($wannaDoScaffold) == 'y' || strtolower($wannaDoAdmin) == 'y') {
+			$vars = $this->__loadController();
+			if (strtolower($wannaDoScaffold) == 'y') {
+				$actions = $this->scaffoldActions;
+				$this->bakeActions($actions, $vars);
+			}
+			if (strtolower($wannaDoAdmin) == 'y') {
+				$admin = $this->Project->getPrefix();
+				$regularActions = $this->scaffoldActions;
+				$adminActions = array();
+				foreach ($regularActions as $action) {
+					$adminActions[] = $admin . $action;
+				}
+				$this->bakeActions($adminActions, $vars);
+			}
+			$this->hr();
+			$this->out();
+			$this->out(__("View Scaffolding Complete.\n", true));
+		} else {
+			$this->customAction();
+		}
+	}
+
+/**
+ * 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->plugin = $this->plugin;
+		$controllerObj->constructClasses();
+		$modelClass = $controllerObj->modelClass;
+		$modelObj =& $controllerObj->{$controllerObj->modelClass};
+
+		if ($modelObj) {
+			$primaryKey = $modelObj->primaryKey;
+			$displayField = $modelObj->displayField;
+			$singularVar = Inflector::variable($modelClass);
+			$singularHumanName = $this->_singularHumanName($this->controllerName);
+			$schema = $modelObj->schema(true);
+			$fields = array_keys($schema);
+			$associations = $this->__associations($modelObj);
+		} else {
+			$primaryKey = $displayField = null;
+			$singularVar = Inflector::variable(Inflector::singularize($this->controllerName));
+			$singularHumanName = $this->_singularHumanName($this->controllerName);
+			$fields = $schema = $associations = array();
+		}
+		$pluralVar = Inflector::variable($this->controllerName);
+		$pluralHumanName = $this->_pluralHumanName($this->controllerName);
+
+		return compact('modelClass', 'schema', 'primaryKey', 'displayField', 'singularVar', 'pluralVar',
+				'singularHumanName', 'pluralHumanName', 'fields','associations');
+	}
+
+/**
+ * Bake a view file for each of the supplied actions
+ *
+ * @param array $actions Array of actions to make files for.
+ * @return void
+ */
+	function bakeActions($actions, $vars) {
+		foreach ($actions as $action) {
+			$content = $this->getContent($action, $vars);
+			$this->bake($action, $content);
+		}
+	}
+
+/**
+ * handle creation of baking a custom action view file
+ *
+ * @return void
+ */
+	function customAction() {
+		$action = '';
+		while ($action == '') {
+			$action = $this->in(__('Action Name? (use lowercase_underscored function name)', true));
+			if ($action == '') {
+				$this->out(__('The action name you supplied was empty. Please try again.', true));
+			}
+		}
+		$this->out();
+		$this->hr();
+		$this->out(__('The following view will be created:', true));
+		$this->hr();
+		$this->out(sprintf(__('Controller Name: %s', true), $this->controllerName));
+		$this->out(sprintf(__('Action Name:     %s', true), $action));
+		$this->out(sprintf(__('Path:            %s', true), $this->params['app'] . DS . 'views' . DS . $this->controllerPath . DS . Inflector::underscore($action) . ".ctp"));
+		$this->hr();
+		$looksGood = $this->in(__('Look okay?', true), array('y','n'), 'y');
+		if (strtolower($looksGood) == 'y') {
+			$this->bake($action, true);
+			$this->_stop();
+		} else {
+			$this->out(__('Bake Aborted.', true));
+		}
+	}
+
+/**
+ * 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($action);
+		}
+		if (empty($content)) {
+			return false;
+		}
+		$path = $this->getPath();
+		$filename = $path . $this->controllerPath . DS . Inflector::underscore($action) . '.ctp';
+		return $this->createFile($filename, $content);
+	}
+
+/**
+ * Builds content from template and variables
+ *
+ * @param string $action name to generate content to
+ * @param array $vars passed for use in templates
+ * @return string content from template
+ * @access public
+ */
+	function getContent($action, $vars = null) {
+		if (!$vars) {
+			$vars = $this->__loadController();
+		}
+
+		$this->Template->set('action', $action);
+		$this->Template->set('plugin', $this->plugin);
+		$this->Template->set($vars);
+		$template = $this->getTemplate($action);
+		if ($template) {
+			return $this->Template->generate('views', $template);
+		}
+		return false;
+	}
+
+/**
+ * Gets the template name based on the action name
+ *
+ * @param string $action name
+ * @return string template name
+ * @access public
+ */
+	function getTemplate($action) {
+		if ($action != $this->template && in_array($action, $this->noTemplateActions)) {
+			return false;
+		}
+		if (!empty($this->template) && $action != $this->template) {
+			return $this->template;
+		} 
+		$template = $action;
+		$prefixes = Configure::read('Routing.prefixes');
+		foreach ((array)$prefixes as $prefix) {
+			if (strpos($template, $prefix) !== false) {
+				$template = str_replace($prefix . '_', '', $template);
+			}
+		}
+		if (in_array($template, array('add', 'edit'))) {
+			$template = 'form';
+		} elseif (preg_match('@(_add|_edit)$@', $template)) {
+			$template = str_replace(array('_add', '_edit'), '_form', $template);
+		}
+		return $template;
+	}
+
+/**
+ * Displays help contents
+ *
+ * @access public
+ */
+	function help() {
+		$this->hr();
+		$this->out("Usage: cake bake view <arg1> <arg2>...");
+		$this->hr();
+		$this->out('Arguments:');
+		$this->out();
+		$this->out("<controller>");
+		$this->out("\tName of the controller views to bake. Can use Plugin.name");
+		$this->out("\tas a shortcut for plugin baking.");
+		$this->out();
+		$this->out("<action>");
+		$this->out("\tName of the action view to bake");
+		$this->out();
+		$this->out('Commands:');
+		$this->out();
+		$this->out("view <controller>");
+		$this->out("\tWill read the given controller for methods");
+		$this->out("\tand bake corresponding views.");
+		$this->out("\tUsing the -admin flag will only bake views for actions");
+		$this->out("\tthat begin with Routing.admin.");
+		$this->out("\tIf var scaffold is found it will bake the CRUD actions");
+		$this->out("\t(index,view,add,edit)");
+		$this->out();
+		$this->out("view <controller> <action>");
+		$this->out("\tWill bake a template. core templates: (index, add, edit, view)");
+		$this->out();
+		$this->out("view <controller> <template> <alias>");
+		$this->out("\tWill use the template specified");
+		$this->out("\tbut name the file based on the alias");
+		$this->out();
+		$this->out("view all");
+		$this->out("\tBake all CRUD action views for all controllers.");
+		$this->out("\tRequires that models and controllers exist.");
+		$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(true));
+			}
+		}
+		return $associations;
+	}
+}

Added: trunk/src/Web/cake/console/libs/testsuite.php
===================================================================
--- trunk/src/Web/cake/console/libs/testsuite.php	                        (rev 0)
+++ trunk/src/Web/cake/console/libs/testsuite.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,370 @@
+<?php
+/**
+ * Test Suite Shell
+ *
+ * This Shell allows the running of test suites via the cake command line
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ * @since         CakePHP(tm) v 1.2.0.4433
+ * @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;
+
+/**
+ * Initialization method installs Simpletest and loads all plugins
+ *
+ * @return void
+ * @access public
+ */
+	function initialize() {
+		$corePath = App::core('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 . 'reporter' . DS . 'cake_cli_reporter.php';
+
+		$plugins = App::objects('plugin');
+		foreach ($plugins as $p) {
+			$this->plugins[] = Inflector::underscore($p);
+		}
+		$this->parseArgs();
+		$this->getManager();
+	}
+
+/**
+ * Parse the arguments given into the Shell object properties.
+ *
+ * @return void
+ * @access public
+ */
+	function parseArgs() {
+		if (empty($this->args)) {
+			return;
+		}
+		$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;
+		}
+	}
+
+/**
+ * Gets a manager instance, and set the app/plugin properties.
+ *
+ * @return void
+ */
+	function getManager() {
+		$this->Manager = new TestManager();
+		$this->Manager->appTest = ($this->category === 'app');
+		if ($this->isPluginTest) {
+			$this->Manager->pluginTest = $this->category;
+		}
+	}
+
+/**
+ * Main entry point to this shell
+ *
+ * @return void
+ * @access public
+ */
+	function main() {
+		$this->out(__('CakePHP Test Shell', true));
+		$this->hr();
+
+		if (count($this->args) == 0) {
+			$this->error(__('Sorry, you did not pass any arguments!', true));
+		}
+
+		if ($this->__canRun()) {
+			$message = sprintf(__('Running %s %s %s', true), $this->category, $this->type, $this->file);
+			$this->out($message);
+
+			$exitCode = 0;
+			if (!$this->__run()) {
+				$exitCode = 1;
+			}
+			$this->_stop($exitCode);
+		} else {
+			$this->error(__('Sorry, the tests could not be found.', true));
+		}
+	}
+
+/**
+ * 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\tcake testsuite app all");
+		$this->out("\t\tcake testsuite core all");
+		$this->out();
+		$this->out("\t\tcake testsuite app case behaviors/debuggable");
+		$this->out("\t\tcake testsuite app case models/my_model");
+		$this->out("\t\tcake testsuite app case controllers/my_controller");
+		$this->out();
+		$this->out("\t\tcake testsuite core case file");
+		$this->out("\t\tcake testsuite core case router");
+		$this->out("\t\tcake testsuite core case set");
+		$this->out();
+		$this->out("\t\tcake testsuite app group mygroup");
+		$this->out("\t\tcake testsuite core group acl");
+		$this->out("\t\tcake testsuite core group socket");
+		$this->out();
+		$this->out("\t\tcake testsuite bugs case models/bug");
+		$this->out("\t\t  // for the plugin 'bugs' and its test case 'models/bug'");
+		$this->out("\t\tcake testsuite bugs group bug");
+		$this->out("\t\t  // for the plugin bugs 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) {
+			$message = sprintf(
+				__('%s is an invalid test category (either "app", "core" or name of a plugin)', true),
+				$this->category
+			);
+			$this->error($message);
+			return false;
+		}
+
+		$folder = $this->__findFolderByCategory($this->category);
+		if (!file_exists($folder)) {
+			$this->err(sprintf(__('%s not found', true), $folder));
+			return false;
+		}
+
+		if (!in_array($this->type, array('all', 'group', 'case'))) {
+			$this->err(sprintf(__('%s is invalid. Should be case, group or all', true), $this->type));
+			return false;
+		}
+
+		$fileName = $this->__getFileName($folder, $this->isPluginTest);
+		if ($fileName === true || file_exists($folder . $fileName)) {
+			return true;
+		}
+
+		$message = sprintf(
+			__('%s %s %s is an invalid test identifier', true),
+			$this->category, $this->type, $this->file
+		);
+		$this->err($message);
+		return false;
+	}
+/**
+ * Executes the tests depending on our settings
+ *
+ * @return void
+ * @access private
+ */
+	function __run() {
+		$Reporter = new CakeCliReporter('utf-8', array(
+			'app' => $this->Manager->appTest,
+			'plugin' => $this->Manager->pluginTest,
+			'group' => ($this->type === 'group'),
+			'codeCoverage' => $this->doCoverage
+		));
+
+		if ($this->type == 'all') {
+			return $this->Manager->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', true));
+				$this->_stop(0);
+			}
+		}
+
+		if ($this->type == 'group') {
+			$ucFirstGroup = ucfirst($this->file);
+			if ($this->doCoverage) {
+				require_once CAKE . 'tests' . DS . 'lib' . DS . 'code_coverage_manager.php';
+				CodeCoverageManager::init($ucFirstGroup, $Reporter);
+				CodeCoverageManager::start();
+			}
+			$result = $this->Manager->runGroupTest($ucFirstGroup, $Reporter);
+			return $result;
+		}
+
+		$folder = $folder = $this->__findFolderByCategory($this->category);
+		$case = $this->__getFileName($folder, $this->isPluginTest);
+
+		if ($this->doCoverage) {
+			require_once CAKE . 'tests' . DS . 'lib' . DS . 'code_coverage_manager.php';
+			CodeCoverageManager::init($case, $Reporter);
+			CodeCoverageManager::start();
+		}
+		$result = $this->Manager->runTestCase($case, $Reporter);
+		return $result;
+	}
+
+/**
+ * Gets the concrete filename for the inputted test name and category/type
+ *
+ * @param string $folder Folder name to look for files in.
+ * @param boolean $isPlugin If the test case is a plugin.
+ * @return mixed Either string filename or boolean false on failure. Or true if the type is 'all'
+ * @access private
+ */
+	function __getFileName($folder, $isPlugin) {
+		$ext = $this->Manager->getExtension($this->type);
+		switch ($this->type) {
+			case 'all':
+				return true;
+			case 'group':
+				return $this->file . $ext; 
+			case 'case':
+				if ($this->category == 'app' || $isPlugin) {
+					return $this->file . $ext;
+				}
+				$coreCase = $this->file . $ext;
+				$coreLibCase = 'libs' . DS . $this->file . $ext;
+
+				if ($this->category == 'core' && file_exists($folder . DS . $coreCase)) {
+					return $coreCase;
+				} elseif ($this->category == 'core' && file_exists($folder . DS . $coreLibCase)) {
+					return $coreLibCase;
+				}
+		}
+		return false;
+	}
+
+/**
+ * Finds the correct folder to look for tests for based on the input category and type.
+ *
+ * @param string $category The category of the test.  Either 'app', 'core' or a plugin name.
+ * @return string the folder path
+ * @access private
+ */
+	function __findFolderByCategory($category) {
+		$folder = '';
+		$paths = array(
+			'core' => CAKE,
+			'app' => APP
+		);
+		$typeDir = $this->type === 'group' ? 'groups' : 'cases';
+
+		if (array_key_exists($category, $paths)) {
+			$folder = $paths[$category] . 'tests' . DS . $typeDir . DS;
+		} else {
+			$pluginPath = App::pluginPath($category);
+			if (is_dir($pluginPath . 'tests')) {
+				$folder = $pluginPath . 'tests' . DS . $typeDir . DS;
+			}
+		}
+		return $folder;
+	}
+
+/**
+ * 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.', true));
+			exit;
+		}
+	}
+}

Added: trunk/src/Web/cake/console/templates/default/actions/controller_actions.ctp
===================================================================
--- trunk/src/Web/cake/console/templates/default/actions/controller_actions.ctp	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/default/actions/controller_actions.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,140 @@
+<?php
+/**
+ * Bake Template for Controller action generation.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.console.libs.template.objects
+ * @since         CakePHP(tm) v 1.3
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+
+	function <?php echo $admin ?>index() {
+		$this-><?php echo $currentModelName ?>->recursive = 0;
+		$this->set('<?php echo $pluralName ?>', $this->paginate());
+	}
+
+	function <?php echo $admin ?>view($id = null) {
+		if (!$id) {
+<?php if ($wannaUseSession): ?>
+			$this->Session->setFlash(__('Invalid <?php echo strtolower($singularHumanName) ?>', true));
+			$this->redirect(array('action' => 'index'));
+<?php else: ?>
+			$this->flash(__('Invalid <?php echo strtolower($singularHumanName); ?>', true), array('action' => 'index'));
+<?php endif; ?>
+		}
+		$this->set('<?php echo $singularName; ?>', $this-><?php echo $currentModelName; ?>->read(null, $id));
+	}
+
+<?php $compact = array(); ?>
+	function <?php echo $admin ?>add() {
+		if (!empty($this->data)) {
+			$this-><?php echo $currentModelName; ?>->create();
+			if ($this-><?php echo $currentModelName; ?>->save($this->data)) {
+<?php if ($wannaUseSession): ?>
+				$this->Session->setFlash(__('The <?php echo strtolower($singularHumanName); ?> has been saved', true));
+				$this->redirect(array('action' => 'index'));
+<?php else: ?>
+				$this->flash(__('<?php echo ucfirst(strtolower($currentModelName)); ?> saved.', true), array('action' => 'index'));
+<?php endif; ?>
+			} else {
+<?php if ($wannaUseSession): ?>
+				$this->Session->setFlash(__('The <?php echo strtolower($singularHumanName); ?> could not be saved. Please, try again.', true));
+<?php endif; ?>
+			}
+		}
+<?php
+	foreach (array('belongsTo', 'hasAndBelongsToMany') as $assoc):
+		foreach ($modelObj->{$assoc} as $associationName => $relation):
+			if (!empty($associationName)):
+				$otherModelName = $this->_modelName($associationName);
+				$otherPluralName = $this->_pluralName($associationName);
+				echo "\t\t\${$otherPluralName} = \$this->{$currentModelName}->{$otherModelName}->find('list');\n";
+				$compact[] = "'{$otherPluralName}'";
+			endif;
+		endforeach;
+	endforeach;
+	if (!empty($compact)):
+		echo "\t\t\$this->set(compact(".join(', ', $compact)."));\n";
+	endif;
+?>
+	}
+
+<?php $compact = array(); ?>
+	function <?php echo $admin; ?>edit($id = null) {
+		if (!$id && empty($this->data)) {
+<?php if ($wannaUseSession): ?>
+			$this->Session->setFlash(__('Invalid <?php echo strtolower($singularHumanName); ?>', true));
+			$this->redirect(array('action' => 'index'));
+<?php else: ?>
+			$this->flash(sprintf(__('Invalid <?php echo strtolower($singularHumanName); ?>', true)), array('action' => 'index'));
+<?php endif; ?>
+		}
+		if (!empty($this->data)) {
+			if ($this-><?php echo $currentModelName; ?>->save($this->data)) {
+<?php if ($wannaUseSession): ?>
+				$this->Session->setFlash(__('The <?php echo strtolower($singularHumanName); ?> has been saved', true));
+				$this->redirect(array('action' => 'index'));
+<?php else: ?>
+				$this->flash(__('The <?php echo strtolower($singularHumanName); ?> has been saved.', true), array('action' => 'index'));
+<?php endif; ?>
+			} else {
+<?php if ($wannaUseSession): ?>
+				$this->Session->setFlash(__('The <?php echo strtolower($singularHumanName); ?> could not be saved. Please, try again.', true));
+<?php endif; ?>
+			}
+		}
+		if (empty($this->data)) {
+			$this->data = $this-><?php echo $currentModelName; ?>->read(null, $id);
+		}
+<?php
+		foreach (array('belongsTo', 'hasAndBelongsToMany') as $assoc):
+			foreach ($modelObj->{$assoc} as $associationName => $relation):
+				if (!empty($associationName)):
+					$otherModelName = $this->_modelName($associationName);
+					$otherPluralName = $this->_pluralName($associationName);
+					echo "\t\t\${$otherPluralName} = \$this->{$currentModelName}->{$otherModelName}->find('list');\n";
+					$compact[] = "'{$otherPluralName}'";
+				endif;
+			endforeach;
+		endforeach;
+		if (!empty($compact)):
+			echo "\t\t\$this->set(compact(".join(', ', $compact)."));\n";
+		endif;
+	?>
+	}
+
+	function <?php echo $admin; ?>delete($id = null) {
+		if (!$id) {
+<?php if ($wannaUseSession): ?>
+			$this->Session->setFlash(__('Invalid id for <?php echo strtolower($singularHumanName); ?>', true));
+			$this->redirect(array('action'=>'index'));
+<?php else: ?>
+			$this->flash(sprintf(__('Invalid <?php echo strtolower($singularHumanName); ?>', true)), array('action' => 'index'));
+<?php endif; ?>
+		}
+		if ($this-><?php echo $currentModelName; ?>->delete($id)) {
+<?php if ($wannaUseSession): ?>
+			$this->Session->setFlash(__('<?php echo ucfirst(strtolower($singularHumanName)); ?> deleted', true));
+			$this->redirect(array('action'=>'index'));
+<?php else: ?>
+			$this->flash(__('<?php echo ucfirst(strtolower($singularHumanName)); ?> deleted', true), array('action' => 'index'));
+<?php endif; ?>
+		}
+<?php if ($wannaUseSession): ?>
+		$this->Session->setFlash(__('<?php echo ucfirst(strtolower($singularHumanName)); ?> was not deleted', true));
+<?php else: ?>
+		$this->flash(__('<?php echo ucfirst(strtolower($singularHumanName)); ?> was not deleted', true), array('action' => 'index'));
+<?php endif; ?>
+		$this->redirect(array('action' => 'index'));
+	}
\ No newline at end of file

Added: trunk/src/Web/cake/console/templates/default/classes/controller.ctp
===================================================================
--- trunk/src/Web/cake/console/templates/default/classes/controller.ctp	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/default/classes/controller.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Controller bake template file
+ *
+ * Allows templating of Controllers generated from bake.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.
+ * @since         CakePHP(tm) v 1.3
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+echo "<?php\n";
+?>
+class <?php echo $controllerName; ?>Controller extends <?php echo $plugin; ?>AppController {
+
+	var $name = '<?php echo $controllerName; ?>';
+<?php if ($isScaffold): ?>
+	var $scaffold;
+<?php else: ?>
+<?php
+if (count($helpers)):
+	echo "\tvar \$helpers = array(";
+	for ($i = 0, $len = count($helpers); $i < $len; $i++):
+		if ($i != $len - 1):
+			echo "'" . Inflector::camelize($helpers[$i]) . "', ";
+		else:
+			echo "'" . Inflector::camelize($helpers[$i]) . "'";
+		endif;
+	endfor;
+	echo ");\n";
+endif;
+
+if (count($components)):
+	echo "\tvar \$components = array(";
+	for ($i = 0, $len = count($components); $i < $len; $i++):
+		if ($i != $len - 1):
+			echo "'" . Inflector::camelize($components[$i]) . "', ";
+		else:
+			echo "'" . Inflector::camelize($components[$i]) . "'";
+		endif;
+	endfor;
+	echo ");\n";
+endif;
+
+echo $actions;
+
+endif; ?>
+
+}

Added: trunk/src/Web/cake/console/templates/default/classes/fixture.ctp
===================================================================
--- trunk/src/Web/cake/console/templates/default/classes/fixture.ctp	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/default/classes/fixture.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,41 @@
+<?php
+/**
+ * Fixture Template file
+ *
+ * Fixture Template used when baking fixtures with bake
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.
+ * @since         CakePHP(tm) v 1.3
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<?php echo '<?php' . "\n"; ?>
+/* <?php echo $model; ?> Fixture generated on: <?php echo  date('Y-m-d H:i:s') . " : ". time(); ?> */
+class <?php echo $model; ?>Fixture extends CakeTestFixture {
+	var $name = '<?php echo $model; ?>';
+<?php if ($table): ?>
+	var $table = '<?php echo $table; ?>';
+<?php endif; ?>
+<?php if ($import): ?>
+	var $import = <?php echo $import; ?>;
+<?php endif; ?>
+
+<?php if ($schema): ?>
+	var $fields = <?php echo $schema; ?>;
+<?php endif;?>
+
+<?php if ($records): ?>
+	var $records = <?php echo $records; ?>;
+<?php endif;?>
+}

Added: trunk/src/Web/cake/console/templates/default/classes/model.ctp
===================================================================
--- trunk/src/Web/cake/console/templates/default/classes/model.ctp	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/default/classes/model.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,141 @@
+<?php
+/**
+ * Model template file.
+ *
+ * Used by bake to create new Model files.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.console.libs.templates.objects
+ * @since         CakePHP(tm) v 1.3
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+echo "<?php\n"; ?>
+class <?php echo $name ?> extends <?php echo $plugin; ?>AppModel {
+	var $name = '<?php echo $name; ?>';
+<?php if ($useDbConfig != 'default'): ?>
+	var $useDbConfig = '<?php echo $useDbConfig; ?>';
+<?php endif;?>
+<?php if ($useTable && $useTable !== Inflector::tableize($name)):
+	$table = "'$useTable'";
+	echo "\tvar \$useTable = $table;\n";
+endif;
+if ($primaryKey !== 'id'): ?>
+	var $primaryKey = '<?php echo $primaryKey; ?>';
+<?php endif;
+if ($displayField): ?>
+	var $displayField = '<?php echo $displayField; ?>';
+<?php endif;
+
+if (!empty($validate)):
+	echo "\tvar \$validate = array(\n";
+	foreach ($validate as $field => $validations):
+		echo "\t\t'$field' => array(\n";
+		foreach ($validations as $key => $validator):
+			echo "\t\t\t'$key' => array(\n";
+			echo "\t\t\t\t'rule' => array('$validator'),\n";
+			echo "\t\t\t\t//'message' => 'Your custom message here',\n";
+			echo "\t\t\t\t//'allowEmpty' => false,\n";
+			echo "\t\t\t\t//'required' => false,\n";
+			echo "\t\t\t\t//'last' => false, // Stop validation after this rule\n";
+			echo "\t\t\t\t//'on' => 'create', // Limit validation to 'create' or 'update' operations\n";
+			echo "\t\t\t),\n";
+		endforeach;
+		echo "\t\t),\n";
+	endforeach;
+	echo "\t);\n";
+endif;
+
+foreach ($associations as $assoc):
+	if (!empty($assoc)):
+?>
+	//The Associations below have been created with all possible keys, those that are not needed can be removed
+<?php
+		break;
+	endif;
+endforeach;
+
+foreach (array('hasOne', 'belongsTo') as $assocType):
+	if (!empty($associations[$assocType])):
+		$typeCount = count($associations[$assocType]);
+		echo "\n\tvar \$$assocType = array(";
+		foreach ($associations[$assocType] as $i => $relation):
+			$out = "\n\t\t'{$relation['alias']}' => array(\n";
+			$out .= "\t\t\t'className' => '{$relation['className']}',\n";
+			$out .= "\t\t\t'foreignKey' => '{$relation['foreignKey']}',\n";
+			$out .= "\t\t\t'conditions' => '',\n";
+			$out .= "\t\t\t'fields' => '',\n";
+			$out .= "\t\t\t'order' => ''\n";
+			$out .= "\t\t)";
+			if ($i + 1 < $typeCount) {
+				$out .= ",";
+			}
+			echo $out;
+		endforeach;
+		echo "\n\t);\n";
+	endif;
+endforeach;
+
+if (!empty($associations['hasMany'])):
+	$belongsToCount = count($associations['hasMany']);
+	echo "\n\tvar \$hasMany = array(";
+	foreach ($associations['hasMany'] as $i => $relation):
+		$out = "\n\t\t'{$relation['alias']}' => array(\n";
+		$out .= "\t\t\t'className' => '{$relation['className']}',\n";
+		$out .= "\t\t\t'foreignKey' => '{$relation['foreignKey']}',\n";
+		$out .= "\t\t\t'dependent' => false,\n";
+		$out .= "\t\t\t'conditions' => '',\n";
+		$out .= "\t\t\t'fields' => '',\n";
+		$out .= "\t\t\t'order' => '',\n";
+		$out .= "\t\t\t'limit' => '',\n";
+		$out .= "\t\t\t'offset' => '',\n";
+		$out .= "\t\t\t'exclusive' => '',\n";
+		$out .= "\t\t\t'finderQuery' => '',\n";
+		$out .= "\t\t\t'counterQuery' => ''\n";
+		$out .= "\t\t)";
+		if ($i + 1 < $belongsToCount) {
+			$out .= ",";
+		}
+		echo $out;
+	endforeach;
+	echo "\n\t);\n\n";
+endif;
+
+if (!empty($associations['hasAndBelongsToMany'])):
+	$habtmCount = count($associations['hasAndBelongsToMany']);
+	echo "\n\tvar \$hasAndBelongsToMany = array(";
+	foreach ($associations['hasAndBelongsToMany'] as $i => $relation):
+		$out = "\n\t\t'{$relation['alias']}' => array(\n";
+		$out .= "\t\t\t'className' => '{$relation['className']}',\n";
+		$out .= "\t\t\t'joinTable' => '{$relation['joinTable']}',\n";
+		$out .= "\t\t\t'foreignKey' => '{$relation['foreignKey']}',\n";
+		$out .= "\t\t\t'associationForeignKey' => '{$relation['associationForeignKey']}',\n";
+		$out .= "\t\t\t'unique' => true,\n";
+		$out .= "\t\t\t'conditions' => '',\n";
+		$out .= "\t\t\t'fields' => '',\n";
+		$out .= "\t\t\t'order' => '',\n";
+		$out .= "\t\t\t'limit' => '',\n";
+		$out .= "\t\t\t'offset' => '',\n";
+		$out .= "\t\t\t'finderQuery' => '',\n";
+		$out .= "\t\t\t'deleteQuery' => '',\n";
+		$out .= "\t\t\t'insertQuery' => ''\n";
+		$out .= "\t\t)";
+		if ($i + 1 < $habtmCount) {
+			$out .= ",";
+		}
+		echo $out;
+	endforeach;
+	echo "\n\t);\n\n";
+endif;
+?>
+}

Added: trunk/src/Web/cake/console/templates/default/classes/test.ctp
===================================================================
--- trunk/src/Web/cake/console/templates/default/classes/test.ctp	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/default/classes/test.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,56 @@
+<?php
+/**
+ * Test Case bake template
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.console.libs.templates.objects
+ * @since         CakePHP(tm) v 1.3
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+echo "<?php\n";
+echo "/* ". $className ." Test cases generated on: " . date('Y-m-d H:i:s') . " : ". time() . "*/\n";
+?>
+App::import('<?php echo $type; ?>', '<?php echo $plugin . $className;?>');
+
+<?php if ($mock and strtolower($type) == 'controller'): ?>
+class Test<?php echo $fullClassName; ?> extends <?php echo $fullClassName; ?> {
+	var $autoRender = false;
+
+	function redirect($url, $status = null, $exit = true) {
+		$this->redirectUrl = $url;
+	}
+}
+
+<?php endif; ?>
+class <?php echo $fullClassName; ?>TestCase extends CakeTestCase {
+<?php if (!empty($fixtures)): ?>
+	var $fixtures = array('<?php echo join("', '", $fixtures); ?>');
+
+<?php endif; ?>
+	function startTest() {
+		$this-><?php echo $className . ' =& ' . $construction; ?>
+	}
+
+	function endTest() {
+		unset($this-><?php echo $className;?>);
+		ClassRegistry::flush();
+	}
+
+<?php foreach ($methods as $method): ?>
+	function test<?php echo Inflector::classify($method); ?>() {
+
+	}
+
+<?php endforeach;?>
+}

Added: trunk/src/Web/cake/console/templates/default/views/form.ctp
===================================================================
--- trunk/src/Web/cake/console/templates/default/views/form.ctp	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/default/views/form.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,66 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.templates.views
+ * @since         CakePHP(tm) v 1.2.0.5234
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<div class="<?php echo $pluralVar;?> form">
+<?php echo "<?php echo \$this->Form->create('{$modelClass}');?>\n";?>
+	<fieldset>
+		<legend><?php printf("<?php __('%s %s'); ?>", Inflector::humanize($action), $singularHumanName); ?></legend>
+<?php
+		echo "\t<?php\n";
+		foreach ($fields as $field) {
+			if (strpos($action, 'add') !== false && $field == $primaryKey) {
+				continue;
+			} elseif (!in_array($field, array('created', 'modified', 'updated'))) {
+				echo "\t\techo \$this->Form->input('{$field}');\n";
+			}
+		}
+		if (!empty($associations['hasAndBelongsToMany'])) {
+			foreach ($associations['hasAndBelongsToMany'] as $assocName => $assocData) {
+				echo "\t\techo \$this->Form->input('{$assocName}');\n";
+			}
+		}
+		echo "\t?>\n";
+?>
+	</fieldset>
+<?php
+	echo "<?php echo \$this->Form->end(__('Submit', true));?>\n";
+?>
+</div>
+<div class="actions">
+	<h3><?php echo "<?php __('Actions'); ?>"; ?></h3>
+	<ul>
+
+<?php if (strpos($action, 'add') === false): ?>
+		<li><?php echo "<?php echo \$this->Html->link(__('Delete', true), array('action' => 'delete', \$this->Form->value('{$modelClass}.{$primaryKey}')), null, sprintf(__('Are you sure you want to delete # %s?', true), \$this->Form->value('{$modelClass}.{$primaryKey}'))); ?>";?></li>
+<?php endif;?>
+		<li><?php echo "<?php echo \$this->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 \$this->Html->link(__('List " . Inflector::humanize($details['controller']) . "', true), array('controller' => '{$details['controller']}', 'action' => 'index')); ?> </li>\n";
+					echo "\t\t<li><?php echo \$this->Html->link(__('New " . Inflector::humanize(Inflector::underscore($alias)) . "', true), array('controller' => '{$details['controller']}', 'action' => 'add')); ?> </li>\n";
+					$done[] = $details['controller'];
+				}
+			}
+		}
+?>
+	</ul>
+</div>
\ No newline at end of file

Added: trunk/src/Web/cake/console/templates/default/views/home.ctp
===================================================================
--- trunk/src/Web/cake/console/templates/default/views/home.ctp	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/default/views/home.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,103 @@
+<?php
+$output = "
+<iframe src=\"http://cakephp.org/bake-banner\" width=\"830\" height=\"160\" style=\"overflow:hidden; border:none;\">
+	<p>For updates and important announcements, visit http://cakefest.org</p>
+</iframe>\n";
+$output .= "<h2>Sweet, \"" . Inflector::humanize($app) . "\" got Baked by CakePHP!</h2>\n";
+$output .="
+<?php
+if (Configure::read() > 0):
+	Debugger::checkSecurityKeys();
+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>
+<div id=\"url-rewriting-warning\" style=\"background-color:#e32; color:#fff; padding:3px; margin: 20px 0\">
+	<?php __('URL rewriting is not properly configured on your server. '); ?>
+	<ol style=\"padding-left:20px\">
+		<li>
+			<a target=\"_blank\" href=\"http://book.cakephp.org/view/917/Apache-and-mod_rewrite-and-htaccess\" style=\"color:#fff;\">
+				<?php __('Help me configure it')?>
+			</a>
+		</li>
+		<li>
+			<a target=\"_blank\" href=\"http://book.cakephp.org/view/931/CakePHP-Core-Configuration-Variables\" style=\"color:#fff;\">
+				<?php __('I don\'t / can\'t use URL rewriting')?>
+			</a>
+		</li>
+	</ol>
+</div>
+<p>
+<?php
+	\$settings = Cache::settings();
+	if (!empty(\$settings)):
+		echo '<span class=\"notice success\">';
+				printf(__('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)):
+	if (!class_exists('ConnectionManager')) {
+		require LIBS . 'model' . DS . 'connection_manager.php';
+	}
+	\$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 .= "\tprintf(__('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";
+?>
\ No newline at end of file

Added: trunk/src/Web/cake/console/templates/default/views/index.ctp
===================================================================
--- trunk/src/Web/cake/console/templates/default/views/index.ctp	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/default/views/index.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,96 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.templates.views
+ * @since         CakePHP(tm) v 1.2.0.5234
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<div class="<?php echo $pluralVar;?> index">
+	<h2><?php echo "<?php __('{$pluralHumanName}');?>";?></h2>
+	<table cellpadding="0" cellspacing="0">
+	<tr>
+	<?php  foreach ($fields as $field):?>
+		<th><?php echo "<?php echo \$this->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 \$this->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><?php echo \${$singularVar}['{$modelClass}']['{$field}']; ?>&nbsp;</td>\n";
+			}
+		}
+
+		echo "\t\t<td class=\"actions\">\n";
+		echo "\t\t\t<?php echo \$this->Html->link(__('View', true), array('action' => 'view', \${$singularVar}['{$modelClass}']['{$primaryKey}'])); ?>\n";
+		echo "\t\t\t<?php echo \$this->Html->link(__('Edit', true), array('action' => 'edit', \${$singularVar}['{$modelClass}']['{$primaryKey}'])); ?>\n";
+		echo "\t\t\t<?php echo \$this->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>
+	<p>
+	<?php echo "<?php
+	echo \$this->Paginator->counter(array(
+	'format' => __('Page %page% of %pages%, showing %current% records out of %count% total, starting on record %start%, ending on %end%', true)
+	));
+	?>";?>
+	</p>
+
+	<div class="paging">
+	<?php echo "\t<?php echo \$this->Paginator->prev('<< ' . __('previous', true), array(), null, array('class'=>'disabled'));?>\n";?>
+	 | <?php echo "\t<?php echo \$this->Paginator->numbers();?>\n"?> |
+	<?php echo "\t<?php echo \$this->Paginator->next(__('next', true) . ' >>', array(), null, array('class' => 'disabled'));?>\n";?>
+	</div>
+</div>
+<div class="actions">
+	<h3><?php echo "<?php __('Actions'); ?>"; ?></h3>
+	<ul>
+		<li><?php echo "<?php echo \$this->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 \$this->Html->link(__('List " . Inflector::humanize($details['controller']) . "', true), array('controller' => '{$details['controller']}', 'action' => 'index')); ?> </li>\n";
+				echo "\t\t<li><?php echo \$this->Html->link(__('New " . Inflector::humanize(Inflector::underscore($alias)) . "', true), array('controller' => '{$details['controller']}', 'action' => 'add')); ?> </li>\n";
+				$done[] = $details['controller'];
+			}
+		}
+	}
+?>
+	</ul>
+</div>
\ No newline at end of file

Added: trunk/src/Web/cake/console/templates/default/views/view.ctp
===================================================================
--- trunk/src/Web/cake/console/templates/default/views/view.ctp	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/default/views/view.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,146 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.templates.views
+ * @since         CakePHP(tm) v 1.2.0.5234
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<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 \$this->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">
+	<h3><?php echo "<?php __('Actions'); ?>"; ?></h3>
+	<ul>
+<?php
+	echo "\t\t<li><?php echo \$this->Html->link(__('Edit " . $singularHumanName ."', true), array('action' => 'edit', \${$singularVar}['{$modelClass}']['{$primaryKey}'])); ?> </li>\n";
+	echo "\t\t<li><?php echo \$this->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 \$this->Html->link(__('List " . $pluralHumanName . "', true), array('action' => 'index')); ?> </li>\n";
+	echo "\t\t<li><?php echo \$this->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 \$this->Html->link(__('List " . Inflector::humanize($details['controller']) . "', true), array('controller' => '{$details['controller']}', 'action' => 'index')); ?> </li>\n";
+				echo "\t\t<li><?php echo \$this->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 \$this->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 \$this->Html->link(__('View', true), array('controller' => '{$details['controller']}', 'action' => 'view', \${$otherSingularVar}['{$details['primaryKey']}'])); ?>\n";
+				echo "\t\t\t\t<?php echo \$this->Html->link(__('Edit', true), array('controller' => '{$details['controller']}', 'action' => 'edit', \${$otherSingularVar}['{$details['primaryKey']}'])); ?>\n";
+				echo "\t\t\t\t<?php echo \$this->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 \$this->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

Added: trunk/src/Web/cake/console/templates/skel/.htaccess
===================================================================
--- trunk/src/Web/cake/console/templates/skel/.htaccess	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/.htaccess	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,5 @@
+<IfModule mod_rewrite.c>
+    RewriteEngine on
+    RewriteRule    ^$    webroot/    [L]
+    RewriteRule    (.*) webroot/$1    [L]
+ </IfModule>
\ No newline at end of file

Added: trunk/src/Web/cake/console/templates/skel/app_controller.php
===================================================================
--- trunk/src/Web/cake/console/templates/skel/app_controller.php	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/app_controller.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Application level Controller
+ *
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Application Controller
+ *
+ * Add your application-wide methods in the class below, your controllers
+ * will inherit them.
+ *
+ * @package       cake
+ * @subpackage    cake.app
+ */
+class AppController extends Controller {
+}

Added: trunk/src/Web/cake/console/templates/skel/app_helper.php
===================================================================
--- trunk/src/Web/cake/console/templates/skel/app_helper.php	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/app_helper.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Application level View Helper
+ *
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Helper', 'Helper', false);
+
+/**
+ * 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 {
+}

Added: trunk/src/Web/cake/console/templates/skel/app_model.php
===================================================================
--- trunk/src/Web/cake/console/templates/skel/app_model.php	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/app_model.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,34 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 {
+}

Added: trunk/src/Web/cake/console/templates/skel/config/acl.ini.php
===================================================================
--- trunk/src/Web/cake/console/templates/skel/config/acl.ini.php	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/config/acl.ini.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,70 @@
+;<?php exit() ?>
+; SVN FILE: $Id$
+;/**
+; * ACL Configuration
+; *
+; *
+; * PHP versions 4 and 5
+; *
+; * CakePHP(tm) : Rapid Development Framework http://www.cakephp.org/
+; * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+; *
+; *  Licensed under The MIT License
+; *  Redistributions of files must retain the above copyright notice.
+; *
+; * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+; * @link          http://cakephp.org CakePHP(tm) Project
+; * @package       cake
+; * @subpackage    cake.app.config
+; * @since         CakePHP(tm) v 0.10.0.1076
+; * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+; */
+
+; 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

Added: trunk/src/Web/cake/console/templates/skel/config/bootstrap.php
===================================================================
--- trunk/src/Web/cake/console/templates/skel/config/bootstrap.php	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/config/bootstrap.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,50 @@
+<?php
+/**
+ * This file is loaded automatically by the app/webroot/index.php file after the core bootstrap.php
+ *
+ * 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.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config
+ * @since         CakePHP(tm) v 0.10.8.2117
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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)
+ *
+ * App::build(array(
+ *     'plugins' => array('/full/path/to/plugins/', '/next/full/path/to/plugins/'),
+ *     'models' =>  array('/full/path/to/models/', '/next/full/path/to/models/'),
+ *     'views' => array('/full/path/to/views/', '/next/full/path/to/views/'),
+ *     'controllers' => array(/full/path/to/controllers/', '/next/full/path/to/controllers/'),
+ *     'datasources' => array('/full/path/to/datasources/', '/next/full/path/to/datasources/'),
+ *     'behaviors' => array('/full/path/to/behaviors/', '/next/full/path/to/behaviors/'),
+ *     'components' => array('/full/path/to/components/', '/next/full/path/to/components/'),
+ *     'helpers' => array('/full/path/to/helpers/', '/next/full/path/to/helpers/'),
+ *     'vendors' => array('/full/path/to/vendors/', '/next/full/path/to/vendors/'),
+ *     'shells' => array('/full/path/to/shells/', '/next/full/path/to/shells/'),
+ *     'locales' => array('/full/path/to/locale/', '/next/full/path/to/locale/')
+ * ));
+ *
+ */
+
+/**
+ * As of 1.3, additional rules for the inflector are added below
+ *
+ * Inflector::rules('singular', array('rules' => array(), 'irregular' => array(), 'uninflected' => array()));
+ * Inflector::rules('plural', array('rules' => array(), 'irregular' => array(), 'uninflected' => array()));
+ *
+ */

Added: trunk/src/Web/cake/console/templates/skel/config/core.php
===================================================================
--- trunk/src/Web/cake/console/templates/skel/config/core.php	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/config/core.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,304 @@
+<?php
+/**
+ * This is core configuration file.
+ *
+ * Use it to configure core behavior of Cake.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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.
+ *
+ * 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);
+
+/**
+ * CakePHP Log Level:
+ *
+ * In case of Production Mode CakePHP gives you the possibility to continue logging errors.
+ *
+ * The following parameters can be used:
+ *  Boolean: Set true/false to activate/deactivate logging
+ *    Configure::write('log', true);
+ *
+ *  Integer: Use built-in PHP constants to set the error level (see error_reporting)
+ *    Configure::write('log', E_ERROR | E_WARNING);
+ *    Configure::write('log', E_ALL ^ E_NOTICE);
+ */
+	Configure::write('log', true);
+
+/**
+ * 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 prefix routes.
+ *
+ * The value of the define determines the names of the routes
+ * and their associated controller actions:
+ *
+ * Set to an array of prefixes you want to use in your application. Use for
+ * admin or other prefixed routes.
+ *
+ * 	Routing.prefixes = array('admin', 'manager');
+ *
+ * Enables:
+ *	`admin_index()` and `/admin/controller/index`
+ *	`manager_index()` and `/manager/controller/index`
+ *
+ * [Note Routing.admin is deprecated in 1.3.  Use Routing.prefixes instead]
+ */
+	//Configure::write('Routing.prefixes', array('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, run the app/config/schema/sessions.php schema using
+ * the cake shell command: cake schema create Sessions
+ *
+ */
+	Configure::write('Session.save', 'php');
+
+/**
+ * The model name to be used for the session model.
+ *
+ * 'Session.save' must be set to 'database' in order to utilize this constant.
+ *
+ * The model name set here should *not* be used elsewhere in your application.
+ */
+	//Configure::write('Session.model', 'Session');
+
+/**
+ * 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.
+ *
+ * Please note that if you set a value for Session.model (above), any value set for
+ * Session.table will be ignored.
+ *
+ * [Note: Session.table is deprecated as of CakePHP 1.3]
+ */
+	//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.
+ *
+ * Note the guidelines for Session names states: "The session name references
+ * the session id in cookies and URLs. It should contain only alphanumeric
+ * characters."
+ * @link http://php.net/session_name
+ */
+	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. You might want to set the value to false, when dealing with
+ * older versions of IE, Chrome Frame or certain web-browsing devices and AJAX
+ */
+	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', 'medium');
+
+/**
+ * A random string used in security hashing methods.
+ */
+	Configure::write('Security.salt', 'DYhG93b0qyJfIxfs2guVoUubWwvniR2G0FgaC9mi');
+
+/**
+ * A random numeric string (digits only) used to encrypt/decrypt strings.
+ */
+	Configure::write('Security.cipherSeed', '76859309657453542496749683645');
+
+/**
+ * Apply timestamps with the last modified time to static assets (js, css, images).
+ * Will append a querystring parameter containing the time the file was modified. This is
+ * useful for invalidating browser caches.
+ *
+ * Set to `true` to apply timestamps, when debug = 0, or set to 'force' to always enable
+ * timestamping.
+ */
+	//Configure::write('Asset.timestamp', true);
+/**
+ * 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');
+
+/**
+ * If you are on PHP 5.3 uncomment this line and correct your server timezone
+ * to fix the date & time related errors.
+ */
+	//date_default_timezone_set('UTC');
+
+/**
+ *
+ * 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)
+ * 		'persistent' => true, // [optional] set this to false for non-persistent connections
+ *	));
+ *
+ */
+	Cache::config('default', array('engine' => 'File'));

Added: trunk/src/Web/cake/console/templates/skel/config/database.php.default
===================================================================
--- trunk/src/Web/cake/console/templates/skel/config/database.php.default	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/config/database.php.default	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,97 @@
+<?php
+/**
+ * This is core configuration file.
+ *
+ * Use it to configure core behaviour ofCake.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+/**
+ * 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' => '',
+		//'encoding' => 'utf8',
+	);
+
+	var $test = array(
+		'driver' => 'mysql',
+		'persistent' => false,
+		'host' => 'localhost',
+		'login' => 'user',
+		'password' => 'password',
+		'database' => 'test_database_name',
+		'prefix' => '',
+		//'encoding' => 'utf8',
+	);
+}

Added: trunk/src/Web/cake/console/templates/skel/config/routes.php
===================================================================
--- trunk/src/Web/cake/console/templates/skel/config/routes.php	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/config/routes.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Routes Configuration
+ *
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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'));

Added: trunk/src/Web/cake/console/templates/skel/config/schema/db_acl.php
===================================================================
--- trunk/src/Web/cake/console/templates/skel/config/schema/db_acl.php	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/config/schema/db_acl.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,75 @@
+<?php
+/*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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config.sql
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/*
+ *
+ * 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))
+		);
+
+}

Added: trunk/src/Web/cake/console/templates/skel/config/schema/db_acl.sql
===================================================================
--- trunk/src/Web/cake/console/templates/skel/config/schema/db_acl.sql	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/config/schema/db_acl.sql	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,40 @@
+# $Id$
+#
+# Copyright 2005-2012, Cake Software Foundation, Inc.
+#
+# Licensed under The MIT License
+# Redistributions of files must retain the above copyright notice.
+# MIT License (http://www.opensource.org/licenses/mit-license.php)
+
+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

Added: trunk/src/Web/cake/console/templates/skel/config/schema/i18n.php
===================================================================
--- trunk/src/Web/cake/console/templates/skel/config/schema/i18n.php	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/config/schema/i18n.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,52 @@
+<?php
+/*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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config.sql
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/*
+ *
+ * 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))
+		);
+
+}

Added: trunk/src/Web/cake/console/templates/skel/config/schema/i18n.sql
===================================================================
--- trunk/src/Web/cake/console/templates/skel/config/schema/i18n.sql	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/config/schema/i18n.sql	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,26 @@
+# $Id$
+#
+# Copyright 2005-2012, Cake Software Foundation, Inc.
+#
+# Licensed under The MIT License
+# Redistributions of files must retain the above copyright notice.
+# MIT License (http://www.opensource.org/licenses/mit-license.php)
+
+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

Added: trunk/src/Web/cake/console/templates/skel/config/schema/sessions.php
===================================================================
--- trunk/src/Web/cake/console/templates/skel/config/schema/sessions.php	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/config/schema/sessions.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,49 @@
+<?php
+/*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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config.sql
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/*
+ *
+ * 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))
+		);
+
+}

Added: trunk/src/Web/cake/console/templates/skel/config/schema/sessions.sql
===================================================================
--- trunk/src/Web/cake/console/templates/skel/config/schema/sessions.sql	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/config/schema/sessions.sql	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,16 @@
+# $Id$
+#
+# Copyright 2005-2012, 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.
+# MIT License (http://www.opensource.org/licenses/mit-license.php)
+
+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

Added: trunk/src/Web/cake/console/templates/skel/controllers/components/empty
===================================================================
Added: trunk/src/Web/cake/console/templates/skel/controllers/pages_controller.php
===================================================================
--- trunk/src/Web/cake/console/templates/skel/controllers/pages_controller.php	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/controllers/pages_controller.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,84 @@
+<?php
+/**
+ * Static content controller.
+ *
+ * This file will render views from views/pages/
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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_for_layout = null;
+
+		if (!empty($path[0])) {
+			$page = $path[0];
+		}
+		if (!empty($path[1])) {
+			$subpage = $path[1];
+		}
+		if (!empty($path[$count - 1])) {
+			$title_for_layout = Inflector::humanize($path[$count - 1]);
+		}
+		$this->set(compact('page', 'subpage', 'title_for_layout'));
+		$this->render(implode('/', $path));
+	}
+}

Added: trunk/src/Web/cake/console/templates/skel/index.php
===================================================================
--- trunk/src/Web/cake/console/templates/skel/index.php	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/index.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,18 @@
+<?php
+/**
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+require 'webroot' . DIRECTORY_SEPARATOR . 'index.php';

Added: trunk/src/Web/cake/console/templates/skel/libs/empty
===================================================================
Added: trunk/src/Web/cake/console/templates/skel/locale/eng/LC_MESSAGES/empty
===================================================================
Added: trunk/src/Web/cake/console/templates/skel/models/behaviors/empty
===================================================================
Added: trunk/src/Web/cake/console/templates/skel/models/datasources/empty
===================================================================
Added: trunk/src/Web/cake/console/templates/skel/plugins/empty
===================================================================
Added: trunk/src/Web/cake/console/templates/skel/tests/cases/behaviors/empty
===================================================================
Added: trunk/src/Web/cake/console/templates/skel/tests/cases/components/empty
===================================================================
Added: trunk/src/Web/cake/console/templates/skel/tests/cases/controllers/empty
===================================================================
Added: trunk/src/Web/cake/console/templates/skel/tests/cases/datasources/empty
===================================================================
Added: trunk/src/Web/cake/console/templates/skel/tests/cases/helpers/empty
===================================================================
Added: trunk/src/Web/cake/console/templates/skel/tests/cases/models/empty
===================================================================
Added: trunk/src/Web/cake/console/templates/skel/tests/cases/shells/empty
===================================================================
Added: trunk/src/Web/cake/console/templates/skel/tests/fixtures/empty
===================================================================
Added: trunk/src/Web/cake/console/templates/skel/tests/groups/empty
===================================================================
Added: trunk/src/Web/cake/console/templates/skel/tmp/cache/models/empty
===================================================================
Added: trunk/src/Web/cake/console/templates/skel/tmp/cache/persistent/empty
===================================================================
Added: trunk/src/Web/cake/console/templates/skel/tmp/cache/views/empty
===================================================================
Added: trunk/src/Web/cake/console/templates/skel/tmp/logs/empty
===================================================================
Added: trunk/src/Web/cake/console/templates/skel/tmp/sessions/empty
===================================================================
Added: trunk/src/Web/cake/console/templates/skel/tmp/tests/empty
===================================================================
Added: trunk/src/Web/cake/console/templates/skel/vendors/shells/tasks/empty
===================================================================
Added: trunk/src/Web/cake/console/templates/skel/views/elements/email/html/default.ctp
===================================================================
--- trunk/src/Web/cake/console/templates/skel/views/elements/email/html/default.ctp	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/views/elements/email/html/default.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,26 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.elements.email.html
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<?php
+$content = explode("\n", $content);
+
+foreach ($content as $line):
+	echo '<p> ' . $line . '</p>';
+endforeach;
+?>
\ No newline at end of file

Added: trunk/src/Web/cake/console/templates/skel/views/elements/email/text/default.ctp
===================================================================
--- trunk/src/Web/cake/console/templates/skel/views/elements/email/text/default.ctp	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/views/elements/email/text/default.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,20 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.elements.email.text
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<?php echo $content; ?>
\ No newline at end of file

Added: trunk/src/Web/cake/console/templates/skel/views/elements/empty
===================================================================
Added: trunk/src/Web/cake/console/templates/skel/views/errors/empty
===================================================================
Added: trunk/src/Web/cake/console/templates/skel/views/helpers/empty
===================================================================
Added: trunk/src/Web/cake/console/templates/skel/views/layouts/ajax.ctp
===================================================================
--- trunk/src/Web/cake/console/templates/skel/views/layouts/ajax.ctp	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/views/layouts/ajax.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,20 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.layouts
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<?php echo $content_for_layout; ?>
\ No newline at end of file

Added: trunk/src/Web/cake/console/templates/skel/views/layouts/default.ctp
===================================================================
--- trunk/src/Web/cake/console/templates/skel/views/layouts/default.ctp	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/views/layouts/default.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,59 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.templates.skel.views.layouts
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<!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 $this->Html->charset(); ?>
+	<title>
+		<?php __('CakePHP: the rapid development php framework:'); ?>
+		<?php echo $title_for_layout; ?>
+	</title>
+	<?php
+		echo $this->Html->meta('icon');
+
+		echo $this->Html->css('cake.generic');
+
+		echo $scripts_for_layout;
+	?>
+</head>
+<body>
+	<div id="container">
+		<div id="header">
+			<h1><?php echo $this->Html->link(__('CakePHP: the rapid development php framework', true), 'http://cakephp.org'); ?></h1>
+		</div>
+		<div id="content">
+
+			<?php echo $this->Session->flash(); ?>
+
+			<?php echo $content_for_layout; ?>
+
+		</div>
+		<div id="footer">
+			<?php echo $this->Html->link(
+					$this->Html->image('cake.power.gif', array('alt'=> __('CakePHP: the rapid development php framework', true), 'border' => '0')),
+					'http://www.cakephp.org/',
+					array('target' => '_blank', 'escape' => false)
+				);
+			?>
+		</div>
+	</div>
+	<?php echo $this->element('sql_dump'); ?>
+</body>
+</html>
\ No newline at end of file

Added: trunk/src/Web/cake/console/templates/skel/views/layouts/email/html/default.ctp
===================================================================
--- trunk/src/Web/cake/console/templates/skel/views/layouts/email/html/default.ctp	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/views/layouts/email/html/default.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,32 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.layouts.email.html
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<!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

Added: trunk/src/Web/cake/console/templates/skel/views/layouts/email/text/default.ctp
===================================================================
--- trunk/src/Web/cake/console/templates/skel/views/layouts/email/text/default.ctp	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/views/layouts/email/text/default.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,23 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.layouts.email.text
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+
+<?php echo $content_for_layout;?>
+
+This email was sent using the CakePHP Framework, http://cakephp.org.

Added: trunk/src/Web/cake/console/templates/skel/views/layouts/flash.ctp
===================================================================
--- trunk/src/Web/cake/console/templates/skel/views/layouts/flash.ctp	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/views/layouts/flash.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,38 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.layouts
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<!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 $this->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

Added: trunk/src/Web/cake/console/templates/skel/views/layouts/js/default.ctp
===================================================================
--- trunk/src/Web/cake/console/templates/skel/views/layouts/js/default.ctp	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/views/layouts/js/default.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -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

Added: trunk/src/Web/cake/console/templates/skel/views/layouts/rss/default.ctp
===================================================================
--- trunk/src/Web/cake/console/templates/skel/views/layouts/rss/default.ctp	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/views/layouts/rss/default.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -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

Added: trunk/src/Web/cake/console/templates/skel/views/layouts/xml/default.ctp
===================================================================
--- trunk/src/Web/cake/console/templates/skel/views/layouts/xml/default.ctp	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/views/layouts/xml/default.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,2 @@
+<?php echo $xml->header(); ?>
+<?php echo $content_for_layout; ?>
\ No newline at end of file

Added: trunk/src/Web/cake/console/templates/skel/views/pages/empty
===================================================================
Added: trunk/src/Web/cake/console/templates/skel/views/scaffolds/empty
===================================================================
Added: trunk/src/Web/cake/console/templates/skel/webroot/.htaccess
===================================================================
--- trunk/src/Web/cake/console/templates/skel/webroot/.htaccess	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/webroot/.htaccess	2012-03-31 12:37:43 UTC (rev 78)
@@ -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

Added: trunk/src/Web/cake/console/templates/skel/webroot/css/cake.generic.css
===================================================================
--- trunk/src/Web/cake/console/templates/skel/webroot/css/cake.generic.css	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/webroot/css/cake.generic.css	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,540 @@
+/**
+ *
+ * Generic CSS for CakePHP
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.webroot.css
+ * @since         CakePHP(tm)
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+* {
+	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 {
+	color: #003d4c;
+	text-decoration: underline;
+	font-weight: bold;
+}
+a:hover {
+	color: #367889;
+	text-decoration:none;
+}
+a img {
+	border:none;
+}
+h1, h2, h3, h4 {
+	font-weight: normal;
+	margin-bottom:0.5em;
+}
+h1 {
+	background:#fff;
+	color: #003d4c;
+	font-size: 100%;
+}
+h2 {
+	background:#fff;
+	color: #e32;
+	font-family:'Gill Sans','lucida grande', helvetica, arial, sans-serif;
+	font-size: 190%;
+}
+h3 {
+	color: #993;
+	font-family:'Gill Sans','lucida grande', helvetica, arial, sans-serif;
+	font-size: 165%;
+}
+h4 {
+	color: #993;
+	font-weight: normal;
+}
+ul, li {
+	margin: 0 12px;
+}
+
+/** Layout **/
+#container {
+	text-align: left;
+}
+
+#header{
+	padding: 10px 20px;
+}
+#header h1 {
+	line-height:20px;
+	background: #003d4c url('../img/cake.icon.png') 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;
+}
+
+/** containers **/
+div.form,
+div.index,
+div.view {
+	float:right;
+	width:76%;
+	border-left:1px solid #666;
+	padding:10px 2%;
+}
+div.actions {
+	float:left;
+	width:16%;
+	padding:10px 1.5%;
+}
+div.actions h3 {
+	padding-top:0;
+	color:#777;
+}
+
+
+/** Tables **/
+table {
+	background: #fff;
+	border-right:0;
+	clear: both;
+	color: #333;
+	margin-bottom: 10px;
+	width: 100%;
+}
+th {
+	border:0;
+	border-bottom:2px solid #555;
+	text-align: left;
+	padding:4px;
+}
+th a {
+	display: block;
+	padding: 2px 4px;
+	text-decoration: none;
+}
+th a.asc:after {
+	content: ' ⇣';
+}
+th a.desc:after {
+	content: ' ⇡';
+}
+table tr td {
+	background: #fff;
+	padding: 6px;
+	text-align: left;
+	vertical-align: top;
+	border-bottom:1px solid #ddd;
+}
+table tr:nth-child(2n) td {
+	background: #f5f5f5;
+}
+table .altrow td {
+	background: #f5f5f5;
+}
+td.actions {
+	text-align: center;
+	white-space: nowrap;
+}
+table td.actions a {
+	margin: 0px 6px;
+	padding:2px 5px;
+}
+.cake-sql-log table {
+	background: #f4f4f4;
+}
+.cake-sql-log td {
+	padding: 4px 8px;
+	text-align: left;
+	font-family: Monaco, Consolas, "Courier New", monospaced;
+}
+.cake-sql-log caption {
+	color:#fff;
+}
+
+/** Paging **/
+div.paging {
+	background:#fff;
+	color: #ccc;
+	margin-top: 1em;
+	clear:both;
+}
+div.paging span.disabled {
+	color: #ddd;
+	display: inline;
+}
+div.paging span.current {
+	color: #c73e14;
+}
+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: 95%;
+}
+fieldset {
+	border: 1px solid #ccc;
+	margin-bottom: 1em;
+	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 .input {
+	color: #444;
+}
+form .required {
+	font-weight: bold;
+}
+form .required label:after {
+	color: #e32;
+	content: '*';
+	display:inline;
+}
+form div.submit {
+	border: 0;
+	clear: both;
+	margin-top: 10px;
+}
+label {
+	display: block;
+	font-size: 110%;
+	margin-bottom:3px;
+}
+input, textarea {
+	clear: both;
+	font-size: 140%;
+	font-family: "frutiger linotype", "lucida grande", "verdana", sans-serif;
+	padding: 1%;
+	width:98%;
+}
+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;
+}
+div.checkbox label {
+	display: inline;
+}
+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%;
+	width: auto;
+}
+form .submit input[type=submit] {
+	background:#62af56;
+	background: -webkit-gradient(linear, left top, left bottom, from(#a8ea9c), to(#62af56));
+	background-image: -moz-linear-gradient(top, #a8ea9c, #62af56);
+	border-color: #2d6324;
+	color: #000;
+	text-shadow: #8cee7c 0px 1px 0px;
+}
+form .submit input[type=submit]:hover {
+	background:#4ca83d;
+	background: -webkit-gradient(linear, left top, left bottom, from(#85e573), to(#4ca83d));
+	background-image: -moz-linear-gradient(top, #85e573, #4ca83d);
+}
+
+/** Notices and Errors **/
+div.message {
+	clear: both;
+	color: #fff;
+	font-size: 140%;
+	font-weight: bold;
+	margin: 0 0 1em 0;
+	background: #c73e14;
+	padding: 5px;
+}
+div.error-message {
+	clear: both;
+	color: #fff;
+	font-weight: bold;
+	background: #c73e14;
+}
+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: 0;
+	padding: 0;
+}
+div.actions li {
+	margin:0 0 0.5em 0;
+	list-style-type: none;
+	white-space: nowrap;
+	padding: 0;
+}
+div.actions ul li a {
+	font-weight: normal;
+	display: block;
+	clear: both;
+}
+div.actions ul li a:hover {
+	text-decoration: underline;
+}
+
+input[type=submit],
+div.actions ul li a,
+td.actions a {
+	font-weight:normal;
+	padding: 4px 8px;
+	background:#e6e49f;
+	background: -webkit-gradient(linear, left top, left bottom, from(#f1f1d4), to(#e6e49f));
+	background-image: -moz-linear-gradient(top, #f1f1d4, #e6e49f);
+	color:#333;
+	border:1px solid #aaac62;
+	-webkit-border-radius:8px;
+	-moz-border-radius:8px;
+	border-radius:8px;
+	text-decoration:none;
+	text-shadow: #fff 0px 1px 0px;
+	min-width: 0;
+}
+input[type=submit]:hover,
+div.actions ul li a:hover,
+td.actions a:hover {
+	background: #f0f09a;
+	background: -webkit-gradient(linear, left top, left bottom, from(#f7f7e1), to(#eeeca9));
+}
+
+/** 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;
+	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;
+}
+
+/** Elements **/
+#url-rewriting-warning {
+	display: none;
+}
\ No newline at end of file


Property changes on: trunk/src/Web/cake/console/templates/skel/webroot/css/cake.generic.css
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Added: trunk/src/Web/cake/console/templates/skel/webroot/css.php
===================================================================
--- trunk/src/Web/cake/console/templates/skel/webroot/css.php	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/webroot/css.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,97 @@
+<?php
+/**
+ * CSS Functions
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.webroot
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+if (!defined('CAKE_CORE_INCLUDE_PATH')) {
+	header('HTTP/1.1 404 Not Found');
+	exit('File Not Found');
+}
+
+/**
+ * Ensure required files are included
+ */
+if (!class_exists('File')) {
+	require LIBS . 'file.php';
+}
+
+/**
+ * Make clean CSS
+ *
+ * @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;
+	}
+
+/**
+ * Write CSS cache
+ *
+ * @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)) {
+		exit('Wrong file name.');
+	}
+
+	$filename = 'css/' . $regs[1];
+	$filepath = CSS . $regs[1];
+	$cachepath = CACHE . 'css' . DS . str_replace(array('/','\\'), '-', $regs[1]);
+
+	if (!file_exists($filepath)) {
+		exit('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;

Added: trunk/src/Web/cake/console/templates/skel/webroot/favicon.ico
===================================================================
(Binary files differ)


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

Added: trunk/src/Web/cake/console/templates/skel/webroot/img/cake.icon.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/console/templates/skel/webroot/img/cake.icon.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/console/templates/skel/webroot/img/cake.power.gif
===================================================================
(Binary files differ)


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

Added: trunk/src/Web/cake/console/templates/skel/webroot/img/test-error-icon.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/console/templates/skel/webroot/img/test-error-icon.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/console/templates/skel/webroot/img/test-fail-icon.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/console/templates/skel/webroot/img/test-fail-icon.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/console/templates/skel/webroot/img/test-pass-icon.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/console/templates/skel/webroot/img/test-pass-icon.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/console/templates/skel/webroot/img/test-skip-icon.png
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/console/templates/skel/webroot/img/test-skip-icon.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/console/templates/skel/webroot/index.php
===================================================================
--- trunk/src/Web/cake/console/templates/skel/webroot/index.php	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/webroot/index.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,87 @@
+<?php
+/**
+ * Index
+ *
+ * The Front Controller for handling every request
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.webroot
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+/**
+ * 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 (php_sapi_name() == 'cli-server') {
+		$_SERVER['PHP_SELF'] = '/'.basename(__FILE__);
+	}
+	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();
+	}

Added: trunk/src/Web/cake/console/templates/skel/webroot/js/empty
===================================================================
Added: trunk/src/Web/cake/console/templates/skel/webroot/test.php
===================================================================
--- trunk/src/Web/cake/console/templates/skel/webroot/test.php	                        (rev 0)
+++ trunk/src/Web/cake/console/templates/skel/webroot/test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,94 @@
+<?php
+/**
+ * Web Access Frontend for TestSuite
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.libs
+ * @since         CakePHP(tm) v 1.2.0.4433
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+set_time_limit(0);
+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 = App::core('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);
+}
+
+if (Configure::read('debug') < 1) {
+	die(__('Debug setting does not allow access to this url.', true));
+}
+
+require_once CAKE_TESTS_LIB . 'cake_test_suite_dispatcher.php';
+
+$Dispatcher = new CakeTestSuiteDispatcher();
+$Dispatcher->dispatch();

Added: trunk/src/Web/cake/dispatcher.php
===================================================================
--- trunk/src/Web/cake/dispatcher.php	                        (rev 0)
+++ trunk/src/Web/cake/dispatcher.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,662 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * List of helpers to include
+ */
+App::import('Core', 'Router');
+App::import('Controller', 'Controller', false);
+
+/**
+ * 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;
+
+/**
+ * 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()) {
+		if ($this->base === false) {
+			$this->base = $this->baseUrl();
+		}
+
+		if (is_array($url)) {
+			$url = $this->__extractParams($url, $additionalParams);
+		} else {
+			if ($url) {
+				$_GET['url'] = $url;
+			}
+			$url = $this->getUrl();
+			$this->params = array_merge($this->parseParams($url), $additionalParams);
+		}
+		$this->here = $this->base . '/' . $url;
+
+		if ($this->asset($url) || $this->cached($url)) {
+			return;
+		}
+		$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 = $this->params['action'][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'], '_') > 0) {
+				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 = isset($this->params['plugin']) ? $this->params['plugin'] : null;
+		$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 (isset($this->params['return']) && $this->params['return'] == 1) {
+			$controller->autoRender = false;
+		}
+		if (!empty($this->params['bare'])) {
+			$controller->autoLayout = false;
+		}
+		return $this->_invoke($controller, $this->params);
+	}
+
+/**
+ * Initializes the components and models a controller will be using.
+ * Triggers the controller action, and invokes the rendering if Controller::$autoRender is true and echo's the output.
+ * Otherwise the return value of the controller action are returned.
+ *
+ * @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->startupProcess();
+
+		$methods = array_flip($controller->methods);
+
+		if (!isset($methods[strtolower($params['action'])])) {
+			if ($controller->scaffold !== false) {
+				App::import('Controller', 'Scaffold', false);
+				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 = call_user_func_array(array(&$controller, $params['action']), $params['pass']);
+
+		if ($controller->autoRender) {
+			$controller->output = $controller->render();
+		} elseif (empty($controller->output)) {
+			$controller->output = $output;
+		}
+		$controller->shutdownProcess();
+
+		if (isset($params['return'])) {
+			return $controller->output;
+		}
+		echo($controller->output);
+	}
+
+/**
+ * Sets the params when $url is passed as an array to Object::requestAction();
+ * Merges the $url and $additionalParams and creates a string url.
+ *
+ * @param array $url Array or request parameters
+ * @param array $additionalParams Array of additional parameters.
+ * @return string $url The generated url string.
+ * @access private
+ */
+	function __extractParams($url, $additionalParams = array()) {
+		$defaults = array('pass' => array(), 'named' => array(), 'form' => array());
+		$params = array_merge($defaults, $url, $additionalParams);
+		$this->params = $params;
+
+		$params += array('base' => false, 'url' => array());
+		return ltrim(Router::reverse($params), '/');
+	}
+
+/**
+ * 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 (!empty($_SERVER)) {
+					$_SERVER['REQUEST_METHOD'] = $params['form']['_method'];
+				} else {
+					$_ENV['REQUEST_METHOD'] = $params['form']['_method'];
+				}
+				unset($params['form']['_method']);
+			}
+		}
+		$namedExpressions = Router::getNamedExpressions();
+		extract($namedExpressions);
+		include CONFIGS . 'routes.php';
+		$params = array_merge(array('controller' => '', 'action' => ''), Router::parse($fromUrl), $params);
+
+		if (empty($params['action'])) {
+			$params['action'] = 'index';
+		}
+		if (isset($params['form']['data'])) {
+			$params['data'] = $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) {
+					if (is_array($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;
+							}
+						}
+					} else {
+						$params['data'][$model][$key] = $fields;
+					}
+				}
+			}
+		}
+		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) {
+			$replace = array('<', '>', '*', '\'', '"');
+			$base = str_replace($replace, '', 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 = '/' . basename($baseUrl);
+		$base = dirname($baseUrl);
+
+		if ($base === DS || $base === '.') {
+			$base = '';
+		}
+		$this->webroot = $base . '/';
+
+		$docRoot = realpath(env('DOCUMENT_ROOT'));
+		$docRootContainsWebroot = strpos($docRoot, $dir . '/' . $webroot);
+
+		if (!empty($base) || !$docRootContainsWebroot) {
+			if (strpos($this->webroot, '/' . $dir . '/') === false) {
+				$this->webroot .= $dir . '/' ;
+			}
+			if (strpos($this->webroot, '/' . $webroot . '/') === false) {
+				$this->webroot .= $webroot . '/';
+			}
+		}
+		return $base . $file;
+	}
+
+/**
+ * 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() {
+		$controller = false;
+		$ctrlClass = $this->__loadController($this->params);
+		if (!$ctrlClass) {
+			return $controller;
+		}
+		$ctrlClass .= 'Controller';
+		if (class_exists($ctrlClass)) {
+			$controller =& new $ctrlClass();
+		}
+		return $controller;
+	}
+
+/**
+ * Load controller and return controller classname
+ *
+ * @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'])) {
+			$pluginName = $controller = Inflector::camelize($params['plugin']);
+			$pluginPath = $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 = explode('?', $uri, 2);
+
+			if (isset($uri[1])) {
+				parse_str($uri[1], $_GET);
+			}
+			$uri = $uri[0];
+		} else {
+			$uri = env('QUERY_STRING');
+		}
+		if (is_string($uri) && 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) === 0) {
+					$elements = explode($base, $uri, 2);
+				} 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 view cache
+ *
+ * @param string $url Requested URL
+ * @access public
+ */
+	function cached($url) {
+		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('View', 'View', false);
+				}
+				$controller = null;
+				$view =& new View($controller);
+				$return = $view->renderCache($filename, getMicrotime());
+				if (!$return) {
+					ClassRegistry::removeObject('view');
+				}
+				return $return;
+			}
+		}
+		return false;
+	}
+
+/**
+ * Checks if a requested asset exists and sends it to the browser
+ *
+ * @param $url string $url Requested URL
+ * @return boolean True on success if the asset file was found and sent
+ * @access public
+ */
+	function asset($url) {
+		if (strpos($url, '..') !== false || strpos($url, '.') === false) {
+			return false;
+		}
+		$filters = Configure::read('Asset.filter');
+		$isCss = (
+			strpos($url, 'ccss/') === 0 ||
+			preg_match('#^(theme/([^/]+)/ccss/)|(([^/]+)(?<!css)/ccss)/#i', $url)
+		);
+		$isJs = (
+			strpos($url, 'cjs/') === 0 ||
+			preg_match('#^/((theme/[^/]+)/cjs/)|(([^/]+)(?<!js)/cjs)/#i', $url)
+		);
+
+		if (($isCss && empty($filters['css'])) || ($isJs && empty($filters['js']))) {
+			header('HTTP/1.1 404 Not Found');
+			return $this->_stop();
+		} elseif ($isCss) {
+			include WWW_ROOT . DS . $filters['css'];
+			$this->_stop();
+		} elseif ($isJs) {
+			include WWW_ROOT . DS . $filters['js'];
+			$this->_stop();
+		}
+		$controller = null;
+		$ext = array_pop(explode('.', $url));
+		$parts = explode('/', $url);
+		$assetFile = null;
+
+		if ($parts[0] === 'theme') {
+			$themeName = $parts[1];
+			unset($parts[0], $parts[1]);
+			$fileFragment = implode(DS, $parts);
+			$path = App::themePath($themeName) . 'webroot' . DS;
+			if (file_exists($path . $fileFragment)) {
+				$assetFile = $path . $fileFragment;
+			}
+		} else {
+			$plugin = $parts[0];
+			unset($parts[0]);
+			$fileFragment = implode(DS, $parts);
+			$pluginWebroot = App::pluginPath($plugin) . 'webroot' . DS;
+			if (file_exists($pluginWebroot . $fileFragment)) {
+				$assetFile = $pluginWebroot . $fileFragment;
+			}
+		}
+
+		if ($assetFile !== null) {
+			$this->_deliverAsset($assetFile, $ext);
+			return true;
+		}
+		return false;
+	}
+
+/**
+ * Sends an asset file to the client
+ *
+ * @param string $assetFile Path to the asset file in the file system
+ * @param string $ext The extension of the file to determine its mime type
+ * @return void
+ * @access protected
+ */
+	function _deliverAsset($assetFile, $ext) {
+		$ob = @ini_get("zlib.output_compression") !== '1' && extension_loaded("zlib") && (strpos(env('HTTP_ACCEPT_ENCODING'), 'gzip') !== false);
+		$compressionEnabled = $ob && Configure::read('Asset.compress');
+		if ($compressionEnabled) {
+			ob_start();
+			ob_start('ob_gzhandler');
+		}
+
+		App::import('View', 'Media');
+		$controller = null;
+		$Media = new MediaView($controller);
+		if (isset($Media->mimeType[$ext])) {
+			$contentType = $Media->mimeType[$ext];
+		} else {
+			$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("Date: " . date("D, j M Y G:i:s ", filemtime($assetFile)) . 'GMT');
+		header('Content-type: ' . $contentType);
+		header("Expires: " . gmdate("D, j M Y H:i:s", time() + DAY) . " GMT");
+		header("Cache-Control: cache");
+		header("Pragma: cache");
+
+		if ($ext === 'css' || $ext === 'js') {
+			include($assetFile);
+		} else {
+			if ($compressionEnabled) {
+				ob_clean();
+			}
+			readfile($assetFile);
+		}
+
+		if ($compressionEnabled) {
+			ob_end_flush();
+		}
+	}
+}

Added: trunk/src/Web/cake/libs/cache/apc.php
===================================================================
--- trunk/src/Web/cake/libs/cache/apc.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/cache/apc.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,127 @@
+<?php
+/**
+ * APC storage engine for cache.
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.cache
+ * @since         CakePHP(tm) v 1.2.0.4933
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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) {
+		if ($duration == 0) {
+			$expires = 0;
+		} else {
+			$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 !== 0 && ($cachetime < $time || ($time + $this->settings['duration']) < $cachetime)) {
+			return false;
+		}
+		return apc_fetch($key);
+	}
+
+/**
+ * Increments the value of an integer cached key
+ *
+ * @param string $key Identifier for the data
+ * @param integer $offset How much to increment
+ * @param integer $duration How long to cache the data, in seconds
+ * @return New incremented value, false otherwise
+ * @access public
+ */
+	function increment($key, $offset = 1) {
+		return apc_inc($key, $offset);
+	}
+
+/**
+ * Decrements the value of an integer cached key
+ *
+ * @param string $key Identifier for the data
+ * @param integer $offset How much to substract
+ * @param integer $duration How long to cache the data, in seconds
+ * @return New decremented value, false otherwise
+ * @access public
+ */
+	function decrement($key, $offset = 1) {
+		return apc_dec($key, $offset);
+	}
+
+/**
+ * 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');
+	}
+}

Added: trunk/src/Web/cake/libs/cache/file.php
===================================================================
--- trunk/src/Web/cake/libs/cache/file.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/cache/file.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,286 @@
+<?php
+/**
+ * File Storage engine for cache
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.cache
+ * @since         CakePHP(tm) v 1.2.0.4933
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+if (!class_exists('File')) {
+	require LIBS . 'file.php';
+}
+/**
+ * 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 File
+ * @access protected
+ */
+	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();
+
+/**
+ * True unless FileEngine::__active(); fails
+ *
+ * @var boolean
+ * @access protected
+ */
+	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)) {
+			$this->_File =& new File($this->settings['path'] . DS . 'cake');
+		}
+
+		if (DIRECTORY_SEPARATOR === '\\') {
+			$this->settings['isWindows'] = true;
+		}
+
+		$path = $this->_File->Folder->cd($this->settings['path']);
+		if ($path) {
+			$this->settings['path'] = $path;
+		}
+		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);
+			}
+		}
+
+		$expires = time() + $duration;
+		$contents = $expires . $lineBreak . $data . $lineBreak;
+		$old = umask(0);
+		$handle = fopen($this->_File->path, 'a');
+		umask($old);
+
+		if (!$handle) {
+			return false;
+		}
+
+		if ($this->settings['lock']) {
+		    flock($handle, LOCK_EX);
+		}
+
+		$success = ftruncate($handle, 0) && fwrite($handle, $contents) && fflush($handle);
+
+		if ($this->settings['lock']) {
+		    flock($handle, LOCK_UN);
+		}
+
+		fclose($handle);
+		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();
+			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'];
+		}
+		$prefixLength = strlen($this->settings['prefix']);
+		while (($entry = $dir->read()) !== false) {
+			if (substr($entry, 0, $prefixLength) !== $this->settings['prefix']) {
+				continue;
+			}
+			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->_init && !is_writable($this->settings['path'])) {
+			$this->_init = false;
+			trigger_error(sprintf(__('%s is not writable', true), $this->settings['path']), E_USER_WARNING);
+			return false;
+		}
+		return true;
+	}
+}

Added: trunk/src/Web/cake/libs/cache/memcache.php
===================================================================
--- trunk/src/Web/cake/libs/cache/memcache.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/cache/memcache.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,223 @@
+<?php
+/**
+ * Memcache storage engine for cache
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.cache
+ * @since         CakePHP(tm) v 1.2.0.4933
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Memcache storage engine for cache.  Memcache has some limitations in the amount of 
+ * control you have over expire times far in the future.  See MemcacheEngine::write() for
+ * more information.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.cache
+ */
+class MemcacheEngine extends CacheEngine {
+
+/**
+ * Memcache wrapper.
+ *
+ * @var Memcache
+ * @access private
+ */
+	var $__Memcache = null;
+
+/**
+ * Settings
+ *
+ *  - servers = string or array of memcache servers, default => 127.0.0.1. If an
+ *    array MemcacheEngine will use them as a pool.
+ *  - 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,
+			'persistent' => true
+			), $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) {
+				list($host, $port) = $this->_parseServerString($server);
+				if ($this->__Memcache->addServer($host, $port, $this->settings['persistent'])) {
+					$return = true;
+				}
+			}
+			return $return;
+		}
+		return true;
+	}
+
+/**
+ * Parses the server address into the host/port.  Handles both IPv6 and IPv4
+ * addresses and Unix sockets
+ *
+ * @param string $server The server address string.
+ * @return array Array containing host, port
+ */
+	function _parseServerString($server) {
+		if ($server[0] == 'u') {
+			return array($server, 0);
+		}
+		if (substr($server, 0, 1) == '[') {
+			$position = strpos($server, ']:');
+			if ($position !== false) {
+				$position++;
+			}
+		} else {
+		    $position = strpos($server, ':');
+		}
+		$port = 11211;
+		$host = $server;
+		if ($position !== false) {
+			$host = substr($server, 0, $position);
+			$port = substr($server, $position + 1);
+		}
+		return array($host, $port);
+	}
+
+/**
+ * Write data for key into cache.  When using memcache as your cache engine
+ * remember that the Memcache pecl extension does not support cache expiry times greater 
+ * than 30 days in the future. Any duration greater than 30 days will be treated as never expiring.
+ *
+ * @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
+ * @see http://php.net/manual/en/memcache.set.php
+ * @access public
+ */
+	function write($key, &$value, $duration) {
+		if ($duration > 30 * DAY) {
+			$duration = 0;
+		}
+		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) {
+		return $this->__Memcache->get($key);
+	}
+
+/**
+ * Increments the value of an integer cached key
+ *
+ * @param string $key Identifier for the data
+ * @param integer $offset How much to increment
+ * @param integer $duration How long to cache the data, in seconds
+ * @return New incremented value, false otherwise
+ * @access public
+ */
+	function increment($key, $offset = 1) {
+		if ($this->settings['compress']) {
+			trigger_error(sprintf(__('Method increment() not implemented for compressed cache in %s', true), get_class($this)), E_USER_ERROR);
+		}
+		return $this->__Memcache->increment($key, $offset);
+	}
+
+/**
+ * Decrements the value of an integer cached key
+ *
+ * @param string $key Identifier for the data
+ * @param integer $offset How much to substract
+ * @param integer $duration How long to cache the data, in seconds
+ * @return New decremented value, false otherwise
+ * @access public
+ */
+	function decrement($key, $offset = 1) {
+		if ($this->settings['compress']) {
+			trigger_error(sprintf(__('Method decrement() not implemented for compressed cache in %s', true), get_class($this)), E_USER_ERROR);
+		}
+		return $this->__Memcache->decrement($key, $offset);
+	}
+
+/**
+ * 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;
+	}
+}

Added: trunk/src/Web/cake/libs/cache/xcache.php
===================================================================
--- trunk/src/Web/cake/libs/cache/xcache.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/cache/xcache.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,183 @@
+<?php
+/**
+ * Xcache storage engine for cache.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.cache
+ * @since         CakePHP(tm) v 1.2.0.4947
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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;
+	}
+
+/**
+ * Increments the value of an integer cached key
+ * If the cache key is not an integer it will be treated as 0
+ *
+ * @param string $key Identifier for the data
+ * @param integer $offset How much to increment
+ * @param integer $duration How long to cache the data, in seconds
+ * @return New incremented value, false otherwise
+ * @access public
+ */
+	function increment($key, $offset = 1) {
+		return xcache_inc($key, $offset);
+	}
+
+/**
+ * Decrements the value of an integer cached key.
+ * If the cache key is not an integer it will be treated as 0
+ *
+ * @param string $key Identifier for the data
+ * @param integer $offset How much to substract
+ * @param integer $duration How long to cache the data, in seconds
+ * @return New decremented value, false otherwise
+ * @access public
+ */
+	function decrement($key, $offset = 1) {
+		return xcache_dec($key, $offset);
+	}
+/**
+ * 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();
+		$max = xcache_count(XC_TYPE_VAR);
+		for ($i = 0; $i < $max; $i++) {
+			xcache_clear_cache(XC_TYPE_VAR, $i);
+		}
+		$this->__auth(true);
+		return true;
+	}
+
+/**
+ * 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;
+				}
+			}
+		}
+	}
+}

Added: trunk/src/Web/cake/libs/cache.php
===================================================================
--- trunk/src/Web/cake/libs/cache.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/cache.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,702 @@
+<?php
+/**
+ * Caching for CakePHP.
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.0.4933
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Caching for CakePHP.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class Cache {
+
+/**
+ * Cache configuration stack
+ * Keeps the permanent/default settings for each cache engine.
+ * These settings are used to reset the engines after temporary modification.
+ *
+ * @var array
+ * @access private
+ */
+	var $__config = array();
+
+/**
+ * Holds name of the current configuration name being used.
+ *
+ * @var array
+ * @access private
+ */
+	var $__name = 'default';
+
+/**
+ * Whether to reset the settings with the next call to Cache::set();
+ *
+ * @var array
+ * @access private
+ */
+	var $__reset = false;
+
+/**
+ * Engine instances keyed by configuration name.
+ *
+ * @var array
+ */
+	var $_engines = array();
+
+/**
+ * Returns a singleton instance
+ *
+ * @return object
+ * @access public
+ * @static
+ */
+	function &getInstance() {
+		static $instance = array();
+		if (!$instance) {
+			$instance[0] =& new Cache();
+		}
+		return $instance[0];
+	}
+
+/**
+ * Set the cache configuration to use.  config() can
+ * both create new configurations, return the settings for already configured
+ * configurations.  It also sets the 'default' configuration to use for subsequent
+ * operations.
+ *
+ * To create a new configuration:
+ *
+ * `Cache::config('my_config', array('engine' => 'File', 'path' => TMP));`
+ *
+ * To get the settings for a configuration, and set it as the currently selected configuration
+ *
+ * `Cache::config('default');`
+ *
+ * The following keys are used in core cache engines:
+ *
+ * - `duration` Specify how long items in this cache configuration last.
+ * - `prefix` Prefix appended to all entries. Good for when you need to share a keyspace
+ *    with either another cache config or annother application.
+ * - `probability` Probability of hitting a cache gc cleanup.  Setting to 0 will disable 
+ *    cache::gc from ever being called automatically.
+ * - `servers' Used by memcache. Give the address of the memcached servers to use.
+ * - `compress` Used by memcache.  Enables memcache's compressed format.
+ * - `serialize` Used by FileCache.  Should cache objects be serialized first.
+ * - `path` Used by FileCache.  Path to where cachefiles should be saved.
+ * - `lock` Used by FileCache.  Should files be locked before writing to them?
+ * - `user` Used by Xcache.  Username for XCache
+ * - `password` Used by Xcache.  Password for XCache
+ *
+ * @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()) {
+		$self =& Cache::getInstance();
+		if (is_array($name)) {
+			$settings = $name;
+		}
+
+		if ($name === null || !is_string($name)) {
+			$name = $self->__name;
+		}
+
+		$current = array();
+		if (isset($self->__config[$name])) {
+			$current = $self->__config[$name];
+		}
+
+		if (!empty($settings)) {
+			$self->__config[$name] = array_merge($current, $settings);
+		}
+
+		if (empty($self->__config[$name]['engine'])) {
+			return false;
+		}
+
+		$engine = $self->__config[$name]['engine'];
+		$self->__name = $name;
+
+		if (!isset($self->_engines[$name])) {
+			$self->_buildEngine($name);
+			$settings = $self->__config[$name] = $self->settings($name);
+		} elseif ($settings = $self->set($self->__config[$name])) {
+			$self->__config[$name] = $settings;
+		}
+		return compact('engine', 'settings');
+	}
+
+/**
+ * Finds and builds the instance of the required engine class.
+ *
+ * @param string $name Name of the config array that needs an engine instance built
+ * @return void
+ * @access protected
+ */
+	function _buildEngine($name) {
+		$config = $this->__config[$name];
+
+		list($plugin, $class) = pluginSplit($config['engine']);
+		$cacheClass = $class . 'Engine';
+		if (!class_exists($cacheClass) && $this->__loadEngine($class, $plugin) === false) {
+			return false;
+		}
+		$cacheClass = $class . 'Engine';
+		$this->_engines[$name] =& new $cacheClass();
+		if ($this->_engines[$name]->init($config)) {
+			if ($this->_engines[$name]->settings['probability'] && time() % $this->_engines[$name]->settings['probability'] === 0) {
+				$this->_engines[$name]->gc();
+			}
+			return true;
+		}
+		return false;
+	}
+
+/**
+ * Returns an array containing the currently configured Cache settings.
+ *
+ * @return array Array of configured Cache config names.
+ */
+	function configured() {
+		$self =& Cache::getInstance();
+		return array_keys($self->__config);
+	}
+
+/**
+ * Drops a cache engine.  Deletes the cache configuration information
+ * If the deleted configuration is the last configuration using an certain engine,
+ * the Engine instance is also unset.
+ *
+ * @param string $name A currently configured cache config you wish to remove.
+ * @return boolen success of the removal, returns false when the config does not exist.
+ */
+	function drop($name) {
+		$self =& Cache::getInstance();
+		if (!isset($self->__config[$name])) {
+			return false;
+		}
+		unset($self->__config[$name]);
+		unset($self->_engines[$name]);
+		return true;
+	}
+
+/**
+ * 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, $plugin = null) {
+		if ($plugin) {
+			return App::import('Lib', $plugin . '.cache' . DS . $name, false);
+		} else {
+			$core = App::core();
+			$path = $core['libs'][0] . 'cache' . DS . strtolower($name) . '.php';
+			if (file_exists($path)) {
+				require $path;
+				return true;
+			}
+			return App::import('Lib', 'cache' . DS . $name, 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 Array of settings.
+ * @access public
+ * @static
+ */
+	function set($settings = array(), $value = null) {
+		$self =& Cache::getInstance();
+		if (!isset($self->__config[$self->__name]) || !isset($self->_engines[$self->__name])) {
+			return false;
+		}
+		$name = $self->__name;
+		if (!empty($settings)) {
+			$self->__reset = true;
+		}
+
+		if ($self->__reset === true) {
+			if (empty($settings)) {
+				$self->__reset = false;
+				$settings = $self->__config[$name];
+			} else {
+				if (is_string($settings) && $value !== null) {
+					$settings = array($settings => $value);
+				}
+				$settings = array_merge($self->__config[$name], $settings);
+				if (isset($settings['duration']) && !is_numeric($settings['duration'])) {
+					$settings['duration'] = strtotime($settings['duration']) - time();
+				}
+			}
+			$self->_engines[$name]->settings = $settings;
+		}
+		return $self->settings($name);
+	}
+
+/**
+ * Garbage collection
+ *
+ * Permanently remove all expired and deleted data
+ *
+ * @return void
+ * @access public
+ * @static
+ */
+	function gc() {
+		$self =& Cache::getInstance();
+		$self->_engines[$self->__name]->gc();
+	}
+
+/**
+ * Write data for key into cache. Will automatically use the currently
+ * active cache configuration.  To set the currently active configuration use
+ * Cache::config()
+ *
+ * ### Usage:
+ *
+ * Writing to the active cache config:
+ *
+ * `Cache::write('cached_data', $data);`
+ *
+ * Writing to a specific cache config:
+ *
+ * `Cache::write('cached_data', $data, 'long_term');`
+ *
+ * @param string $key Identifier for the data
+ * @param mixed $value Data to be cached - anything except a resource
+ * @param string $config Optional string configuration name to write to.
+ * @return boolean True if the data was successfully cached, false on failure
+ * @access public
+ * @static
+ */
+	function write($key, $value, $config = null) {
+		$self =& Cache::getInstance();
+
+		if (!$config) {
+			$config = $self->__name;
+		}
+		$settings = $self->settings($config);
+
+		if (empty($settings)) {
+			return null;
+		}
+		if (!$self->isInitialized($config)) {
+			return false;
+		}
+		$key = $self->_engines[$config]->key($key);
+
+		if (!$key || is_resource($value)) {
+			return false;
+		}
+
+		$success = $self->_engines[$config]->write($settings['prefix'] . $key, $value, $settings['duration']);
+		$self->set();
+		return $success;
+	}
+
+/**
+ * Read a key from the cache.  Will automatically use the currently
+ * active cache configuration.  To set the currently active configuration use
+ * Cache::config()
+ *
+ * ### Usage:
+ *
+ * Reading from the active cache configuration.
+ *
+ * `Cache::read('my_data');`
+ *
+ * Reading from a specific cache configuration.
+ *
+ * `Cache::read('my_data', 'long_term');`
+ *
+ * @param string $key Identifier for the data
+ * @param string $config optional 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) {
+		$self =& Cache::getInstance();
+
+		if (!$config) {
+			$config = $self->__name;
+		}
+		$settings = $self->settings($config);
+
+		if (empty($settings)) {
+			return null;
+		}
+		if (!$self->isInitialized($config)) {
+			return false;
+		}
+		$key = $self->_engines[$config]->key($key);
+		if (!$key) {
+			return false;
+		}
+		$success = $self->_engines[$config]->read($settings['prefix'] . $key);
+
+		if ($config !== null && $config !== $self->__name) {
+			$self->set();
+		}
+		return $success;
+	}
+
+/**
+ * Increment a number under the key and return incremented value.
+ *
+ * @param string $key Identifier for the data
+ * @param integer $offset How much to add
+ * @param string $config Optional string configuration name.  If not specified the current
+ *   default config will be used.
+ * @return mixed new value, or false if the data doesn't exist, is not integer,
+ *    or if there was an error fetching it.
+ * @access public
+ */
+	function increment($key, $offset = 1, $config = null) {
+		$self =& Cache::getInstance();
+
+		if (!$config) {
+			$config = $self->__name;
+		}
+		$settings = $self->settings($config);
+
+		if (empty($settings)) {
+			return null;
+		}
+		if (!$self->isInitialized($config)) {
+			return false;
+		}
+		$key = $self->_engines[$config]->key($key);
+
+		if (!$key || !is_integer($offset) || $offset < 0) {
+			return false;
+		}
+		$success = $self->_engines[$config]->increment($settings['prefix'] . $key, $offset);
+		$self->set();
+		return $success;
+	}
+/**
+ * Decrement a number under the key and return decremented value.
+ *
+ * @param string $key Identifier for the data
+ * @param integer $offset How much to substract
+ * @param string $config Optional string configuration name, if not specified the current
+ *   default config will be used.
+ * @return mixed new value, or false if the data doesn't exist, is not integer,
+ *   or if there was an error fetching it
+ * @access public
+ */
+	function decrement($key, $offset = 1, $config = null) {
+		$self =& Cache::getInstance();
+
+		if (!$config) {
+			$config = $self->__name;
+		}
+		$settings = $self->settings($config);
+
+		if (empty($settings)) {
+			return null;
+		}
+		if (!$self->isInitialized($config)) {
+			return false;
+		}
+		$key = $self->_engines[$config]->key($key);
+
+		if (!$key || !is_integer($offset) || $offset < 0) {
+			return false;
+		}
+		$success = $self->_engines[$config]->decrement($settings['prefix'] . $key, $offset);
+		$self->set();
+		return $success;
+	}
+/**
+ * Delete a key from the cache. Will automatically use the currently
+ * active cache configuration.  To set the currently active configuration use
+ * Cache::config()
+ *
+ * ### Usage:
+ *
+ * Deleting from the active cache configuration.
+ *
+ * `Cache::delete('my_data');`
+ *
+ * Deleting from a specific cache configuration.
+ *
+ * `Cache::delete('my_data', 'long_term');`
+ *
+ * @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) {
+		$self =& Cache::getInstance();
+		if (!$config) {
+			$config = $self->__name;
+		}
+		$settings = $self->settings($config);
+
+		if (empty($settings)) {
+			return null;
+		}
+		if (!$self->isInitialized($config)) {
+			return false;
+		}
+		$key = $self->_engines[$config]->key($key);
+		if (!$key) {
+			return false;
+		}
+
+		$success = $self->_engines[$config]->delete($settings['prefix'] . $key);
+		$self->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) {
+		$self =& Cache::getInstance();
+		if (!$config) {
+			$config = $self->__name;
+		}
+		$settings = $self->settings($config);
+
+		if (empty($settings)) {
+			return null;
+		}
+
+		if (!$self->isInitialized($config)) {
+			return false;
+		}
+		$success = $self->_engines[$config]->clear($check);
+		$self->set();
+		return $success;
+	}
+
+/**
+ * Check if Cache has initialized a working config for the given name.
+ *
+ * @param string $engine Name of the engine
+ * @param string $config Name of the configuration setting
+ * @return bool Whether or not the config name has been initialized.
+ * @access public
+ * @static
+ */
+	function isInitialized($name = null) {
+		if (Configure::read('Cache.disable')) {
+			return false;
+		}
+		$self =& Cache::getInstance();
+		if (!$name && isset($self->__config[$self->__name])) {
+			$name = $self->__name;
+		}
+		return isset($self->_engines[$name]);
+	}
+
+/**
+ * Return the settings for current cache engine. If no name is supplied the settings
+ * for the 'active default' configuration will be returned.  To set the 'active default'
+ * configuration use `Cache::config()`
+ *
+ * @param string $engine Name of the configuration to get settings for.
+ * @return array list of settings for this engine
+ * @see Cache::config()
+ * @access public
+ * @static
+ */
+	function settings($name = null) {
+		$self =& Cache::getInstance();
+		if (!$name && isset($self->__config[$self->__name])) {
+			$name = $self->__name;
+		}
+		if (!empty($self->_engines[$name])) {
+			return $self->_engines[$name]->settings();
+		}
+		return array();
+	}
+
+/**
+ * Write the session when session data is persisted with cache.
+ *
+ * @return void
+ * @access public
+ */ 
+	function __destruct() {
+		if (Configure::read('Session.save') == 'cache' && function_exists('session_write_close')) {
+			session_write_close();
+		}
+	}
+}
+
+/**
+ * Storage engine for CakePHP caching
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class CacheEngine {
+
+/**
+ * Settings of current engine instance
+ *
+ * @var int
+ * @access public
+ */
+	var $settings = array();
+
+/**
+ * Initialize 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);
+	}
+
+/**
+ * Increment a number under the key and return incremented value
+ *
+ * @param string $key Identifier for the data
+ * @param integer $offset How much to add
+ * @return New incremented value, false otherwise
+ * @access public
+ */
+	function increment($key, $offset = 1) {
+		trigger_error(sprintf(__('Method increment() not implemented in %s', true), get_class($this)), E_USER_ERROR);
+	}
+/**
+ * Decrement a number under the key and return decremented value
+ *
+ * @param string $key Identifier for the data
+ * @param integer $value How much to substract
+ * @return New incremented value, false otherwise
+ * @access public
+ */
+	function decrement($key, $offset = 1) {
+		trigger_error(sprintf(__('Method decrement() 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 for use with cache engine storage engines.
+ *
+ * @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;
+	}
+}

Added: trunk/src/Web/cake/libs/cake_log.php
===================================================================
--- trunk/src/Web/cake/libs/cake_log.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/cake_log.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,296 @@
+<?php
+/**
+ * Logging.
+ *
+ * Log messages to text files.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.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 configured Log adapters.  One or more adapters can be configured
+ * using CakeLogs's methods.  If you don't configure any adapters, and write to the logs
+ * a default FileLog will be autoconfigured for you.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class CakeLog {
+
+/**
+ * An array of connected streams.
+ * Each stream represents a callable that will be called when write() is called.
+ *
+ * @var array
+ * @access protected
+ */
+	var $_streams = array();
+
+/**
+ * Get an instance
+ *
+ * @return void
+ * @static
+ */
+	function &getInstance() {
+		static $instance = array();
+		if (!isset($instance[0])) {
+			$instance[0] =& new CakeLog();
+		}
+		return $instance[0];
+	}
+
+/**
+ * Configure and add a new logging stream to CakeLog
+ * You can use add loggers from app/libs use app.loggername, or any plugin/libs using plugin.loggername.
+ *
+ * ### Usage:
+ *
+ * {{{
+ * CakeLog::config('second_file', array(
+ * 		'engine' => 'FileLog',
+ * 		'path' => '/var/logs/my_app/'
+ * ));
+ * }}}
+ *
+ * Will configure a FileLog instance to use the specified path.  All options that are not `engine`
+ * are passed onto the logging adapter, and handled there.  Any class can be configured as a logging
+ * adapter as long as it implements a `write` method with the following signature.
+ *
+ * `write($type, $message)`
+ *
+ * For an explaination of these parameters, see CakeLog::write()
+ *
+ * @param string $key The keyname for this logger, used to remove the logger later.
+ * @param array $config Array of configuration information for the logger
+ * @return boolean success of configuration.
+ * @static
+ */
+	function config($key, $config) {
+		if (empty($config['engine'])) {
+			trigger_error(__('Missing logger classname', true), E_USER_WARNING);
+			return false;
+		}
+		$self =& CakeLog::getInstance();
+		$className = $self->_getLogger($config['engine']);
+		if (!$className) {
+			return false;
+		}
+		unset($config['engine']);
+		$self->_streams[$key] = new $className($config);
+		return true;
+	}
+
+/**
+ * Attempts to import a logger class from the various paths it could be on.
+ * Checks that the logger class implements a write method as well.
+ *
+ * @param string $loggerName the plugin.className of the logger class you want to build.
+ * @return mixed boolean false on any failures, string of classname to use if search was successful.
+ * @access protected
+ */
+	function _getLogger($loggerName) {
+		list($plugin, $loggerName) = pluginSplit($loggerName);
+
+		if ($plugin) {
+			App::import('Lib', $plugin . '.log/' . $loggerName);
+		} else {
+			if (!App::import('Lib', 'log/' . $loggerName)) {
+				App::import('Core', 'log/' . $loggerName);
+			}
+		}
+		if (!class_exists($loggerName)) {
+			trigger_error(sprintf(__('Could not load logger class %s', true), $loggerName), E_USER_WARNING);
+			return false;
+		}
+		if (!is_callable(array($loggerName, 'write'))) {
+			trigger_error(
+				sprintf(__('logger class %s does not implement a write method.', true), $loggerName),
+				E_USER_WARNING
+			);
+			return false;
+		}
+		return $loggerName;
+	}
+
+/**
+ * Returns the keynames of the currently active streams
+ *
+ * @return array Array of configured log streams.
+ * @access public
+ * @static
+ */
+	function configured() {
+		$self =& CakeLog::getInstance();
+		return array_keys($self->_streams);
+	}
+
+/**
+ * Removes a stream from the active streams.  Once a stream has been removed
+ * it will no longer have messages sent to it.
+ *
+ * @param string $keyname Key name of a configured stream to remove.
+ * @return void
+ * @access public
+ * @static
+ */
+	function drop($streamName) {
+		$self =& CakeLog::getInstance();
+		unset($self->_streams[$streamName]);
+	}
+
+/**
+ * Configures the automatic/default stream a FileLog.
+ *
+ * @return void
+ * @access protected
+ */
+	function _autoConfig() {
+		if (!class_exists('FileLog')) {
+			App::import('Core', 'log/FileLog');
+		}
+		$this->_streams['default'] =& new FileLog(array('path' => LOGS));
+	}
+
+/**
+ * Writes the given message and type to all of the configured log adapters.
+ * Configured adapters are passed both the $type and $message variables. $type
+ * is one of the following strings/values.
+ *
+ * ### Types:
+ *
+ * - `LOG_WARNING` => 'warning',
+ * - `LOG_NOTICE` => 'notice',
+ * - `LOG_INFO` => 'info',
+ * - `LOG_DEBUG` => 'debug',
+ * - `LOG_ERR` => 'error',
+ * - `LOG_ERROR` => 'error'
+ *
+ * ### Usage:
+ *
+ * Write a message to the 'warning' log:
+ * 
+ * `CakeLog::write('warning', 'Stuff is broken here');`
+ *
+ * @param string $type Type of message being written
+ * @param string $message Message content to log
+ * @return boolean Success
+ * @access public
+ * @static
+ */
+	function write($type, $message) {
+		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];
+		}
+		$self =& CakeLog::getInstance();
+		if (empty($self->_streams)) {
+			$self->_autoConfig();
+		}
+		$keys = array_keys($self->_streams);
+		foreach ($keys as $key) {
+			$logger =& $self->_streams[$key];
+			$logger->write($type, $message);
+		}
+		return true;
+	}
+
+/**
+ * An error_handler that will log errors to file using CakeLog::write();
+ * You can control how verbose and what type of errors this error_handler will 
+ * catch using `Configure::write('log', $value)`.  See core.php for more information.
+ *
+ *
+ * @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 void
+ */
+	function handleError($code, $description, $file = null, $line = null, $context = null) {
+		if ($code === 2048 || $code === 8192 || error_reporting() === 0) {
+			return;
+		}
+		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;
+			break;
+		}
+		$message = $error . ' (' . $code . '): ' . $description . ' in [' . $file . ', line ' . $line . ']';
+		CakeLog::write($level, $message);
+	}
+}
+
+if (!defined('DISABLE_DEFAULT_ERROR_HANDLING')) {
+	$cakeLog =& CakeLog::getInstance();
+	if (PHP5) {
+		set_error_handler(array($cakeLog, 'handleError'), error_reporting());
+	} else {
+		set_error_handler(array($cakeLog, 'handleError'));
+	}
+}

Added: trunk/src/Web/cake/libs/cake_session.php
===================================================================
--- trunk/src/Web/cake/libs/cake_session.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/cake_session.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,794 @@
+<?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.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v .0.10.0.1222
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+if (!class_exists('Security')) {
+	App::import('Core', 'Security');
+}
+
+/**
+ * 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;
+
+/**
+ * The number of seconds to set for session.cookie_lifetime.  0 means
+ * at browser close.
+ *
+ * @var integer
+ */
+	var $cookieLifeTime = 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;
+
+/**
+ * Hostname
+ *
+ * @var string
+ * @access public
+ */
+	var $host = null;
+
+/**
+ * Session timeout multiplier factor
+ *
+ * @var integer
+ * @access public
+ */
+	var $timeout = 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) {
+		App::import('Core', array('Set', 'Security'));
+		$this->time = time();
+
+		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'));
+			}
+		}
+		if (Configure::read('Session.save') === 'database') {
+			$modelName = Configure::read('Session.model');
+			$database = Configure::read('Session.database');
+			$table = Configure::read('Session.table');
+
+			if (empty($database)) {
+				$database = 'default';
+			}
+			$settings = array(
+				'class' => 'Session',
+				'alias' => 'Session',
+				'table' => 'cake_sessions',
+				'ds' => $database
+			);
+			if (!empty($modelName)) {
+				$settings['class'] = $modelName;
+			}
+			if (!empty($table)) {
+				$settings['table'] = $table;
+			}
+			ClassRegistry::init($settings);
+		}
+		if ($start === true) {
+			if (!empty($base)) {
+				$this->path = $base;
+				if (strpos($base, 'index.php') !== false) {
+				   $this->path = str_replace('index.php', '', $base);
+				}
+				if (strpos($base, '?') !== false) {
+				   $this->path = str_replace('?', '', $base);
+				}
+			}
+			$this->host = env('HTTP_HOST');
+
+			if (strpos($this->host, ':') !== false) {
+				$this->host = substr($this->host, 0, strpos($this->host, ':'));
+			}
+		}
+		if (isset($_SESSION) || $start === true) {
+			$this->sessionTime = $this->time + (Security::inactiveMins() * Configure::read('Session.timeout'));
+			$this->security = Configure::read('Security.level');
+		}
+		parent::__construct();
+	}
+
+/**
+ * Starts the Session.
+ *
+ * @return boolean True if session was started
+ * @access public
+ */
+	function start() {
+		if ($this->started()) {
+			return true;
+		}
+		if (function_exists('session_write_close')) {
+			session_write_close();
+		}
+		$this->__initSession();
+		$this->__startSession();
+		return $this->started();
+	}
+
+/**
+ * Determine if Session has been started.
+ *
+ * @access public
+ * @return boolean True if session has been started.
+ */
+	function started() {
+		if (isset($_SESSION) && session_id()) {
+			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) {
+		if (empty($name)) {
+			return false;
+		}
+		$result = Set::classicExtract($_SESSION, $name);
+		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 ($this->started()) {
+			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 delete($name) {
+		if ($this->check($name)) {
+			if (in_array($name, $this->watchKeys)) {
+				trigger_error(sprintf(__('Deleting session key {%s}', true), $name), E_USER_NOTICE);
+			}
+			$this->__overwrite($_SESSION, Set::remove($_SESSION, $name));
+			return ($this->check($name) == false);
+		}
+		$this->__setError(2, sprintf(__("%s doesn't exist", true), $name));
+		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::classicExtract($_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) {
+		if (empty($var)) {
+			return false;
+		}
+		if (!in_array($var, $this->watchKeys, true)) {
+			$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) {
+		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) {
+		if (empty($name)) {
+			return false;
+		}
+		if (in_array($name, $this->watchKeys)) {
+			trigger_error(sprintf(__('Writing session key {%s}: %s', true), $name, Debugger::exportVar($value)), E_USER_NOTICE);
+		}
+		$this->__overwrite($_SESSION, Set::insert($_SESSION, $name, $value));
+		return (Set::classicExtract($_SESSION, $name) === $value);
+	}
+
+/**
+ * Helper method to destroy invalid sessions.
+ *
+ * @return void
+ * @access public
+ */
+	function destroy() {
+		if ($this->started()) {
+			session_destroy();
+		}
+		$_SESSION = null;
+		$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);
+		}
+		if ($iniSet && ($this->security === 'high' || $this->security === 'medium')) {
+			ini_set('session.referer_check', $this->host);
+		}
+
+		if ($this->security == 'high') {
+			$this->cookieLifeTime = 0;
+		} else {
+			$this->cookieLifeTime = Configure::read('Session.timeout') * (Security::inactiveMins() * 60);
+		}
+
+		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.model') === null) {
+						trigger_error(__("You must set the all Configure::write('Session.*') in core.php to use database storage"), E_USER_WARNING);
+						$this->_stop();
+					}
+					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')) {
+						require LIBS . 'cache.php';
+					}
+					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('Cache', 'gc')
+				);
+			break;
+			default:
+				$config = CONFIGS . Configure::read('Session.save') . '.php';
+
+				if (is_file($config)) {
+					require($config);
+				}
+			break;
+		}
+	}
+
+/**
+ * Helper method to start a session
+ *
+ * @access private
+ */
+	function __startSession() {
+		if (headers_sent()) {
+			if (empty($_SESSION)) {
+				$_SESSION = array();
+			}
+			return true;
+		} 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 -= 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 {
+			$this->write('Config.userAgent', $this->_userAgent);
+			$this->write('Config.time', $this->sessionTime);
+			$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) {
+			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) {
+				$sessionPath = session_save_path();
+				if (empty($sessionPath)) {
+					$sessionPath = '/tmp';
+				}
+				$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();
+	}
+
+/**
+ * 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 $id 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($id) {
+		$model =& ClassRegistry::getObject('Session');
+
+		$row = $model->find('first', array(
+			'conditions' => array($model->primaryKey => $id)
+		));
+
+		if (empty($row[$model->alias]['data'])) {
+			return false;
+		}
+
+		return $row[$model->alias]['data'];
+	}
+
+/**
+ * Helper function called on write for database sessions.
+ *
+ * @param integer $id ID that uniquely identifies session in database
+ * @param mixed $data The value of the data to be saved.
+ * @return boolean True for successful write, false otherwise.
+ * @access private
+ */
+	function __write($id, $data) {
+		if (!$id) {
+			return false;
+		}
+		$expires = time() + Configure::read('Session.timeout') * Security::inactiveMins();
+		$model =& ClassRegistry::getObject('Session');
+		$return = $model->save(array($model->primaryKey => $id) + compact('data', 'expires'));
+		return $return;
+	}
+
+/**
+ * Method called on the destruction of a database session.
+ *
+ * @param integer $id ID that uniquely identifies session in database
+ * @return boolean True for successful delete, false otherwise.
+ * @access private
+ */
+	function __destroy($id) {
+		$model =& ClassRegistry::getObject('Session');
+		$return = $model->delete($id);
+
+		return $return;
+	}
+
+/**
+ * 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) {
+		$model =& ClassRegistry::getObject('Session');
+
+		if (!$expires) {
+			$expires = time();
+		}
+
+		$return = $model->deleteAll(array($model->alias . ".expires <" => $expires), false, false);
+		return $return;
+	}
+}

Added: trunk/src/Web/cake/libs/cake_socket.php
===================================================================
--- trunk/src/Web/cake/libs/cake_socket.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/cake_socket.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,310 @@
+<?php
+/**
+ * Cake Socket connection class.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+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
+ * @see CakeSocket::$_baseConfig
+ */
+	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($errNum, $errStr);
+		}
+
+		$this->connected = is_resource($this->connection);
+		if ($this->connected) {
+			stream_set_timeout($this->connection, $this->config['timeout']);
+		}
+		return $this->connected;
+	}
+
+/**
+ * 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;
+			}
+		}
+		$totalBytes = strlen($data);
+		for ($written = 0, $rv = 0; $written < $totalBytes; $written += $rv) {
+			$rv = fwrite($this->connection, substr($data, $written));
+			if ($rv === false || $rv === 0) {
+				return $written;
+			}
+		}
+		return $written;
+	}
+
+/**
+ * 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)) {
+			$buffer = fread($this->connection, $length);
+			$info = stream_get_meta_data($this->connection);
+			if ($info['timed_out']) {
+				$this->setLastError(E_WARNING, __('Connection timed out', true));
+				return false;
+			}
+			return $buffer;
+		} 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;
+	}
+}

Added: trunk/src/Web/cake/libs/class_registry.php
===================================================================
--- trunk/src/Web/cake/libs/class_registry.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/class_registry.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,365 @@
+<?php
+/**
+ * Class collections.
+ *
+ * A repository for class objects, each registered with a key.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 0.9.2
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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. ClassRegistry::init()
+ * is used as a factory for models, and handle correct injecting of settings, that assist in testing.
+ *
+ * Examples
+ * Simple Use: Get a Post model instance ```ClassRegistry::init('Post');```
+ *
+ * Exapanded: ```array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry', 'type' => 'Model');```
+ *
+ * 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' => 'Model'),
+ *		array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry', 'type' => 'Model'),
+ *		array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry', 'type' => 'Model')
+ * );
+ * }}}
+ * @param mixed $class as a string or a single key => value array instance will be created,
+ *  stored in the registry and returned.
+ * @param string $type Only model is accepted as a valid value for $type.
+ * @return object instance 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)) {
+				$pluginPath = null;
+				$settings = array_merge($defaults, $settings);
+				$class = $settings['class'];
+				
+				list($plugin, $class) = pluginSplit($class);
+				if ($plugin) {
+					$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 or boolean false if the object does not exist.
+ * @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();
+	}
+}

Added: trunk/src/Web/cake/libs/configure.php
===================================================================
--- trunk/src/Web/cake/libs/configure.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/configure.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,1328 @@
+<?php
+/**
+ * App and Configure classes
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.0.0.2363
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Configuration class (singleton). Used for managing runtime configuration information.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @link          http://book.cakephp.org/view/924/The-Configuration-Class
+ */
+class Configure extends Object {
+
+/**
+ * Current debug level.
+ *
+ * @link          http://book.cakephp.org/view/931/CakePHP-Core-Configuration-Variables
+ * @var integer
+ * @access public
+ */
+	var $debug = 0;
+
+/**
+ * Returns a singleton instance of the Configure class.
+ *
+ * @return Configure instance
+ * @access public
+ */
+	function &getInstance($boot = true) {
+		static $instance = array();
+		if (!$instance) {
+			if (!class_exists('Set')) {
+				require LIBS . 'set.php';
+			}
+			$instance[0] =& new Configure();
+			$instance[0]->__loadBootstrap($boot);
+		}
+		return $instance[0];
+	}
+
+/**
+ * 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/926/write
+ * @param array $config Name of var to write
+ * @param mixed $value Value to set for var
+ * @return boolean True if write was successful
+ * @access public
+ */
+	function write($config, $value = null) {
+		$_this =& Configure::getInstance();
+
+		if (!is_array($config)) {
+			$config = array($config => $value);
+		}
+
+		foreach ($config as $name => $value) {
+			if (strpos($name, '.') === false) {
+				$_this->{$name} = $value;
+			} else {
+				$names = explode('.', $name, 4);
+				switch (count($names)) {
+					case 2:
+						$_this->{$names[0]}[$names[1]] = $value;
+					break;
+					case 3:
+						$_this->{$names[0]}[$names[1]][$names[2]] = $value;
+						break;
+					case 4:
+						$names = explode('.', $name, 2);
+						if (!isset($_this->{$names[0]})) {
+							$_this->{$names[0]} = array();
+						}
+						$_this->{$names[0]} = Set::insert($_this->{$names[0]}, $names[1], $value);
+					break;
+				}
+			}
+		}
+
+		if (isset($config['debug']) || isset($config['log'])) {
+			$reporting = 0;
+			if ($_this->debug) {
+				if (!class_exists('Debugger')) {
+					require LIBS . 'debugger.php';
+				}
+				$reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT;
+				if (function_exists('ini_set')) {
+					ini_set('display_errors', 1);
+				}
+				$callback = array('Debugger', 'getInstance');
+			} elseif (function_exists('ini_set')) {
+				ini_set('display_errors', 0);
+			}
+
+			if (isset($_this->log) && $_this->log) {
+				if (is_integer($_this->log) && !$_this->debug) {
+					$reporting = $_this->log;
+				} else {
+					$reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT;
+				}
+				error_reporting($reporting);
+				if (!class_exists('CakeLog')) {
+					require LIBS . 'cake_log.php';
+				}
+				if (empty($callback)) {
+					$callback = array('CakeLog', 'getInstance');
+				}
+			}
+			if (!empty($callback) && !defined('DISABLE_DEFAULT_ERROR_HANDLING') && class_exists('Debugger')) {
+				Debugger::invoke(call_user_func($callback));
+			}
+			error_reporting($reporting);
+		}
+		return true;
+	}
+
+/**
+ * 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/927/read
+ * @param string $var Variable to obtain.  Use '.' to access array elements.
+ * @return string value of Configure::$var
+ * @access public
+ */
+	function read($var = 'debug') {
+		$_this =& Configure::getInstance();
+
+		if ($var === 'debug') {
+			return $_this->debug;
+		}
+
+		if (strpos($var, '.') !== false) {
+			$names = explode('.', $var, 3);
+			$var = $names[0];
+		}
+		if (!isset($_this->{$var})) {
+			return null;
+		}
+		if (!isset($names[1])) {
+			return $_this->{$var};
+		}
+		switch (count($names)) {
+			case 2:
+				if (isset($_this->{$var}[$names[1]])) {
+					return $_this->{$var}[$names[1]];
+				}
+			break;
+			case 3:
+				if (isset($_this->{$var}[$names[1]][$names[2]])) {
+					return $_this->{$var}[$names[1]][$names[2]];
+				}
+				if (!isset($_this->{$var}[$names[1]])) {
+					return null;
+				}
+				return Set::classicExtract($_this->{$var}[$names[1]], $names[2]);
+			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/928/delete
+ * @param string $var the var to be deleted
+ * @return void
+ * @access public
+ */
+	function delete($var = null) {
+		$_this =& Configure::getInstance();
+
+		if (strpos($var, '.') === false) {
+			unset($_this->{$var});
+			return;
+		}
+
+		$names = explode('.', $var, 2);
+		$_this->{$names[0]} = Set::remove($_this->{$names[0]}, $names[1]);
+	}
+
+/**
+ * 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. load() is also used to
+ * load stored config files created with Configure::store()
+ *
+ * - To load config files from app/config use `Configure::load('configure_file');`.
+ * - To load config files from a plugin `Configure::load('plugin.configure_file');`.
+ *
+ * @link http://book.cakephp.org/view/929/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 = $plugin = $pluginPath = false;
+		list($plugin, $fileName) = pluginSplit($fileName);
+		if ($plugin) {
+			$pluginPath = App::pluginPath($plugin);
+		}
+		$pos = strpos($fileName, '..');
+
+		if ($pos === false) {
+			if ($pluginPath && file_exists($pluginPath . 'config' . DS . $fileName . '.php')) {
+				include($pluginPath . 'config' . DS . $fileName . '.php');
+				$found = true;
+			} elseif (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 (App::core('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)) {
+			trigger_error(sprintf(__('Configure::load() - no variable $config found in %s.php', true), $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/930/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'] = " . var_export($value, true) . ";\n";
+		}
+		if (is_null($type)) {
+			$write = false;
+		}
+		Configure::__writeConfig($content, $name, $write);
+	}
+
+/**
+ * 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);
+			}
+		}
+	}
+
+/**
+ * @deprecated
+ * @see App::objects()
+ */
+	function listObjects($type, $path = null, $cache = true) {
+		return App::objects($type, $path, $cache);
+	}
+
+/**
+ * @deprecated
+ * @see App::core()
+ */
+	function corePaths($type = null) {
+		return App::core($type);
+	}
+
+/**
+ * @deprecated
+ * @see App::build()
+ */
+	function buildPaths($paths) {
+		return App::build($paths);
+	}
+
+/**
+ * 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) {
+		if ($boot) {
+			Configure::write('App', array('base' => false, 'baseUrl' => false, 'dir' => APP_DIR, 'webroot' => WEBROOT_DIR, 'www_root' => WWW_ROOT));
+
+			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 (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', true), 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((array)$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((array)$cache['settings'], array(
+						'prefix' => $prefix . 'cake_model_', 'path' => $path . DS . 'models' . DS,
+						'serialize' => true, 'duration' => $duration
+					)));
+				}
+				Cache::config('default');
+			}
+			App::build();
+			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);
+			}
+		}
+	}
+}
+
+/**
+ * Class/file loader and path management.
+ *
+ * @link          http://book.cakephp.org/view/933/The-App-Class
+ * @since         CakePHP(tm) v 1.2.0.6001
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class App extends Object {
+
+/**
+ * List of object types and their properties
+ *
+ * @var array
+ * @access public
+ */
+	var $types = array(
+		'class' => array('suffix' => '.php', 'extends' => null, 'core' => true),
+		'file' => array('suffix' => '.php', 'extends' => null, 'core' => true),
+		'model' => array('suffix' => '.php', 'extends' => 'AppModel', 'core' => false),
+		'behavior' => array('suffix' => '.php', 'extends' => 'ModelBehavior', 'core' => true),
+		'controller' => array('suffix' => '_controller.php', 'extends' => 'AppController', 'core' => true),
+		'component' => array('suffix' => '.php', 'extends' => null, 'core' => true),
+		'lib' => array('suffix' => '.php', 'extends' => null, 'core' => true),
+		'view' => array('suffix' => '.php', 'extends' => null, 'core' => true),
+		'helper' => array('suffix' => '.php', 'extends' => 'AppHelper', 'core' => true),
+		'vendor' => array('suffix' => '', 'extends' => null, 'core' => true),
+		'shell' => array('suffix' => '.php', 'extends' => 'Shell', 'core' => true),
+		'plugin' => array('suffix' => '', 'extends' => null, 'core' => true)
+	);
+
+/**
+ * List of additional path(s) where model files reside.
+ *
+ * @var array
+ * @access public
+ */
+	var $models = array();
+
+/**
+ * List of additional path(s) where behavior files reside.
+ *
+ * @var array
+ * @access public
+ */
+	var $behaviors = array();
+
+/**
+ * List of additional path(s) where controller files reside.
+ *
+ * @var array
+ * @access public
+ */
+	var $controllers = array();
+
+/**
+ * List of additional path(s) where component files reside.
+ *
+ * @var array
+ * @access public
+ */
+	var $components = array();
+
+/**
+ * List of additional path(s) where datasource files reside.
+ *
+ * @var array
+ * @access public
+ */
+	var $datasources = array();
+
+/**
+ * List of additional path(s) where libs files reside.
+ *
+ * @var array
+ * @access public
+ */
+	var $libs = array();
+/**
+ * List of additional path(s) where view files reside.
+ *
+ * @var array
+ * @access public
+ */
+	var $views = array();
+
+/**
+ * List of additional path(s) where helper files reside.
+ *
+ * @var array
+ * @access public
+ */
+	var $helpers = array();
+
+/**
+ * List of additional path(s) where plugins reside.
+ *
+ * @var array
+ * @access public
+ */
+	var $plugins = array();
+
+/**
+ * List of additional path(s) where vendor packages reside.
+ *
+ * @var array
+ * @access public
+ */
+	var $vendors = array();
+
+/**
+ * List of additional path(s) where locale files reside.
+ *
+ * @var array
+ * @access public
+ */
+	var $locales = array();
+
+/**
+ * List of additional path(s) where console shell files reside.
+ *
+ * @var array
+ * @access public
+ */
+	var $shells = array();
+
+/**
+ * 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;
+
+/**
+ * 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();
+
+/**
+ * Holds and key => value array of object types.
+ *
+ * @var array
+ * @access private
+ */
+	var $__objects = array();
+
+/**
+ * Used to read information stored path
+ *
+ * Usage:
+ *
+ * `App::path('models'); will return all paths for models`
+ *
+ * @param string $type type of path
+ * @return string array
+ * @access public
+ */
+	function path($type) {
+		$_this =& App::getInstance();
+		if (!isset($_this->{$type})) {
+			return array();
+		}
+		return $_this->{$type};
+	}
+
+/**
+ * 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
+ * @param boolean $reset true will set paths, false merges paths [default] false
+ * @return void
+ * @access public
+ */
+	function build($paths = array(), $reset = false) {
+		$_this =& App::getInstance();
+		$defaults = array(
+			'models' => array(MODELS),
+			'behaviors' => array(BEHAVIORS),
+			'datasources' => array(MODELS . 'datasources'),
+			'controllers' => array(CONTROLLERS),
+			'components' => array(COMPONENTS),
+			'libs' => array(APPLIBS),
+			'views' => array(VIEWS),
+			'helpers' => array(HELPERS),
+			'locales' => array(APP . 'locale' . DS),
+			'shells' => array(APP . 'vendors' . DS . 'shells' . DS, VENDORS . 'shells' . DS),
+			'vendors' => array(APP . 'vendors' . DS, VENDORS),
+			'plugins' => array(APP . 'plugins' . DS)
+		);
+
+		if ($reset == true) {
+			foreach ($paths as $type => $new) {
+				$_this->{$type} = (array)$new;
+			}
+			return $paths;
+		}
+
+		$core = $_this->core();
+		$app = array('models' => true, 'controllers' => true, 'helpers' => true);
+
+		foreach ($defaults as $type => $default) {
+			$merge = array();
+
+			if (isset($app[$type])) {
+				$merge = array(APP);
+			}
+			if (isset($core[$type])) {
+				$merge = array_merge($merge, (array)$core[$type]);
+			}
+
+			if (empty($_this->{$type}) || empty($paths)) {
+				$_this->{$type} = $default;
+			}
+
+			if (!empty($paths[$type])) {
+				$path = array_flip(array_flip(array_merge(
+					(array)$paths[$type], $_this->{$type}, $merge
+				)));
+				$_this->{$type} = array_values($path);
+			} else {
+				$path = array_flip(array_flip(array_merge($_this->{$type}, $merge)));
+				$_this->{$type} = array_values($path);
+			}
+		}
+	}
+
+/**
+ * Get the path that a plugin is on.  Searches through the defined plugin paths.
+ *
+ * @param string $plugin CamelCased/lower_cased plugin name to find the path of.
+ * @return string full path to the plugin.
+ */
+	function pluginPath($plugin) {
+		$_this =& App::getInstance();
+		$pluginDir = Inflector::underscore($plugin);
+		for ($i = 0, $length = count($_this->plugins); $i < $length; $i++) {
+			if (is_dir($_this->plugins[$i] . $pluginDir)) {
+				return $_this->plugins[$i] . $pluginDir . DS ;
+			}
+		}
+		return $_this->plugins[0] . $pluginDir . DS;
+	}
+
+/**
+ * Find the path that a theme is on.  Search through the defined theme paths.
+ *
+ * @param string $theme lower_cased theme name to find the path of.
+ * @return string full path to the theme.
+ */
+	function themePath($theme) {
+		$_this =& App::getInstance();
+		$themeDir = 'themed' . DS . Inflector::underscore($theme);
+		for ($i = 0, $length = count($_this->views); $i < $length; $i++) {
+			if (is_dir($_this->views[$i] . $themeDir)) {
+				return $_this->views[$i] . $themeDir . DS ;
+			}
+		}
+		return $_this->views[0] . $themeDir . DS;
+	}
+
+/**
+ * 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 core($type = null) {
+		static $paths = false;
+		if ($paths === false) {
+			$paths = Cache::read('core_paths', '_cake_core_');
+		}
+		if (!$paths) {
+			$paths = array();
+			$libs = dirname(__FILE__) . DS;
+			$cake = dirname($libs) . DS;
+			$path = dirname($cake) . DS;
+
+			$paths['cake'][] = $cake;
+			$paths['libs'][] = $libs;
+			$paths['models'][] = $libs . 'model' . DS;
+			$paths['datasources'][] = $libs . 'model' . DS . 'datasources' . DS;
+			$paths['behaviors'][] = $libs . 'model' . DS . 'behaviors' . DS;
+			$paths['controllers'][] = $libs . 'controller' . DS;
+			$paths['components'][] = $libs . 'controller' . DS . 'components' . DS;
+			$paths['views'][] = $libs . 'view' . DS;
+			$paths['helpers'][] = $libs . 'view' . DS . 'helpers' . DS;
+			$paths['plugins'][] = $path . 'plugins' . DS;
+			$paths['vendors'][] = $path . 'vendors' . DS;
+			$paths['shells'][] = $cake . 'console' . DS . 'libs' . DS;
+
+			Cache::write('core_paths', array_filter($paths), '_cake_core_');
+		}
+		if ($type && isset($paths[$type])) {
+			return $paths[$type];
+		}
+		return $paths;
+	}
+
+/**
+ * Returns an array of objects of the given type.
+ *
+ * Example usage:
+ *
+ * `App::objects('plugin');` returns `array('DebugKit', 'Blog', 'User');`
+ *
+ * @param string $type Type of object, i.e. 'model', 'controller', 'helper', or 'plugin'
+ * @param mixed $path Optional Scan only the path given. If null, paths for the chosen
+ *   type will be used.
+ * @param boolean $cache Set to false to rescan objects of the chosen type. Defaults to true.
+ * @return mixed Either false on incorrect / miss.  Or an array of found objects.
+ * @access public
+ */
+	function objects($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 =& App::getInstance();
+
+		if (empty($_this->__objects) && $cache === true) {
+			$_this->__objects = Cache::read('object_map', '_cake_core_');
+		}
+
+		if (!isset($_this->__objects[$name]) || $cache !== true) {
+			$types = $_this->types;
+
+			if (!isset($types[$type])) {
+				return false;
+			}
+			$objects = array();
+
+			if (empty($path)) {
+				$path = $_this->{"{$type}s"};
+				if (isset($types[$type]['core']) && $types[$type]['core'] === false) {
+					array_pop($path);
+				}
+			}
+			$items = array();
+
+			foreach ((array)$path as $dir) {
+				if ($dir != APP) {
+					$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) {
+				$_this->__resetCache(true);
+			}
+			$_this->__objects[$name] = $objects;
+		}
+
+		return $_this->__objects[$name];
+	}
+
+/**
+ * Finds classes based on $name or specific file(s) to search.  Calling App::import() will
+ * not construct any classes contained in the files. It will only find and require() the file.
+ *
+ * @link          http://book.cakephp.org/view/934/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, $parent)) {
+					return false;
+				}
+			}
+			return true;
+		}
+
+		if ($name != null && strpos($name, '.') !== false) {
+			list($plugin, $name) = explode('.', $name);
+			$plugin = Inflector::camelize($plugin);
+		}
+		$_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'], $parent);
+
+					if ($_this->return) {
+						return include($load);
+					}
+					return true;
+				} else {
+					$_this->__remove($name . $ext['class'], $type, $plugin);
+					$_this->__resetCache(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'];
+					}
+				}
+			}
+
+			if (strtolower($type) !== 'vendor' && empty($search) && $_this->__load($file)) {
+				$directory = false;
+			} else {
+				$file = $find;
+				$directory = $_this->__find($find, true);
+			}
+
+			if ($directory !== null) {
+				$_this->__resetCache(true);
+				$_this->__map($directory . $file, $name . $ext['class'], $type, $plugin);
+				$_this->__overload($type, $name . $ext['class'], $parent);
+
+				if ($_this->return) {
+					return include($directory . $file);
+				}
+				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 = (array)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) {
+		static $appPath = false;
+
+		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) {
+			if ($appPath === false) {
+				$appPath = rtrim(APP, DS);
+			}
+			$path = rtrim($path, DS);
+
+			if ($path === $appPath) {
+				$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, array('.svn', '.git', 'CVS', 'tests', 'templates'), 'dir');
+				sort($directories);
+				$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 camelized if object is from a plugin, the name of the plugin
+ * @return void
+ * @access private
+ */
+	function __map($file, $name, $type, $plugin) {
+		if ($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 camelized 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) {
+			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, $parent) {
+		if (($type === 'Model' || $type === 'Helper') && $parent !== false) {
+			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 camelized 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 array('class' => null, 'suffix' => null, 'path' => null);
+		}
+
+		if ($plugin) {
+			$pluginPath = Inflector::underscore($plugin);
+		}
+		$path = null;
+		$load = strtolower($type);
+
+		switch ($load) {
+			case 'model':
+				if (!class_exists('Model')) {
+					require LIBS . 'model' . DS . 'model.php';
+				}
+				if (!class_exists('AppModel')) {
+					App::import($type, 'AppModel', false);
+				}
+				if ($plugin) {
+					if (!class_exists($plugin . 'AppModel')) {
+						App::import($type, $plugin . '.' . $plugin . 'AppModel', false, array(), $pluginPath . DS . $pluginPath . '_app_model.php');
+					}
+					$path = $pluginPath . DS . 'models' . DS;
+				}
+				return array('class' => null, 'suffix' => null, 'path' => $path);
+			break;
+			case 'behavior':
+				if ($plugin) {
+					$path = $pluginPath . DS . 'models' . DS . 'behaviors' . DS;
+				}
+				return array('class' => $type, 'suffix' => null, 'path' => $path);
+			break;
+			case 'datasource':
+				if ($plugin) {
+					$path = $pluginPath . DS . 'models' . DS . 'datasources' . DS;
+				}
+				return array('class' => $type, 'suffix' => null, 'path' => $path);
+			case 'controller':
+				App::import($type, 'AppController', false);
+				if ($plugin) {
+					App::import($type, $plugin . '.' . $plugin . 'AppController', false, array(), $pluginPath . DS . $pluginPath . '_app_controller.php');
+					$path = $pluginPath . DS . 'controllers' . DS;
+				}
+				return array('class' => $type, 'suffix' => $type, 'path' => $path);
+			break;
+			case 'component':
+				if ($plugin) {
+					$path = $pluginPath . DS . 'controllers' . DS . 'components' . DS;
+				}
+				return array('class' => $type, 'suffix' => null, 'path' => $path);
+			break;
+			case 'lib':
+				if ($plugin) {
+					$path = $pluginPath . DS . 'libs' . DS;
+				}
+				return array('class' => null, 'suffix' => null, 'path' => $path);
+			break;
+			case 'view':
+				if ($plugin) {
+					$path = $pluginPath . 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 = $pluginPath . DS . 'views' . DS . 'helpers' . DS;
+				}
+				return array('class' => $type, 'suffix' => null, 'path' => $path);
+			break;
+			case 'vendor':
+				if ($plugin) {
+					$path = $pluginPath . 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);
+		$paths = array();
+
+		if ($type === 'core') {
+			return App::core('libs');
+		}
+		if (isset($this->{$type . 's'})) {
+			return $this->{$type . 's'};
+		}
+		return $paths;
+	}
+
+/**
+ * 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 camelized name of plugin
+ * @return void
+ * @access private
+ */
+	function __remove($name, $type, $plugin) {
+		if ($plugin) {
+			unset($this->__map['Plugin'][$plugin][$type][$name]);
+		} else {
+			unset($this->__map[$type][$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
+ * @access private
+ */
+	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;
+	}
+	
+/**
+ * Determines if $__maps, $__objects and $__paths cache should be reset.
+ *
+ * @param boolean $reset 
+ * @return boolean
+ * @access private
+ */	
+	function __resetCache($reset = null) {
+		static $cache = array();
+		if (!$cache && $reset === true) {
+			$cache = true;	
+		}
+		return $cache;
+	}
+
+/**
+ * Object destructor.
+ *
+ * Writes cache file if changes have been made to the $__map or $__paths
+ *
+ * @return void
+ * @access private
+ */
+	function __destruct() {
+		if ($this->__resetCache() === true) {
+			$core = App::core('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_');
+			Cache::write('object_map', $this->__objects, '_cake_core_');
+		}
+	}
+}

Added: trunk/src/Web/cake/libs/controller/app_controller.php
===================================================================
--- trunk/src/Web/cake/libs/controller/app_controller.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/controller/app_controller.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Application level Controller
+ *
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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
+ * @link http://book.cakephp.org/view/957/The-App-Controller
+ */
+class AppController extends Controller {
+}

Added: trunk/src/Web/cake/libs/controller/component.php
===================================================================
--- trunk/src/Web/cake/libs/controller/component.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/controller/component.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,267 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller
+ * @since         CakePHP(tm) v TBD
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Handler for Controller::$components
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller
+ * @link          http://book.cakephp.org/view/993/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
+		);
+
+		$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/998/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/998/MVC-Class-Access-Within-Components
+ * @deprecated See Component::triggerCallback()
+ */
+	function startup(&$controller) {
+		$this->triggerCallback('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
+ * @deprecated See Component::triggerCallback()
+ */
+	function beforeRender(&$controller) {
+		$this->triggerCallback('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
+ * @deprecated See Component::triggerCallback()
+ */
+	function shutdown(&$controller) {
+		$this->triggerCallback('shutdown', $controller);
+	}
+
+/**
+ * Trigger a callback on all primary components.  Will fire $callback on all components
+ * that have such a method.  You can implement and fire custom callbacks in addition to the
+ * standard ones.
+ *
+ * example use, from inside a controller:
+ *
+ * `$this->Component->triggerCallback('beforeFilter', $this);`
+ *
+ * will trigger the beforeFilter callback on all components that have implemented one. You
+ * can trigger any method in this fashion.
+ *
+ * @param Controller $controller Controller instance
+ * @param string $callback Callback to trigger.
+ * @return void
+ * @access public
+ */
+	function triggerCallback($callback, &$controller) {
+		foreach ($this->_primary as $name) {
+			$component =& $this->_loaded[$name];
+			if (method_exists($component, $callback) && $component->enabled === true) {
+				$component->{$callback}($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) {
+		$base = $this->__controllerVars['base'];
+		$normal = Set::normalize($object->components);
+		foreach ((array)$normal as $component => $config) {
+			$plugin = isset($this->__controllerVars['plugin']) ? $this->__controllerVars['plugin'] . '.' : null;
+			list($plugin, $component) = pluginSplit($component, true, $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 {
+					if (PHP5) {
+						$object->{$component} = new $componentCn();
+					} 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);
+				}
+			}
+		}
+	}
+}

Added: trunk/src/Web/cake/libs/controller/components/acl.php
===================================================================
--- trunk/src/Web/cake/libs/controller/components/acl.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/controller/components/acl.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,638 @@
+<?php
+/**
+ * Access Control List factory class.
+ *
+ * Permissions system.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller.components
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Access Control List factory class.
+ *
+ * Uses a strategy pattern to allow custom ACL implementations to be used with the same component interface.
+ * You can define by changing `Configure::write('Acl.classname', 'DbAcl');` in your core.php. Concrete ACL
+ * implementations should extend `AclBase` and implement the methods it defines.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller.components
+ * @link http://book.cakephp.org/view/1242/Access-Control-Lists
+ */
+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 as defined in `Configure::read('Acl.classname')`
+ *
+ */
+	function __construct() {
+		$name = Inflector::camelize(strtolower(Configure::read('Acl.classname')));
+		if (!class_exists($name)) {
+			if (App::import('Component', $name)) {
+				list($plugin, $name) = pluginSplit($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.  Check methods
+ * are used to check whether or not an ARO can access an ACO
+ *
+ * @param string $aro ARO The requesting object identifier.
+ * @param string $aco ACO The controlled object identifier.
+ * @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. Allow methods
+ * are used to grant an ARO access to an ACO.
+ *
+ * @param string $aro ARO The requesting object identifier.
+ * @param string $aco ACO The controlled object identifier.
+ * @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. Deny methods
+ * are used to remove permission from an ARO to access an ACO.
+ *
+ * @param string $aro ARO The requesting object identifier.
+ * @param string $aco ACO The controlled object identifier.
+ * @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. Inherit methods
+ * modify the permission for an ARO to be that of its parent object.
+ *
+ * @param string $aro ARO The requesting object identifier.
+ * @param string $aco ACO The controlled object identifier.
+ * @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. An alias for AclComponent::allow()
+ *
+ * @param string $aro ARO The requesting object identifier.
+ * @param string $aco ACO The controlled object identifier.
+ * @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. An alias for AclComponent::deny()
+ *
+ * @param string $aro ARO The requesting object identifier.
+ * @param string $aco ACO The controlled object identifier.
+ * @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 The requesting object identifier.
+ * @param string $aco ACO The controlled object identifier.
+ * @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) {
+	}
+}
+
+/**
+ * DbAcl implements an ACL control system in the database.  ARO's and ACO's are 
+ * structured into trees and a linking table is used to define permissions.  You 
+ * can install the schema for DbAcl with the Schema Shell.
+ *
+ * `$aco` and `$aro` parameters can be slash delimited paths to tree nodes.
+ *
+ * eg. `controllers/Users/edit`
+ *
+ * Would point to a tree structure like
+ *
+ * {{{
+ *	controllers
+ *		Users
+ *			edit
+ * }}}
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model
+ */
+class DbAcl extends AclBase {
+
+/**
+ * Constructor
+ *
+ */
+	function __construct() {
+		parent::__construct();
+		if (!class_exists('AclNode')) {
+			require LIBS . 'model' . DS . 'db_acl.php';
+		}
+		$this->Aro =& ClassRegistry::init(array('class' => 'Aro', 'alias' => 'Aro'));
+		$this->Aco =& ClassRegistry::init(array('class' => 'Aco', 'alias' => 'Aco'));
+	}
+
+/**
+ * Initializes the containing component and sets the Aro/Aco objects to it.
+ *
+ * @param AclComponent $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 The requesting object identifier.
+ * @param string $aco ACO The controlled object identifier.
+ * @param string $action Action (defaults to *)
+ * @return boolean Success (true if ARO has access to action in ACO, false otherwise)
+ * @access public
+ * @link http://book.cakephp.org/view/1249/Checking-Permissions-The-ACL-Component
+ */
+	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: ", true) . 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: ", true) . 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 The requesting object identifier.
+ * @param string $aco ACO The controlled object identifier.
+ * @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
+ * @link http://book.cakephp.org/view/1248/Assigning-Permissions
+ */
+	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 && !empty($perms['link'])) {
+			$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 The requesting object identifier.
+ * @param string $aco ACO The controlled object identifier.
+ * @param string $actions Action (defaults to *)
+ * @return boolean Success
+ * @access public
+ * @link http://book.cakephp.org/view/1248/Assigning-Permissions
+ */
+	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 The requesting object identifier.
+ * @param string $aco ACO The controlled object identifier.
+ * @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 The requesting object identifier.
+ * @param string $aco ACO The controlled object identifier.
+ * @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 The requesting object identifier.
+ * @param string $aco ACO The controlled object identifier.
+ * @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 The requesting object identifier.
+ * @param string $aco ACO The controlled object identifier.
+ * @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;
+	}
+}
+
+/**
+ * IniAcl implements an access control system using an INI file.  An example 
+ * of the ini file used can be found in /config/acl.ini.php.
+ *
+ * @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;
+	}
+}

Added: trunk/src/Web/cake/libs/controller/components/auth.php
===================================================================
--- trunk/src/Web/cake/libs/controller/components/auth.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/controller/components/auth.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,959 @@
+<?php
+/**
+ * Authentication component
+ *
+ * Manages user logins and permissions.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller.components
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+App::import('Core', array('Router', 'Security'), false);
+
+/**
+ * Authentication control component class
+ *
+ * Binds access control with user authentication and session management.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller.components
+ * @link http://book.cakephp.org/view/1250/Authentication
+ */
+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
+ * @link http://book.cakephp.org/view/1278/authenticate
+ */
+	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
+ * @link http://book.cakephp.org/view/1275/authorize
+ */
+	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
+ * @link http://book.cakephp.org/view/1277/ajaxLogin
+ */
+	var $ajaxLogin = null;
+
+/**
+ * The name of the element used for SessionComponent::setFlash
+ *
+ * @var string
+ * @access public
+ */
+	var $flashElement = 'default';
+
+/**
+ * The name of the model that represents users which will be authenticated.  Defaults to 'User'.
+ *
+ * @var string
+ * @access public
+ * @link http://book.cakephp.org/view/1266/userModel
+ */
+	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
+ * @link http://book.cakephp.org/view/1268/userScope
+ */
+	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
+ * @link http://book.cakephp.org/view/1267/fields
+ */
+	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
+ * @link http://book.cakephp.org/view/1276/sessionKey
+ */
+	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
+ * @link http://book.cakephp.org/view/1279/actionPath
+ */
+	var $actionPath = null;
+
+/**
+ * A URL (defined as a string or array) to the controller action that handles
+ * logins.
+ *
+ * @var mixed
+ * @access public
+ * @link http://book.cakephp.org/view/1269/loginAction
+ */
+	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
+ * @link http://book.cakephp.org/view/1270/loginRedirect
+ */
+	var $loginRedirect = null;
+
+/**
+ * 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()
+ * @link http://book.cakephp.org/view/1271/logoutRedirect
+ */
+	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
+ * @link http://book.cakephp.org/view/1272/loginError
+ */
+	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
+ * @link http://book.cakephp.org/view/1273/authError
+ */
+	var $authError = null;
+
+/**
+ * Determines whether AuthComponent will automatically redirect and exit if login is successful.
+ *
+ * @var boolean
+ * @access public
+ * @link http://book.cakephp.org/view/1274/autoRedirect
+ */
+	var $autoRedirect = true;
+
+/**
+ * Controller actions for which user validation is not required.
+ *
+ * @var array
+ * @access public
+ * @see AuthComponent::allow()
+ * @link http://book.cakephp.org/view/1251/Setting-Auth-Component-Variables
+ */
+	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, $settings = array()) {
+		$this->params = $controller->params;
+		$crud = array('create', 'read', 'update', 'delete');
+		$this->actionMap = array_merge($this->actionMap, array_combine($crud, $crud));
+		$this->_methods = $controller->methods;
+
+		$prefixes = Router::prefixes();
+		if (!empty($prefixes)) {
+			foreach ($prefixes as $prefix) {
+				$this->actionMap = array_merge($this->actionMap, array(
+					$prefix . '_index' => 'read',
+					$prefix . '_add' => 'create',
+					$prefix . '_edit' => 'update',
+					$prefix . '_view' => 'read',
+					$prefix . '_remove' => 'delete',
+					$prefix . '_create' => 'create',
+					$prefix . '_read' => 'read',
+					$prefix . '_update' => 'update',
+					$prefix . '_delete' => 'delete'
+				));
+			}
+		}
+		$this->_set($settings);
+		if (Configure::read() > 0) {
+			App::import('Debugger');
+			Debugger::checkSecurityKeys();
+		}
+	}
+
+/**
+ * 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)
+		);
+		if ($isErrorOrTests) {
+			return true;
+		}
+
+		$methods = array_flip($controller->methods);
+		$action = strtolower($controller->params['action']);
+		$isMissingAction = (
+			$controller->scaffold === false &&
+			!isset($methods[$action])
+		);
+
+		if ($isMissingAction) {
+			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);
+
+		$allowedActions = array_map('strtolower', $this->allowedActions);
+		$isAllowed = (
+			$this->allowedActions == array('*') ||
+			in_array($action, $allowedActions)
+		);
+
+		if ($loginAction != $url && $isAllowed) {
+			return true;
+		}
+
+		if ($loginAction == $url) {
+			$model =& $this->getModel();
+			if (empty($controller->data) || !isset($controller->data[$model->alias])) {
+				if (!$this->Session->check('Auth.redirect') && !$this->loginRedirect && env('HTTP_REFERER')) {
+					$this->Session->write('Auth.redirect', $controller->referer(null, true));
+				}
+				return false;
+			}
+
+			$isValid = !empty($controller->data[$model->alias][$this->fields['username']]) &&
+				!empty($controller->data[$model->alias][$this->fields['password']]);
+
+			if ($isValid) {
+				$username = $controller->data[$model->alias][$this->fields['username']];
+				$password = $controller->data[$model->alias][$this->fields['password']];
+
+				$data = array(
+					$model->alias . '.' . $this->fields['username'] => $username,
+					$model->alias . '.' . $this->fields['password'] => $password
+				);
+
+				if ($this->login($data)) {
+					if ($this->autoRedirect) {
+						$controller->redirect($this->redirect(), null, true);
+					}
+					return true;
+				}
+			}
+
+			$this->Session->setFlash($this->loginError, $this->flashElement, array(), 'auth');
+			$controller->data[$model->alias][$this->fields['password']] = null;
+			return false;
+		} else {
+			$user = $this->user();
+			if (!$user) {
+				if (!$this->RequestHandler->isAjax()) {
+					$this->Session->setFlash($this->authError, $this->flashElement, array(), 'auth');
+					if (!empty($controller->params['url']) && count($controller->params['url']) >= 2) {
+						$query = $controller->params['url'];
+						unset($query['url'], $query['ext']);
+						$url .= Router::queryString($query, array());
+					}
+					$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 {
+					trigger_error(__('Could not find AclComponent. Please include Acl in Controller::$components.', 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, null, $user)) {
+			return true;
+		}
+
+		$this->Session->setFlash($this->authError, $this->flashElement, 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;
+		}
+		list($plugin, $model) = pluginSplit($this->userModel);
+		$defaults = array(
+			'loginAction' => array(
+				'controller' => Inflector::underscore(Inflector::pluralize($model)),
+				'action' => 'login',
+				'plugin' => Inflector::underscore($plugin),
+			),
+			'sessionKey' => 'Auth.' . $model,
+			'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':
+				if (!isset($this->actionMap[$this->params['action']])) {
+					trigger_error(
+						sprintf(__('Auth::startup() - Attempted access of un-mapped action "%1$s" in controller "%2$s"', 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':
+				$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 mixed $action Controller action name or array of actions
+ * @param string $action Controller action name
+ * @param string ... etc.
+ * @return void
+ * @access public
+ * @link http://book.cakephp.org/view/1257/allow
+ */
+	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, array_map('strtolower', $args));
+		}
+	}
+
+/**
+ * Removes items from the list of allowed actions.
+ *
+ * @param mixed $action Controller action name or array of actions
+ * @param string $action Controller action name
+ * @param string ... etc.
+ * @return void
+ * @see AuthComponent::allow()
+ * @access public
+ * @link http://book.cakephp.org/view/1258/deny
+ */
+	function deny() {
+		$args = func_get_args();
+		if (isset($args[0]) && is_array($args[0])) {
+			$args = $args[0];
+		}
+		foreach ($args as $arg) {
+			$i = array_search(strtolower($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
+ * @link http://book.cakephp.org/view/1260/mapActions
+ */
+	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
+ * @link http://book.cakephp.org/view/1261/login
+ */
+	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
+ * @link http://book.cakephp.org/view/1262/logout
+ */
+	function logout() {
+		$this->__setDefaults();
+		$this->Session->delete($this->sessionKey);
+		$this->Session->delete('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
+ * @link http://book.cakephp.org/view/1264/user
+ */
+	function user($key = null) {
+		$this->__setDefaults();
+		if (!$this->Session->check($this->sessionKey)) {
+			return null;
+		}
+
+		if ($key == null) {
+			$model =& $this->getModel();
+			return array($model->alias => $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
+ * @link http://book.cakephp.org/view/1256/action
+ */
+	function action($action = ':plugin/:controller/:action') {
+		$plugin = empty($this->params['plugin']) ? null : Inflector::camelize($this->params['plugin']) . '/';
+		return str_replace(
+			array(':controller', ':action', ':plugin/'),
+			array(Inflector::camelize($this->params['controller']), $this->params['action'], $plugin),
+			$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 = array();
+		} elseif (is_array($conditions)) {
+			$conditions = array_merge((array)$this->userScope, $conditions);
+		} else {
+			$conditions = $this->userScope;
+		}
+		$model =& $this->getModel();
+		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[$model->alias];
+		} elseif (is_array($user) && isset($user[$model->alias])) {
+			$user = $user[$model->alias];
+		}
+
+		if (is_array($user) && (isset($user[$this->fields['username']]) || isset($user[$model->alias . '.' . $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(
+					$model->alias.'.'.$this->fields['username'] => $user[$this->fields['username']],
+					$model->alias.'.'.$this->fields['password'] => $user[$this->fields['password']]
+				);
+			} elseif (isset($user[$model->alias . '.' . $this->fields['username']]) && !empty($user[$model->alias . '.' . $this->fields['username']])) {
+				if (trim($user[$model->alias . '.' . $this->fields['username']]) == '=' || trim($user[$model->alias . '.' . $this->fields['password']]) == '=') {
+					return false;
+				}
+				$find = array(
+					$model->alias.'.'.$this->fields['username'] => $user[$model->alias . '.' . $this->fields['username']],
+					$model->alias.'.'.$this->fields['password'] => $user[$model->alias . '.' . $this->fields['password']]
+				);
+			} else {
+				return false;
+			}
+			$data = $model->find('first', array(
+				'conditions' => array_merge($find, $conditions),
+				'recursive' => 0
+			));
+			if (empty($data) || empty($data[$model->alias])) {
+				return null;
+			}
+		} elseif (!empty($user) && is_string($user)) {
+			$data = $model->find('first', array(
+				'conditions' => array_merge(array($model->escapeField() => $user), $conditions),
+			));
+			if (empty($data) || empty($data[$model->alias])) {
+				return null;
+			}
+		}
+
+		if (!empty($data)) {
+			if (!empty($data[$model->alias][$this->fields['password']])) {
+				unset($data[$model->alias][$this->fields['password']]);
+			}
+			return $data[$model->alias];
+		}
+		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
+ * @link http://book.cakephp.org/view/1259/hashPasswords
+ */
+	function hashPasswords($data) {
+		if (is_object($this->authenticate) && method_exists($this->authenticate, 'hashPasswords')) {
+			return $this->authenticate->hashPasswords($data);
+		}
+
+		if (is_array($data)) {
+			$model =& $this->getModel();
+			
+			if(isset($data[$model->alias])) {
+				if (isset($data[$model->alias][$this->fields['username']]) && isset($data[$model->alias][$this->fields['password']])) {
+					$data[$model->alias][$this->fields['password']] = $this->password($data[$model->alias][$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
+ * @link http://book.cakephp.org/view/1263/password
+ */
+	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->delete('Auth.redirect');
+		}
+	}
+}

Added: trunk/src/Web/cake/libs/controller/components/cookie.php
===================================================================
--- trunk/src/Web/cake/libs/controller/components/cookie.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/controller/components/cookie.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,488 @@
+<?php
+/**
+ * Cookie Component
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller.components
+ * @since         CakePHP(tm) v 1.2.0.4213
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Load Security class
+ */
+App::import('Core', 'Security');
+
+/**
+ * Cookie Component.
+ *
+ * Cookie handling for the controller.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller.components
+ * @link http://book.cakephp.org/view/1280/Cookies
+ *
+ */
+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);
+		if (isset($this->time)) {
+			$this->__expire($this->time);
+		}
+	}
+
+/**
+ * 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.], required 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)) {
+			$key = array($key => $value);
+		}
+
+		foreach ($key as $name => $value) {
+			if (strpos($name, '.') === false) {
+				$this->__values[$name] = $value;
+				$this->__write("[$name]", $value);
+
+			} else {
+				$names = explode('.', $name, 2);
+				if (!isset($this->__values[$names[0]])) {
+					$this->__values[$names[0]] = array();
+				}
+				$this->__values[$names[0]] = Set::insert($this->__values[$names[0]], $names[1], $value);
+				$this->__write('[' . implode('][', $names) . ']', $value);
+			}
+		}
+		$this->__encrypted = true;
+	}
+
+/**
+ * Read the value of the $_COOKIE[$key];
+ *
+ * Optional [Name.], required 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;
+		}
+
+		if (strpos($key, '.') !== false) {
+			$names = explode('.', $key, 2);
+			$key = $names[0];
+		}
+		if (!isset($this->__values[$key])) {
+			return null;
+		}
+
+		if (!empty($names[1])) {
+			return Set::extract($this->__values[$key], $names[1]);
+		}
+		return $this->__values[$key];
+	}
+
+/**
+ * Delete a cookie value
+ *
+ * Optional [Name.], required 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 delete($key) {
+		if (empty($this->__values)) {
+			$this->read();
+		}
+		if (strpos($key, '.') === false) {
+			if (isset($this->__values[$key]) && is_array($this->__values[$key])) {
+				foreach ($this->__values[$key] as $idx => $val) {
+					$this->__delete("[$key][$idx]");
+				}
+			}
+			$this->__delete("[$key]");
+			unset($this->__values[$key]);
+			return;
+		}
+		$names = explode('.', $key, 2);
+		if (isset($this->__values[$names[0]])) {
+			$this->__values[$names[0]] = Set::remove($this->__values[$names[0]], $names[1]);
+		}
+		$this->__delete('[' . implode('][', $names) . ']');
+	}
+
+/**
+ * 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 ($expires == 0) {
+			return $this->__expires = 0;
+		}
+
+		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 ((array)$values as $name => $value) {
+			if (is_array($value)) {
+				foreach ($value as $key => $val) {
+					$pos = strpos($val, 'Q2FrZQ==.');
+
+					if ($pos !== false) {
+						$val = substr($val, 8);
+						$decrypted[$name][$key] = $this->__explode(Security::$type(base64_decode($val), $this->key));
+					} else {
+						$decrypted[$name][$key] = $this->__explode($val);
+					}
+				}
+			} else {
+				$pos = strpos($value, 'Q2FrZQ==.');
+
+				if ($pos !== false) {
+					$value = substr($value, 8);
+					$decrypted[$name] = $this->__explode(Security::$type(base64_decode($value), $this->key));
+				} else {
+					$decrypted[$name] = $this->__explode($value);
+				}
+			}
+		}
+		return $decrypted;
+	}
+
+/**
+ * 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 mixed If array, map of key and values. If string, value.
+ * @access private
+ */
+	function __explode($string) {
+		$first = substr($string, 0, 1);
+		if ($first !== false && ($first === '{' || $first === '[') && function_exists('json_decode')) {
+			$ret = json_decode($string, true);
+			return ($ret != null) ? $ret : $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;
+	}
+}

Added: trunk/src/Web/cake/libs/controller/components/email.php
===================================================================
--- trunk/src/Web/cake/libs/controller/components/email.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/controller/components/email.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,1008 @@
+<?php
+/**
+ * Email Component
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller.components
+ * @since         CakePHP(tm) v 1.2.0.3467
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Core', 'Multibyte');
+App::import('Core', 'String');
+
+/**
+ * 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
+ * @link http://book.cakephp.org/view/1283/Email
+ *
+ */
+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 date to put in the Date: header.  This should be a date
+ * conformant with the RFC2822 standard.  Leave null, to have
+ * today's date generated.
+ *
+ * @var string
+ */
+	var $date = null;
+
+/**
+ * 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;
+
+/**
+ * Line feed character(s) to be used when sending using mail() function
+ * By default PHP_EOL is used.
+ * RFC2822 requires it to be CRLF but some Unix
+ * mail transfer agents replace LF by CRLF automatically
+ * (which leads to doubling CR if CRLF is used).
+ *
+ * @var string
+ * @access public
+ */
+	var $lineFeed = PHP_EOL;
+
+/**
+ * @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
+ * - client
+ *
+ * @var array
+ * @access public
+ * @link http://book.cakephp.org/view/1290/Sending-A-Message-Using-SMTP
+ */
+	var $smtpOptions = array();
+
+/**
+ * Placeholder for any errors that might happen with the
+ * smtp mail methods
+ *
+ * @var string
+ * @access public
+ */
+	var $smtpError = null;
+
+/**
+ * Contains the rendered plain text message if one was sent.
+ *
+ * @var string
+ * @access public
+ */
+	var $textMessage = null;
+
+/**
+ * Contains the rendered HTML message if one was sent.
+ *
+ * @var string
+ * @access public
+ */
+	var $htmlMessage = null;
+
+/**
+ * Whether to generate a Message-ID header for the
+ * e-mail. True to generate a Message-ID, False to let
+ * it be handled by sendmail (or similar) or a string
+ * to completely override the Message-ID.
+ *
+ * If you are sending Email from a shell, be sure to set this value.  As you
+ * could encounter delivery issues if you do not.
+ *
+ * @var mixed
+ * @access public
+ */
+	var $messageId = true;
+
+/**
+ * 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
+ *  If you are rendering a template this variable will be sent to the templates as `$content`
+ * @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";
+		}
+
+		$this->htmlMessage = $this->textMessage = null;
+		if ($content) {
+			if ($this->sendAs === 'html') {
+				$this->htmlMessage = $content;
+			} elseif ($this->sendAs === 'text') {
+				$this->textMessage = $content;
+			} else {
+				$this->htmlMessage = $this->textMessage = $content;
+			}
+		}
+
+		if ($this->sendAs === 'text') {
+			$message = $this->_wrap($content);
+		} else {
+			$message = $this->_wrap($content, 998);
+		}
+
+		if ($this->template === null) {
+			$message = $this->_formatMessage($message);
+		} else {
+			$message = $this->_render($message);
+		}
+
+		$message[] = '';
+		$this->__message = $message;
+
+		if (!empty($this->attachments)) {
+			$this->_attachFiles();
+		}
+
+		if (!empty($this->attachments)) {
+			$this->__message[] = '';
+			$this->__message[] = '--' . $this->__boundary . '--';
+			$this->__message[] = '';
+		}
+
+		$_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
+ * @link http://book.cakephp.org/view/1285/Sending-Multiple-Emails-in-a-loop
+ */
+	function reset() {
+		$this->template = null;
+		$this->to = array();
+		$this->from = null;
+		$this->replyTo = null;
+		$this->return = null;
+		$this->cc = array();
+		$this->bcc = array();
+		$this->headers = array();
+		$this->subject = null;
+		$this->additionalParams = null;
+		$this->date = null;
+		$this->smtpError = null;
+		$this->attachments = array();
+		$this->htmlMessage = null;
+		$this->textMessage = null;
+		$this->messageId = true;
+		$this->delivery = 'mail';
+		$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 _render($content) {
+		$viewClass = $this->Controller->view;
+
+		if ($viewClass != 'View') {
+			list($plugin, $viewClass) = pluginSplit($viewClass);
+			$viewClass = $viewClass . 'View';
+			App::import('View', $this->Controller->view);
+		}
+
+		$View = new $viewClass($this->Controller);
+		$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", $this->textMessage = 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", $this->htmlMessage = str_replace(array("\r\n", "\r"), "\n", $View->renderLayout($htmlContent)));
+
+			$msg = array_merge($msg, $htmlContent);
+
+			$msg[] = '';
+			$msg[] = '--alt-' . $this->__boundary . '--';
+			$msg[] = '';
+
+			ClassRegistry::removeObject('view');
+			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", $rendered = str_replace(array("\r\n", "\r"), "\n", $View->renderLayout($content)));
+
+		if ($this->sendAs === 'html') {
+			$this->htmlMessage = $rendered;
+		} else {
+			$this->textMessage = $rendered;
+		}
+
+		$msg = array_merge($msg, $content);
+		ClassRegistry::removeObject('view');
+
+		return $msg;
+	}
+
+/**
+ * Create unique boundary identifier
+ *
+ * @access private
+ */
+	function _createboundary() {
+		$this->__boundary = md5(uniqid(time()));
+	}
+
+/**
+ * Sets headers for the message
+ *
+ * @access public
+ * @param array Associative array containing headers to be set.
+ */
+	function header($headers) {
+		foreach ($headers as $header => $value) {
+			$this->__header[] = sprintf('%s: %s', trim($header), trim($value));
+		}
+	}
+/**
+ * Create emails headers including (but not limited to) from email address, reply to,
+ * bcc and cc.
+ *
+ * @access private
+ */
+	function _createHeader() {
+        $headers = array();
+
+		if ($this->delivery == 'smtp') {
+			$headers['To'] = implode(', ', array_map(array($this, '_formatAddress'), (array)$this->to));
+		}
+		$headers['From'] = $this->_formatAddress($this->from);
+
+		if (!empty($this->replyTo)) {
+			$headers['Reply-To'] = $this->_formatAddress($this->replyTo);
+		}
+		if (!empty($this->return)) {
+			$headers['Return-Path'] = $this->_formatAddress($this->return);
+		}
+		if (!empty($this->readReceipt)) {
+			$headers['Disposition-Notification-To'] = $this->_formatAddress($this->readReceipt);
+		}
+
+		if (!empty($this->cc)) {
+			$headers['Cc'] = implode(', ', array_map(array($this, '_formatAddress'), (array)$this->cc));
+		}
+
+		if (!empty($this->bcc) && $this->delivery != 'smtp') {
+			$headers['Bcc'] = implode(', ', array_map(array($this, '_formatAddress'), (array)$this->bcc));
+		}
+		if ($this->delivery == 'smtp') {
+			$headers['Subject'] = $this->_encode($this->subject);
+		}
+
+		if ($this->messageId !== false) {
+			if ($this->messageId === true) {
+				$headers['Message-ID'] = '<' . str_replace('-', '', String::uuid()) . '@' . env('HTTP_HOST') . '>';
+			} else {
+				$headers['Message-ID'] = $this->messageId;
+			}
+		}
+
+		$date = $this->date;
+		if ($date == false) {
+			$date = date(DATE_RFC2822);
+		}
+		$headers['Date'] = $date;
+
+		$headers['X-Mailer'] = $this->xMailer;
+
+		if (!empty($this->headers)) {
+			foreach ($this->headers as $key => $val) {
+				$headers['X-' . $key] = $val;
+			}
+		}
+
+		if (!empty($this->attachments) || $this->sendAs === 'both') {
+			$this->_createBoundary();
+		}
+
+		if (!empty($this->attachments)) {
+			$headers['MIME-Version'] = '1.0';
+			$headers['Content-Type'] = 'multipart/mixed; boundary="' . $this->__boundary . '"';
+		} elseif ($this->sendAs === 'text') {
+			$headers['Content-Type'] = 'text/plain; charset=' . $this->charset;
+		} elseif ($this->sendAs === 'html') {
+			$headers['Content-Type'] = 'text/html; charset=' . $this->charset;
+		} elseif ($this->sendAs === 'both') {
+			$headers['Content-Type'] = 'multipart/alternative; boundary="alt-' . $this->__boundary . '"';
+		}
+
+		$headers['Content-Transfer-Encoding'] = '7bit';
+
+        $this->header($headers);
+	}
+
+/**
+ * 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);
+			if ($this->sendAs === 'text') {
+				$prefix[] = 'Content-Type: text/plain; charset=' . $this->charset;
+			} elseif ($this->sendAs === 'html') {
+				$prefix[] = 'Content-Type: text/html; charset=' . $this->charset;
+			} elseif ($this->sendAs === 'both') {
+				$prefix[] = 'Content-Type: multipart/alternative; boundary="alt-' . $this->__boundary . '"';
+			}
+			$prefix[] = 'Content-Transfer-Encoding: 7bit';
+			$prefix[] = '';
+			$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 $filename => $attachment) {
+			$file = $this->_findFiles($attachment);
+			if (!empty($file)) {
+				if (is_int($filename)) {
+					$filename = basename($file);
+				}
+				$files[$filename] = $file;
+			}
+		}
+
+		foreach ($files as $filename => $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($filename) . '"';
+			$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
+ * @param integer $lineLength Max length of line
+ * @return array Wrapped message
+ * @access protected
+ */
+	function _wrap($message, $lineLength = null) {
+		$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', true), E_USER_WARNING);
+			$this->lineLength = $this->_lineLength;
+		}
+
+		if (!$lineLength) {
+			$lineLength = $this->lineLength;
+		}
+
+		foreach ($lines as $line) {
+			if (substr($line, 0, 1) == '.') {
+				$line = '.' . $line;
+			}
+			$formatted = array_merge($formatted, explode("\n", wordwrap($line, $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 = '';
+		}
+		$internalEncoding = function_exists('mb_internal_encoding');
+		if ($internalEncoding) {
+			$restore = mb_internal_encoding();
+			mb_internal_encoding($this->charset);
+		}
+		$return = mb_encode_mimeheader($subject, $this->charset, 'B', $nl);
+		if ($internalEncoding) {
+			mb_internal_encoding($restore);
+		}
+		return $return;
+	}
+
+/**
+ * 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) {
+		$hasAlias = preg_match('/((.*))?\s?<(.+)>/', $string, $matches);
+		if ($smtp && $hasAlias) {
+			return $this->_strip('<' .  $matches[3] . '>');
+		} elseif ($smtp) {
+			return $this->_strip('<' . $string . '>');
+		}
+
+		if ($hasAlias && !empty($matches[2])) {
+			return $this->_encode(trim($matches[2])) . $this->_strip(' <' . $matches[3] . '>');
+		}
+		return $this->_strip($string);
+	}
+
+/**
+ * Remove certain elements (such as bcc:, to:, %0a) from given value.
+ * Helps prevent header injection / mainipulation on user content.
+ *
+ * @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)\:';
+		$search .= '|charset\=|mime-version\:|multipart/mixed|(?:[\n\r]+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($this->lineFeed, $this->__header);
+		$message = implode($this->lineFeed, $this->__message);
+		if (is_array($this->to)) {
+			$to = implode(', ', array_map(array($this, '_formatAddress'), $this->to));
+		} else {
+			$to = $this->to;
+		}
+		if (ini_get('safe_mode')) {
+			return @mail($to, $this->_encode($this->subject), $message, $header);
+		}
+		return @mail($to, $this->_encode($this->subject), $message, $header, $this->additionalParams);
+	}
+
+
+/**
+ * Helper method to get socket, overridden in tests
+ *
+ * @param array $config Config data for the socket.
+ * @return void
+ * @access protected
+ */
+	function _getSocket($config) {
+		$this->__smtpConnection =& new CakeSocket($config);
+	}
+
+/**
+ * Sends out email via SMTP
+ *
+ * @return bool Success
+ * @access private
+ */
+	function _smtp() {
+		App::import('Core', array('CakeSocket'));
+
+		$defaults = array(
+			'host' => 'localhost',
+			'port' => 25,
+			'protocol' => 'smtp',
+			'timeout' => 30
+		);
+		$this->smtpOptions = array_merge($defaults, $this->smtpOptions);
+		$this->_getSocket($this->smtpOptions);
+
+		if (!$this->__smtpConnection->connect()) {
+			$this->smtpError = $this->__smtpConnection->lastError();
+			return false;
+		} elseif (!$this->_smtpSend(null, '220')) {
+			return false;
+		}
+
+		$httpHost = env('HTTP_HOST');
+
+		if (isset($this->smtpOptions['client'])) {
+			$host = $this->smtpOptions['client'];
+		} elseif (!empty($httpHost)) {
+			list($host) = explode(':', $httpHost);
+		} else {
+			$host = 'localhost';
+		}
+
+		if (!$this->_smtpSend("EHLO {$host}", '250') && !$this->_smtpSend("HELO {$host}", '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 (!is_array($this->to)) {
+			$tos = array_map('trim', explode(',', $this->to));
+		} else {
+			$tos = $this->to;
+		}
+		foreach ($tos as $to) {
+			if (!$this->_smtpSend('RCPT TO: ' . $this->_formatAddress($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;
+	}
+
+/**
+ * Protected 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 protected
+ */
+	function _smtpSend($data, $checkCode = '250') {
+		if (!is_null($data)) {
+			$this->__smtpConnection->write($data . "\r\n");
+		}
+		while ($checkCode !== false) {
+			$response = '';
+			$startTime = time();
+			while (substr($response, -2) !== "\r\n" && ((time() - $startTime) < $this->smtpOptions['timeout'])) {
+				$response .= $this->__smtpConnection->read();
+			}
+			if (substr($response, -2) !== "\r\n") {
+				$this->smtpError = 'timeout';
+				return false;
+			}
+			$response = end(explode("\r\n", rtrim($response, "\r\n")));
+
+			if (preg_match('/^(' . $checkCode . ')(.)/', $response, $code)) {
+				if ($code[2] === '-') {
+					continue;
+				}
+				return $code[1];
+			}
+			$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 (is_array($this->to)) {
+			$to = implode(', ', array_map(array($this, '_formatAddress'), $this->to));
+		} else {
+			$to = $this->to;
+		}
+		$fm .= sprintf('%s %s%s', 'To:', $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>';
+
+		if (isset($this->Controller->Session)) {
+			$this->Controller->Session->setFlash($fm, 'default', null, 'email');
+			return true;
+		}
+		return $fm;
+	}
+}

Added: trunk/src/Web/cake/libs/controller/components/request_handler.php
===================================================================
--- trunk/src/Web/cake/libs/controller/components/request_handler.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/controller/components/request_handler.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,831 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller.components
+ * @since         CakePHP(tm) v 0.10.4.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Request object for handling HTTP requests
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller.components
+ * @link http://book.cakephp.org/view/1291/Request-Handling
+ *
+ */
+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('text/csv', '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'
+	);
+
+/**
+ * List of regular expressions for matching mobile device's user agent string
+ *
+ * @var array
+ * @access public
+ */
+	var $mobileUA = array(
+		'Android',
+		'AvantGo',
+		'BlackBerry',
+		'DoCoMo',
+		'iPod',
+		'iPhone',
+		'iPad',
+		'J2ME',
+		'MIDP',
+		'NetFront',
+		'Nokia',
+		'Opera Mini',
+		'Opera Mobi',
+		'PalmOS',
+		'PalmSource',
+		'portalmmm',
+		'Plucker',
+		'ReqwirelessWeb',
+		'SonyEricsson',
+		'Symbian',
+		'UP\.Browser',
+		'webOS',
+		'Windows CE',
+		'Windows Phone OS',
+		'Xiino'
+	);
+
+/**
+ * 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'));
+		$this->__acceptTypes = array_map('trim', $this->__acceptTypes);
+
+		foreach ($this->__acceptTypes as $i => $type) {
+			if (strpos($type, ';')) {
+				$type = explode(';', $type);
+				$this->__acceptTypes[$i] = trim($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
+ * @param array $settings Array of settings to _set().
+ * @return void
+ * @see Router::parseExtensions()
+ * @access public
+ */
+	function initialize(&$controller, $settings = array()) {
+		if (isset($controller->params['url']['ext'])) {
+			$this->ext = $controller->params['url']['ext'];
+		}
+		$this->params = $controller->params;
+		$this->_set($settings);
+	}
+
+/**
+ * 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');
+		} elseif (empty($this->ext) || in_array($this->ext, array('html', 'htm'))) {
+			$this->respondAs('html', array('charset' => Configure::read('App.encoding')));
+		}
+
+		if ($this->requestedWith('xml')) {
+			if (!class_exists('XmlNode')) {
+				App::import('Core', 'Xml');
+			}
+			$xml = new Xml(trim(file_get_contents('php://input')));
+
+			if (count($xml->children) == 1 && is_object($dataNode = $xml->child('data'))) {
+				$controller->data = $dataNode->toArray();
+			} else {
+				$controller->data = $xml->toArray();
+			}
+		}
+	}
+
+/**
+ * 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
+ * @param mixed HTTP Status for redirect
+ * @access public
+ */
+	function beforeRedirect(&$controller, $url, $status = null) {
+		if (!$this->isAjax()) {
+			return;
+		}
+		foreach ($_POST as $key => $val) {
+			unset($_POST[$key]);
+		}
+		if (is_array($url)) {
+			$url = Router::url($url + array('base' => false));
+		}
+		if (!empty($status)) {
+			$statusCode = $controller->httpCodes($status);
+			$code = key($statusCode);
+			$msg = $statusCode[$code];
+			$controller->header("HTTP/1.1 {$code} {$msg}");
+		}
+		echo $this->requestAction($url, array('return', 'bare' => false));
+		$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
+ * @deprecated Use of constant REQUEST_MOBILE_UA is deprecated and will be removed in future versions
+ */
+	function isMobile() {
+		if (defined('REQUEST_MOBILE_UA')) {
+			$regex = '/' . REQUEST_MOBILE_UA . '/i';
+		} else {
+			$regex = '/' . implode('|', $this->mobileUA) . '/i';
+		}
+
+		if (preg_match($regex, env('HTTP_USER_AGENT')) || $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 getReferer() {
+		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;
+		}
+
+		$types = $type;
+		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];
+		}
+
+		$acceptedTypes = array();
+		foreach ($this->__acceptTypes as $type) {
+			$acceptedTypes[] = $this->mapType($type);
+		}
+		$accepts = array_intersect($acceptedTypes, $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 .= DS . $type;
+		} else {
+			$remove = preg_replace("/([\/\\\\]{$this->__renderType})$/", DS . $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 (!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'])) {
+				$this->_header("Content-Disposition: attachment; filename=\"{$options['attachment']}\"");
+			}
+			if (Configure::read() < 2 && !defined('CAKEPHP_SHELL') && empty($this->params['requested'])) {
+				$this->_header($header);
+			}
+			$this->__responseTypeSet = $cType;
+			return true;
+		}
+		return false;
+	}
+
+/**
+ * Wrapper for header() so calls can be easily tested.
+ *
+ * @param string $header The header to be sent.
+ * @return void
+ * @access protected
+ */
+	function _header($header) {
+		header($header);
+	}
+
+/**
+ * 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;
+	}
+}

Added: trunk/src/Web/cake/libs/controller/components/security.php
===================================================================
--- trunk/src/Web/cake/libs/controller/components/security.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/controller/components/security.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,749 @@
+<?php
+/**
+ * Security Component
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller.components
+ * @since         CakePHP(tm) v 0.10.8.2156
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Core', array('String', 'Security'));
+
+/**
+ * SecurityComponent
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller.components
+ * @link http://book.cakephp.org/view/1296/Security-Component
+ */
+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.
+ *
+ * @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;
+
+/**
+ * Initialize the SecurityComponent
+ *
+ * @param object $controller Controller instance for the request
+ * @param array $settings Settings to set to the component
+ * @return void
+ * @access public
+ */
+	function initialize(&$controller, $settings = array()) {
+		$this->_set($settings);
+	}
+
+/**
+ * Component startup. All security checking happens here.
+ *
+ * @param object $controller Instantiating controller
+ * @return void
+ * @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
+ * @link http://book.cakephp.org/view/1299/requirePost
+ */
+	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
+ * @link http://book.cakephp.org/view/1300/requireSecure
+ */
+	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
+ * @link http://book.cakephp.org/view/1301/requireAuth
+ */
+	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
+ * @link http://book.cakephp.org/view/1302/requireLogin
+ */
+	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
+ * @link http://book.cakephp.org/view/1303/loginCredentials-string-type
+ */
+	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
+ * @link http://book.cakephp.org/view/1304/loginRequest-array-options
+ */
+	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 . ' ' . implode(',', $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
+ * @link http://book.cakephp.org/view/1305/parseDigestAuthData-string-digest
+ */
+	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()
+ * @link http://book.cakephp.org/view/1306/generateDigestResponseHash-array-data
+ */
+	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
+ * @link http://book.cakephp.org/view/1307/blackHole-object-controller-string-error
+ */
+	function blackHole(&$controller, $error = '') {
+		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()) {
+		if (isset($actions[0]) && is_array($actions[0])) {
+			$actions = $actions[0];
+		}
+		$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']) || !isset($data['_Token']['key'])) {
+			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;
+			}
+		} else {
+			return false;
+		}
+
+		$locked = null;
+		$check = $controller->data;
+		$token = urldecode($check['_Token']['fields']);
+
+		if (strpos($token, ':')) {
+			list($token, $locked) = explode(':', $token, 2);
+		}
+		unset($check['_Token']);
+
+		$locked = explode('|', $locked);
+
+		$lockedFields = array();
+		$fields = Set::flatten($check);
+		$fieldList = array_keys($fields);
+		$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) {
+			if ($this->Session->check('_Token')) {
+				$tokenData = unserialize($this->Session->read('_Token'));
+				$controller->params['_Token'] = $tokenData;
+			}
+			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 {
+			return null;
+		}
+	}
+}

Added: trunk/src/Web/cake/libs/controller/components/session.php
===================================================================
--- trunk/src/Web/cake/libs/controller/components/session.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/controller/components/session.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,295 @@
+<?php
+/**
+ * SessionComponent.  Provides access to Sessions from the Controller layer
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller.components
+ * @since         CakePHP(tm) v 0.10.0.1232
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+if (!class_exists('cakesession')) {
+	require LIBS . 'cake_session.php';
+}
+
+/**
+ * Session Component.
+ *
+ * Session handling from the controller.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller.components
+ * @link http://book.cakephp.org/view/1310/Sessions
+ *
+ */
+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 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;
+		}
+	}
+
+/**
+ * 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
+ * @link http://book.cakephp.org/view/1312/write
+ */
+	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
+ * @link http://book.cakephp.org/view/1314/read
+ */
+	function read($name = null) {
+		if ($this->__active === true) {
+			$this->__start();
+			return parent::read($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
+ * @link http://book.cakephp.org/view/1316/delete
+ */
+	function delete($name) {
+		if ($this->__active === true) {
+			$this->__start();
+			return parent::delete($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
+ * @link http://book.cakephp.org/view/1315/check
+ */
+	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
+ * @link http://book.cakephp.org/view/1318/error
+ */
+	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 $element Element 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
+ * @link http://book.cakephp.org/view/1313/setFlash
+ */
+	function setFlash($message, $element = 'default', $params = array(), $key = 'flash') {
+		if ($this->__active === true) {
+			$this->__start();
+			$this->write('Message.' . $key, compact('message', 'element', '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
+ * @link http://book.cakephp.org/view/1317/destroy
+ */
+	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()) {
+				parent::_checkValid();
+			} else {
+				parent::start();
+			}
+		}
+		return $this->started();
+	}
+}

Added: trunk/src/Web/cake/libs/controller/controller.php
===================================================================
--- trunk/src/Web/cake/libs/controller/controller.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/controller/controller.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,1320 @@
+<?php
+/**
+ * Base controller class.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Include files
+ */
+App::import('Controller', 'Component', false);
+App::import('View', 'View', false);
+/**
+ * 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/956/Introduction
+ */
+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/959/Controller-Attributes
+ */
+	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.
+ *
+ * @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');`
+ *
+ * Can be set to array() to use no models.  Can be set to false to
+ * use no models and prevent the merging of $uses with AppController
+ *
+ * @var mixed A single name as a string or a list of names as an array.
+ * @access protected
+ * @link http://book.cakephp.org/view/961/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/961/components-helpers-and-uses
+ */
+	var $helpers = array('Session', '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/963/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/1231/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();
+
+/**
+ * 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/962/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/961/components-helpers-and-uses
+ */
+	var $components = array('Session');
+
+/**
+ * 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/1380/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/1103/Scaffolding
+ */
+	var $scaffold = false;
+
+/**
+ * Holds current methods of the controller
+ *
+ * @var array
+ * @access public
+ * @link
+ */
+	var $methods = array();
+
+/**
+ * This controller's primary model class name, the Inflector::classify()'ed version of
+ * the controller's $name property.
+ *
+ * Example: For a controller named 'Comments', the modelClass would be 'Comment'
+ *
+ * @var string
+ * @access public
+ */
+	var $modelClass = null;
+
+/**
+ * This controller's model key name, an underscored version of the controller's $modelClass property.
+ *
+ * Example: For a controller named 'ArticleComments', the modelKey would be 'article_comment'
+ *
+ * @var string
+ * @access public
+ */
+	var $modelKey = null;
+
+/**
+ * Holds any validation errors produced by the last call of the validateErrors() method/
+ *
+ * @var array Validation errors, or false if none
+ * @access public
+ */
+	var $validationErrors = null;
+
+/**
+ * Contains a list of the HTTP codes that CakePHP recognizes. These may be
+ * queried and/or modified through Controller::httpCodes(), which is also
+ * tasked with their lazy-loading.
+ *
+ * @var array Associative array of HTTP codes and their associated messages.
+ * @access private
+ */
+	var $__httpCodes = null;
+
+/**
+ * Constructor.
+ *
+ */
+	function __construct() {
+		if ($this->name === null) {
+			$r = null;
+			if (!preg_match('/(.*)Controller/i', get_class($this), $r)) {
+				__("Controller::__construct() : Can not get or parse my own class name, exiting.");
+				$this->_stop();
+			}
+			$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);
+				}
+			} else {
+				$merge[] = 'uses';
+			}
+
+			foreach ($merge as $var) {
+				if (!empty($appVars[$var]) && is_array($this->{$var})) {
+					if ($var !== 'uses') {
+						$normal = Set::normalize($this->{$var});
+						$app = Set::normalize($appVars[$var]);
+						if ($app !== $normal) {
+							$this->{$var} = Set::merge($app, $normal);
+						}
+					} else {
+						$this->{$var} = array_merge($this->{$var}, array_diff($appVars[$var], $this->{$var}));
+					}
+				}
+			}
+		}
+
+		if ($pluginController && $pluginName != null) {
+			$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 !== 'uses') {
+						$normal = Set::normalize($this->{$var});
+						$app = Set::normalize($appVars[$var]);
+						if ($app !== $normal) {
+							$this->{$var} = Set::merge($app, $normal);
+						}
+					} else {
+						$this->{$var} = array_merge($this->{$var}, array_diff($appVars[$var], $this->{$var}));
+					}
+				}
+			}
+		}
+	}
+
+/**
+ * Loads Model classes based on 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/977/Controller-Methods#constructClasses-986
+ */
+	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;
+	}
+
+/**
+ * Perform the startup process for this controller.
+ * Fire the Component and Controller callbacks in the correct order.
+ *
+ * - Initializes components, which fires their `initialize` callback
+ * - Calls the controller `beforeFilter`.
+ * - triggers Component `startup` methods.
+ *
+ * @return void
+ * @access public
+ */
+	function startupProcess() {
+		$this->Component->initialize($this);
+		$this->beforeFilter();
+		$this->Component->triggerCallback('startup', $this);
+	}
+
+/**
+ * Perform the various shutdown processes for this controller.
+ * Fire the Component and Controller callbacks in the correct order.
+ *
+ * - triggers the component `shutdown` callback.
+ * - calls the Controller's `afterFilter` method.
+ *
+ * @return void
+ * @access public
+ */
+	function shutdownProcess() {
+		$this->Component->triggerCallback('shutdown', $this);
+		$this->afterFilter();
+	}
+
+/**
+ * Queries & sets valid HTTP response codes & messages.
+ *
+ * @param mixed $code If $code is an integer, then the corresponding code/message is
+ *        returned if it exists, null if it does not exist. If $code is an array,
+ *        then the 'code' and 'message' keys of each nested array are added to the default
+ *        HTTP codes. Example:
+ *
+ *        httpCodes(404); // returns array(404 => 'Not Found')
+ *
+ *        httpCodes(array(
+ *            701 => 'Unicorn Moved',
+ *            800 => 'Unexpected Minotaur'
+ *        )); // sets these new values, and returns true
+ *
+ * @return mixed Associative array of the HTTP codes as keys, and the message
+ *    strings as values, or null of the given $code does not exist.
+ */
+	function httpCodes($code = null) {
+		if (empty($this->__httpCodes)) {
+			$this->__httpCodes = 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 (empty($code)) {
+			return $this->__httpCodes;
+		}
+
+		if (is_array($code)) {
+			$this->__httpCodes = $code + $this->__httpCodes;
+			return true;
+		}
+
+		if (!isset($this->__httpCodes[$code])) {
+			return null;
+		}
+		return array($code => $this->__httpCodes[$code]);
+	}
+
+/**
+ * Loads and instantiates models required by this controller.
+ * If Controller::$persistModel; is true, controller will cache 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 model 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 . '.';
+			}
+		}
+		list($plugin, $modelClass) = pluginSplit($modelClass, true, $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;
+		}
+
+		return true;
+	}
+
+/**
+ * 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/982/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 = $this->httpCodes();
+
+			if (is_string($status)) {
+				$codes = array_flip($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;
+			}
+			$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 and object wrapper method for header().  Useful when doing tests and
+ * asserting that particular headers have been set.
+ *
+ * @param string $status The header message that is being set.
+ * @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/979/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);
+		}
+		$this->viewVars = $data + $this->viewVars;
+	}
+
+/**
+ * Internally redirects one action to another. Does not perform another HTTP request unlike Controller::redirect()
+ *
+ * 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/1275/authorize
+ */
+	function isAuthorized() {
+		trigger_error(sprintf(
+			__('%sController::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 (empty($objects)) {
+			return false;
+		}
+
+		$errors = array();
+		foreach ($objects as $object) {
+			if (isset($this->{$object->alias})) {
+				$object =& $this->{$object->alias};
+			}
+			$object->set($object->data);
+			$errors = array_merge($errors, (array)$object->invalidFields());
+		}
+
+		return $this->validationErrors = (!empty($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/980/render
+ */
+	function render($action = null, $layout = null, $file = null) {
+		$this->beforeRender();
+		$this->Component->triggerCallback('beforeRender', $this);
+
+		$viewClass = $this->view;
+		if ($this->view != 'View') {
+			list($plugin, $viewClass) = pluginSplit($viewClass);
+			$viewClass = $viewClass . 'View';
+			App::import('View', $this->view);
+		}
+
+		$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);
+				}
+				$isValidModel = (
+					isset($this->$currentModel) && is_a($this->$currentModel, 'Model') &&
+					!empty($this->$currentModel->validationErrors)
+				);
+				if ($isValidModel) {
+					$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/987/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) {
+			$url = Router::url($default, true);
+			return $url;
+		}
+		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/988/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 mixed $url Relative string or array-based URL to redirect to after the time expires
+ * @param integer $pause Time to show the message
+ * @param string $layout Layout you want to use, defaults to 'flash'
+ * @return void Renders flash layout
+ * @access public
+ * @link http://book.cakephp.org/view/983/flash
+ */
+	function flash($message, $url, $pause = 1, $layout = 'flash') {
+		$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, $layout);
+	}
+
+/**
+ * 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/989/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 = '';
+		}
+
+		$arrayOp = is_array($op);
+		foreach ($data as $model => $fields) {
+			foreach ($fields as $field => $value) {
+				$key = $model.'.'.$field;
+				$fieldOp = $op;
+				if ($arrayOp) {
+					if (array_key_exists($key, $op)) {
+						$fieldOp = $op[$key];
+					} elseif (array_key_exists($field, $op)) {
+						$fieldOp = $op[$field];
+					} else {
+						$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/1232/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) = pluginSplit($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 ($object->hasField($field)) {
+				$options['order'][$alias . '.' . $field] = $value;
+			} elseif ($object->hasField($key, true)) {
+				$options['order'][$field] = $value;
+			} elseif (isset($object->{$alias}) && $object->{$alias}->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();
+		}
+
+		$type = 'all';
+
+		if (isset($defaults[0])) {
+			$type = $defaults[0];
+			unset($defaults[0]);
+		}
+
+		$options = array_merge(array('page' => 1, 'limit' => 20), $defaults, $options);
+		$options['limit'] = (int) $options['limit'];
+		if (empty($options['limit']) || $options['limit'] < 1) {
+			$options['limit'] = 1;
+		}
+
+		extract($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;
+		}
+
+		$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;
+		}
+		$page = $options['page'] = (integer)$page;
+
+		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/984/Callbacks
+ */
+	function beforeFilter() {
+	}
+
+/**
+ * Called after the controller action is run, but before the view is rendered.
+ *
+ * @access public
+ * @link http://book.cakephp.org/view/984/Callbacks
+ */
+	function beforeRender() {
+	}
+
+/**
+ * Called after the controller action is run and rendered.
+ *
+ * @access public
+ * @link http://book.cakephp.org/view/984/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/984/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/984/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/984/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/984/Callbacks
+ */
+	function _scaffoldError($method) {
+		return false;
+	}
+}

Added: trunk/src/Web/cake/libs/controller/pages_controller.php
===================================================================
--- trunk/src/Web/cake/libs/controller/pages_controller.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/controller/pages_controller.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,85 @@
+<?php
+/**
+ * Static content controller.
+ *
+ * This file will render views from views/pages/
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Static content controller
+ *
+ * Override this controller by placing a copy in controllers directory of an application
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller
+ * @link http://book.cakephp.org/view/958/The-Pages-Controller
+ */
+class PagesController extends AppController {
+
+/**
+ * Controller name
+ *
+ * @var string
+ * @access public
+ */
+	var $name = 'Pages';
+
+/**
+ * Default helper
+ *
+ * @var array
+ * @access public
+ */
+	var $helpers = array('Html', 'Session');
+
+/**
+ * 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_for_layout = null;
+
+		if (!empty($path[0])) {
+			$page = $path[0];
+		}
+		if (!empty($path[1])) {
+			$subpage = $path[1];
+		}
+		if (!empty($path[$count - 1])) {
+			$title_for_layout = Inflector::humanize($path[$count - 1]);
+		}
+		$this->set(compact('page', 'subpage', 'title_for_layout'));
+		$this->render(implode('/', $path));
+	}
+}

Added: trunk/src/Web/cake/libs/controller/scaffold.php
===================================================================
--- trunk/src/Web/cake/libs/controller/scaffold.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/controller/scaffold.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,615 @@
+<?php
+/**
+ * Scaffold.
+ *
+ * Automatic forms and actions generation for rapid web application development.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller
+ * @since         Cake v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Scaffolding is a set of automatic actions 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 Controller
+ * @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;
+
+/**
+ * valid session.
+ *
+ * @var boolean
+ * @access public
+ */
+	var $_validSession = 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;
+		$title_for_layout = __('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(Inflector::underscore($modelClass));
+		$pluralHumanName = Inflector::humanize(Inflector::underscore($this->controller->name));
+		$scaffoldFields = array_keys($this->ScaffoldModel->schema());
+		$associations = $this->__associations();
+
+		$this->controller->set(compact(
+			'title_for_layout', 'modelClass', 'primaryKey', 'displayField', 'singularVar', 'pluralVar',
+			'singularHumanName', 'pluralHumanName', 'scaffoldFields', 'associations'
+		));
+
+		if ($this->controller->view) {
+			$this->controller->view = 'Scaffold';
+		}
+		$this->_validSession = (
+			isset($this->controller->Session) && $this->controller->Session->valid() != false
+		);
+		$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')) {
+
+			$message = sprintf(__("No id set for %s::view()", true), Inflector::humanize($this->modelKey));
+			if (isset($params['pass'][0])) {
+				$this->ScaffoldModel->id = $params['pass'][0];
+			} elseif ($this->_validSession) {
+				$this->controller->Session->setFlash($message);
+				$this->controller->redirect($this->redirect);
+			} else {
+				return $this->controller->flash($message, '/' . 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->viewVars['scaffoldFields'] = array_merge(
+			$this->controller->viewVars['scaffoldFields'],
+			array_keys($this->ScaffoldModel->hasAndBelongsToMany)
+		);
+		$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()) {
+					$message = sprintf(__("Invalid id for %s::edit()", true), Inflector::humanize($this->modelKey));
+					if ($this->_validSession) {
+						$this->controller->Session->setFlash($message);
+						$this->controller->redirect($this->redirect);
+					} else {
+						$this->controller->flash($message, $this->redirect);
+						$this->_output();
+					}
+				}
+			}
+
+			if (!empty($this->controller->data)) {
+				if ($action == 'create') {
+					$this->ScaffoldModel->create();
+				}
+
+				if ($this->ScaffoldModel->save($this->controller->data)) {
+					if ($this->controller->_afterScaffoldSave($action)) {
+						$message = sprintf(
+							__('The %1$s has been %2$s', true),
+							Inflector::humanize($this->modelKey),
+							$success
+						);
+						if ($this->_validSession) {
+							$this->controller->Session->setFlash($message);
+							$this->controller->redirect($this->redirect);
+						} else {
+							$this->controller->flash($message, $this->redirect);
+							return $this->_output();
+						}
+					} else {
+						return $this->controller->_afterScaffoldSaveError($action);
+					}
+				} else {
+					if ($this->_validSession) {
+						$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')) {
+			$message = sprintf(
+				__("No id set for %s::delete()", true),
+				Inflector::humanize($this->modelKey)
+			);
+			if (isset($params['pass'][0])) {
+				$id = $params['pass'][0];
+			} elseif ($this->_validSession) {
+				$this->controller->Session->setFlash($message);
+				$this->controller->redirect($this->redirect);
+			} else {
+				$this->controller->flash($message, $this->redirect);
+				return $this->_output();
+			}
+
+			if ($this->ScaffoldModel->delete($id)) {
+				$message = sprintf(
+					__('The %1$s with id: %2$d has been deleted.', true),
+					Inflector::humanize($this->modelClass), $id
+				);
+				if ($this->_validSession) {
+					$this->controller->Session->setFlash($message);
+					$this->controller->redirect($this->redirect);
+				} else {
+					$this->controller->flash($message, $this->redirect);
+					return $this->_output();
+				}
+			} else {
+				$message = sprintf(
+					__('There was an error deleting the %1$s with id: %2$d', true),
+					Inflector::humanize($this->modelClass), $id
+				);
+				if ($this->_validSession) {
+					$this->controller->Session->setFlash($message);
+					$this->controller->redirect($this->redirect);
+				} else {
+					$this->controller->flash($message, $this->redirect);
+					return $this->_output();
+				}
+			}
+		} 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:
+ * `var $scaffold;` 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);
+		$prefixes = Configure::read('Routing.prefixes');
+		$scaffoldPrefix = $this->scaffoldActions;
+
+		if (isset($db)) {
+			if (empty($this->scaffoldActions)) {
+				$this->scaffoldActions = array(
+					'index', 'list', 'view', 'add', 'create', 'edit', 'update', 'delete'
+				);
+			} elseif (!empty($prefixes) && in_array($scaffoldPrefix, $prefixes)) {
+				$this->scaffoldActions = array(
+					$scaffoldPrefix . '_index',
+					$scaffoldPrefix . '_list',
+					$scaffoldPrefix . '_view',
+					$scaffoldPrefix . '_add',
+					$scaffoldPrefix . '_create',
+					$scaffoldPrefix . '_edit',
+					$scaffoldPrefix . '_update',
+					$scaffoldPrefix . '_delete'
+				);
+			}
+
+			if (in_array($params['action'], $this->scaffoldActions)) {
+				if (!empty($prefixes)) {
+					$params['action'] = str_replace($scaffoldPrefix . '_', '', $params['action']);
+				}
+				switch ($params['action']) {
+					case 'index':
+					case 'list':
+						$this->__scaffoldIndex($params);
+					break;
+					case 'view':
+						$this->__scaffoldView($params);
+					break;
+					case 'add':
+					case 'create':
+						$this->__scaffoldSave($params, 'add');
+					break;
+					case 'edit':
+					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']));
+
+				if ($type == 'hasAndBelongsToMany') {
+					$associations[$type][$assocKey]['with'] = $assocData['with'];
+				}
+			}
+		}
+		return $associations;
+	}
+}
+
+/**
+ * Scaffold View.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.controller
+*/
+if (!class_exists('ThemeView')) {
+	App::import('View', 'Theme');
+}
+
+/**
+ * ScaffoldView provides specific view file loading features for scaffolded views.
+ *
+ * @package cake.libs.view
+ */
+class ScaffoldView extends ThemeView {
+
+/**
+ * Override _getViewFileName Appends special scaffolding views in.
+ *
+ * @param string $name name of the view file to get.
+ * @return string action
+ * @access protected
+ */
+	function _getViewFileName($name = null) {
+		if ($name === null) {
+			$name = $this->action;
+		}
+		$name = Inflector::underscore($name);
+		$prefixes = Configure::read('Routing.prefixes');
+
+		if (!empty($prefixes)) {
+			foreach ($prefixes as $prefix) {
+				if (strpos($name, $prefix . '_') !== false) {
+					$name = substr($name, strlen($prefix) + 1);
+					break;
+				}
+			}
+		}
+
+		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);
+		if ($this->ext !== '.ctp') {
+			array_push($exts, '.ctp');
+		}
+		foreach ($exts as $ext) {
+			foreach ($paths as $path) {
+				foreach ($names as $name) {
+					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');
+	}
+}

Added: trunk/src/Web/cake/libs/debugger.php
===================================================================
--- trunk/src/Web/cake/libs/debugger.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/debugger.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,721 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.4560
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Included libraries.
+ *
+ */
+if (!class_exists('Object')) {
+	require_once LIBS . 'object.php';
+}
+if (!class_exists('CakeLog')) {
+	require_once LIBS . 'cake_log.php';
+}
+if (!class_exists('String')) {
+	require_once LIBS . 'string.php';
+}
+
+/**
+ * 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/1191/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';
+
+/**
+ * Templates used when generating trace or error strings.  Can be global or indexed by the format
+ * value used in $_outputFormat.
+ *
+ * @var string
+ * @access protected
+ */
+	var $_templates = array(
+		'log' => array(
+			'trace' => '{:reference} - {:path}, line {:line}',
+			'error' => "{:error} ({:code}): {:description} in [{:file}, line {:line}]"
+		),
+		'js' => array(
+			'error' => '',
+			'info' => '',
+			'trace' => '<pre class="stack-trace">{:trace}</pre>',
+			'code' => '',
+			'context' => '',
+			'links' => array()
+		),
+		'html' => array(
+			'trace' => '<pre class="cake-debug trace"><b>Trace</b> <p>{:trace}</p></pre>',
+			'context' => '<pre class="cake-debug context"><b>Context</b> <p>{:context}</p></pre>'
+		),
+		'txt' => array(
+			'error' => "{:error}: {:code} :: {:description} on line {:line} of {:path}\n{:info}",
+			'context' => "Context:\n{:context}\n",
+			'trace' => "Trace:\n{:trace}\n",
+			'code' => '',
+			'info' => ''
+		),
+		'base' => array(
+			'traceLine' => '{:reference} - {:path}, line {:line}'
+		)
+	);
+
+/**
+ * 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) && function_exists('ini_set')) {
+			ini_set('docref_root', 'http://php.net/');
+		}
+		if (!defined('E_RECOVERABLE_ERROR')) {
+			define('E_RECOVERABLE_ERROR', 4096);
+		}
+		if (!defined('E_DEPRECATED')) {
+			define('E_DEPRECATED', 8192);
+		}
+
+		$e = '<pre class="cake-debug">';
+		$e .= '<a href="javascript:void(0);" onclick="document.getElementById(\'{:id}-trace\')';
+		$e .= '.style.display = (document.getElementById(\'{:id}-trace\').style.display == ';
+		$e .= '\'none\' ? \'\' : \'none\');"><b>{:error}</b> ({:code})</a>: {:description} ';
+		$e .= '[<b>{:path}</b>, line <b>{:line}</b>]';
+
+		$e .= '<div id="{:id}-trace" class="cake-stack-trace" style="display: none;">';
+		$e .= '{:links}{:info}</div>';
+		$e .= '</pre>';
+		$this->_templates['js']['error'] = $e;
+
+		$t = '<div id="{:id}-trace" class="cake-stack-trace" style="display: none;">';
+		$t .= '{:context}{:code}{:trace}</div>';
+		$this->_templates['js']['info'] = $t;
+
+		$links = array();
+		$link = '<a href="javascript:void(0);" onclick="document.getElementById(\'{:id}-code\')';
+		$link .= '.style.display = (document.getElementById(\'{:id}-code\').style.display == ';
+		$link .= '\'none\' ? \'\' : \'none\')">Code</a>';
+		$links['code'] = $link;
+
+		$link = '<a href="javascript:void(0);" onclick="document.getElementById(\'{:id}-context\')';
+		$link .= '.style.display = (document.getElementById(\'{:id}-context\').style.display == ';
+		$link .= '\'none\' ? \'\' : \'none\')">Context</a>';
+		$links['context'] = $link;
+
+		$links['help'] = '<a href="{:helpPath}{:code}" target="_blank">Help</a>';
+		$this->_templates['js']['links'] = $links;
+
+		$this->_templates['js']['context'] = '<pre id="{:id}-context" class="cake-context" ';
+		$this->_templates['js']['context'] .= 'style="display: none;">{:context}</pre>';
+
+		$this->_templates['js']['code'] = '<div id="{:id}-code" class="cake-code-dump" ';
+		$this->_templates['js']['code'] .= 'style="display: none;"><pre>{:code}</pre></div>';
+
+		$e = '<pre class="cake-debug"><b>{:error}</b> ({:code}) : {:description} ';
+		$e .= '[<b>{:path}</b>, line <b>{:line}]</b></pre>';
+		$this->_templates['html']['error'] = $e;
+
+		$this->_templates['html']['context'] = '<pre class="cake-debug context"><b>Context</b> ';
+		$this->_templates['html']['context'] .= '<p>{:context}</p></pre>';
+	}
+
+/**
+ * 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 Debugger::exportVar()
+ * @access public
+ * @static
+ * @link http://book.cakephp.org/view/1191/Using-the-Debugger-Class
+*/
+	function dump($var) {
+		$_this =& Debugger::getInstance();
+		pr($_this->exportVar($var));
+	}
+
+/**
+ * Creates an entry in the log file.  The log entry will contain a stack trace from where it was called.
+ * as well as export the variable using exportVar. By default the log is written to the 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/1191/Using-the-Debugger-Class
+ */
+	function log($var, $level = LOG_DEBUG) {
+		$_this =& Debugger::getInstance();
+		$source = $_this->trace(array('start' => 1)) . "\n";
+		CakeLog::write($level, "\n" . $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 || $code === 8192) {
+			return;
+		}
+
+		$_this =& Debugger::getInstance();
+
+		if (empty($file)) {
+			$file = '[internal]';
+		}
+		if (empty($line)) {
+			$line = '??';
+		}
+		$path = $_this->trimPath($file);
+
+		$info = compact('code', 'description', 'file', 'line');
+		if (!in_array($info, $_this->errors)) {
+			$_this->errors[] = $info;
+		} else {
+			return;
+		}
+
+		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;
+			break;
+		}
+
+		$helpCode = null;
+		if (!empty($_this->helpPath) && preg_match('/.*\[([0-9]+)\]$/', $description, $codes)) {
+			if (isset($codes[1])) {
+				$helpID = $codes[1];
+				$description = trim(preg_replace('/\[[0-9]+\]$/', '', $description));
+			}
+		}
+
+		$data = compact(
+			'level', 'error', 'code', 'helpID', 'description', 'file', 'path', 'line', 'context'
+		);
+		echo $_this->_output($data);
+
+		if (Configure::read('log')) {
+			$tpl = $_this->_templates['log']['error'];
+			$options = array('before' => '{:', 'after' => '}');
+			CakeLog::write($level, String::insert($tpl, $data, $options));
+		}
+
+		if ($error == 'Fatal Error') {
+			exit();
+		}
+		return true;
+	}
+
+/**
+ * Outputs a stack trace based on the supplied options.
+ *
+ * ### Options
+ *
+ * - `depth` - The number of stack frames to return. Defaults to 999
+ * - `format` - The format you want the return.  Defaults to the currently selected format.  If
+ *    format is 'array' or 'points' the return will be an array.
+ * - `args` - Should arguments for functions be shown?  If true, the arguments for each method call
+ *   will be displayed.
+ * - `start` - The stack frame to start generating a trace from.  Defaults to 0
+ *
+ * @param array $options Format for outputting stack trace
+ * @return mixed Formatted stack trace
+ * @access public
+ * @static
+ * @link http://book.cakephp.org/view/1191/Using-the-Debugger-Class
+ */
+	function trace($options = array()) {
+		$_this =& Debugger::getInstance();
+		$defaults = array(
+			'depth'   => 999,
+			'format'  => $_this->_outputFormat,
+			'args'    => false,
+			'start'   => 0,
+			'scope'   => null,
+			'exclude' => null
+		);
+		$options += $defaults;
+
+		$backtrace = debug_backtrace();
+		$count = count($backtrace);
+		$back = array();
+
+		$_trace = array(
+			'line'     => '??',
+			'file'     => '[internal]',
+			'class'    => null,
+			'function' => '[main]'
+		);
+
+		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($_trace, $backtrace[$i + 1]);
+				$reference = $next['function'];
+
+				if (!empty($next['class'])) {
+					$reference = $next['class'] . '::' . $reference . '(';
+					if ($options['args'] && isset($next['args'])) {
+						$args = array();
+						foreach ($next['args'] as $arg) {
+							$args[] = Debugger::exportVar($arg);
+						}
+						$reference .= join(', ', $args);
+					}
+					$reference .= ')';
+				}
+			} else {
+				$reference = '[main]';
+			}
+			if (in_array($reference, array('call_user_func_array', 'trigger_error'))) {
+				continue;
+			}
+			if ($options['format'] == 'points' && $trace['file'] != '[internal]') {
+				$back[] = array('file' => $trace['file'], 'line' => $trace['line']);
+			} elseif ($options['format'] == 'array') {
+				$back[] = $trace;
+			} else {
+				if (isset($_this->_templates[$options['format']]['traceLine'])) {
+					$tpl = $_this->_templates[$options['format']]['traceLine'];
+				} else {
+					$tpl = $_this->_templates['base']['traceLine'];
+				}
+				$trace['path'] = Debugger::trimPath($trace['file']);
+				$trace['reference'] = $reference;
+				unset($trace['object'], $trace['args']);
+				$back[] = String::insert($tpl, $trace, array('before' => '{:', 'after' => '}'));
+			}
+		}
+
+		if ($options['format'] == 'array' || $options['format'] == 'points') {
+			return $back;
+		}
+		return implode("\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 = App::core('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/1191/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/1191/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':
+				$var = array_merge($var,  array_intersect_key(array(
+					'password' => '*****',
+					'login'  => '*****',
+					'host' => '*****',
+					'database' => '*****',
+					'port' => '*****',
+					'prefix' => '*****',
+					'schema' => '*****'
+				), $var));
+
+				$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 (!empty($vars)) {
+					$n = "\n";
+				}
+				return $out . implode(",", $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 implode("\n", $out);
+	}
+
+/**
+ * Switches output format, updates format strings
+ *
+ * @param string $format Format to use, including 'js' for JavaScript-enhanced HTML, 'html' for
+ *    straight HTML output, or 'txt' for unformatted text.
+ * @param array $strings Template strings to be used for the output format.
+ * @access protected
+ */
+	function output($format = null, $strings = array()) {
+		$_this =& Debugger::getInstance();
+		$data = null;
+
+		if (is_null($format)) {
+			return $_this->_outputFormat;
+		}
+
+		if (!empty($strings)) {
+			if (isset($_this->_templates[$format])) {
+				if (isset($strings['links'])) {
+					$_this->_templates[$format]['links'] = array_merge(
+						$_this->_templates[$format]['links'],
+						$strings['links']
+					);
+					unset($strings['links']);
+				}
+				$_this->_templates[$format] = array_merge($_this->_templates[$format], $strings);
+			} else {
+				$_this->_templates[$format] = $strings;
+			}
+			return $_this->_templates[$format];
+		}
+
+		if ($format === true && !empty($_this->_data)) {
+			$data = $_this->_data;
+			$_this->_data = array();
+			$format = false;
+		}
+		$_this->_outputFormat = $format;
+
+		return $data;
+	}
+
+/**
+ * Renders error messages
+ *
+ * @param array $data Data about the current error
+ * @access private
+ */
+	function _output($data = array()) {
+		$defaults = array(
+			'level' => 0,
+			'error' => 0,
+			'code' => 0,
+			'helpID' => null,
+			'description' => '',
+			'file' => '',
+			'line' => 0,
+			'context' => array()
+		);
+		$data += $defaults;
+
+		$files = $this->trace(array('start' => 2, 'format' => 'points'));
+		$code = '';
+		if (isset($files[1]['file'])) {
+			$code = $this->excerpt($files[1]['file'], $files[1]['line'] - 1, 1);
+		}
+		$trace = $this->trace(array('start' => 2, 'depth' => '20'));
+		$insertOpts = array('before' => '{:', 'after' => '}');
+		$context = array();
+		$links = array();
+		$info = '';
+
+		foreach ((array)$data['context'] as $var => $value) {
+			$context[] = "\${$var}\t=\t" . $this->exportVar($value, 1);
+		}
+
+		switch ($this->_outputFormat) {
+			case false:
+				$this->_data[] = compact('context', 'trace') + $data;
+				return;
+			case 'log':
+				$this->log(compact('context', 'trace') + $data);
+				return;
+		}
+
+		if (empty($this->_outputFormat) || !isset($this->_templates[$this->_outputFormat])) {
+			$this->_outputFormat = 'js';
+		}
+
+		$data['id'] = 'cakeErr' . count($this->errors);
+		$tpl = array_merge($this->_templates['base'], $this->_templates[$this->_outputFormat]);
+		$insert = array('context' => join("\n", $context), 'helpPath' => $this->helpPath) + $data;
+
+		$detect = array('help' => 'helpID', 'context' => 'context');
+
+		if (isset($tpl['links'])) {
+			foreach ($tpl['links'] as $key => $val) {
+				if (isset($detect[$key]) && empty($insert[$detect[$key]])) {
+					continue;
+				}
+				$links[$key] = String::insert($val, $insert, $insertOpts);
+			}
+		}
+
+		foreach (array('code', 'context', 'trace') as $key) {
+			if (empty($$key) || !isset($tpl[$key])) {
+				continue;
+			}
+			if (is_array($$key)) {
+				$$key = join("\n", $$key);
+			}
+			$info .= String::insert($tpl[$key], compact($key) + $insert, $insertOpts);
+		}
+		$links = join(' | ', $links);
+		unset($data['context']);
+
+		echo String::insert($tpl['error'], compact('links', 'info') + $data, $insertOpts);
+	}
+
+/**
+ * Verifies that the application's salt and cipher seed value has been changed from the default value.
+ *
+ * @access public
+ * @static
+ */
+	function checkSecurityKeys() {
+		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);
+		}
+
+		if (Configure::read('Security.cipherSeed') === '76859309657453542496749683645') {
+			trigger_error(__('Please change the value of \'Security.cipherSeed\' in app/config/core.php to a numeric (digits only) seed 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/1191/Using-the-Debugger-Class
+ */
+	function invoke(&$debugger) {
+		set_error_handler(array(&$debugger, 'handleError'));
+	}
+}
+
+if (!defined('DISABLE_DEFAULT_ERROR_HANDLING')) {
+	Debugger::invoke(Debugger::getInstance());
+}

Added: trunk/src/Web/cake/libs/error.php
===================================================================
--- trunk/src/Web/cake/libs/error.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/error.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,462 @@
+<?php
+/**
+ * Error handler
+ *
+ * Provides Error Capturing for Framework errors.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 0.10.5.1732
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+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 Controller
+ * @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('debug') == 0) {
+				$parentClass = get_parent_class($this);
+				if (strtolower($parentClass) != 'errorhandler') {
+					$method = 'error404';
+				}
+				$parentMethods = array_map('strtolower', get_class_methods($parentClass));
+				if (in_array(strtolower($method), $parentMethods)) {
+					$method = 'error404';
+				}
+				if (isset($messages[0]['code']) && $messages[0]['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);
+		$this->controller->header("HTTP/1.0 404 Not Found");
+		$this->controller->set(array(
+			'code' => '404',
+			'name' => __('Not Found', true),
+			'message' => h($url),
+			'base' => $this->controller->base
+		));
+		$this->_outputMessage('error404');
+	}
+
+/**
+ * Convenience method to display a 500 page.
+ *
+ * @param array $params Parameters for controller
+ * @access public
+ */
+	function error500($params) {
+		extract($params, EXTR_OVERWRITE);
+
+		if (!isset($url)) {
+			$url = $this->controller->here;
+		}
+		$url = Router::normalize($url);
+		$this->controller->header("HTTP/1.0 500 Internal Server Error");
+		$this->controller->set(array(
+			'code' => '500',
+			'name' => __('An Internal Error Has Occurred', true),
+			'message' => h($url),
+			'base' => $this->controller->base
+		));
+		$this->_outputMessage('error500');
+	}
+/**
+ * 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->header("HTTP/1.0 500 Internal Server Error");
+		$this->controller->set(array(
+			'code' => '500',
+			'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->header("HTTP/1.0 500 Internal Server Error");
+		$this->controller->set(array(
+			'code' => '500',
+			'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->header("HTTP/1.0 500 Internal Server Error");
+		$this->controller->set(array(
+			'code' => '500',
+			'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 Behavior file web page.
+ *
+ * @param array $params Parameters for controller
+ * @access public
+ */
+	function missingBehaviorFile($params) {
+		extract($params, EXTR_OVERWRITE);
+
+		$this->controller->set(array(
+			'behaviorClass' => Inflector::camelize($behavior) . "Behavior",
+			'file' => $file,
+			'title' => __('Missing Behavior File', true)
+		));
+		$this->_outputMessage('missingBehaviorFile');
+	}
+
+/**
+ * Renders the Missing Behavior class web page.
+ *
+ * @param array $params Parameters for controller
+ * @access public
+ */
+	function missingBehaviorClass($params) {
+		extract($params, EXTR_OVERWRITE);
+
+		$this->controller->set(array(
+			'behaviorClass' => Inflector::camelize($behavior) . "Behavior",
+			'file' => $file,
+			'title' => __('Missing Behavior Class', true)
+		));
+		$this->_outputMessage('missingBehaviorClass');
+	}
+
+/**
+ * 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;
+	}
+}

Added: trunk/src/Web/cake/libs/file.php
===================================================================
--- trunk/src/Web/cake/libs/file.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/file.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,546 @@
+<?php
+/**
+ * Convenience class for reading, writing and appending to files.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Included libraries.
+ *
+ */
+if (!class_exists('Object')) {
+	require LIBS . 'object.php';
+}
+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 Folder
+ * @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
+ */
+	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();
+		$create && !$this->exists() && $this->safe($path) && $this->create();
+	}
+
+/**
+ * Closes the current file if it is opened
+ *
+ */
+	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 A `fread` compatible 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);
+		}
+
+		if ($this->lock !== null) {
+			flock($this->handle, LOCK_UN);
+		}
+		if ($bytes === false) {
+			$this->close();
+		}
+		return trim($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.  Converts line endings to the 
+ * correct terminator for the current platform.  If windows "\r\n" will be used
+ * all other platforms will use "\n"
+ *
+ * @param string $data Data to prepare for writing.
+ * @return string The with converted line endings.
+ * @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 (is_resource($this->handle)) {
+			fclose($this->handle);
+			$this->handle = null;
+		}
+		if ($this->exists()) {
+			return unlink($this->path);
+		}
+		return false;
+	}
+
+/**
+ * Returns the File info.
+ *
+ * @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
+ * @param strin $ext The name of the extension to make safe if different from $this->ext
+ * @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);
+		}
+
+		$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's 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;
+	}
+
+/**
+ * Copy the File to $dest
+ *
+ * @param string $dest destination for the copy
+ * @param boolean $overwrite Overwrite $dest if exists
+ * @return boolean Succes
+ * @access public
+ */
+	function copy($dest, $overwrite = true) {
+		if (!$this->exists() || is_file($dest) && !$overwrite) {
+			return false;
+		}
+		return copy($this->path, $dest);
+	}
+}

Added: trunk/src/Web/cake/libs/folder.php
===================================================================
--- trunk/src/Web/cake/libs/folder.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/folder.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,787 @@
+<?php
+/**
+ * Convenience class for handling directories.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Included libraries.
+ *
+ */
+if (!class_exists('Object')) {
+	require LIBS . 'object.php';
+}
+
+/**
+ * Folder structure browser, lists folders and files.
+ * Provides an Object interface for Common directory related tasks.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class Folder extends Object {
+
+/**
+ * Path to Folder.
+ *
+ * @var string
+ * @access public
+ */
+	var $path = null;
+
+/**
+ * Sortedness. Whether or not list results
+ * should be sorted by name.
+ *
+ * @var boolean
+ * @access public
+ */
+	var $sort = false;
+
+/**
+ * Mode to be used on create. Does nothing on windows platforms.
+ *
+ * @var integer
+ * @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 Whether you want the results sorted, set this and the sort property
+ *   to false to get unsorted results.
+ * @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 (!$this->pwd()) {
+			return array($dirs, $files);
+		}
+		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: .*)
+ * @param boolean $sort Whether results should be sorted.
+ * @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: .*)
+ * @param boolean $sort Whether results should be sorted.
+ * @return array Files matching $pattern
+ * @access public
+ */
+	function findRecursive($pattern = '.*', $sort = false) {
+		if (!$this->pwd()) {
+			return array();
+		}
+		$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
+ * @param boolean $sort Whether results should be sorted.
+ * @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, $sort));
+		}
+		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) {
+		return (preg_match('/^[A-Z]:\\\\/i', $path) || substr($path, 0, 2) == '\\\\');
+	}
+
+/**
+ * Returns true if given $path is an absolute path.
+ *
+ * @param string $path Path to check
+ * @return bool true if path is absolute.
+ * @access public
+ * @static
+ */
+	function isAbsolute($path) {
+		return !empty($path) && ($path[0] === '/' || preg_match('/^[A-Z]:\\\\/i', $path) || substr($path, 0, 2) == '\\\\');
+	}
+
+/**
+ * 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) {
+		return (Folder::isWindowsPath($path)) ? '\\' : '/';
+	}
+
+/**
+ * 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 rtrim($path, DS) . DS . $element;
+	}
+
+/**
+ * Returns true if the File is in a given CakePath.
+ *
+ * @param string $path The path to check.
+ * @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.
+ *
+ * @param string $path The path to check that the current pwd() resides with in.
+ * @param boolean $reverse
+ * @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);
+		}
+		return (bool)$return;
+	}
+
+/**
+ * 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, set to false to only change the current directory.
+ * @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 mixed $exceptions Array of files to exclude, defaults to excluding hidden files.
+ * @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);
+		if (!$this->cd($path)) {
+			if ($type === null) {
+				return array(array(), array());
+			}
+			return array();
+		}
+		$this->__files = array();
+		$this->__directories = array($this->realpath($path));
+		$directories = array();
+
+		if ($exceptions === false) {
+			$exceptions = true;
+		}
+		while (!empty($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 The Path to read.
+ * @param mixed $exceptions Array of files to exclude from the read that will be performed.
+ * @access private
+ */
+	function __tree($path, $exceptions) {
+		$this->path = $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. Can be used to create
+ * deep path structures like `/foo/bar/baz/shoe/horn`
+ *
+ * @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;
+		}
+		$pathname = rtrim($pathname, DS);
+		$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 false;
+	}
+
+/**
+ * Returns the size in bytes of this Folder and its contents.
+ *
+ * @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 the system allows.
+ *
+ * @param string $path Path of directory to delete
+ * @return boolean Success
+ * @access public
+ */
+	function delete($path = null) {
+		if (!$path) {
+			$path = $this->pwd();
+		}
+		if (!$path) {
+			return null;
+		}
+		$path = Folder::slashTerm($path);
+		if (is_dir($path) === true) {
+			$normalFiles = glob($path . '*');
+			$hiddenFiles = glob($path . '\.?*');
+
+			$normalFiles = $normalFiles ? $normalFiles : array();
+			$hiddenFiles = $hiddenFiles ? $hiddenFiles : array();
+
+			$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.
+ *
+ * ### Options
+ *
+ * - `to` The directory to copy to.
+ * - `from` The directory to copy from, this will cause a cd() to occur, changing the results of pwd().
+ * - `mode` The mode to copy the files/directories with.
+ * - `skip` Files/directories to skip.
+ *
+ * @param mixed $options Either an array of options (see above) or a string of the destination directory.
+ * @return bool Success
+ * @access public
+ */
+	function copy($options = array()) {
+		if (!$this->pwd()) {
+			return false;
+		}
+		$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->create($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.
+ *
+ * ### Options
+ *
+ * - `to` The directory to copy to.
+ * - `from` The directory to copy from, this will cause a cd() to occur, changing the results of pwd().
+ * - `chmod` The mode to copy the files/directories with.
+ * - `skip` Files/directories to skip.
+ *
+ * @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;
+	}
+
+/**
+ * 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 (!empty($newparts)) {
+					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 === '\\';
+	}
+}

Added: trunk/src/Web/cake/libs/http_socket.php
===================================================================
--- trunk/src/Web/cake/libs/http_socket.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/http_socket.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,1081 @@
+<?php
+/**
+ * HTTP Socket connection class.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Core', array('CakeSocket', 'Set', 'Router'));
+
+/**
+ * Cake network socket connection class.
+ *
+ * Core base class for HTTP network communication. HttpSocket can be used as an
+ * Object Oriented replacement for cURL in many places.
+ *
+ * @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.
+ *
+ * You can use a url string to set the url and use default configurations for
+ * all other options:
+ *
+ * `$http =& new HttpSocket('http://cakephp.org/');`
+ *
+ * Or use an array to configure multiple options:
+ *
+ * {{{
+ * $http =& new HttpSocket(array(
+ *    'host' => 'cakephp.org',
+ *    'timeout' => 20
+ * ));
+ * }}}
+ *
+ * See HttpSocket::$config for options that can be used.
+ *
+ * @param mixed $config Configuration information, either a string url or an array of options.
+ * @access public
+ */
+	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. HttpSocket::get() and HttpSocket::post() wrap this
+ * method and provide a more granular interface.
+ *
+ * @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']);
+		$hadAuth = false;
+		if (is_array($uri) && array_key_exists('user', $uri)) {
+			$hadAuth = true;
+		}
+		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);
+
+		if (!$hadAuth && !empty($this->config['request']['auth']['user'])) {
+			$this->request['uri']['user'] = $this->config['request']['auth']['user'];
+			$this->request['uri']['pass'] = $this->config['request']['auth']['pass'];
+		}
+		$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']);
+			}
+			$Host = $this->request['uri']['host'];
+			$schema = '';
+			$port = 0;
+			if (isset($this->request['uri']['schema'])) {
+				$schema = $this->request['uri']['schema'];
+			}
+			if (isset($this->request['uri']['port'])) {
+				$port = $this->request['uri']['port'];
+			}
+			if (
+				($schema === 'http' && $port != 80) ||
+				($schema === 'https' && $port != 443) ||
+				($port != 80 && $port != 443)
+			) {
+				$Host .= ':' . $port;
+			}
+			$this->request['header'] = array_merge(compact('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 = null;
+		if (isset($this->request['header']['Connection'])) {
+			$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.
+ *
+ * Using a string uri and an array of query string parameters:
+ *
+ * `$response = $http->get('http://google.com/search', array('q' => 'cakephp', 'client' => 'safari'));`
+ *
+ * Would do a GET request to `http://google.com/search?q=cakephp&client=safari`
+ *
+ * You could express the same thing using a uri array and query string parameters:
+ *
+ * {{{
+ * $response = $http->get(
+ *     array('host' => 'google.com', 'path' => '/search'),
+ *     array('q' => 'cakephp', 'client' => 'safari')
+ * );
+ * }}}
+ *
+ * @param mixed $uri URI to request. Either a string uri, or a uri array, see HttpSocket::_parseUri()
+ * @param array $query Querystring parameters to append to URI
+ * @param array $request An indexed array with indexes such as 'method' or uri
+ * @return mixed Result of request, either false on failure or the response to the 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.
+ *
+ * `post()` can be used to post simple data arrays to a url:
+ *
+ * {{{
+ * $response = $http->post('http://example.com', array(
+ *     'username' => 'batman',
+ *     'password' => 'bruce_w4yne'
+ * ));
+ * }}}
+ *
+ * @param mixed $uri URI to request. See HttpSocket::_parseUri()
+ * @param array $data Array of POST data keys and values.
+ * @param array $request An indexed array with indexes such as 'method' or uri
+ * @return mixed Result of request, either false on failure or the response to the 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 HttpSocket::_parseUri()
+ * @param array $data Array of PUT data keys and values.
+ * @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 $data 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);
+	}
+
+/**
+ * Normalizes urls into a $uriTemplate.  If no template is provided
+ * a default one will be used. Will generate the url using the
+ * current config information.
+ *
+ * ### Usage:
+ *
+ * After configuring part of the request parameters, you can use url() to generate
+ * urls.
+ *
+ * {{{
+ * $http->configUri('http://www.cakephp.org');
+ * $url = $http->url('/search?q=bar');
+ * }}}
+ *
+ * Would return `http://www.cakephp.org/search?q=bar`
+ *
+ * url() can also be used with custom templates:
+ *
+ * `$url = $http->url('http://www.cakephp/search?q=socket', '/%path?%query');`
+ *
+ * Would return `/search?q=socket`.
+ *
+ * @param mixed $url Either a string or array of url options to create a url with.
+ * @param string $uriTemplate A template string to use for url formatting.
+ * @return mixed Either false on failure or a string containing the composed url.
+ * @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']);
+		$transferEncoding = null;
+		if (isset($response['header']['Transfer-Encoding'])) {
+			$transferEncoding = $response['header']['Transfer-Encoding'];
+		}
+		$decoded = $this->_decodeBody($response['raw']['body'], $transferEncoding);
+		$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 of response headers and body 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 of response headers and body 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 HttpSocket::_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 mixed $uri Either A $uri array, or a request string.  Will use $this->config if left empty.
+ * @param string $uriTemplate The Uri template/format to use.
+ * @return mixed A fully qualified URL formated according to $uriTemplate, or false on failure
+ * @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@',
+			'host' => '%host:%port/'
+		);
+
+		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, 2);
+				} 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 = implode(',', $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;
+	}
+
+/**
+ * Parses cookies in response headers.
+ *
+ * @param array $header Header array containing one ore more 'Set-Cookie' headers.
+ * @return mixed Either false on no cookies, or an array of cookies received.
+ * @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) {
+			if (strpos($cookie, '";"') !== false) {
+				$cookie = str_replace('";"', "{__cookie_replace__}", $cookie);
+				$parts  = str_replace("{__cookie_replace__}", '";"', explode(';', $cookie));
+			} else {
+				$parts = preg_split('/\;[ \t]*/', $cookie);
+			}
+
+			list($name, $value) = explode('=', array_shift($parts), 2);
+			$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;
+	}
+
+/**
+ * Builds cookie headers for a request.
+ *
+ * @param array $cookies Array of cookies to send with the request.
+ * @return string Cookie header string to be sent with the request.
+ * @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' => implode('; ', $header)), 'pragmatic');
+		return $header;
+	}
+
+/**
+ * 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 protected
+ * @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;
+	}
+}

Added: trunk/src/Web/cake/libs/i18n.php
===================================================================
--- trunk/src/Web/cake/libs/i18n.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/i18n.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,568 @@
+<?php
+/**
+ * Internationalization
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.0.4116
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Included libraries.
+ */
+App::import('Core', array('l10n', 'Multibyte'));
+
+/**
+ * I18n handles translation of Text and time format strings.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class I18n extends Object {
+
+/**
+ * Instance of the I10n class for localization
+ *
+ * @var I10n
+ * @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;
+
+/**
+ * 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_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME', 'LC_MESSAGES'
+	);
+
+/**
+ * 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 App::import('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 The domain of the translation.  Domains are often used by plugin translations
+ * @param string $category Category The integer value of the category to use.
+ * @param integer $count Count Count is used with $plural to choose the correct plural form.
+ * @return string translated string.
+ * @access public
+ */
+	function translate($singular, $plural = null, $domain = null, $category = 6, $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->lang;
+
+		if (!isset($_this->__domains[$domain][$_this->__lang])) {
+			$_this->__domains[$domain][$_this->__lang] = Cache::read($_this->domain, '_cake_core_');
+		}
+
+		if (!isset($_this->__domains[$domain][$_this->__lang][$_this->category])) {
+			$_this->__bindTextDomain($domain);
+			Cache::write($_this->domain, $_this->__domains[$domain][$_this->__lang], '_cake_core_');
+		}
+
+		if ($_this->category == 'LC_TIME') {
+			return $_this->__translateTime($singular,$domain);
+		}
+
+		if (!isset($count)) {
+			$plurals = 0;
+		} elseif (!empty($_this->__domains[$domain][$_this->__lang][$_this->category]["%plural-c"]) && $_this->__noLocale === false) {
+			$header = $_this->__domains[$domain][$_this->__lang][$_this->category]["%plural-c"];
+			$plurals = $_this->__pluralGuess($header, $count);
+		} else {
+			if ($count != 1) {
+				$plurals = 1;
+			} else {
+				$plurals = 0;
+			}
+		}
+
+		if (!empty($_this->__domains[$domain][$_this->__lang][$_this->category][$singular])) {
+			if (($trans = $_this->__domains[$domain][$_this->__lang][$_this->category][$singular]) || ($plurals) && ($trans = $_this->__domains[$domain][$_this->__lang][$_this->category][$plural])) {
+				if (is_array($trans)) {
+					if (isset($trans[$plurals])) {
+						$trans = $trans[$plurals];
+					}
+				}
+				if (strlen($trans)) {
+					return $trans;
+				}
+			}
+		}
+
+		if (!empty($plurals)) {
+			return $plural;
+		}
+		return $singular;
+	}
+
+/**
+ * Clears the domains internal data array.  Useful for testing i18n.
+ *
+ * @return void
+ */
+	function clear() {
+		$self =& I18n::getInstance();
+		$self->__domains = array();
+	}
+
+/**
+ * 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 = App::path('locales');
+		$plugins = App::objects('plugin');
+
+		if (!empty($plugins)) {
+			foreach ($plugins as $plugin) {
+				$plugin = Inflector::underscore($plugin);
+				if ($plugin === $domain) {
+					$searchPaths[] = App::pluginPath($plugin) . DS . 'locale' . DS;
+					$searchPaths = array_reverse($searchPaths);
+					break;
+				}
+			}
+		}
+
+
+		foreach ($searchPaths as $directory) {
+
+			foreach ($this->l10n->languagePath as $lang) {
+				$file = $directory . $lang . DS . $this->category . DS . $domain;
+				$localeDef = $directory . $lang . DS . $this->category;
+
+				if ($core) {
+					$app = $directory . $lang . DS . $this->category . DS . 'core';
+
+					if (file_exists($fn = "$app.mo")) {
+						$this->__loadMo($fn, $domain);
+						$this->__noLocale = false;
+						$merge[$domain][$this->__lang][$this->category] = $this->__domains[$domain][$this->__lang][$this->category];
+						$core = null;
+					} elseif (file_exists($fn = "$app.po") && ($f = fopen($fn, "r"))) {
+						$this->__loadPo($f, $domain);
+						$this->__noLocale = false;
+						$merge[$domain][$this->__lang][$this->category] = $this->__domains[$domain][$this->__lang][$this->category];
+						$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;
+				} elseif (is_file($localeDef) && ($f = fopen($localeDef, "r"))) {
+					$this->__loadLocaleDefinition($f, $domain);
+					$this->__noLocale = false;
+					return $domain;
+				}
+			}
+		}
+
+		if (empty($this->__domains[$domain][$this->__lang][$this->category])) {
+			$this->__domains[$domain][$this->__lang][$this->category] = array();
+			return $domain;
+		}
+		
+		if (isset($this->__domains[$domain][$this->__lang][$this->category][""])) {
+			$head = $this->__domains[$domain][$this->__lang][$this->category][""];
+			
+			foreach (explode("\n", $head) as $line) {
+				$header = strtok($line,":");
+				$line = trim(strtok("\n"));
+				$this->__domains[$domain][$this->__lang][$this->category]["%po-header"][strtolower($header)] = $line;
+			}
+
+			if (isset($this->__domains[$domain][$this->__lang][$this->category]["%po-header"]["plural-forms"])) {
+				$switch = preg_replace("/(?:[() {}\\[\\]^\\s*\\]]+)/", "", $this->__domains[$domain][$this->__lang][$this->category]["%po-header"]["plural-forms"]);
+				$this->__domains[$domain][$this->__lang][$this->category]["%plural-c"] = $switch;
+				unset($this->__domains[$domain][$this->__lang][$this->category]["%po-header"]);
+			}
+			$this->__domains = Set::pushDiff($this->__domains, $merge);
+
+			if (isset($this->__domains[$domain][$this->__lang][$this->category][null])) {
+				unset($this->__domains[$domain][$this->__lang][$this->category][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[$domain][$this->__lang][$this->category][$msgid] = $msgstr;
+
+					if (isset($msgid_plural)) {
+						$this->__domains[$domain][$this->__lang][$this->category][$msgid_plural] =& $this->__domains[$domain][$this->__lang][$this->category][$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));
+			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[$domain][$this->__lang][$this->category] = array_merge($merge ,$translations);
+	}
+
+/**
+ * Parses a locale definition file following the POSIX standard
+ *
+ * @param resource $file file handler
+ * @param string $domain Domain where locale definitions will be stored
+ * @return void
+ * @access private
+ */
+	function __loadLocaleDefinition($file, $domain = null) {
+		$comment = '#';
+		$escape = '\\';
+		$currentToken = false;
+		$value = '';
+		while ($line = fgets($file)) {
+			$line = trim($line);
+			if (empty($line) || $line[0] === $comment) {
+				continue;
+			}
+			$parts = preg_split("/[[:space:]]+/",$line);
+			if ($parts[0] === 'comment_char') {
+				$comment = $parts[1];
+				continue;
+			}
+			if ($parts[0] === 'escape_char') {
+				$escape = $parts[1];
+				continue;
+			}
+			$count = count($parts);
+			if ($count == 2) {
+				$currentToken = $parts[0];
+				$value = $parts[1];
+			} elseif ($count == 1) {
+				$value .= $parts[0];
+			} else {
+				continue;
+			}
+
+			$len = strlen($value) - 1;
+			if ($value[$len] === $escape) {
+				$value = substr($value, 0, $len);
+				continue;
+			}
+
+			$mustEscape = array($escape . ',' , $escape . ';', $escape . '<', $escape . '>', $escape . $escape);
+			$replacements = array_map('crc32', $mustEscape);
+			$value = str_replace($mustEscape, $replacements, $value);
+			$value = explode(';', $value);
+			$this->__escape = $escape;
+			foreach ($value as $i => $val) {
+				$val = trim($val, '"');
+				$val = preg_replace_callback('/(?:<)?(.[^>]*)(?:>)?/', array(&$this, '__parseLiteralValue'), $val);
+				$val = str_replace($replacements, $mustEscape, $val);
+				$value[$i] = $val;
+			}
+			if (count($value) == 1) {
+				$this->__domains[$domain][$this->__lang][$this->category][$currentToken] = array_pop($value);
+			} else {
+				$this->__domains[$domain][$this->__lang][$this->category][$currentToken] = $value;
+			}
+		}
+	}
+
+/**
+ * Auxiliary function to parse a symbol from a locale definition file
+ *
+ * @param string $string Symbol to be parsed
+ * @return string parsed symbol
+ * @access private
+ */
+	function __parseLiteralValue($string) {
+		$string = $string[1];
+		if (substr($string, 0, 2) === $this->__escape . 'x') {
+			$delimiter = $this->__escape . 'x';
+			return join('', array_map('chr', array_map('hexdec',array_filter(explode($delimiter, $string)))));
+		}
+		if (substr($string, 0, 2) === $this->__escape . 'd') {
+			$delimiter = $this->__escape . 'd';
+			return join('', array_map('chr', array_filter(explode($delimiter, $string))));
+		}
+		if ($string[0] === $this->__escape && isset($string[1]) && is_numeric($string[1])) {
+			$delimiter = $this->__escape;
+			return join('', array_map('chr', array_filter(explode($delimiter, $string))));
+		}
+		if (substr($string, 0, 3) === 'U00') {
+			$delimiter = 'U00';
+			return join('', array_map('chr', array_map('hexdec', array_filter(explode($delimiter, $string)))));
+		}
+		if (preg_match('/U([0-9a-fA-F]{4})/', $string, $match)) {
+			return Multibyte::ascii(array(hexdec($match[1])));
+		}
+		return $string;
+	}
+
+/**
+ * Returns a Time format definition from corresponding domain
+ *
+ * @param string $format Format to be translated
+ * @param string $domain Domain where format is stored
+ * @return mixed translated format string if only value or array of translated strings for corresponding format.
+ * @access private
+ */
+	function __translateTime($format, $domain) {
+		if (!empty($this->__domains[$domain][$this->__lang]['LC_TIME'][$format])) {
+			if (($trans = $this->__domains[$domain][$this->__lang][$this->category][$format])) {
+				return $trans;
+			}
+		}
+		return $format;
+	}
+}

Added: trunk/src/Web/cake/libs/inflector.php
===================================================================
--- trunk/src/Web/cake/libs/inflector.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/inflector.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,633 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Pluralize and singularize English words.
+ *
+ * Inflector pluralizes and singularizes English nouns.
+ * Used by Cake's naming conventions throughout the framework.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @link          http://book.cakephp.org/view/1478/Inflector
+ */
+class Inflector {
+
+/**
+ * Plural inflector rules
+ *
+ * @var array
+ * @access protected
+ */
+	var $_plural = array(
+		'rules' => 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$/i' => 'uses',
+			'/(alias)$/i' => '\1es',
+			'/(ax|cris|test)is$/i' => '\1es',
+			'/s$/' => 's',
+			'/^$/' => '',
+			'/$/' => 's',
+		),
+		'uninflected' => array(
+			'.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', 'people'
+		),
+		'irregular' => array(
+			'atlas' => 'atlases',
+			'beef' => 'beefs',
+			'brother' => 'brothers',
+			'cafe' => 'cafes',
+			'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',
+			'niche' => 'niches',
+			'numen' => 'numina',
+			'occiput' => 'occiputs',
+			'octopus' => 'octopuses',
+			'opus' => 'opuses',
+			'ox' => 'oxen',
+			'penis' => 'penises',
+			'person' => 'people',
+			'sex' => 'sexes',
+			'soliloquy' => 'soliloquies',
+			'testis' => 'testes',
+			'trilby' => 'trilbys',
+			'turf' => 'turfs'
+		)
+	);
+
+/**
+ * Singular inflector rules
+ *
+ * @var array
+ * @access protected
+ */
+	var $_singular = array(
+		'rules' => 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',
+			'/([ftw]ax)es/i' => '\1',
+			'/(cris|ax|test)es$/i' => '\1is',
+			'/(shoe|slave)s$/i' => '\1',
+			'/(o)es$/i' => '\1',
+			'/ouses$/' => 'ouse',
+			'/([^a])uses$/' => '\1us',
+			'/([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',
+			'/(analy|ba|diagno|(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',
+			'/eaus$/' => 'eau',
+			'/^(.*us)$/' => '\\1',
+			'/s$/i' => ''
+		),
+		'uninflected' => array(
+			'.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', '.*ss'
+		),
+		'irregular' => array(
+			'foes' => 'foe',
+			'waves' => 'wave',
+			'curves' => 'curve'
+		)
+	);
+
+/**
+ * Words that should not be inflected
+ *
+ * @var array
+ * @access protected
+ */
+	var $_uninflected = array(
+		'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'
+	);
+
+/**
+ * Default map of accented and special characters to ASCII characters
+ *
+ * @var array
+ * @access protected
+ */
+	var $_transliteration = array(
+		'/ä|æ|ǽ/' => 'ae',
+		'/ö|œ/' => 'oe',
+		'/ü/' => 'ue',
+		'/Ä/' => 'Ae',
+		'/Ü/' => 'Ue',
+		'/Ö/' => 'Oe',
+		'/À|Á|Â|Ã|Ä|Å|Ǻ|Ā|Ă|Ą|Ǎ/' => 'A',
+		'/à|á|â|ã|å|ǻ|ā|ă|ą|ǎ|ª/' => 'a',
+		'/Ç|Ć|Ĉ|Ċ|Č/' => 'C',
+		'/ç|ć|ĉ|ċ|č/' => 'c',
+		'/Ð|Ď|Đ/' => 'D',
+		'/ð|ď|đ/' => 'd',
+		'/È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě/' => 'E',
+		'/è|é|ê|ë|ē|ĕ|ė|ę|ě/' => 'e',
+		'/Ĝ|Ğ|Ġ|Ģ/' => 'G',
+		'/ĝ|ğ|ġ|ģ/' => 'g',
+		'/Ĥ|Ħ/' => 'H',
+		'/ĥ|ħ/' => 'h',
+		'/Ì|Í|Î|Ï|Ĩ|Ī|Ĭ|Ǐ|Į|İ/' => 'I',
+		'/ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı/' => 'i',
+		'/Ĵ/' => 'J',
+		'/ĵ/' => 'j',
+		'/Ķ/' => 'K',
+		'/ķ/' => 'k',
+		'/Ĺ|Ļ|Ľ|Ŀ|Ł/' => 'L',
+		'/ĺ|ļ|ľ|ŀ|ł/' => 'l',
+		'/Ñ|Ń|Ņ|Ň/' => 'N',
+		'/ñ|ń|ņ|ň|ʼn/' => 'n',
+		'/Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ/' => 'O',
+		'/ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º/' => 'o',
+		'/Ŕ|Ŗ|Ř/' => 'R',
+		'/ŕ|ŗ|ř/' => 'r',
+		'/Ś|Ŝ|Ş|Š/' => 'S',
+		'/ś|ŝ|ş|š|ſ/' => 's',
+		'/Ţ|Ť|Ŧ/' => 'T',
+		'/ţ|ť|ŧ/' => 't',
+		'/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ/' => 'U',
+		'/ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ/' => 'u',
+		'/Ý|Ÿ|Ŷ/' => 'Y',
+		'/ý|ÿ|ŷ/' => 'y',
+		'/Ŵ/' => 'W',
+		'/ŵ/' => 'w',
+		'/Ź|Ż|Ž/' => 'Z',
+		'/ź|ż|ž/' => 'z',
+		'/Æ|Ǽ/' => 'AE',
+		'/ß/'=> 'ss',
+		'/IJ/' => 'IJ',
+		'/ij/' => 'ij',
+		'/Œ/' => 'OE',
+		'/ƒ/' => 'f'
+	);
+
+/**
+ * Cached array identity map of pluralized words.
+ *
+ * @var array
+ * @access protected
+ */
+	var $_pluralized = array();
+
+/**
+ * Cached array identity map of singularized words.
+ *
+ * @var array
+ * @access protected
+ */
+	var $_singularized = array();
+
+/**
+ * Cached Underscore Inflections
+ *
+ * @var array
+ * @access protected
+ */
+	var $_underscore = array();
+
+/**
+ * Cached Camelize Inflections
+ *
+ * @var array
+ * @access protected
+ */
+	var $_camelize = array();
+
+/**
+ * Classify cached inflecctions
+ *
+ * @var array
+ * @access protected
+ */
+	var $_classify = array();
+
+/**
+ * Tablize cached inflections
+ *
+ * @var array
+ * @access protected
+ */
+	var $_tableize = array();
+
+/**
+ * Humanize cached inflections
+ *
+ * @var array
+ * @access protected
+ */
+	var $_humanize = array();
+
+/**
+ * Gets a reference to the Inflector object instance
+ *
+ * @return object
+ * @access public
+ */
+	function &getInstance() {
+		static $instance = array();
+
+		if (!$instance) {
+			$instance[0] =& new Inflector();
+		}
+		return $instance[0];
+	}
+
+/**
+ * Cache inflected values, and return if already available
+ *
+ * @param string $type Inflection type
+ * @param string $key Original value
+ * @param string $value Inflected value
+ * @return string Inflected value, from cache
+ * @access protected
+ */
+	function _cache($type, $key, $value = false) {
+		$key = '_' . $key;
+		$type = '_' . $type;
+		if ($value !== false) {
+			$this->{$type}[$key] = $value;
+			return $value;
+		}
+
+		if (!isset($this->{$type}[$key])) {
+			return false;
+		}
+		return $this->{$type}[$key];
+	}
+
+/**
+ * Adds custom inflection $rules, of either 'plural', 'singular' or 'transliteration' $type.
+ *
+ * ### Usage:
+ *
+ * {{{
+ * Inflector::rules('plural', array('/^(inflect)or$/i' => '\1ables'));
+ * Inflector::rules('plural', array(
+ *     'rules' => array('/^(inflect)ors$/i' => '\1ables'),
+ *     'uninflected' => array('dontinflectme'),
+ *     'irregular' => array('red' => 'redlings')
+ * ));
+ * Inflector::rules('transliteration', array('/å/' => 'aa'));
+ * }}}
+ *
+ * @param string $type The type of inflection, either 'plural', 'singular' or 'transliteration'
+ * @param array $rules Array of rules to be added.
+ * @param boolean $reset If true, will unset default inflections for all
+ *        new rules that are being defined in $rules.
+ * @access public
+ * @return void
+ * @static
+ */
+	function rules($type, $rules, $reset = false) {
+		$_this =& Inflector::getInstance();
+		$var = '_'.$type;
+
+		switch ($type) {
+			case 'transliteration':
+				if ($reset) {
+					$_this->_transliteration = $rules;
+				} else {
+					$_this->_transliteration = $rules + $_this->_transliteration;
+				}
+			break;
+
+			default:
+				foreach ($rules as $rule => $pattern) {
+					if (is_array($pattern)) {
+						if ($reset) {
+							$_this->{$var}[$rule] = $pattern;
+						} else {
+							if ($rule === 'uninflected') {
+								$_this->{$var}[$rule] = array_merge($pattern, $_this->{$var}[$rule]);
+							} else {
+								$_this->{$var}[$rule] = $pattern + $_this->{$var}[$rule];
+							}
+						}
+						unset($rules[$rule], $_this->{$var}['cache' . ucfirst($rule)]);
+						if (isset($_this->{$var}['merged'][$rule])) {
+							unset($_this->{$var}['merged'][$rule]);
+						}
+						if ($type === 'plural') {
+							$_this->_pluralized = $_this->_tableize = array();
+						} elseif ($type === 'singular') {
+							$_this->_singularized = array();
+						}
+					}
+				}
+				$_this->{$var}['rules'] = $rules + $_this->{$var}['rules'];
+			break;
+		}
+	}
+
+/**
+ * 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/1479/Class-methods
+ */
+	function pluralize($word) {
+		$_this =& Inflector::getInstance();
+
+		if (isset($_this->_pluralized[$word])) {
+			return $_this->_pluralized[$word];
+		}
+
+		if (!isset($_this->_plural['merged']['irregular'])) {
+			$_this->_plural['merged']['irregular'] = $_this->_plural['irregular'];
+		}
+
+		if (!isset($_this->plural['merged']['uninflected'])) {
+			$_this->_plural['merged']['uninflected'] = array_merge($_this->_plural['uninflected'], $_this->_uninflected);
+		}
+
+		if (!isset($_this->_plural['cacheUninflected']) || !isset($_this->_plural['cacheIrregular'])) {
+			$_this->_plural['cacheUninflected'] = '(?:' . implode('|', $_this->_plural['merged']['uninflected']) . ')';
+			$_this->_plural['cacheIrregular'] = '(?:' . implode('|', array_keys($_this->_plural['merged']['irregular'])) . ')';
+		}
+
+		if (preg_match('/(.*)\\b(' . $_this->_plural['cacheIrregular'] . ')$/i', $word, $regs)) {
+			$_this->_pluralized[$word] = $regs[1] . substr($word, 0, 1) . substr($_this->_plural['merged']['irregular'][strtolower($regs[2])], 1);
+			return $_this->_pluralized[$word];
+		}
+
+		if (preg_match('/^(' . $_this->_plural['cacheUninflected'] . ')$/i', $word, $regs)) {
+			$_this->_pluralized[$word] = $word;
+			return $word;
+		}
+
+		foreach ($_this->_plural['rules'] as $rule => $replacement) {
+			if (preg_match($rule, $word)) {
+				$_this->_pluralized[$word] = preg_replace($rule, $replacement, $word);
+				return $_this->_pluralized[$word];
+			}
+		}
+	}
+
+/**
+ * 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/1479/Class-methods
+ */
+	function singularize($word) {
+		$_this =& Inflector::getInstance();
+
+		if (isset($_this->_singularized[$word])) {
+			return $_this->_singularized[$word];
+		}
+
+		if (!isset($_this->_singular['merged']['uninflected'])) {
+			$_this->_singular['merged']['uninflected'] = array_merge($_this->_singular['uninflected'], $_this->_uninflected);
+		}
+
+		if (!isset($_this->_singular['merged']['irregular'])) {
+			$_this->_singular['merged']['irregular'] = array_merge($_this->_singular['irregular'], array_flip($_this->_plural['irregular']));
+		}
+
+		if (!isset($_this->_singular['cacheUninflected']) || !isset($_this->_singular['cacheIrregular'])) {
+			$_this->_singular['cacheUninflected'] = '(?:' . join( '|', $_this->_singular['merged']['uninflected']) . ')';
+			$_this->_singular['cacheIrregular'] = '(?:' . join( '|', array_keys($_this->_singular['merged']['irregular'])) . ')';
+		}
+
+		if (preg_match('/(.*)\\b(' . $_this->_singular['cacheIrregular'] . ')$/i', $word, $regs)) {
+			$_this->_singularized[$word] = $regs[1] . substr($word, 0, 1) . substr($_this->_singular['merged']['irregular'][strtolower($regs[2])], 1);
+			return $_this->_singularized[$word];
+		}
+
+		if (preg_match('/^(' . $_this->_singular['cacheUninflected'] . ')$/i', $word, $regs)) {
+			$_this->_singularized[$word] = $word;
+			return $word;
+		}
+
+		foreach ($_this->_singular['rules'] 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/1479/Class-methods
+ */
+	function camelize($lowerCaseAndUnderscoredWord) {
+		$_this =& Inflector::getInstance();
+		if (!($result = $_this->_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord))) {
+			$result = str_replace(' ', '', Inflector::humanize($lowerCaseAndUnderscoredWord));
+			$_this->_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord, $result);
+		}
+		return $result;
+	}
+
+/**
+ * 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/1479/Class-methods
+ */
+	function underscore($camelCasedWord) {
+		$_this =& Inflector::getInstance();
+		if (!($result = $_this->_cache(__FUNCTION__, $camelCasedWord))) {
+			$result = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $camelCasedWord));
+			$_this->_cache(__FUNCTION__, $camelCasedWord, $result);
+		}
+		return $result;
+	}
+
+/**
+ * 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/1479/Class-methods
+ */
+	function humanize($lowerCaseAndUnderscoredWord) {
+		$_this =& Inflector::getInstance();
+		if (!($result = $_this->_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord))) {
+			$result = ucwords(str_replace('_', ' ', $lowerCaseAndUnderscoredWord));
+			$_this->_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord, $result);
+		}
+		return $result;
+	}
+
+/**
+ * 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/1479/Class-methods
+ */
+	function tableize($className) {
+		$_this =& Inflector::getInstance();
+		if (!($result = $_this->_cache(__FUNCTION__, $className))) {
+			$result = Inflector::pluralize(Inflector::underscore($className));
+			$_this->_cache(__FUNCTION__, $className, $result);
+		}
+		return $result;
+	}
+
+/**
+ * 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/1479/Class-methods
+ */
+	function classify($tableName) {
+		$_this =& Inflector::getInstance();
+		if (!($result = $_this->_cache(__FUNCTION__, $tableName))) {
+			$result = Inflector::camelize(Inflector::singularize($tableName));
+			$_this->_cache(__FUNCTION__, $tableName, $result);
+		}
+		return $result;
+	}
+
+/**
+ * Returns camelBacked version of an underscored string.
+ *
+ * @param string $string
+ * @return string in variable form
+ * @access public
+ * @static
+ * @link http://book.cakephp.org/view/1479/Class-methods
+ */
+	function variable($string) {
+		$_this =& Inflector::getInstance();
+		if (!($result = $_this->_cache(__FUNCTION__, $string))) {
+			$string2 = Inflector::camelize(Inflector::underscore($string));
+			$replace = strtolower(substr($string2, 0, 1));
+			$result = preg_replace('/\\w/', $replace, $string2, 1);
+			$_this->_cache(__FUNCTION__, $string, $result);
+		}
+		return $result;
+	}
+
+/**
+ * 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 the string you want to slug
+ * @param string $replacement will replace keys in map
+ * @param array $map extra elements to map to the replacement
+ * @deprecated $map param will be removed in future versions. Use Inflector::rules() instead
+ * @return string
+ * @access public
+ * @static
+ * @link http://book.cakephp.org/view/1479/Class-methods
+ */
+	function slug($string, $replacement = '_', $map = array()) {
+		$_this =& Inflector::getInstance();
+
+		if (is_array($replacement)) {
+			$map = $replacement;
+			$replacement = '_';
+		}
+		$quotedReplacement = preg_quote($replacement, '/');
+
+		$merge = array(
+			'/[^\s\p{Ll}\p{Lm}\p{Lo}\p{Lt}\p{Lu}\p{Nd}]/mu' => ' ',
+			'/\\s+/' => $replacement,
+			sprintf('/^[%s]+|[%s]+$/', $quotedReplacement, $quotedReplacement) => '',
+		);
+
+		$map = $map + $_this->_transliteration + $merge;
+		return preg_replace(array_keys($map), array_values($map), $string);
+	}
+}

Added: trunk/src/Web/cake/libs/l10n.php
===================================================================
--- trunk/src/Web/cake/libs/l10n.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/l10n.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,492 @@
+<?php
+/**
+ * Localization
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.0.4116
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Localization
+ *
+ * @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';
+
+/**
+ * Text direction for current locale
+ *
+ * @var string
+ * @access public
+ */
+	var $direction = 'ltr';
+
+/**
+ * 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',
+								/* Tibetan */ 'bod' => 'bo',
+								/* 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',
+								/* Slovak */ 'slo' => 'sk',
+								/* Slovak */ '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',
+								/* Welsh */ 'cym' => 'cy',
+								/* 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', 'direction' => 'ltr'),
+										'ar' => array('language' => 'Arabic', 'locale' => 'ara', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+										'ar-ae' => array('language' => 'Arabic (U.A.E.)', 'locale' => 'ar_ae', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+										'ar-bh' => array('language' => 'Arabic (Bahrain)', 'locale' => 'ar_bh', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+										'ar-dz' => array('language' => 'Arabic (Algeria)', 'locale' => 'ar_dz', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+										'ar-eg' => array('language' => 'Arabic (Egypt)', 'locale' => 'ar_eg', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+										'ar-iq' => array('language' => 'Arabic (Iraq)', 'locale' => 'ar_iq', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+										'ar-jo' => array('language' => 'Arabic (Jordan)', 'locale' => 'ar_jo', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+										'ar-kw' => array('language' => 'Arabic (Kuwait)', 'locale' => 'ar_kw', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+										'ar-lb' => array('language' => 'Arabic (Lebanon)', 'locale' => 'ar_lb', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+										'ar-ly' => array('language' => 'Arabic (Libya)', 'locale' => 'ar_ly', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+										'ar-ma' => array('language' => 'Arabic (Morocco)', 'locale' => 'ar_ma', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+										'ar-om' => array('language' => 'Arabic (Oman)', 'locale' => 'ar_om', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+										'ar-qa' => array('language' => 'Arabic (Qatar)', 'locale' => 'ar_qa', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+										'ar-sa' => array('language' => 'Arabic (Saudi Arabia)', 'locale' => 'ar_sa', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+										'ar-sy' => array('language' => 'Arabic (Syria)', 'locale' => 'ar_sy', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+										'ar-tn' => array('language' => 'Arabic (Tunisia)', 'locale' => 'ar_tn', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+										'ar-ye' => array('language' => 'Arabic (Yemen)', 'locale' => 'ar_ye', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+										'be' => array('language' => 'Byelorussian', 'locale' => 'bel', 'localeFallback' => 'bel', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'bg' => array('language' => 'Bulgarian', 'locale' => 'bul', 'localeFallback' => 'bul', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'bo' => array('language' => 'Tibetan', 'locale' => 'bod', 'localeFallback' => 'bod', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'bo-cn' => array('language' => 'Tibetan (China)', 'locale' => 'bo_cn', 'localeFallback' => 'bod', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'bo-in' => array('language' => 'Tibetan (India)', 'locale' => 'bo_in', 'localeFallback' => 'bod', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'bs' => array('language' => 'Bosnian', 'locale' => 'bos', 'localeFallback' => 'bos', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'ca' => array('language' => 'Catalan', 'locale' => 'cat', 'localeFallback' => 'cat', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'cs' => array('language' => 'Czech', 'locale' => 'cze', 'localeFallback' => 'cze', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'da' => array('language' => 'Danish', 'locale' => 'dan', 'localeFallback' => 'dan', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'de' => array('language' => 'German (Standard)', 'locale' => 'deu', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'de-at' => array('language' => 'German (Austria)', 'locale' => 'de_at', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'de-ch' => array('language' => 'German (Swiss)', 'locale' => 'de_ch', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'de-de' => array('language' => 'German (Germany)', 'locale' => 'de_de', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'de-li' => array('language' => 'German (Liechtenstein)', 'locale' => 'de_li', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'de-lu' => array('language' => 'German (Luxembourg)', 'locale' => 'de_lu', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'e' => array('language' => 'Greek', 'locale' => 'gre', 'localeFallback' => 'gre', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'el' => array('language' => 'Greek', 'locale' => 'gre', 'localeFallback' => 'gre', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'en' => array('language' => 'English', 'locale' => 'eng', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'en-au' => array('language' => 'English (Australian)', 'locale' => 'en_au', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'en-bz' => array('language' => 'English (Belize)', 'locale' => 'en_bz', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'en-ca' => array('language' => 'English (Canadian)', 'locale' => 'en_ca', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'en-gb' => array('language' => 'English (British)', 'locale' => 'en_gb', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'en-ie' => array('language' => 'English (Ireland)', 'locale' => 'en_ie', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'en-jm' => array('language' => 'English (Jamaica)', 'locale' => 'en_jm', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'en-nz' => array('language' => 'English (New Zealand)', 'locale' => 'en_nz', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'en-tt' => array('language' => 'English (Trinidad)', 'locale' => 'en_tt', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'en-us' => array('language' => 'English (United States)', 'locale' => 'en_us', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'en-za' => array('language' => 'English (South Africa)', 'locale' => 'en_za', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'es' => array('language' => 'Spanish (Spain - Traditional)', 'locale' => 'spa', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'es-ar' => array('language' => 'Spanish (Argentina)', 'locale' => 'es_ar', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'es-bo' => array('language' => 'Spanish (Bolivia)', 'locale' => 'es_bo', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'es-cl' => array('language' => 'Spanish (Chile)', 'locale' => 'es_cl', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'es-co' => array('language' => 'Spanish (Colombia)', 'locale' => 'es_co', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'es-cr' => array('language' => 'Spanish (Costa Rica)', 'locale' => 'es_cr', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'es-do' => array('language' => 'Spanish (Dominican Republic)', 'locale' => 'es_do', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'es-ec' => array('language' => 'Spanish (Ecuador)', 'locale' => 'es_ec', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'es-es' => array('language' => 'Spanish (Spain)', 'locale' => 'es_es', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'es-gt' => array('language' => 'Spanish (Guatemala)', 'locale' => 'es_gt', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'es-hn' => array('language' => 'Spanish (Honduras)', 'locale' => 'es_hn', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'es-mx' => array('language' => 'Spanish (Mexican)', 'locale' => 'es_mx', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'es-ni' => array('language' => 'Spanish (Nicaragua)', 'locale' => 'es_ni', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'es-pa' => array('language' => 'Spanish (Panama)', 'locale' => 'es_pa', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'es-pe' => array('language' => 'Spanish (Peru)', 'locale' => 'es_pe', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'es-pr' => array('language' => 'Spanish (Puerto Rico)', 'locale' => 'es_pr', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'es-py' => array('language' => 'Spanish (Paraguay)', 'locale' => 'es_py', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'es-sv' => array('language' => 'Spanish (El Salvador)', 'locale' => 'es_sv', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'es-uy' => array('language' => 'Spanish (Uruguay)', 'locale' => 'es_uy', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'es-ve' => array('language' => 'Spanish (Venezuela)', 'locale' => 'es_ve', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'et' => array('language' => 'Estonian', 'locale' => 'est', 'localeFallback' => 'est', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'eu' => array('language' => 'Basque', 'locale' => 'baq', 'localeFallback' => 'baq', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'fa' => array('language' => 'Farsi', 'locale' => 'per', 'localeFallback' => 'per', 'charset' => 'utf-8', 'direction' => 'rtl'),
+										'fi' => array('language' => 'Finnish', 'locale' => 'fin', 'localeFallback' => 'fin', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'fo' => array('language' => 'Faeroese', 'locale' => 'fao', 'localeFallback' => 'fao', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'fr' => array('language' => 'French (Standard)', 'locale' => 'fre', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'fr-be' => array('language' => 'French (Belgium)', 'locale' => 'fr_be', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'fr-ca' => array('language' => 'French (Canadian)', 'locale' => 'fr_ca', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'fr-ch' => array('language' => 'French (Swiss)', 'locale' => 'fr_ch', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'fr-fr' => array('language' => 'French (France)', 'locale' => 'fr_fr', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'fr-lu' => array('language' => 'French (Luxembourg)', 'locale' => 'fr_lu', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'ga' => array('language' => 'Irish', 'locale' => 'gle', 'localeFallback' => 'gle', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'gd' => array('language' => 'Gaelic (Scots)', 'locale' => 'gla', 'localeFallback' => 'gla', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'gd-ie' => array('language' => 'Gaelic (Irish)', 'locale' => 'gd_ie', 'localeFallback' => 'gla', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'gl' => array('language' => 'Galician', 'locale' => 'glg', 'localeFallback' => 'glg', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'he' => array('language' => 'Hebrew', 'locale' => 'heb', 'localeFallback' => 'heb', 'charset' => 'utf-8', 'direction' => 'rtl'),
+										'hi' => array('language' => 'Hindi', 'locale' => 'hin', 'localeFallback' => 'hin', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'hr' => array('language' => 'Croatian', 'locale' => 'hrv', 'localeFallback' => 'hrv', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'hu' => array('language' => 'Hungarian', 'locale' => 'hun', 'localeFallback' => 'hun', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'hy' => array('language' => 'Armenian - Armenia', 'locale' => 'hye', 'localeFallback' => 'hye', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'id' => array('language' => 'Indonesian', 'locale' => 'ind', 'localeFallback' => 'ind', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'in' => array('language' => 'Indonesian', 'locale' => 'ind', 'localeFallback' => 'ind', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'is' => array('language' => 'Icelandic', 'locale' => 'ice', 'localeFallback' => 'ice', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'it' => array('language' => 'Italian', 'locale' => 'ita', 'localeFallback' => 'ita', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'it-ch' => array('language' => 'Italian (Swiss) ', 'locale' => 'it_ch', 'localeFallback' => 'ita', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'ja' => array('language' => 'Japanese', 'locale' => 'jpn', 'localeFallback' => 'jpn', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'ko' => array('language' => 'Korean', 'locale' => 'kor', 'localeFallback' => 'kor', 'charset' => 'kr', 'direction' => 'ltr'),
+										'ko-kp' => array('language' => 'Korea (North)', 'locale' => 'ko_kp', 'localeFallback' => 'kor', 'charset' => 'kr', 'direction' => 'ltr'),
+										'ko-kr' => array('language' => 'Korea (South)', 'locale' => 'ko_kr', 'localeFallback' => 'kor', 'charset' => 'kr', 'direction' => 'ltr'),
+										'koi8-r' => array('language' => 'Russian', 'locale' => 'koi8_r', 'localeFallback' => 'rus', 'charset' => 'koi8-r', 'direction' => 'ltr'),
+										'lt' => array('language' => 'Lithuanian', 'locale' => 'lit', 'localeFallback' => 'lit', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'lv' => array('language' => 'Latvian', 'locale' => 'lav', 'localeFallback' => 'lav', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'mk' => array('language' => 'FYRO Macedonian', 'locale' => 'mk', 'localeFallback' => 'mac', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'mk-mk' => array('language' => 'Macedonian', 'locale' => 'mk_mk', 'localeFallback' => 'mac', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'ms' => array('language' => 'Malaysian', 'locale' => 'may', 'localeFallback' => 'may', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'mt' => array('language' => 'Maltese', 'locale' => 'mlt', 'localeFallback' => 'mlt', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'n' => array('language' => 'Dutch (Standard)', 'locale' => 'dut', 'localeFallback' => 'dut', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'nb' => array('language' => 'Norwegian Bokmal', 'locale' => 'nob', 'localeFallback' => 'nor', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'nl' => array('language' => 'Dutch (Standard)', 'locale' => 'dut', 'localeFallback' => 'dut', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'nl-be' => array('language' => 'Dutch (Belgium)', 'locale' => 'nl_be', 'localeFallback' => 'dut', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'nn' => array('language' => 'Norwegian Nynorsk', 'locale' => 'nno', 'localeFallback' => 'nor', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'no' => array('language' => 'Norwegian', 'locale' => 'nor', 'localeFallback' => 'nor', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'p' => array('language' => 'Polish', 'locale' => 'pol', 'localeFallback' => 'pol', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'pl' => array('language' => 'Polish', 'locale' => 'pol', 'localeFallback' => 'pol', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'pt' => array('language' => 'Portuguese (Portugal)', 'locale' => 'por', 'localeFallback' => 'por', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'pt-br' => array('language' => 'Portuguese (Brazil)', 'locale' => 'pt_br', 'localeFallback' => 'por', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'rm' => array('language' => 'Rhaeto-Romanic', 'locale' => 'roh', 'localeFallback' => 'roh', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'ro' => array('language' => 'Romanian', 'locale' => 'rum', 'localeFallback' => 'rum', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'ro-mo' => array('language' => 'Romanian (Moldavia)', 'locale' => 'ro_mo', 'localeFallback' => 'rum', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'ru' => array('language' => 'Russian', 'locale' => 'rus', 'localeFallback' => 'rus', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'ru-mo' => array('language' => 'Russian (Moldavia)', 'locale' => 'ru_mo', 'localeFallback' => 'rus', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'sb' => array('language' => 'Sorbian', 'locale' => 'wen', 'localeFallback' => 'wen', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'sk' => array('language' => 'Slovak', 'locale' => 'slo', 'localeFallback' => 'slo', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'sl' => array('language' => 'Slovenian', 'locale' => 'slv', 'localeFallback' => 'slv', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'sq' => array('language' => 'Albanian', 'locale' => 'alb', 'localeFallback' => 'alb', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'sr' => array('language' => 'Serbian', 'locale' => 'scc', 'localeFallback' => 'scc', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'sv' => array('language' => 'Swedish', 'locale' => 'swe', 'localeFallback' => 'swe', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'sv-fi' => array('language' => 'Swedish (Finland)', 'locale' => 'sv_fi', 'localeFallback' => 'swe', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'sx' => array('language' => 'Sutu', 'locale' => 'sx', 'localeFallback' => 'sx', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'sz' => array('language' => 'Sami (Lappish)', 'locale' => 'smi', 'localeFallback' => 'smi', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'th' => array('language' => 'Thai', 'locale' => 'tha', 'localeFallback' => 'tha', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'tn' => array('language' => 'Tswana', 'locale' => 'tsn', 'localeFallback' => 'tsn', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'tr' => array('language' => 'Turkish', 'locale' => 'tur', 'localeFallback' => 'tur', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'ts' => array('language' => 'Tsonga', 'locale' => 'tso', 'localeFallback' => 'tso', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'uk' => array('language' => 'Ukrainian', 'locale' => 'ukr', 'localeFallback' => 'ukr', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'ur' => array('language' => 'Urdu', 'locale' => 'urd', 'localeFallback' => 'urd', 'charset' => 'utf-8', 'direction' => 'rtl'),
+										've' => array('language' => 'Venda', 'locale' => 'ven', 'localeFallback' => 'ven', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'vi' => array('language' => 'Vietnamese', 'locale' => 'vie', 'localeFallback' => 'vie', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'cy' => array('language' => 'Welsh', 'locale' => 'cym', 'localeFallback' => 'cym', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'xh' => array('language' => 'Xhosa', 'locale' => 'xho', 'localeFallback' => 'xho', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'yi' => array('language' => 'Yiddish', 'locale' => 'yid', 'localeFallback' => 'yid', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'zh' => array('language' => 'Chinese', 'locale' => 'chi', 'localeFallback' => 'chi', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'zh-cn' => array('language' => 'Chinese (PRC)', 'locale' => 'zh_cn', 'localeFallback' => 'chi', 'charset' => 'GB2312', 'direction' => 'ltr'),
+										'zh-hk' => array('language' => 'Chinese (Hong Kong)', 'locale' => 'zh_hk', 'localeFallback' => 'chi', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'zh-sg' => array('language' => 'Chinese (Singapore)', 'locale' => 'zh_sg', 'localeFallback' => 'chi', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'zh-tw' => array('language' => 'Chinese (Taiwan)', 'locale' => 'zh_tw', 'localeFallback' => 'chi', 'charset' => 'utf-8', 'direction' => 'ltr'),
+										'zu' => array('language' => 'Zulu', 'locale' => 'zul', 'localeFallback' => 'zul', 'charset' => 'utf-8', 'direction' => 'ltr'));
+
+/**
+ * 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 L10n::__autoLanguage(); if this fails
+ * the method will get the settings from L10n::__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 = $language = 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'];
+			$this->direction = $this->__l10nCatalog[$langKey]['direction'];
+		} 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);
+		}
+
+		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 = preg_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
+ * @access public
+ */
+	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];
+			} else if (isset($this->__l10nMap[$language]) && isset($this->__l10nCatalog[$this->__l10nMap[$language]])) {
+				return $this->__l10nCatalog[$this->__l10nMap[$language]];
+			}
+			return false;
+		}
+		return $this->__l10nCatalog;
+	}
+}

Added: trunk/src/Web/cake/libs/log/file_log.php
===================================================================
--- trunk/src/Web/cake/libs/log/file_log.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/log/file_log.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,77 @@
+<?php
+/**
+ * File Storage stream for Logging
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.log
+ * @since         CakePHP(tm) v 1.3
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+if (!class_exists('File')) {
+	require LIBS . 'file.php';
+}
+/**
+ * File Storage stream for Logging.  Writes logs to different files
+ * based on the type of log it is.
+ *
+ * @package cake
+ * @subpackage cake.cake.libs.log
+ */
+class FileLog {
+
+/**
+ * Path to save log files on.
+ *
+ * @var string
+ */
+	var $_path = null;
+
+/**
+ * Constructs a new File Logger.
+ * 
+ * Options
+ *
+ * - `path` the path to save logs on.
+ *
+ * @param array $options Options for the FileLog, see above.
+ * @return void
+ */
+	function FileLog($options = array()) {
+		$options += array('path' => LOGS);
+		$this->_path = $options['path'];
+	}
+
+/**
+ * Implements writing to log files.
+ *
+ * @param string $type The type of log you are making.
+ * @param string $message The message you want to log.
+ * @return boolean success of write.
+ */
+	function write($type, $message) {
+		$debugTypes = array('notice', 'info', 'debug');
+
+		if ($type == 'error' || $type == 'warning') {
+			$filename = $this->_path  . 'error.log';
+		} elseif (in_array($type, $debugTypes)) {
+			$filename = $this->_path . 'debug.log';
+		} else {
+			$filename = $this->_path . $type . '.log';
+		}
+		$output = date('Y-m-d H:i:s') . ' ' . ucfirst($type) . ': ' . $message . "\n";
+		$log = new File($filename, true);
+		if ($log->writable()) {
+			return $log->append($output);
+		}
+	}
+}

Added: trunk/src/Web/cake/libs/magic_db.php
===================================================================
--- trunk/src/Web/cake/libs/magic_db.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/magic_db.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,302 @@
+<?php
+/**
+ * MagicDb parser and file analyzer
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+if (!class_exists('Object')) {
+	require LIBS . 'object.php';
+}
+if (!class_exists('File')) {
+	require LIBS . 'file.php';
+}
+
+/**
+ * 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;
+		}
+	}
+}

Added: trunk/src/Web/cake/libs/model/app_model.php
===================================================================
--- trunk/src/Web/cake/libs/model/app_model.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/model/app_model.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,35 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 {
+}

Added: trunk/src/Web/cake/libs/model/behaviors/acl.php
===================================================================
--- trunk/src/Web/cake/libs/model/behaviors/acl.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/model/behaviors/acl.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,123 @@
+<?php
+/**
+ * ACL behavior class.
+ *
+ * Enables objects to easily tie into an ACL system
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP :  Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://cakephp.org CakePHP Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.behaviors
+ * @since         CakePHP v 1.2.0.4487
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * ACL behavior
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.behaviors
+ * @link http://book.cakephp.org/view/1320/ACL
+ */
+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);
+		$this->settings[$model->name]['type'] = strtolower($this->settings[$model->name]['type']);
+
+		$type = $this->__typeMaps[$this->settings[$model->name]['type']];
+		if (!class_exists('AclNode')) {
+			require LIBS . 'model' . DS . 'db_acl.php';
+		}
+		if (PHP5) {
+			$model->{$type} = ClassRegistry::init($type);
+		} else {
+			$model->{$type} =& ClassRegistry::init($type);
+		}
+		if (!method_exists($model, 'parentNode')) {
+			trigger_error(sprintf(__('Callback parentNode() not defined in %s', true), $model->alias), E_USER_WARNING);
+		}
+	}
+
+/**
+ * Retrieves the Aro/Aco node for this model
+ *
+ * @param mixed $ref
+ * @return array
+ * @access public
+ * @link http://book.cakephp.org/view/1322/node
+ */
+	function node(&$model, $ref = null) {
+		$type = $this->__typeMaps[$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) {
+		$type = $this->__typeMaps[$this->settings[$model->name]['type']];
+		$parent = $model->parentNode();
+		if (!empty($parent)) {
+			$parent = $this->node($model, $parent);
+		}
+		$data = array(
+			'parent_id' => isset($parent[0][$type]['id']) ? $parent[0][$type]['id'] : null,
+			'model' => $model->name,
+			'foreign_key' => $model->id
+		);
+		if (!$created) {
+			$node = $this->node($model);
+			$data['id'] = isset($node[0][$type]['id']) ? $node[0][$type]['id'] : null;
+		}
+		$model->{$type}->create();
+		$model->{$type}->save($data);
+	}
+
+/**
+ * Destroys the ARO/ACO node bound to the deleted record
+ *
+ * @return void
+ * @access public
+ */
+	function afterDelete(&$model) {
+		$type = $this->__typeMaps[$this->settings[$model->name]['type']];
+		$node = Set::extract($this->node($model), "0.{$type}.id");
+		if (!empty($node)) {
+			$model->{$type}->delete($node);
+		}
+	}
+}

Added: trunk/src/Web/cake/libs/model/behaviors/containable.php
===================================================================
--- trunk/src/Web/cake/libs/model/behaviors/containable.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/model/behaviors/containable.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,433 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs
+ * @since         CakePHP(tm) v 1.2.0.5669
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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
+ * @link http://book.cakephp.org/view/1323/Containable
+ */
+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) {
+					if (!empty($instance->__backAssociation[$relation])) {
+						$backupBindings[$relation] = $instance->__backAssociation[$relation];
+					} else {
+						$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;
+						}
+						$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] = $backupBindings[$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'];
+		foreach (array('hasOne', 'belongsTo') as $type) {
+			if (!empty($Model->{$type})) {
+				foreach ($Model->{$type} as $assoc => $data) {
+					if ($Model->useDbConfig == $Model->{$assoc}->useDbConfig && !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);
+					if ($Model->useDbConfig == $Model->{$modelName}->useDbConfig) {
+						$field = $modelName . '.' . (
+							($field === '--primaryKey--') ? $Model->$modelName->primaryKey : $field
+						);
+					} else {
+						$field = null;
+					}
+				}
+				if ($field !== null) {
+					$query['fields'][] = $field;
+				}
+			}
+		}
+		$query['fields'] = array_unique($query['fields']);
+		return $query;
+	}
+
+/**
+ * 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
+ * @link http://book.cakephp.org/view/1323/Containable#Using-Containable-1324
+ */
+	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(implode('.', $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] = is_array($val) ? $val : array($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;
+	}
+}

Added: trunk/src/Web/cake/libs/model/behaviors/translate.php
===================================================================
--- trunk/src/Web/cake/libs/model/behaviors/translate.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/model/behaviors/translate.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,609 @@
+<?php
+/**
+ * Translate behavior
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.behaviors
+ * @since         CakePHP(tm) v 1.2.0.4525
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Translate behavior
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.behaviors
+ * @link http://book.cakephp.org/view/1328/Translate
+ */
+class TranslateBehavior extends ModelBehavior {
+
+/**
+ * Used for runtime configuration of model
+ * 
+ * @var array
+ */
+	var $runtime = array();
+
+/**
+ * Stores the joinTable object for generating joins.
+ *
+ * @var object
+ */
+	var $_joinTable;
+
+/**
+ * Stores the runtime model for generating joins.
+ *
+ * @var Model
+ */
+	var $_runtimeModel;
+
+/**
+ * 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 Model $model Model the behavior is being attached to.
+ * @param array $config Array of configuration information.
+ * @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);
+	}
+
+/**
+ * Cleanup Callback unbinds bound translations and deletes setting information.
+ *
+ * @param Model $model Model being detached.
+ * @return void
+ * @access public
+ */
+	function cleanup(&$model) {
+		$this->unbindTranslation($model);
+		unset($this->settings[$model->alias]);
+		unset($this->runtime[$model->alias]);
+	}
+
+/**
+ * beforeFind Callback
+ *
+ * @param Model $model Model find is being run on.
+ * @param array $query Array of Query parameters.
+ * @return array Modified query
+ * @access public
+ */
+	function beforeFind(&$model, $query) {
+		$locale = $this->_getLocale($model);
+		if (empty($locale)) {
+			return $query;
+		}
+		$db =& ConnectionManager::getDataSource($model->useDbConfig);
+		$RuntimeModel =& $this->translateModel($model);
+
+		if (!empty($RuntimeModel->tablePrefix)) {
+			$tablePrefix = $RuntimeModel->tablePrefix;
+		} else {
+			$tablePrefix = $db->config['prefix'];
+		}
+		$joinTable = new StdClass();
+		$joinTable->tablePrefix = $tablePrefix;
+		$joinTable->table = $RuntimeModel->table;
+
+		$this->_joinTable = $joinTable;
+		$this->_runtimeModel = $RuntimeModel;
+
+		if (is_string($query['fields']) && 'COUNT(*) AS ' . $db->name('count') == $query['fields']) {
+			$query['fields'] = 'COUNT(DISTINCT('.$db->name($model->alias . '.' . $model->primaryKey) . ')) ' . $db->alias . 'count';
+
+			$query['joins'][] = array(
+				'type' => 'INNER',
+				'alias' => $RuntimeModel->alias,
+				'table' => $joinTable,
+				'conditions' => array(
+					$model->alias . '.' . $model->primaryKey => $db->identifier($RuntimeModel->alias.'.foreign_key'),
+					$RuntimeModel->alias.'.model' => $model->name,
+					$RuntimeModel->alias.'.locale' => $locale
+				)
+			);
+			$conditionFields = $this->_checkConditions($model, $query);
+			foreach ($conditionFields as $field) {
+				$query = $this->_addJoin($model, $query, $field, $locale, false);
+			}
+			unset($this->_joinTable, $this->_runtimeModel);
+			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]);
+					}
+				}
+				$query = $this->_addJoin($model, $query, $field, $locale, true);
+			}
+		}
+		$this->runtime[$model->alias]['beforeFind'] = $addFields;
+		unset($this->_joinTable, $this->_runtimeModel);
+		return $query;
+	}
+
+/**
+ * Check a query's conditions for translated fields.
+ * Return an array of translated fields found in the conditions.
+ *
+ * @param Model $model The model being read.
+ * @param array $query The query array.
+ * @return array The list of translated fields that are in the conditions.
+ */
+	function _checkConditions(&$model, $query) {
+		$conditionFields = array();
+		if (empty($query['conditions']) || (!empty($query['conditions']) && !is_array($query['conditions'])) ) {
+			return $conditionFields;
+		}
+		foreach ($query['conditions'] as $col => $val) {
+			foreach ($this->settings[$model->alias] as $field => $assoc) {
+				if (is_numeric($field)) {
+					$field = $assoc;
+				}
+				if (strpos($col, $field) !== false) {
+					$conditionFields[] = $field;
+				}
+			}
+		}
+		return $conditionFields;
+	}
+
+/**
+ * Appends a join for translated fields and possibly a field.
+ *
+ * @param Model $model The model being worked on.
+ * @param object $joinTable The jointable object.
+ * @param array $query The query array to append a join to.
+ * @param string $field The field name being joined.
+ * @param mixed $locale The locale(s) having joins added.
+ * @param boolean $addField Whether or not to add a field.
+ * @return array The modfied query
+ */
+	function _addJoin(&$model, $query, $field, $locale, $addField = false) {
+		$db =& ConnectionManager::getDataSource($model->useDbConfig);
+
+		$RuntimeModel = $this->_runtimeModel;
+		$joinTable = $this->_joinTable;
+
+		if (is_array($locale)) {
+			foreach ($locale as $_locale) {
+				if ($addField) {
+					$query['fields'][] = 'I18n__'.$field.'__'.$_locale.'.content';
+				}
+				$query['joins'][] = array(
+					'type' => 'LEFT',
+					'alias' => 'I18n__'.$field.'__'.$_locale,
+					'table' => $joinTable,
+					'conditions' => array(
+						$model->alias . '.' . $model->primaryKey => $db->identifier("I18n__{$field}__{$_locale}.foreign_key"),
+						'I18n__'.$field.'__'.$_locale.'.model' => $model->name,
+						'I18n__'.$field.'__'.$_locale.'.'.$RuntimeModel->displayField => $field,
+						'I18n__'.$field.'__'.$_locale.'.locale' => $_locale
+					)
+				);
+			}
+		} else {
+			if ($addField) {
+				$query['fields'][] = 'I18n__'.$field.'.content';
+			}
+			$query['joins'][] = array(
+				'type' => 'INNER',
+				'alias' => 'I18n__'.$field,
+				'table' => $joinTable,
+				'conditions' => array(
+					$model->alias . '.' . $model->primaryKey => $db->identifier("I18n__{$field}.foreign_key"),
+					'I18n__'.$field.'.model' => $model->name,
+					'I18n__'.$field.'.'.$RuntimeModel->displayField => $field,
+					'I18n__'.$field.'.locale' => $locale
+				)
+			);
+		}
+		return $query;
+	}
+
+/**
+ * afterFind Callback
+ *
+ * @param Model $model Model find was run on
+ * @param array $results Array of model results.
+ * @param boolean $primary Did the find originate on $model.
+ * @return array Modified results
+ * @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
+ *
+ * @param Model $model Model invalidFields was called on.
+ * @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 Model $model Model the callback is called on
+ * @param boolean $created Whether or not the save created a record.
+ * @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
+ *
+ * @param Model $model Model the callback was run on.
+ * @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
+ *
+ * @param Model $model Model the locale needs to be set/get on.
+ * @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.
+ *
+ * If the model has a translateModel property set, this will be used as the class
+ * name to find/use.  If no translateModel property is found 'I18nModel' will be used.
+ *
+ * @param Model $model Model to get a translatemodel for.
+ * @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);
+		} elseif (empty($model->translateTable) && empty($model->translateModel)) {
+			$this->runtime[$model->alias]['model']->setSource('i18n');
+		}
+		$model =& $this->runtime[$model->alias]['model'];
+		return $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 $model instance of model
+ * @param mixed $fields 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) && empty($this->settings[$model->alias])) {
+			return false;
+		}
+		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';
+	}
+}

Added: trunk/src/Web/cake/libs/model/behaviors/tree.php
===================================================================
--- trunk/src/Web/cake/libs/model/behaviors/tree.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/model/behaviors/tree.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,980 @@
+<?php
+/**
+ * Tree behavior class.
+ *
+ * Enables a model object to act as a node-based tree.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP :  Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://cakephp.org CakePHP Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.behaviors
+ * @since         CakePHP v 1.2.0.4487
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Tree Behavior.
+ *
+ * Enables a model object to act as a node-based tree. Using Modified Preorder Tree Traversal
+ *
+ * @see http://en.wikipedia.org/wiki/Tree_traversal
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.behaviors
+ * @link http://book.cakephp.org/view/1339/Tree
+ */
+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 instance of model
+ * @param array $config array of configuration settings.
+ * @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 Model instance.
+ * @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 Model instance
+ * @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 Model instance
+ * @return boolean true to continue, false to abort the save
+ * @access public
+ */
+	function beforeSave(&$Model) {
+		extract($this->settings[$Model->alias]);
+
+		$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 {
+				$values = $Model->find('first', array(
+					'conditions' => array($scope,$Model->escapeField() => $Model->id),
+					'fields' => array($Model->primaryKey, $parent, $left, $right ), 'recursive' => $recursive)
+				);
+
+				if ($values === false) {
+					return false;
+				}
+				list($node) = array_values($values);
+
+				$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 Model instance
+ * @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
+ * @link http://book.cakephp.org/view/1347/Counting-children
+ */
+	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 ($Model->id === $id && 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 Model instance
+ * @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
+ * @link http://book.cakephp.org/view/1346/Children
+ */
+	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((array)$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 Model instance
+ * @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
+ * @link http://book.cakephp.org/view/1348/generatetreelist
+ */
+	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 Model instance
+ * @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
+ * @link http://book.cakephp.org/view/1349/getparentnode
+ */
+	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->find('first', array('conditions' => array($Model->primaryKey => $id), 'fields' => array($parent), 'recursive' => -1));
+
+		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 Model instance
+ * @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
+ * @link http://book.cakephp.org/view/1350/getpath
+ */
+	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 Model instance
+ * @param mixed $id The ID of the record to move
+ * @param int|bool $number how many places to move the node or true to move to last position
+ * @return boolean true on success, false on failure
+ * @access public
+ * @link http://book.cakephp.org/view/1352/moveDown
+ */
+	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 Model instance
+ * @param mixed $id The ID of the record to move
+ * @param int|bool $number how many places to move the node, or true to move to first position
+ * @return boolean true on success, false on failure
+ * @access public
+ * @link http://book.cakephp.org/view/1353/moveUp
+ */
+	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 Model instance
+ * @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
+ * @link http://book.cakephp.org/view/1628/Recover
+ */
+	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->name,
+				'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) {
+				$lft = $count++;
+				$rght = $count++;
+				$Model->create(false);
+				$Model->id = $array[$Model->alias][$Model->primaryKey];
+				$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(false);
+				$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.
+ *
+ * Options:
+ *
+ * - 'id' id of record to use as top node for reordering
+ * - 'field' Which field to use in reordeing defaults to displayField
+ * - 'order' Direction to order either DESC or ASC (defaults to ASC)
+ * - 'verify' Whether or not to verify the tree before reorder. defaults to true.
+ *
+ * @param AppModel $Model Model instance
+ * @param array $options array of options to use in reordering.
+ * @return boolean true on success, false on failure
+ * @link http://book.cakephp.org/view/1355/reorder
+ * @link http://book.cakephp.org/view/1629/Reorder
+ */
+	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);
+
+		$cacheQueries = $Model->cacheQueries;
+		$Model->cacheQueries = false;
+		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'));
+				}
+			}
+		}
+		$Model->cacheQueries = $cacheQueries;
+		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 Model instance
+ * @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
+ * @link http://book.cakephp.org/view/1354/removeFromTree
+ */
+	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($Model->escapeField($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 Model instance
+ * @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
+ * @link http://book.cakephp.org/view/1630/Verify
+ */
+	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->name,
+			'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 Model instance
+ * @param mixed $parentId
+ * @param boolean $created
+ * @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 {
+			$values = $Model->find('first', array(
+				'conditions' => array($scope, $Model->escapeField() => $parentId),
+				'fields' => array($Model->primaryKey, $left, $right),
+				'recursive' => $recursive
+			));
+
+			if ($values === false) {
+				return false;
+			}
+			$parentNode = array_values($values);
+
+			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
+ * @param int $recursive
+ * @param boolean $created
+ * @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;
+			}
+		}
+		$name = $Model->alias . '.' . $right;
+		list($edge) = array_values($Model->find('first', array(
+			'conditions' => $scope,
+			'fields' => $db->calculate($Model, 'max', array($name, $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);
+		$name = $Model->alias . '.' . $left;
+		list($edge) = array_values($Model->find('first', array(
+			'conditions' => $scope,
+			'fields' => $db->calculate($Model, 'min', array($name, $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 boolean $created
+ * @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;
+	}
+}

Added: trunk/src/Web/cake/libs/model/cake_schema.php
===================================================================
--- trunk/src/Web/cake/libs/model/cake_schema.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/model/cake_schema.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,714 @@
+<?php
+/**
+ * Schema database management for CakePHP.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model
+ * @since         CakePHP(tm) v 1.2.0.5550
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Core', array('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';
+
+/**
+ * plugin name.
+ *
+ * @var string
+ */
+	var $plugin = null;
+
+/**
+ * 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 (!empty($options['plugin'])) {
+			$this->plugin = $options['plugin'];
+		}
+
+		if (strtolower($this->name) === 'cake') {
+			$this->name = Inflector::camelize(Inflector::slug(Configure::read('App.dir')));
+		}
+
+		if (empty($options['path'])) {
+			if (is_dir(CONFIGS . 'schema')) {
+				$this->path = CONFIGS . 'schema';
+			} else {
+				$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('plugin', '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;
+		} elseif (!empty($this->plugin)) {
+			$this->path = App::pluginPath($this->plugin) . 'config' . DS . 'schema';
+		}
+	}
+
+/**
+ * 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;
+		}
+		$false = false;
+		return $false;
+	}
+
+/**
+ * Reads database and creates schema tables
+ *
+ * Options
+ *
+ * - 'connection' - the db connection to use
+ * - 'name' - name of the schema
+ * - 'models' - a list of models to use, or false to ignore models
+ *
+ * @param array $options schema object properties
+ * @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');
+		if (isset($this->plugin)) {
+			App::import('Model', Inflector::camelize($this->plugin) . 'AppModel');
+		}
+
+		$tables = array();
+		$currentTables = $db->listSources();
+
+		$prefix = null;
+		if (isset($db->config['prefix'])) {
+			$prefix = $db->config['prefix'];
+		}
+
+		if (!is_array($models) && $models !== false) {
+			if (isset($this->plugin)) {
+				$models = App::objects('model', App::pluginPath($this->plugin) . 'models' . DS, false);
+			} else {
+				$models = App::objects('model');
+			}
+		}
+
+		if (is_array($models)) {
+			foreach ($models as $model) {
+				$importModel = $model;
+				if (isset($this->plugin)) {
+					$importModel = $this->plugin . '.' . $model;
+				}
+				if (!App::import('Model', $importModel)) {
+					continue;
+				}
+				$vars = get_class_vars($model);
+				if (empty($vars['useDbConfig']) || $vars['useDbConfig'] != $connection) {
+					continue;
+				}
+
+				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) {
+					$fulltable = $table = $db->fullTableName($Object, false);
+					if ($prefix && strpos($table, $prefix) !== 0) {
+						continue;
+					}
+					$table = $this->_noPrefixTable($prefix, $table);
+
+					if (in_array($fulltable, $currentTables)) {
+						$key = array_search($fulltable, $currentTables);
+						if (empty($tables[$table])) {
+							$tables[$table] = $this->__columns($Object);
+							$tables[$table]['indexes'] = $db->index($Object);
+							$tables[$table]['tableParameters'] = $db->readTableParameters($fulltable);
+							unset($currentTables[$key]);
+						}
+						if (!empty($Object->hasAndBelongsToMany)) {
+							foreach ($Object->hasAndBelongsToMany as $Assoc => $assocData) {
+								if (isset($assocData['with'])) {
+									$class = $assocData['with'];
+								}
+								if (is_object($Object->$class)) {
+									$withTable = $db->fullTableName($Object->$class, false);
+									if ($prefix && strpos($withTable, $prefix) !== 0) {
+										continue;
+									}
+									if (in_array($withTable, $currentTables)) {
+										$key = array_search($withTable, $currentTables);
+										$noPrefixWith = $this->_noPrefixTable($prefix, $withTable);
+	
+										$tables[$noPrefixWith] = $this->__columns($Object->$class);
+										$tables[$noPrefixWith]['indexes'] = $db->index($Object->$class);
+										$tables[$noPrefixWith]['tableParameters'] = $db->readTableParameters($withTable);
+										unset($currentTables[$key]);
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+
+		if (!empty($currentTables)) {
+			foreach ($currentTables as $table) {
+				if ($prefix) {
+					if (strpos($table, $prefix) !== 0) {
+						continue;
+					}
+					$table = $this->_noPrefixTable($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'
+				);
+				
+				$fulltable = $db->fullTableName($Object, false);
+				
+				if (in_array($table, $systemTables)) {
+					$tables[$Object->table] = $this->__columns($Object);
+					$tables[$Object->table]['indexes'] = $db->index($Object);
+					$tables[$Object->table]['tableParameters'] = $db->readTableParameters($fulltable);
+				} elseif ($models === false) {
+					$tables[$table] = $this->__columns($Object);
+					$tables[$table]['indexes'] = $db->index($Object);
+					$tables[$table]['tableParameters'] = $db->readTableParameters($fulltable);
+				} else {
+					$tables['missing'][$table] = $this->__columns($Object);
+					$tables['missing'][$table]['indexes'] = $db->index($Object);
+					$tables['missing'][$table]['tableParameters'] = $db->readTableParameters($fulltable);
+				}
+			}
+		}
+
+		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 .= $this->generateTable($table, $fields);
+			}
+		}
+		$out .= "}\n";
+
+		$File =& new File($path . DS . $file, true);
+		$header = '$Id';
+		$content = "<?php \n/* {$name} schema generated on: " . date('Y-m-d H:i:s') . " : ". time() . "*/\n{$out}?>";
+		$content = $File->prepare($content);
+		if ($File->write($content)) {
+			return $content;
+		}
+		return false;
+	}
+
+/**
+ * Generate the code for a table. Takes a table name and $fields array
+ * Returns a completed variable declaration to be used in schema classes
+ *
+ * @param string $table Table name you want returned.
+ * @param array $fields Array of field information to generate the table with.
+ * @return string Variable declaration for a schema class
+ */
+	function generateTable($table, $fields) {
+		$out = "\tvar \${$table} = array(\n";
+		if (is_array($fields)) {
+			$cols = array();
+			foreach ($fields as $field => $value) {
+				if ($field != 'indexes' && $field != 'tableParameters') {
+					if (is_string($value)) {
+						$type = $value;
+						$value = array('type'=> $type);
+					}
+					$col = "\t\t'{$field}' => array('type' => '" . $value['type'] . "', ";
+					unset($value['type']);
+					$col .= join(', ',  $this->__values($value));
+				} elseif ($field == 'indexes') {
+					$col = "\t\t'indexes' => array(";
+					$props = array();
+					foreach ((array)$value as $key => $index) {
+						$props[] = "'{$key}' => array(" . join(', ',  $this->__values($index)) . ")";
+					}
+					$col .= join(', ', $props);
+				} elseif ($field == 'tableParameters') {
+					//@todo add charset, collate and engine here
+					$col = "\t\t'tableParameters' => array(";
+					$props = array();
+					foreach ((array)$value as $key => $param) {
+						$props[] = "'{$key}' => '$param'";
+					}
+					$col .= join(', ', $props);
+				}
+				$col .= ")";
+				$cols[] = $col;
+			}
+			$out .= join(",\n", $cols);
+		}
+		$out .= "\n\t);\n";
+		return $out;
+	}
+
+/**
+ * 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') {
+				continue;
+			}
+			if (!array_key_exists($table, $old)) {
+				$tables[$table]['add'] = $fields;
+			} else {
+				$diff = $this->_arrayDiffAssoc($fields, $old[$table]);
+				if (!empty($diff)) {
+					$tables[$table]['add'] = $diff;
+				}
+				$diff = $this->_arrayDiffAssoc($old[$table], $fields);
+				if (!empty($diff)) {
+					$tables[$table]['drop'] = $diff;
+				}
+			}
+
+			foreach ($fields as $field => $value) {
+				if (isset($old[$table][$field])) {
+					$diff = $this->_arrayDiffAssoc($value, $old[$table][$field]);
+					if (!empty($diff) && $field !== 'indexes' && $field !== 'tableParameters') {
+						$tables[$table]['change'][$field] = array_merge($old[$table][$field], $diff);
+					}
+				}
+
+				if (isset($tables[$table]['add'][$field]) && $field !== 'indexes' && $field !== 'tableParameters') {
+					$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) {
+					if (!isset($tables[$table])) {
+						$tables[$table] = array();
+					}
+					if (isset($diff['drop'])) {
+						$tables[$table]['drop']['indexes'] = $diff['drop'];
+					}
+					if ($diff && isset($diff['add'])) {
+						$tables[$table]['add']['indexes'] = $diff['add'];
+					}
+				}
+			}
+			if (isset($old[$table]['tableParameters']) && isset($new[$table]['tableParameters'])) {
+				$diff = $this->_compareTableParameters($new[$table]['tableParameters'], $old[$table]['tableParameters']);
+				if ($diff) {
+					$tables[$table]['change']['tableParameters'] = $diff;
+				}
+			}
+		}
+		return $tables;
+	}
+
+/**
+ * Extended array_diff_assoc noticing change from/to NULL values
+ *
+ * It behaves almost the same way as array_diff_assoc except for NULL values: if
+ * one of the values is not NULL - change is detected. It is useful in situation
+ * where one value is strval('') ant other is strval(null) - in string comparing
+ * methods this results as EQUAL, while it is not.
+ *
+ * @param array $array1 Base array
+ * @param array $array2 Corresponding array checked for equality
+ * @return array Difference as array with array(keys => values) from input array
+ *     where match was not found.
+ * @access protected
+ */
+	function _arrayDiffAssoc($array1, $array2) {
+		$difference = array();
+		foreach ($array1 as $key => $value) {
+			if (!array_key_exists($key, $array2)) {
+				$difference[$key] = $value;
+				continue;
+			}
+			$correspondingValue = $array2[$key];
+			if (is_null($value) !== is_null($correspondingValue)) {
+				$difference[$key] = $value;
+				continue;
+			}
+			if (is_bool($value) !== is_bool($correspondingValue)) {
+				$difference[$key] = $value;
+				continue;
+			}
+			if (is_array($value) && is_array($correspondingValue)) {
+				continue;
+			}
+			$compare = strval($value);
+			$correspondingValue = strval($correspondingValue);
+			if ($compare === $correspondingValue) {
+				continue;
+			}
+			$difference[$key] = $value;
+		}
+		return $difference;
+	}
+
+/**
+ * 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('" . implode("', '",  $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(sprintf(__('Schema generation error: invalid column type %s does not exist in DBO', true), $value['type']), 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 files table Parameters
+ *
+ * @param array $new New indexes
+ * @param array $old Old indexes
+ * @return mixed False on failure, or an array of parameters to add & drop.
+ */
+	function _compareTableParameters($new, $old) {
+		if (!is_array($new) || !is_array($old)) {
+			return false;
+		}
+		$change = $this->_arrayDiffAssoc($new, $old);
+		return $change;
+	}
+
+/**
+ * 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 = $this->_arrayDiffAssoc($new, $old);
+		if (!empty($diff)) {
+			$add = $diff;
+		}
+
+		$diff = $this->_arrayDiffAssoc($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'));
+	}
+
+/**
+ * Trim the table prefix from the full table name, and return the prefix-less table
+ *
+ * @param string $prefix Table prefix
+ * @param string $table Full table name
+ * @return string Prefix-less table name
+ */
+	function _noPrefixTable($prefix, $table) {
+		return preg_replace('/^' . preg_quote($prefix) . '/', '', $table);
+	}
+}

Added: trunk/src/Web/cake/libs/model/connection_manager.php
===================================================================
--- trunk/src/Web/cake/libs/model/connection_manager.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/model/connection_manager.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,295 @@
+<?php
+/**
+ * Datasource connection manager
+ *
+ * Provides an interface for loading and enumerating connections defined in app/config/database.php
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model
+ * @since         CakePHP(tm) v 0.10.x.1402
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+require LIBS . 'model' . DS . 'datasources' . DS . 'datasource.php';
+include_once CONFIGS . 'database.php';
+
+/**
+ * Manages loaded instances of DataSource objects
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model
+ */
+class ConnectionManager extends Object {
+
+/**
+ * Holds a loaded instance of the Connections object
+ *
+ * @var DATABASE_CONFIG
+ * @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();
+			$this->_getConnectionObjects();
+		}
+	}
+
+/**
+ * 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/database.php
+ * @return object Instance
+ * @access public
+ * @static
+ */
+	function &getDataSource($name) {
+		$_this =& ConnectionManager::getInstance();
+
+		if (!empty($_this->_dataSources[$name])) {
+			$return =& $_this->_dataSources[$name];
+			return $return;
+		}
+
+		if (empty($_this->_connectionsEnum[$name])) {
+			trigger_error(sprintf(__("ConnectionManager::getDataSource - Non-existent data source %s", true), $name), E_USER_ERROR);
+			$null = null;
+			return $null;
+		}
+		$conn = $_this->_connectionsEnum[$name];
+		$class = $conn['classname'];
+
+		if ($_this->loadDataSource($name) === null) {
+			trigger_error(sprintf(__("ConnectionManager::getDataSource - Could not load class %s", true), $class), E_USER_ERROR);
+			$null = null;
+			return $null;
+		}
+		$_this->_dataSources[$name] =& new $class($_this->config->{$name});
+		$_this->_dataSources[$name]->configKeyName = $name;
+
+		$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.
+ *
+ * **Warning** this method may cause fatal errors in PHP4.
+ *
+ * @param object $source DataSource object
+ * @return string Datasource name, or null if source is not present
+ *    in the ConnectionManager.
+ * @access public
+ * @static
+ */
+	function getSourceName(&$source) {
+		$_this =& ConnectionManager::getInstance();
+		foreach ($_this->_dataSources as $name => $ds) {
+			if ($ds == $source) {
+				return $name;
+			}
+		}
+		return '';
+	}
+
+/**
+ * Loads the DataSource class for the given connection name
+ *
+ * @param mixed $connName A string name of the connection, as defined in app/config/database.php,
+ *                        or an array containing the filename (without extension) and class name of the object,
+ *                        to be found in app/models/datasources/ or cake/libs/model/datasources/.
+ * @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 {
+			$conn = $_this->_connectionsEnum[$connName];
+		}
+
+		if (class_exists($conn['classname'])) {
+			return false;
+		}
+
+		if (!empty($conn['parent'])) {
+			$_this->loadDataSource($conn['parent']);
+		}
+
+		$conn = array_merge(array('plugin' => null, 'classname' => null, 'parent' => null), $conn);
+		$class = "{$conn['plugin']}.{$conn['classname']}";
+
+		if (!App::import('Datasource', $class, !is_null($conn['plugin']))) {
+			trigger_error(sprintf(__('ConnectionManager::loadDataSource - Unable to import DataSource class %s', true), $class), E_USER_ERROR);
+			return null;
+		}
+		return true;
+	}
+
+/**
+ * Return a list of 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();
+
+		return $_this->_connectionsEnum;
+	}
+
+/**
+ * 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->__connectionData($config);
+		$return =& $_this->getDataSource($name);
+		return $return;
+	}
+
+/**
+ * Gets a list of class and file names associated with the user-defined DataSource connections
+ *
+ * @return void
+ * @access protected
+ * @static
+ */
+	function _getConnectionObjects() {
+		$connections = get_object_vars($this->config);
+
+		if ($connections != null) {
+			foreach ($connections as $name => $config) {
+				$this->_connectionsEnum[$name] = $this->__connectionData($config);
+			}
+		} else {
+			$this->cakeError('missingConnection', array(array('code' => 500, 'className' => 'ConnectionManager')));
+		}
+	}
+
+/**
+ * Returns the file, class name, and parent for the given driver.
+ *
+ * @return array An indexed array with: filename, classname, plugin and parent
+ * @access private
+ */
+	function __connectionData($config) {
+		if (!isset($config['datasource'])) {
+			$config['datasource'] = 'dbo';
+		}
+		$filename = $classname = $parent = $plugin = null;
+
+		if (!empty($config['driver'])) {
+			$parent = $this->__connectionData(array('datasource' => $config['datasource']));
+			$parentSource = preg_replace('/_source$/', '', $parent['filename']);
+
+			list($plugin, $classname) = pluginSplit($config['driver']);
+			if ($plugin) {
+				$source = Inflector::underscore($classname);
+			} else {
+				$source = $parentSource . '_' . $config['driver'];
+				$classname = Inflector::camelize(strtolower($source));
+			}
+			$filename = $parentSource . DS . $source;
+		} else {
+			list($plugin, $classname) = pluginSplit($config['datasource']);
+			if ($plugin) {
+				$filename = Inflector::underscore($classname);
+			} else {
+				$filename = Inflector::underscore($config['datasource']);
+			}
+			if (substr($filename, -7) != '_source') {
+				$filename .= '_source';
+			}
+			$classname = Inflector::camelize(strtolower($filename));
+		}
+		return compact('filename', 'classname', 'parent', 'plugin');
+	}
+
+/**
+ * Destructor.
+ *
+ * @access private
+ */
+	function __destruct() {
+		if (Configure::read('Session.save') == 'database' && function_exists('session_write_close')) {
+			session_write_close();
+		}
+	}
+}

Added: trunk/src/Web/cake/libs/model/datasources/datasource.php
===================================================================
--- trunk/src/Web/cake/libs/model/datasources/datasource.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/model/datasources/datasource.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,599 @@
+<?php
+/**
+ * DataSource base class
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources
+ * @since         CakePHP(tm) v 0.10.5.1790
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * DataSource base class
+ *
+ * @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
+ * @access public
+ */
+	var $startQuote = null;
+
+/**
+ * The ending character that this DataSource uses for quoted identifiers.
+ *
+ * @var string
+ * @access public
+ */
+	var $endQuote = null;
+
+/**
+ * Result
+ *
+ * @var array
+ * @access protected
+ */
+	var $_result = null;
+
+/**
+ * Queries count.
+ *
+ * @var int
+ * @access protected
+ */
+	var $_queriesCnt = 0;
+
+/**
+ * Total duration of all queries.
+ *
+ * @var unknown_type
+ * @access protected
+ */
+	var $_queriesTime = null;
+
+/**
+ * Log of queries executed by this DataSource
+ *
+ * @var unknown_type
+ * @access protected
+ */
+	var $_queriesLog = array();
+
+/**
+ * Maximum number of items in query log
+ *
+ * This is to prevent query log taking over too much memory.
+ *
+ * @var int Maximum number of queries in the queries log.
+ * @access protected
+ */
+	var $_queriesLogMax = 200;
+
+/**
+ * Caches serialzed results of executed queries
+ *
+ * @var array Maximum number of queries in the queries log.
+ * @access protected
+ */
+	var $_queryCache = array();
+
+/**
+ * The default configuration of a specific DataSource
+ *
+ * @var array
+ * @access protected
+ */
+	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
+ * @access public
+ */
+	var $cacheSources = true;
+
+/**
+ * Constructor.
+ *
+ * @param array $config Array of configuration information for the datasource.
+ * @return void.
+ */
+	function __construct($config = array()) {
+		parent::__construct();
+		$this->setConfig($config);
+	}
+
+/**
+ * Caches/returns cached results for child instances
+ *
+ * @param mixed $data
+ * @return array Array of sources available in this datasource.
+ * @access public
+ */
+	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.
+ *
+ * @param boolean $reset Whether or not the source list should be reset.
+ * @return array Array of sources available in this datasource
+ * @access public
+ */
+	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 array Array of Metadata for the $model
+ * @access public
+ */
+	function describe(&$model) {
+		if ($this->cacheSources === false) {
+			return null;
+		}
+		$table = $model->tablePrefix . $model->table;
+
+		if (isset($this->__descriptions[$table])) {
+			return $this->__descriptions[$table];
+		}
+		$cache = $this->__cacheDescription($table);
+
+		if ($cache !== null) {
+			$this->__descriptions[$table] =& $cache;
+			return $cache;
+		}
+		return null;
+	}
+
+/**
+ * Begin a transaction
+ *
+ * @return boolean Returns true if a transaction is not in progress
+ * @access public
+ */
+	function begin(&$model) {
+		return !$this->_transactionStarted;
+	}
+
+/**
+ * Commit a transaction
+ *
+ * @return boolean Returns true if a transaction is in progress
+ * @access public
+ */
+	function commit(&$model) {
+		return $this->_transactionStarted;
+	}
+
+/**
+ * Rollback a transaction
+ *
+ * @return boolean Returns true if a transaction is in progress
+ * @access public
+ */
+	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")
+ * @access public
+ */
+	function column($real) {
+		return false;
+	}
+
+/**
+ * Used to create new records. The "C" CRUD.
+ *
+ * To-be-overridden in subclasses.
+ *
+ * @param Model $model The Model to be created.
+ * @param array $fields An Array of fields to be saved.
+ * @param array $values An Array of values to save.
+ * @return boolean success
+ * @access public
+ */
+	function create(&$model, $fields = null, $values = null) {
+		return false;
+	}
+
+/**
+ * Used to read records from the Datasource. The "R" in CRUD
+ *
+ * To-be-overridden in subclasses.
+ *
+ * @param Model $model The model being read.
+ * @param array $queryData An array of query data used to find the data you want
+ * @return mixed
+ * @access public
+ */
+	function read(&$model, $queryData = array()) {
+		return false;
+	}
+
+/**
+ * Update a record(s) in the datasource.
+ *
+ * To-be-overridden in subclasses.
+ *
+ * @param Model $model Instance of the model class being updated
+ * @param array $fields Array of fields to be updated
+ * @param array $values Array of values to be update $fields to.
+ * @return boolean Success
+ * @access public
+ */
+	function update(&$model, $fields = null, $values = null) {
+		return false;
+	}
+
+/**
+ * Delete a record(s) in the datasource.
+ *
+ * To-be-overridden in subclasses.
+ *
+ * @param Model $model The model class having record(s) deleted
+ * @param mixed $conditions The conditions to use for deleting.
+ * @access public
+ */
+	function delete(&$model, $conditions = null) {
+		return false;
+	}
+
+/**
+ * Returns the ID generated from the previous INSERT operation.
+ *
+ * @param unknown_type $source
+ * @return mixed Last ID key generated in previous INSERT
+ * @access public
+ */
+	function lastInsertId($source = null) {
+		return false;
+	}
+
+/**
+ * Returns the number of rows returned by last operation.
+ *
+ * @param unknown_type $source
+ * @return integer Number of rows returned by last operation
+ * @access public
+ */
+	function lastNumRows($source = null) {
+		return false;
+	}
+
+/**
+ * Returns the number of rows affected by last query.
+ *
+ * @param unknown_type $source
+ * @return integer Number of rows affected by last query.
+ * @access public
+ */
+	function lastAffected($source = null) {
+		return false;
+	}
+
+/**
+ * Check whether the conditions for the Datasource being available
+ * are satisfied.  Often used from connect() to check for support
+ * before establishing a connection.
+ *
+ * @return boolean Whether or not the Datasources conditions for use are met.
+ * @access public
+ */
+	function enabled() {
+		return true;
+	}
+
+/**
+ * Returns true if the DataSource supports the given interface (method)
+ *
+ * @param string $interface The name of the interface (method)
+ * @return boolean True on success
+ * @access public
+ */
+	function isInterfaceSupported($interface) {
+		static $methods = false;
+		if ($methods === false) {
+			$methods = array_map('strtolower', get_class_methods($this));
+		}
+		return in_array(strtolower($interface), $methods);
+	}
+
+/**
+ * Sets the configuration for the DataSource.
+ * Merges the $config information with the _baseConfig and the existing $config property.
+ *
+ * @param array $config The configuration array
+ * @return void
+ * @access public
+ */
+	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
+ * @return mixed
+ * @access private
+ */
+	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;
+	}
+
+/**
+ * Replaces `{$__cakeID__$}` and `{$__cakeForeignKey__$}` placeholders in query data.
+ *
+ * @param string $query Query string needing replacements done.
+ * @param array $data Array of data with values that will be inserted in placeholders.
+ * @param string $association Name of association model being replaced
+ * @param unknown_type $assocData
+ * @param Model $model Instance of the model to replace $__cakeID__$
+ * @param Model $linkModel Instance of model to replace $__cakeForeignKey__$
+ * @param array $stack
+ * @return string String of query data with placeholders replaced.
+ * @access public
+ * @todo Remove and refactor $assocData, ensure uses of the method have the param removed too.
+ */
+	function insertQueryData($query, $data, $association, $assocData, &$model, &$linkModel, $stack) {
+		$keys = array('{$__cakeID__$}', '{$__cakeForeignKey__$}');
+
+		foreach ($keys as $key) {
+			$val = null;
+			$type = 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 = '';
+							}
+						}
+						$type = $model->getColumnType($model->primaryKey);
+					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'];
+										$assocModel = $model->$assocName;
+										$type = $assocModel->getColumnType($assocModel->primaryKey);
+
+										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, $type), $query);
+			}
+		}
+		return $query;
+	}
+
+/**
+ * To-be-overridden in subclasses.
+ *
+ * @param Model $model Model instance
+ * @param string $key Key name to make
+ * @return string Key name for model.
+ * @access public
+ */
+	function resolveKey(&$model, $key) {
+		return $model->alias . $key;
+	}
+
+/**
+ * Closes the current datasource.
+ *
+ * @return void
+ * @access public
+ */
+	function __destruct() {
+		if ($this->_transactionStarted) {
+			$null = null;
+			$this->rollback($null);
+		}
+		if ($this->connected) {
+			$this->close();
+		}
+	}
+}

Added: trunk/src/Web/cake/libs/model/datasources/dbo/dbo_mssql.php
===================================================================
--- trunk/src/Web/cake/libs/model/datasources/dbo/dbo_mssql.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/model/datasources/dbo/dbo_mssql.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,792 @@
+<?php
+/**
+ * MS SQL layer for DBO
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources.dbo
+ * @since         CakePHP(tm) v 0.10.5.1790
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * MS SQL layer for DBO
+ *
+ * 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'
+	);
+
+/**
+ * Define if the last query had error
+ *
+ * @var string
+ * @access private
+ */
+	var $__lastQueryHadError = false;
+/**
+ * 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/", true), 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;
+	}
+
+/**
+ * Check that MsSQL is installed/loaded
+ *
+ * @return boolean
+ */
+	function enabled() {
+		return extension_loaded('mssql');
+	}
+/**
+ * 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) {
+		$result = @mssql_query($sql, $this->connection);
+		$this->__lastQueryHadError = ($result === false);
+		return $result;
+	}
+
+/**
+ * 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;
+		}
+
+		$table = $this->fullTableName($model, false);
+		$cols = $this->fetchAll("SELECT COLUMN_NAME as Field, DATA_TYPE as Type, COL_LENGTH('" . $table . "', COLUMN_NAME) as Length, IS_NULLABLE As [Null], COLUMN_DEFAULT as [Default], COLUMNPROPERTY(OBJECT_ID('" . $table . "'), COLUMN_NAME, 'IsIdentity') as [Key], NUMERIC_SCALE as Size FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '" . $table . "'", false);
+
+		$fields = 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') ? 'primary' : false
+			);
+			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 (in_array($column, array('integer', 'float', 'binary')) && $data === '') {
+			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 &&
+			strpos($fields[0], 'COUNT(*)') === false &&
+			strpos($fields[0], 'COUNT(DISTINCT') === false
+		) {
+			$result = array();
+			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 (substr($fields[$i], -1) == '*') {
+						if (strpos($fields[$i], '.') !== false && $fields[$i] != $alias . '.*') {
+							$build = explode('.', $fields[$i]);
+							$AssociatedModel = $model->{$build[0]};
+						} else {
+							$AssociatedModel = $model;
+						}
+
+						$_fields = $this->fields($AssociatedModel, $AssociatedModel->alias, array_keys($AssociatedModel->schema()));
+						$result = array_merge($result, $_fields);
+						continue;
+					}
+
+					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}";
+				}
+				$result[] = $prepend . $fields[$i];
+			}
+			return $result;
+		} else {
+			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);
+		}
+		$primaryKey = $this->_getPrimaryKey($model);
+
+		if (array_key_exists($primaryKey, $fields)) {
+			if (empty($fields[$primaryKey])) {
+				unset($fields[$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($primaryKey, $fields) && !empty($fields[$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]);
+		}
+		if (empty($fields)) {
+			return true;
+		}
+		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() {
+		if ($this->__lastQueryHadError) {
+			$error = mssql_get_last_message();
+			if ($error && !preg_match('/contexto de la base de datos a|contesto di database|changed database|contexte de la base de don|datenbankkontext/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" . implode(",\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;
+		}
+	}
+
+/**
+ * Inserts multiple values into a table
+ *
+ * @param string $table
+ * @param string $fields
+ * @param array $values
+ * @access protected
+ */
+	function insertMulti($table, $fields, $values) {
+		$primaryKey = $this->_getPrimaryKey($table);
+		$hasPrimaryKey = $primaryKey != null && (
+			(is_array($fields) && in_array($primaryKey, $fields)
+			|| (is_string($fields) && strpos($fields, $this->startQuote . $primaryKey . $this->endQuote) !== false))
+		);
+
+		if ($hasPrimaryKey) {
+			$this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($table) . ' ON');
+		}
+		parent::insertMulti($table, $fields, $values);
+		if ($hasPrimaryKey) {
+			$this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($table) . ' OFF');
+		}
+	}
+
+/**
+ * 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));
+		if (strpos($result, 'DEFAULT NULL') !== false) {
+			$result = str_replace('DEFAULT NULL', 'NULL', $result);
+		} else if (array_keys($column) == array('type', 'name')) {
+			$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') {
+				$join[] = 'PRIMARY KEY (' . $this->name($value['column']) . ')';
+			} else if (isset($value['unique']) && $value['unique']) {
+				$out = "ALTER TABLE {$table} ADD CONSTRAINT {$name} UNIQUE";
+
+				if (is_array($value['column'])) {
+					$value['column'] = implode(', ', array_map(array(&$this, 'name'), $value['column']));
+				} else {
+					$value['column'] = $this->name($value['column']);
+				}
+				$out .= "({$value['column']});";
+				$join[] = $out;
+			}
+		}
+		return $join;
+	}
+
+/**
+ * Makes sure it will return the primary key
+ *
+ * @param mixed $model
+ * @access protected
+ * @return string
+ */
+	function _getPrimaryKey($model) {
+		if (is_object($model)) {
+			$schema = $model->schema();
+		} else {
+			$schema = $this->describe($model);
+		}
+
+		foreach ($schema as $field => $props) {
+			if (isset($props['key']) && $props['key'] == 'primary') {
+				return $field;
+			}
+		}
+		return null;
+	}
+}

Added: trunk/src/Web/cake/libs/model/datasources/dbo/dbo_mysql.php
===================================================================
--- trunk/src/Web/cake/libs/model/datasources/dbo/dbo_mysql.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/model/datasources/dbo/dbo_mysql.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,812 @@
+<?php
+/**
+ * MySQL layer for DBO
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources.dbo
+ * @since         CakePHP(tm) v 0.10.5.1790
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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'
+	);
+
+/**
+ * List of engine specific additional field parameters used on table creating
+ *
+ * @var array
+ * @access public
+ */
+	var $fieldParameters = array(
+		'charset' => array('value' => 'CHARACTER SET', 'quote' => false, 'join' => ' ', 'column' => false, 'position' => 'beforeDefault'),
+		'collate' => array('value' => 'COLLATE', 'quote' => false, 'join' => ' ', 'column' => 'Collation', 'position' => 'beforeDefault'),
+		'comment' => array('value' => 'COMMENT', 'quote' => true, 'join' => ' ', 'column' => 'Comment', 'position' => 'afterDefault')
+	);
+
+/**
+ * List of table engine specific parameters used on table creating
+ *
+ * @var array
+ * @access public
+ */
+	var $tableParameters = array(
+		'charset' => array('value' => 'DEFAULT CHARSET', 'quote' => false, 'join' => '=', 'column' => 'charset'),
+		'collate' => array('value' => 'COLLATE', 'quote' => false, 'join' => '=', 'column' => 'Collation'),
+		'engine' => array('value' => 'ENGINE', 'quote' => false, 'join' => '=', 'column' => 'Engine')
+	);
+
+/**
+ * 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')
+	);
+
+/**
+ * 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('SHOW FULL COLUMNS FROM ' . $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']];
+				}
+				foreach ($this->fieldParameters as $name => $value) {
+					if (!empty($column[0][$value['column']])) {
+						$fields[$column[0]['Field']][$name] = $column[0][$value['column']];
+					}
+				}
+				if (isset($fields[$column[0]['Field']]['collate'])) {
+					$charset = $this->getCharsetName($fields[$column[0]['Field']]['collate']);
+					if ($charset) {
+						$fields[$column[0]['Field']]['charset'] = $charset;
+					}
+				}
+			}
+		}
+		$this->__cacheDescription($this->fullTableName($model, false), $fields);
+		return $fields;
+	}
+
+/**
+ * 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 = implode(', ', $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;
+		}
+		$complexConditions = false;
+		foreach ((array)$conditions as $key => $value) {
+			if (strpos($key, $model->alias) === false) {
+				$complexConditions = true;
+				break;
+			}
+		}
+		if (!$complexConditions) {
+			$joins = false;
+		}
+
+		$conditions = $this->conditions($this->defaultConditions($model, $conditions, $alias), true, true, $model);
+		if ($conditions === false) {
+			return false;
+		}
+		if ($this->execute($this->renderStatement('delete', compact('alias', 'table', 'joins', 'conditions'))) === false) {
+			$model->onError();
+			return false;
+		}
+		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);
+			if (isset($indexes[0]['STATISTICS'])) {
+				$keys = Set::extract($indexes, '{n}.STATISTICS');
+			} else {
+				$keys = Set::extract($indexes, '{n}.0');
+			}
+			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 = $tableParameters = $colList = 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']);
+					}
+					if (isset($column['tableParameters'])) {
+						$tableParameters[$type] = $column['tableParameters'];
+						unset($column['tableParameters']);
+					}
+					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));
+				$colList = array_merge($colList, $this->_alterTableParameters($curTable, $tableParameters));
+				$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 table parameter alteration statementes for a table.
+ *
+ * @param string $table Table to alter parameters for.
+ * @param array $parameters Parameters to add & drop.
+ * @return array Array of table property alteration statementes.
+ * @todo Implement this method.
+ */
+	function _alterTableParameters($table, $parameters) {
+		if (isset($parameters['change'])) {
+			return $this->buildTableParameters($parameters['change']);
+		}
+		return array();
+	}
+
+/**
+ * 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 .' (' . implode(', ', 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 = implode(', ', array_map(array(&$this, 'name'), $fields));
+		}
+		$values = implode(', ', $values);
+		$this->query("INSERT INTO {$table} ({$fields}) VALUES {$values}");
+	}
+/**
+ * Returns an detailed array of sources (tables) in the database.
+ *
+ * @param string $name Table name to get parameters 
+ * @return array Array of tablenames in the database
+ */
+	function listDetailedSources($name = null) {
+		$condition = '';
+		if (is_string($name)) {
+			$condition = ' LIKE ' . $this->value($name);
+		}
+		$result = $this->query('SHOW TABLE STATUS FROM ' . $this->name($this->config['database']) . $condition . ';');
+		if (!$result) {
+			return array();
+		} else {
+			$tables = array();
+			foreach ($result as $row) {
+				$tables[$row['TABLES']['Name']] = $row['TABLES'];
+				if (!empty($row['TABLES']['Collation'])) {
+					$charset = $this->getCharsetName($row['TABLES']['Collation']);
+					if ($charset) {
+						$tables[$row['TABLES']['Name']]['charset'] = $charset;
+					}
+				}
+			}
+			if (is_string($name)) {
+				return $tables[$name];
+			}
+			return $tables;
+		}
+	}
+
+/**
+ * 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';
+	}
+}
+
+/**
+ * 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 {
+
+/**
+ * Datasource description
+ *
+ * @var string
+ */
+	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'
+	);
+
+/**
+ * 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 = mysql_connect($config['host'] . ':' . $config['port'], $config['login'], $config['password'], true);
+			$config['connect'] = 'mysql_connect';
+		} else {
+			$this->connection = mysql_pconnect($config['host'] . ':' . $config['port'], $config['login'], $config['password']);
+		}
+
+		if (!$this->connection) {
+			return false;
+		}
+
+		if (mysql_select_db($config['database'], $this->connection)) {
+			$this->connected = true;
+		}
+
+		if (!empty($config['encoding'])) {
+			$this->setEncoding($config['encoding']);
+		}
+
+		$this->_useAlias = (bool)version_compare(mysql_get_server_info($this->connection), "4.1", ">=");
+
+		return $this->connected;
+	}
+
+/**
+ * Check whether the MySQL extension is installed/loaded
+ *
+ * @return boolean
+ */
+	function enabled() {
+		return extension_loaded('mysql');
+	}
+/**
+ * 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_row($result)) {
+				$tables[] = $line[0];
+			}
+			parent::listSources($tables);
+			return $tables;
+		}
+	}
+
+/**
+ * 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 || (is_array($data) && empty($data))) {
+			return 'NULL';
+		}
+		if ($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_float($data)) {
+					return str_replace(',', '.', strval($data));
+				}
+				if ((is_int($data) || $data === '0') || (
+					is_numeric($data) && strpos($data, ',') === false &&
+					$data[0] != '0' && strpos($data, 'e') === false)
+				) {
+					return $data;
+				}
+			default:
+				return "'" . mysql_real_escape_string($data, $this->connection) . "'";
+			break;
+		}
+	}
+
+/**
+ * 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;
+	}
+
+/**
+ * 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) && strpos($column->name, $this->virtualFieldSeparator) === false) {
+				$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);
+	}
+
+/**
+ * Query charset by collation
+ *
+ * @param string $name Collation name
+ * @return string Character set name
+ */
+	function getCharsetName($name) {
+		if ((bool)version_compare(mysql_get_server_info($this->connection), "5", ">=")) {
+			$cols = $this->query('SELECT CHARACTER_SET_NAME FROM INFORMATION_SCHEMA.COLLATIONS WHERE COLLATION_NAME= ' . $this->value($name) . ';');
+			if (isset($cols[0]['COLLATIONS']['CHARACTER_SET_NAME'])) {
+				return $cols[0]['COLLATIONS']['CHARACTER_SET_NAME'];
+			}
+		}
+		return false;
+	}
+}

Added: trunk/src/Web/cake/libs/model/datasources/dbo/dbo_mysqli.php
===================================================================
--- trunk/src/Web/cake/libs/model/datasources/dbo/dbo_mysqli.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/model/datasources/dbo/dbo_mysqli.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,337 @@
+<?php
+/**
+ * MySQLi layer for DBO
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources.dbo
+ * @since         CakePHP(tm) v 1.1.4.2974
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Datasource', '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 {
+
+/**
+ * Datasource Description
+ *
+ * @var string
+ */
+	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',
+		'socket' => null
+	);
+
+/**
+ * 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;
+
+		$this->connection = mysqli_connect($config['host'], $config['login'], $config['password'], $config['database'], $config['port'], $config['socket']);
+
+		if ($this->connection !== false) {
+			$this->connected = true;
+		} else {
+			return false;
+		}
+
+		$this->_useAlias = (bool)version_compare(mysqli_get_server_info($this->connection), "4.1", ">=");
+
+		if (!empty($config['encoding'])) {
+			$this->setEncoding($config['encoding']);
+		}
+		return $this->connected;
+	}
+
+/**
+ * Check that MySQLi is installed/enabled
+ *
+ * @return boolean
+ */
+	function enabled() {
+		return extension_loaded('mysqli');
+	}
+/**
+ * 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);
+		}
+		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();
+		}
+
+		$tables = array();
+
+		while ($line = mysqli_fetch_row($result)) {
+			$tables[] = $line[0];
+		}
+		parent::listSources($tables);
+		return $tables;
+	}
+
+/**
+ * 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 || (is_array($data) && empty($data))) {
+			return 'NULL';
+		}
+		if ($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' :
+			case null :
+				if ($data === '') {
+					return 'NULL';
+				}
+				if (is_float($data)) {
+					return str_replace(',', '.', strval($data));
+				}
+				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;
+	}
+
+/**
+ * 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;
+	}
+
+/**
+ * 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) && strpos($column->name, $this->virtualFieldSeparator) === false) {
+				$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();
+			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];
+			}
+			return $resultRow;
+		}
+		return false;
+	}
+
+/**
+ * Gets the database encoding
+ *
+ * @return string The database encoding
+ */
+	function getEncoding() {
+		return mysqli_client_encoding($this->connection);
+	}
+
+/**
+ * Query charset by collation
+ *
+ * @param string $name Collation name
+ * @return string Character set name
+ */
+	function getCharsetName($name) {
+		if ((bool)version_compare(mysqli_get_server_info($this->connection), "5", ">=")) {
+			$cols = $this->query('SELECT CHARACTER_SET_NAME FROM INFORMATION_SCHEMA.COLLATIONS WHERE COLLATION_NAME= ' . $this->value($name) . ';');
+			if (isset($cols[0]['COLLATIONS']['CHARACTER_SET_NAME'])) {
+				return $cols[0]['COLLATIONS']['CHARACTER_SET_NAME'];
+			}
+		}
+		return false;
+	}
+
+/**
+ * Checks if the result is valid
+ *
+ * @return boolean True if the result is valid, else false
+ */
+	function hasResult() {
+		return is_object($this->_result);
+	}
+}

Added: trunk/src/Web/cake/libs/model/datasources/dbo/dbo_oracle.php
===================================================================
--- trunk/src/Web/cake/libs/model/datasources/dbo/dbo_oracle.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/model/datasources/dbo/dbo_oracle.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,1159 @@
+<?php
+/**
+ * Oracle layer for DBO.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources.dbo
+ * @since         CakePHP v 1.2.0.4041
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Oracle layer for DBO.
+ *
+ * Long description for class
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources.dbo
+ */
+class DboOracle extends DboSource {
+
+/**
+ * Configuration options
+ *
+ * @var array
+ * @access public
+ */
+	var $config = array();
+
+/**
+ * Alias
+ *
+ * @var string
+ */
+	var $alias = '';
+
+/**
+ * Sequence names as introspected from the database
+ */
+	var $_sequences = array();
+
+/**
+ * Transaction in progress flag
+ *
+ * @var boolean
+ */
+	var $__transactionStarted = false;
+
+/**
+ * Column definitions
+ *
+ * @var array
+ * @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'));
+
+/**
+ * Connection object
+ *
+ * @var mixed
+ * @access protected
+ */
+	var $connection;
+
+/**
+ * Query limit
+ *
+ * @var int
+ * @access protected
+ */
+	var $_limit = -1;
+
+/**
+ * Query offset
+ *
+ * @var int
+ * @access protected
+ */
+	var $_offset = 0;
+
+/**
+ * Enter description here...
+ *
+ * @var unknown_type
+ * @access protected
+ */
+	var $_map;
+
+/**
+ * Current Row
+ *
+ * @var mixed
+ * @access protected
+ */
+	var $_currentRow;
+
+/**
+ * Number of rows
+ *
+ * @var int
+ * @access protected
+ */
+	var $_numRows;
+
+/**
+ * Query results
+ *
+ * @var mixed
+ * @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 (!$config['persistent']) {
+			$this->connection = @ocilogon($config['login'], $config['password'], $config['database'], $config['charset']);
+		} else {
+			$this->connection = @ociplogon($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;
+	}
+
+/**
+ * Fetch result row
+ *
+ * @return array
+ * @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);
+	}
+
+/**
+ * Create trigger
+ *
+ * @param string $table
+ * @return mixed
+ * @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']);
+		}
+		parent::listSources($sources);
+		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) {
+		$table = $this->fullTableName($model, false);
+
+		if (!empty($model->sequence)) {
+			$this->_sequenceMap[$table] = $model->sequence;
+		} elseif (!empty($model->table)) {
+			$this->_sequenceMap[$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', true));
+		}
+
+		$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', true));
+			}
+		}
+		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" . implode(",\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':
+				$data = date('Y-m-d H:i:s', strtotime($data));
+				$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} {$group} {$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" . implode(",\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) {
+					echo '<div style = "font: Verdana bold 12px; color: #FF0000">' . sprintf(__('SQL Error in model %s:', true), $model->alias) . ' ';
+					if (isset($this->error) && $this->error != null) {
+						echo $this->error;
+					}
+					echo '</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__$}', implode(', ', $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__$}', '(' .implode(', ', $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;
+		}
+}

Added: trunk/src/Web/cake/libs/model/datasources/dbo/dbo_postgres.php
===================================================================
--- trunk/src/Web/cake/libs/model/datasources/dbo/dbo_postgres.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/model/datasources/dbo/dbo_postgres.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,978 @@
+<?php
+/**
+ * PostgreSQL layer for DBO.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources.dbo
+ * @since         CakePHP(tm) v 0.9.1.114
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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(
+		'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')
+	);
+
+/**
+ * Starting Quote
+ *
+ * @var string
+ * @access public
+ */
+	var $startQuote = '"';
+
+/**
+ * Ending Quote
+ *
+ * @var string
+ * @access public
+ */
+	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;
+	}
+
+/**
+ * Check if PostgreSQL is enabled/loaded
+ *
+ * @return boolean
+ */
+	function enabled() {
+		return extension_loaded('pgsql');
+	}
+/**
+ * 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'])) {
+						if ($c['type'] == 'character varying') {
+							$length = null;
+							$c['type'] = 'text';
+						} else {
+							$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];
+						}
+					}
+					if ($fields[$c['name']]['type'] == 'boolean' && !empty($fields[$c['name']]['default'])) {
+						$fields[$c['name']]['default'] = constant($fields[$c['name']]['default']);
+					}
+				}
+			}
+			$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 || (is_array($data) && empty($data))) {
+			return 'NULL';
+		}
+		if (empty($column)) {
+			$column = $this->introspectType($data);
+		}
+
+		switch($column) {
+			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;
+			case 'float':
+				if (is_float($data)) {
+					$data = sprintf('%F', $data);
+				}
+			case 'inet':
+			case 'integer':
+			case 'date':
+			case 'datetime':
+			case 'timestamp':
+			case 'time':
+				if ($data === '') {
+					return $read ? 'NULL' : 'DEFAULT';
+				}
+			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 ($this->execute('DELETE FROM ' . $this->fullTableName($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 && strpos($fields[0], 'COUNT(*)') === false) {
+			$result = array();
+			for ($i = 0; $i < $count; $i++) {
+				if (!preg_match('/^.+\\(.*\\)/', $fields[$i]) && !preg_match('/\s+AS\s+/', $fields[$i])) {
+					if (substr($fields[$i], -1) == '*') {
+						if (strpos($fields[$i], '.') !== false && $fields[$i] != $alias . '.*') {
+							$build = explode('.', $fields[$i]);
+							$AssociatedModel = $model->{$build[0]};
+						} else {
+							$AssociatedModel = $model;
+						}
+
+						$_fields = $this->fields($AssociatedModel, $AssociatedModel->alias, array_keys($AssociatedModel->schema()));
+						$result = array_merge($result, $_fields);
+						continue;
+					}
+
+					$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]);
+					}
+				} else {
+					$fields[$i] = preg_replace_callback('/\(([\s\.\w]+)\)/',  array(&$this, '__quoteFunctionField'), $fields[$i]);
+				}
+				$result[] = $fields[$i];
+			}
+			return $result;
+		}
+		return $fields;
+	}
+
+/**
+ * Auxiliary function to quote matched `(Model.fields)` from a preg_replace_callback call
+ * Quotes the fields in a function call.
+ *
+ * @param string matched string
+ * @return string quoted strig
+ * @access private
+ */
+	function __quoteFunctionField($match) {
+		$prepend = '';
+		if (strpos($match[1], 'DISTINCT') !== false) {
+			$prepend = 'DISTINCT ';
+			$match[1] = trim(str_replace('DISTINCT', '', $match[1]));
+		}
+		$constant = preg_match('/^\d+|NULL|FALSE|TRUE$/i', $match[1]);
+
+		if (!$constant && strpos($match[1], '.') === false) {
+			$match[1] = $this->name($match[1]);
+		} elseif (!$constant){
+			$parts = explode('.', $match[1]);
+			if (!Set::numeric($parts)) {
+				$match[1] = $this->name($match[1]);
+			}
+		}
+		return '(' . $prepend .$match[1] . ')';
+	}
+
+/**
+ * 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 = $colList = 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;
+								$colList[] = 'ADD COLUMN '.$this->buildColumn($col);
+							}
+						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);
+
+								$default = isset($col['default']) ? $col['default'] : null;
+								$nullable = isset($col['null']) ? $col['null'] : null;
+								unset($col['default'], $col['null']);
+								$colList[] = 'ALTER COLUMN '. $fieldName .' TYPE ' . str_replace(array($fieldName, 'NOT NULL'), '', $this->buildColumn($col));
+								if (isset($nullable)) {
+									$nullable = ($nullable) ? 'DROP NOT NULL' : 'SET NOT NULL';
+									$colList[] = 'ALTER COLUMN '. $fieldName .'  ' . $nullable;
+								}
+
+								if (isset($default)) {
+									$colList[] = 'ALTER COLUMN '. $fieldName .'  SET DEFAULT ' . $this->value($default, $col['type']);
+								} else {
+									$colList[] = 'ALTER COLUMN '. $fieldName .'  DROP DEFAULT';
+								}
+
+							}
+						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" . implode(",\n\t", $colList) . ";\n\n";
+				} else {
+					$out = '';
+				}
+				$out .= implode(";\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 . ' (' . implode(', ', 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'] = implode(', ', 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} = implode($join[$var], array_filter(${$var}));
+					}
+				}
+				return "CREATE TABLE {$table} (\n\t{$columns}\n);\n{$indexes}";
+			break;
+			default:
+				return parent::renderStatement($type, $data);
+			break;
+		}
+	}
+}

Added: trunk/src/Web/cake/libs/model/datasources/dbo/dbo_sqlite.php
===================================================================
--- trunk/src/Web/cake/libs/model/datasources/dbo/dbo_sqlite.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/model/datasources/dbo/dbo_sqlite.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,623 @@
+<?php
+/**
+ * SQLite layer for DBO
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources.dbo
+ * @since         CakePHP(tm) v 0.9.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * DBO implementation for the SQLite DBMS.
+ *
+ * Long description for class
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources.dbo
+ */
+class DboSqlite extends DboSource {
+
+/**
+ * Datasource Description
+ *
+ * @var string
+ */
+	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
+	);
+
+/**
+ * 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' => 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' => 'boolean')
+	);
+
+/**
+ * List of engine specific additional field parameters used on table creating
+ *
+ * @var array
+ * @access public
+ */
+	var $fieldParameters = array(
+		'collate' => array(
+			'value' => 'COLLATE',
+			'quote' => false,
+			'join' => ' ', 
+			'column' => 'Collate', 
+			'position' => 'afterDefault',
+			'options' => array(
+				'BINARY', 'NOCASE', 'RTRIM'
+			)
+		),
+	);
+
+/**
+ * Connects to the database using config['database'] as a filename.
+ *
+ * @param array $config Configuration array for connecting
+ * @return mixed
+ */
+	function connect() {
+		$config = $this->config;
+
+		if (!$config['persistent']) {
+			$this->connection = sqlite_open($config['database']);
+		} else {
+			$this->connection = sqlite_popen($config['database']);
+		}
+		$this->connected = is_resource($this->connection);
+
+		if ($this->connected) {
+			$this->_execute('PRAGMA count_changes = 1;');
+		}
+		return $this->connected;
+	}
+
+/**
+ * Check that SQLite is enabled/installed
+ *
+ * @return boolean
+ */
+	function enabled() {
+		return extension_loaded('sqlite');
+	}
+/**
+ * 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) {
+				$colLength = $this->length($column[0]['type']);
+				$fields[$column[0]['name']] = array(
+					'type' => $fields[$column[0]['name']]['type'],
+					'null' => false,
+					'default' => $column[0]['dflt_value'],
+					'key' => $this->index['PRI'],
+					'length'=> ($colLength != null) ? $colLength : 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', true), E_USER_WARNING);
+			return null;
+		}
+
+		if (!isset($this->columns[$type])) {
+			trigger_error(sprintf(__('Column type %s does not exist', true), $type), E_USER_WARNING);
+			return null;
+		}
+
+		$real = $this->columns[$type];
+		$out = $this->name($name) . ' ' . $real['name'];
+		if (isset($column['key']) && $column['key'] == 'primary' && $type == 'integer') {
+			return $this->name($name) . ' ' . $this->columns['primary_key']['name'];
+		}
+		return parent::buildColumn($column);
+	}
+
+/**
+ * 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'] = implode(', ', 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" . implode(",\n\t", array_filter(${$var}));
+					}
+				}
+				return "CREATE TABLE {$table} (\n{$columns});\n{$indexes}";
+			break;
+			default:
+				return parent::renderStatement($type, $data);
+			break;
+		}
+	}
+}

Added: trunk/src/Web/cake/libs/model/datasources/dbo_source.php
===================================================================
--- trunk/src/Web/cake/libs/model/datasources/dbo_source.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/model/datasources/dbo_source.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,2939 @@
+<?php
+/**
+ * Dbo Source
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model.datasources
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+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 string
+ * @access public
+ */
+	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
+ * @access public
+ */
+	var $alias = 'AS ';
+
+/**
+ * Caches result from query parsing operations.  Cached results for both DboSource::name() and
+ * DboSource::conditions() will be stored here.  Method caching uses `crc32()` which is
+ * fast but can collisions more easily than other hashing algorithms.  If you have problems
+ * with collisions, set DboSource::$cacheMethods to false.
+ *
+ * @var array
+ * @access public
+ */
+	var $methodCache = array();
+
+/**
+ * Whether or not to cache the results of DboSource::name() and DboSource::conditions()
+ * into the memory cache.  Set to false to disable the use of the memory cache.
+ *
+ * @var boolean.
+ * @access public
+ */
+	var $cacheMethods = true ;
+
+/**
+ * Bypass automatic adding of joined fields/associations.
+ *
+ * @var boolean
+ * @access private
+ */
+	var $__bypass = false;
+
+/**
+ * The set of valid SQL operations usable in a WHERE statement
+ *
+ * @var array
+ * @access private
+ */
+	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'
+	);
+
+/**
+ * Separator string for virtualField composition
+ *
+ * @var string
+ */
+	var $virtualFieldSeparator = '__';
+
+/**
+ * List of table engine specific parameters used on table creating
+ *
+ * @var array
+ * @access public
+ */
+	var $tableParameters = array();
+
+/**
+ * List of engine specific additional field parameters used on table creating
+ *
+ * @var array
+ * @access public
+ */
+	var $fieldParameters = array();
+
+/**
+ * Constructor
+ *
+ * @param array $config Array of configuration information for the Datasource.
+ * @param boolean $autoConnect Whether or not the datasource should automatically connect.
+ * @access public
+ */
+	function __construct($config = null, $autoConnect = true) {
+		if (!isset($config['prefix'])) {
+			$config['prefix'] = '';
+		}
+		parent::__construct($config);
+		$this->fullDebug = Configure::read() > 1;
+		if (!$this->enabled()) {
+			return false;
+		}
+		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
+ * @access public
+ */
+	function reconnect($config = array()) {
+		$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.
+ * @access public
+ */
+	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
+ * @access public
+ */
+	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
+ * @access public
+ */
+	function expression($expression) {
+		$obj = new stdClass();
+		$obj->type = 'expression';
+		$obj->value = $expression;
+		return $obj;
+	}
+
+/**
+ * Executes given SQL statement.
+ *
+ * @param string $sql SQL statement
+ * @return boolean
+ * @access public
+ */
+	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 Configure::read('debug') is set, the log is shown all the time, else it is only shown on errors.
+ *
+ * ### Options
+ *
+ * - stats - Collect meta data stats for this query. Stats include time take, rows affected,
+ *   any errors, and number of rows returned. Defaults to `true`.
+ * - log - Whether or not the query should be logged to the memory log.
+ *
+ * @param string $sql
+ * @param array $options
+ * @return mixed Resource or object representing the result set, or false on failure
+ * @access public
+ */
+	function execute($sql, $options = array()) {
+		$defaults = array('stats' => true, 'log' => $this->fullDebug);
+		$options = array_merge($defaults, $options);
+
+		$t = getMicrotime();
+		$this->_result = $this->_execute($sql);
+		if ($options['stats']) {
+			$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;
+		}
+		return $this->_result;
+	}
+
+/**
+ * DataSource Query abstraction
+ *
+ * @return resource Result resource identifier.
+ * @access public
+ */
+	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
+ * @access public
+ */
+	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();
+			if (!empty($resultRow)) {
+				$this->fetchVirtualField($resultRow);
+			}
+			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
+ * @access public
+ */
+	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()) {
+				$this->fetchVirtualField($item);
+				$out[] = $item;
+			}
+
+			if ($cache) {
+				if (preg_match('/^\s*select/i', $sql)) {
+					$this->_queryCache[$sql] = $out;
+				}
+			}
+			if (empty($out) && is_bool($this->_result)) {
+				return $this->_result;
+			}
+			return $out;
+		} else {
+			return false;
+		}
+	}
+
+/**
+ * Modifies $result array to place virtual fields in model entry where they belongs to
+ *
+ * @param array $resut REference to the fetched row
+ * @return void
+ */
+	function fetchVirtualField(&$result) {
+		if (isset($result[0]) && is_array($result[0])) {
+			foreach ($result[0] as $field => $value) {
+				if (strpos($field, $this->virtualFieldSeparator) === false) {
+					continue;
+				}
+				list($alias, $virtual) = explode($this->virtualFieldSeparator, $field);
+
+				if (!ClassRegistry::isKeySet($alias)) {
+					return;
+				}
+				$model = ClassRegistry::getObject($alias);
+				if ($model->isVirtualField($virtual)) {
+					$result[$alias][$virtual] = $value;
+					unset($result[0][$field]);
+				}
+			}
+			if (empty($result[0])) {
+				unset($result[0]);
+			}
+		}
+	}
+
+/**
+ * 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 mixed Value of field read.
+ * @access public
+ */
+	function field($name, $sql) {
+		$data = $this->fetchRow($sql);
+		if (!isset($data[$name]) || empty($data[$name])) {
+			return false;
+		} else {
+			return $data[$name];
+		}
+	}
+
+/**
+ * Empties the method caches.
+ * These caches are used by DboSource::name() and DboSource::conditions()
+ *
+ * @return void
+ */
+	function flushMethodCache() {
+		$this->methodCache = array();
+	}
+
+/**
+ * Cache a value into the methodCaches.  Will respect the value of DboSource::$cacheMethods.
+ * Will retrieve a value from the cache if $value is null.
+ *
+ * If caching is disabled and a write is attempted, the $value will be returned.
+ * A read will either return the value or null.
+ *
+ * @param string $method Name of the method being cached.
+ * @param string $key The keyname for the cache operation.
+ * @param mixed $value The value to cache into memory.
+ * @return mixed Either null on failure, or the value if its set.
+ */
+	function cacheMethod($method, $key, $value = null) {
+		if ($this->cacheMethods === false) {
+			return $value;
+		}
+		if ($value === null) {
+			return (isset($this->methodCache[$method][$key])) ? $this->methodCache[$method][$key] : null;
+		}
+		return $this->methodCache[$method][$key] = $value;
+	}
+
+/**
+ * Returns a quoted name of $data for use in an SQL statement.
+ * Strips fields out of SQL functions before quoting.
+ *
+ * Results of this method are stored in a memory cache.  This improves performance, but
+ * because the method uses a simple hashing algorithm it can infrequently have collisions.
+ * Setting DboSource::$cacheMethods to false will disable the memory cache.
+ *
+ * @param mixed $data Either a string with a column to quote. An array of columns to quote or an
+ *   object from DboSource::expression() or DboSource::identifier()
+ * @return string SQL field
+ * @access public
+ */
+	function name($data) {
+		if (is_object($data) && isset($data->type)) {
+			return $data->value;
+		}
+		if ($data === '*') {
+			return '*';
+		}
+		if (is_array($data)) {
+			foreach ($data as $i => $dataItem) {
+				$data[$i] = $this->name($dataItem);
+			}
+			return $data;
+		}
+		$cacheKey = crc32($this->startQuote.$data.$this->endQuote);
+		if ($return = $this->cacheMethod(__FUNCTION__, $cacheKey)) {
+			return $return;
+		}
+		$data = trim($data);
+		if (preg_match('/^[\w-]+(?:\.[^ \*]*)*$/', $data)) { // string, string.string
+			if (strpos($data, '.') === false) { // string
+				return $this->cacheMethod(__FUNCTION__, $cacheKey, $this->startQuote . $data . $this->endQuote);
+			}
+			$items = explode('.', $data);
+			return $this->cacheMethod(__FUNCTION__, $cacheKey,
+				$this->startQuote . implode($this->endQuote . '.' . $this->startQuote, $items) . $this->endQuote
+			);
+		}
+		if (preg_match('/^[\w-]+\.\*$/', $data)) { // string.*
+			return $this->cacheMethod(__FUNCTION__, $cacheKey,
+				$this->startQuote . str_replace('.*', $this->endQuote . '.*', $data)
+			);
+		}
+		if (preg_match('/^([\w-]+)\((.*)\)$/', $data, $matches)) { // Functions
+			return $this->cacheMethod(__FUNCTION__, $cacheKey,
+				 $matches[1] . '(' . $this->name($matches[2]) . ')'
+			);
+		}
+		if (
+			preg_match('/^([\w-]+(\.[\w-]+|\(.*\))*)\s+' . preg_quote($this->alias) . '\s*([\w-]+)$/i', $data, $matches
+		)) {
+			return $this->cacheMethod(
+				__FUNCTION__, $cacheKey,
+				preg_replace(
+					'/\s{2,}/', ' ', $this->name($matches[1]) . ' ' . $this->alias . ' ' . $this->name($matches[3])
+				)
+			);
+		}
+		if (preg_match('/^[\w-_\s]*[\w-_]+/', $data)) {
+			return $this->cacheMethod(__FUNCTION__, $cacheKey, $this->startQuote . $data . $this->endQuote);
+		}
+		return $this->cacheMethod(__FUNCTION__, $cacheKey, $data);
+	}
+
+/**
+ * Checks if the source is connected to the database.
+ *
+ * @return boolean True if the database is connected, else false
+ * @access public
+ */
+	function isConnected() {
+		return $this->connected;
+	}
+
+/**
+ * Checks if the result is valid
+ *
+ * @return boolean True if the result is valid else false
+ * @access public
+ */
+	function hasResult() {
+		return is_resource($this->_result);
+	}
+
+/**
+ * Get the query log as an array.
+ *
+ * @param boolean $sorted Get the queries sorted by time taken, defaults to false.
+ * @return array Array of queries run as an array
+ * @access public
+ */
+	function getLog($sorted = false, $clear = true) {
+		if ($sorted) {
+			$log = sortByKey($this->_queriesLog, 'took', 'desc', SORT_NUMERIC);
+		} else {
+			$log = $this->_queriesLog;
+		}
+		if ($clear) {
+			$this->_queriesLog = array();
+		}
+		return array('log' => $log, 'count' => $this->_queriesCnt, 'time' => $this->_queriesTime);
+	}
+
+/**
+ * Outputs the contents of the queries log. If in a non-CLI environment the sql_log element
+ * will be rendered and output.  If in a CLI environment, a plain text log is generated.
+ *
+ * @param boolean $sorted Get the queries sorted by time taken, defaults to false.
+ * @return void
+ */
+	function showLog($sorted = false) {
+		$log = $this->getLog($sorted, false);
+		if (empty($log['log'])) {
+			return;
+		}
+		if (PHP_SAPI != 'cli') {
+			App::import('Core', 'View');
+			$controller = null;
+			$View =& new View($controller, false);
+			$View->set('logs', array($this->configKeyName => $log));
+			echo $View->element('sql_dump', array('_forced_from_dbo_' => true));
+		} else {
+			foreach ($log['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
+ * @access public
+ */
+	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.
+ * @access public
+ */
+	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:', true) . "</b> {$this->error}</span>", E_USER_WARNING);
+			} else {
+				$out = ('<small>[' . sprintf(__('Aff:%s Num:%s Took:%sms', true), $this->affected, $this->numRows, $this->took) . ']</small>');
+			}
+			pr(sprintf('<p style="text-align:left"><b>' . __('Query:', true) . '</b> %s %s</p>', $sql, $out));
+		}
+	}
+
+/**
+ * Gets full table name including prefix
+ *
+ * @param mixed $model Either a Model object or a string table name.
+ * @param boolean $quote Whether you want the table name quoted.
+ * @return string Full quoted table name
+ * @access public
+ */
+	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
+ *
+ * Creates new records in the database.
+ *
+ * @param Model $model Model object that the record is for.
+ * @param array $fields An array of field names to insert. If null, $model->data will be
+ *   used to generate field names.
+ * @param array $values An array of values with keys matching the fields. If null, $model->data will
+ *   be used to generate values.
+ * @return boolean Success
+ * @access public
+ */
+	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);
+			$fieldInsert[] = $this->name($fields[$i]);
+			if ($fields[$i] == $model->primaryKey) {
+				$id = $values[$i];
+			}
+		}
+		$query = array(
+			'table' => $this->fullTableName($model),
+			'fields' => implode(', ', $fieldInsert),
+			'values' => implode(', ', $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
+ *
+ * Reads record(s) from the database.
+ *
+ * @param Model $model A Model object that the query is for.
+ * @param array $queryData An array of queryData information containing keys similar to Model::find()
+ * @param integer $recursive Number of levels of association
+ * @return mixed boolean false on error/failure.  An array of results on success.
+ */
+	function read(&$model, $queryData = array(), $recursive = null) {
+		$queryData = $this->__scrubQueryData($queryData);
+
+		$null = null;
+		$array = array('callbacks' => $queryData['callbacks']);
+		$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);
+		}
+
+		$_associations = $model->__associations;
+
+		if ($model->recursive == -1) {
+			$_associations = array();
+		} else if ($model->recursive == 0) {
+			unset($_associations[2], $_associations[3]);
+		}
+
+		foreach ($_associations as $type) {
+			foreach ($model->{$type} as $assoc => $assocData) {
+				$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] = true;
+					}
+				}
+			}
+		}
+
+		$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 = array();
+
+		if ($queryData['callbacks'] === true || $queryData['callbacks'] === 'after') {
+			$filtered = $this->__filterResults($resultSet, $model);
+		}
+
+		if ($model->recursive > -1) {
+			foreach ($_associations as $type) {
+				foreach ($model->{$type} as $assoc => $assocData) {
+					$linkModel =& $model->{$assoc};
+
+					if (empty($linkedModels[$type . '/' . $assoc])) {
+						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) && method_exists($db, 'queryAssociation')) {
+						$stack = array($assoc);
+						$db->queryAssociation($model, $linkModel, $type, $assoc, $assocData, $array, true, $resultSet, $model->recursive - 1, $stack);
+						unset($db);
+
+						if ($type === 'hasMany') {
+							$filtered []= $assoc;
+						}
+					}
+				}
+			}
+			if ($queryData['callbacks'] === true || $queryData['callbacks'] === 'after') {
+				$this->__filterResults($resultSet, $model, $filtered);
+			}
+		}
+
+		if (!is_null($recursive)) {
+			$model->recursive = $_recursive;
+		}
+		return $resultSet;
+	}
+
+/**
+ * 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 array Array of results that have been filtered through $model->afterFind
+ * @access private
+ */
+	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;
+	}
+
+/**
+ * Queries associations.  Used to fetch results on recursive models.
+ *
+ * @param Model $model Primary Model object
+ * @param Model $linkModel Linked model that
+ * @param string $type Association type, one of the model association types ie. hasMany
+ * @param unknown_type $association
+ * @param unknown_type $assocData
+ * @param array $queryData
+ * @param boolean $external Whether or not the association query is on an external datasource.
+ * @param array $resultSet Existing results
+ * @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) {
+					echo '<div style = "font: Verdana bold 12px; color: #FF0000">' . sprintf(__('SQL Error in model %s:', true), $model->alias) . ' ';
+					if (isset($this->error) && $this->error != null) {
+						echo $this->error;
+					}
+					echo '</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)) {
+					$ins = array_unique($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);
+							}
+						}
+					}
+				}
+				if ($queryData['callbacks'] === true || $queryData['callbacks'] === 'after') {
+					$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)) {
+					$ins = array_unique($ins);
+					if (count($ins) > 1) {
+						$query = str_replace('{$__cakeID__$}', '(' .implode(', ', $ins) .')', $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])
+							) {
+								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], false);
+					}
+				} 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
+ * @access public
+ */
+	function fetchAssociated($model, $query, $ids) {
+		$query = str_replace('{$__cakeID__$}', implode(', ', $ids), $query);
+		if (count($ids) > 1) {
+			$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
+ * @access private
+ */
+	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) {
+					$insert = array();
+					if (count($row) === 1) {
+						$insert = $row[$association];
+					} elseif (isset($row[$association])) {
+						$insert = array_merge($row[$association], $row);
+						unset($insert[$association]);
+					}
+
+					if (empty($data[$association]) || (isset($data[$association]) && !in_array($insert, $data[$association], true))) {
+						$data[$association][] = $insert;
+					}
+				}
+			}
+		}
+	}
+
+/**
+ * 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
+ * @access public
+ */
+	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 = (bool)preg_match('/^[a-z]+\(/i', $passedFields[0]);
+
+				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' => $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 = $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 = $assocData['joinTable'];
+					$joinAlias = $this->fullTableName($assocData['joinTable']);
+				}
+				$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
+ * @access public
+ */
+	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
+ * @access public
+ * @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));
+		}
+		if (!empty($data['table'])) {
+			$data['table'] = $this->fullTableName($data['table']);
+		}
+		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
+ * @access public
+ * @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' => implode(', ', $query['fields']),
+			'table' => $query['table'],
+			'alias' => $this->alias . $this->name($query['alias']),
+			'order' => $this->order($query['order'], 'ASC', $model),
+			'limit' => $this->limit($query['limit'], $query['offset']),
+			'joins' => implode(' ', $query['joins']),
+			'group' => $this->group($query['group'], $model)
+		));
+	}
+
+/**
+ * Renders a final SQL JOIN statement
+ *
+ * @param array $data
+ * @return string
+ * @access public
+ */
+	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 type of query being run.  e.g select, create, update, delete, schema, alter.
+ * @param array $data Array of data to insert into the query.
+ * @return string Rendered SQL expression to be run.
+ * @access public
+ */
+	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', 'tableParameters') as $var) {
+					if (is_array(${$var})) {
+						${$var} = "\t" . join(",\n\t", array_filter(${$var}));
+					} else {
+						${$var} = '';
+					}
+				}
+				if (trim($indexes) != '') {
+					$columns .= ',';
+				}
+				return "CREATE TABLE {$table} (\n{$columns}{$indexes}){$tableParameters};";
+			break;
+			case 'alter':
+			break;
+		}
+	}
+
+/**
+ * Merges a mixed set of string/array conditions
+ *
+ * @return array
+ * @access private
+ */
+	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
+ * @access public
+ */
+	function update(&$model, $fields = array(), $values = null, $conditions = null) {
+		if ($values == null) {
+			$combined = $fields;
+		} else {
+			$combined = array_combine($fields, $values);
+		}
+
+		$fields = implode(', ', $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;
+
+		$updates = array();
+		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
+ * @access public
+ */
+	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 {
+			$noJoin = true;
+			foreach ($conditions as $field => $value) {
+				$originalField = $field;
+				if (strpos($field, '.') !== false) {
+					list($alias, $field) = explode('.', $field);
+					$field = ltrim($field, $this->startQuote);
+					$field = rtrim($field, $this->endQuote);
+				}
+				if (!$model->hasField($field)) {
+					$noJoin = false;
+					break;
+				}
+				if ($field !== $originalField) {
+					$conditions[$field] = $value;
+					unset($conditions[$originalField]);
+				}
+			}
+			if ($noJoin === true) {
+				return $this->conditions($conditions);
+			}
+			$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
+ * @access protected
+ */
+	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' => $model->{$assoc},
+					'alias' => $assoc,
+					'type' => isset($assocData['type']) ? $assocData['type'] : 'LEFT',
+					'conditions' => trim($this->conditions(
+						$this->__mergeConditions($assocData['conditions'], $this->getConstraint($assocData['association'], $model, $model->{$assoc}, $assoc, $assocData)),
+						true, false, $model
+					))
+				));
+			}
+		}
+		return $join;
+	}
+
+/**
+ * Returns 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';
+				}
+				if (is_object($model) && $model->isVirtualField($params[0])){
+					$arg = $this->__quoteFields($model->getVirtualField($params[0]));
+				} else {
+					$arg = $this->name($params[0]);
+				}
+				return 'COUNT(' . $arg . ') AS ' . $this->name($params[1]);
+			case 'max':
+			case 'min':
+				if (!isset($params[1])) {
+					$params[1] = $params[0];
+				}
+				if (is_object($model) && $model->isVirtualField($params[0])) {
+					$arg = $this->__quoteFields($model->getVirtualField($params[0]));
+				} else {
+					$arg = $this->name($params[0]);
+				}
+				return strtoupper($func) . '(' . $arg . ') 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).
+ * @access public
+ */
+	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).
+ * @access public
+ */
+	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).
+ * @access public
+ */
+	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.
+ * If conditions are supplied then they will be returned.  If a model doesn't exist and no conditions
+ * were provided either null or false will be returned based on what was input.
+ *
+ * @param object $model
+ * @param mixed $conditions Array of conditions, conditions string, null or false. If an array of conditions,
+ *   or string conditions those conditions will be returned.  With other values the model's existance will be checked.
+ *   If the model doesn't exist a null or false will be returned depending on the input value.
+ * @param boolean $useAlias Use model aliases rather than table names when generating conditions
+ * @return mixed Either null, false, $conditions or an array of default conditions to use.
+ * @see DboSource::update()
+ * @see DboSource::conditions()
+ * @access public
+ */
+	function defaultConditions(&$model, $conditions, $useAlias = true) {
+		if (!empty($conditions)) {
+			return $conditions;
+		}
+		$exists = $model->exists();
+		if (!$exists && $conditions !== null) {
+			return false;
+		} elseif (!$exists) {
+			return null;
+		}
+		$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
+ * @access public
+ */
+	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
+ * @access public
+ */
+	function __scrubQueryData($data) {
+		foreach (array('conditions', 'fields', 'joins', 'order', 'limit', 'offset', 'group') as $key) {
+			if (empty($data[$key])) {
+				$data[$key] = array();
+			}
+		}
+		if (!array_key_exists('callbacks', $data)) {
+			$data['callbacks'] = null;
+		}
+		return $data;
+	}
+
+/**
+ * Converts model virtual fields into sql expressions to be fetched later
+ *
+ * @param Model $model
+ * @param string $alias Alias tablename
+ * @param mixed $fields virtual fields to be used on query
+ * @return array
+ */
+	function _constructVirtualFields(&$model, $alias, $fields) {
+		$virtual = array();
+		foreach ($fields as $field) {
+			$virtualField = $this->name($alias . $this->virtualFieldSeparator . $field);
+			$expression = $this->__quoteFields($model->getVirtualField($field));
+			$virtual[] = '(' . $expression . ") {$this->alias} {$virtualField}";
+		}
+		return $virtual;
+	}
+
+/**
+ * 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
+ * @access public
+ */
+	function fields(&$model, $alias = null, $fields = array(), $quote = true) {
+		if (empty($alias)) {
+			$alias = $model->alias;
+		}
+		$cacheKey = array(
+			$model->useDbConfig,
+			$model->table,
+			array_keys($model->schema()),
+			$model->name,
+			$model->getVirtualField(),
+			$alias,
+			$fields,
+			$quote
+		);
+		$cacheKey = crc32(serialize($cacheKey));
+		if ($return = $this->cacheMethod(__FUNCTION__, $cacheKey)) {
+			return $return;
+		}
+		$allFields = empty($fields);
+		if ($allFields) {
+			$fields = array_keys($model->schema());
+		} elseif (!is_array($fields)) {
+			$fields = String::tokenize($fields);
+		}
+		$fields = array_values(array_filter($fields));
+		$allFields = $allFields || in_array('*', $fields) || in_array($model->alias . '.*', $fields);
+
+		$virtual = array();
+		$virtualFields = $model->getVirtualField();
+		if (!empty($virtualFields)) {
+			$virtualKeys = array_keys($virtualFields);
+			foreach ($virtualKeys as $field) {
+				$virtualKeys[] = $model->alias . '.' . $field;
+			}
+			$virtual = ($allFields) ? $virtualKeys : array_intersect($virtualKeys, $fields);
+			foreach ($virtual as $i => $field) {
+				if (strpos($field, '.') !== false) {
+					$virtual[$i] = str_replace($model->alias . '.', '', $field);
+				}
+				$fields = array_diff($fields, array($field));
+			}
+			$fields = array_values($fields);
+		}
+
+		if (!$quote) {
+			if (!empty($virtual)) {
+				$fields = array_merge($fields, $this->_constructVirtualFields($model, $alias, $virtual));
+			}
+			return $fields;
+		}
+		$count = count($fields);
+
+		if ($count >= 1 && !in_array($fields[0], array('*', 'COUNT(*)'))) {
+			for ($i = 0; $i < $count; $i++) {
+				if (is_string($fields[$i]) && in_array($fields[$i], $virtual)) {
+					unset($fields[$i]);
+					continue;
+				}
+				if (is_object($fields[$i]) && isset($fields[$i]->type) && $fields[$i]->type === 'expression') {
+					$fields[$i] = $fields[$i]->value;
+				} elseif (preg_match('/^\(.*\)\s' . $this->alias . '.*/i', $fields[$i])){
+					continue;
+				} elseif (!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(implode('.', $build));
+							}
+						}
+					}
+					$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] = implode('.', array_map(array(&$this, 'name'), $field[0]));
+								$fields[$i] = preg_replace('/\(' . $field[1] . '\)/', '(' . $field[0] . ')', $fields[$i], 1);
+							}
+						}
+					}
+				}
+			}
+		}
+		if (!empty($virtual)) {
+			$fields = array_merge($fields, $this->_constructVirtualFields($model, $alias, $virtual));
+		}
+		return $this->cacheMethod(__FUNCTION__, $cacheKey, array_unique($fields));
+	}
+
+/**
+ * Creates a WHERE clause by parsing given conditions data.  If an array or string
+ * conditions are provided those conditions will be parsed and quoted.  If a boolean
+ * is given it will be integer cast as condition.  Null will return 1 = 1.
+ *
+ * Results of this method are stored in a memory cache.  This improves performance, but
+ * because the method uses a simple hashing algorithm it can infrequently have collisions.
+ * Setting DboSource::$cacheMethods to false will disable the memory cache.
+ *
+ * @param mixed $conditions Array or string of conditions, or any value.
+ * @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
+ * @access public
+ */
+	function conditions($conditions, $quoteValues = true, $where = true, $model = null) {
+		if (is_object($model)) {
+			$cacheKey = array(
+				$model->useDbConfig,
+				$model->table,
+				$model->schema(),
+				$model->name,
+				$model->getVirtualField(),
+				$conditions,
+				$quoteValues,
+				$where
+			);
+		} else {
+			$cacheKey = array($conditions, $quoteValues, $where);
+		}
+		$cacheKey = crc32(serialize($cacheKey));
+		if ($return = $this->cacheMethod(__FUNCTION__, $cacheKey)) {
+			return $return;
+		}
+
+		$clause = $out = '';
+
+		if ($where) {
+			$clause = ' WHERE ';
+		}
+
+		if (is_array($conditions) && !empty($conditions)) {
+			$out = $this->conditionKeysToString($conditions, $quoteValues, $model);
+
+			if (empty($out)) {
+				return $this->cacheMethod(__FUNCTION__, $cacheKey, $clause . ' 1 = 1');
+			}
+			return $this->cacheMethod(__FUNCTION__, $cacheKey, $clause . implode(' AND ', $out));
+		}
+		if ($conditions === false || $conditions === true) {
+			return $this->cacheMethod(__FUNCTION__, $cacheKey,  $clause . (int)$conditions . ' = 1');
+		}
+
+		if (empty($conditions) || trim($conditions) == '') {
+			return $this->cacheMethod(__FUNCTION__, $cacheKey, $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 $this->cacheMethod(__FUNCTION__, $cacheKey, $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
+ * @access public
+ */
+	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 . '(' . implode(') ' . 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 ($keys === array_values($keys)) {
+						$count = count($value);
+						if ($count === 1) {
+							$data = $this->__quoteFields($key) . ' = (';
+						} else {
+							$data = $this->__quoteFields($key) . ' IN (';
+						}
+						if ($quoteValues) {
+							if (is_object($model)) {
+								$columnType = $model->getColumnType($key);
+							}
+							$data .= implode(', ', $this->value($value, $columnType));
+						}
+						$data .= ')';
+					} else {
+						$ret = $this->conditionKeysToString($value, $quoteValues, $model);
+						if (count($ret) > 1) {
+							$data = '(' . implode(') 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) {
+					$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 = '/^(((' . implode(')|(', $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);
+			}
+		}
+
+		$virtual = false;
+		if (is_object($model) && $model->isVirtualField($key)) {
+			$key = $this->__quoteFields($model->getVirtualField($key));
+			$virtual = true;
+		}
+
+		$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);
+
+		if (!$virtual && $key !== '?') {
+			$isKey = (strpos($key, '(') !== false || strpos($key, ')') !== false);
+			$key = $isKey ? $this->__quoteFields($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 = implode(', ', $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;
+			}
+		}
+		if ($virtual) {
+			return "({$key}) {$operator} {$value}";
+		}
+		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);
+		$conditions = preg_replace_callback('/(?:[\'\"][^\'\"\\\]*(?:\\\.[^\'\"\\\]*)*[\'\"])|([a-z0-9_' . $start . $end . ']*\\.[a-z0-9_' . $start . $end . ']*)/i', array(&$this, '__quoteMatchedField'), $conditions);
+
+		if ($conditions !== null) {
+			return $conditions;
+		}
+		return $original;
+	}
+
+/**
+ * Auxiliary function to quote matches `Model.fields` from a preg_replace_callback call
+ *
+ * @param string matched string
+ * @return string quoted strig
+ * @access private
+ */
+	function __quoteMatchedField($match) {
+		if (is_numeric($match[0])) {
+			return $match[0];
+		}
+		return $this->name($match[0]);
+	}
+
+/**
+ * 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
+ * @access public
+ */
+	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)
+ * @param object $model model reference (used to look for virtual field)
+ * @return string ORDER BY clause
+ * @access public
+ */
+	function order($keys, $direction = 'ASC', $model = null) {
+		if (!is_array($keys)) {
+			$keys = array($keys);
+		}
+		$keys = array_filter($keys);
+		$result = array();
+		while (!empty($keys)) {
+			list($key, $dir) = each($keys);
+			array_shift($keys);
+
+			if (is_numeric($key)) {
+				$key = $dir;
+				$dir = $direction;
+			}
+
+			if (is_string($key) && strpos($key, ',') && !preg_match('/\(.+\,.+\)/', $key)) {
+				$key = array_map('trim', explode(',', $key));
+			}
+			if (is_array($key)) {
+				//Flatten the array
+				$key = array_reverse($key, true);
+				foreach ($key as $k => $v) {
+					if (is_numeric($k)) {
+						array_unshift($keys, $v);
+					} else {
+						$keys = array($k => $v) + $keys;
+					}
+				}
+				continue;
+			} elseif (is_object($key) && isset($key->type) && $key->type === 'expression') {
+				$result[] = $key->value;
+				continue;
+			}
+
+			if (preg_match('/\\x20(ASC|DESC).*/i', $key, $_dir)) {
+				$dir = $_dir[0];
+				$key = preg_replace('/\\x20(ASC|DESC).*/i', '', $key);
+			}
+
+			$key = trim($key);
+
+			if (is_object($model) && $model->isVirtualField($key)) {
+				$key =  '(' . $this->__quoteFields($model->getVirtualField($key)) . ')';
+			}
+
+			if (strpos($key, '.')) {
+				$key = preg_replace_callback('/([a-zA-Z0-9_-]{1,})\\.([a-zA-Z0-9_-]{1,})/', array(&$this, '__quoteMatchedField'), $key);
+			}
+			if (!preg_match('/\s/', $key) && !strpos($key, '.')) {
+				$key = $this->name($key);
+			}
+			$key .= ' ' . trim($dir);
+			$result[] = $key;
+		}
+		if (!empty($result)) {
+			return ' ORDER BY ' . implode(', ', $result);
+		}
+		return '';
+	}
+
+/**
+ * Create a GROUP BY SQL clause
+ *
+ * @param string $group Group By Condition
+ * @return mixed string condition or null
+ * @access public
+ */
+	function group($group, $model = null) {
+		if ($group) {
+			if (!is_array($group)) {
+				$group = array($group);
+			}
+			foreach($group as $index => $key) {
+				if (is_object($model) && $model->isVirtualField($key)) {
+					$group[$index] = '(' . $model->getVirtualField($key) . ')';
+				}
+			}
+			$group = implode(', ', $group);
+			return ' GROUP BY ' . $this->__quoteFields($group);
+		}
+		return null;
+	}
+
+/**
+ * Disconnects database, kills the connection and says the connection is closed.
+ *
+ * @return void
+ * @access public
+ */
+	function close() {
+		$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
+ * @access public
+ */
+	function hasAny(&$Model, $sql) {
+		$sql = $this->conditions($sql);
+		$table = $this->fullTableName($Model);
+		$alias = $this->alias . $this->name($Model->alias);
+		$where = $sql ? "{$sql}" : ' WHERE 1 = 1';
+		$id = $Model->escapeField();
+
+		$out = $this->fetchRow("SELECT COUNT({$id}) {$this->alias}count FROM {$table} {$alias}{$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
+ * @access public
+ */
+	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
+ * @access public
+ */
+	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 = implode(', ', 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
+ * @access public
+ */
+	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
+ * @access public
+ */
+	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 = $tableParameters = 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' && $name !== 'tableParameters') {
+						$col['name'] = $name;
+						if (!isset($col['type'])) {
+							$col['type'] = 'string';
+						}
+						$cols[] = $this->buildColumn($col);
+					} elseif ($name == 'indexes') {
+						$indexes = array_merge($indexes, $this->buildIndex($col, $table));
+					} elseif ($name == 'tableParameters') {
+						$tableParameters = array_merge($tableParameters, $this->buildTableParameters($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', 'tableParameters')) . "\n\n";
+			}
+		}
+		return $out;
+	}
+
+/**
+ * Generate a alter syntax from	 CakeSchema::compare()
+ *
+ * @param unknown_type $schema
+ * @return boolean
+ */
+	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
+ * @access public
+ */
+	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
+ * @access public
+ */
+	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', true), E_USER_WARNING);
+			return null;
+		}
+
+		if (!isset($this->columns[$type])) {
+			trigger_error(sprintf(__('Column type %s does not exist', true), $type), 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;
+		}
+		$out = $this->_buildFieldParameters($out, $column, 'beforeDefault');
+
+		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 ($type !== 'timestamp' && !empty($column['null'])) {
+			$out .= ' DEFAULT NULL';
+		} elseif ($type === 'timestamp' && !empty($column['null'])) {
+			$out .= ' NULL';
+		} elseif (isset($column['null']) && $column['null'] == false) {
+			$out .= ' NOT NULL';
+		}
+		if ($type == 'timestamp' && isset($column['default']) && strtolower($column['default']) == 'current_timestamp') {
+			$out = str_replace(array("'CURRENT_TIMESTAMP'", "'current_timestamp'"), 'CURRENT_TIMESTAMP', $out);
+		}
+		$out = $this->_buildFieldParameters($out, $column, 'afterDefault');
+		return $out;
+	}
+
+/**
+ * Build the field parameters, in a position
+ *
+ * @param string $columnString The partially built column string
+ * @param array $columnData The array of column data.
+ * @param string $position The position type to use. 'beforeDefault' or 'afterDefault' are common
+ * @return string a built column with the field parameters added.
+ * @access public
+ */
+	function _buildFieldParameters($columnString, $columnData, $position) {
+		foreach ($this->fieldParameters as $paramName => $value) {
+			if (isset($columnData[$paramName]) && $value['position'] == $position) {
+				if (isset($value['options']) && !in_array($columnData[$paramName], $value['options'])) {
+					continue;
+				}
+				$val = $columnData[$paramName];
+				if ($value['quote']) {
+					$val = $this->value($val);
+				}
+				$columnString .= ' ' . $value['value'] . $value['join'] . $val;
+			}
+		}
+		return $columnString;
+	}
+
+/**
+ * Format indexes for create table
+ *
+ * @param array $indexes
+ * @param string $table
+ * @return array
+ * @access public
+ */
+	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 ';
+				}
+				$name = $this->startQuote . $name . $this->endQuote;
+			}
+			if (is_array($value['column'])) {
+				$out .= 'KEY ' . $name . ' (' . implode(', ', array_map(array(&$this, 'name'), $value['column'])) . ')';
+			} else {
+				$out .= 'KEY ' . $name . ' (' . $this->name($value['column']) . ')';
+			}
+			$join[] = $out;
+		}
+		return $join;
+	}
+
+/**
+ * Read additional table parameters
+ *
+ * @param array $parameters
+ * @param string $table
+ * @return array
+ * @access public
+ */
+	function readTableParameters($name) {
+		$parameters = array();
+		if ($this->isInterfaceSupported('listDetailedSources')) {
+			$currentTableDetails = $this->listDetailedSources($name);
+			foreach ($this->tableParameters as $paramName => $parameter) {
+				if (!empty($parameter['column']) && !empty($currentTableDetails[$parameter['column']])) {
+					$parameters[$paramName] = $currentTableDetails[$parameter['column']];
+				}
+			}
+		}
+		return $parameters;
+	}
+
+/**
+ * Format parameters for create table
+ *
+ * @param array $parameters
+ * @param string $table
+ * @return array
+ * @access public
+ */
+	function buildTableParameters($parameters, $table = null) {
+		$result = array();
+		foreach ($parameters as $name => $value) {
+			if (isset($this->tableParameters[$name])) {
+				if ($this->tableParameters[$name]['quote']) {
+					$value = $this->value($value);
+				}
+				$result[] = $this->tableParameters[$name]['value'] . $this->tableParameters[$name]['join'] . $value;
+			}
+		}
+		return $result;
+	}
+
+/**
+ * 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';
+	}
+}

Added: trunk/src/Web/cake/libs/model/db_acl.php
===================================================================
--- trunk/src/Web/cake/libs/model/db_acl.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/model/db_acl.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,332 @@
+<?php
+/**
+ * This is core configuration file.
+ *
+ * Use it to configure core behaviour ofCake.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Load Model and AppModel
+ */
+App::import('Model', 'App');
+
+/**
+ * ACL Node
+ *
+ *
+ * @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' => $table,
+					'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' => $table,
+					'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'),
+						$db->name("{$type}{$j}.id") . ' = ' . $db->name("{$type}{$i}.parent_id")
+					)
+				);
+
+				$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(sprintf(__("Model class '%s' not found in AclNode::node() when trying to bind %s object", true), $type, $this->alias), 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' => $table,
+					'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(sprintf(__("AclNode::node() - Couldn't find %s node identified by \"%s\"", true), $type, 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();
+	}
+}

Added: trunk/src/Web/cake/libs/model/model.php
===================================================================
--- trunk/src/Web/cake/libs/model/model.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/model/model.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,3093 @@
+<?php
+/**
+ * Object-relational mapper.
+ *
+ * DBO-backed object data model, for mapping database tables to Cake objects.
+ *
+ * PHP versions 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model
+ * @since         CakePHP(tm) v 0.10.0.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Included libs
+ */
+App::import('Core', array('ClassRegistry', 'Validation', 'Set', 'String'));
+App::import('Model', 'ModelBehavior', false);
+App::import('Model', 'ConnectionManager', false);
+
+if (!class_exists('Overloadable')) {
+	require LIBS . 'overloadable.php';
+}
+
+/**
+ * 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/1000/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/1057/Model-Attributes#useDbConfig-1058
+ */
+	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/1057/Model-Attributes#useTable-1059
+ */
+	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/1057/Model-Attributes#displayField-1062
+ */
+	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/1057/Model-Attributes#data-1065
+ */
+	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/1057/Model-Attributes#primaryKey-1061
+ */
+	var $primaryKey = null;
+
+/**
+ * Field-by-field table metadata.
+ *
+ * @var array
+ * @access protected
+ * @link http://book.cakephp.org/view/1057/Model-Attributes#_schema-1066
+ */
+	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/1057/Model-Attributes#validate-1067
+ * @link http://book.cakephp.org/view/1143/Data-Validation
+ */
+	var $validate = array();
+
+/**
+ * List of validation errors.
+ *
+ * @var array
+ * @access public
+ * @link http://book.cakephp.org/view/1182/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/1057/Model-Attributes#tablePrefix-1060
+ */
+	var $tablePrefix = null;
+
+/**
+ * Name of the model.
+ *
+ * @var string
+ * @access public
+ * @link http://book.cakephp.org/view/1057/Model-Attributes#name-1068
+ */
+	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 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/1057/Model-Attributes#cacheQueries-1069
+ */
+	var $cacheQueries = false;
+
+/**
+ * Detailed list of belongsTo associations.
+ *
+ * @var array
+ * @access public
+ * @link http://book.cakephp.org/view/1042/belongsTo
+ */
+	var $belongsTo = array();
+
+/**
+ * Detailed list of hasOne associations.
+ *
+ * @var array
+ * @access public
+ * @link http://book.cakephp.org/view/1041/hasOne
+ */
+	var $hasOne = array();
+
+/**
+ * Detailed list of hasMany associations.
+ *
+ * @var array
+ * @access public
+ * @link http://book.cakephp.org/view/1043/hasMany
+ */
+	var $hasMany = array();
+
+/**
+ * Detailed list of hasAndBelongsToMany associations.
+ *
+ * @var array
+ * @access public
+ * @link http://book.cakephp.org/view/1044/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/1072/Using-Behaviors
+ */
+	var $actsAs = null;
+
+/**
+ * Holds the Behavior objects currently bound to this model.
+ *
+ * @var BehaviorCollection
+ * @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/1057/Model-Attributes#recursive-1063
+ */
+	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/1057/Model-Attributes#order-1064
+ */
+	var $order = null;
+
+/**
+ * Array of virtual fields this model has.  Virtual fields are aliased
+ * SQL expressions. Fields added to this property will be read as other fields in a model
+ * but will not be saveable.
+ *
+ * `var $virtualFields = array('two' => '1 + 1');`
+ *
+ * Is a simplistic example of how to set virtualFields
+ *
+ * @var array
+ * @access public
+ */
+	var $virtualFields = array();
+
+/**
+ * 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.
+ *
+ * If `$id` is an array it can be used to pass several options into the model.
+ *
+ * - id - The id to start the model on.
+ * - table - The table to use for this model.
+ * - ds - The connection name this model is connected to.
+ * - name - The name of the model eg. Post.
+ * - alias - The alias of the model, this is used for registering the instance in the `ClassRegistry`.
+ *   eg. `ParentThread`
+ *
+ * ### Overriding Model's __construct method.
+ *
+ * When overriding Model::__construct() be careful to include and pass in all 3 of the
+ * arguments to `parent::__construct($id, $table, $ds);`
+ *
+ * ### Dynamically creating models
+ *
+ * You can dynamically create model instances using the $id array syntax.
+ *
+ * {{{
+ * $Post = new Model(array('table' => 'posts', 'name' => 'Post', 'ds' => 'connection2'));
+ * }}}
+ *
+ * Would create a model attached to the posts table on connection2.  Dynamic model creation is useful
+ * when you want a model object that contains no associations or attached behaviors.
+ *
+ * @param mixed $id Set this ID for this model on startup, can also be an array of options, see above.
+ * @param string $table Name of database table to use.
+ * @param string $ds DataSource connection name.
+ */
+	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 ($ds !== null) {
+			$this->useDbConfig = $ds;
+		}
+
+		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);
+			}
+			$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 `$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:
+ *
+ * `$this->User->bindModel( array('hasOne' => array('Profile')) );`
+ *
+ * Bindings that are not made permanent will be reset by the next Model::find() call on this
+ * model.
+ *
+ * @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/1045/Creating-and-Destroying-Associations-on-the-Fly
+ */
+	function bindModel($params, $reset = true) {
+		foreach ($params as $assoc => $model) {
+			if ($reset === true && !isset($this->__backAssociation[$assoc])) {
+				$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;
+
+				if ($reset === false && isset($this->__backAssociation[$assoc])) {
+					$this->__backAssociation[$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:
+ *
+ * `$this->User->unbindModel( array('hasMany' => array('Supportrequest')) );`
+ *
+ * unbound models that are not made permanent will reset with the next call to Model::find()
+ *
+ * @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/1045/Creating-and-Destroying-Associations-on-the-Fly
+ */
+	function unbindModel($params, $reset = true) {
+		foreach ($params as $assoc => $models) {
+			if ($reset === true && !isset($this->__backAssociation[$assoc])) {
+				$this->__backAssociation[$assoc] = $this->{$assoc};
+			}
+			foreach ($models as $model) {
+				if ($reset === false && isset($this->__backAssociation[$assoc][$model])) {
+					unset($this->__backAssociation[$assoc][$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) = pluginSplit($assoc, true);
+							$this->{$type}[$assoc] = $value;
+						}
+					}
+					$className =  $assoc;
+
+					if (!empty($value['className'])) {
+						list($plugin, $className) = pluginSplit($value['className'], true);
+						$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 (strpos($className, '.') !== false) {
+				ClassRegistry::addObject($className, $this->{$assoc});
+			}
+			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 .= '.';
+					$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,
+					'code' => 500
+				)));
+			}
+			$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
+ * @link http://book.cakephp.org/view/1031/Saving-Your-Data
+ */
+	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) {
+		if (!is_array($data)) {
+			return $data;
+		}
+
+		$copy = $data;
+		$type = $this->getColumnType($field);
+
+		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');
+			$timeFields = array('H' => 'hour', 'i' => 'min', 's' => 'sec');
+
+			$db =& ConnectionManager::getDataSource($this->useDbConfig);
+			$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';
+			}
+			if ($type == 'time') {
+				foreach ($timeFields as $key => $val) {
+					if (!isset($data[$val]) || $data[$val] === '0' || $data[$val] === '00') {
+						$data[$val] = '00';
+					} elseif ($data[$val] === '') {
+						$data[$val] = '';
+					} else {
+						$data[$val] = sprintf('%02d', $data[$val]);
+					}
+					if (!empty($data[$val])) {
+						$date[$key] = $data[$val];
+					} else {
+						return null;
+					}
+				}
+			}
+
+			if ($type == 'datetime' || $type == 'timestamp' || $type == 'date') {
+				foreach ($dateFields as $key => $val) {
+					if ($val == 'hour' || $val == 'min' || $val == 'sec') {
+						if (!isset($data[$val]) || $data[$val] === '0' || $data[$val] === '00') {
+							$data[$val] = '00';
+						} else {
+							$data[$val] = sprintf('%02d', $data[$val]);
+						}
+					}
+					if (!isset($data[$val]) || isset($data[$val]) && (empty($data[$val]) || $data[$val][0] === '-')) {
+						return null;
+					}
+					if (isset($data[$val]) && !empty($data[$val])) {
+						$date[$key] = $data[$val];
+					}
+				}
+			}
+			$date = str_replace(array_keys($date), array_values($date), $format);
+			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
+ * @param boolean $checkVirtual checks if the field is declared as virtual
+ * @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, $checkVirtual = false) {
+		if (is_array($name)) {
+			foreach ($name as $n) {
+				if ($this->hasField($n, $checkVirtual)) {
+					return $n;
+				}
+			}
+			return false;
+		}
+
+		if ($checkVirtual && !empty($this->virtualFields)) {
+			if ($this->isVirtualField($name)) {
+				return true;
+			}
+		}
+
+		if (empty($this->_schema)) {
+			$this->schema();
+		}
+
+		if ($this->_schema != null) {
+			return isset($this->_schema[$name]);
+		}
+		return false;
+	}
+
+/**
+ * Returns true if the supplied field is a model Virtual Field
+ *
+ * @param mixed $name Name of field to look for
+ * @return boolean indicating whether the field exists as a model virtual field.
+ * @access public
+ */
+	function isVirtualField($field) {
+		if (empty($this->virtualFields) || !is_string($field)) {
+			return false;
+		}
+		if (isset($this->virtualFields[$field])) {
+			return true;
+		}
+		if (strpos($field, '.') !== false) {
+			list($model, $field) = explode('.', $field);
+			if ($model == $this->alias && isset($this->virtualFields[$field])) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+/**
+ * Returns the expression for a model virtual field
+ *
+ * @param mixed $name Name of field to look for
+ * @return mixed If $field is string expression bound to virtual field $field
+ *    If $field is null, returns an array of all model virtual fields
+ *    or false if none $field exist.
+ * @access public
+ */
+	function getVirtualField($field = null) {
+		if ($field == null) {
+			return empty($this->virtualFields) ? false : $this->virtualFields;
+		}
+		if ($this->isVirtualField($field)) {
+			if (strpos($field, '.') !== false) {
+				list($model, $field) = explode('.', $field);
+			}
+			return $this->virtualFields[$field];
+		}
+		return false;
+	}
+
+/**
+ * Initializes the model for writing a new record, loading the default values
+ * for those fields that are not defined in $data, and clearing previous validation errors.
+ * 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/1031/Saving-Your-Data
+ */
+	function create($data = array(), $filterKey = false) {
+		$defaults = array();
+		$this->id = false;
+		$this->data = array();
+		$this->validationErrors = array();
+
+		if ($data !== null && $data !== false) {
+			foreach ($this->schema() as $field => $properties) {
+				if ($this->primaryKey !== $field && isset($properties['default']) && $properties['default'] !== '') {
+					$defaults[$field] = $properties['default'];
+				}
+			}
+			$this->set($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
+ * @link http://book.cakephp.org/view/1017/Retrieving-Your-Data#read-1029
+ */
+	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('first', array(
+				'conditions' => array($this->alias . '.' . $this->primaryKey => $id),
+				'fields' => $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/1017/Retrieving-Your-Data#field-1028
+ */
+	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;
+		}
+		$fields = $name;
+		if ($data = $this->find('first', compact('conditions', 'fields', '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) {
+				return array_shift($data[0]);
+			}
+		} 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/1031/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/1031/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]);
+			}
+		}
+
+		$exists = $this->exists();
+		$dateFields = array('modified', 'updated');
+
+		if (!$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;
+			}
+		}
+
+		if (empty($this->data[$this->alias][$this->primaryKey])) {
+			unset($this->data[$this->alias][$this->primaryKey]);
+		}
+		$fields = $values = array();
+
+		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 (!$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 {
+				$fInfo = $this->_schema[$this->primaryKey];
+				$isUUID = ($fInfo['length'] == 36 &&
+					($fInfo['type'] === 'string' || $fInfo['type'] === 'binary')
+				);
+				if (empty($this->data[$this->alias][$this->primaryKey]) && $isUUID) {
+					if (array_key_exists($this->primaryKey, $this->data[$this->alias])) {
+						$j = array_search($this->primaryKey, $fields);
+						$values[$j] = String::uuid();
+					} else {
+						list($fields[], $values[]) = array($this->primaryKey, String::uuid());
+					}
+				}
+
+				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, $db);
+		}
+
+		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->_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) {
+		foreach ($joined as $assoc => $data) {
+
+			if (isset($this->hasAndBelongsToMany[$assoc])) {
+				list($join) = $this->joinModel($this->hasAndBelongsToMany[$assoc]['with']);
+
+				$isUUID = !empty($this->{$join}->primaryKey) && (
+						$this->{$join}->_schema[$this->{$join}->primaryKey]['length'] == 36 && (
+						$this->{$join}->_schema[$this->{$join}->primaryKey]['type'] === 'string' ||
+						$this->{$join}->_schema[$this->{$join}->primaryKey]['type'] === 'binary'
+					)
+				);
+
+				$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 = implode(',', $values);
+						$newValues[] = "({$values})";
+						unset($values);
+					} elseif (isset($row[$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) {
+						$newData[] = $row;
+					} elseif (isset($row[$join]) && isset($row[$join][$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) {
+						$newData[] = $row[$join];
+					}
+				}
+
+				if ($this->hasAndBelongsToMany[$assoc]['unique']) {
+					$conditions = array(
+						$join . '.' . $this->hasAndBelongsToMany[$assoc]['foreignKey'] => $id
+					);
+					if (!empty($this->hasAndBelongsToMany[$assoc]['conditions'])) {
+						$conditions = array_merge($conditions, (array)$this->hasAndBelongsToMany[$assoc]['conditions']);
+					}
+					$links = $this->{$join}->find('all', array(
+						'conditions' => $conditions,
+						'recursive' => empty($this->hasAndBelongsToMany[$assoc]['conditions']) ? -1 : 0,
+						'fields' => $this->hasAndBelongsToMany[$assoc]['associationForeignKey']
+					));
+
+					$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 = implode(',', $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]) {
+						$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($this->primaryKey => $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.
+ *
+ * #### Options
+ *
+ * - validate: Set to false to disable validation, true to validate each record before saving,
+ *   'first' to validate *all* records before any are saved (default),
+ *   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.
+ * - fieldList: Equivalent to the $fieldList parameter in Model::save()
+ *
+ * @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, See $options above.
+ * @return mixed If atomic: True on success, or false on failure.
+ *    Otherwise: array similar to the $data array passed, but values are set to true/false
+ *    depending on whether each record saved successfully.
+ * @access public
+ * @link http://book.cakephp.org/view/1032/Saving-Related-Model-Data-hasOne-hasMany-belongsTo
+ * @link http://book.cakephp.org/view/1031/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' => 'first', 'atomic' => true), $options);
+		$this->validationErrors = $validationErrors = array();
+		$validates = true;
+		$return = array();
+
+		if (empty($data) && $options['validate'] !== false) {
+			$result = $this->save($data, $options);
+			return !empty($result);
+		}
+
+		if ($options['atomic'] && $options['validate'] !== 'only') {
+			$transactionBegun = $db->begin($this);
+		}
+
+		if (Set::numeric(array_keys($data))) {
+			while ($validates) {
+				$return = array();
+				foreach ($data as $key => $record) {
+					if (!$currentValidates = $this->__save($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;
+					break;
+					default:
+						if ($options['atomic']) {
+							if ($validates) {
+								if ($transactionBegun) {
+									return $db->commit($this) !== false;
+								} else {
+									return true;
+								}
+							}
+							$db->rollback($this);
+							return false;
+						}
+						return $return;
+					break;
+				}
+			}
+			if ($options['atomic'] && !$validates) {
+				$db->rollback($this);
+				return false;
+			}
+			return $return;
+		}
+		$associations = $this->getAssociated();
+
+		while ($validates) {
+			foreach ($data as $association => $values) {
+				if (isset($associations[$association])) {
+					switch ($associations[$association]) {
+						case 'belongsTo':
+							if ($this->{$association}->__save($values, $options)) {
+								$data[$this->alias][$this->belongsTo[$association]['foreignKey']] = $this->{$association}->id;
+							} else {
+								$validationErrors[$association] = $this->{$association}->validationErrors;
+								$validates = false;
+							}
+							if (!$options['atomic']) {
+								$return[$association][] = $validates;
+							}
+						break;
+					}
+				}
+			}
+
+			if (!$this->__save($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':
+							if (!$validating) {
+								$values[$this->{$type}[$association]['foreignKey']] = $this->id;
+							}
+
+							if (!$this->{$association}->__save($values, $options)) {
+								$validationErrors[$association] = $this->{$association}->validationErrors;
+								$validates = false;
+							}
+							if (!$options['atomic']) {
+								$return[$association][] = $validates;
+							}
+						break;
+						case 'hasMany':
+							if (!$validating) {
+								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;
+					$return = array();
+				break;
+				default:
+					if ($options['atomic']) {
+						if ($validates) {
+							if ($transactionBegun) {
+								return $db->commit($this) !== false;
+							} else {
+								return true;
+							}
+						} else {
+							$db->rollback($this);
+						}
+					}
+					return $return;
+				break;
+			}
+			if ($options['atomic'] && !$validates) {
+				$db->rollback($this);
+				return false;
+			}
+		}
+	}
+
+/**
+ * Private helper method used by saveAll.
+ *
+ * @return boolean Success
+ * @access private
+ * @see Model::saveAll()
+ */
+	function __save($data, $options) {
+		if ($options['validate'] === 'first' || $options['validate'] === 'only') {
+			if (!($this->create($data) && $this->validates($options))) {
+				return false;
+			}
+		} elseif (!($this->create(null) !== null && $this->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/1031/Saving-Your-Data
+ */
+	function updateAll($fields, $conditions = true) {
+		$db =& ConnectionManager::getDataSource($this->useDbConfig);
+		return $db->update($this, $fields, null, $conditions);
+	}
+
+/**
+ * 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/1036/delete
+ */
+	function delete($id = null, $cascade = true) {
+		if (!empty($id)) {
+			$this->id = $id;
+		}
+		$id = $this->id;
+
+		if ($this->beforeDelete($cascade)) {
+			$filters = $this->Behaviors->trigger($this, 'beforeDelete', array($cascade), array(
+				'break' => true, 'breakOn' => false
+			));
+			if (!$filters || !$this->exists()) {
+				return false;
+			}
+			$db =& ConnectionManager::getDataSource($this->useDbConfig);
+
+			$this->_deleteDependent($id, $cascade);
+			$this->_deleteLinks($id);
+			$this->id = $id;
+
+			if (!empty($this->belongsTo)) {
+				$keys = $this->find('first', array(
+					'fields' => $this->__collectForeignKeys(),
+					'conditions' => array($this->alias . '.' . $this->primaryKey => $id)
+				));
+			}
+
+			if ($db->delete($this, array($this->alias . '.' . $this->primaryKey => $id))) {
+				if (!empty($this->belongsTo)) {
+					$this->updateCounterCache($keys[$this->alias]);
+				}
+				$this->Behaviors->trigger($this, 'afterDelete');
+				$this->afterDelete();
+				$this->_clearCache();
+				$this->id = false;
+				return true;
+			}
+		}
+		return false;
+	}
+
+/**
+ * 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();
+		}
+		if ($cascade === true) {
+			foreach (array_merge($this->hasMany, $this->hasOne) as $assoc => $data) {
+				if ($data['dependent'] === true) {
+
+					$model =& $this->{$assoc};
+					if ($data['foreignKey'] === false && $data['conditions'] && in_array($this->name, $model->getAssociated('belongsTo'))) {
+						$model->recursive = 0;
+						$conditions = array($this->escapeField(null, $this->name) => $id);
+					} else {
+						$model->recursive = -1;
+						$conditions = array($model->escapeField($data['foreignKey']) => $id);
+						if ($data['conditions']) {
+							$conditions = array_merge((array)$data['conditions'], $conditions);
+						}
+					}
+
+					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) {
+		foreach ($this->hasAndBelongsToMany as $assoc => $data) {
+			$joinModel = $data['with'];
+			$records = $this->{$joinModel}->find('all', array(
+				'conditions' => array_merge(array($this->{$joinModel}->escapeField($data['foreignKey']) => $id)),
+				'fields' => $this->{$joinModel}->primaryKey,
+				'recursive' => -1
+			));
+			if (!empty($records)) {
+				foreach ($records as $record) {
+					$this->{$joinModel}->delete($record[$this->{$joinModel}->alias][$this->{$joinModel}->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
+ * @return boolean True on success, false on failure
+ * @access public
+ * @link http://book.cakephp.org/view/1038/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 = $this->find('all', array_merge(array(
+				'fields' => "{$this->alias}.{$this->primaryKey}",
+				'recursive' => 0), compact('conditions'))
+			);
+			if ($ids === false) {
+				return false;
+			}
+
+			$ids = Set::extract($ids, "{n}.{$this->alias}.{$this->primaryKey}");
+			if (empty($ids)) {
+				return true;
+			}
+
+			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.
+ *
+ * Internally calls Model::getID() to obtain the current record ID to verify,
+ * and then performs a Model::find('count') on the currently configured datasource
+ * to ascertain the existence of the record in persistent storage.
+ *
+ * @return boolean True if such a record exists
+ * @access public
+ */
+	function exists() {
+		if ($this->getID() === false) {
+			return false;
+		}
+		$conditions = array($this->alias . '.' . $this->primaryKey => $this->getID());
+		$query = array('conditions' => $conditions, 'recursive' => -1, 'callbacks' => false);
+		return ($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);
+	}
+
+/**
+ * Queries the datasource and 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'
+ * ));
+ * }}}
+ *
+ * In addition to the standard query keys above, you can provide Datasource, and behavior specific
+ * keys.  For example, when using a SQL based datasource you can use the joins key to specify additional
+ * joins that should be part of the query.
+ *
+ * {{{
+ * find('all', array(
+ * 		'conditions' => array('name' => 'Thomas Anderson'),
+ * 		'joins' => array(
+ *			array(
+ * 				'alias' => 'Thought',
+ * 				'table' => 'thoughts',
+ * 				'type' => 'LEFT',
+ * 				'conditions' => '`Thought`.`person_id` = `Person`.`id`'
+ *			)
+ * 		)
+ * ));
+ * }}}
+ *
+ * Behaviors and find types can also define custom finder keys which are passed into find().
+ *
+ * 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.
+ *
+ *  Note: find(list) + database views have issues with MySQL 5.0. Try upgrading to MySQL 5.1 if you
+ *  have issues with database views.
+ *
+ * @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/1018/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);
+		}
+
+		$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;
+			}
+		}
+
+		if (!$db =& ConnectionManager::getDataSource($this->useDbConfig)) {
+			return false;
+		}
+		$results = $db->read($this, $query);
+		$this->resetAssociations();
+
+		if ($query['callbacks'] === true || $query['callbacks'] === 'after') {
+			$results = $this->__filterResults($results);
+		}
+
+		$this->findQueryType = null;
+
+		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;
+			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);
+	}
+
+/**
+ * This resets the association arrays for the model back
+ * to those originally defined in the model. Normally called at the end
+ * of each call to Model::find()
+ *
+ * @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/1027/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. Will validate hasAndBelongsToMany associations
+ * that use the 'with' key as well. Since __saveMulti is incapable of exiting a save operation.
+ *
+ * Will validate the currently set data.  Use Model::set() or Model::create() to set the active data.
+ *
+ * @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/1182/Validating-Data-from-the-Controller
+ */
+	function validates($options = array()) {
+		$errors = $this->invalidFields($options);
+		if (empty($errors) && $errors !== false) {
+			$errors = $this->__validateWithModels($options);
+		}
+		if (is_array($errors)) {
+			return count($errors) === 0;
+		}
+		return $errors;
+	}
+
+/**
+ * Returns an array of fields that have failed validation. On the current model.
+ *
+ * @param string $options An optional array of custom options to be made available in the beforeValidate callback
+ * @return array Array of invalid fields
+ * @see Model::validates()
+ * @access public
+ * @link http://book.cakephp.org/view/1182/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 false;
+		}
+
+		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();
+		$exists = null;
+
+		$_validate = $this->validate;
+		$whitelist = $this->whitelist;
+
+		if (!empty($options['fieldList'])) {
+			$whitelist = $options['fieldList'];
+		}
+
+		if (!empty($whitelist)) {
+			$validate = array();
+			foreach ((array)$whitelist 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'])) {
+					if ($exists === null) {
+						$exists = $this->exists();
+					}
+					if (($validator['on'] == 'create' && $exists) || ($validator['on'] == 'update' && !$exists)) {
+						continue;
+					}
+				}
+
+				$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]);
+					} elseif (Configure::read('debug') > 0) {
+						trigger_error(sprintf(__('Could not find validation handler %s for %s', true), $rule, $fieldName), E_USER_WARNING);
+					}
+
+					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;
+	}
+
+/**
+ * Runs validation for hasAndBelongsToMany associations that have 'with' keys
+ * set. And data in the set() data set.
+ *
+ * @param array $options Array of options to use on Valdation of with models
+ * @return boolean Failure of validation on with models.
+ * @access private
+ * @see Model::validates()
+ */
+	function __validateWithModels($options) {
+		$valid = true;
+		foreach ($this->hasAndBelongsToMany as $assoc => $association) {
+			if (empty($association['with']) || !isset($this->data[$assoc])) {
+				continue;
+			}
+			list($join) = $this->joinModel($this->hasAndBelongsToMany[$assoc]['with']);
+			$data = $this->data[$assoc];
+
+			$newData = array();
+			foreach ((array)$data as $row) {
+				if (isset($row[$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) {
+					$newData[] = $row;
+				} elseif (isset($row[$join]) && isset($row[$join][$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) {
+					$newData[] = $row[$join];
+				}
+			}
+			if (empty($newData)) {
+				continue;
+			}
+			foreach ($newData as $data) {
+				$data[$this->hasAndBelongsToMany[$assoc]['foreignKey']] = $this->id;
+				$this->{$join}->create($data);
+				$valid = ($valid && $this->{$join}->validates($options));
+			}
+		}
+		return $valid;
+	}
+/**
+ * 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, or validation message to
+ *    be returned. 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);
+	}
+
+/**
+ * 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) || !is_object($db)) {
+			return $this->cakeError('missingConnection', array(array('code' => 500, '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)));
+		}
+		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/1048/Callback-Methods#beforeFind-1049
+ */
+	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/1048/Callback-Methods#afterFind-1050
+ */
+	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/1048/Callback-Methods#beforeSave-1052
+ */
+	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/1048/Callback-Methods#afterSave-1053
+ */
+	function afterSave($created) {
+	}
+
+/**
+ * Called before 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/1048/Callback-Methods#beforeDelete-1054
+ */
+	function beforeDelete($cascade = true) {
+		return true;
+	}
+
+/**
+ * Called after every deletion operation.
+ *
+ * @access public
+ * @link http://book.cakephp.org/view/1048/Callback-Methods#afterDelete-1055
+ */
+	function afterDelete() {
+	}
+
+/**
+ * Called during validation 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/1048/Callback-Methods#beforeValidate-1051
+ */
+	function beforeValidate($options = array()) {
+		return true;
+	}
+
+/**
+ * Called when a DataSource-level error occurs.
+ *
+ * @access public
+ * @link http://book.cakephp.org/view/1048/Callback-Methods#onError-1056
+ */
+	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() {
+	}
+}
+if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) {
+	Overloadable::overload('Model');
+}

Added: trunk/src/Web/cake/libs/model/model_behavior.php
===================================================================
--- trunk/src/Web/cake/libs/model/model_behavior.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/model/model_behavior.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,533 @@
+<?php
+/**
+ * Model behaviors base class.
+ *
+ * Adds methods and automagic functionality to Cake Models.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.model
+ * @since         CakePHP(tm) v 1.2.0.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 mixed False if the operation should abort. An array will replace the value of $query.
+ * @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 An array value will replace the value of $results - any other value will be ignored.
+ * @access public
+ */
+	function afterFind(&$model, $results, $primary) { }
+
+/**
+ * Before validate callback
+ *
+ * @param object $model Model using this behavior
+ * @return mixed False if the operation should abort. Any other result will continue.
+ * @access public
+ */
+	function beforeValidate(&$model) { }
+
+/**
+ * Before save callback
+ *
+ * @param object $model Model using this behavior
+ * @return mixed False if the operation should abort. Any other result will continue.
+ * @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 mixed False if the operation should abort. Any other result will continue.
+ * @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:
+				$params = array_merge(array(&$model), $params);
+				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 string
+ * @access public
+ */
+	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()) {
+		list($plugin, $name) = pluginSplit($behavior);
+		$class = $name . 'Behavior';
+
+		if (!App::import('Behavior', $behavior)) {
+			$this->cakeError('missingBehaviorFile', array(array(
+				'behavior' => $behavior,
+				'file' => Inflector::underscore($behavior) . '.php',
+				'code' => 500,
+				'base' => '/'
+			)));
+			return false;
+		}
+		if (!class_exists($class)) {
+			$this->cakeError('missingBehaviorClass', array(array(
+				'behavior' => $class,
+				'file' => Inflector::underscore($class) . '.php',
+				'code' => 500,
+				'base' => '/'
+			)));
+			return false;
+		}
+
+		if (!isset($this->{$name})) {
+			if (ClassRegistry::isKeySet($class)) {
+				if (PHP5) {
+					$this->{$name} = ClassRegistry::getObject($class);
+				} else {
+					$this->{$name} =& ClassRegistry::getObject($class);
+				}
+			} else {
+				if (PHP5) {
+					$this->{$name} = new $class;
+				} else {
+					$this->{$name} =& new $class;
+				}
+				ClassRegistry::addObject($class, $this->{$name});
+				if (!empty($plugin)) {
+					ClassRegistry::addObject($plugin.'.'.$class, $this->{$name});
+				}
+			}
+		} elseif (isset($this->{$name}->settings) && isset($this->{$name}->settings[$this->modelName])) {
+			if ($config !== null && $config !== false) {
+				$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', 'cleanup', 'beforeFind', 'afterFind', 'beforeSave', 'afterSave',
+			'beforeDelete', 'afterDelete', 'afterError'
+		);
+
+		foreach ($methods as $m) {
+			if (!isset($parentMethods[$m])) {
+				$methodAllowed = (
+					$m[0] != '_' && !array_key_exists($m, $this->__methods) &&
+					!in_array($m, $callbacks)
+				);
+				if ($methodAllowed) {
+					$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) {
+		list($plugin, $name) = pluginSplit($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(sprintf(__("BehaviorCollection::dispatchMethod() - Method %s not found in any attached behavior", true), $method), 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;
+		}
+		$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;
+	}
+}

Added: trunk/src/Web/cake/libs/multibyte.php
===================================================================
--- trunk/src/Web/cake/libs/multibyte.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/multibyte.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,1172 @@
+<?php
+/**
+ * Multibyte handling methods.
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.0.6833
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+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 (empty($values)) {
+					$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, $count = count($keys[$key]['lower']); $ii < $count; $ii++) {
+								$nextChar = $utf8Map[$i + $ii];
+
+								if (isset($nextChar) && ($nextChar == $keys[$key]['lower'][$j + $ii])) {
+									$replace++;
+								}
+							}
+							if ($replace == $count) {
+								$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, $count = count($keys[$ii]['lower']); $jj < $count; $jj++) {
+										$nextChar = $utf8Map[$i + $jj];
+
+										if (isset($nextChar) && ($nextChar == $keys[$ii]['lower'][$j + $jj])) {
+											$replace++;
+										}
+									}
+									if ($replace == $count) {
+										$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 = (int)$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;
+	}
+}

Added: trunk/src/Web/cake/libs/object.php
===================================================================
--- trunk/src/Web/cake/libs/object.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/object.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,298 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 {
+
+/**
+ * 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. Can be used to connect controllers together
+ * or tie plugins into a main application. requestAction can be used to return rendered views
+ * or fetch the return value from controller actions.
+ *
+ * @param mixed $url String or array-based url.
+ * @param array $extra if array includes the key "return" it sets the AutoRender to true.
+ * @return mixed Boolean true or false on success/failure, or contents
+ *    of rendered action 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`
+ *
+ * @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.  Wraps exit() making 
+ * testing easier.
+ *
+ * @param $status see http://php.net/exit for values
+ * @return void
+ * @access public
+ */
+	function _stop($status = 0) {
+		exit($status);
+	}
+
+/**
+ * Convience method to write a message to CakeLog.  See CakeLog::write()
+ * for more information on writing to logs.
+ *
+ * @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')) {
+			require LIBS . 'cake_log.php';
+		}
+		if (!is_string($msg)) {
+			$msg = print_r($msg, true);
+		}
+		return CakeLog::write($type, $msg);
+	}
+
+/**
+ * Allows setting of multiple properties of the object in a single line of code.  Will only set 
+ * properties that are part of a class declaration.
+ *
+ * @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) . '\' ?>';
+		$duration = '+999 days';
+		if (Configure::read() >= 1) {
+			$duration = '+10 seconds';
+		}
+		cache($file, $data, $duration);
+	}
+
+/**
+ * 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) {
+					if (strpos($key, '_behavior') !== false) {
+						App::import('Behavior', Inflector::classify(substr($key, 0, -9)));
+					} else {
+						App::import('Model', Inflector::camelize($key));
+					}
+					unset ($value);
+				}
+				unset($vars);
+				$vars = unserialize(${$name});
+				foreach ($vars['0'] as $key => $value) {
+					ClassRegistry::addObject($key, $value);
+					unset ($value);
+				}
+				unset($vars);
+			break;
+			default:
+				$vars = unserialize(${$name});
+				$this->{$name} = $vars['0'];
+				unset($vars);
+			break;
+		}
+	}
+}

Added: trunk/src/Web/cake/libs/overloadable.php
===================================================================
--- trunk/src/Web/cake/libs/overloadable.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/overloadable.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Overload abstraction interface.  Merges differences between PHP4 and 5.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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');
+}

Added: trunk/src/Web/cake/libs/overloadable_php4.php
===================================================================
--- trunk/src/Web/cake/libs/overloadable_php4.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/overloadable_php4.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,165 @@
+<?php
+/**
+ * Overload abstraction interface.  Merges differences between PHP4 and 5.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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');

Added: trunk/src/Web/cake/libs/overloadable_php5.php
===================================================================
--- trunk/src/Web/cake/libs/overloadable_php5.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/overloadable_php5.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,108 @@
+<?php
+/**
+ * Overload abstraction interface.  Merges differences between PHP4 and 5.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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);
+	}
+}

Added: trunk/src/Web/cake/libs/router.php
===================================================================
--- trunk/src/Web/cake/libs/router.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/router.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,1656 @@
+<?php
+/**
+ * Parses the request URL into controller, action, and parameters.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Parses the request URL into controller, action, and parameters.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class Router {
+
+/**
+ * Array of routes connected with Router::connect()
+ *
+ * @var array
+ * @access public
+ */
+	var $routes = array();
+
+/**
+ * List of action prefixes used in connected routes.
+ * Includes admin prefix
+ *
+ * @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();
+
+/**
+ * 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;
+
+/**
+ * Keeps track of whether the connection of default routes is enabled or disabled.
+ *
+ * @var boolean
+ * @access private
+ */
+	var $__connectDefaults = true;
+
+/**
+ * Constructor for Router.
+ * Builds __prefixes
+ *
+ * @return void
+ */
+	function Router() {
+		$this->__setPrefixes();
+	}
+
+/**
+ * Sets the Routing prefixes. Includes compatibility for existing Routing.admin
+ * configurations.
+ *
+ * @return void
+ * @access private
+ * @todo Remove support for Routing.admin in future versions.
+ */
+	function __setPrefixes() {
+		$routing = Configure::read('Routing');
+		if (!empty($routing['admin'])) {
+			$this->__prefixes[] = $routing['admin'];
+		}
+		if (!empty($routing['prefixes'])) {
+			$this->__prefixes = array_merge($this->__prefixes, (array)$routing['prefixes']);
+		}
+	}
+
+/**
+ * Gets a reference to the Router object instance
+ *
+ * @return Router Instance of the Router.
+ * @access public
+ * @static
+ */
+	function &getInstance() {
+		static $instance = array();
+
+		if (!$instance) {
+			$instance[0] =& new Router();
+		}
+		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() {
+		$self =& Router::getInstance();
+		return $self->__named;
+	}
+
+/**
+ * Connects a new Route in the router.
+ *
+ * Routes are a way of connecting request urls to objects in your application.  At their core routes
+ * are a set or regular expressions that are used to match requests to destinations.
+ *
+ * Examples:
+ *
+ * `Router::connect('/:controller/:action/*');`
+ *
+ * The first parameter will be used as a controller name while the second is used as the action name.
+ * the '/*' syntax makes this route greedy in that it will match requests like `/posts/index` as well as requests
+ * like `/posts/edit/1/foo/bar`.
+ *
+ * `Router::connect('/home-page', array('controller' => 'pages', 'action' => 'display', 'home'));`
+ *
+ * The above shows the use of route parameter defaults. And providing routing parameters for a static route.
+ *
+ * {{{
+ * Router::connect(
+ *   '/:lang/:controller/:action/:id',
+ *   array(),
+ *   array('id' => '[0-9]+', 'lang' => '[a-z]{3}')
+ * );
+ * }}}
+ *
+ * Shows connecting a route with custom route parameters as well as providing patterns for those parameters.
+ * Patterns for routing parameters do not need capturing groups, as one will be added for each route params.
+ *
+ * $options offers three 'special' keys. `pass`, `persist` and `routeClass` have special meaning in the $options array.
+ *
+ * `pass` is used to define which of the routed parameters should be shifted into the pass array.  Adding a
+ * parameter to pass will remove it from the regular route array. Ex. `'pass' => array('slug')`
+ *
+ * `persist` is used to define which route parameters should be automatically included when generating
+ * new urls. You can override persistent parameters by redefining them in a url or remove them by
+ * setting the parameter to `false`.  Ex. `'persist' => array('lang')`
+ *
+ * `routeClass` is used to extend and change how individual routes parse requests and handle reverse routing,
+ * via a custom routing class. Ex. `'routeClass' => 'SlugRoute'`
+ *
+ * @param string $route A string describing the template of the route
+ * @param array $defaults An array describing the default route parameters. These parameters will be used by default
+ *   and can supply routing parameters that are not dynamic. See above.
+ * @param array $options An array matching the named elements in the route to regular expressions which that
+ *   element should match.  Also contains additional parameters such as which routed parameters should be
+ *   shifted into the passed arguments, supplying patterns for routing parameters and supplying the name of a
+ *   custom routing class.
+ * @see routes
+ * @return array Array of routes
+ * @access public
+ * @static
+ */
+	function connect($route, $defaults = array(), $options = array()) {
+		$self =& Router::getInstance();
+
+		foreach ($self->__prefixes as $prefix) {
+			if (isset($defaults[$prefix])) {
+				$defaults['prefix'] = $prefix;
+				break;
+			}
+		}
+		if (isset($defaults['prefix'])) {
+			$self->__prefixes[] = $defaults['prefix'];
+			$self->__prefixes = array_keys(array_flip($self->__prefixes));
+		}
+		$defaults += array('plugin' => null);
+		if (empty($options['action'])) {
+			$defaults += array('action' => 'index'); 
+		}
+		$routeClass = 'CakeRoute';
+		if (isset($options['routeClass'])) {
+			$routeClass = $options['routeClass'];
+			unset($options['routeClass']);
+		}
+		//TODO 2.0 refactor this to use a string class name, throw exception, and then construct.
+		$Route =& new $routeClass($route, $defaults, $options);
+		if ($routeClass !== 'CakeRoute' && !is_subclass_of($Route, 'CakeRoute')) {
+			trigger_error(__('Route classes must extend CakeRoute', true), E_USER_WARNING);
+			return false;
+		}
+		$self->routes[] =& $Route;
+		return $self->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()) {
+		$self =& Router::getInstance();
+
+		if (isset($options['argSeparator'])) {
+			$self->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();
+		} else {
+			$options = array_merge(array('default' => false, 'reset' => false, 'greedy' => true), $options);
+		}
+
+		if ($options['reset'] == true || $self->named['rules'] === false) {
+			$self->named['rules'] = array();
+		}
+
+		if ($options['default']) {
+			$named = array_merge($named, $self->named['default']);
+		}
+
+		foreach ($named as $key => $val) {
+			if (is_numeric($key)) {
+				$self->named['rules'][$val] = true;
+			} else {
+				$self->named['rules'][$key] = $val;
+			}
+		}
+		$self->named['greedy'] = $options['greedy'];
+		return $self->named;
+	}
+
+/**
+ * Tell router to connect or not connect the default routes.
+ *
+ * If default routes are disabled all automatic route generation will be disabled
+ * and you will need to manually configure all the routes you want.
+ *
+ * @param boolean $connect Set to true or false depending on whether you want or don't want default routes.
+ * @return void
+ * @access public
+ * @static
+ */
+	function defaults($connect = true) {
+		$self =& Router::getInstance();
+		$self->__connectDefaults = $connect;
+	}
+
+/**
+ * Creates REST resource routes for the given controller(s)
+ *
+ * ### Options:
+ *
+ * - '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 '/'.
+ *
+ * @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
+ * @return void
+ * @access public
+ * @static
+ */
+	function mapResources($controller, $options = array()) {
+		$self =& Router::getInstance();
+		$options = array_merge(array('prefix' => '/', 'id' => $self->__named['ID'] . '|' . $self->__named['UUID']), $options);
+		$prefix = $options['prefix'];
+
+		foreach ((array)$controller as $ctlName) {
+			$urlName = Inflector::underscore($ctlName);
+
+			foreach ($self->__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'))
+				);
+			}
+			$self->__resourceMapped[] = $urlName;
+		}
+	}
+
+/**
+ * Returns the list of prefixes used in connected routes
+ *
+ * @return array A list of prefixes used in connected routes
+ * @access public
+ * @static
+ */
+	function prefixes() {
+		$self =& Router::getInstance();
+		return $self->__prefixes;
+	}
+
+/**
+ * Parses given URL and returns an array of controller, 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) {
+		$self =& Router::getInstance();
+		if (!$self->__defaultsMapped && $self->__connectDefaults) {
+			$self->__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($self->__parseExtension($url));
+
+		for ($i = 0, $len = count($self->routes); $i < $len; $i++) {
+			$route =& $self->routes[$i];
+			if (($r = $route->parse($url)) !== false) {
+				$self->__currentRoute[] =& $route;
+
+				$params = $route->options;
+				$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']);
+				}
+				$out = $r;
+
+				if (isset($out['_args_'])) {
+					$argOptions['context'] = array('action' => $out['action'], 'controller' => $out['controller']);
+					$parsedArgs = $self->getArgs($out['_args_'], $argOptions);
+					$out['pass'] = array_merge($out['pass'], $parsedArgs['pass']);
+					$out['named'] = $parsedArgs['named'];
+					unset($out['_args_']);
+				}
+
+				if (isset($params['pass'])) {
+					$j = count($params['pass']);
+					while($j--) {
+						if (isset($out[$params['pass'][$j]])) {
+							array_unshift($out['pass'], $out[$params['pass'][$j]]);
+						}
+					}
+				}
+				break;
+			}
+		}
+
+		if (!empty($ext) && !isset($out['url']['ext'])) {
+			$out['url']['ext'] = $ext;
+		}
+		return $out;
+	}
+
+/**
+ * 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;
+							break;
+						}
+					}
+				}
+			}
+			if (empty($ext)) {
+				$ext = 'html';
+			}
+		}
+		return compact('ext', 'url');
+	}
+
+/**
+ * Connects the default, built-in routes, including prefix and plugin routes. The following routes are created
+ * in the order below:
+ *
+ * For each of the Routing.prefixes the following routes are created. Routes containing `:plugin` are only
+ * created when your application has one or more plugins.
+ *
+ * - `/:prefix/:plugin` a plugin shortcut route.
+ * - `/:prefix/:plugin/:action/*` a plugin shortcut route.
+ * - `/:prefix/:plugin/:controller`
+ * - `/:prefix/:plugin/:controller/:action/*`
+ * - `/:prefix/:controller`
+ * - `/:prefix/:controller/:action/*`
+ *
+ * If plugins are found in your application the following routes are created:
+ *
+ * - `/:plugin` a plugin shortcut route.
+ * - `/:plugin/:action/*` a plugin shortcut route.
+ * - `/:plugin/:controller`
+ * - `/:plugin/:controller/:action/*`
+ *
+ * And lastly the following catch-all routes are connected.
+ *
+ * - `/:controller'
+ * - `/:controller/:action/*'
+ *
+ * You can disable the connection of default routes with Router::defaults().
+ *
+ * @return void
+ * @access private
+ */
+	function __connectDefaultRoutes() {
+		if ($plugins = App::objects('plugin')) {
+			foreach ($plugins as $key => $value) {
+				$plugins[$key] = Inflector::underscore($value);
+			}
+			$pluginPattern = implode('|', $plugins);
+			$match = array('plugin' => $pluginPattern);
+			$shortParams = array('routeClass' => 'PluginShortRoute', 'plugin' => $pluginPattern);
+
+			foreach ($this->__prefixes as $prefix) {
+				$params = array('prefix' => $prefix, $prefix => true);
+				$indexParams = $params + array('action' => 'index');
+				$this->connect("/{$prefix}/:plugin", $indexParams, $shortParams);
+				$this->connect("/{$prefix}/:plugin/:controller", $indexParams, $match);
+				$this->connect("/{$prefix}/:plugin/:controller/:action/*", $params, $match);
+			}
+			$this->connect('/:plugin', array('action' => 'index'), $shortParams);
+			$this->connect('/:plugin/:controller', array('action' => 'index'), $match);
+			$this->connect('/:plugin/:controller/:action/*', array(), $match);
+		}
+
+		foreach ($this->__prefixes as $prefix) {
+			$params = array('prefix' => $prefix, $prefix => true);
+			$indexParams = $params + array('action' => 'index');
+			$this->connect("/{$prefix}/:controller", $indexParams);
+			$this->connect("/{$prefix}/: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, sets these
+ * parameters as the current request parameters that are merged with url arrays 
+ * created later in the request.
+ *
+ * @param array $params Parameters and path information
+ * @return void
+ * @access public
+ * @static
+ */
+	function setRequestInfo($params) {
+		$self =& 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($self->__params[], $self->__paths[]) = $params;
+
+		if (count($self->__paths)) {
+			if (isset($self->__paths[0]['namedArgs'])) {
+				foreach ($self->__paths[0]['namedArgs'] as $arg => $value) {
+					$self->named['rules'][$arg] = true;
+				}
+			}
+		}
+	}
+
+/**
+ * Gets parameter information
+ *
+ * @param boolean $current Get current request parameter, useful when using requestAction
+ * @return array Parameter information
+ * @access public
+ * @static
+ */
+	function getParams($current = false) {
+		$self =& Router::getInstance();
+		if ($current) {
+			return $self->__params[count($self->__params) - 1];
+		}
+		if (isset($self->__params[0])) {
+			return $self->__params[0];
+		}
+		return array();
+	}
+
+/**
+ * Gets URL parameter by name
+ *
+ * @param string $name Parameter name
+ * @param boolean $current Current parameter, useful when using requestAction
+ * @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, useful when using requestAction
+ * @return array
+ * @access public
+ * @static
+ */
+	function getPaths($current = false) {
+		$self =& Router::getInstance();
+		if ($current) {
+			return $self->__paths[count($self->__paths) - 1];
+		}
+		if (!isset($self->__paths[0])) {
+			return array('base' => null);
+		}
+		return $self->__paths[0];
+	}
+
+/**
+ * Reloads default Router settings.  Resets all class variables and 
+ * removes all connected routes.
+ *
+ * @access public
+ * @return void
+ * @static
+ */
+	function reload() {
+		$self =& Router::getInstance();
+		foreach (get_class_vars('Router') as $key => $val) {
+			$self->{$key} = $val;
+		}
+		$self->__setPrefixes();
+	}
+
+/**
+ * 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 Returns false if no route exists at the position specified by $which.
+ * @access public
+ * @static
+ */
+	function promote($which = null) {
+		$self =& Router::getInstance();
+		if ($which === null) {
+			$which = count($self->routes) - 1;
+		}
+		if (!isset($self->routes[$which])) {
+			return false;
+		}
+		$route =& $self->routes[$which];
+		unset($self->routes[$which]);
+		array_unshift($self->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 address to actual controller/action.
+ * - '/' - the method will find base URL of application.
+ * - A combination of controller/action - the method will find url for it.
+ *
+ * There are a few 'special' parameters that can change the final URL string that is generated
+ * 
+ * - `base` - Set to false to remove the base path from the generated url. If your application
+ *   is not in the root directory, this can be used to generate urls that are 'cake relative'.
+ *   cake relative urls are required when using requestAction.
+ * - `?` - Takes an array of query string parameters
+ * - `#` - Allows you to set url hash fragments.
+ * - `full_base` - If true the `FULL_BASE_URL` constant will be prepended to generated urls.
+ *
+ * @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 $full 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) {
+		$self =& Router::getInstance();
+		$defaults = $params = array('plugin' => null, 'controller' => null, 'action' => 'index');
+
+		if (is_bool($full)) {
+			$escape = false;
+		} else {
+			extract($full + array('escape' => false, 'full' => false));
+		}
+
+		if (!empty($self->__params)) {
+			if (isset($this) && !isset($this->params['requested'])) {
+				$params = $self->__params[0];
+			} else {
+				$params = end($self->__params);
+			}
+		}
+		$path = array('base' => null);
+
+		if (!empty($self->__paths)) {
+			if (isset($this) && !isset($this->params['requested'])) {
+				$path = $self->__paths[0];
+			} else {
+				$path = end($self->__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';
+				}
+			}
+
+			$prefixExists = (array_intersect_key($url, array_flip($self->__prefixes)));
+			foreach ($self->__prefixes as $prefix) {
+				if (!empty($params[$prefix]) && !$prefixExists) {
+					$url[$prefix] = true;
+				} elseif (isset($url[$prefix]) && !$url[$prefix]) {
+					unset($url[$prefix]);
+				}
+				if (isset($url[$prefix]) && strpos($url['action'], $prefix . '_') === 0) {
+					$url['action'] = substr($url['action'], strlen($prefix) + 1);
+				}
+			}
+
+			$url += array('controller' => $params['controller'], 'plugin' => $params['plugin']);
+
+			if (isset($url['ext'])) {
+				$extension = '.' . $url['ext'];
+				unset($url['ext']);
+			}
+			$match = false;
+
+			for ($i = 0, $len = count($self->routes); $i < $len; $i++) {
+				$originalUrl = $url;
+
+				if (isset($self->routes[$i]->options['persist'], $params)) {
+					$url = $self->routes[$i]->persistParams($url, $params);
+				}
+
+				if ($match = $self->routes[$i]->match($url)) {
+					$output = trim($match, '/');
+					break;
+				}
+				$url = $originalUrl;
+			}
+			if ($match === false) {
+				$output = $self->_handleNoRoute($url);
+			}
+			$output = str_replace('//', '/', $base . '/' . $output);
+		} else {
+			if (((strpos($url, '://')) !== false || (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 . '/';
+				foreach ($self->__prefixes as $prefix) {
+					if (isset($params[$prefix])) {
+						$output .= $prefix . '/';
+						break;
+					}
+				}
+				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 . $self->queryString($q, array(), $escape) . $frag;
+	}
+
+/**
+ * A special fallback method that handles url arrays that cannot match
+ * any defined routes.
+ *
+ * @param array $url A url that didn't match any routes
+ * @return string A generated url for the array
+ * @access protected
+ * @see Router::url()
+ */
+	function _handleNoRoute($url) {
+		$named = $args = array();
+		$skip = array_merge(
+			array('bare', 'action', 'controller', 'plugin', 'prefix'),
+			$this->__prefixes
+		);
+
+		$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 (is_numeric($keys[$i])) {
+				$args[] = $url[$keys[$i]];
+			} else {
+				$named[$keys[$i]] = $url[$keys[$i]];
+			}
+		}
+
+		list($args, $named) = array(Set::filter($args, true), Set::filter($named, true));
+		foreach ($this->__prefixes as $prefix) {
+			if (!empty($url[$prefix])) {
+				$url['action'] = str_replace($prefix . '_', '', $url['action']);
+				break;
+			}
+		}
+
+		if (empty($named) && empty($args) && (!isset($url['action']) || $url['action'] === 'index')) {
+			$url['action'] = null;
+		}
+
+		$urlOut = array_filter(array($url['controller'], $url['action']));
+
+		if (isset($url['plugin'])) {
+			array_unshift($urlOut, $url['plugin']);
+		}
+
+		foreach ($this->__prefixes as $prefix) {
+			if (isset($url[$prefix])) {
+				array_unshift($urlOut, $prefix);
+				break;
+			}
+		}
+		$output = implode('/', $urlOut);
+
+		if (!empty($args)) {
+			$output .= '/' . implode('/', $args);
+		}
+
+		if (!empty($named)) {
+			foreach ($named as $name => $value) {
+				if (is_array($value)) {
+					$flattend = Set::flatten($value, '][');
+					foreach ($flattend as $namedKey => $namedValue) {
+						$output .= '/' . $name . "[$namedKey]" . $this->named['separator'] . $namedValue;
+					}
+				} else {
+					$output .= '/' . $name . $this->named['separator'] . $value;
+				}
+			}
+		}
+		return $output;
+	}
+
+/**
+ * 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) {
+		$self =& Router::getInstance();
+		$named = array();
+
+		foreach ($params as $param => $val) {
+			if (isset($self->named['rules'][$param])) {
+				$rule = $self->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
+ * @static
+ */
+	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;
+		}
+		return (!isset($rule['match']) || preg_match('/' . $rule['match'] . '/', $val));
+	}
+
+/**
+ * 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;
+	}
+
+/**
+ * Reverses a parsed parameter array into a string. Works similarly to Router::url(), but
+ * Since parsed URL's contain additional 'pass' and 'named' as well as 'url.url' keys.
+ * Those keys need to be specially handled in order to reverse a params array into a string url.
+ *
+ * This will strip out 'autoRender', 'bare', 'requested', and 'return' param names as those
+ * are used for CakePHP internals and should not normally be part of an output url.
+ *
+ * @param array $param The params array that needs to be reversed.
+ * @return string The string that is the reversed result of the array
+ * @access public
+ * @static
+ */
+	function reverse($params) {
+		$pass = $params['pass'];
+		$named = $params['named'];
+		$url = $params['url'];
+		unset(
+			$params['pass'], $params['named'], $params['paging'], $params['models'], $params['url'], $url['url'],
+			$params['autoRender'], $params['bare'], $params['requested'], $params['return']
+		);
+		$params = array_merge($params, $pass, $named);
+		if (!empty($url)) {
+			$params['?'] = $url;
+		}
+		return Router::url($params);
+	}
+
+/**
+ * Normalizes a URL for purposes of comparison.  Will strip the base path off
+ * and replace any double /'s.  It will not unify the casing and underscoring
+ * of the input value.
+ *
+ * @param mixed $url URL to normalize Either an array or a string url.
+ * @return string Normalized URL
+ * @access public
+ * @static
+ */
+	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 CakeRoute Matching route object.
+ * @access public
+ * @static
+ */
+	function &requestRoute() {
+		$self =& Router::getInstance();
+		return $self->__currentRoute[0];
+	}
+
+/**
+ * Returns the route matching the current request (useful for requestAction traces)
+ *
+ * @return CakeRoute Matching route object.
+ * @access public
+ * @static
+ */
+	function &currentRoute() {
+		$self =& Router::getInstance();
+		return $self->__currentRoute[count($self->__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 = null) {
+		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;
+	}
+
+/**
+ * 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() {
+		$self =& Router::getInstance();
+		$self->__parseExtensions = true;
+		if (func_num_args() > 0) {
+			$self->__validExtensions = func_get_args();
+		}
+	}
+
+/**
+ * Takes a 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()) {
+		$self =& Router::getInstance();
+		$pass = $named = array();
+		$args = explode('/', $args);
+
+		$greedy = isset($options['greedy']) ? $options['greedy'] : $self->named['greedy'];
+		$context = array();
+		if (isset($options['context'])) {
+			$context = $options['context'];
+		}
+		$rules = $self->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;
+			}
+
+			$separatorIsPresent = strpos($param, $self->named['separator']) !== false;
+			if ((!isset($options['named']) || !empty($options['named'])) && $separatorIsPresent) {
+				list($key, $val) = explode($self->named['separator'], $param, 2);
+				$hasRule = isset($rules[$key]);
+				$passIt = (!$hasRule && !$greedy) || ($hasRule && !$self->matchNamed($key, $val, $rules[$key], $context));
+				if ($passIt) {
+					$pass[] = $param;
+				} else {
+					$named[$key] = $val;
+				}
+			} else {
+				$pass[] = $param;
+			}
+		}
+		return compact('pass', 'named');
+	}
+}
+
+/**
+ * A single Route used by the Router to connect requests to
+ * parameter maps.
+ *
+ * Not normally created as a standalone.  Use Router::connect() to create
+ * Routes for your application.
+ *
+ * @package cake.libs
+ * @since 1.3.0
+ * @see Router::connect()
+ */
+class CakeRoute {
+
+/**
+ * An array of named segments in a Route.
+ * `/:controller/:action/:id` has 3 key elements
+ *
+ * @var array
+ * @access public
+ */
+	var $keys = array();
+
+/**
+ * An array of additional parameters for the Route.
+ *
+ * @var array
+ * @access public
+ */
+	var $options = array();
+
+/**
+ * Default parameters for a Route
+ *
+ * @var array
+ * @access public
+ */
+	var $defaults = array();
+
+/**
+ * The routes template string.
+ *
+ * @var string
+ * @access public
+ */
+	var $template = null;
+
+/**
+ * Is this route a greedy route?  Greedy routes have a `/*` in their
+ * template
+ *
+ * @var string
+ * @access protected
+ */
+	var $_greedy = false;
+
+/**
+ * The compiled route regular expresssion
+ *
+ * @var string
+ * @access protected
+ */
+	var $_compiledRoute = null;
+
+/**
+ * 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'
+	);
+
+/**
+ * Constructor for a Route
+ *
+ * @param string $template Template string with parameter placeholders
+ * @param array $defaults Array of defaults for the route.
+ * @param string $params Array of parameters and additional options for the Route
+ * @return void
+ * @access public
+ */
+	function CakeRoute($template, $defaults = array(), $options = array()) {
+		$this->template = $template;
+		$this->defaults = (array)$defaults;
+		$this->options = (array)$options;
+	}
+
+/**
+ * Check if a Route has been compiled into a regular expression.
+ *
+ * @return boolean
+ * @access public
+ */
+	function compiled() {
+		return !empty($this->_compiledRoute);
+	}
+
+/**
+ * Compiles the route's regular expression.  Modifies defaults property so all necessary keys are set
+ * and populates $this->names with the named routing elements.
+ *
+ * @return array Returns a string regular expression of the compiled route.
+ * @access public
+ */
+	function compile() {
+		if ($this->compiled()) {
+			return $this->_compiledRoute;
+		}
+		$this->_writeRoute();
+		return $this->_compiledRoute;
+	}
+
+/**
+ * Builds a route regular expression.  Uses the template, defaults and options
+ * properties to compile a regular expression that can be used to parse request strings.
+ *
+ * @return void
+ * @access protected
+ */
+	function _writeRoute() {
+		if (empty($this->template) || ($this->template === '/')) {
+			$this->_compiledRoute = '#^/*$#';
+			$this->keys = array();
+			return;
+		}
+		$route = $this->template;
+		$names = $routeParams = array();
+		$parsed = preg_quote($this->template, '#');
+		$parsed = str_replace('\\-', '-', $parsed);
+
+		preg_match_all('#:([A-Za-z0-9_-]+[A-Z0-9a-z])#', $parsed, $namedElements);
+		foreach ($namedElements[1] as $i => $name) {
+			$search = '\\' . $namedElements[0][$i];
+			if (isset($this->options[$name])) {
+				$option = null;
+				if ($name !== 'plugin' && array_key_exists($name, $this->defaults)) {
+					$option = '?';
+				}
+				$slashParam = '/\\' . $namedElements[0][$i];
+				if (strpos($parsed, $slashParam) !== false) {
+					$routeParams[$slashParam] = '(?:/(' . $this->options[$name] . ')' . $option . ')' . $option;
+				} else {
+					$routeParams[$search] = '(?:(' . $this->options[$name] . ')' . $option . ')' . $option;
+				}
+			} else {
+				$routeParams[$search] = '(?:([^/]+))';
+			}
+			$names[] = $name;
+		}
+		if (preg_match('#\/\*$#', $route, $m)) {
+			$parsed = preg_replace('#/\\\\\*$#', '(?:/(?P<_args_>.*))?', $parsed);
+			$this->_greedy = true;
+		}
+		krsort($routeParams);
+		$parsed = str_replace(array_keys($routeParams), array_values($routeParams), $parsed);
+		$this->_compiledRoute = '#^' . $parsed . '[/]*$#';
+		$this->keys = $names;
+	}
+
+/**
+ * Checks to see if the given URL can be parsed by this route.
+ * If the route can be parsed an array of parameters will be returned; if not,
+ * false will be returned. String urls are parsed if they match a routes regular expression.
+ *
+ * @param string $url The url to attempt to parse.
+ * @return mixed Boolean false on failure, otherwise an array or parameters
+ * @access public
+ */
+	function parse($url) {
+		if (!$this->compiled()) {
+			$this->compile();
+		}
+		if (!preg_match($this->_compiledRoute, $url, $parsed)) {
+			return false;
+		} else {
+			foreach ($this->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;
+					}
+				}
+			}
+			array_shift($parsed);
+			$route = array();
+			foreach ($this->keys as $i => $key) {
+				if (isset($parsed[$i])) {
+					$route[$key] = $parsed[$i];
+				}
+			}
+			$route['pass'] = $route['named'] = array();
+			$route += $this->defaults;
+			if (isset($parsed['_args_'])) {
+				$route['_args_'] = $parsed['_args_'];
+			}
+			foreach ($route as $key => $value) {
+				if (is_integer($key)) {
+					$route['pass'][] = $value;
+					unset($route[$key]);
+				}
+			}
+			return $route;
+		}
+	}
+
+/**
+ * Apply persistent parameters to a url array. Persistant parameters are a special 
+ * key used during route creation to force route parameters to persist when omitted from 
+ * a url array.
+ *
+ * @param array $url The array to apply persistent parameters to.
+ * @param array $params An array of persistent values to replace persistent ones.
+ * @return array An array with persistent parameters applied.
+ * @access public
+ */
+	function persistParams($url, $params) {
+		foreach ($this->options['persist'] as $persistKey) {
+			if (array_key_exists($persistKey, $params) && !isset($url[$persistKey])) {
+				$url[$persistKey] = $params[$persistKey];
+			}
+		}
+		return $url;
+	}
+
+/**
+ * Attempt to match a url array.  If the url matches the route parameters and settings, then
+ * return a generated string url.  If the url doesn't match the route parameters, false will be returned.
+ * This method handles the reverse routing or conversion of url arrays into string urls.
+ *
+ * @param array $url An array of parameters to check matching with.
+ * @return mixed Either a string url for the parameters if they match or false.
+ * @access public
+ */
+	function match($url) {
+		if (!$this->compiled()) {
+			$this->compile();
+		}
+		$defaults = $this->defaults;
+
+		if (isset($defaults['prefix'])) {
+			$url['prefix'] = $defaults['prefix'];
+		}
+
+		//check that all the key names are in the url
+		$keyNames = array_flip($this->keys);
+		if (array_intersect_key($keyNames, $url) != $keyNames) {
+			return false;
+		}
+
+		$diffUnfiltered = Set::diff($url, $defaults);
+		$diff = array();
+
+		foreach ($diffUnfiltered as $key => $var) {
+			if ($var === 0 || $var === '0' || !empty($var)) {
+				$diff[$key] = $var;
+			}
+		}
+
+		//if a not a greedy route, no extra params are allowed.
+		if (!$this->_greedy && array_diff_key($diff, $keyNames) != array()) {
+			return false;
+		}
+
+		//remove defaults that are also keys. They can cause match failures
+		foreach ($this->keys as $key) {
+			unset($defaults[$key]);
+		}
+		$filteredDefaults = array_filter($defaults);
+
+		//if the difference between the url diff and defaults contains keys from defaults its not a match
+		if (array_intersect_key($filteredDefaults, $diffUnfiltered) !== array()) {
+			return false;
+		}
+
+		$passedArgsAndParams = array_diff_key($diff, $filteredDefaults, $keyNames);
+		list($named, $params) = Router::getNamedElements($passedArgsAndParams, $url['controller'], $url['action']);
+
+		//remove any pass params, they have numeric indexes, skip any params that are in the defaults
+		$pass = array();
+		$i = 0;
+		while (isset($url[$i])) {
+			if (!isset($diff[$i])) {
+				$i++;
+				continue;
+			}
+			$pass[] = $url[$i];
+			unset($url[$i], $params[$i]);
+			$i++;
+		}
+
+		//still some left over parameters that weren't named or passed args, bail.
+		if (!empty($params)) {
+			return false;
+		}
+
+		//check patterns for routed params
+		if (!empty($this->options)) {
+			foreach ($this->options as $key => $pattern) {
+				if (array_key_exists($key, $url) && !preg_match('#^' . $pattern . '$#', $url[$key])) {
+					return false;
+				}
+			}
+		}
+		return $this->_writeUrl(array_merge($url, compact('pass', 'named')));
+	}
+
+/**
+ * Converts a matching route array into a url string. Composes the string url using the template
+ * used to create the route.
+ *
+ * @param array $params The params to convert to a string url.
+ * @return string Composed route string.
+ * @access protected
+ */
+	function _writeUrl($params) {
+		if (isset($params['prefix'], $params['action'])) {
+			$params['action'] = str_replace($params['prefix'] . '_', '', $params['action']);
+			unset($params['prefix']);
+		}
+
+		if (is_array($params['pass'])) {
+			$params['pass'] = implode('/', $params['pass']);
+		}
+
+		$instance =& Router::getInstance();
+		$separator = $instance->named['separator'];
+
+		if (!empty($params['named']) && is_array($params['named'])) {
+			$named = array();
+			foreach ($params['named'] as $key => $value) {
+				$named[] = $key . $separator . $value;
+			}
+			$params['pass'] = $params['pass'] . '/' . implode('/', $named);
+		}
+		$out = $this->template;
+
+		$search = $replace = array();
+		foreach ($this->keys as $key) {
+			$string = null;
+			if (isset($params[$key])) {
+				$string = $params[$key];
+			} elseif (strpos($out, $key) != strlen($out) - strlen($key)) {
+				$key .= '/';
+			}
+			$search[] = ':' . $key;
+			$replace[] = $string;
+		}
+		$out = str_replace($search, $replace, $out);
+
+		if (strpos($this->template, '*')) {
+			$out = str_replace('*', $params['pass'], $out);
+		}
+		$out = str_replace('//', '/', $out);
+		return $out;
+	}
+}
+
+/**
+ * Plugin short route, that copies the plugin param to the controller parameters
+ * It is used for supporting /:plugin routes.
+ *
+ * @package cake.libs
+ */
+class PluginShortRoute extends CakeRoute {
+
+/**
+ * Parses a string url into an array.  If a plugin key is found, it will be copied to the 
+ * controller parameter
+ *
+ * @param string $url The url to parse
+ * @return mixed false on failure, or an array of request parameters
+ */
+	function parse($url) {
+		$params = parent::parse($url);
+		if (!$params) {
+			return false;
+		}
+		$params['controller'] = $params['plugin'];
+		return $params;
+	}
+
+/**
+ * Reverse route plugin shortcut urls.  If the plugin and controller
+ * are not the same the match is an auto fail.
+ *
+ * @param array $url Array of parameters to convert to a string.
+ * @return mixed either false or a string url.
+ */
+	function match($url) {
+		if (isset($url['controller']) && isset($url['plugin']) && $url['plugin'] != $url['controller']) {
+			return false;
+		}
+		$this->defaults['controller'] = $url['controller'];
+		$result = parent::match($url);
+		unset($this->defaults['controller']);
+		return $result;
+	}
+}

Added: trunk/src/Web/cake/libs/sanitize.php
===================================================================
--- trunk/src/Web/cake/libs/sanitize.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/sanitize.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,346 @@
+<?php
+App::import('Core', 'ConnectionManager');
+/**
+ * Washes strings from unwanted noise.
+ *
+ * Helpful methods to make unsafe strings usable.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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
+ * @param array $allowed An array of additional characters that are not to be removed.
+ * @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.
+ *
+ * strip_tags() does not validating HTML syntax or structure, so it might strip whole passages
+ * with broken HTML.
+ *
+ * ### Options:
+ *
+ * - remove (boolean) if true strips all HTML tags before encoding
+ * - charset (string) the charset used to encode the string
+ * - quotes (int) see http://php.net/manual/en/function.htmlentities.php
+ *
+ * @param string $string String from where to strip tags
+ * @param array $options Array of options to use.
+ * @return string Sanitized string
+ * @access public
+ * @static
+ */
+	function html($string, $options = array()) {
+		static $defaultCharset = false;
+		if ($defaultCharset === false) {
+			$defaultCharset = Configure::read('App.encoding');
+			if ($defaultCharset === null) {
+				$defaultCharset = 'UTF-8';
+			}
+		}
+		$default = array(
+			'remove' => false,
+			'charset' => $defaultCharset,
+			'quotes' => ENT_QUOTES
+		);
+
+		$options = array_merge($default, $options);
+
+		if ($options['remove']) {
+			$string = strip_tags($string);
+		}
+
+		return htmlentities($string, $options['quotes'], $options['charset']);
+	}
+
+/**
+ * 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,}/u', ' ', $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>, <img> elements removed.
+ * @access public
+ * @static
+ */
+	function stripScripts($str) {
+		return preg_replace('/(<link[^>]+rel="[^"]*stylesheet"[^>]*>|<img[^>]*>|style="[^"]*")|<script[^>]*>.*?<\/script>|<style[^>]*>.*?<\/style>|<!--.*?-->/is', '', $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.
+ *
+ * Ex.`$clean = Sanitize::stripTags($dirty, 'b', 'p', 'div');`
+ *
+ * Will remove all `<b>`, `<p>`, and `<div>` tags from the $dirty string.
+ *
+ * @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, $count = count($params); $i < $count; $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 - removes any non space whitespace characters
+ * - encode - Encode any html entities. Encode must be true for the `remove_html` to work.
+ * - dollar - Escape `$` with `\$`
+ * - carriage - Remove `\r`
+ * - unicode -
+ * - escape - Should the string be SQL escaped.
+ * - backslash -
+ * - remove_html - Strip HTML with strip_tags. `encode` must be true for this option to work.
+ *
+ * @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,
+			'remove_html' => false,
+			'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), '', $data);
+			}
+			if ($options['encode']) {
+				$data = Sanitize::html($data, array('remove' => $options['remove_html']));
+			}
+			if ($options['dollar']) {
+				$data = str_replace("\\\$", "$", $data);
+			}
+			if ($options['carriage']) {
+				$data = str_replace("\r", "", $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;
+						}
+						*/
+					}
+				}
+			}
+		}
+	}
+}

Added: trunk/src/Web/cake/libs/security.php
===================================================================
--- trunk/src/Web/cake/libs/security.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/security.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,191 @@
+<?php
+/**
+ * Core Security
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v .0.10.0.1233
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Security Library contains utility methods related to security
+ *
+ * @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() {
+		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 '';
+		}
+
+		srand(Configure::read('Security.cipherSeed'));
+		$out = '';
+		$keyLength = strlen($key);
+		for ($i = 0, $textLength = strlen($text); $i < $textLength; $i++) {
+			$j = ord(substr($key, $i % $keyLength, 1));
+			while ($j--) {
+				rand(0, 255);
+			}
+			$mask = rand(0, 255);
+			$out .= chr(ord(substr($text, $i, 1)) ^ $mask);
+		}
+		srand();
+		return $out;
+	}
+}

Added: trunk/src/Web/cake/libs/set.php
===================================================================
--- trunk/src/Web/cake/libs/set.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/set.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,1160 @@
+<?php
+/**
+ * Library of array functions for Cake.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Class used for manipulation of arrays.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class Set {
+
+/**
+ * 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 (array_key_exists($j, $data[$i])) {
+						$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
+ * implement are also very welcome!
+ *
+ * @param string $path An absolute XPath 2.0 path
+ * @param array $data An array of data to extract from
+ * @param array $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 (is_string($data)) {
+			$tmp = $data;
+			$data = $path;
+			$path = $tmp;
+		}
+		if (strpos($path, '/') === false) {
+			return Set::classicExtract($data, $path);
+		}
+		if (empty($data)) {
+			return array();
+		}
+		if ($path === '/') {
+			return $data;
+		}
+		$contexts = $data;
+		$options = array_merge(array('flatten' => true), $options);
+		if (!isset($contexts[0])) {
+			$current = current($data);
+			if ((is_array($current) && count($data) < 1) || !is_array($current) || !Set::numeric(array_keys($data))) {
+				$contexts = array($data);
+			}
+		}
+		$tokens = array_slice(preg_split('/(?<!=|\\\\)\/(?![a-z-\s]*\])/', $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 = implode('/', $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];
+					} elseif (!empty($context['item'][$key])) {
+						$context['item'] = $context['item'][$key];
+					} else {
+						$context['item'] = array_shift($context['item']);
+					}
+					$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'])
+					&& !(strval($key) === strval($token) && count($tokens) == 1 && $tokens[0] === '.')) {
+					$items = $context['item'][$token];
+					if (!is_array($items)) {
+						$items = array($items);
+					} elseif (!isset($items[0])) {
+						$current = current($items);
+						$currentKey = key($items);
+						if (!is_array($current) || (is_array($current) && count($items) <= 1 && !is_numeric($currentKey))) {
+							$items = array($items);
+						}
+					}
+
+					foreach ($items as $key => $item) {
+						$ctext = array($context['key']);
+						if (!is_numeric($key)) {
+							$ctext[] = $token;
+							$tok = array_shift($tokens);
+							if (isset($items[$tok])) {
+								$ctext[] = $tok;
+								$item = $items[$tok];
+								$matches[] = array(
+									'trace' => array_merge($context['trace'], $ctext),
+									'key' => $tok,
+									'item' => $item,
+								);
+								break;
+							} elseif ($tok !== null) {
+								array_unshift($tokens, $tok);
+							}
+						} 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[$i] = $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];
+			}
+			if (!is_array($_list)) {
+				return array();
+			}
+		}
+		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 Returns the key => value pairs that are not common in $val1 and $val2
+ * The expression for this function is ($val1 - $val2) + ($val2 - ($val1 - $val2))
+ * @access public
+ * @static
+ */
+	function diff($val1, $val2 = null) {
+		if (empty($val1)) {
+			return (array)$val2;
+		}
+		if (empty($val2)) {
+			return (array)$val1;
+		}
+		$intersection = array_intersect_key($val1, $val2);
+		while (($key = key($intersection)) !== null) {
+			if ($val1[$key] == $val2[$key]) {
+				unset($val1[$key]);
+				unset($val2[$key]);
+			}
+			next($intersection);
+		}
+
+		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 mixed $data Array or object 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($keys)) {
+			return array();
+		}
+
+		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;
+			}
+		}
+		if (empty($vals)) {
+			return array();
+		}
+		return array_combine($keys, $vals);
+	}
+
+/**
+ * Converts an object into an array.
+ * @param object $object Object to reverse
+ * @return array Array representation of given object
+ * @public
+ * @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) && !empty($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 An array of data to sort
+ * @param string $path A Set-compatible path to the array value
+ * @param string $dir Direction of sorting - either ascending (ASC), or descending (DESC)
+ * @return array Sorted array of data
+ * @static
+ */
+	function sort($data, $path, $dir) {
+		$originalKeys = array_keys($data);
+		if (is_numeric(implode('', $originalKeys))) {
+			$data = array_values($data);
+		}
+		$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;
+	}
+
+/**
+ * Allows the application of a callback method to elements of an
+ * array extracted by a Set::extract() compatible path.
+ *
+ * @param mixed $path Set-compatible path to the array value
+ * @param array $data An array of data to extract from & then process with the $callback.
+ * @param mixed $callback Callback method to be applied to extracted data.
+ * See http://ca2.php.net/manual/en/language.pseudo-types.php#language.types.callback for examples
+ * of callback formats.
+ * @param array $options Options are:
+ *                       - type : can be pass, map, or reduce. Map will handoff the given callback
+ *                                to array_map, reduce will handoff to array_reduce, and pass will
+ *                                use call_user_func_array().
+ * @return mixed Result of the callback when applied to extracted data
+ * @access public
+ * @static
+ */
+	function apply($path, $data, $callback, $options = array()) {
+		$defaults = array('type' => 'pass');
+		$options = array_merge($defaults, $options);
+
+		$extracted = Set::extract($path, $data);
+
+		if ($options['type'] === 'map') {
+			$result = array_map($callback, $extracted);
+
+		} elseif ($options['type'] === 'reduce') {
+			$result = array_reduce($extracted, $callback);
+
+		} elseif ($options['type'] === 'pass') {
+			$result = call_user_func_array($callback, array($extracted));
+		} else {
+			return null;
+		}
+
+		return  $result;
+	}
+}

Added: trunk/src/Web/cake/libs/string.php
===================================================================
--- trunk/src/Web/cake/libs/string.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/string.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,329 @@
+<?php
+/**
+ * String handling methods.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.0.5551
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * String handling methods.
+ *
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ */
+class String {
+
+/**
+ * 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.
+ * @param string $leftBound The left boundary to ignore separators in.
+ * @param string $rightBound The right boundary to ignore separators in.
+ * @return array Array of tokens in $data.
+ * @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: `String::insert(':name is :age years old.', array('name' => 'Bob', '65'));`
+ * Returns: Bob is 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()) {
+		$defaults = array(
+			'before' => ':', 'after' => null, 'escape' => '\\', 'format' => null, 'clean' => false
+		);
+		$options += $defaults;
+		$format = $options['format'];
+		$data = (array)$data;
+		if (empty($data)) {
+			return ($options['clean']) ? String::cleanInsert($str, $options) : $str;
+		}
+
+		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 (strpos($str, '?') !== false && is_numeric(key($data))) {
+			$offset = 0;
+			while (($pos = strpos($str, '?', $offset)) !== false) {
+				$val = array_shift($data);
+				$offset = $pos + strlen($val);
+				$str = substr_replace($str, $val, $pos, 1);
+			}
+			return ($options['clean']) ? String::cleanInsert($str, $options) : $str;
+		} else {
+			asort($data);
+
+			$hashKeys = array();
+			foreach ($data as $key => $value) {
+				$hashKeys[] = crc32($key);
+			}
+
+			$tempData = array_combine(array_keys($data), array_values($hashKeys));
+			krsort($tempData);
+			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 => $tmpValue) {
+				$tmpValue = (is_array($tmpValue)) ? '' : $tmpValue;
+				$str = str_replace($tmpHash, $tmpValue, $str);
+			}
+		}
+
+		if (!isset($options['format']) && isset($options['before'])) {
+			$str = str_replace($options['escape'].$options['before'], $options['before'], $str);
+		}
+		return ($options['clean']) ? String::cleanInsert($str, $options) : $str;
+	}
+
+/**
+ * Cleans up a String::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 String::insert().
+ *
+ * @param string $str
+ * @param string $options
+ * @return string
+ * @access public
+ * @static
+ * @see String::insert()
+ */
+	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;
+	}
+}

Added: trunk/src/Web/cake/libs/validation.php
===================================================================
--- trunk/src/Web/cake/libs/validation.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/validation.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,1060 @@
+<?php
+/**
+ * Validation Class.  Used for validation of model data
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.0.3830
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+if (!class_exists('Multibyte')) {
+	App::import('Core', 'Multibyte', false);
+}
+/**
+ * Offers different validation methods.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP v 1.2.0.3830
+ */
+class Validation extends Object {
+
+/**
+ * Set 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(
+		'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 = mb_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 (mb_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)?|(Sep)(tember)?|(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)) {
+			if (function_exists('getmxrr') && getmxrr($regs[1], $mxhosts)) {
+				return true;
+			}
+			if (function_exists('checkdnsrr') && checkdnsrr($regs[1], 'MX')) {
+				return true;
+			}
+			return is_array(gethostbynamel($regs[1]));
+		}
+		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;
+	}
+
+/**
+ * Validation of an IP address.
+ *
+ * Valid IP version strings for type restriction are:
+ * - both: Check both IPv4 and IPv6, return true if the supplied address matches either version
+ * - IPv4: Version 4 (Eg: 127.0.0.1, 192.168.10.123, 203.211.24.8)
+ * - IPv6: Version 6 (Eg: ::1, 2001:0db8::1428:57ab)
+ *
+ * @param string $check The string to test.
+ * @param string $type The IP Version to test against
+ * @return boolean Success
+ * @access public
+ */
+	function ip($check, $type = 'both') {
+		$_this =& Validation::getInstance();
+		$success = false;
+		$type = strtolower($type);
+		if ($type === 'ipv4' || $type === 'both') {
+			$success |= $_this->_ipv4($check);
+		}
+		if ($type === 'ipv6' || $type === 'both') {
+			$success |= $_this->_ipv6($check);
+		}
+		return $success;
+	}
+
+/**
+ * Validation of IPv4 addresses.
+ *
+ * @param string $check IP Address to test
+ * @return boolean Success
+ * @access protected
+ */
+	function _ipv4($check) {
+		if (function_exists('filter_var')) {
+			return filter_var($check, FILTER_VALIDATE_IP, array('flags' => FILTER_FLAG_IPV4)) !== false;
+		}
+		$this->__populateIp();
+		$this->check = $check;
+		$this->regex = '/^' . $this->__pattern['IPv4'] . '$/';
+		return $this->_check();
+	}
+
+/**
+ * Validation of IPv6 addresses.
+ *
+ * @param string $check IP Address to test
+ * @return boolean Success
+ * @access protected
+ */
+	function _ipv6($check) {
+		if (function_exists('filter_var')) {
+			return filter_var($check, FILTER_VALIDATE_IP, array('flags' => FILTER_FLAG_IPV6)) !== false;
+		}
+		$this->__populateIp();
+		$this->check = $check;
+		$this->regex = '/^' . $this->__pattern['IPv6'] . '$/';
+		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 = mb_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 = mb_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.
+ *
+ * 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
+ *
+ * @param mixed $check Value to check
+ * @param mixed $options Options for the check.
+ * @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'] && count($check) > $options['max']) {
+			return false;
+		}
+		if ($options['min'] && count($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':
+				case 'all':
+				case 'can':
+				// includes all NANPA members. see http://en.wikipedia.org/wiki/North_American_Numbering_Plan#List_of_NANPA_countries_and_territories
+					$_this->regex  = '/^(?:\+?1)?[-. ]?\\(?[2-9][0-8][0-9]\\)?[-. ]?[2-9][0-9]{2}[-. ]?[0-9]{4}$/';
+				break;
+			}
+		}
+		if (empty($_this->regex)) {
+			return $_this->_pass('phone', $check, $country);
+		}
+		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 (empty($country)) {
+			$_this->country = 'us';
+		}
+
+		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':
+					$_this->regex  = '/\\A\\b[0-9]{5}(?:-[0-9]{4})?\\b\\z/i';
+					break;
+			}
+		}
+		if (empty($_this->regex)) {
+			return $_this->_pass('postal', $check, $country);
+		}
+		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':
+					$_this->regex  = '/\\A\\b[0-9]{3}-[0-9]{2}-[0-9]{4}\\b\\z/i';
+					break;
+			}
+		}
+		if (empty($_this->regex)) {
+			return $_this->_pass('ssn', $check, $country);
+		}
+		return $_this->_check();
+	}
+
+/**
+ * Checks that a value is a valid uuid - http://tools.ietf.org/html/rfc4122
+ *
+ * @param string $check Value to check
+ * @return boolean Success
+ * @access public
+ */
+	function uuid($check) {
+		$_this =& Validation::getInstance();
+		$_this->check = $check;
+		$_this->regex = '/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i';
+		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
+ * @param boolean $strict Require URL to be prefixed by a valid scheme (one of http(s)/ftp(s)/file/news/gopher)
+ * @return boolean Success
+ * @access public
+ */
+	function url($check, $strict = false) {
+		$_this =& Validation::getInstance();
+		$_this->__populateIp();
+		$_this->check = $check;
+		$validChars = '([' . preg_quote('!"$&\'()*+,-. @ _:;=~[]') . '\/0-9a-z\p{L}\p{N}]|(%[0-9a-f]{2}))';
+		$_this->regex = '/^(?:(?:https?|ftps?|file|news|gopher):\/\/)' . (!empty($strict) ? '' : '?') .
+			'(?:' . $_this->__pattern['IPv4'] . '|\[' . $_this->__pattern['IPv6'] . '\]|' . $_this->__pattern['hostname'] . ')' .
+			'(?::[1-9][0-9]{0,4})?' .
+			'(?:\/?|\/' . $validChars . '*)?' .
+			'(?:\?' . $validChars . '*)?' .
+			'(?:#' . $validChars . '*)?$/iu';
+		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));
+	}
+
+/**
+ * Attempts to pass unhandled Validation locales to a class starting with $classPrefix
+ * and ending with Validation.  For example $classPrefix = 'nl', the class would be
+ * `NlValidation`.
+ *
+ * @param string $method The method to call on the other class.
+ * @param mixed $check The value to check or an array of parameters for the method to be called.
+ * @param string $classPrefix The prefix for the class to do the validation.
+ * @return mixed Return of Passed method, false on failure
+ * @access protected
+ **/
+	function _pass($method, $check, $classPrefix) {
+		$className = ucwords($classPrefix) . 'Validation';
+		if (!class_exists($className)) {
+			trigger_error(sprintf(__('Could not find %s class, unable to complete validation.', true), $className), E_USER_WARNING);
+			return false;
+		}
+		if (!is_callable(array($className, $method))) {
+			trigger_error(sprintf(__('Method %s does not exist on %s unable to complete validation.', true), $method, $className), E_USER_WARNING);
+			return false;
+		}
+		$check = (array)$check;
+		return call_user_func_array(array($className, $method), $check);
+	}
+
+/**
+ * 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 = mb_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);
+	}
+
+/*
+ * Lazily popualate the IP address patterns used for validations
+ *
+ * @return void
+ * @access private
+ */
+	function __populateIp() {
+		if (!isset($this->__pattern['IPv6'])) {
+			$pattern  = '((([0-9A-Fa-f]{1,4}:){7}(([0-9A-Fa-f]{1,4})|:))|(([0-9A-Fa-f]{1,4}:){6}';
+			$pattern .= '(:|((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})';
+			$pattern .= '|(:[0-9A-Fa-f]{1,4})))|(([0-9A-Fa-f]{1,4}:){5}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})';
+			$pattern .= '(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:)';
+			$pattern .= '{4}(:[0-9A-Fa-f]{1,4}){0,1}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2}))';
+			$pattern .= '{3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:){3}(:[0-9A-Fa-f]{1,4}){0,2}';
+			$pattern .= '((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|';
+			$pattern .= '((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:){2}(:[0-9A-Fa-f]{1,4}){0,3}';
+			$pattern .= '((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2}))';
+			$pattern .= '{3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:)(:[0-9A-Fa-f]{1,4})';
+			$pattern .= '{0,4}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)';
+			$pattern .= '|((:[0-9A-Fa-f]{1,4}){1,2})))|(:(:[0-9A-Fa-f]{1,4}){0,5}((:((25[0-5]|2[0-4]';
+			$pattern .= '\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4})';
+			$pattern .= '{1,2})))|(((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})))(%.+)?';
+
+			$this->__pattern['IPv6'] = $pattern;
+		}
+		if (!isset($this->__pattern['IPv4'])) {
+			$pattern = '(?:(?: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])';
+			$this->__pattern['IPv4'] = $pattern;
+		}
+	}
+
+/**
+ * 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();
+	}
+}

Added: trunk/src/Web/cake/libs/view/elements/email/html/default.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/elements/email/html/default.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/elements/email/html/default.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,26 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.elements.email.html
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<?php
+$content = explode("\n", $content);
+
+foreach ($content as $line):
+	echo '<p> ' . $line . "</p>\n";
+endforeach;
+?>
\ No newline at end of file

Added: trunk/src/Web/cake/libs/view/elements/email/text/default.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/elements/email/text/default.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/elements/email/text/default.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,20 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.elements.email.text
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<?php echo $content; ?>
\ No newline at end of file

Added: trunk/src/Web/cake/libs/view/elements/sql_dump.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/elements/sql_dump.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/elements/sql_dump.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,61 @@
+<?php
+/**
+ * SQL Dump element.  Dumps out SQL log information 
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.elements
+ * @since         CakePHP(tm) v 1.3
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+if (!class_exists('ConnectionManager') || Configure::read('debug') < 2) {
+	return false;
+}
+$noLogs = !isset($logs);
+if ($noLogs):
+	$sources = ConnectionManager::sourceList();
+
+	$logs = array();
+	foreach ($sources as $source):
+		$db =& ConnectionManager::getDataSource($source);
+		if (!$db->isInterfaceSupported('getLog')):
+			continue;
+		endif;
+		$logs[$source] = $db->getLog();
+	endforeach;
+endif;
+
+if ($noLogs || isset($_forced_from_dbo_)):
+	foreach ($logs as $source => $logInfo):
+		$text = $logInfo['count'] > 1 ? 'queries' : 'query';
+		printf(
+			'<table class="cake-sql-log" id="cakeSqlLog_%s" summary="Cake SQL Log" cellspacing="0" border = "0">',
+			preg_replace('/[^A-Za-z0-9_]/', '_', uniqid(time(), true))
+		);
+		printf('<caption>(%s) %s %s took %s ms</caption>', $source, $logInfo['count'], $text, $logInfo['time']);
+	?>
+	<thead>
+		<tr><th>Nr</th><th>Query</th><th>Error</th><th>Affected</th><th>Num. rows</th><th>Took (ms)</th></tr>
+	</thead>
+	<tbody>
+	<?php
+		foreach ($logInfo['log'] as $k => $i) :
+			echo "<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";
+		endforeach;
+	?>
+	</tbody></table>
+	<?php 
+	endforeach;
+else:
+	echo '<p>Encountered unexpected $logs cannot generate SQL log</p>';
+endif;
+?>

Added: trunk/src/Web/cake/libs/view/errors/error404.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/errors/error404.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/errors/error404.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,24 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<h2><?php echo $name; ?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php printf(__('The requested address %s was not found on this server.', true), "<strong>'{$message}'</strong>"); ?>
+</p>
\ No newline at end of file

Added: trunk/src/Web/cake/libs/view/errors/error500.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/errors/error500.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/errors/error500.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,24 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<h2><?php echo $name; ?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php printf(__('An Internal Error Has Occurred.', true), "<strong>'{$message}'</strong>"); ?>
+</p>

Added: trunk/src/Web/cake/libs/view/errors/missing_action.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/errors/missing_action.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/errors/missing_action.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,46 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<h2><?php printf(__('Missing Method in %s', true), $controller); ?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php printf(__('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 printf(__('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 printf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . 'views' . DS . 'errors' . DS . 'missing_action.ctp'); ?>
+</p>

Added: trunk/src/Web/cake/libs/view/errors/missing_behavior_class.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/errors/missing_behavior_class.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/errors/missing_behavior_class.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,39 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 1.3
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<h2><?php __('Missing Behavior Class'); ?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php printf(__('The behavior class <em>%s</em> can not be found or does not exist.', true), $behaviorClass); ?>
+</p>
+<p  class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php printf(__('Create the class below in file: %s', true), APP_DIR . DS . 'models' . DS . 'behaviors' . DS . $file); ?>
+</p>
+<pre>
+&lt;?php
+class <?php echo $behaviorClass;?> extends ModelBehavior {
+
+}
+?&gt;
+</pre>
+<p class="notice">
+	<strong><?php __('Notice'); ?>: </strong>
+	<?php printf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . 'views' . DS . 'errors' . DS . 'missing_behavior_class.ctp'); ?>
+</p>
\ No newline at end of file

Added: trunk/src/Web/cake/libs/view/errors/missing_behavior_file.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/errors/missing_behavior_file.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/errors/missing_behavior_file.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,39 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 1.3
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<h2><?php __('Missing Behavior File'); ?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php printf(__('The Behavior file %s can not be found or does not exist.', true), APP_DIR . DS . 'models' . DS . 'behaviors' . DS . $file); ?>
+</p>
+<p  class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php printf(__('Create the class below in file: %s', true), APP_DIR . DS . 'models' . DS . 'behaviors' . DS . $file); ?>
+</p>
+<pre>
+&lt;?php
+class <?php echo $behaviorClass;?> extends ModelBehavior {
+
+}
+?&gt;
+</pre>
+<p class="notice">
+	<strong><?php __('Notice'); ?>: </strong>
+	<?php printf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . 'views' . DS . 'errors' . DS . 'missing_behavior_file.ctp'); ?>
+</p>

Added: trunk/src/Web/cake/libs/view/errors/missing_component_class.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/errors/missing_component_class.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/errors/missing_component_class.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,39 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<h2><?php __('Missing Component Class'); ?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php printf(__('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 printf(__('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 printf(__('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

Added: trunk/src/Web/cake/libs/view/errors/missing_component_file.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/errors/missing_component_file.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/errors/missing_component_file.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,39 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<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 printf(__('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 printf(__('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

Added: trunk/src/Web/cake/libs/view/errors/missing_connection.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/errors/missing_connection.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/errors/missing_connection.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,32 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<h2><?php __('Missing Database Connection'); ?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php printf(__('%s requires a database connection', true), $model); ?>
+</p>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php printf(__('Confirm you have created the file : %s.', true), APP_DIR . DS . 'config' . DS . 'database.php'); ?>
+</p>
+<p class="notice">
+	<strong><?php __('Notice'); ?>: </strong>
+	<?php printf(__('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

Added: trunk/src/Web/cake/libs/view/errors/missing_controller.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/errors/missing_controller.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/errors/missing_controller.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,40 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<h2><?php __('Missing Controller'); ?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php printf(__('%s could not be found.', true), '<em>' . $controller . '</em>'); ?>
+</p>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php printf(__('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 printf(__('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

Added: trunk/src/Web/cake/libs/view/errors/missing_helper_class.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/errors/missing_helper_class.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/errors/missing_helper_class.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,39 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<h2><?php __('Missing Helper Class'); ?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php printf(__('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 printf(__('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 printf(__('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

Added: trunk/src/Web/cake/libs/view/errors/missing_helper_file.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/errors/missing_helper_file.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/errors/missing_helper_file.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,39 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<h2><?php __('Missing Helper File'); ?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php printf(__('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 printf(__('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 printf(__('If you want to customize this error message, create %s', true), APP_DIR . DS . 'views' . DS . 'errors' . DS . 'missing_helper_file.ctp'); ?>
+</p>

Added: trunk/src/Web/cake/libs/view/errors/missing_layout.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/errors/missing_layout.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/errors/missing_layout.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,32 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<h2><?php __('Missing Layout'); ?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php printf(__('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 printf(__('Confirm you have created the file: %s', true), '<em>' . $file . '</em>'); ?>
+</p>
+<p class="notice">
+	<strong><?php __('Notice'); ?>: </strong>
+	<?php printf(__('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

Added: trunk/src/Web/cake/libs/view/errors/missing_model.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/errors/missing_model.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/errors/missing_model.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,41 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<h2><?php __('Missing Model'); ?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php printf(__('<em>%s</em> could not be found.', true), $model); ?>
+</p>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php printf(__('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 printf(__('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

Added: trunk/src/Web/cake/libs/view/errors/missing_scaffolddb.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/errors/missing_scaffolddb.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/errors/missing_scaffolddb.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,32 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<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 printf(__('Confirm you have created the file: %s', true), APP_DIR . DS . 'config' . DS . 'database.php'); ?>
+</p>
+<p class="notice">
+	<strong><?php __('Notice'); ?>: </strong>
+	<?php printf(__('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

Added: trunk/src/Web/cake/libs/view/errors/missing_table.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/errors/missing_table.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/errors/missing_table.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,28 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<h2><?php __('Missing Database Table'); ?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php printf(__('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 printf(__('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

Added: trunk/src/Web/cake/libs/view/errors/missing_view.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/errors/missing_view.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/errors/missing_view.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,32 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<h2><?php __('Missing View'); ?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php printf(__('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 printf(__('Confirm you have created the file: %s', true), $file); ?>
+</p>
+<p class="notice">
+	<strong><?php __('Notice'); ?>: </strong>
+	<?php printf(__('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

Added: trunk/src/Web/cake/libs/view/errors/private_action.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/errors/private_action.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/errors/private_action.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,28 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<h2><?php printf(__('Private Method in %s', true), $controller); ?></h2>
+<p class="error">
+	<strong><?php __('Error'); ?>: </strong>
+	<?php printf(__('%s%s cannot be accessed directly.', true), '<em>' . $controller . '::</em>', '<em>' . $action . '()</em>'); ?>
+</p>
+<p class="notice">
+	<strong><?php __('Notice'); ?>: </strong>
+	<?php printf(__('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

Added: trunk/src/Web/cake/libs/view/errors/scaffold_error.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/errors/scaffold_error.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/errors/scaffold_error.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,35 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.errors
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<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 printf(__('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

Added: trunk/src/Web/cake/libs/view/helper.php
===================================================================
--- trunk/src/Web/cake/libs/view/helper.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/helper.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,912 @@
+<?php
+/**
+ * Backend for helpers.
+ *
+ * Internal methods for the Helpers.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Included libs
+ */
+App::import('Core', 'Overloadable');
+
+/**
+ * Abstract base class for all other Helpers in CakePHP.
+ * Provides common methods and features.
+ *
+ * @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;
+
+/**
+ * The current theme name if any.
+ *
+ * @var string
+ */
+	var $theme = 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 inside app/config to load.
+ * @return array merged tags from config/$name.php
+ * @access public
+ */
+	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 a URL pointing at the provided parameters.
+ *
+ * @param mixed $url Either a relative string url like `/products/view/23` or
+ *    an array of url parameters.  Using an array for urls will allow you to leverage
+ *    the reverse routing features of CakePHP.
+ * @param boolean $full If true, the full base URL will be prepended to the result
+ * @return string  Full translated URL with base path.
+ * @access public
+ * @link http://book.cakephp.org/view/1448/url
+ */
+	function url($url = null, $full = false) {
+		return h(Router::url($url, $full));
+	}
+
+/**
+ * Checks if a file exists when theme is used, if no file is found default location is returned
+ *
+ * @param string $file The file to create a webroot path to.
+ * @return string Web accessible path to file.
+ * @access public
+ */
+	function webroot($file) {
+		$asset = explode('?', $file);
+		$asset[1] = isset($asset[1]) ? '?' . $asset[1] : null;
+		$webPath = "{$this->webroot}" . $asset[0];
+		$file = $asset[0];
+
+		if (!empty($this->theme)) {
+			$file = trim($file, '/');
+			$theme = $this->theme . '/';
+
+			if (DS === '\\') {
+				$file = str_replace('/', '\\', $file);
+			}
+
+			if (file_exists(Configure::read('App.www_root') . 'theme' . DS . $this->theme . DS  . $file)) {
+				$webPath = "{$this->webroot}theme/" . $theme . $asset[0];
+			} else {
+				$viewPaths = App::path('views');
+
+				foreach ($viewPaths as $viewPath) {
+					$path = $viewPath . 'themed'. DS . $this->theme . DS  . 'webroot' . DS  . $file;
+
+					if (file_exists($path)) {
+						$webPath = "{$this->webroot}theme/" . $theme . $asset[0];
+						break;
+					}
+				}
+			}
+		}
+		if (strpos($webPath, '//') !== false) {
+			return str_replace('//', '/', $webPath . $asset[1]);
+		}
+		return $webPath . $asset[1];
+	}
+
+/**
+ * Adds a timestamp to a file based resource based on the value of `Asset.timestamp` in
+ * Configure.  If Asset.timestamp is true and debug > 0, or Asset.timestamp == 'force'
+ * a timestamp will be added.
+ *
+ * @param string $path The file path to timestamp, the path must be inside WWW_ROOT
+ * @return string Path with a timestamp added, or not.
+ * @access public
+ */
+	function assetTimestamp($path) {
+		$timestampEnabled = (
+			(Configure::read('Asset.timestamp') === true && Configure::read() > 0) ||
+			Configure::read('Asset.timestamp') === 'force'
+		);
+		if (strpos($path, '?') === false && $timestampEnabled) {
+			$filepath = preg_replace('/^' . preg_quote($this->webroot, '/') . '/', '', $path);
+			$webrootPath = WWW_ROOT . str_replace('/', DS, $filepath);
+			if (file_exists($webrootPath)) {
+				return $path . '?' . @filemtime($webrootPath);
+			}
+			$segments = explode('/', ltrim($filepath, '/'));
+			if ($segments[0] === 'theme') {
+				$theme = $segments[1];
+				unset($segments[0], $segments[1]);
+				$themePath = App::themePath($theme) . 'webroot' . DS . implode(DS, $segments);
+				return $path . '?' . @filemtime($themePath);
+			} else {
+				$plugin = $segments[0];
+				unset($segments[0]);
+				$pluginPath = App::pluginPath($plugin) . 'webroot' . DS . implode(DS, $segments);
+				return $path . '?' . @filemtime($pluginPath);
+			}
+		}
+		return $path;
+	}
+
+/**
+ * Used to remove harmful tags from content.  Removes a number of well known XSS attacks
+ * from content.  However, is not guaranteed to remove all possiblities.  Escaping
+ * content is the best way to prevent all possible attacks.
+ *
+ * @param mixed $output Either an array of strings to clean or a single string to clean.
+ * @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' (string)
+ * - 1 (integer)
+ * - true (boolean)
+ * - 'true' (string)
+ *
+ * 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.
+ *
+ * 'escape' is a special option in that it controls the conversion of
+ *  attributes to their html-entity encoded equivalents.  Set to false to disable html-encoding.
+ *
+ * If value for any option key is set to `null` or `false`, that option will be excluded from output.
+ *
+ * @param array $options Array of options.
+ * @param array $exclude Array of options to be excluded, the options here will not be part of the return.
+ * @param string $insertBefore String to be inserted before options.
+ * @param string $insertAfter String to be inserted after options.
+ * @return string Composed attributes.
+ * @access public
+ */
+	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($exclude, array('escape')));
+			$values = array_intersect_key(array_values($options), $keys);
+			$escape = $options['escape'];
+			$attributes = array();
+
+			foreach ($keys as $index => $key) {
+				if ($values[$index] !== false && $values[$index] !== null) {
+					$attributes[] = $this->__formatAttribute($key, $values[$index], $escape);
+				}
+			}
+			$out = implode(' ', $attributes);
+		} else {
+			$out = $options;
+		}
+		return $out ? $insertBefore . $out . $insertAfter : '';
+	}
+
+/**
+ * Formats an individual attribute, and returns the string value of the composed attribute.
+ * Works with minimized attributes that have the same value as their name such as 'disabled' and 'checked'
+ *
+ * @param string $key The name of the attribute to create
+ * @param string $value The value of the attribute to create.
+ * @return string The composed attribute.
+ * @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 === '1' || $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
+ * @access public
+ */
+	function setEntity($entity, $setScope = false) {
+		$view =& ClassRegistry::getObject('view');
+
+		if ($setScope) {
+			$view->modelScope = false;
+		} elseif (!empty($view->entityPath) && $view->entityPath == $entity) {
+			return;
+		}
+
+		if ($entity === null) {
+			$view->model = null;
+			$view->association = null;
+			$view->modelId = null;
+			$view->modelScope = false;
+			$view->entityPath = null;
+			return;
+		}
+
+		$view->entityPath = $entity;
+		$model = $view->model;
+		$sameScope = $hasField = false;
+		$parts = array_values(Set::filter(explode('.', $entity), true));
+
+		if (empty($parts)) {
+			return;
+		}
+
+		$count = count($parts);
+		if ($count === 1) {
+			$sameScope = true;
+		} else {
+			if (is_numeric($parts[0])) {
+				$sameScope = true;
+			}
+			$reverse = array_reverse($parts);
+			$field = array_shift($reverse);
+			while(!empty($reverse)) {
+				$subject = array_shift($reverse);
+				if (is_numeric($subject)) {
+					continue;
+				}
+				if (ClassRegistry::isKeySet($subject)) {
+					$model = $subject;
+					break;
+				}
+			}
+		}
+
+		if (ClassRegistry::isKeySet($model)) {
+			$ModelObj =& ClassRegistry::getObject($model);
+			for ($i = 0; $i < $count; $i++) {
+				if (
+					is_a($ModelObj, 'Model') && 
+					($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;
+			default:
+				$reverse = array_reverse($parts);
+
+				if ($hasField) {
+						$view->field = $field;
+						if (!is_numeric($reverse[1]) && $reverse[1] != $model) {
+							$view->field = $reverse[1];
+							$view->fieldSuffix = $field;
+						}
+				}
+				if (is_numeric($parts[0])) {
+					$view->modelId = $parts[0];
+				} elseif ($view->model == $parts[0] && is_numeric($parts[1])) {
+					$view->modelId = $parts[1];
+				}
+				$view->association = $model;
+			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
+ * @access public
+ */
+	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
+ * @access public
+ */
+	function modelID() {
+		$view =& ClassRegistry::getObject('view');
+		return $view->modelId;
+	}
+
+/**
+ * Gets the currently-used model field of the rendering context.
+ *
+ * @return string
+ * @access public
+ */
+	function field() {
+		$view =& ClassRegistry::getObject('view');
+		return $view->field;
+	}
+
+/**
+ * Returns null if given FORM field has no errors. Otherwise it returns the constant set in
+ * the array Model->validationErrors.
+ *
+ * @param string $model Model name as a string
+ * @param string $field Fieldname as a string
+ * @param integer $modelID Unique index identifying this record within the form
+ * @return mixed Null if no errors, string with error otherwhise.
+ */
+	function tagIsInvalid($model = null, $field = null, $modelID = null) {
+		$view =& ClassRegistry::getObject('view');
+		$errors = $this->validationErrors;
+		$entity = $view->entity();
+		if (!empty($entity)) {
+			return Set::extract(join('.', $entity), $errors);
+		}
+	}
+
+/**
+ * Generates a DOM ID for the selected element, if one is not set.
+ * Uses the current View::entity() settings to generate a CamelCased id attribute.
+ *
+ * @param mixed $options Either an array of html attributes to add $id into, or a string
+ *   with a view entity path to get a domId for.
+ * @param string $id The name of the 'id' attribute.
+ * @return mixed If $options was an array, an array will be returned with $id set.  If a string
+ *   was supplied, a string will be returned.
+ * @todo Refactor this method to not have as many input/output options.
+ */
+	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();
+		}
+
+		$entity = $view->entity();
+		$model = array_shift($entity);
+		$dom = $model . join('', array_map(array('Inflector', 'camelize'), $entity));
+
+		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. Creates input name attributes
+ * using CakePHP's data[Model][field] formatting.
+ *
+ * @param mixed $options If an array, should be an array of attributes that $key needs to be added to.
+ *   If a string or null, will be used as the View entity.
+ * @param string $field
+ * @param string $key The name of the attribute to be set, defaults to 'name'
+ * @return mixed If an array was given for $options, an array with $key set will be returned.
+ *   If a string was supplied a string will be returned.
+ * @access protected
+ * @todo Refactor this method to not have as many input/output options.
+ */
+	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[' . implode('][', $view->entity()) . ']';
+			break;
+		}
+
+		if (is_array($options)) {
+			$options[$key] = $name;
+			return $options;
+		} else {
+			return $name;
+		}
+	}
+
+/**
+ * Gets the data for the current tag
+ *
+ * @param mixed $options If an array, should be an array of attributes that $key needs to be added to.
+ *   If a string or null, will be used as the View entity.
+ * @param string $field
+ * @param string $key The name of the attribute to be set, defaults to 'value'
+ * @return mixed If an array was given for $options, an array with $key set will be returned.
+ *   If a string was supplied a string will be returned.
+ * @access public
+ * @todo Refactor this method to not have as many input/output options.
+ */
+	function value($options = array(), $field = null, $key = 'value') {
+		if ($options === null) {
+			$options = array();
+		} elseif (is_string($options)) {
+			$field = $options;
+			$options = 0;
+		}
+
+		if (is_array($options) && isset($options[$key])) {
+			return $options;
+		}
+
+		if (!empty($field)) {
+			$this->setEntity($field);
+		}
+
+		$view =& ClassRegistry::getObject('view');
+		$result = null;
+
+		$entity = $view->entity();
+		if (!empty($this->data) && !empty($entity)) {
+			$result = Set::extract(join('.', $entity), $this->data);
+		}
+
+		$habtmKey = $this->field();
+		if (empty($result) && isset($this->data[$habtmKey][$habtmKey]) && is_array($this->data[$habtmKey])) {
+			$result = $this->data[$habtmKey][$habtmKey];
+		} elseif (empty($result) && isset($this->data[$habtmKey]) && is_array($this->data[$habtmKey])) {
+			if (ClassRegistry::isKeySet($habtmKey)) {
+				$model =& ClassRegistry::getObject($habtmKey);
+				$result = $this->__selectedArray($this->data[$habtmKey], $model->primaryKey);
+			}
+		}
+
+		if (is_array($result)) {
+			if (array_key_exists($view->fieldSuffix, $result)) {
+				$result = $result[$view->fieldSuffix];
+			}
+		}
+
+		if (is_array($options)) {
+			if ($result === null && 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.  Will set the
+ * name, value, and id attributes for an array of html attributes. Will also
+ * add a 'form-error' class if the field contains validation errors.
+ *
+ * @param string $field The field name to initialize.
+ * @param array $options Array of options to use while initializing an input field.
+ * @return array Array options for the form input.
+ * @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() !== null) {
+			$options = $this->addClass($options, 'form-error');
+		}
+		return $options;
+	}
+
+/**
+ * Adds the given class to the element options
+ *
+ * @param array $options Array options/attributes to add a class to
+ * @param string $class The classname being added.
+ * @param string $key the key to use for class.
+ * @return array Array of options with $key set.
+ * @access public
+ */
+	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
+ * @deprecated This method will be removed in future versions.
+ */
+	function output($str) {
+		return $str;
+	}
+
+/**
+ * Before render callback. beforeRender is called before the view file is rendered.
+ *
+ * Overridden in subclasses.
+ *
+ * @return void
+ * @access public
+ */
+	function beforeRender() {
+	}
+
+/**
+ * After render callback.  afterRender is called after the view file is rendered
+ * but before the layout has been rendered.
+ *
+ * Overridden in subclasses.
+ *
+ * @return void
+ * @access public
+ */
+	function afterRender() {
+	}
+
+/**
+ * Before layout callback.  beforeLayout is called before the layout is rendered.
+ *
+ * Overridden in subclasses.
+ *
+ * @return void
+ * @access public
+ */
+	function beforeLayout() {
+	}
+
+/**
+ * After layout callback.  afterLayout is called after the layout has rendered.
+ *
+ * Overridden in subclasses.
+ *
+ * @return void
+ * @access public
+ */
+	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
+ *
+ * @return void
+ * @access private
+ */
+	function __reset() {
+		$this->__tainted = null;
+		$this->__cleaned = null;
+	}
+
+/**
+ * Removes harmful content from output
+ *
+ * @return void
+ * @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);
+	}
+}

Added: trunk/src/Web/cake/libs/view/helpers/ajax.php
===================================================================
--- trunk/src/Web/cake/libs/view/helpers/ajax.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/helpers/ajax.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,1036 @@
+<?php
+/**
+ * Helper for AJAX operations.
+ *
+ * Helps doing AJAX using the Prototype library.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * AjaxHelper helper library.
+ *
+ * Helps doing AJAX using the Prototype library.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @link http://book.cakephp.org/view/1358/AJAX
+ */
+class AjaxHelper extends AppHelper {
+
+/**
+ * Included helpers.
+ *
+ * @var array
+ */
+	var $helpers = array('Html', 'Javascript', 'Form');
+
+/**
+ * HtmlHelper instance
+ *
+ * @var HtmlHelper
+ * @access public
+ */
+	var $Html = null;
+
+/**
+ * JavaScriptHelper instance
+ *
+ * @var JavaScriptHelper
+ * @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', 'scroll', 'scrollSensitivity',
+		'onStart', 'onDrag', 'onEnd'
+	);
+
+/**
+ * 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 mixed $url Cake-relative URL or array of URL parameters, or external URL (starts with http://)
+ * @param array $options Options for JavaScript function
+ * @param string $confirm Confirmation message. Calls up a JavaScript confirm() message.
+ *
+ * @return string HTML code for link to remote action
+ * @link http://book.cakephp.org/view/1363/link
+ */
+	function link($title, $url = null, $options = array(), $confirm = null) {
+		if (!isset($url)) {
+			$url = $title;
+		}
+		if (!isset($options['url'])) {
+			$options['url'] = $url;
+		}
+
+		if (!empty($confirm)) {
+			$options['confirm'] = $confirm;
+			unset($confirm);
+		}
+		$htmlOptions = $this->__getHtmlOptions($options, array('url'));
+		$options += array('safe' => true);
+
+		unset($options['escape']);
+		if (empty($options['fallback']) || !isset($options['fallback'])) {
+			$options['fallback'] = $url;
+		}
+		$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, $url, $htmlOptions);
+		$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.
+ * @link http://book.cakephp.org/view/1364/remoteFunction
+ */
+	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'] = implode(' ', $options['update']);
+			}
+			$options['requestHeaders']['X-Update'] = $options['update'];
+		} else {
+			$func = "new Ajax.Request(";
+		}
+
+		$url = isset($options['url']) ? $options['url'] : "";
+		if (empty($options['safe'])) {
+			$url = $this->url($url);
+		} else {
+			$url = Router::url($url);
+		}
+
+		$func .= "'" . $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()
+ * @link http://book.cakephp.org/view/1365/remoteTimer
+ */
+	function remoteTimer($options = null) {
+		$frequency = (isset($options['frequency'])) ? $options['frequency'] : 10;
+		$callback = $this->remoteFunction($options);
+		$code = "new PeriodicalExecuter(function(pe) {{$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()
+ * @link http://book.cakephp.org/view/1366/form
+ */
+	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()
+ * @link http://book.cakephp.org/view/1367/submit
+ */
+	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
+ * @link http://book.cakephp.org/view/1368/observeField
+ */
+	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
+ * @link http://book.cakephp.org/view/1369/observeForm
+ */
+	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
+ * @link http://book.cakephp.org/view/1370/autoComplete
+ */
+	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 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->Html->tags['blockend'];
+	}
+
+/**
+ * Detects Ajax requests
+ *
+ * @return boolean True if the current request is a Prototype Ajax update call
+ * @link http://book.cakephp.org/view/1371/isAjax
+ */
+	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
+ * @link http://book.cakephp.org/view/1372/drag-drop
+ */
+	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
+ * @link http://book.cakephp.org/view/1372/drag-drop
+ */
+	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
+ * @link http://book.cakephp.org/view/1373/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
+ * @link http://book.cakephp.org/view/1374/editor
+ */
+	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
+ * @link http://book.cakephp.org/view/1375/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'] = '[' . implode(', ', $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 = implode(', ', $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)) {
+				echo $this->__ajaxBuffer[$divs[0]];
+			} else {
+				foreach ($this->__ajaxBuffer as $key => $val) {
+					if (in_array($key, $divs)) {
+						$data[] = $key . ':"' . rawurlencode($val) . '"';
+					}
+				}
+				$out  = 'var __ajaxUpdater__ = {' . implode(", \n", $data) . '};' . "\n";
+				$out .= 'for (n in __ajaxUpdater__) { if (typeof __ajaxUpdater__[n] == "string"';
+				$out .= ' && $(n)) Element.update($(n), unescape(decodeURIComponent(';
+				$out .= '__ajaxUpdater__[n]))); }';
+				echo $this->Javascript->codeBlock($out, false);
+			}
+			$scripts = $this->Javascript->getCache();
+
+			if (!empty($scripts)) {
+				echo $this->Javascript->codeBlock($scripts, false);
+			}
+			$this->_stop();
+		}
+	}
+}

Added: trunk/src/Web/cake/libs/view/helpers/app_helper.php
===================================================================
--- trunk/src/Web/cake/libs/view/helpers/app_helper.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/helpers/app_helper.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Application level View Helper
+ *
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('View', 'Helper', false);
+
+/**
+ * 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 {
+}

Added: trunk/src/Web/cake/libs/view/helpers/cache.php
===================================================================
--- trunk/src/Web/cake/libs/view/helpers/cache.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/helpers/cache.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,299 @@
+<?php
+/**
+ * CacheHelper helps create full page view caching.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @since         CakePHP(tm) v 1.0.0.2277
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * CacheHelper helps create full page view caching.
+ *
+ * When using CacheHelper you don't call any of its methods, they are all automatically
+ * called by View, and use the $cacheAction settings set in the controller.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @link http://book.cakephp.org/view/1376/Cache
+ */
+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();
+
+/**
+ * cache action time
+ *
+ * @var object
+ * @access public
+ */
+	var $cacheAction;
+
+/**
+ * Counter used for counting nocache section tags.
+ *
+ * @var integer
+ */
+	var $_counter = 0;
+
+/**
+ * Main method used to cache a view
+ *
+ * @param string $file File to cache
+ * @param string $out output to cache
+ * @param boolean $cache Whether or not a cache file should be written.
+ * @return string view ouput
+ */
+	function cache($file, $out, $cache = false) {
+		$cacheTime = 0;
+		$useCallbacks = false;
+		if (is_array($this->cacheAction)) {
+			$keys = array_keys($this->cacheAction);
+			$index = null;
+
+			foreach ($keys as $action) {
+				if ($action == $this->params['action']) {
+					$index = $action;
+					break;
+				}
+			}
+
+			if (!isset($index) && $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 (isset($options['duration'])) {
+				$cacheTime = $options['duration'];
+			}
+			if (isset($options['callbacks'])) {
+				$useCallbacks = $options['callbacks'];
+			}
+		} else {
+			$cacheTime = $this->cacheAction;
+		}
+
+		if ($cacheTime != '' && $cacheTime > 0) {
+			$out = preg_replace_callback('/<cake\:nocache>/', array($this, '_replaceSection'), $out);
+
+			$this->__parseFile($file, $out);
+			if ($cache === true) {
+				$cached = $this->__parseOutput($out);
+				$this->__writeFile($cached, $cacheTime, $useCallbacks);
+				$out = $this->_stripTags($out);
+			}
+			return $out;
+		} else {
+			return $out;
+		}
+	}
+
+/**
+ * Parse file searching for no cache tags
+ *
+ * @param string $file The filename that needs to be parsed.
+ * @param string $cache The cached content
+ * @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:\d{3}>(?<=<cake:nocache:\d{3}>)[\\s\\S]*?(?=<\/cake:nocache>)<\/cake:nocache>)/i', $cache, $outputResult, PREG_PATTERN_ORDER);
+		preg_match_all('/(?<=<cake:nocache>)([\\s\\S]*?)(?=<\/cake:nocache>)/i', $file, $fileResult, PREG_PATTERN_ORDER);
+		$fileResult = $fileResult[0];
+		$outputResult = $outputResult[0];
+
+		if (!empty($this->__replace)) {
+			foreach ($outputResult as $i => $element) {
+				$index = array_search($element, $this->__match);
+				if ($index !== false) {
+					unset($outputResult[$i]);
+				}
+			}
+			$outputResult = array_values($outputResult);
+		}
+
+		if (!empty($fileResult)) {
+			$i = 0;
+			foreach ($fileResult as $cacheBlock) {
+				if (isset($outputResult[$i])) {
+					$this->__replace[] = $cacheBlock;
+					$this->__match[] = $outputResult[$i];
+				}
+				$i++;
+			}
+		}
+	}
+
+/**
+ * Munges the output from a view with cache tags, and numbers the sections.
+ * This helps solve issues with empty/duplicate content.
+ *
+ * @param string $content The content to munge.
+ * @return string The content with cake:nocache tags replaced.
+ */
+	function _replaceSection($matches) {
+		$this->_counter += 1;
+		return sprintf('<cake:nocache:%03d>', $this->_counter);
+	}
+
+/**
+ * Strip cake:nocache tags from a string. Since View::render()
+ * only removes un-numbered nocache tags, remove all the numbered ones.
+ * This is the complement to _replaceSection.
+ *
+ * @param string $content String to remove tags from.
+ * @return string String with tags removed.
+ */
+	function _stripTags($content) {
+		return preg_replace('#<\/?cake\:nocache(\:\d{3})?>#', '', $content);
+	}
+
+/**
+ * Parse the output and replace cache tags
+ *
+ * @param string $cache Output to replace content in.
+ * @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 $content view content to write to a cache file.
+ * @param sting $timestamp Duration to set for cache file.
+ * @return boolean success of caching 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 = \'' . addslashes($this->here) . '\';
+				$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->viewVars = $this->viewVars = unserialize(base64_decode(\'' . base64_encode(serialize($this->viewVars)) . '\'));
+				$controller->theme = $this->theme = \'' . $this->theme . '\';
+				Router::setRequestInfo(array($this->params, array(\'base\' => $this->base, \'webroot\' => $this->webroot)));';
+
+		if ($useCallbacks == true) {
+			$file .= '
+				$controller->constructClasses();
+				$controller->Component->initialize($controller);
+				$controller->beforeFilter();
+				$controller->Component->startup($controller);
+				$this->params = $controller->params;';
+		}
+
+		$file .= '
+				$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};
+					$this->{$helper} =& $loadedHelpers[$helper];
+				}
+				extract($this->viewVars, EXTR_SKIP);
+		?>';
+		$content = preg_replace("/(<\\?xml)/", "<?php echo '$1';?>",$content);
+		$file .= $content;
+		return cache('views' . DS . $cache, $file, $timestamp);
+	}
+}

Added: trunk/src/Web/cake/libs/view/helpers/form.php
===================================================================
--- trunk/src/Web/cake/libs/view/helpers/form.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/helpers/form.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,2226 @@
+<?php
+/**
+ * Automatic generation of HTML FORMs from given data.
+ *
+ * Used for scaffolding.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Form helper library.
+ *
+ * Automatic generation of HTML FORMs from given data.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @link http://book.cakephp.org/view/1383/Form
+ */
+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();
+
+/**
+ * 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;
+
+/**
+ * The default model being used for the current form.
+ *
+ * @var string
+ * @access public
+ */
+	var $defaultModel = null;
+
+
+/**
+ * Persistent default options used by input(). Set by FormHelper::create().
+ *
+ * @var array
+ * @access protected
+ */
+	var $_inputDefaults = array();
+
+/**
+ * Introspects model information and extracts information related
+ * to validation, field length and field type. Appends information into
+ * $this->fieldset.
+ *
+ * @return Model Returns a model instance
+ * @access protected
+ */
+	function &_introspectModel($model) {
+		$object = null;
+		if (is_string($model) && strpos($model, '.') !== false) {
+			$path = explode('.', $model);
+			$model = end($path);
+		}
+
+		if (ClassRegistry::isKeySet($model)) {
+			$object =& ClassRegistry::getObject($model);
+		}
+
+		if (!empty($object)) {
+			$fields = $object->schema();
+			foreach ($fields as $key => $value) {
+				unset($fields[$key]);
+				$fields[$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 ($this->_isRequiredField($validateProperties)) {
+						$validates[] = $validateField;
+					}
+				}
+			}
+			$defaults = array('fields' => array(), 'key' => 'id', 'validates' => array());
+			$key = $object->primaryKey;
+			$this->fieldset[$model] = array_merge($defaults, compact('fields', 'key', 'validates'));
+		}
+
+		return $object;
+	}
+
+/**
+ * Returns if a field is required to be filled based on validation properties from the validating object
+ *
+ * @return boolean true if field is required to be filled, false otherwise
+ * @access protected
+ */
+	function _isRequiredField($validateProperties) {
+		$required = false;
+		if (is_array($validateProperties)) {
+
+			$dims = Set::countDim($validateProperties);
+			if ($dims == 1 || ($dims == 2 && isset($validateProperties['rule']))) {
+				$validateProperties = array($validateProperties);
+			}
+
+			foreach ($validateProperties as $rule => $validateProp) {
+				if (isset($validateProp['allowEmpty']) && $validateProp['allowEmpty'] === true) {
+					return false;
+				}
+				$rule = isset($validateProp['rule']) ? $validateProp['rule'] : false;
+				$required = $rule || empty($validateProp);
+				if ($required) {
+					break;
+				}
+			}
+		}
+		return $required;
+	}
+
+/**
+ * Returns an HTML FORM element.
+ *
+ * ### Options:
+ *
+ * - `type` Form method defaults to POST
+ * - `action`  The controller action the form submits to, (optional).
+ * - `url`  The url the form submits to. Can be a string or a url array.  If you use 'url'
+ *    you should leave 'action' undefined.
+ * - `default`  Allows for the creation of Ajax forms. Set this to false to prevent the default event handler.
+ *   Will create an onsubmit attribute if it doesn't not exist. If it does, default action suppression
+ *   will be appended.
+ * - `onsubmit` Used in conjunction with 'default' to create ajax forms.
+ * - `inputDefaults` set the default $options for FormHelper::input(). Any options that would
+ *    be set when using FormHelper::input() can be set here.  Options set with `inputDefaults`
+ *    can be overridden when calling input()
+ * - `encoding` Set the accept-charset encoding for the form.  Defaults to `Configure::read('App.encoding')`
+ *
+ * @access public
+ * @param string $model The model object which the form is being defined for
+ * @param array $options An array of html attributes and options.
+ * @return string An formatted opening FORM tag.
+ * @link http://book.cakephp.org/view/1384/Creating-Forms
+ */
+	function create($model = null, $options = array()) {
+		$created = $id = false;
+		$append = '';
+		$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];
+			$this->defaultModel = $this->params['models'][0];
+		} elseif (empty($model) && empty($this->params['models'])) {
+			$model = false;
+		}
+
+		$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;
+				}
+			}
+		}
+
+		$object = $this->_introspectModel($model);
+		$this->setEntity($model . '.', true);
+
+		$modelEntity = $this->model();
+		if (isset($this->fieldset[$modelEntity]['key'])) {
+			$data = $this->fieldset[$modelEntity];
+			$recordExists = (
+				isset($this->data[$model]) &&
+				!empty($this->data[$model][$data['key']]) &&
+				!is_array($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,
+			'encoding' => strtolower(Configure::read('App.encoding')),
+			'inputDefaults' => array()),
+		$options);
+		$this->_inputDefaults = $options['inputDefaults'];
+		unset($options['inputDefaults']);
+
+		if (empty($options['url']) || is_array($options['url'])) {
+			if (empty($options['url']['controller'])) {
+				if (!empty($model) && $model != $this->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'] = $this->params['action'];
+			}
+
+			$actionDefaults = array(
+				'plugin' => $this->plugin,
+				'controller' => $view->viewPath,
+				'action' => $options['action']
+			);
+			if (!empty($options['action']) && !isset($options['id'])) {
+				$options['id'] = $this->domId($options['action'] . 'Form');
+			}
+			$options['action'] = array_merge($actionDefaults, (array)$options['url']);
+			if (empty($options['action'][0])) {
+				$options['action'][0] = $id;
+			}
+		} 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']);
+
+		$action = $this->url($options['action']);
+		unset($options['type'], $options['action']);
+
+		if ($options['default'] == false) {
+			if (!isset($options['onsubmit'])) {
+				$options['onsubmit'] = '';
+			}
+			$htmlAttributes['onsubmit'] = $options['onsubmit'] . 'event.returnValue = false; return false;';
+		}
+		unset($options['default']);
+
+		if (!empty($options['encoding'])) {
+			$htmlAttributes['accept-charset'] = $options['encoding'];
+			unset($options['encoding']);
+		}
+
+		$htmlAttributes = array_merge($options, $htmlAttributes);
+
+		$this->fields = array();
+		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['block'], ' style="display:none;"', $append);
+		}
+
+		$this->setEntity($model . '.', true);
+		$attributes = sprintf('action="%s" ', $action) . $this->_parseAttributes($htmlAttributes, null, '');
+		return 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. Options can be either a string or an array.
+ *
+ * {{{
+ * 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"
+ * }}}
+ *
+ * @param mixed $options as a string will use $options as the value of button,
+ * @return string a closing FORM tag optional submit button.
+ * @access public
+ * @link http://book.cakephp.org/view/1389/Closing-the-Form
+ */
+	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;
+			}
+			$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 $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;
+		}
+		$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 = implode(array_keys($locked), '|');
+
+		$out = $this->hidden('_Token.fields', array(
+			'value' => urlencode($fields . ':' . $locked),
+			'id' => 'TokenFields' . mt_rand()
+		));
+		$out = sprintf($this->Html->tags['block'], ' style="display:none;"', $out);
+		return $out;
+	}
+
+/**
+ * Determine which fields of a form should be used for hash.
+ * Populates $this->fields
+ *
+ * @param mixed $field Reference to field to be secured
+ * @param mixed $value Field value, if value should not be tampered with.
+ * @return void
+ * @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;
+				}
+			}
+		}
+
+		$last = end($field);
+		if (is_numeric($last) || empty($last)) {
+			array_pop($field);
+		}
+
+		$field = implode('.', $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
+ * @link http://book.cakephp.org/view/1426/isFieldError
+ */
+	function isFieldError($field) {
+		$this->setEntity($field);
+		return (bool)$this->tagIsInvalid();
+	}
+
+/**
+ * Returns a formatted error message for given FORM field, NULL if no errors.
+ *
+ * ### Options:
+ *
+ * - `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
+ *
+ * @param string $field A field name, like "Modelname.fieldname"
+ * @param mixed $text Error message or array of $options. If array, `attributes` key
+ * will get used as html attributes for error container
+ * @param array $options Rendering options for <div /> wrapper tag
+ * @return string If there are errors this method returns an error message, otherwise null.
+ * @access public
+ * @link http://book.cakephp.org/view/1423/error
+ */
+	function error($field, $text = null, $options = array()) {
+		$defaults = array('wrap' => true, 'class' => 'error-message', 'escape' => true);
+		$options = array_merge($defaults, $options);
+		$this->setEntity($field);
+
+		$error = $this->tagIsInvalid();
+		if ($error !== null) {
+			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)) {
+				$options = array_merge($options, array_intersect_key($text, $defaults));
+				if (isset($text['attributes']) && is_array($text['attributes'])) {
+					$options = array_merge($options, $text['attributes']);
+				}
+				$text = isset($text[$error]) ? $text[$error] : null;
+				unset($options[$error]);
+			}
+
+			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. Will automatically generate
+ * a for attribute if one is not provided.
+ *
+ * @param string $fieldName This should be "Modelname.fieldname"
+ * @param string $text Text that will appear in the label field.
+ * @param mixed $options An array of HTML attributes, or a string, to be used as a class name.
+ * @return string The formatted LABEL element
+ * @link http://book.cakephp.org/view/1427/label
+ */
+	function label($fieldName = null, $text = null, $options = 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 (is_string($options)) {
+			$options = array('class' => $options);
+		}
+
+		if (isset($options['for'])) {
+			$labelFor = $options['for'];
+			unset($options['for']);
+		} else {
+			$labelFor = $this->domId($fieldName);
+		}
+
+		return sprintf(
+			$this->Html->tags['label'],
+			$labelFor,
+			$this->_parseAttributes($options), $text
+		);
+	}
+
+/**
+ * Generate a set of inputs for `$fields`.  If $fields is null the current model
+ * will be used.
+ *
+ * In addition to controller fields output, `$fields` can be used to control legend
+ * and fieldset rendering with the `fieldset` and `legend` keys.
+ * `$form->inputs(array('legend' => 'My legend'));` Would generate an input set with
+ * a custom legend.  You can customize individual inputs through `$fields` as well.
+ *
+ * {{{
+ *	$form->inputs(array(
+ *		'name' => array('label' => 'custom label')
+ *	));
+ * }}}
+ *
+ * In addition to fields control, inputs() allows you to use a few additional options.
+ *
+ * - `fieldset` Set to false to disable the fieldset. If a string is supplied it will be used as
+ *    the classname for the fieldset element.
+ * - `legend` Set to false to disable the legend for the generated input set. Or supply a string
+ *    to customize the legend text.
+ *
+ * @param mixed $fields An array of fields to generate inputs for, or null.
+ * @param array $blacklist a simple array of fields to not create inputs for.
+ * @return string Completed form inputs.
+ * @access public
+ */
+	function inputs($fields = null, $blacklist = null) {
+		$fieldset = $legend = true;
+		$model = $this->model();
+		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[$model]['fields']);
+		}
+
+		if ($legend === true) {
+			$actionName = __('New %s', true);
+			$isEdit = (
+				strpos($this->action, 'update') !== false ||
+				strpos($this->action, 'edit') !== false
+			);
+			if ($isEdit) {
+				$actionName = __('Edit %s', true);
+			}
+			$modelName = Inflector::humanize(Inflector::underscore($model));
+			$legend = sprintf($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
+ *
+ * ### Options
+ *
+ * See each field type method for more information. Any options that are part of
+ * $attributes or $options for the different **type** methods can be included in `$options` for input().i
+ * Additionally, any unknown keys that are not in the list below, or part of the selected type's options
+ * will be treated as a regular html attribute for the generated input.
+ *
+ * - `type` - Force the type of widget you want. e.g. `type => 'select'`
+ * - `label` - Either a string label, or an array of options for the label. See FormHelper::label()
+ * - `div` - Either `false` to disable the div, or an array of options for the div.
+ *    See HtmlHelper::div() for more options.
+ * - `options` - for widgets that take options e.g. radio, select
+ * - `error` - control the error message that is produced
+ * - `empty` - String or boolean to enable empty select box options.
+ * - `before` - Content to place before the label + input.
+ * - `after` - Content to place after the label + input.
+ * - `between` - Content to place between the label + input.
+ * - `format` - format template for element order. Any element that is not in the array, will not be in the output.
+ *    - Default input format order: array('before', 'label', 'between', 'input', 'after', 'error')
+ *    - Default checkbox format order: array('before', 'input', 'between', 'label', 'after', 'error')
+ *    - Hidden input will not be formatted
+ *    - Radio buttons cannot have the order of input and label elements controlled with these settings.
+ *
+ * @param string $fieldName This should be "Modelname.fieldname"
+ * @param array $options Each type of input takes different options.
+ * @return string Completed form widget.
+ * @access public
+ * @link http://book.cakephp.org/view/1390/Automagic-Form-Elements
+ */
+	function input($fieldName, $options = array()) {
+		$this->setEntity($fieldName);
+
+		$options = array_merge(
+			array('before' => null, 'between' => null, 'after' => null, 'format' => null),
+			$this->_inputDefaults,
+			$options
+		);
+
+		$modelKey = $this->model();
+		$fieldKey = $this->field();
+		if (!isset($this->fieldset[$modelKey])) {
+			$this->_introspectModel($modelKey);
+		}
+
+		if (!isset($options['type'])) {
+			$magicType = true;
+			$options['type'] = 'text';
+			if (isset($options['options'])) {
+				$options['type'] = 'select';
+			} elseif (in_array($fieldKey, array('psword', 'passwd', 'password'))) {
+				$options['type'] = 'password';
+			} elseif (isset($this->fieldset[$modelKey]['fields'][$fieldKey])) {
+				$fieldDef = $this->fieldset[$modelKey]['fields'][$fieldKey];
+				$type = $fieldDef['type'];
+				$primaryKey = $this->fieldset[$modelKey]['key'];
+			}
+
+			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 ($fieldKey == $primaryKey) {
+					$options['type'] = 'hidden';
+				}
+			}
+			if (preg_match('/_id$/', $fieldKey) && $options['type'] !== 'hidden') {
+				$options['type'] = 'select';
+			}
+
+			if ($modelKey === $fieldKey) {
+				$options['type'] = 'select';
+				if (!isset($options['multiple'])) {
+					$options['multiple'] = 'multiple';
+				}
+			}
+		}
+		$types = array('checkbox', 'radio', 'select');
+
+		if (
+			(!isset($options['options']) && in_array($options['type'], $types)) ||
+			(isset($magicType) && $options['type'] == 'text')
+		) {
+			$view =& ClassRegistry::getObject('view');
+			$varName = Inflector::variable(
+				Inflector::pluralize(preg_replace('/_id$/', '', $fieldKey))
+			);
+			$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;
+		}
+
+		$divOptions = array();
+		$div = $this->_extractOption('div', $options, true);
+		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 (
+				isset($this->fieldset[$modelKey]) &&
+				in_array($fieldKey, $this->fieldset[$modelKey]['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'])) {
+				$radioOptions = (array)$options['options'];
+				unset($options['options']);
+			}
+		}
+
+		if ($label !== false) {
+			$label = $this->_inputLabel($fieldName, $label, $options);
+		}
+
+		$error = $this->_extractOption('error', $options, null);
+		unset($options['error']);
+
+		$selected = $this->_extractOption('selected', $options, null);
+		unset($options['selected']);
+
+		if (isset($options['rows']) || isset($options['cols'])) {
+			$options['type'] = 'textarea';
+		}
+
+		if ($options['type'] === 'datetime' || $options['type'] === 'date' || $options['type'] === 'time' || $options['type'] === 'select') {
+			$options += array('empty' => false);
+		}
+		if ($options['type'] === 'datetime' || $options['type'] === 'date' || $options['type'] === 'time') {
+			$dateFormat = $this->_extractOption('dateFormat', $options, 'MDY');
+			$timeFormat = $this->_extractOption('timeFormat', $options, 12);
+			unset($options['dateFormat'], $options['timeFormat']);
+		}
+
+		$type = $options['type'];
+		$out = array_merge(
+			array('before' => null, 'label' => null, 'between' => null, 'input' => null, 'after' => null, 'error' => null),
+			array('before' => $options['before'], 'label' => $label, 'between' => $options['between'], 'after' => $options['after'])
+		);
+		$format = null;
+		if (is_array($options['format']) && in_array('input', $options['format'])) {
+			$format = $options['format'];
+		}
+		unset($options['type'], $options['before'], $options['between'], $options['after'], $options['format']);
+
+		switch ($type) {
+			case 'hidden':
+				$input = $this->hidden($fieldName, $options);
+				$format = array('input');
+				unset($divOptions);
+			break;
+			case 'checkbox':
+				$input = $this->checkbox($fieldName, $options);
+				$format = $format ? $format : array('before', 'input', 'between', 'label', 'after', 'error');
+			break;
+			case 'radio':
+				$input = $this->radio($fieldName, $radioOptions, $options);
+			break;
+			case 'text':
+			case 'password':
+			case 'file':
+				$input = $this->{$type}($fieldName, $options);
+			break;
+			case 'select':
+				$options += array('options' => array());
+				$list = $options['options'];
+				unset($options['options']);
+				$input = $this->select($fieldName, $list, $selected, $options);
+			break;
+			case 'time':
+				$input = $this->dateTime($fieldName, null, $timeFormat, $selected, $options);
+			break;
+			case 'date':
+				$input = $this->dateTime($fieldName, $dateFormat, null, $selected, $options);
+			break;
+			case 'datetime':
+				$input = $this->dateTime($fieldName, $dateFormat, $timeFormat, $selected, $options);
+			break;
+			case 'textarea':
+			default:
+				$input = $this->textarea($fieldName, $options + array('cols' => '30', 'rows' => '6'));
+			break;
+		}
+
+		if ($type != 'hidden' && $error !== false) {
+			$errMsg = $this->error($fieldName, $error);
+			if ($errMsg) {
+				$divOptions = $this->addClass($divOptions, 'error');
+				$out['error'] = $errMsg;
+			}
+		}
+
+		$out['input'] = $input;
+		$format = $format ? $format : array('before', 'label', 'between', 'input', 'after', 'error');
+		$output = '';
+		foreach ($format as $element) {
+			$output .= $out[$element];
+			unset($out[$element]);
+		}
+
+		if (!empty($divOptions['tag'])) {
+			$tag = $divOptions['tag'];
+			unset($divOptions['tag']);
+			$output = $this->Html->tag($tag, $output, $divOptions);
+		}
+		return $output;
+	}
+
+/**
+ * Extracts a single option from an options array.
+ *
+ * @param string $name The name of the option to pull out.
+ * @param array $options The array of options you want to extract.
+ * @param mixed $default The default option value
+ * @return the contents of the option or default
+ * @access protected
+ */
+	function _extractOption($name, $options, $default = null) {
+		if (array_key_exists($name, $options)) {
+			return $options[$name];
+		}
+		return $default;
+	}
+
+/**
+ * Generate a label for an input() call.
+ *
+ * @param array $options Options for the label element.
+ * @return string Generated label element
+ * @access protected
+ */
+	function _inputLabel($fieldName, $label, $options) {
+		$labelAttributes = $this->domId(array(), 'for');
+		if ($options['type'] === 'date' || $options['type'] === 'datetime') {
+			if (isset($options['dateFormat']) && $options['dateFormat'] === 'NONE') {
+				$labelAttributes['for'] .= 'Hour';
+				$idKey = 'hour';
+			} else {
+				$labelAttributes['for'] .= 'Month';
+				$idKey = 'month';
+			}
+			if (isset($options['id']) && isset($options['id'][$idKey])) {
+				$labelAttributes['for'] = $options['id'][$idKey];
+			}
+		} elseif ($options['type'] === 'time') {
+			$labelAttributes['for'] .= 'Hour';
+			if (isset($options['id']) && isset($options['id']['hour'])) {
+				$labelAttributes['for'] = $options['id']['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']) && is_string($options['id'])) {
+			$labelAttributes = array_merge($labelAttributes, array('for' => $options['id']));
+		}
+		return $this->label($fieldName, $labelText, $labelAttributes);
+	}
+
+/**
+ * Creates a checkbox input widget.
+ *
+ * ### Options:
+ *
+ * - `value` - the value of the checkbox
+ * - `checked` - boolean indicate that this checkbox is checked.
+ * - `hiddenField` - boolean to indicate if you want the results of checkbox() to include
+ *    a hidden input with a value of ''.
+ * - `disabled` - create a disabled input.
+ *
+ * @param string $fieldName Name of a field, like this "Modelname.fieldname"
+ * @param array $options Array of HTML attributes.
+ * @return string An HTML text input element.
+ * @access public
+ * @link http://book.cakephp.org/view/1414/checkbox
+ */
+	function checkbox($fieldName, $options = array()) {
+		$options = $this->_initInputField($fieldName, $options) + array('hiddenField' => true);
+		$value = current($this->value());
+		$output = "";
+
+		if (empty($options['value'])) {
+			$options['value'] = 1;
+		} elseif (
+			(!isset($options['checked']) && !empty($value) && $value === $options['value']) ||
+			!empty($options['checked'])
+		) {
+			$options['checked'] = 'checked';
+		}
+		if ($options['hiddenField']) {
+			$hiddenOptions = array(
+				'id' => $options['id'] . '_', 'name' => $options['name'],
+				'value' => '0', 'secure' => false
+			);
+			if (isset($options['disabled']) && $options['disabled'] == true) {
+				$hiddenOptions['disabled'] = 'disabled';
+			}
+			$output = $this->hidden($fieldName, $hiddenOptions);
+		}
+		unset($options['hiddenField']);
+
+		return $output . sprintf(
+			$this->Html->tags['checkbox'],
+			$options['name'],
+			$this->_parseAttributes($options, array('name'), null, ' ')
+		);
+	}
+
+/**
+ * Creates a set of radio widgets. Will create a legend and fieldset
+ * by default.  Use $options to control this
+ *
+ * ### 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
+ * - `hiddenField` - boolean to indicate if you want the results of radio() to include
+ *    a hidden input with a value of ''. This is useful for creating radio sets that non-continuous
+ *
+ * @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, and special attributes above.
+ * @return string Completed radio widget set.
+ * @access public
+ * @link http://book.cakephp.org/view/1429/radio
+ */
+	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();
+
+		$hiddenField = isset($attributes['hiddenField']) ? $attributes['hiddenField'] : true;
+		unset($attributes['hiddenField']);
+
+		foreach ($options as $optValue => $optTitle) {
+			$optionsHere = array('value' => $optValue);
+
+			if (isset($value) && $value !== '' && $optValue == $value) {
+				$optionsHere['checked'] = 'checked';
+			}
+			$parsedOptions = $this->_parseAttributes(
+				array_merge($attributes, $optionsHere),
+				array('name', 'type', 'id'), '', ' '
+			);
+			$tagName = Inflector::camelize(
+				$attributes['id'] . '_' . Inflector::slug($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 ($hiddenField) {
+			if (!isset($value) || $value === '') {
+				$hidden = $this->hidden($fieldName, array(
+					'id' => $attributes['id'] . '_', 'value' => '', 'name' => $attributes['name']
+				));
+			}
+		}
+		$out = $hidden . implode($inbetween, $out);
+
+		if ($legend) {
+			$out = sprintf(
+				$this->Html->tags['fieldset'], '',
+				sprintf($this->Html->tags['legend'], $legend) . $out
+			);
+		}
+		return $out;
+	}
+
+/**
+ * Creates a text input widget.
+ *
+ * @param string $fieldName Name of a field, in the form "Modelname.fieldname"
+ * @param array $options Array of HTML attributes.
+ * @return string A generated HTML text input element
+ * @access public
+ * @link http://book.cakephp.org/view/1432/text
+ */
+	function text($fieldName, $options = array()) {
+		$options = $this->_initInputField($fieldName, array_merge(
+			array('type' => 'text'), $options
+		));
+		return 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 A generated password input.
+ * @access public
+ * @link http://book.cakephp.org/view/1428/password
+ */
+	function password($fieldName, $options = array()) {
+		$options = $this->_initInputField($fieldName, $options);
+		return sprintf(
+			$this->Html->tags['password'],
+			$options['name'],
+			$this->_parseAttributes($options, array('name'), null, ' ')
+		);
+	}
+
+/**
+ * Creates a textarea widget.
+ *
+ * ### Options:
+ *
+ * - `escape` - Whether or not the contents of the textarea should be escaped. Defaults to true.
+ *
+ * @param string $fieldName Name of a field, in the form "Modelname.fieldname"
+ * @param array $options Array of HTML attributes, and special options above.
+ * @return string A generated HTML text input element
+ * @access public
+ * @link http://book.cakephp.org/view/1433/textarea
+ */
+	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 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 of "Modelname.fieldname"
+ * @param array $options Array of HTML attributes.
+ * @return string A generated hidden input
+ * @access public
+ * @link http://book.cakephp.org/view/1425/hidden
+ */
+	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 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 A generated file input.
+ * @access public
+ * @link http://book.cakephp.org/view/1424/file
+ */
+	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 sprintf($this->Html->tags['file'], $options['name'], $attributes);
+	}
+
+/**
+ * Creates a `<button>` tag.  The type attribute defaults to `type="submit"`
+ * You can change it to a different value by using `$options['type']`.
+ *
+ * ### Options:
+ *
+ * - `escape` - HTML entity encode the $title of the button. Defaults to false.
+ *
+ * @param string $title The button's caption. Not automatically HTML encoded
+ * @param array $options Array of options and HTML attributes.
+ * @return string A HTML button tag.
+ * @access public
+ * @link http://book.cakephp.org/view/1415/button
+ */
+	function button($title, $options = array()) {
+		$options += array('type' => 'submit', 'escape' => false);
+		if ($options['escape']) {
+			$title = h($title);
+		}
+		return sprintf(
+			$this->Html->tags['button'],
+			$options['type'],
+			$this->_parseAttributes($options, array('type'), ' ', ''),
+			$title
+		);
+	}
+
+/**
+ * Creates a submit button element.  This method will generate `<input />` elements that
+ * can be used to submit, and reset forms by using $options.  image submits can be created by supplying an
+ * image path for $caption.
+ *
+ * ### Options
+ *
+ * - `div` - Include a wrapping div?  Defaults to true.  Accepts sub options similar to
+ *   FormHelper::input().
+ * - `before` - Content to include before the input.
+ * - `after` - Content to include after the input.
+ * - `type` - Set to 'reset' for reset inputs.  Defaults to 'submit'
+ * - Other attributes will be assigned to the input element.
+ *
+ * ### Options
+ *
+ * - `div` - Include a wrapping div?  Defaults to true.  Accepts sub options similar to
+ *   FormHelper::input().
+ * - Other attributes will be assigned to the input 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 Array of options.  See above.
+ * @return string A HTML submit button
+ * @access public
+ * @link http://book.cakephp.org/view/1431/submit
+ */
+	function submit($caption = null, $options = array()) {
+		if (!is_string($caption) && empty($caption)) {
+			$caption = __('Submit', true);
+		}
+		$out = null;
+		$div = true;
+
+		if (isset($options['div'])) {
+			$div = $options['div'];
+			unset($options['div']);
+		}
+		$options += array('type' => 'submit', 'before' => null, 'after' => null);
+		$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);
+		}
+
+		$before = $options['before'];
+		$after = $options['after'];
+		unset($options['before'], $options['after']);
+
+		if (strpos($caption, '://') !== false) {
+			unset($options['type']);
+			$out .=  $before . sprintf(
+				$this->Html->tags['submitimage'],
+				$caption,
+				$this->_parseAttributes($options, null, '', ' ')
+			) . $after;
+		} elseif (preg_match('/\.(jpg|jpe|jpeg|gif|png|ico)$/', $caption)) {
+			unset($options['type']);
+			if ($caption{0} !== '/') {
+				$url = $this->webroot(IMAGES_URL . $caption);
+			} else {
+				$caption = trim($caption, '/');
+				$url = $this->webroot($caption);
+			}
+			$out .= $before . sprintf(
+				$this->Html->tags['submitimage'],
+				$this->assetTimestamp($url),
+				$this->_parseAttributes($options, null, '', ' ')
+			) . $after;
+		} else {
+			$options['value'] = $caption;
+			$out .= $before . sprintf(
+				$this->Html->tags['submit'],
+				$this->_parseAttributes($options, null, '', ' ')
+			). $after;
+		}
+
+		if (isset($divOptions)) {
+			$tag = $divOptions['tag'];
+			unset($divOptions['tag']);
+			$out = $this->Html->tag($tag, $out, $divOptions);
+		}
+		return $out;
+	}
+
+/**
+ * Returns a formatted SELECT element.
+ *
+ * ### Attributes:
+ *
+ * - `showParents` - If included in the array and set to true, an additional option element
+ *   will be added for the parent of each option group. You can set an option with the same name
+ *   and it's key will be used for the value of the option.
+ * - `multiple` - show a multiple select box.  If set to 'checkbox' multiple checkboxes will be
+ *   created instead.
+ * - `empty` - If true, the empty select option is shown.  If a string,
+ *   that string is displayed as the empty element.
+ * - `escape` - If true contents of options will be HTML entity encoded. Defaults to true.
+ * - `class` - When using multiple = checkbox the classname to apply to the divs. Defaults to 'checkbox'.
+ *
+ * ### Using options
+ *
+ * A simple array will create normal options:
+ *
+ * {{{
+ * $options = array(1 => 'one', 2 => 'two);
+ * $this->Form->select('Model.field', $options));
+ * }}}
+ *
+ * While a nested options array will create optgroups with options inside them.
+ * {{{
+ * $options = array(
+ *    1 => 'bill',
+ *    'fred' => array(
+ *        2 => 'fred',
+ *        3 => 'fred jr.'
+ *     )
+ * );
+ * $this->Form->select('Model.field', $options);
+ * }}}
+ *
+ * In the above `2 => 'fred'` will not generate an option element.  You should enable the `showParents`
+ * attribute to show the fred option.
+ *
+ * @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.
+ * @return string Formatted SELECT element
+ * @access public
+ * @link http://book.cakephp.org/view/1430/select
+ */
+	function select($fieldName, $options = array(), $selected = null, $attributes = array()) {
+		$select = array();
+		$style = null;
+		$tag = null;
+		$attributes += array(
+			'class' => null,
+			'escape' => true,
+			'secure' => null,
+			'empty' => '',
+			'showParents' => false,
+			'hiddenField' => true
+		);
+
+		$escapeOptions = $this->_extractOption('escape', $attributes);
+		$secure = $this->_extractOption('secure', $attributes);
+		$showEmpty = $this->_extractOption('empty', $attributes);
+		$showParents = $this->_extractOption('showParents', $attributes);
+		$hiddenField = $this->_extractOption('hiddenField', $attributes);
+		unset($attributes['escape'], $attributes['secure'], $attributes['empty'], $attributes['showParents'], $attributes['hiddenField']);
+
+		$attributes = $this->_initInputField($fieldName, array_merge(
+			(array)$attributes, array('secure' => false)
+		));
+
+		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 (!isset($selected)) {
+			$selected = $attributes['value'];
+		}
+
+		if (!empty($attributes['multiple'])) {
+			$style = ($attributes['multiple'] === 'checkbox') ? 'checkbox' : null;
+			$template = ($style) ? 'checkboxmultiplestart' : 'selectmultiplestart';
+			$tag = $this->Html->tags[$template];
+			if ($hiddenField) {
+				$hiddenAttributes = array(
+					'value' => '',
+					'id' => $attributes['id'] . ($style ? '' : '_'),
+					'secure' => false,
+					'name' => $attributes['name']
+				);
+				$select[] = $this->hidden(null, $hiddenAttributes);
+			}
+		} else {
+			$tag = $this->Html->tags['selectstart'];
+		}
+
+		if (!empty($tag) || isset($template)) {
+			if (!isset($secure) || $secure == true) {
+				$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, 'name' => $attributes['name'], 'class' => $attributes['class'])
+		));
+
+		$template = ($style == 'checkbox') ? 'checkboxmultipleend' : 'selectend';
+		$select[] = $this->Html->tags[$template];
+		return implode("\n", $select);
+	}
+
+/**
+ * Returns a SELECT element for days.
+ *
+ * ### Attributes:
+ *
+ * - `empty` - If true, the empty select option is shown.  If a string,
+ *   that string is displayed as the empty element.
+ *
+ * @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
+ * @return string A generated day select box.
+ * @access public
+ * @link http://book.cakephp.org/view/1419/day
+ */
+	function day($fieldName, $selected = null, $attributes = array()) {
+		$attributes += array('empty' => true);
+		$selected = $this->__dateTimeSelected('day', $fieldName, $selected, $attributes);
+
+		if (strlen($selected) > 2) {
+			$selected = date('d', strtotime($selected));
+		} elseif ($selected === false) {
+			$selected = null;
+		}
+		return $this->select($fieldName . ".day", $this->__generateOptions('day'), $selected, $attributes);
+	}
+
+/**
+ * Returns a SELECT element for years
+ *
+ * ### Attributes:
+ *
+ * - `empty` - If true, the empty select option is shown.  If a string,
+ *   that string is displayed as the empty element.
+ * - `orderYear` - Ordering of year values in select options.
+ *   Possible values 'asc', 'desc'. Default 'desc'
+ *
+ * @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.
+ * @return string Completed year select input
+ * @access public
+ * @link http://book.cakephp.org/view/1416/year
+ */
+	function year($fieldName, $minYear = null, $maxYear = null, $selected = null, $attributes = array()) {
+		$attributes += array('empty' => true);
+		if ((empty($selected) || $selected === true) && $value = $this->value($fieldName)) {
+			if (is_array($value)) {
+				extract($value);
+				$selected = $year;
+			} else {
+				if (empty($value)) {
+					if (!$attributes['empty'] && !$maxYear) {
+						$selected = 'now';
+
+					} elseif (!$attributes['empty'] && $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, 'order' => 'desc');
+		if (isset($attributes['orderYear'])) {
+			$yearOptions['order'] = $attributes['orderYear'];
+			unset($attributes['orderYear']);
+		}
+		return $this->select(
+			$fieldName . '.year', $this->__generateOptions('year', $yearOptions),
+			$selected, $attributes
+		);
+	}
+
+/**
+ * Returns a SELECT element for months.
+ *
+ * ### Attributes:
+ *
+ * - `monthNames` - If false, 2 digit numbers will be used instead of text.
+ *   If a array, the given array will be used.
+ * - `empty` - If true, the empty select option is shown.  If a string,
+ *   that string is displayed as the empty element.
+ *
+ * @param string $fieldName Prefix name for the SELECT element
+ * @param string $selected Option which is selected.
+ * @param array $attributes Attributes for the select element
+ * @return string A generated month select dropdown.
+ * @access public
+ * @link http://book.cakephp.org/view/1417/month
+ */
+	function month($fieldName, $selected = null, $attributes = array()) {
+		$attributes += array('empty' => true);
+		$selected = $this->__dateTimeSelected('month', $fieldName, $selected, $attributes);
+
+		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
+		);
+	}
+
+/**
+ * Returns a SELECT element for hours.
+ *
+ * ### Attributes:
+ *
+ * - `empty` - If true, the empty select option is shown.  If a string,
+ *   that string is displayed as the empty element.
+ *
+ * @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
+ * @return string Completed hour select input
+ * @access public
+ * @link http://book.cakephp.org/view/1420/hour
+ */
+	function hour($fieldName, $format24Hours = false, $selected = null, $attributes = array()) {
+		$attributes += array('empty' => true);
+		$selected = $this->__dateTimeSelected('hour', $fieldName, $selected, $attributes);
+
+		if (strlen($selected) > 2) {
+			if ($format24Hours) {
+				$selected = date('H', strtotime($selected));
+			} else {
+				$selected = date('g', strtotime($selected));
+			}
+		} elseif ($selected === false) {
+			$selected = null;
+		}
+		return $this->select(
+			$fieldName . ".hour",
+			$this->__generateOptions($format24Hours ? 'hour24' : 'hour'),
+			$selected, $attributes
+		);
+	}
+
+/**
+ * Returns a SELECT element for minutes.
+ *
+ * ### Attributes:
+ *
+ * - `empty` - If true, the empty select option is shown.  If a string,
+ *   that string is displayed as the empty element.
+ *
+ * @param string $fieldName Prefix name for the SELECT element
+ * @param string $selected Option which is selected.
+ * @param string $attributes Array of Attributes
+ * @return string Completed minute select input.
+ * @access public
+ * @link http://book.cakephp.org/view/1421/minute
+ */
+	function minute($fieldName, $selected = null, $attributes = array()) {
+		$attributes += array('empty' => true);
+		$selected = $this->__dateTimeSelected('min', $fieldName, $selected, $attributes);
+
+		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
+		);
+	}
+
+/**
+ * Selects values for dateTime selects.
+ *
+ * @param string $select Name of element field. ex. 'day'
+ * @param string $fieldName Name of fieldName being generated ex. Model.created
+ * @param mixed $selected The current selected value.
+ * @param array $attributes Array of attributes, must contain 'empty' key.
+ * @return string Currently selected value.
+ * @access private
+ */
+	function __dateTimeSelected($select, $fieldName, $selected, $attributes) {
+		if ((empty($selected) || $selected === true) && $value = $this->value($fieldName)) {
+			if (is_array($value) && isset($value[$select])) {
+				$selected = $value[$select];
+			} else {
+				if (empty($value)) {
+					if (!$attributes['empty']) {
+						$selected = 'now';
+					}
+				} else {
+					$selected = $value;
+				}
+			}
+		}
+		return $selected;
+	}
+
+/**
+ * Returns a SELECT element for AM or PM.
+ *
+ * ### Attributes:
+ *
+ * - `empty` - If true, the empty select option is shown.  If a string,
+ *   that string is displayed as the empty element.
+ *
+ * @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 Completed meridian select input
+ * @access public
+ * @link http://book.cakephp.org/view/1422/meridian
+ */
+	function meridian($fieldName, $selected = null, $attributes = array()) {
+		$attributes += array('empty' => true);
+		if ((empty($selected) || $selected === true) && $value = $this->value($fieldName)) {
+			if (is_array($value)) {
+				extract($value);
+				$selected = $meridian;
+			} else {
+				if (empty($value)) {
+					if (!$attribues['empty']) {
+						$selected = date('a');
+					}
+				} else {
+					$selected = date('a', strtotime($value));
+				}
+			}
+		}
+
+		if ($selected === false) {
+			$selected = null;
+		}
+		return $this->select(
+			$fieldName . ".meridian", $this->__generateOptions('meridian'),
+			$selected, $attributes
+		);
+	}
+
+/**
+ * Returns a set of SELECT elements for a full datetime setup: day, month and year, and then time.
+ *
+ * ### Attributes:
+ *
+ * - `monthNames` If false, 2 digit numbers will be used instead of text.
+ *   If a array, the given array will be used.
+ * - `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 '-'
+ * - `empty` - If true, the empty select option is shown.  If a string,
+ *   that string is displayed as the empty element.
+ * - `value` | `default` The default value to be used by the input.  A value in `$this->data`
+ *   matching the field name will override this value.  If no default is provided `time()` will be used.
+ *
+ * @param string $fieldName Prefix name for the SELECT element
+ * @param string $dateFormat DMY, MDY, YMD.
+ * @param string $timeFormat 12, 24.
+ * @param string $selected Option which is selected.
+ * @param string $attributes array of Attributes
+ * @return string Generated set of select boxes for the date and time formats chosen.
+ * @access public
+ * @link http://book.cakephp.org/view/1418/dateTime
+ */
+	function dateTime($fieldName, $dateFormat = 'DMY', $timeFormat = '12', $selected = null, $attributes = array()) {
+		$attributes += array('empty' => true);
+		$year = $month = $day = $hour = $min = $meridian = null;
+
+		if (empty($selected)) {
+			$selected = $this->value($attributes, $fieldName);
+			if (isset($selected['value'])) {
+				$selected = $selected['value'];
+			} else {
+				$selected = null;
+			}
+		}
+
+		if ($selected === null && $attributes['empty'] != 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 (!empty($timeFormat)) {
+					$time = explode(':', $days[1]);
+
+					if (($time[0] > 12) && $timeFormat == '12') {
+						$time[0] = $time[0] - 12;
+						$meridian = 'pm';
+					} elseif ($time[0] == '12' && $timeFormat == '12') {
+						$meridian = 'pm';
+					} elseif ($time[0] == '00' && $timeFormat == '12') {
+						$time[0] = 12;
+					} elseif ($time[0] > 12) {
+						$meridian = 'pm';
+					}
+					if ($time[0] == 0 && $timeFormat == '12') {
+						$time[0] = 12;
+					}
+					$hour = $min = null;
+					if (isset($time[1])) {
+						$hour = $time[0];
+						$min = $time[1];
+					}
+				}
+			}
+		}
+
+		$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
+				$attributes['id'] += array(
+					'month' => '', 'year' => '', 'day' => '',
+					'hour' => '', 'minute' => '', 'meridian' => ''
+				);
+				foreach ($elements as $element) {
+					$selectAttrName = 'select' . $element . 'Attr';
+					${$selectAttrName} = $attributes;
+					${$selectAttrName}['id'] = $attributes['id'][strtolower($element)];
+				}
+			}
+		} else {
+			// build the selectAttrName with empty id's to pass
+			foreach ($elements as $element) {
+				$selectAttrName = 'select' . $element . 'Attr';
+				${$selectAttrName} = $attributes;
+			}
+		}
+
+		$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
+					);
+				break;
+				case 'M':
+					$selectMonthAttr['monthNames'] = $monthNames;
+					$selects[] = $this->month($fieldName, $month, $selectMonthAttr);
+				break;
+				case 'D':
+					$selects[] = $this->day($fieldName, $day, $selectDayAttr);
+				break;
+			}
+		}
+		$opt = implode($separator, $selects);
+
+		if (!empty($interval) && $interval > 1 && !empty($min)) {
+			$min = round($min * (1 / $interval)) * $interval;
+		}
+		$selectMinuteAttr['interval'] = $interval;
+		switch ($timeFormat) {
+			case '24':
+				$opt .= $this->hour($fieldName, true, $hour, $selectHourAttr) . ':' .
+				$this->minute($fieldName, $min, $selectMinuteAttr);
+			break;
+			case '12':
+				$opt .= $this->hour($fieldName, false, $hour, $selectHourAttr) . ':' .
+				$this->minute($fieldName, $min, $selectMinuteAttr) . ' ' .
+				$this->meridian($fieldName, $meridian, $selectMeridianAttr);
+			break;
+			default:
+				$opt .= '';
+			break;
+		}
+		return $opt;
+	}
+
+/**
+ * Gets the input field name for the current tag
+ *
+ * @param array $options
+ * @param string $key
+ * @return array
+ * @access protected
+ */
+	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;
+			}
+
+			$view = ClassRegistry::getObject('view');
+			$name = !empty($view->field) ? $view->field : $view->model;
+			if (!empty($view->fieldSuffix)) {
+				$name .= '[' . $view->fieldSuffix . ']';
+			}
+
+			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, 'class' => 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)) {
+					$name = $attributes['escape'] ? h($name) : $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 (
+					(!$selectedIsArray && !$selectedIsEmpty && (string)$selected == (string)$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::slug($name)
+						);
+						$htmlOptions['id'] = $tagName;
+						$label = array('for' => $tagName);
+
+						if (isset($htmlOptions['checked']) && $htmlOptions['checked'] === true) {
+							$label['class'] = 'selected';
+						}
+
+						$name = $attributes['name'];
+
+						if (empty($attributes['class'])) {
+							$attributes['class'] = 'checkbox';
+						} elseif ($attributes['class'] === 'form-error') {
+							$attributes['class'] = 'checkbox ' . $attributes['class'];
+						}
+						$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[sprintf('%02d', $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'] === true) {
+					$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 if (is_array($options['monthNames'])) {
+					$data = $options['monthNames'];
+				} 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;
+				}
+				if ($options['order'] != 'asc') {
+					$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
+ *
+ * Options
+ *
+ *  - `secure` - boolean whether or not the field should be added to the security fields.
+ *
+ * @param string $field Name of the field to initialize options for.
+ * @param array $options Array of options to append options into.
+ * @return array Array of options for the input.
+ * @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']));
+		}
+
+		$fieldName = null;
+		if ($secure && !empty($options['name'])) {
+			preg_match_all('/\[(.*?)\]/', $options['name'], $matches);
+			if (isset($matches[1])) {
+				$fieldName = $matches[1];
+			}
+		}
+
+		$result = parent::_initInputField($field, $options);
+
+		if ($secure) {
+			$this->__secure($fieldName);
+		}
+		return $result;
+	}
+}

Added: trunk/src/Web/cake/libs/view/helpers/html.php
===================================================================
--- trunk/src/Web/cake/libs/view/helpers/html.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/helpers/html.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,835 @@
+<?php
+/**
+ * Html Helper class file.
+ *
+ * Simplifies the construction of HTML elements.
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @since         CakePHP(tm) v 0.9.1
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+/**
+ * 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
+ * @link http://book.cakephp.org/view/1434/HTML
+ */
+class HtmlHelper extends AppHelper {
+/**
+ * html tags used by this helper.
+ *
+ * @var array
+ * @access public
+ */
+	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 %s/>',
+		'submitimage' => '<input type="image" src="%s" %s/>',
+		'button' => '<button type="%s"%s>%s</button>',
+		'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>',
+		'javascriptblock' => '<script type="text/javascript"%s>%s</script>',
+		'javascriptstart' => '<script type="text/javascript">',
+		'javascriptlink' => '<script type="text/javascript" src="%s"%s></script>',
+		'javascriptend' => '</script>'
+	);
+
+/**
+ * Breadcrumbs.
+ *
+ * @var array
+ * @access protected
+ */
+	var $_crumbs = array();
+
+/**
+ * Names of script files that have been included once
+ *
+ * @var array
+ * @access private
+ */
+	var $__includedScripts = array();
+/**
+ * Options for the currently opened script block buffer if any.
+ *
+ * @var array
+ * @access protected
+ */
+	var $_scriptBlockOptions = 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')
+ * @return void
+ * @see HtmlHelper::link() for details on $options that can be used.
+ * @access public
+ */
+	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 string
+ * @access public
+ * @link http://book.cakephp.org/view/1439/docType
+ */
+	function docType($type = 'xhtml-strict') {
+		if (isset($this->__docTypes[$type])) {
+			return $this->__docTypes[$type];
+		}
+		return null;
+	}
+
+/**
+ * Creates a link to an external resource and handles basic meta tags
+ *
+ * ### Options
+ *
+ * - `inline` Whether or not the link element should be output inline, or in scripts_for_layout.
+ *
+ * @param string $type The title of the external resource
+ * @param mixed $url The address of the external resource or string for content attribute
+ * @param array $options Other attributes for the generated tag. If the type attribute is html,
+ *    rss, atom, or icon, the mime-type is returned.
+ * @return string A completed `<link />` element.
+ * @access public
+ * @link http://book.cakephp.org/view/1438/meta
+ */
+	function meta($type, $url = null, $options = array()) {
+		$inline = isset($options['inline']) ? $options['inline'] : true;
+		unset($options['inline']);
+
+		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($options['type']) && $url !== null) {
+				if (is_array($url) && isset($url['ext'])) {
+					$type = $types[$url['ext']];
+				} else {
+					$type = $types['rss'];
+				}
+			} elseif (isset($options['type']) && isset($types[$options['type']])) {
+				$type = $types[$options['type']];
+				unset($options['type']);
+			} else {
+				$type = array();
+			}
+		} elseif ($url !== null) {
+			$inline = $url;
+		}
+		$options = array_merge($type, $options);
+		$out = null;
+
+		if (isset($options['link'])) {
+			if (isset($options['rel']) && $options['rel'] === 'icon') {
+				$out = sprintf($this->tags['metalink'], $options['link'], $this->_parseAttributes($options, array('link'), ' ', ' '));
+				$options['rel'] = 'shortcut icon';
+			} else {
+				$options['link'] = $this->url($options['link'], true);
+			}
+			$out .= sprintf($this->tags['metalink'], $options['link'], $this->_parseAttributes($options, array('link'), ' ', ' '));
+		} else {
+			$out = sprintf($this->tags['meta'], $this->_parseAttributes($options, array('type'), ' ', ' '));
+		}
+
+		if ($inline) {
+			return $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. If empty,
+ *  The App.encoding value will be used. Example: "utf-8".
+ * @return string A meta tag containing the specified character set.
+ * @access public
+ * @link http://book.cakephp.org/view/1436/charset
+ */
+	function charset($charset = null) {
+		if (empty($charset)) {
+			$charset = strtolower(Configure::read('App.encoding'));
+		}
+		return 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.
+ *
+ * ### Options
+ *
+ * - `escape` Set to false to disable escaping of title and attributes.
+ *
+ * @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 $options Array of HTML attributes.
+ * @param string $confirmMessage JavaScript confirmation message.
+ * @return string An `<a />` element.
+ * @access public
+ * @link http://book.cakephp.org/view/1442/link
+ */
+	function link($title, $url = null, $options = array(), $confirmMessage = false) {
+		$escapeTitle = true;
+		if ($url !== null) {
+			$url = $this->url($url);
+		} else {
+			$url = $this->url($title);
+			$title = $url;
+			$escapeTitle = false;
+		}
+
+		if (isset($options['escape'])) {
+			$escapeTitle = $options['escape'];
+		}
+
+		if ($escapeTitle === true) {
+			$title = h($title);
+		} elseif (is_string($escapeTitle)) {
+			$title = htmlentities($title, ENT_QUOTES, $escapeTitle);
+		}
+
+		if (!empty($options['confirm'])) {
+			$confirmMessage = $options['confirm'];
+			unset($options['confirm']);
+		}
+		if ($confirmMessage) {
+			$confirmMessage = str_replace("'", "\'", $confirmMessage);
+			$confirmMessage = str_replace('"', '\"', $confirmMessage);
+			$options['onclick'] = "return confirm('{$confirmMessage}');";
+		} elseif (isset($options['default']) && $options['default'] == false) {
+			if (isset($options['onclick'])) {
+				$options['onclick'] .= ' event.returnValue = false; return false;';
+			} else {
+				$options['onclick'] = 'event.returnValue = false; return false;';
+			}
+			unset($options['default']);
+		}
+		return sprintf($this->tags['link'], $url, $this->_parseAttributes($options), $title);
+	}
+
+/**
+ * Creates a link element for CSS stylesheets.
+ *
+ * ### Options
+ *
+ * - `inline` If set to false, the generated tag appears in the head tag of the layout. Defaults to true
+ *
+ * @param mixed $path The name of a CSS style sheet or an array containing names of
+ *   CSS stylesheets. If `$path` is prefixed with '/', the path will be relative to the webroot
+ *   of your application. Otherwise, the path will be relative to your CSS path, usually webroot/css.
+ * @param string $rel Rel attribute. Defaults to "stylesheet". If equal to 'import' the stylesheet will be imported.
+ * @param array $options Array of HTML attributes.
+ * @return string CSS <link /> or <style /> tag, depending on the type of link.
+ * @access public
+ * @link http://book.cakephp.org/view/1437/css
+ */
+	function css($path, $rel = null, $options = array()) {
+		$options += array('inline' => true);
+		if (is_array($path)) {
+			$out = '';
+			foreach ($path as $i) {
+				$out .= "\n\t" . $this->css($i, $rel, $options);
+			}
+			if ($options['inline'])  {
+				return $out . "\n";
+			}
+			return;
+		}
+
+		if (strpos($path, '//') !== false) {
+			$url = $path;
+		} else {
+			if ($path[0] !== '/') {
+				$path = CSS_URL . $path;
+			}
+
+			if (strpos($path, '?') === false) {
+				if (substr($path, -4) !== '.css') {
+					$path .= '.css';
+				}
+			}
+			$url = $this->assetTimestamp($this->webroot($path));
+
+			if (Configure::read('Asset.filter.css')) {
+				$pos = strpos($url, CSS_URL);
+				if ($pos !== false) {
+					$url = substr($url, 0, $pos) . 'ccss/' . substr($url, $pos + strlen(CSS_URL));
+				}
+			}
+		}
+
+		if ($rel == 'import') {
+			$out = sprintf($this->tags['style'], $this->_parseAttributes($options, array('inline'), '', ' '), '@import url(' . $url . ');');
+		} else {
+			if ($rel == null) {
+				$rel = 'stylesheet';
+			}
+			$out = sprintf($this->tags['css'], $rel, $url, $this->_parseAttributes($options, array('inline'), '', ' '));
+		}
+
+		if ($options['inline']) {
+			return $out;
+		} else {
+			$view =& ClassRegistry::getObject('view');
+			$view->addScript($out);
+		}
+	}
+
+/**
+ * Returns one or many `<script>` tags depending on the number of scripts given.
+ *
+ * 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.
+ *
+ * Can include one or many Javascript files.
+ *
+ * ### Options
+ *
+ * - `inline` - Whether script should be output inline or into scripts_for_layout.
+ * - `once` - Whether or not the script should be checked for uniqueness. If true scripts will only be
+ *   included once, use false to allow the same script to be included more than once per request.
+ *
+ * @param mixed $url String or array of javascript files to include
+ * @param mixed $options Array of options, and html attributes see above. If boolean sets $options['inline'] = value
+ * @return mixed String of `<script />` tags or null if $inline is false or if $once is true and the file has been
+ *   included before.
+ * @access public
+ * @link http://book.cakephp.org/view/1589/script
+ */
+	function script($url, $options = array()) {
+		if (is_bool($options)) {
+			list($inline, $options) = array($options, array());
+			$options['inline'] = $inline;
+		}
+		$options = array_merge(array('inline' => true, 'once' => true), $options);
+		if (is_array($url)) {
+			$out = '';
+			foreach ($url as $i) {
+				$out .= "\n\t" . $this->script($i, $options);
+			}
+			if ($options['inline'])  {
+				return $out . "\n";
+			}
+			return null;
+		}
+		if ($options['once'] && isset($this->__includedScripts[$url])) {
+			return null;
+		}
+		$this->__includedScripts[$url] = true;
+
+		if (strpos($url, '//') === false) {
+			if ($url[0] !== '/') {
+				$url = JS_URL . $url;
+			}
+			if (strpos($url, '?') === false && substr($url, -3) !== '.js') {
+				$url .= '.js';
+			}
+			$url = $this->assetTimestamp($this->webroot($url));
+
+			if (Configure::read('Asset.filter.js')) {
+				$url = str_replace(JS_URL, 'cjs/', $url);
+			}
+		}
+		$attributes = $this->_parseAttributes($options, array('inline', 'once'), ' ');
+		$out = sprintf($this->tags['javascriptlink'], $url, $attributes);
+
+		if ($options['inline']) {
+			return $out;
+		} else {
+			$view =& ClassRegistry::getObject('view');
+			$view->addScript($out);
+		}
+	}
+
+/**
+ * Wrap $script in a script tag.
+ *
+ * ### Options
+ *
+ * - `safe` (boolean) Whether or not the $script should be wrapped in <![CDATA[ ]]>
+ * - `inline` (boolean) Whether or not the $script should be added to $scripts_for_layout or output inline
+ *
+ * @param string $script The script to wrap
+ * @param array $options The options to use.
+ * @return mixed string or null depending on the value of `$options['inline']`
+ * @access public
+ * @link http://book.cakephp.org/view/1604/scriptBlock
+ */
+	function scriptBlock($script, $options = array()) {
+		$options += array('safe' => true, 'inline' => true);
+		if ($options['safe']) {
+			$script  = "\n" . '//<![CDATA[' . "\n" . $script . "\n" . '//]]>' . "\n";
+		}
+		$inline = $options['inline'];
+		unset($options['inline'], $options['safe']);
+		$attributes = $this->_parseAttributes($options, ' ', ' ');
+		if ($inline) {
+			return sprintf($this->tags['javascriptblock'], $attributes, $script);
+		} else {
+			$view =& ClassRegistry::getObject('view');
+			$view->addScript(sprintf($this->tags['javascriptblock'], $attributes, $script));
+			return null;
+		}
+	}
+
+/**
+ * Begin a script block that captures output until HtmlHelper::scriptEnd()
+ * is called. This capturing block will capture all output between the methods
+ * and create a scriptBlock from it.
+ *
+ * ### Options
+ *
+ * - `safe` Whether the code block should contain a CDATA
+ * - `inline` Should the generated script tag be output inline or in `$scripts_for_layout`
+ *
+ * @param array $options Options for the code block.
+ * @return void
+ * @access public
+ * @link http://book.cakephp.org/view/1605/scriptStart
+ */
+	function scriptStart($options = array()) {
+		$options += array('safe' => true, 'inline' => true);
+		$this->_scriptBlockOptions = $options;
+		ob_start();
+		return null;
+	}
+
+/**
+ * End a Buffered section of Javascript capturing.
+ * Generates a script tag inline or in `$scripts_for_layout` depending on the settings
+ * used when the scriptBlock was started
+ *
+ * @return mixed depending on the settings of scriptStart() either a script tag or null
+ * @access public
+ * @link http://book.cakephp.org/view/1606/scriptEnd
+ */
+	function scriptEnd() {
+		$buffer = ob_get_clean();
+		$options = $this->_scriptBlockOptions;
+		$this->_scriptBlockOptions = array();
+		return $this->scriptBlock($buffer, $options);
+	}
+
+/**
+ * Builds CSS style data from an array of CSS properties
+ *
+ * ### Usage:
+ *
+ * {{{
+ * echo $html->style(array('margin' => '10px', 'padding' => '10px'), true);
+ *
+ * // creates
+ * 'margin:10px;padding:10px;'
+ * }}}
+ *
+ * @param array $data Style data array, keys will be used as property names, values as property values.
+ * @param boolean $oneline Whether or not the style block should be displayed on one line.
+ * @return string CSS styling data
+ * @access public
+ * @link http://book.cakephp.org/view/1440/style
+ */
+	function style($data, $oneline = true) {
+		if (!is_array($data)) {
+			return $data;
+		}
+		$out = array();
+		foreach ($data as $key=> $value) {
+			$out[] = $key.':'.$value.';';
+		}
+		if ($oneline) {
+			return join(' ', $out);
+		}
+		return implode("\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 Composed bread crumbs
+ * @access public
+ */
+	function getCrumbs($separator = '&raquo;', $startText = false) {
+		if (!empty($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 join($separator, $out);
+		} else {
+			return null;
+		}
+	}
+
+/**
+ * Creates a formatted IMG element. If `$options['url']` is provided, an image link will be
+ * generated with the link pointed at `$options['url']`.  This method will set an empty
+ * alt attribute if one is not supplied.
+ *
+ * ### Usage
+ *
+ * Create a regular image:
+ *
+ * `echo $html->image('cake_icon.png', array('alt' => 'CakePHP'));`
+ *
+ * Create an image link:
+ *
+ * `echo $html->image('cake_icon.png', array('alt' => 'CakePHP', 'url' => 'http://cakephp.org'));`
+ *
+ * @param string $path Path to the image file, relative to the app/webroot/img/ directory.
+ * @param array $options Array of HTML attributes.
+ * @return string completed img tag
+ * @access public
+ * @link http://book.cakephp.org/view/1441/image
+ */
+	function image($path, $options = array()) {
+		if (is_array($path)) {
+			$path = $this->url($path);
+		} elseif (strpos($path, '://') === false) {
+			if ($path[0] !== '/') {
+				$path = IMAGES_URL . $path;
+			}
+			$path = $this->assetTimestamp($this->webroot($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 sprintf($this->tags['link'], $this->url($url), null, $image);
+		}
+		return $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 Completed table headers
+ * @access public
+ * @link http://book.cakephp.org/view/1446/tableHeaders
+ */
+	function tableHeaders($names, $trOptions = null, $thOptions = null) {
+		$out = array();
+		foreach ($names as $arg) {
+			$out[] = sprintf($this->tags['tableheader'], $this->_parseAttributes($thOptions), $arg);
+		}
+		return sprintf($this->tags['tablerow'], $this->_parseAttributes($trOptions), join(' ', $out));
+	}
+
+/**
+ * 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
+ * @access public
+ * @link http://book.cakephp.org/view/1447/tableCells
+ */
+	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, implode(' ', $cellsOut));
+		}
+		return implode("\n", $out);
+	}
+
+/**
+ * Returns a formatted block tag, i.e DIV, SPAN, P.
+ *
+ * ### Options
+ *
+ * - `escape` Whether or not the contents should be html_entity escaped.
+ *
+ * @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 $options Additional HTML attributes of the DIV tag, see above.
+ * @return string The formatted tag element
+ * @access public
+ * @link http://book.cakephp.org/view/1443/tag
+ */
+	function tag($name, $text = null, $options = array()) {
+		if (is_array($options) && isset($options['escape']) && $options['escape']) {
+			$text = h($text);
+			unset($options['escape']);
+		}
+		if (!is_array($options)) {
+			$options = array('class' => $options);
+		}
+		if ($text === null) {
+			$tag = 'tagstart';
+		} else {
+			$tag = 'tag';
+		}
+		return sprintf($this->tags[$tag], $name, $this->_parseAttributes($options, null, ' ', ''), $text, $name);
+	}
+
+/**
+ * Returns a formatted DIV tag for HTML FORMs.
+ *
+ * ### Options
+ *
+ * - `escape` Whether or not the contents should be html_entity escaped.
+ *
+ * @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 $options Additional HTML attributes of the DIV tag
+ * @return string The formatted DIV element
+ * @access public
+ * @link http://book.cakephp.org/view/1444/div
+ */
+	function div($class = null, $text = null, $options = array()) {
+		if (!empty($class)) {
+			$options['class'] = $class;
+		}
+		return $this->tag('div', $text, $options);
+	}
+
+/**
+ * Returns a formatted P tag.
+ *
+ * ### Options
+ *
+ * - `escape` Whether or not the contents should be html_entity escaped.
+ *
+ * @param string $class CSS class name of the p element.
+ * @param string $text String content that will appear inside the p element.
+ * @param array $options Additional HTML attributes of the P tag
+ * @return string The formatted P element
+ * @access public
+ * @link http://book.cakephp.org/view/1445/para
+ */
+	function para($class, $text, $options = array()) {
+		if (isset($options['escape'])) {
+			$text = h($text);
+		}
+		if ($class != null && !empty($class)) {
+			$options['class'] = $class;
+		}
+		if ($text === null) {
+			$tag = 'parastart';
+		} else {
+			$tag = 'para';
+		}
+		return sprintf($this->tags[$tag], $this->_parseAttributes($options, null, ' ', ''), $text);
+	}
+
+/**
+ * Build a nested list (UL/OL) out of an associative array.
+ *
+ * @param array $list Set of elements to list
+ * @param array $options Additional HTML attributes of the list (ol/ul) tag or if ul/ol use that as tag
+ * @param array $itemOptions 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, $options = array(), $itemOptions = array(), $tag = 'ul') {
+		if (is_string($options)) {
+			$tag = $options;
+			$options = array();
+		}
+		$items = $this->__nestedListItem($list, $options, $itemOptions, $tag);
+		return sprintf($this->tags[$tag], $this->_parseAttributes($options, null, ' ', ''), $items);
+	}
+
+/**
+ * Internal function to build a nested list (UL/OL) out of an associative array.
+ *
+ * @param array $items Set of elements to list
+ * @param array $options Additional HTML attributes of the list (ol/ul) tag
+ * @param array $itemOptions 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 HtmlHelper::nestedList()
+ */
+	function __nestedListItem($items, $options, $itemOptions, $tag) {
+		$out = '';
+
+		$index = 1;
+		foreach ($items as $key => $item) {
+			if (is_array($item)) {
+				$item = $key . $this->nestedList($item, $options, $itemOptions, $tag);
+			}
+			if (isset($itemOptions['even']) && $index % 2 == 0) {
+				$itemOptions['class'] = $itemOptions['even'];
+			} else if (isset($itemOptions['odd']) && $index % 2 != 0) {
+				$itemOptions['class'] = $itemOptions['odd'];
+			}
+			$out .= sprintf($this->tags['li'], $this->_parseAttributes($itemOptions, array('even', 'odd'), ' ', ''), $item);
+			$index++;
+		}
+		return $out;
+	}
+}

Added: trunk/src/Web/cake/libs/view/helpers/javascript.php
===================================================================
--- trunk/src/Web/cake/libs/view/helpers/javascript.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/helpers/javascript.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,721 @@
+<?php
+/**
+ * Javascript Helper class file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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
+ * @link http://book.cakephp.org/view/1450/Javascript
+ */
+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(
+		'javascriptstart' => '<script type="text/javascript">',
+		'javascriptend' => '</script>',
+		'javascriptblock' => '<script type="text/javascript">%s</script>',
+		'javascriptlink' => '<script type="text/javascript" src="%s"></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.
+ *
+ * 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).
+ *
+ * @param string $script The JavaScript to be wrapped in SCRIPT tags.
+ * @param array $options Set of options:
+ * @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, $options);
+
+		if (empty($script)) {
+			$this->__scriptBuffer = @ob_get_contents();
+			$this->_blockOptions = $options;
+			$this->inBlock = true;
+			@ob_end_clean();
+			ob_start();
+			return null;
+		}
+		if ($this->_cacheEvents && $this->_cacheAll && $options['allowCache']) {
+			$this->_cachedEvents[] = $script;
+			return null;
+		}
+		if ($options['safe'] || $this->safe) {
+			$script  = "\n" . '//<![CDATA[' . "\n" . $script . "\n" . '//]]>' . "\n";
+		}
+		if ($options['inline']) {
+			return sprintf($this->tags['javascriptblock'], $script);
+		} else {
+			$view =& ClassRegistry::getObject('view');
+			$view->addScript(sprintf($this->tags['javascriptblock'], $script));
+		}
+	}
+
+/**
+ * Ends a block of cached JavaScript code
+ *
+ * @return mixed
+ */
+	function blockEnd() {
+		if (!isset($this->inBlock) || !$this->inBlock) {
+			return;
+		}
+		$script = @ob_get_contents();
+		@ob_end_clean();
+		ob_start();
+		echo $this->__scriptBuffer;
+		$this->__scriptBuffer = null;
+		$options = $this->_blockOptions;
+		$this->_blockOptions = array();
+		$this->inBlock = false;
+
+		if (empty($script)) {
+			return null;
+		}
+
+		return $this->codeBlock($script, $options);
+	}
+
+/**
+ * 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 (substr($url, -3) !== '.js') {
+					$url .= '.js';
+				}
+			}
+			$url = $this->assetTimestamp($this->webroot($url));
+
+			if (Configure::read('Asset.filter.js')) {
+				$pos = strpos($url, JS_URL);
+				if ($pos !== false) {
+					$url = substr($url, 0, $pos) . 'cjs/' . substr($url, $pos + strlen(JS_URL));
+				}
+			}
+		}
+		$out = 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) {
+		App::import('Core', 'Multibyte');
+		$escape = array("\r\n" => "\n", "\r" => "\n");
+		$string = str_replace(array_keys($escape), array_values($escape), $string);
+		return $this->_utf8ToHex($string);
+	}
+
+/**
+ * Encode a string into JSON.  Converts and escapes necessary characters.
+ *
+ * @return void
+ */
+	function _utf8ToHex($string) {
+		$length = strlen($string);
+		$return = '';
+		for ($i = 0; $i < $length; ++$i) {
+			$ord = ord($string{$i});
+			switch (true) {
+				case $ord == 0x08:
+					$return .= '\b';
+					break;
+				case $ord == 0x09:
+					$return .= '\t';
+					break;
+				case $ord == 0x0A:
+					$return .= '\n';
+					break;
+				case $ord == 0x0C:
+					$return .= '\f';
+					break;
+				case $ord == 0x0D:
+					$return .= '\r';
+					break;
+				case $ord == 0x22:
+				case $ord == 0x2F:
+				case $ord == 0x5C:
+				case $ord == 0x27:
+					$return .= '\\' . $string{$i};
+					break;
+				case (($ord >= 0x20) && ($ord <= 0x7F)):
+					$return .= $string{$i};
+					break;
+				case (($ord & 0xE0) == 0xC0):
+					if ($i + 1 >= $length) {
+						$i += 1;
+						$return .= '?';
+						break;
+					}
+					$charbits = $string{$i} . $string{$i + 1};
+					$char = Multibyte::utf8($charbits);
+					$return .= sprintf('\u%04s', dechex($char[0]));
+					$i += 1;
+					break;
+				case (($ord & 0xF0) == 0xE0):
+					if ($i + 2 >= $length) {
+						$i += 2;
+						$return .= '?';
+						break;
+					}
+					$charbits = $string{$i} . $string{$i + 1} . $string{$i + 2};
+					$char = Multibyte::utf8($charbits);
+					$return .= sprintf('\u%04s', dechex($char[0]));
+					$i += 2;
+					break;
+				case (($ord & 0xF8) == 0xF0):
+					if ($i + 3 >= $length) {
+					   $i += 3;
+					   $return .= '?';
+					   break;
+					}
+					$charbits = $string{$i} . $string{$i + 1} . $string{$i + 2} . $string{$i + 3};
+					$char = Multibyte::utf8($charbits);
+					$return .= sprintf('\u%04s', dechex($char[0]));
+					$i += 3;
+					break;
+				case (($ord & 0xFC) == 0xF8):
+					if ($i + 4 >= $length) {
+					   $i += 4;
+					   $return .= '?';
+					   break;
+					}
+					$charbits = $string{$i} . $string{$i + 1} . $string{$i + 2} . $string{$i + 3} . $string{$i + 4};
+					$char = Multibyte::utf8($charbits);
+					$return .= sprintf('\u%04s', dechex($char[0]));
+					$i += 4;
+					break;
+				case (($ord & 0xFE) == 0xFC):
+					if ($i + 5 >= $length) {
+					   $i += 5;
+					   $return .= '?';
+					   break;
+					}
+					$charbits = $string{$i} . $string{$i + 1} . $string{$i + 2} . $string{$i + 3} . $string{$i + 4} . $string{$i + 5};
+					$char = Multibyte::utf8($charbits);
+					$return .= sprintf('\u%04s', dechex($char[0]));
+					$i += 5;
+					break;
+			}
+		}
+		return $return;
+	}
+
+/**
+ * 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
+ *
+ * ### Options
+ *
+ * - block - Wraps the return value in a script tag if true. Default is false
+ * - prefix - Prepends the string to the returned data. Default is ''
+ * - postfix - Appends the string to the returned data. Default is ''
+ * - stringKeys - A list of array keys to be treated as a string.
+ * - quoteKeys - If false treats $stringKeys as a list of keys **not** to be quoted. Default is true.
+ * - q - The type of quote to use. Default is '"'.  This option only affects the keys, not the values.
+ *
+ * @param array $data Data to be converted
+ * @param array $options Set of options: block, prefix, postfix, stringKeys, quoteKeys, q
+ * @return string A JSON code block
+ */
+	function object($data = array(), $options = array()) {
+		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_null($data)) {
+				return 'null';
+			}
+			if (is_bool($data)) {
+				return $data ? 'true' : 'false';
+			}
+
+			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, 'prefix' => '', 'postfix' => ''))
+					);
+				} 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 = '{' . implode(',', $out) . '}';
+			} else {
+				$rt = '[' . implode(',', $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 = !empty($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);
+	}
+}

Added: trunk/src/Web/cake/libs/view/helpers/jquery_engine.php
===================================================================
--- trunk/src/Web/cake/libs/view/helpers/jquery_engine.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/helpers/jquery_engine.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,363 @@
+<?php
+/**
+ * jQuery Engine Helper for JsHelper
+ *
+ * Provides jQuery specific Javascript for JsHelper.
+ *
+ * Implements the JsHelper interface for jQuery.  All $options arrays
+ * support all options found in the JsHelper, as well as those in the jQuery
+ * documentation.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright       Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link            http://cakephp.org CakePHP Project
+ * @package         cake
+ * @subpackage      cake.view.helpers
+ * @license         MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Helper', 'Js');
+
+class JqueryEngineHelper extends JsBaseEngineHelper {
+/**
+ * Option mappings for jQuery
+ *
+ * @var array
+ * @access protected
+ */
+	var $_optionMap = array(
+		'request' => array(
+			'type' => 'dataType',
+			'before' => 'beforeSend',
+			'method' => 'type',
+		),
+		'sortable' => array(
+			'complete' => 'stop',
+		),
+		'drag' => array(
+			'snapGrid' => 'grid',
+			'container' => 'containment',
+		),
+		'drop' => array(
+			'leave' => 'out',
+			'hover' => 'over'
+		),
+		'slider' => array(
+			'complete' => 'stop',
+			'direction' => 'orientation'
+		)
+	);
+
+/**
+ * Callback arguments lists
+ *
+ * @var string
+ * @access protected
+ */
+	var $_callbackArguments = array(
+		'slider' => array(
+			'start' => 'event, ui',
+			'slide' => 'event, ui',
+			'change' => 'event, ui',
+			'stop' => 'event, ui'
+		),
+		'sortable' => array(
+			'start' => 'event, ui',
+			'sort' => 'event, ui',
+			'change' => 'event, ui',
+			'beforeStop' => 'event, ui',
+			'stop' => 'event, ui',
+			'update' => 'event, ui',
+			'receive' => 'event, ui',
+			'remove' => 'event, ui',
+			'over' => 'event, ui',
+			'out' => 'event, ui',
+			'activate' => 'event, ui',
+			'deactivate' => 'event, ui'
+		),
+		'drag' => array(
+			'start' => 'event, ui',
+			'drag' => 'event, ui',
+			'stop' => 'event, ui',
+		),
+		'drop' => array(
+			'activate' => 'event, ui',
+			'deactivate' => 'event, ui',
+			'over' => 'event, ui',
+			'out' => 'event, ui',
+			'drop' => 'event, ui'
+		),
+		'request' => array(
+			'beforeSend' => 'XMLHttpRequest',
+			'error' => 'XMLHttpRequest, textStatus, errorThrown',
+			'success' => 'data, textStatus',
+			'complete' => 'XMLHttpRequest, textStatus',
+			'xhr' => ''
+		)
+	);
+
+/**
+ * The variable name of the jQuery Object, useful
+ * when jQuery is put into noConflict() mode.
+ *
+ * @var string
+ * @access public
+ */
+	 var $jQueryObject = '$';
+
+/**
+ * Helper function to wrap repetitive simple method templating.
+ *
+ * @param string $method The method name being generated.
+ * @param string $template The method template
+ * @param string $selection the selection to apply
+ * @param string $options Array of options for method
+ * @param string $callbacks Array of callback / special options.
+ * @return string Composed method string
+ * @access public
+ */
+	function _methodTemplate($method, $template, $options, $extraSafeKeys = array()) {
+		$options = $this->_mapOptions($method, $options);
+		$options = $this->_prepareCallbacks($method, $options);
+		$callbacks = array_keys($this->_callbackArguments[$method]);
+		if (!empty($extraSafeKeys)) {
+			$callbacks = array_merge($callbacks, $extraSafeKeys);
+		}
+		$options = $this->_parseOptions($options, $callbacks);
+		return sprintf($template, $this->selection, $options);
+	}
+
+/**
+ * Create javascript selector for a CSS rule
+ *
+ * @param string $selector The selector that is targeted
+ * @return object instance of $this. Allows chained methods.
+ * @access public
+ */
+	function get($selector) {
+		if ($selector == 'window' || $selector == 'document') {
+			$this->selection = $this->jQueryObject . '(' . $selector .')';
+		} else {
+			$this->selection = $this->jQueryObject . '("' . $selector . '")';
+		}
+		return $this;
+	}
+
+/**
+ * Add an event to the script cache. Operates on the currently selected elements.
+ *
+ * ### Options
+ *
+ * - 'wrap' - Whether you want the callback wrapped in an anonymous function. (defaults true)
+ * - 'stop' - Whether you want the event to stopped. (defaults true)
+ *
+ * @param string $type Type of event to bind to the current dom id
+ * @param string $callback The Javascript function you wish to trigger or the function literal
+ * @param array $options Options for the event.
+ * @return string completed event handler
+ * @access public
+ */
+	function event($type, $callback, $options = array()) {
+		$defaults = array('wrap' => true, 'stop' => true);
+		$options = array_merge($defaults, $options);
+
+		$function = 'function (event) {%s}';
+		if ($options['wrap'] && $options['stop']) {
+			$callback .= "\nreturn false;";
+		}
+		if ($options['wrap']) {
+			$callback = sprintf($function, $callback);
+		}
+		return sprintf('%s.bind("%s", %s);', $this->selection, $type, $callback);
+	}
+
+/**
+ * Create a domReady event. For jQuery. This method does not 
+ * bind a 'traditional event' as `$(document).bind('ready', fn)`
+ * Works in an entirely different fashion than  `$(document).ready()`
+ * The first will not run the function when eval()'d as part of a response
+ * The second will.  Because of the way that ajax pagination is done
+ * `$().ready()` is used.
+ *
+ * @param string $functionBody The code to run on domReady
+ * @return string completed domReady method
+ * @access public
+ */
+	function domReady($functionBody) {
+		return $this->jQueryObject . '(document).ready(function () {' . $functionBody . '});';
+	}
+
+/**
+ * Create an iteration over the current selection result.
+ *
+ * @param string $method The method you want to apply to the selection
+ * @param string $callback The function body you wish to apply during the iteration.
+ * @return string completed iteration
+ * @access public
+ */
+	function each($callback) {
+		return $this->selection . '.each(function () {' . $callback . '});';
+	}
+
+/**
+ * Trigger an Effect.
+ *
+ * @param string $name The name of the effect to trigger.
+ * @param array $options Array of options for the effect.
+ * @return string completed string with effect.
+ * @access public
+ * @see JsBaseEngineHelper::effect()
+ */
+	function effect($name, $options = array()) {
+		$speed = null;
+		if (isset($options['speed']) && in_array($options['speed'], array('fast', 'slow'))) {
+			$speed = $this->value($options['speed']);
+		}
+		$effect = '';
+		switch ($name) {
+			case 'slideIn':
+			case 'slideOut':
+				$name = ($name == 'slideIn') ? 'slideDown' : 'slideUp';
+			case 'hide':
+			case 'show':
+ 			case 'fadeIn':
+			case 'fadeOut':
+			case 'slideDown':
+			case 'slideUp':
+				$effect = ".$name($speed);";
+			break;
+		}
+		return $this->selection . $effect;
+	}
+
+/**
+ * Create an $.ajax() call.
+ *
+ * If the 'update' key is set, success callback will be overridden.
+ *
+ * @param mixed $url
+ * @param array $options See JsHelper::request() for options.
+ * @return string The completed ajax call.
+ * @access public
+ * @see JsBaseEngineHelper::request() for options list.
+ */
+	function request($url, $options = array()) {
+		$url = $this->url($url);
+		$options = $this->_mapOptions('request', $options);
+		if (isset($options['data']) && is_array($options['data'])) {
+			$options['data'] = $this->_toQuerystring($options['data']);
+		}
+		$options['url'] = $url;
+		if (isset($options['update'])) {
+			$wrapCallbacks = isset($options['wrapCallbacks']) ? $options['wrapCallbacks'] : true;
+			$success = '';
+			if(isset($options['success']) AND !empty($options['success'])) {
+				$success .= $options['success'];
+			}
+			$success .= $this->jQueryObject . '("' . $options['update'] . '").html(data);';
+			if (!$wrapCallbacks) {
+				$success = 'function (data, textStatus) {' . $success . '}';
+			}
+			$options['dataType'] = 'html';
+			$options['success'] = $success;
+			unset($options['update']);
+		}
+		$callbacks = array('success', 'error', 'beforeSend', 'complete');
+		if (!empty($options['dataExpression'])) {
+			$callbacks[] = 'data';
+			unset($options['dataExpression']);
+		}
+		$options = $this->_prepareCallbacks('request', $options);
+		$options = $this->_parseOptions($options, $callbacks);
+		return $this->jQueryObject . '.ajax({' . $options .'});';
+	}
+
+/**
+ * Create a sortable element.
+ *
+ * Requires both Ui.Core and Ui.Sortables to be loaded.
+ *
+ * @param array $options Array of options for the sortable.
+ * @return string Completed sortable script.
+ * @access public
+ * @see JsBaseEngineHelper::sortable() for options list.
+ */
+	function sortable($options = array()) {
+		$template = '%s.sortable({%s});';
+		return $this->_methodTemplate('sortable', $template, $options);
+	}
+
+/**
+ * Create a Draggable element
+ *
+ * Requires both Ui.Core and Ui.Draggable to be loaded.
+ *
+ * @param array $options Array of options for the draggable element.
+ * @return string Completed Draggable script.
+ * @access public
+ * @see JsBaseEngineHelper::drag() for options list.
+ */
+	function drag($options = array()) {
+		$template = '%s.draggable({%s});';
+		return $this->_methodTemplate('drag', $template, $options);
+	}
+
+/**
+ * Create a Droppable element
+ *
+ * Requires both Ui.Core and Ui.Droppable to be loaded.
+ *
+ * @param array $options Array of options for the droppable element.
+ * @return string Completed Droppable script.
+ * @access public
+ * @see JsBaseEngineHelper::drop() for options list.
+ */
+	function drop($options = array()) {
+		$template = '%s.droppable({%s});';
+		return $this->_methodTemplate('drop', $template, $options);
+	}
+
+/**
+ * Create a Slider element
+ *
+ * Requires both Ui.Core and Ui.Slider to be loaded.
+ *
+ * @param array $options Array of options for the droppable element.
+ * @return string Completed Slider script.
+ * @access public
+ * @see JsBaseEngineHelper::slider() for options list.
+ */
+	function slider($options = array()) {
+		$callbacks = array('start', 'change', 'slide', 'stop');
+		$template = '%s.slider({%s});';
+		return $this->_methodTemplate('slider', $template, $options, $callbacks);
+	}
+
+/**
+ * Serialize a form attached to $selector. If the current selection is not an input or
+ * form, errors will be created in the Javascript.
+ *
+ * @param array $options Options for the serialization
+ * @return string completed form serialization script.
+ * @access public
+ * @see JsBaseEngineHelper::serializeForm() for option list.
+ */
+	function serializeForm($options = array()) {
+		$options = array_merge(array('isForm' => false, 'inline' => false), $options);
+		$selector = $this->selection;
+		if (!$options['isForm']) {
+			$selector = $this->selection . '.closest("form")';
+		}
+		$method = '.serialize()';
+		if (!$options['inline']) {
+			$method .= ';';
+		}
+		return $selector . $method;
+	}
+}

Added: trunk/src/Web/cake/libs/view/helpers/js.php
===================================================================
--- trunk/src/Web/cake/libs/view/helpers/js.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/helpers/js.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,1132 @@
+<?php
+/**
+ * Javascript Generator class file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP :  Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://cakephp.org CakePHP Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @since         CakePHP v 1.2
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 AppHelper {
+/**
+ * Whether or not you want scripts to be buffered or output.
+ *
+ * @var boolean
+ * @access public
+ */
+	var $bufferScripts = true;
+
+/**
+ * helpers
+ *
+ * @var array
+ * @access public
+ */
+	var $helpers = array('Html', 'Form');
+
+/**
+ * Variables to pass to Javascript.
+ *
+ * @var array
+ * @see JsHelper::set()
+ * @access private
+ */
+	var $__jsVars = array();
+
+/**
+ * Scripts that are queued for output
+ *
+ * @var array
+ * @see JsHelper::buffer()
+ * @access private
+ */
+	var $__bufferedScripts = array();
+
+/**
+ * Current Javascript Engine that is being used
+ *
+ * @var string
+ * @access private
+ */
+	var $__engineName;
+
+/**
+ * The javascript variable created by set() variables.
+ *
+ * @var string
+ * @access public
+ */
+	var $setVariable = 'app';
+
+/**
+ * Constructor - determines engine helper
+ *
+ * @param array $settings Settings array contains name of engine helper.
+ * @return void
+ * @access public
+ */
+	function __construct($settings = array()) {
+		$className = 'Jquery';
+		if (is_array($settings) && isset($settings[0])) {
+			$className = $settings[0];
+		} elseif (is_string($settings)) {
+			$className = $settings;
+		}
+		$engineName = $className;
+		list($plugin, $className) = pluginSplit($className);
+
+		$this->__engineName = $className . 'Engine';
+		$engineClass = $engineName . 'Engine';
+		$this->helpers[] = $engineClass;
+		parent::__construct();
+	}
+
+/**
+ * call__ Allows for dispatching of methods to the Engine Helper.
+ * methods in the Engines bufferedMethods list will be automatically buffered.
+ * You can control buffering with the buffer param as well. By setting the last parameter to
+ * any engine method to a boolean you can force or disable buffering.
+ *
+ * e.g. `$js->get('#foo')->effect('fadeIn', array('speed' => 'slow'), true);`
+ *
+ * Will force buffering for the effect method. If the method takes an options array you may also add
+ * a 'buffer' param to the options array and control buffering there as well.
+ *
+ * e.g. `$js->get('#foo')->event('click', $functionContents, array('buffer' => true));`
+ *
+ * The buffer parameter will not be passed onto the EngineHelper.
+ *
+ * @param string $method Method to be called
+ * @param array $params Parameters for the method being called.
+ * @return mixed Depends on the return of the dispatched method, or it could be an instance of the EngineHelper
+ * @access public
+ */
+	function call__($method, $params) {
+		if (isset($this->{$this->__engineName}) && method_exists($this->{$this->__engineName}, $method)) {
+			$buffer = false;
+			if (in_array(strtolower($method), $this->{$this->__engineName}->bufferedMethods)) {
+				$buffer = true;
+			}
+			if (count($params) > 0) {
+				$lastParam = $params[count($params) - 1];
+				$hasBufferParam = (is_bool($lastParam) || is_array($lastParam) && isset($lastParam['buffer']));
+				if ($hasBufferParam && is_bool($lastParam)) {
+					$buffer = $lastParam;
+					unset($params[count($params) - 1]);
+				} elseif ($hasBufferParam && is_array($lastParam)) {
+					$buffer = $lastParam['buffer'];
+					unset($params['buffer']);
+				}
+			}
+			$out = $this->{$this->__engineName}->dispatchMethod($method, $params);
+			if ($this->bufferScripts && $buffer && is_string($out)) {
+				$this->buffer($out);
+				return null;
+			}
+			if (is_object($out) && is_a($out, 'JsBaseEngineHelper')) {
+				return $this;
+			}
+			return $out;
+		}
+		if (method_exists($this, $method . '_')) {
+			return $this->dispatchMethod($method . '_', $params);
+		}
+		trigger_error(sprintf(__('JsHelper:: Missing Method %s is undefined', true), $method), E_USER_WARNING);
+	}
+
+/**
+ * Workaround for Object::Object() existing. Since Object::object exists, it does not
+ * fall into call__ and is not passed onto the engine helper. See JsBaseEngineHelper::object() for
+ * more information on this method.
+ *
+ * @param mixed $data Data to convert into JSON
+ * @param array $options Options to use for encoding JSON.  See JsBaseEngineHelper::object() for more details.
+ * @return string encoded JSON
+ * @deprecated Remove when support for PHP4 and Object::object are removed.
+ * @access public
+ */
+	function object($data = array(), $options = array()) {
+		return $this->{$this->__engineName}->object($data, $options);
+	}
+
+/**
+ * Overwrite inherited Helper::value()
+ * See JsBaseEngineHelper::value() for more information on this method.
+ *
+ * @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
+ * @access public
+ **/
+	function value($val, $quoteString = true) {
+		return $this->{$this->__engineName}->value($val, $quoteString);
+	}
+
+/**
+ * Writes all Javascript generated so far to a code block or
+ * caches them to a file and returns a linked script.  If no scripts have been
+ * buffered this method will return null.  If the request is an XHR(ajax) request
+ * onDomReady will be set to false. As the dom is already 'ready'.
+ *
+ * ### Options
+ *
+ * - `inline` - Set to true to have scripts output as a script block inline
+ *   if `cache` is also true, a script link tag will be generated. (default true)
+ * - `cache` - Set to true to have scripts cached to a file and linked in (default false)
+ * - `clear` - Set to false to prevent script cache from being cleared (default true)
+ * - `onDomReady` - wrap cached scripts in domready event (default true)
+ * - `safe` - if an inline block is generated should it be wrapped in <![CDATA[ ... ]]> (default true)
+ *
+ * @param array $options options for the code block
+ * @return mixed Completed javascript tag if there are scripts, if there are no buffered
+ *   scripts null will be returned.
+ * @access public
+ */
+	function writeBuffer($options = array()) {
+		$domReady = isset($this->params['isAjax']) ? !$this->params['isAjax'] : true;
+		$defaults = array(
+			'onDomReady' => $domReady, 'inline' => true, 
+			'cache' => false, 'clear' => true, 'safe' => true
+		);
+		$options = array_merge($defaults, $options);
+		$script = implode("\n", $this->getBuffer($options['clear']));
+
+		if (empty($script)) {
+			return null;
+		}
+
+		if ($options['onDomReady']) {
+			$script = $this->{$this->__engineName}->domReady($script);
+		}
+		$opts = $options;
+		unset($opts['onDomReady'], $opts['cache'], $opts['clear']);
+
+		if (!$options['cache'] && $options['inline']) {
+			return $this->Html->scriptBlock($script, $opts);
+		}
+
+		if ($options['cache'] && $options['inline']) {
+			$filename = md5($script);
+			if (!file_exists(JS . $filename . '.js')) {
+				cache(str_replace(WWW_ROOT, '', JS) . $filename . '.js', $script, '+999 days', 'public');
+			}
+			return $this->Html->script($filename);
+		}
+		$this->Html->scriptBlock($script, $opts);
+		return null;
+	}
+
+/**
+ * Write a script to the buffered scripts.
+ *
+ * @param string $script Script string to add to the buffer.
+ * @param boolean $top If true the script will be added to the top of the 
+ *   buffered scripts array.  If false the bottom.
+ * @return void
+ * @access public
+ */
+	function buffer($script, $top = false) {
+		if ($top) {
+			array_unshift($this->__bufferedScripts, $script);
+		} else {
+			$this->__bufferedScripts[] = $script;
+		}
+	}
+
+/**
+ * Get all the buffered scripts
+ *
+ * @param boolean $clear Whether or not to clear the script caches (default true)
+ * @return array Array of scripts added to the request.
+ * @access public
+ */
+	function getBuffer($clear = true) {
+		$this->_createVars();
+		$scripts = $this->__bufferedScripts;
+		if ($clear) {
+			$this->__bufferedScripts = array();
+			$this->__jsVars = array();
+		}
+		return $scripts;
+	}
+
+/**
+ * Generates the object string for variables passed to javascript.
+ *
+ * @return string Generated JSON object of all set vars
+ * @access protected
+ */
+	function _createVars() {
+		if (!empty($this->__jsVars)) {
+			$setVar = (strpos($this->setVariable, '.')) ? $this->setVariable : 'window.' . $this->setVariable;
+			$this->buffer($setVar . ' = ' . $this->object($this->__jsVars) . ';', true);
+		}
+	}
+
+/**
+ * Generate an 'Ajax' link.  Uses the selected JS engine to create a link
+ * element that is enhanced with Javascript.  Options can include
+ * both those for HtmlHelper::link() and JsBaseEngine::request(), JsBaseEngine::event();
+ *
+ * ### Options
+ *
+ * - `confirm` - Generate a confirm() dialog before sending the event.
+ * - `id` - use a custom id.
+ * - `htmlAttributes` - additional non-standard htmlAttributes.  Standard attributes are class, id,
+ *    rel, title, escape, onblur and onfocus.
+ * - `buffer` - Disable the buffering and return a script tag in addition to the link.
+ *
+ * @param string $title Title for the link.
+ * @param mixed $url Mixed either a string URL or an cake url array.
+ * @param array $options Options for both the HTML element and Js::request()
+ * @return string Completed link. If buffering is disabled a script tag will be returned as well.
+ * @access public
+ */
+	function link($title, $url = null, $options = array()) {
+		if (!isset($options['id'])) {
+			$options['id'] = 'link-' . intval(mt_rand());
+		}
+		list($options, $htmlOptions) = $this->_getHtmlOptions($options);
+		$out = $this->Html->link($title, $url, $htmlOptions);
+		$this->get('#' . $htmlOptions['id']);
+		$requestString = $event = '';
+		if (isset($options['confirm'])) {
+			$requestString = $this->confirmReturn($options['confirm']);
+			unset($options['confirm']);
+		}
+		$buffer = isset($options['buffer']) ? $options['buffer'] : null;
+		$safe = isset($options['safe']) ? $options['safe'] : true;
+		unset($options['buffer'], $options['safe']);
+
+		$requestString .= $this->request($url, $options);
+
+		if (!empty($requestString)) {
+			$event = $this->event('click', $requestString, $options + array('buffer' => $buffer));
+		}
+		if (isset($buffer) && !$buffer) {
+			$opts = array('safe' => $safe);
+			$out .= $this->Html->scriptBlock($event, $opts);
+		}
+		return $out;
+	}
+
+/**
+ * Pass variables into Javascript.  Allows you to set variables that will be
+ * output when the buffer is fetched with `JsHelper::getBuffer()` or `JsHelper::writeBuffer()`
+ * The Javascript variable used to output set variables can be controlled with `JsHelper::$setVariable`
+ *
+ * @param mixed $one Either an array of variables to set, or the name of the variable to set.
+ * @param mixed $two If $one is a string, $two is the value for that key.
+ * @return void
+ * @access public
+ */
+	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;
+		}
+		$this->__jsVars = array_merge($this->__jsVars, $data);
+	}
+
+/**
+ * Uses the selected JS engine to create a submit input
+ * element that is enhanced with Javascript.  Options can include
+ * both those for FormHelper::submit() and JsBaseEngine::request(), JsBaseEngine::event();
+ *
+ * Forms submitting with this method, cannot send files. Files do not transfer over XmlHttpRequest
+ * and require an iframe or flash.
+ *
+ * ### Options
+ * 
+ * - `url` The url you wish the XHR request to submit to.
+ * - `confirm` A string to use for a confirm() message prior to submitting the request.
+ * - `method` The method you wish the form to send by, defaults to POST
+ * - `buffer` Whether or not you wish the script code to be buffered, defaults to true.
+ * - Also see options for JsHelper::request() and JsHelper::event()
+ *
+ * @param string $title The display text of the submit button.
+ * @param array $options Array of options to use. See the options for the above mentioned methods.
+ * @return string Completed submit button.
+ * @access public
+ */
+	function submit($caption = null, $options = array()) {
+		if (!isset($options['id'])) {
+			$options['id'] = 'submit-' . intval(mt_rand());
+		}
+		$formOptions = array('div');
+		list($options, $htmlOptions) = $this->_getHtmlOptions($options, $formOptions);
+		$out = $this->Form->submit($caption, $htmlOptions);
+
+		$this->get('#' . $htmlOptions['id']);
+
+		$options['data'] = $this->serializeForm(array('isForm' => false, 'inline' => true));
+		$requestString = $url = '';
+		if (isset($options['confirm'])) {
+			$requestString = $this->confirmReturn($options['confirm']);
+			unset($options['confirm']);
+		}
+		if (isset($options['url'])) {
+			$url = $options['url'];
+			unset($options['url']);
+		}
+		if (!isset($options['method'])) {
+			$options['method'] = 'post';
+		}
+		$options['dataExpression'] = true;
+
+		$buffer = isset($options['buffer']) ? $options['buffer'] : null;
+		$safe = isset($options['safe']) ? $options['safe'] : true;
+		unset($options['buffer'], $options['safe']);
+
+		$requestString .= $this->request($url, $options);
+		if (!empty($requestString)) {
+			$event = $this->event('click', $requestString, $options + array('buffer' => $buffer));
+		}
+		if (isset($buffer) && !$buffer) {
+			$opts = array('safe' => $safe);
+			$out .= $this->Html->scriptBlock($event, $opts);
+		}
+		return $out;
+	}
+
+/**
+ * Parse a set of Options and extract the Html options.
+ * Extracted Html Options are removed from the $options param.
+ *
+ * @param array $options Options to filter.
+ * @param array $additional Array of additional keys to extract and include in the return options array.
+ * @return array Array of js options and Htmloptions
+ * @access protected
+ */
+	function _getHtmlOptions($options, $additional = array()) {
+		$htmlKeys = array_merge(
+			array('class', 'id', 'escape', 'onblur', 'onfocus', 'rel', 'title', 'style'), 
+			$additional
+		);
+		$htmlOptions = array();
+		foreach ($htmlKeys as $key) {
+			if (isset($options[$key])) {
+				$htmlOptions[$key] = $options[$key];
+			}
+			unset($options[$key]);
+		}
+		if (isset($options['htmlAttributes'])) {
+			$htmlOptions = array_merge($htmlOptions, $options['htmlAttributes']);
+			unset($options['htmlAttributes']);
+		}
+		return array($options, $htmlOptions);
+	}
+}
+
+/**
+ * JsEngineBaseClass
+ *
+ * Abstract Base Class for All JsEngines to extend. Provides generic methods.
+ *
+ * @package cake.view.helpers
+ */
+class JsBaseEngineHelper extends AppHelper {
+/**
+ * Determines whether native JSON extension is used for encoding.  Set by object constructor.
+ *
+ * @var boolean
+ * @access public
+ */
+	var $useNative = false;
+
+/**
+ * The js snippet for the current selection.
+ *
+ * @var string
+ * @access public
+ */
+	var $selection;
+
+/**
+ * Collection of option maps. Option maps allow other helpers to use generic names for engine
+ * callbacks and options.  Allowing uniform code access for all engine types.  Their use is optional
+ * for end user use though.
+ *
+ * @var array
+ * @access protected
+ */
+	var $_optionMap = array();
+
+/**
+ * An array of lowercase method names in the Engine that are buffered unless otherwise disabled.
+ * This allows specific 'end point' methods to be automatically buffered by the JsHelper.
+ *
+ * @var array
+ * @access public
+ */
+	var $bufferedMethods = array('event', 'sortable', 'drag', 'drop', 'slider');
+
+/**
+ * Contains a list of callback names -> default arguments.
+ *
+ * @var array
+ * @access protected
+ */
+	var $_callbackArguments = array();
+
+/**
+ * Constructor.
+ *
+ * @return void
+ */
+	function __construct() {
+		parent::__construct();
+		$this->useNative = function_exists('json_encode');
+	}
+
+/**
+ * Create an `alert()` message in Javascript
+ *
+ * @param string $message Message you want to alter.
+ * @return string completed alert()
+ * @access public
+ */
+	function alert($message) {
+		return 'alert("' . $this->escape($message) . '");';
+	}
+
+/**
+ * Redirects to a URL.  Creates a window.location modification snippet
+ * that can be used to trigger 'redirects' from Javascript.
+ *
+ * @param mixed $url
+ * @param array  $options
+ * @return string completed redirect in javascript
+ * @access public
+ */
+	function redirect($url = null) {
+		return 'window.location = "' . Router::url($url) . '";';
+	}
+
+/**
+ * Create a `confirm()` message
+ *
+ * @param string $message Message you want confirmed.
+ * @return string completed confirm()
+ * @access public
+ */
+	function confirm($message) {
+		return 'confirm("' . $this->escape($message) . '");';
+	}
+
+/**
+ * Generate a confirm snippet that returns false from the current
+ * function scope.
+ *
+ * @param string $message Message to use in the confirm dialog.
+ * @return string completed confirm with return script
+ * @access public
+ */
+	function confirmReturn($message) {
+		$out = 'var _confirm = ' . $this->confirm($message);
+		$out .= "if (!_confirm) {\n\treturn false;\n}";
+		return $out;
+	}
+
+/**
+ * Create a `prompt()` Javascript function
+ *
+ * @param string $message Message you want to prompt.
+ * @param string $default Default message
+ * @return string completed prompt()
+ * @access public
+ */
+	function prompt($message, $default = '') {
+		return 'prompt("' . $this->escape($message) . '", "' . $this->escape($default) . '");';
+	}
+
+/**
+ * Generates a JavaScript object in JavaScript Object Notation (JSON)
+ * from an array.  Will use native JSON encode method if available, and $useNative == true
+ *
+ * ### Options:
+ *
+ * - `prefix` - String prepended to the returned data.
+ * - `postfix` - String appended to the returned data.
+ *
+ * @param array $data Data to be converted.
+ * @param array $options Set of options, see above.
+ * @return string A JSON code block
+ * @access public
+ */
+	function object($data = array(), $options = array()) {
+		$defaultOptions = array(
+			'prefix' => '', 'postfix' => '',
+		);
+		$options = array_merge($defaultOptions, $options);
+
+		if (is_object($data)) {
+			$data = get_object_vars($data);
+		}
+
+		$out = $keys = array();
+		$numeric = true;
+
+		if ($this->useNative && function_exists('json_encode')) {
+			$rt = json_encode($data);
+		} else {
+			if (is_null($data)) {
+				return 'null';
+			}
+			if (is_bool($data)) {
+				return $data ? 'true' : 'false';
+			}
+			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);
+				} else {
+					$val = $this->value($val);
+				}
+				if (!$numeric) {
+					$val = '"' . $this->value($key, false) . '":' . $val;
+				}
+				$out[] = $val;
+			}
+
+			if (!$numeric) {
+				$rt = '{' . join(',', $out) . '}';
+			} else {
+				$rt = '[' . join(',', $out) . ']';
+			}
+		}
+		$rt = $options['prefix'] . $rt . $options['postfix'];
+		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
+ * @access public
+ */
+	function value($val, $quoteString = 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 = ($val === true) ? 'true' : 'false';
+			break;
+			case (is_int($val)):
+				$val = $val;
+			break;
+			case (is_float($val)):
+				$val = sprintf("%.11f", $val);
+			break;
+			default:
+				$val = $this->escape($val);
+				if ($quoteString) {
+					$val = '"' . $val . '"';
+				}
+			break;
+		}
+		return $val;
+	}
+
+/**
+ * Escape a string to be JSON friendly.
+ *
+ * List of escaped elements:
+ *
+ * - "\r" => '\n'
+ * - "\n" => '\n'
+ * - '"' => '\"'
+ *
+ * @param  string $script String that needs to get escaped.
+ * @return string Escaped string.
+ * @access public
+ */
+	function escape($string) {
+		App::import('Core', 'Multibyte');
+		return $this->_utf8ToHex($string);
+	}
+
+/**
+ * Encode a string into JSON.  Converts and escapes necessary characters.
+ *
+ * @param string $string The string that needs to be utf8->hex encoded
+ * @return void
+ * @access protected
+ */
+	function _utf8ToHex($string) {
+		$length = strlen($string);
+		$return = '';
+		for ($i = 0; $i < $length; ++$i) {
+			$ord = ord($string{$i});
+			switch (true) {
+				case $ord == 0x08:
+					$return .= '\b';
+					break;
+				case $ord == 0x09:
+					$return .= '\t';
+					break;
+				case $ord == 0x0A:
+					$return .= '\n';
+					break;
+				case $ord == 0x0C:
+					$return .= '\f';
+					break;
+				case $ord == 0x0D:
+					$return .= '\r';
+					break;
+				case $ord == 0x22:
+				case $ord == 0x2F:
+				case $ord == 0x5C:
+					$return .= '\\' . $string{$i};
+					break;
+				case (($ord >= 0x20) && ($ord <= 0x7F)):
+					$return .= $string{$i};
+					break;
+				case (($ord & 0xE0) == 0xC0):
+					if ($i + 1 >= $length) {
+						$i += 1;
+						$return .= '?';
+						break;
+					}
+					$charbits = $string{$i} . $string{$i + 1};
+					$char = Multibyte::utf8($charbits);
+					$return .= sprintf('\u%04s', dechex($char[0]));
+					$i += 1;
+					break;
+				case (($ord & 0xF0) == 0xE0):
+					if ($i + 2 >= $length) {
+						$i += 2;
+						$return .= '?';
+						break;
+					}
+					$charbits = $string{$i} . $string{$i + 1} . $string{$i + 2};
+					$char = Multibyte::utf8($charbits);
+					$return .= sprintf('\u%04s', dechex($char[0]));
+					$i += 2;
+					break;
+				case (($ord & 0xF8) == 0xF0):
+					if ($i + 3 >= $length) {
+					   $i += 3;
+					   $return .= '?';
+					   break;
+					}
+					$charbits = $string{$i} . $string{$i + 1} . $string{$i + 2} . $string{$i + 3};
+					$char = Multibyte::utf8($charbits);
+					$return .= sprintf('\u%04s', dechex($char[0]));
+					$i += 3;
+					break;
+				case (($ord & 0xFC) == 0xF8):
+					if ($i + 4 >= $length) {
+					   $i += 4;
+					   $return .= '?';
+					   break;
+					}
+					$charbits = $string{$i} . $string{$i + 1} . $string{$i + 2} . $string{$i + 3} . $string{$i + 4};
+					$char = Multibyte::utf8($charbits);
+					$return .= sprintf('\u%04s', dechex($char[0]));
+					$i += 4;
+					break;
+				case (($ord & 0xFE) == 0xFC):
+					if ($i + 5 >= $length) {
+					   $i += 5;
+					   $return .= '?';
+					   break;
+					}
+					$charbits = $string{$i} . $string{$i + 1} . $string{$i + 2} . $string{$i + 3} . $string{$i + 4} . $string{$i + 5};
+					$char = Multibyte::utf8($charbits);
+					$return .= sprintf('\u%04s', dechex($char[0]));
+					$i += 5;
+					break;
+			}
+		}
+		return $return;
+	}
+
+/**
+ * Create javascript selector for a CSS rule
+ *
+ * @param string $selector The selector that is targeted
+ * @return object instance of $this. Allows chained methods.
+ * @access public
+ */
+	function get($selector) {
+		trigger_error(sprintf(__('%s does not have get() implemented', true), get_class($this)), E_USER_WARNING);
+		return $this;
+	}
+
+/**
+ * Add an event to the script cache. Operates on the currently selected elements.
+ *
+ * ### Options
+ *
+ * - `wrap` - Whether you want the callback wrapped in an anonymous function. (defaults to true)
+ * - `stop` - Whether you want the event to stopped. (defaults to true)
+ *
+ * @param string $type Type of event to bind to the current dom id
+ * @param string $callback The Javascript function you wish to trigger or the function literal
+ * @param array $options Options for the event.
+ * @return string completed event handler
+ * @access public
+ */
+	function event($type, $callback, $options = array()) {
+		trigger_error(sprintf(__('%s does not have event() implemented', true), get_class($this)), E_USER_WARNING);
+	}
+
+/**
+ * Create a domReady event. This is a special event in many libraries
+ *
+ * @param string $functionBody The code to run on domReady
+ * @return string completed domReady method
+ * @access public
+ */
+	function domReady($functionBody) {
+		trigger_error(sprintf(__('%s does not have domReady() implemented', true), get_class($this)), E_USER_WARNING);
+	}
+
+/**
+ * Create an iteration over the current selection result.
+ *
+ * @param string $callback The function body you wish to apply during the iteration.
+ * @return string completed iteration
+ */
+	function each($callback) {
+		trigger_error(sprintf(__('%s does not have each() implemented', true), get_class($this)), E_USER_WARNING);
+	}
+
+/**
+ * Trigger an Effect.
+ *
+ * ### Supported Effects
+ *
+ * The following effects are supported by all core JsEngines
+ *
+ * - `show` - reveal an element.
+ * - `hide` - hide an element.
+ * - `fadeIn` - Fade in an element.
+ * - `fadeOut` - Fade out an element.
+ * - `slideIn` - Slide an element in.
+ * - `slideOut` - Slide an element out.
+ *
+ * ### Options
+ *
+ * - `speed` - Speed at which the animation should occur. Accepted values are 'slow', 'fast'. Not all effects use
+ *   the speed option.
+ *
+ * @param string $name The name of the effect to trigger.
+ * @param array $options Array of options for the effect.
+ * @return string completed string with effect.
+ * @access public
+ */
+	function effect($name, $options) {
+		trigger_error(sprintf(__('%s does not have effect() implemented', true), get_class($this)), E_USER_WARNING);
+	}
+
+/**
+ * Make an XHR request
+ *
+ * ### Event Options
+ *
+ * - `complete` - Callback to fire on complete.
+ * - `success` - Callback to fire on success.
+ * - `before` - Callback to fire on request initialization.
+ * - `error` - Callback to fire on request failure.
+ *
+ * ### Options
+ *
+ * - `method` - The method to make the request with defaults to GET in more libraries
+ * - `async` - Whether or not you want an asynchronous request.
+ * - `data` - Additional data to send.
+ * - `update` - Dom id to update with the content of the request.
+ * - `type` - Data type for response. 'json' and 'html' are supported. Default is html for most libraries.
+ * - `evalScripts` - Whether or not <script> tags should be eval'ed.
+ * - `dataExpression` - Should the `data` key be treated as a callback.  Useful for supplying `$options['data']` as
+ *    another Javascript expression.
+ *
+ * @param mixed $url Array or String URL to target with the request.
+ * @param array $options Array of options. See above for cross library supported options
+ * @return string XHR request.
+ * @access public
+ */
+	function request($url, $options = array()) {
+		trigger_error(sprintf(__('%s does not have request() implemented', true), get_class($this)), E_USER_WARNING);
+	}
+
+/**
+ * Create a draggable element.  Works on the currently selected element.
+ * Additional options may be supported by the library implementation.
+ *
+ * ### Options
+ *
+ * - `handle` - selector to the handle element.
+ * - `snapGrid` - The pixel grid that movement snaps to, an array(x, y)
+ * - `container` - The element that acts as a bounding box for the draggable element.
+ *
+ * ### Event Options
+ *
+ * - `start` - Event fired when the drag starts
+ * - `drag` - Event fired on every step of the drag
+ * - `stop` - Event fired when dragging stops (mouse release)
+ *
+ * @param array $options Options array see above.
+ * @return string Completed drag script
+ * @access public
+ */
+	function drag($options = array()) {
+		trigger_error(sprintf(__('%s does not have drag() implemented', true), get_class($this)), E_USER_WARNING);
+	}
+
+/**
+ * Create a droppable element. Allows for draggable elements to be dropped on it.
+ * Additional options may be supported by the library implementation.
+ *
+ * ### Options
+ *
+ * - `accept` - Selector for elements this droppable will accept.
+ * - `hoverclass` - Class to add to droppable when a draggable is over.
+ *
+ * ### Event Options
+ *
+ * - `drop` - Event fired when an element is dropped into the drop zone.
+ * - `hover` - Event fired when a drag enters a drop zone.
+ * - `leave` - Event fired when a drag is removed from a drop zone without being dropped.
+ *
+ * @return string Completed drop script
+ * @access public
+ */
+	function drop($options = array()) {
+		trigger_error(sprintf(__('%s does not have drop() implemented', true), get_class($this)), E_USER_WARNING);
+	}
+
+/**
+ * Create a sortable element.
+ * Additional options may be supported by the library implementation.
+ *
+ * ### Options
+ *
+ * - `containment` - Container for move action
+ * - `handle` - Selector to handle element. Only this element will start sort action.
+ * - `revert` - Whether or not to use an effect to move sortable into final position.
+ * - `opacity` - Opacity of the placeholder
+ * - `distance` - Distance a sortable must be dragged before sorting starts.
+ *
+ * ### Event Options
+ *
+ * - `start` - Event fired when sorting starts
+ * - `sort` - Event fired during sorting
+ * - `complete` - Event fired when sorting completes.
+ *
+ * @param array $options Array of options for the sortable. See above.
+ * @return string Completed sortable script.
+ * @access public
+ */
+	function sortable() {
+		trigger_error(sprintf(__('%s does not have sortable() implemented', true), get_class($this)), E_USER_WARNING);
+	}
+
+/**
+ * Create a slider UI widget.  Comprised of a track and knob.
+ * Additional options may be supported by the library implementation.
+ *
+ * ### Options
+ *
+ * - `handle` - The id of the element used in sliding.
+ * - `direction` - The direction of the slider either 'vertical' or 'horizontal'
+ * - `min` - The min value for the slider.
+ * - `max` - The max value for the slider.
+ * - `step` - The number of steps or ticks the slider will have.
+ * - `value` - The initial offset of the slider.
+ *
+ * ### Events
+ *
+ * - `change` - Fired when the slider's value is updated
+ * - `complete` - Fired when the user stops sliding the handle
+ *
+ * @return string Completed slider script
+ * @access public
+ */
+	function slider() {
+		trigger_error(sprintf(__('%s does not have slider() implemented', true), get_class($this)), E_USER_WARNING);
+	}
+
+/**
+ * Serialize the form attached to $selector.
+ * Pass `true` for $isForm if the current selection is a form element.
+ * Converts the form or the form element attached to the current selection into a string/json object
+ * (depending on the library implementation) for use with XHR operations.
+ *
+ * ### Options
+ *
+ * - `isForm` - is the current selection a form, or an input? (defaults to false)
+ * - `inline` - is the rendered statement going to be used inside another JS statement? (defaults to false)
+ *
+ * @param array $options options for serialization generation.
+ * @return string completed form serialization script
+ * @access public
+ */
+	function serializeForm() {
+		trigger_error(
+			sprintf(__('%s does not have serializeForm() implemented', true), get_class($this)), E_USER_WARNING
+		);
+	}
+
+/**
+ * Parse an options assoc array into an Javascript object literal.
+ * Similar to object() but treats any non-integer value as a string,
+ * does not include `{ }`
+ *
+ * @param array $options Options to be converted
+ * @param array $safeKeys Keys that should not be escaped.
+ * @return string Parsed JSON options without enclosing { }.
+ * @access protected
+ */
+	function _parseOptions($options, $safeKeys = array()) {
+		$out = array();
+		$safeKeys = array_flip($safeKeys);
+		foreach ($options as $key => $value) {
+			if (!is_int($value) && !isset($safeKeys[$key])) {
+				$value = $this->value($value);
+			}
+			$out[] = $key . ':' . $value;
+		}
+		sort($out);
+		return join(', ', $out);
+	}
+
+/**
+ * Maps Abstract options to engine specific option names.
+ * If attributes are missing from the map, they are not changed.
+ *
+ * @param string $method Name of method whose options are being worked with.
+ * @param array $options Array of options to map.
+ * @return array Array of mapped options.
+ * @access protected
+ */
+	function _mapOptions($method, $options) {
+		if (!isset($this->_optionMap[$method])) {
+			return $options;
+		}
+		foreach ($this->_optionMap[$method] as $abstract => $concrete) {
+			if (isset($options[$abstract])) {
+				$options[$concrete] = $options[$abstract];
+				unset($options[$abstract]);
+			}
+		}
+		return $options;
+	}
+
+/**
+ * Prepare callbacks and wrap them with function ([args]) { } as defined in
+ * _callbackArgs array.
+ *
+ * @param string $method Name of the method you are preparing callbacks for.
+ * @param array $options Array of options being parsed
+ * @param string $callbacks Additional Keys that contain callbacks
+ * @return array Array of options with callbacks added.
+ * @access protected
+ */
+	function _prepareCallbacks($method, $options, $callbacks = array()) {
+		$wrapCallbacks = true;
+		if (isset($options['wrapCallbacks'])) {
+			$wrapCallbacks = $options['wrapCallbacks'];
+		}
+		unset($options['wrapCallbacks']);
+		if (!$wrapCallbacks) {
+			return $options;
+		}
+		$callbackOptions = array();
+		if (isset($this->_callbackArguments[$method])) {
+			$callbackOptions = $this->_callbackArguments[$method];
+		}
+		$callbacks = array_unique(array_merge(array_keys($callbackOptions), (array)$callbacks));
+
+		foreach ($callbacks as $callback) {
+			if (empty($options[$callback])) {
+				continue;
+			}
+			$args = null;
+			if (!empty($callbackOptions[$callback])) {
+				$args = $callbackOptions[$callback];
+			}
+			$options[$callback] = 'function (' . $args . ') {' . $options[$callback] . '}';
+		}
+		return $options;
+	}
+
+/**
+ * Conveinence wrapper method for all common option processing steps.
+ * Runs _mapOptions, _prepareCallbacks, and _parseOptions in order.
+ *
+ * @param string $method Name of method processing options for.
+ * @param array $options Array of options to process.
+ * @return string Parsed options string.
+ * @access protected
+ */
+	function _processOptions($method, $options) {
+		$options = $this->_mapOptions($method, $options);
+		$options = $this->_prepareCallbacks($method, $options);
+		$options = $this->_parseOptions($options, array_keys($this->_callbackArguments[$method]));
+		return $options;
+	}
+
+/**
+ * Convert an array of data into a query string
+ *
+ * @param array $parameters Array of parameters to convert to a query string
+ * @return string Querystring fragment
+ * @access protected
+ */
+	function _toQuerystring($parameters) {
+		$out = '';
+		$keys = array_keys($parameters);
+		$count = count($parameters);
+		for ($i = 0; $i < $count; $i++) {
+			$out .= $keys[$i] . '=' . $parameters[$keys[$i]];
+			if ($i < $count - 1) {
+				$out .= '&';
+			}
+		}
+		return $out;
+	}
+}

Added: trunk/src/Web/cake/libs/view/helpers/mootools_engine.php
===================================================================
--- trunk/src/Web/cake/libs/view/helpers/mootools_engine.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/helpers/mootools_engine.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,374 @@
+<?php
+/**
+ * MooTools Engine Helper for JsHelper
+ *
+ * Provides MooTools specific Javascript for JsHelper.
+ * Assumes that you have the following MooTools packages
+ *
+ * - Remote, Remote.HTML, Remote.JSON
+ * - Fx, Fx.Tween, Fx.Morph
+ * - Selectors, DomReady,
+ * - Drag, Drag.Move
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.libs.view.helpers
+ * @since         CakePHP(tm) v 1.3
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Helper', 'Js');
+
+class MootoolsEngineHelper extends JsBaseEngineHelper {
+/**
+ * Option mappings for MooTools
+ *
+ * @var array
+ */
+	var $_optionMap = array(
+		'request' => array(
+			'complete' => 'onComplete',
+			'success' => 'onSuccess',
+			'before' => 'onRequest',
+			'error' => 'onFailure'
+		),
+		'sortable' => array(
+			'distance' => 'snap',
+			'containment' => 'constrain',
+			'sort' => 'onSort',
+			'complete' => 'onComplete',
+			'start' => 'onStart',
+		),
+		'drag' => array(
+			'snapGrid' => 'snap',
+			'start' => 'onStart',
+			'drag' => 'onDrag',
+			'stop' => 'onComplete',
+		),
+		'drop' => array(
+			'drop' => 'onDrop',
+			'hover' => 'onEnter',
+			'leave' => 'onLeave',
+		),
+		'slider' => array(
+			'complete' => 'onComplete',
+			'change' => 'onChange',
+			'direction' => 'mode',
+			'step' => 'steps'
+		)
+	);
+
+/**
+ * Contains a list of callback names -> default arguments.
+ *
+ * @var array
+ */
+	var $_callbackArguments = array(
+		'slider' => array(
+			'onTick' => 'position',
+			'onChange' => 'step',
+			'onComplete' => 'event'
+		),
+		'request' => array(
+			'onRequest' => '',
+			'onComplete' => '',
+			'onCancel' => '',
+			'onSuccess' => 'responseText, responseXML',
+			'onFailure' => 'xhr',
+			'onException' => 'headerName, value',
+		),
+		'drag' => array(
+			'onBeforeStart' => 'element',
+			'onStart' => 'element',
+			'onSnap' => 'element',
+			'onDrag' => 'element, event',
+			'onComplete' => 'element, event',
+			'onCancel' => 'element',
+		),
+		'drop' => array(
+			'onBeforeStart' => 'element',
+			'onStart' => 'element',
+			'onSnap' => 'element',
+			'onDrag' => 'element, event',
+			'onComplete' => 'element, event',
+			'onCancel' => 'element',
+			'onDrop' => 'element, droppable, event',
+			'onLeave' => 'element, droppable',
+			'onEnter' => 'element, droppable',
+		),
+		'sortable' => array(
+			'onStart' => 'element, clone',
+			'onSort' => 'element, clone',
+			'onComplete' => 'element',
+		)
+	);
+
+/**
+ * Create javascript selector for a CSS rule
+ *
+ * @param string $selector The selector that is targeted
+ * @return object instance of $this. Allows chained methods.
+ */
+	function get($selector) {
+		$this->_multipleSelection = false;
+		if ($selector == 'window' || $selector == 'document') {
+			$this->selection = "$(" . $selector .")";
+			return $this;
+		}
+		if (preg_match('/^#[^\s.]+$/', $selector)) {
+			$this->selection = '$("' . substr($selector, 1) . '")';
+			return $this;
+		}
+		$this->_multipleSelection = true;
+		$this->selection = '$$("' . $selector . '")';
+		return $this;
+	}
+
+/**
+ * Add an event to the script cache. Operates on the currently selected elements.
+ *
+ * ### Options
+ *
+ * - 'wrap' - Whether you want the callback wrapped in an anonymous function. (defaults true)
+ * - 'stop' - Whether you want the event to stopped. (defaults true)
+ *
+ * @param string $type Type of event to bind to the current dom id
+ * @param string $callback The Javascript function you wish to trigger or the function literal
+ * @param array $options Options for the event.
+ * @return string completed event handler
+ */
+	function event($type, $callback, $options = array()) {
+		$defaults = array('wrap' => true, 'stop' => true);
+		$options = array_merge($defaults, $options);
+
+		$function = 'function (event) {%s}';
+		if ($options['wrap'] && $options['stop']) {
+			$callback = "event.stop();\n" . $callback;
+		}
+		if ($options['wrap']) {
+			$callback = sprintf($function, $callback);
+		}
+		$out = $this->selection . ".addEvent(\"{$type}\", $callback);";
+		return $out;
+	}
+
+/**
+ * Create a domReady event. This is a special event in many libraries
+ *
+ * @param string $functionBody The code to run on domReady
+ * @return string completed domReady method
+ */
+	function domReady($functionBody) {
+		$this->selection = 'window';
+		return $this->event('domready', $functionBody, array('stop' => false));
+	}
+
+/**
+ * Create an iteration over the current selection result.
+ *
+ * @param string $method The method you want to apply to the selection
+ * @param string $callback The function body you wish to apply during the iteration.
+ * @return string completed iteration
+ */
+	function each($callback) {
+		return $this->selection . '.each(function (item, index) {' . $callback . '});';
+	}
+
+/**
+ * Trigger an Effect.
+ *
+ * @param string $name The name of the effect to trigger.
+ * @param array $options Array of options for the effect.
+ * @return string completed string with effect.
+ * @see JsBaseEngineHelper::effect()
+ */
+	function effect($name, $options = array()) {
+		$speed = null;
+		if (isset($options['speed']) && in_array($options['speed'], array('fast', 'slow'))) {
+			if ($options['speed'] == 'fast') {
+				$speed = '"short"';
+			} elseif ($options['speed'] == 'slow') {
+				$speed = '"long"';
+			}
+		}
+		$effect = '';
+		switch ($name) {
+			case 'hide':
+				$effect = 'setStyle("display", "none")';
+			break;
+			case 'show':
+				$effect = 'setStyle("display", "")';
+			break;
+			case 'fadeIn':
+			case 'fadeOut':
+			case 'slideIn':
+			case 'slideOut':
+				list($effectName, $direction) = preg_split('/([A-Z][a-z]+)/', $name, -1, PREG_SPLIT_DELIM_CAPTURE);
+				$direction = strtolower($direction);
+				if ($speed) {
+					$effect .= "set(\"$effectName\", {duration:$speed}).";
+				}
+				$effect .= "$effectName(\"$direction\")";
+			break;
+		}
+		return $this->selection . '.' . $effect . ';';
+	}
+
+/**
+ * Create an new Request.
+ *
+ * Requires `Request`.  If you wish to use 'update' key you must have ```Request.HTML```
+ * if you wish to do Json requests you will need ```JSON``` and ```Request.JSON```.
+ *
+ * @param mixed $url
+ * @param array $options
+ * @return string The completed ajax call.
+ */
+	function request($url, $options = array()) {
+		$url = $this->url($url);
+		$options = $this->_mapOptions('request', $options);
+		$type = $data = null;
+		if (isset($options['type']) || isset($options['update'])) {
+			if (isset($options['type']) && strtolower($options['type']) == 'json') {
+				$type = '.JSON';
+			}
+			if (isset($options['update'])) {
+				$options['update'] = str_replace('#', '', $options['update']);
+				$type = '.HTML';
+			}
+			unset($options['type']);
+		}
+		if (!empty($options['data'])) {
+			$data = $options['data'];
+			unset($options['data']);
+		}
+		$options['url'] = $url;
+		$options = $this->_prepareCallbacks('request', $options);
+		if (!empty($options['dataExpression'])) {
+			$callbacks[] = 'data';
+			unset($options['dataExpression']);
+		} elseif (!empty($data)) {
+			$data = $this->object($data);
+		}
+		$options = $this->_parseOptions($options, array_keys($this->_callbackArguments['request']));
+		return "var jsRequest = new Request$type({{$options}}).send($data);";
+	}
+
+/**
+ * Create a sortable element.
+ *
+ * Requires the `Sortables` plugin from MootoolsMore
+ *
+ * @param array $options Array of options for the sortable.
+ * @return string Completed sortable script.
+ * @see JsBaseEngineHelper::sortable() for options list.
+ */
+	function sortable($options = array()) {
+		$options = $this->_processOptions('sortable', $options);
+		return 'var jsSortable = new Sortables(' . $this->selection . ', {' . $options . '});';
+	}
+
+/**
+ * Create a Draggable element.
+ *
+ * Requires the `Drag` plugin from MootoolsMore
+ *
+ * @param array $options Array of options for the draggable.
+ * @return string Completed draggable script.
+ * @see JsHelper::drag() for options list.
+ */
+	function drag($options = array()) {
+		$options = $this->_processOptions('drag', $options);
+		return $this->selection . '.makeDraggable({' . $options . '});';
+	}
+
+/**
+ * Create a Droppable element.
+ *
+ * Requires the `Drag` and `Drag.Move` plugins from MootoolsMore
+ *
+ * Droppables in Mootools function differently from other libraries.  Droppables
+ * are implemented as an extension of Drag.  So in addtion to making a get() selection for
+ * the droppable element. You must also provide a selector rule to the draggable element. Furthermore,
+ * Mootools droppables inherit all options from Drag.
+ *
+ * @param array $options Array of options for the droppable.
+ * @return string Completed droppable script.
+ * @see JsBaseEngineHelper::drop() for options list.
+ */
+	function drop($options = array()) {
+		if (empty($options['drag'])) {
+			trigger_error(
+				__('MootoolsEngine::drop() requires a "drag" option to properly function', true), E_USER_WARNING
+			);
+			return false;
+		}
+		$options['droppables'] = $this->selection;
+
+		$this->get($options['drag']);
+		unset($options['drag']);
+
+		$options = $this->_mapOptions('drag', $this->_mapOptions('drop', $options));
+		$options = $this->_prepareCallbacks('drop', $options);
+		$safe = array_merge(array_keys($this->_callbackArguments['drop']), array('droppables'));
+		$optionString = $this->_parseOptions($options, $safe);
+		$out = $this->selection . '.makeDraggable({' . $optionString . '});';
+		$this->selection = $options['droppables'];
+		return $out;
+	}
+
+/**
+ * Create a slider control
+ *
+ * Requires `Slider` from MootoolsMore
+ *
+ * @param array $options Array of options for the slider.
+ * @return string Completed slider script.
+ * @see JsBaseEngineHelper::slider() for options list.
+ */
+	function slider($options = array()) {
+		$slider = $this->selection;
+		$this->get($options['handle']);
+		unset($options['handle']);
+
+		if (isset($options['min']) && isset($options['max'])) {
+			$options['range'] = array($options['min'], $options['max']);
+			unset($options['min'], $options['max']);
+		}
+		$optionString = $this->_processOptions('slider', $options);
+		if (!empty($optionString)) {
+			$optionString = ', {' . $optionString . '}';
+		}
+		$out = 'var jsSlider = new Slider(' . $slider . ', ' . $this->selection . $optionString . ');';
+		$this->selection = $slider;
+		return $out;
+	}
+
+/**
+ * Serialize the form attached to $selector.
+ *
+ * @param array $options Array of options.
+ * @return string Completed serializeForm() snippet
+ * @see JsBaseEngineHelper::serializeForm()
+ */
+	function serializeForm($options = array()) {
+		$options = array_merge(array('isForm' => false, 'inline' => false), $options);
+		$selection = $this->selection;
+		if (!$options['isForm']) {
+			$selection = '$(' . $this->selection . '.form)';
+		}
+		$method = '.toQueryString()';
+		if (!$options['inline']) {
+			$method .= ';';
+		}
+		return $selection . $method;
+	}
+}

Added: trunk/src/Web/cake/libs/view/helpers/number.php
===================================================================
--- trunk/src/Web/cake/libs/view/helpers/number.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/helpers/number.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,257 @@
+<?php
+/**
+ * Number Helper.
+ *
+ * Methods to make numbers more readable.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Number helper library.
+ *
+ * Methods to make numbers more readable.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @link http://book.cakephp.org/view/1452/Number
+ */
+class NumberHelper extends AppHelper {
+
+/**
+ * Currencies supported by the helper.  You can add additional currency formats
+ * with NumberHelper::addFormat
+ *
+ * @var array
+ * @access protected
+ */
+	var $_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' => false, 'zero' => 0, 'places' => 2, 'thousands' => '.',
+			'decimals' => ',', 'negative' => '()', 'escape' => false
+		)
+	);
+
+/**
+ * Default options for currency formats
+ *
+ * @var array
+ * @access protected
+ */
+	var $_currencyDefaults = array(
+		'before'=>'', 'after' => false, 'zero' => '0', 'places' => 2, 'thousands' => ',',
+		'decimals' => '.','negative' => '()', 'escape' => true
+	);
+
+/**
+ * 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 Formatted float.
+ * @access public
+ * @link http://book.cakephp.org/view/1454/precision
+ */
+	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
+ * @access public
+ * @link http://book.cakephp.org/view/1456/toReadableSize
+ */
+	function toReadableSize($size) {
+		switch (true) {
+			case $size < 1024:
+				return sprintf(__n('%d Byte', '%d Bytes', $size, true), $size);
+			case round($size / 1024) < 1024:
+				return sprintf(__('%d KB', true), $this->precision($size / 1024, 0));
+			case round($size / 1024 / 1024, 2) < 1024:
+				return sprintf(__('%.2f MB', true), $this->precision($size / 1024 / 1024, 2));
+			case round($size / 1024 / 1024 / 1024, 2) < 1024:
+				return sprintf(__('%.2f GB', true), $this->precision($size / 1024 / 1024 / 1024, 2));
+			default:
+				return sprintf(__('%.2f TB', true), $this->precision($size / 1024 / 1024 / 1024 / 1024, 2));
+		}
+	}
+
+/**
+ * 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
+ * @access public
+ * @link http://book.cakephp.org/view/1455/toPercentage
+ */
+	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
+ * @access public
+ * @link http://book.cakephp.org/view/1457/format
+ */
+	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.
+ *
+ * ### Options
+ *
+ * - `before` - The currency symbol to place before whole numbers ie. '$'
+ * - `after` - The currency symbol to place after decimal numbers ie. 'c'. Set to boolean false to 
+ *    use no decimal symbol.  eg. 0.35 => $0.35.
+ * - `zero` - The text to use for zero values, can be a string or a number. ie. 0, 'Free!'
+ * - `places` - Number of decimal places to use. ie. 2
+ * - `thousands` - Thousands separator ie. ','
+ * - `decimals` - Decimal separator symbol ie. '.'
+ * - `negative` - Symbol for negative numbers. If equal to '()', the number will be wrapped with ( and )
+ * - `escape` - Should the output be htmlentity escaped? Defaults to true
+ *
+ * @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.
+ * @access public
+ * @link http://book.cakephp.org/view/1453/currency
+ */
+	function currency($number, $currency = 'USD', $options = array()) {
+		$default = $this->_currencyDefaults;
+
+		if (isset($this->_currencies[$currency])) {
+			$default = $this->_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 ) {
+			if ($options['after'] !== false) {
+				$multiply = intval('1' . str_pad('', $options['places'], '0'));
+				$number = $number * $multiply;
+				$options['before'] = null;
+				$options['places'] = null;
+			}
+		} elseif (empty($options['before'])) {
+			$options['before'] = 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;
+	}
+
+/**
+ * Add a currency format to the Number helper.  Makes reusing
+ * currency formats easier.
+ *
+ * {{{ $number->addFormat('NOK', array('before' => 'Kr. ')); }}}
+ * 
+ * You can now use `NOK` as a shortform when formatting currency amounts.
+ *
+ * {{{ $number->currency($value, 'NOK'); }}}
+ *
+ * Added formats are merged with the following defaults.
+ *
+ * {{{
+ *	array(
+ *		'before' => '$', 'after' => 'c', 'zero' => 0, 'places' => 2, 'thousands' => ',',
+ *		'decimals' => '.', 'negative' => '()', 'escape' => true
+ *	)
+ * }}}
+ *
+ * @param string $formatName The format name to be used in the future.
+ * @param array $options The array of options for this format.
+ * @return void
+ * @see NumberHelper::currency()
+ * @access public
+ */
+	function addFormat($formatName, $options) {
+		$this->_currencies[$formatName] = $options + $this->_currencyDefaults;
+	}
+
+}

Added: trunk/src/Web/cake/libs/view/helpers/paginator.php
===================================================================
--- trunk/src/Web/cake/libs/view/helpers/paginator.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/helpers/paginator.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,814 @@
+<?php
+/**
+ * Pagination Helper class file.
+ *
+ * Generates pagination links
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @since         CakePHP(tm) v 1.2.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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
+ * @link http://book.cakephp.org/view/1458/Paginator
+ */
+class PaginatorHelper extends AppHelper {
+
+/**
+ * Helper dependencies
+ *
+ * @var array
+ */
+	var $helpers = array('Html');
+
+/**
+ * Holds the default model for paged recordsets
+ *
+ * @var string
+ */
+	var $__defaultModel = null;
+
+/**
+ * The class used for 'Ajax' pagination links.
+ *
+ * @var string
+ */
+	var $_ajaxHelperClass = 'Js';
+
+/**
+ * Holds the default options for pagination links
+ *
+ * The values that may be specified are:
+ *
+ *  - `$options['format']` 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% .
+ *  - `$options['separator']` The separator of the actual page and number of pages (default: ' of ').
+ *  - `$options['url']` Url of the action. See Router::url()
+ *  - `$options['url']['sort']`  the key that the recordset is sorted.
+ *  - `$options['url']['direction']` Direction of the sorting (default: 'asc').
+ *  - `$options['url']['page']` Page # to display.
+ *  - `$options['model']` The name of the model.
+ *  - `$options['escape']` Defines if the title field for the link should be escaped (default: true).
+ *  - `$options['update']` 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.
+ *  - `$options['indicator']` DOM id of the element that will be shown when doing AJAX requests. **Only supported by
+ *     AjaxHelper**
+ *
+ * @var array
+ * @access public
+ */
+	var $options = array();
+
+/**
+ * Constructor for the helper. Sets up the helper that is used for creating 'AJAX' links.
+ *
+ * Use `var $helpers = array('Paginator' => array('ajax' => 'CustomHelper'));` to set a custom Helper
+ * or choose a non JsHelper Helper.  If you want to use a specific library with JsHelper declare JsHelper and its
+ * adapter before including PaginatorHelper in your helpers array.
+ *
+ * The chosen custom helper must implement a `link()` method.
+ *
+ * @return void
+ */
+	function __construct($config = array()) {
+		parent::__construct($config);
+		$ajaxProvider = isset($config['ajax']) ? $config['ajax'] : 'Js';
+		$this->helpers[] = $ajaxProvider;
+		$this->_ajaxHelperClass = $ajaxProvider;
+
+		App::import('Helper', $ajaxProvider);
+		$classname = $ajaxProvider . 'Helper';
+		if (!is_callable(array($classname, 'link'))) {
+			trigger_error(sprintf(__('%s does not implement a link() method, it is incompatible with PaginatorHelper', true), $classname), E_USER_WARNING);
+		}
+	}
+
+/**
+ * Before render callback. Overridden to merge passed args with url options.
+ *
+ * @return void
+ * @access public
+ */
+	function beforeRender() {
+		$this->options['url'] = array_merge($this->params['pass'], $this->params['named']);
+
+		parent::beforeRender();
+	}
+
+/**
+ * Gets the current paging parameters from the resultset for the given model
+ *
+ * @param string $model Optional model name.  Uses the default if none is specified.
+ * @return array The array of paging parameters for the paginated resultset.
+ * @access public
+ */
+	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 PaginatorHelper::$options for list of keys.
+ * @return void
+ * @access public
+ */
+	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.
+ * @access public
+ */
+	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.
+ * @access public
+ */
+	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'])) {
+			return $options['sort'];
+		} elseif (isset($options['order']) && is_array($options['order'])) {
+			return key($options['order']);
+		} elseif (isset($options['order']) && is_string($options['order'])) {
+			return $options['order'];
+		}
+		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.
+ * @access public
+ */
+	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
+ *
+ * ### Options:
+ *
+ * - `tag` The tag wrapping tag you want to use, defaults to 'span'
+ * - `escape` Whether you want the contents html entity encoded, defaults to true
+ * - `model` The model to use, defaults to PaginatorHelper::defaultModel()
+ *
+ * @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.
+ * @access public
+ */
+	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
+ *
+ * ### Options:
+ *
+ * - `tag` The tag wrapping tag you want to use, defaults to 'span'
+ * - `escape` Whether you want the contents html entity encoded, defaults to true
+ * - `model` The model to use, defaults to PaginatorHelper::defaultModel()
+ *
+ * @param string $title Title for the link. Defaults to 'Next >>'.
+ * @param mixed $options Options for pagination link. See above for list of keys.
+ * @param string $disabledTitle Title when the link is disabled.
+ * @param mixed $disabledOptions Options for the disabled pagination link. See above for list of keys.
+ * @return string A "next" link or or $disabledTitle text if the link is disabled.
+ * @access public
+ */
+	function next($title = 'Next >>', $options = array(), $disabledTitle = null, $disabledOptions = array()) {
+		return $this->__pagingLink('Next', $title, $options, $disabledTitle, $disabledOptions);
+	}
+
+/**
+ * Generates a sorting link. Sets named parameters for the sort and direction.  Handles
+ * direction switching automatically.
+ *
+ * ### Options:
+ *
+ * - `escape` Whether you want the contents html entity encoded, defaults to true
+ * - `model` The model to use, defaults to PaginatorHelper::defaultModel()
+ *
+ * @param string $title Title for the link.
+ * @param string $key The name of the key that the recordset should be sorted.  If $key is null
+ *   $title will be used for the key, and a title will be generated by inflection.
+ * @param array $options Options for sorting link. See above 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'.
+ * @access public
+ */
+	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 = isset($options['direction']) ? $options['direction'] : 'asc';
+		unset($options['direction']);
+
+		$sortKey = $this->sortKey($options['model']);
+		$defaultModel = $this->defaultModel();
+		$isSorted = (
+			$sortKey === $key || 
+			$sortKey === $defaultModel . '.' . $key ||
+			$key === $defaultModel . '.' . $sortKey
+		);
+
+		if ($isSorted) {
+			$dir = $this->sortDir($options['model']) === 'asc' ? 'desc' : 'asc';
+			$class = $dir === 'asc' ? 'desc' : 'asc';
+			if (!empty($options['class'])) {
+				$options['class'] .= ' ' . $class;
+			} else {
+				$options['class'] = $class;
+			}
+		}
+		if (is_array($title) && array_key_exists($dir, $title)) {
+			$title = $title[$dir];
+		}
+
+		$url = array_merge(array('sort' => $key, 'direction' => $dir), $url, array('order' => null));
+		return $this->link($title, $url, $options);
+	}
+
+/**
+ * Generates a plain or Ajax link with pagination parameters
+ *
+ * ### Options
+ *
+ * - `update` The Id of the DOM element you wish to update.  Creates Ajax enabled links
+ *    with the AjaxHelper.
+ * - `escape` Whether you want the contents html entity encoded, defaults to true
+ * - `model` The model to use, defaults to PaginatorHelper::defaultModel()
+ *
+ * @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.
+ * @access public
+ */
+	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']) ? $this->_ajaxHelperClass : 'Html';
+		$url = array_merge(array('page' => $this->current($model)), $url);
+		$url = array_merge(Set::filter($url, true), array_intersect_key($url, array('plugin' => true)));
+		return $this->{$obj}->link($title, $url, $options);
+	}
+
+/**
+ * Merges passed URL options with current pagination state to generate a pagination URL.
+ *
+ * @param array $options Pagination/URL options array
+ * @param boolean $asArray Return the url as an array, or a URI string
+ * @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)
+ * @access public
+ */
+	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']));
+			}
+			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
+ *
+ * @access protected
+ */
+	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' => 'span', 'class' => strtolower($which)
+		);
+		$options = array_merge($_defaults, (array)$options);
+		$paging = $this->params($options['model']);
+		if (empty($disabledOptions)) {
+			$disabledOptions = $options;
+		}
+
+		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->Html->tag($tag, $this->link($title, $url, array_merge($options, compact('escape', 'class'))));
+		} else {
+			return $this->Html->tag($tag, $title, array_merge($options, compact('escape', 'class')));
+		}
+	}
+
+/**
+ * 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.
+ * @access public
+ */
+	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.
+ * @access public
+ */
+	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.
+ * @access public
+ */
+	function hasPage($model = null, $page = 1) {
+		if (is_numeric($model)) {
+			$page = $model;
+			$model = null;
+		}
+		$paging = $this->params($model);
+		return $page <= $paging['pageCount'];
+	}
+
+/**
+ * Does $model have $page in its range?
+ *
+ * @param string $model Model name to get parameters for.
+ * @param integer $page Page number you are checking.
+ * @return boolean Whether model has $page
+ * @access protected
+ */
+	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.
+ * @access public
+ */
+	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
+ *
+ * ### Options
+ *
+ * - `model` The model to use, defaults to PaginatorHelper::defaultModel();
+ * - `format` The format string you want to use, defaults to 'pages' Which generates output like '1 of 5'
+ *    set to 'range' to generate output like '1 - 3 of 13'.  Can also be set to a custom string, containing
+ *    the following placeholders `%page%`, `%pages%`, `%current%`, `%count%`, `%start%`, `%end%` and any
+ *    custom content you would like.
+ * - `separator` The separator string to use, default to ' of '
+ *
+ * @param mixed $options Options for the counter string. See #options for list of keys.
+ * @return string Counter string.
+ * @access public
+ */
+	function counter($options = array()) {
+		if (is_string($options)) {
+			$options = array('format' => $options);
+		}
+
+		$options = array_merge(
+			array(
+				'model' => $this->defaultModel(),
+				'format' => 'pages',
+				'separator' => __(' of ', true)
+			),
+		$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];
+				$out .= $paging['count'];
+			break;
+			case 'pages':
+				$out = $paging['page'] . $options['separator'] . $paging['pageCount'];
+			break;
+			default:
+				$map = array(
+					'%page%' => $paging['page'],
+					'%pages%' => $paging['pageCount'],
+					'%current%' => $paging['current'],
+					'%count%' => $paging['count'],
+					'%start%' => $start,
+					'%end%' => $end
+				);
+				$out = str_replace(array_keys($map), array_values($map), $options['format']);
+
+				$newKeys = array(
+					'{:page}', '{:pages}', '{:current}', '{:count}', '{:start}', '{:end}'
+				);
+				$out = str_replace($newKeys, array_values($map), $out);
+			break;
+		}
+		return $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)
+ *
+ * ### Options
+ *
+ * - `before` Content to be inserted before the numbers
+ * - `after` Content to be inserted after the numbers
+ * - `model` Model to create numbers for, defaults to PaginatorHelper::defaultModel()
+ * - `modulus` how many numbers to include on either side of the current page, defaults to 8.
+ * - `separator` Separator content defaults to ' | '
+ * - `tag` The tag to wrap links in, defaults to 'span'
+ * - `first` Whether you want first links generated, set to an integer to define the number of 'first'
+ *    links to generate
+ * - `last` Whether you want last links generated, set to an integer to define the number of 'last'
+ *    links to generate
+ *
+ * @param mixed $options Options for the numbers, (before, after, model, modulus, separator)
+ * @return string numbers string.
+ * @access public
+ */
+	function numbers($options = array()) {
+		if ($options === true) {
+			$options = array(
+				'before' => ' | ', 'after' => ' | ', 'first' => 'first', 'last' => 'last'
+			);
+		}
+
+		$defaults = array(
+			'tag' => 'span', 'before' => null, 'after' => null, 'model' => $this->defaultModel(),
+			'modulus' => '8', 'separator' => ' | ', 'first' => null, 'last' => null,
+		);
+		$options += $defaults;
+
+		$params = (array)$this->params($options['model']) + array('page'=> 1);
+		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 > 1) {
+				$offset = ($start <= (int)$first) ? $start - 1 : $first;
+				if ($offset < $start - 1) {
+					$out .= $this->first($offset, array('tag' => $tag, 'separator' => $separator));
+				} else {
+					$out .= $this->first($offset, array('tag' => $tag, 'after' => $separator, 'separator' => $separator));
+				}
+			}
+
+			$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']) {
+				$offset = ($params['pageCount'] < $end + (int)$last) ? $params['pageCount'] - $end : $last;
+				if ($offset <= $last && $params['pageCount'] - $end > $offset) {
+					$out .= $this->last($offset, array('tag' => $tag, 'separator' => $separator));
+				} else {
+					$out .= $this->last($offset, array('tag' => $tag, 'before' => $separator, 'separator' => $separator));
+				}
+			}
+
+		} 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 $out;
+	}
+
+/**
+ * Returns a first or set of numbers for the first pages
+ *
+ * ### Options:
+ *
+ * - `tag` The tag wrapping tag you want to use, defaults to 'span'
+ * - `after` Content to insert after the link/tag
+ * - `model` The model to use defaults to PaginatorHelper::defaultModel()
+ * - `separator` Content between the generated links, defaults to ' | '
+ *
+ * @param mixed $first if string use as label for the link, if numeric print page numbers
+ * @param mixed $options
+ * @return string numbers string.
+ * @access public
+ */
+	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
+ *
+ * ### Options:
+ *
+ * - `tag` The tag wrapping tag you want to use, defaults to 'span'
+ * - `before` Content to insert before the link/tag
+ * - `model` The model to use defaults to PaginatorHelper::defaultModel()
+ * - `separator` Content between the generated links, defaults to ' | '
+ *
+ * @param mixed $last if string use as label for the link, if numeric print page numbers
+ * @param mixed $options Array of options
+ * @return string numbers string.
+ * @access public
+ */
+	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;
+	}
+}

Added: trunk/src/Web/cake/libs/view/helpers/prototype_engine.php
===================================================================
--- trunk/src/Web/cake/libs/view/helpers/prototype_engine.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/helpers/prototype_engine.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,370 @@
+<?php
+/**
+ * Prototype Engine Helper for JsHelper
+ *
+ * Provides Prototype specific Javascript for JsHelper. Requires at least
+ * Prototype 1.6
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.libs.view.helpers
+ * @since         CakePHP(tm) v 1.3
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Helper', 'Js');
+
+class PrototypeEngineHelper extends JsBaseEngineHelper {
+/**
+ * Is the current selection a multiple selection? or is it just a single element.
+ *
+ * @var boolean
+ */
+	var $_multiple = false;
+
+/**
+ * Option mappings for Prototype
+ *
+ * @var array
+ */
+	var $_optionMap = array(
+		'request' => array(
+			'async' => 'asynchronous',
+			'data' => 'parameters',
+			'before' => 'onCreate',
+			'success' => 'onSuccess',
+			'complete' => 'onComplete',
+			'error' => 'onFailure'
+		),
+		'sortable' => array(
+			'sort' => 'onChange',
+			'complete' => 'onUpdate',
+		),
+		'drag' => array(
+			'snapGrid' => 'snap',
+			'container' => 'constraint',
+			'stop' => 'onEnd',
+			'start' => 'onStart',
+			'drag' => 'onDrag',
+		),
+		'drop' => array(
+			'hover' => 'onHover',
+			'drop' => 'onDrop',
+			'hoverClass' => 'hoverclass',
+		),
+		'slider' => array(
+			'direction' => 'axis',
+			'change' => 'onSlide',
+			'complete' => 'onChange',
+			'value' => 'sliderValue',
+		)
+	);
+
+/**
+ * Contains a list of callback names -> default arguments.
+ *
+ * @var array
+ */
+	var $_callbackArguments = array(
+		'slider' => array(
+			'onSlide' => 'value',
+			'onChange' => 'value',
+		),
+		'drag' => array(
+			'onStart' => 'event',
+			'onDrag' => 'event',
+			'change' => 'draggable',
+			'onEnd' => 'event',
+		),
+		'drop' => array(
+			'onHover' => 'draggable, droppable, event',
+			'onDrop' => 'draggable, droppable, event',
+		),
+		'request' => array(
+			'onCreate' => 'transport',
+			'onComplete' => 'transport',
+			'onFailure' => 'response, jsonHeader',
+			'onRequest' => 'transport',
+			'onSuccess' => 'response, jsonHeader'
+		),
+		'sortable' => array(
+			'onStart' => 'element',
+			'onChange' => 'element',
+			'onUpdate' => 'element',
+		),
+	);
+
+/**
+ * Create javascript selector for a CSS rule
+ *
+ * @param string $selector The selector that is targeted
+ * @return object instance of $this. Allows chained methods.
+ */
+	function get($selector) {
+		$this->_multiple = false;
+		if ($selector == 'window' || $selector == 'document') {
+			$this->selection = "$(" . $selector .")";
+			return $this;
+		}
+		if (preg_match('/^#[^\s.]+$/', $selector)) {
+			$this->selection = '$("' . substr($selector, 1) . '")';
+			return $this;
+		}
+		$this->_multiple = true;
+		$this->selection = '$$("' . $selector . '")';
+		return $this;
+	}
+
+/**
+ * Add an event to the script cache. Operates on the currently selected elements.
+ *
+ * ### Options
+ *
+ * - `wrap` - Whether you want the callback wrapped in an anonymous function. (defaults true)
+ * - `stop` - Whether you want the event to stopped. (defaults true)
+ *
+ * @param string $type Type of event to bind to the current 946 id
+ * @param string $callback The Javascript function you wish to trigger or the function literal
+ * @param array $options Options for the event.
+ * @return string completed event handler
+ */
+	function event($type, $callback, $options = array()) {
+		$defaults = array('wrap' => true, 'stop' => true);
+		$options = array_merge($defaults, $options);
+
+		$function = 'function (event) {%s}';
+		if ($options['wrap'] && $options['stop']) {
+			$callback = "event.stop();\n" . $callback;
+		}
+		if ($options['wrap']) {
+			$callback = sprintf($function, $callback);
+		}
+		$out = $this->selection . ".observe(\"{$type}\", $callback);";
+		return $out;
+	}
+
+/**
+ * Create a domReady event. This is a special event in many libraries
+ *
+ * @param string $functionBody The code to run on domReady
+ * @return string completed domReady method
+ * @access public
+ */
+	function domReady($functionBody) {
+		$this->selection = 'document';
+		return $this->event('dom:loaded', $functionBody, array('stop' => false));
+	}
+
+/**
+ * Create an iteration over the current selection result.
+ *
+ * @param string $method The method you want to apply to the selection
+ * @param string $callback The function body you wish to apply during the iteration.
+ * @return string completed iteration
+ * @access public
+ */
+	function each($callback) {
+		return $this->selection . '.each(function (item, index) {' . $callback . '});';
+	}
+
+/**
+ * Trigger an Effect.
+ *
+ * ### Note: Effects require Scriptaculous to be loaded.
+ *
+ * @param string $name The name of the effect to trigger.
+ * @param array $options Array of options for the effect.
+ * @return string completed string with effect.
+ * @access public
+ * @see JsBaseEngineHelper::effect()
+ */
+	function effect($name, $options = array()) {
+		$effect = '';
+		$optionString = null;
+		if (isset($options['speed'])) {
+			if ($options['speed'] == 'fast') {
+				$options['duration'] = 0.5;
+			} elseif ($options['speed'] == 'slow') {
+				$options['duration'] = 2;
+			} else {
+				$options['duration'] = 1;
+			}
+			unset($options['speed']);
+		}
+		if (!empty($options)) {
+			$optionString = ', {' . $this->_parseOptions($options) . '}';
+		}
+		switch ($name) {
+			case 'hide':
+			case 'show':
+				$effect = $this->selection . '.' . $name . '();';
+			break;
+			case 'slideIn':
+			case 'slideOut':
+				$name = ($name == 'slideIn') ? 'slideDown' : 'slideUp';
+				$effect = 'Effect.' . $name . '(' . $this->selection . $optionString . ');';
+			break;
+			case 'fadeIn':
+			case 'fadeOut':
+				$name = ($name == 'fadeIn') ? 'appear' : 'fade';
+				$effect = $this->selection . '.' . $name .'(' . substr($optionString, 2) . ');';
+			break;
+		}
+		return $effect;
+	}
+
+/**
+ * Create an Ajax or Ajax.Updater call.
+ *
+ * @param mixed $url
+ * @param array $options
+ * @return string The completed ajax call.
+ * @access public
+ */
+	function request($url, $options = array()) {
+		$url = '"'. $this->url($url) . '"';
+		$options = $this->_mapOptions('request', $options);
+		$type = '.Request';
+		$data = null;
+		if (isset($options['type']) && strtolower($options['type']) == 'json') {
+			unset($options['type']);
+		}
+		if (isset($options['update'])) {
+			$url = '"' . str_replace('#', '', $options['update']) . '", ' . $url;
+			$type = '.Updater';
+			unset($options['update'], $options['type']);
+		}
+		$safe = array_keys($this->_callbackArguments['request']);
+		$options = $this->_prepareCallbacks('request', $options, $safe);
+		if (!empty($options['dataExpression'])) {
+			$safe[] = 'parameters';
+			unset($options['dataExpression']);
+		}
+		$options = $this->_parseOptions($options, $safe);
+		if (!empty($options)) {
+			$options = ', {' . $options . '}';
+		}
+		return "var jsRequest = new Ajax$type($url$options);";
+	}
+
+/**
+ * Create a sortable element.
+ *
+ * #### Note: Requires scriptaculous to be loaded.
+ *
+ * The scriptaculous implementation of sortables does not suppot the 'start'
+ * and 'distance' options.
+ *
+ * @param array $options Array of options for the sortable.
+ * @return string Completed sortable script.
+ * @access public
+ * @see JsBaseEngineHelper::sortable() for options list.
+ */
+	function sortable($options = array()) {
+		$options = $this->_processOptions('sortable', $options);
+		if (!empty($options)) {
+			$options = ', {' . $options . '}';
+		}
+		return 'var jsSortable = Sortable.create(' . $this->selection . $options . ');';
+	}
+
+/**
+ * Create a Draggable element.
+ *
+ * #### Note: Requires scriptaculous to be loaded.
+ *
+ * @param array $options Array of options for the draggable.
+ * @return string Completed draggable script.
+ * @access public
+ * @see JsBaseEngineHelper::draggable() for options list.
+ */
+	function drag($options = array()) {
+		$options = $this->_processOptions('drag', $options);
+		if (!empty($options)) {
+			$options = ', {' . $options . '}';
+		}
+		if ($this->_multiple) {
+			return $this->each('new Draggable(item' . $options . ');');
+		}
+		return 'var jsDrag = new Draggable(' . $this->selection . $options . ');';
+	}
+
+/**
+ * Create a Droppable element.
+ *
+ * #### Note: Requires scriptaculous to be loaded.
+ *
+ * @param array $options Array of options for the droppable.
+ * @return string Completed droppable script.
+ * @access public
+ * @see JsBaseEngineHelper::droppable() for options list.
+ */
+	function drop($options = array()) {
+		$options = $this->_processOptions('drop', $options);
+		if (!empty($options)) {
+			$options = ', {' . $options . '}';
+		}
+		return 'Droppables.add(' . $this->selection . $options . ');';
+	}
+
+/**
+ * Creates a slider control widget.
+ *
+ * ### Note: Requires scriptaculous to be loaded.
+ *
+ * @param array $options Array of options for the slider.
+ * @return string Completed slider script.
+ * @access public
+ * @see JsBaseEngineHelper::slider() for options list.
+ */
+	function slider($options = array()) {
+		$slider = $this->selection;
+		$this->get($options['handle']);
+		unset($options['handle']);
+
+		if (isset($options['min']) && isset($options['max'])) {
+			$options['range'] = sprintf('$R(%s,%s)', $options['min'], $options['max']);
+			unset($options['min'], $options['max']);
+		}
+		$options = $this->_mapOptions('slider', $options);
+		$options = $this->_prepareCallbacks('slider', $options);
+		$optionString = $this->_parseOptions(
+			$options, array_merge(array_keys($this->_callbackArguments['slider']), array('range'))
+		);
+		if (!empty($optionString)) {
+			$optionString = ', {' . $optionString . '}';
+		}
+		$out = 'var jsSlider = new Control.Slider(' . $this->selection . ', ' . $slider . $optionString . ');';
+		$this->selection = $slider;
+		return $out;
+	}
+
+/**
+ * Serialize the form attached to $selector.
+ *
+ * @param array $options Array of options.
+ * @return string Completed serializeForm() snippet
+ * @access public
+ * @see JsBaseEngineHelper::serializeForm()
+ */
+	function serializeForm($options = array()) {
+		$options = array_merge(array('isForm' => false, 'inline' => false), $options);
+		$selection = $this->selection;
+		if (!$options['isForm']) {
+			$selection = '$(' . $this->selection . '.form)';
+		}
+		$method = '.serialize()';
+		if (!$options['inline']) {
+			$method .= ';';
+		}
+		return $selection . $method;
+	}
+}

Added: trunk/src/Web/cake/libs/view/helpers/rss.php
===================================================================
--- trunk/src/Web/cake/libs/view/helpers/rss.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/helpers/rss.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,292 @@
+<?php
+/**
+ * RSS Helper class file.
+ *
+ * Simplifies the output of RSS feeds.
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @since         CakePHP(tm) v 1.2
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+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
+ * @link http://book.cakephp.org/view/1460/RSS
+ */
+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
+ * @access public
+ */
+	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 />`
+ * @access public
+ */
+	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
+ * @access public
+ */
+	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
+ * @access public
+ */
+	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();
+			
+			$escape = true;
+			if (is_array($val) && isset($val['convertEntities'])) {
+				$escape = $val['convertEntities'];
+				unset($val['convertEntities']);
+			}
+			
+			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] = implode('', $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;
+			}
+			if (!is_null($val) && $escape) {
+				$val = h($val);
+			}
+			$elements[$key] = $this->elem($key, $attrib, $val);
+		}
+		if (!empty($elements)) {
+			$content = implode('', $elements);
+		}
+		return $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);
+	}
+}

Added: trunk/src/Web/cake/libs/view/helpers/session.php
===================================================================
--- trunk/src/Web/cake/libs/view/helpers/session.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/helpers/session.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,201 @@
+<?php
+/**
+ * Session Helper provides access to the Session in the Views.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @since         CakePHP(tm) v 1.1.7.3328
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+if (!class_exists('cakesession')) {
+	require LIBS . 'cake_session.php';
+}
+/**
+ * Session Helper.
+ *
+ * Session reading from the view.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @link http://book.cakephp.org/view/1465/Session
+ */
+class SessionHelper extends CakeSession {
+
+/**
+ * List of helpers used by this helper
+ *
+ * @var array
+ */
+	var $helpers = array();
+
+/**
+ * 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);
+			$this->start();
+			$this->__active = true;
+		} else {
+			$this->__active = false;
+		}
+	}
+
+/**
+ * Turn sessions on if 'Session.start' is set to false in core.php
+ *
+ * @param string $base
+ * @access public
+ */
+	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
+ * @link http://book.cakephp.org/view/1466/Methods
+ */
+	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
+ * @link http://book.cakephp.org/view/1466/Methods
+ */
+	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
+ * @link http://book.cakephp.org/view/1466/Methods
+ */
+	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 boolean|string Will return the value if $key is set, or false if not set.
+ * @access public
+ * @link http://book.cakephp.org/view/1466/Methods
+ * @link http://book.cakephp.org/view/1467/flash
+ */
+	function flash($key = 'flash') {
+		$out = false;
+
+		if ($this->__active === true && $this->__start()) {
+			if (parent::check('Message.' . $key)) {
+				$flash = parent::read('Message.' . $key);
+
+				if ($flash['element'] == '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['element'] == '' || $flash['element'] == null) {
+					$out = $flash['message'];
+				} else {
+					$view =& ClassRegistry::getObject('view');
+					$tmpVars = $flash['params'];
+					$tmpVars['message'] = $flash['message'];
+					$out = $view->element($flash['element'], $tmpVars);
+				}
+				parent::delete('Message.' . $key);
+			}
+		}
+		return $out;
+	}
+
+/**
+ * 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);
+	}
+
+/**
+ * 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 private
+ */
+	function __start() {
+		if (!$this->started()) {
+			return $this->start();
+		}
+		return true;
+	}
+}

Added: trunk/src/Web/cake/libs/view/helpers/text.php
===================================================================
--- trunk/src/Web/cake/libs/view/helpers/text.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/helpers/text.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,335 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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
+ * @link http://book.cakephp.org/view/1469/Text
+ */
+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.
+ *
+ * ### Options:
+ *
+ * - `format` The piece of html with that the phrase will be highlighted
+ * - `html` If true, will ignore any HTML tags, ensuring that only the correct text is highlighted
+ *
+ * @param string $text Text to search the phrase in
+ * @param string $phrase The phrase that will be searched
+ * @param array $options An array of html attributes and options.
+ * @return string The highlighted text
+ * @access public
+ * @link http://book.cakephp.org/view/1469/Text#highlight-1622
+ */
+	function highlight($text, $phrase, $options = array()) {
+		if (empty($phrase)) {
+			return $text;
+		}
+
+		$default = array(
+			'format' => '<span class="highlight">\1</span>',
+			'html' => false
+		);
+		$options = array_merge($default, $options);
+		extract($options);
+
+		if (is_array($phrase)) {
+			$replace = array();
+			$with = array();
+
+			foreach ($phrase as $key => $segment) {
+				$segment = '(' . preg_quote($segment, '|') . ')';
+				if ($html) {
+					$segment = "(?![^<]+>)$segment(?![^<]+>)";
+				}
+
+				$with[] = (is_array($format)) ? $format[$key] : $format;
+				$replace[] = "|$segment|iu";
+			}
+
+			return preg_replace($replace, $with, $text);
+		} else {
+			$phrase = '(' . preg_quote($phrase, '|') . ')';
+			if ($html) {
+				$phrase = "(?![^<]+>)$phrase(?![^<]+>)";
+			}
+
+			return preg_replace("|$phrase|iu", $format, $text);
+		}
+	}
+
+/**
+ * Strips given text of all links (<a href=....)
+ *
+ * @param string $text Text
+ * @return string The text without links
+ * @access public
+ * @link http://book.cakephp.org/view/1469/Text#stripLinks-1623
+ */
+	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 $options Array of HTML options.
+ * @return string The text with links
+ * @access public
+ * @link http://book.cakephp.org/view/1469/Text#autoLinkUrls-1619
+ */
+	function autoLinkUrls($text, $htmlOptions = array()) {
+		$options = var_export($htmlOptions, true);
+		$text = preg_replace_callback('#(?<!href="|">)((?:https?|ftp|nntp)://[^\s<>()]+)#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://" . $matches[0],' . $options . ');'), $text);
+	}
+
+/**
+ * Adds email links (<a href="mailto:....) to a given text.
+ *
+ * @param string $text Text
+ * @param array $options Array of HTML options.
+ * @return string The text with links
+ * @access public
+ * @link http://book.cakephp.org/view/1469/Text#autoLinkEmails-1618
+ */
+	function autoLinkEmails($text, $options = array()) {
+		$linkOptions = 'array(';
+		foreach ($options as $option => $value) {
+			$value = var_export($value, true);
+			$linkOptions .= "'$option' => $value, ";
+		}
+		$linkOptions .= ')';
+		$atom = '[a-z0-9!#$%&\'*+\/=?^_`{|}~-]';
+
+		return preg_replace_callback(
+			'/(' . $atom . '+(?:\.' . $atom . '+)*@[a-z0-9-]+(?:\.[a-z0-9-]+)+)/i',
+			create_function('$matches', '$Html = new HtmlHelper(); $Html->tags = $Html->loadConfig(); return $Html->link($matches[0], "mailto:" . $matches[0],' . $linkOptions . ');'), $text);
+	}
+
+/**
+ * Convert all links and email adresses to HTML links.
+ *
+ * @param string $text Text
+ * @param array $options Array of HTML options.
+ * @return string The text with links
+ * @access public
+ * @link http://book.cakephp.org/view/1469/Text#autoLink-1620
+ */
+	function autoLink($text, $options = array()) {
+		return $this->autoLinkEmails($this->autoLinkUrls($text, $options), $options);
+	}
+
+/**
+ * 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.
+ *
+ * ### Options:
+ *
+ * - `ending` Will be used as Ending and appended to the trimmed string
+ * - `exact` If false, $text will not be cut mid-word
+ * - `html` If true, HTML tags would be handled correctly
+ *
+ * @param string  $text String to truncate.
+ * @param integer $length Length of returned string, including ellipsis.
+ * @param array $options An array of html attributes and options.
+ * @return string Trimmed string.
+ * @access public
+ * @link http://book.cakephp.org/view/1469/Text#truncate-1625
+ */
+	function truncate($text, $length = 100, $options = array()) {
+		$default = array(
+			'ending' => '...', 'exact' => true, 'html' => false
+		);
+		$options = array_merge($default, $options);
+		extract($options);
+
+		if ($html) {
+			if (mb_strlen(preg_replace('/<.*?>/', '', $text)) <= $length) {
+				return $text;
+			}
+			$totalLength = mb_strlen(strip_tags($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 - mb_strlen($ending));
+			}
+		}
+		if (!$exact) {
+			$spacepos = mb_strrpos($truncate, ' ');
+			if (isset($spacepos)) {
+				if ($html) {
+					$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 ($html) {
+			foreach ($openTags as $tag) {
+				$truncate .= '</'.$tag.'>';
+			}
+		}
+
+		return $truncate;
+	}
+
+/**
+ * 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
+ * @link http://book.cakephp.org/view/1469/Text#excerpt-1621
+ */
+	function excerpt($text, $phrase, $radius = 100, $ending = '...') {
+		if (empty($text) or empty($phrase)) {
+			return $this->truncate($text, $radius * 2, array('ending' => $ending));
+		}
+
+		$append = $prepend = $ending;
+
+		$phraseLen = mb_strlen($phrase);
+		$textLen = mb_strlen($text);
+
+		$pos = mb_strpos(mb_strtolower($text), mb_strtolower($phrase));
+		if ($pos === false) {
+			return mb_substr($text, 0, $radius) . $ending;
+		}
+
+		$startPos = $pos - $radius;
+		if ($startPos <= 0) {
+			$startPos = 0;
+			$prepend = '';
+		}
+
+		$endPos = $pos + $phraseLen + $radius;
+		if ($endPos >= $textLen) {
+			$endPos = $textLen;
+			$append = '';
+		}
+
+		$excerpt = mb_substr($text, $startPos, $endPos - $startPos);
+		$excerpt = $prepend . $excerpt . $append;
+		
+		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
+ * @param string $and The word used to join the last and second last items together with. Defaults to 'and'
+ * @param string $separator The separator used to join all othe other items together. Defaults to ', '
+ * @return string The glued together string.
+ * @access public
+ * @link http://book.cakephp.org/view/1469/Text#toList-1624
+ */
+	function toList($list, $and = 'and', $separator = ', ') {
+		if (count($list) > 1) {
+			return implode($separator, array_slice($list, null, -1)) . ' ' . $and . ' ' . array_pop($list);
+		} else {
+			return array_pop($list);
+		}
+	}
+}

Added: trunk/src/Web/cake/libs/view/helpers/time.php
===================================================================
--- trunk/src/Web/cake/libs/view/helpers/time.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/helpers/time.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,784 @@
+<?php
+/**
+ * Time Helper class file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+if (!class_exists('Multibyte')) {
+	App::import('Core', 'Multibyte');
+}
+
+/**
+ * Time Helper class for easy use of time data.
+ *
+ * Manipulation of time data.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @link http://book.cakephp.org/view/1470/Time
+ */
+class TimeHelper extends AppHelper {
+
+/**
+ * Converts a string representing the format for the function strftime and returns a
+ * windows safe and i18n aware format.
+ *
+ * @param string $format Format with specifiers for strftime function.
+ *    Accepts the special specifier %S which mimics th modifier S for date()
+ * @param string UNIX timestamp
+ * @return string windows safe and date() function compatible format for strftime
+ * @access public
+ */
+	function convertSpecifiers($format, $time = null) {
+		if (!$time) {
+			$time = time();
+		}
+		$this->__time = $time;
+		return preg_replace_callback('/\%(\w+)/', array($this, '__translateSpecifier'), $format);
+	}
+
+/**
+ * Auxiliary function to translate a matched specifier element from a regular expresion into
+ * a windows safe and i18n aware specifier
+ *
+ * @param array $specifier match from regular expression
+ * @return string converted element
+ * @access private
+ */
+	function __translateSpecifier($specifier) {
+		switch ($specifier[1]) {
+			case 'a':
+				$abday = __c('abday', 5, true);
+				if (is_array($abday)) {
+					return $abday[date('w', $this->__time)];
+				}
+				break;
+			case 'A':
+				$day = __c('day',5,true);
+				if (is_array($day)) {
+					return $day[date('w', $this->__time)];
+				}
+				break;
+			case 'c':
+				$format = __c('d_t_fmt',5,true);
+				if ($format != 'd_t_fmt') {
+					return $this->convertSpecifiers($format, $this->__time);
+				}
+				break;
+			case 'C':
+				return sprintf("%02d", date('Y', $this->__time) / 100);
+			case 'D':
+				return '%m/%d/%y';
+			case 'e':
+				if (DS === '/') {
+					return '%e';
+				}
+				$day = date('j', $this->__time);
+				if ($day < 10) {
+					$day = ' ' . $day;
+				}
+				return $day;
+			case 'eS' :
+				return date('jS', $this->__time);
+			case 'b':
+			case 'h':
+				$months = __c('abmon', 5, true);
+				if (is_array($months)) {
+					return $months[date('n', $this->__time) -1];
+				}
+				return '%b';
+			case 'B':
+				$months = __c('mon',5,true);
+				if (is_array($months)) {
+					return $months[date('n', $this->__time) -1];
+				}
+				break;
+			case 'n':
+				return "\n";
+			case 'p':
+			case 'P':
+				$default = array('am' => 0, 'pm' => 1);
+				$meridiem = $default[date('a',$this->__time)];
+				$format = __c('am_pm', 5, true);
+				if (is_array($format)) {
+					$meridiem = $format[$meridiem];
+					return ($specifier[1] == 'P') ? strtolower($meridiem) : strtoupper($meridiem);
+				}
+				break;
+			case 'r':
+				$complete = __c('t_fmt_ampm', 5, true);
+				if ($complete != 't_fmt_ampm') {
+					return str_replace('%p',$this->__translateSpecifier(array('%p', 'p')),$complete);
+				}
+				break;
+			case 'R':
+				return date('H:i', $this->__time);
+			case 't':
+				return "\t";
+			case 'T':
+				return '%H:%M:%S';
+			case 'u':
+				return ($weekDay = date('w', $this->__time)) ? $weekDay : 7;
+			case 'x':
+				$format = __c('d_fmt', 5, true);
+				if ($format != 'd_fmt') {
+					return $this->convertSpecifiers($format, $this->__time);
+				}
+				break;
+			case 'X':
+				$format = __c('t_fmt',5,true);
+				if ($format != 't_fmt') {
+					return $this->convertSpecifiers($format, $this->__time);
+				}
+				break;
+		}
+		return $specifier[0];
+	}
+
+/**
+ * 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
+ * @access public
+ */
+	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
+ * @access public
+ */
+	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
+ * @access public
+ * @link http://book.cakephp.org/view/1471/Formatting
+ */
+	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);
+		}
+		if ($date === -1) {
+			return false;
+		}
+		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
+ * @access public
+ * @link http://book.cakephp.org/view/1471/Formatting
+ */
+	function nice($dateString = null, $userOffset = null) {
+		if ($dateString != null) {
+			$date = $this->fromString($dateString, $userOffset);
+		} else {
+			$date = time();
+		}
+		$format = $this->convertSpecifiers('%a, %b %eS %Y, %H:%M', $date);
+		return $this->_strftime($format, $date);
+	}
+
+/**
+ * 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
+ * @access public
+ * @link http://book.cakephp.org/view/1471/Formatting
+ */
+	function niceShort($dateString = null, $userOffset = null) {
+		$date = $dateString ? $this->fromString($dateString, $userOffset) : time();
+
+		$y = $this->isThisYear($date) ? '' : ' %Y';
+
+		if ($this->isToday($dateString, $userOffset)) {
+			$ret = sprintf(__('Today, %s',true), $this->_strftime("%H:%M", $date));
+		} elseif ($this->wasYesterday($dateString, $userOffset)) {
+			$ret = sprintf(__('Yesterday, %s',true), $this->_strftime("%H:%M", $date));
+		} else {
+			$format = $this->convertSpecifiers("%b %eS{$y}, %H:%M", $date);
+			$ret = $this->_strftime($format, $date);
+		}
+
+		return $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.
+ * @access public
+ * @link http://book.cakephp.org/view/1471/Formatting
+ */
+	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';
+
+		return "($fieldName >= '$begin') AND ($fieldName <= '$end')";
+	}
+
+/**
+ * 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.
+ * @access public
+ * @link http://book.cakephp.org/view/1471/Formatting
+ */
+	function dayAsSql($dateString, $fieldName, $userOffset = null) {
+		$date = $this->fromString($dateString, $userOffset);
+		return $this->daysAsSql($dateString, $dateString, $fieldName);
+	}
+
+/**
+ * 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
+ * @access public
+ */
+	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
+ * @access public
+ * @link http://book.cakephp.org/view/1472/Testing-Time
+ */
+	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
+ * @access public
+ * @link http://book.cakephp.org/view/1472/Testing-Time
+ */
+	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
+ * @access public
+ * @link http://book.cakephp.org/view/1472/Testing-Time
+ */
+	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
+ * @access public
+ * @link http://book.cakephp.org/view/1472/Testing-Time
+ *
+ */
+	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
+ * @access public
+ * @link http://book.cakephp.org/view/1472/Testing-Time
+ */
+	function isTomorrow($dateString, $userOffset = null) {
+		$date = $this->fromString($dateString, $userOffset);
+		return date('Y-m-d', $date) == date('Y-m-d', strtotime('tomorrow'));
+	}
+
+/**
+ * Returns the quarter
+ *
+ * @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
+ * @access public
+ * @link http://book.cakephp.org/view/1471/Formatting
+ */
+	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 $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
+ * @access public
+ * @link http://book.cakephp.org/view/1471/Formatting
+ */
+	function toUnix($dateString, $userOffset = null) {
+		return $this->fromString($dateString, $userOffset);
+	}
+
+/**
+ * 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
+ * @access public
+ * @link http://book.cakephp.org/view/1471/Formatting
+ */
+	function toAtom($dateString, $userOffset = null) {
+		$date = $this->fromString($dateString, $userOffset);
+		return date('Y-m-d\TH:i:s\Z', $date);
+	}
+
+/**
+ * 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
+ * @access public
+ * @link http://book.cakephp.org/view/1471/Formatting
+ */
+	function toRSS($dateString, $userOffset = null) {
+		$date = $this->fromString($dateString, $userOffset);
+
+		if(!is_null($userOffset)) {
+			if($userOffset == 0) {
+				$timezone = '+0000';
+			} else {
+				$hours = (int) floor(abs($userOffset));
+				$minutes = (int) (fmod(abs($userOffset), $hours) * 60);
+				$timezone = ($userOffset < 0 ? '-' : '+') . str_pad($hours, 2, '0', STR_PAD_LEFT) . str_pad($minutes, 2, '0', STR_PAD_LEFT);
+			}
+			return date('D, d M Y H:i:s', $date) . ' ' . $timezone;
+		}
+		return date("r", $date);
+	}
+
+/**
+ * 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
+ *
+ * Default date formatting is d/m/yy e.g: on 18/2/09
+ *
+ * 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.
+ * @access public
+ * @link http://book.cakephp.org/view/1471/Formatting
+ */
+	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 ? ', ' : '') . sprintf(__n('%d year', '%d years', $years, true), $years);
+				$relativeDate .= $months > 0 ? ($relativeDate ? ', ' : '') . sprintf(__n('%d month', '%d months', $months, true), $months) : '';
+				$relativeDate .= $weeks > 0 ? ($relativeDate ? ', ' : '') . sprintf(__n('%d week', '%d weeks', $weeks, true), $weeks) : '';
+				$relativeDate .= $days > 0 ? ($relativeDate ? ', ' : '') . sprintf(__n('%d day', '%d days', $days, true), $days) : '';
+			} elseif (abs($months) > 0) {
+				// months, weeks and days
+				$relativeDate .= ($relativeDate ? ', ' : '') . sprintf(__n('%d month', '%d months', $months, true), $months);
+				$relativeDate .= $weeks > 0 ? ($relativeDate ? ', ' : '') . sprintf(__n('%d week', '%d weeks', $weeks, true), $weeks) : '';
+				$relativeDate .= $days > 0 ? ($relativeDate ? ', ' : '') . sprintf(__n('%d day', '%d days', $days, true), $days) : '';
+			} elseif (abs($weeks) > 0) {
+				// weeks and days
+				$relativeDate .= ($relativeDate ? ', ' : '') . sprintf(__n('%d week', '%d weeks', $weeks, true), $weeks);
+				$relativeDate .= $days > 0 ? ($relativeDate ? ', ' : '') . sprintf(__n('%d day', '%d days', $days, true), $days) : '';
+			} elseif (abs($days) > 0) {
+				// days and hours
+				$relativeDate .= ($relativeDate ? ', ' : '') . sprintf(__n('%d day', '%d days', $days, true), $days);
+				$relativeDate .= $hours > 0 ? ($relativeDate ? ', ' : '') . sprintf(__n('%d hour', '%d hours', $hours, true), $hours) : '';
+			} elseif (abs($hours) > 0) {
+				// hours and minutes
+				$relativeDate .= ($relativeDate ? ', ' : '') . sprintf(__n('%d hour', '%d hours', $hours, true), $hours);
+				$relativeDate .= $minutes > 0 ? ($relativeDate ? ', ' : '') . sprintf(__n('%d minute', '%d minutes', $minutes, true), $minutes) : '';
+			} elseif (abs($minutes) > 0) {
+				// minutes only
+				$relativeDate .= ($relativeDate ? ', ' : '') . sprintf(__n('%d minute', '%d minutes', $minutes, true), $minutes);
+			} else {
+				// seconds only
+				$relativeDate .= ($relativeDate ? ', ' : '') . sprintf(__n('%d second', '%d seconds', $seconds, true), $seconds);
+			}
+
+			if (!$backwards) {
+				$relativeDate = sprintf(__('%s ago', true), $relativeDate);
+			}
+		}
+		return $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
+ * @access public
+ * @deprecated This method alias will be removed in future versions.
+ * @link http://book.cakephp.org/view/1471/Formatting
+ */
+	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
+ * @access public
+ * @link http://book.cakephp.org/view/1472/Testing-Time
+ */
+	function wasWithinLast($timeInterval, $dateString, $userOffset = null) {
+		$tmp = str_replace(' ', '', $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
+ * @access public
+ * @link http://book.cakephp.org/view/1471/Formatting
+ */
+	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);
+	}
+
+/**
+ * Returns a formatted date string, given either a UNIX timestamp or a valid strtotime() date string.
+ * This function also accepts a time string and a format string as first and second parameters.
+ * In that case this function behaves as a wrapper for TimeHelper::i18nFormat()
+ *
+ * @param string $format date format string (or a DateTime string)
+ * @param string $dateString Datetime string (or a date format 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
+ * @access public
+ */
+	function format($format, $date = null, $invalid = false, $userOffset = null) {
+		$time = $this->fromString($date, $userOffset);
+		$_time = $this->fromString($format, $userOffset);
+
+		if (is_numeric($_time) && $time === false) {
+			$format = $date;
+			return $this->i18nFormat($_time, $format, $invalid, $userOffset);
+		}
+		if ($time === false && $invalid !== false) {
+			return $invalid;
+		}
+		return date($format, $time);
+	}
+
+/**
+ * Returns a formatted date string, given either a UNIX timestamp or a valid strtotime() date string.
+ * It take in account the default date format for the current language if a LC_TIME file is used.
+ *
+ * @param string $dateString Datetime string
+ * @param string $format strftime format string.
+ * @param boolean $invalid flag to ignore results of fromString == false
+ * @param int $userOffset User's offset from GMT (in hours)
+ * @return string Formatted and translated date string @access public
+ * @access public
+ */
+	function i18nFormat($date, $format = null, $invalid = false, $userOffset = null) {
+		$date = $this->fromString($date, $userOffset);
+		if ($date === false && $invalid !== false) {
+			return $invalid;
+		}
+		if (empty($format)) {
+			$format = '%x';
+		}
+		$format = $this->convertSpecifiers($format, $date);
+		return $this->_strftime($format, $date);
+	}
+
+/**
+ * Multibyte wrapper for strftime.
+ *
+ * Handles utf8_encoding the result of strftime when necessary.
+ *
+ * @param string $format Format string.
+ * @param int $date Timestamp to format.
+ * @return string formatted string with correct encoding.
+ */
+	function _strftime($format, $date) {
+		$format = strftime($format, $date);
+		$encoding = Configure::read('App.encoding');
+
+		if (!empty($encoding) && $encoding === 'UTF-8') {
+			if (function_exists('mb_check_encoding')) {
+				$valid = mb_check_encoding($format, $encoding);
+			} else {
+				$valid = !Multibyte::checkMultibyte($format);
+			}
+			if (!$valid) {
+				$format = utf8_encode($format);
+			}
+		}
+		return $format;
+	}
+}

Added: trunk/src/Web/cake/libs/view/helpers/xml.php
===================================================================
--- trunk/src/Web/cake/libs/view/helpers/xml.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/helpers/xml.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,177 @@
+<?php
+/**
+ * XML Helper class file.
+ *
+ * Simplifies the output of XML documents.
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.helpers
+ * @since         CakePHP(tm) v 1.2
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+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
+ * @link http://book.cakephp.org/view/1473/XML
+ */
+class XmlHelper extends AppHelper {
+
+/**
+ * Default document encoding
+ *
+ * @access public
+ * @var string
+ */
+	var $encoding = 'UTF-8';
+
+	var $Xml;
+	var $XmlElement;
+/**
+ * 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
+ * @access public
+ * @link http://book.cakephp.org/view/1476/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->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()
+ * @access public
+ */
+	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
+ * @access public
+ * @link http://book.cakephp.org/view/1475/elem
+ */
+	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) && array_key_exists('value', $content)) {
+			$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->XmlElement =& $elem;
+		}
+		return $out;
+	}
+
+/**
+ * Create closing tag for current element
+ *
+ * @return string
+ * @access public
+ */
+	function closeElem() {
+		$elem = (empty($this->XmlElement)) ? $this->Xml : $this->XmlElement;
+		$name = $elem->name();
+		if ($parent =& $elem->parent()) {
+			$this->XmlElement =& $parent;
+		}
+		return '</' . $name . '>';
+	}
+
+/**
+ * Serializes a model resultset into XML
+ *
+ * @param mixed $data The content to be converted to XML
+ * @param array $options The data formatting options.  For a list of valid options, see
+ *     Xml::__construct().
+ * @return string A copy of $data in XML format
+ * @see Xml::__construct()
+ * @access public
+ * @link http://book.cakephp.org/view/1474/serialize
+ */
+	function serialize($data, $options = array()) {
+		$options += array('attributes' => false, 'format' => 'attributes');
+		$data =& new Xml($data, $options);
+		return $data->toString($options + array('header' => false));
+	}
+}

Added: trunk/src/Web/cake/libs/view/layouts/ajax.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/layouts/ajax.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/layouts/ajax.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,20 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.layouts
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<?php echo $content_for_layout; ?>
\ No newline at end of file

Added: trunk/src/Web/cake/libs/view/layouts/default.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/layouts/default.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/layouts/default.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,59 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.layouts
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<!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 $this->Html->charset(); ?>
+	<title>
+		<?php __('CakePHP: the rapid development php framework:'); ?>
+		<?php echo $title_for_layout; ?>
+	</title>
+	<?php
+		echo $this->Html->meta('icon');
+
+		echo $this->Html->css('cake.generic');
+
+		echo $scripts_for_layout;
+	?>
+</head>
+<body>
+	<div id="container">
+		<div id="header">
+			<h1><?php echo $this->Html->link(__('CakePHP: the rapid development php framework', true), 'http://cakephp.org'); ?></h1>
+		</div>
+		<div id="content">
+
+			<?php echo $this->Session->flash(); ?>
+
+			<?php echo $content_for_layout; ?>
+
+		</div>
+		<div id="footer">
+			<?php echo $this->Html->link(
+					$this->Html->image('cake.power.gif', array('alt'=> __('CakePHP: the rapid development php framework', true), 'border' => '0')),
+					'http://www.cakephp.org/',
+					array('target' => '_blank', 'escape' => false)
+				);
+			?>
+		</div>
+	</div>
+	<?php echo $this->element('sql_dump'); ?>
+</body>
+</html>
\ No newline at end of file

Added: trunk/src/Web/cake/libs/view/layouts/email/html/default.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/layouts/email/html/default.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/layouts/email/html/default.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,30 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.layouts.email.html
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<!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

Added: trunk/src/Web/cake/libs/view/layouts/email/text/default.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/layouts/email/text/default.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/layouts/email/text/default.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,22 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.layouts.email.text
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<?php echo $content_for_layout;?>
+
+This email was sent using the CakePHP Framework, http://cakephp.org.

Added: trunk/src/Web/cake/libs/view/layouts/flash.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/layouts/flash.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/layouts/flash.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,38 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.layouts
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<!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 $this->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

Added: trunk/src/Web/cake/libs/view/layouts/js/default.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/layouts/js/default.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/layouts/js/default.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -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

Added: trunk/src/Web/cake/libs/view/layouts/rss/default.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/layouts/rss/default.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/layouts/rss/default.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,16 @@
+<?php
+echo $this->Rss->header();
+
+if (!isset($channel)) {
+	$channel = array();
+}
+if (!isset($channel['title'])) {
+	$channel['title'] = $title_for_layout;
+}
+
+echo $this->Rss->document(
+	$this->Rss->channel(
+		array(), $channel, $content_for_layout
+	)
+);
+?>
\ No newline at end of file

Added: trunk/src/Web/cake/libs/view/layouts/xml/default.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/layouts/xml/default.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/layouts/xml/default.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,2 @@
+<?php echo $this->Xml->header(); ?>
+<?php echo $content_for_layout; ?>
\ No newline at end of file

Added: trunk/src/Web/cake/libs/view/media.php
===================================================================
--- trunk/src/Web/cake/libs/view/media.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/media.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,285 @@
+<?php
+/**
+ * Methods to display or download any type of file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view
+ * @since         CakePHP(tm) v 1.2.0.5714
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('View', 'View', false);
+
+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', 'eot' => 'application/vnd.ms-fontobject', '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', 'ico' => 'image/vnd.microsoft.icon', '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',
+		'otf' => 'font/otf', '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', 'ttf' => 'font/ttf',
+		'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');
+
+/**
+ * Holds headers sent to browser before rendering media
+ *
+ * @var array
+ * @access protected
+ */
+	var $_headers = array();
+
+/**
+ * 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 (!file_exists($path)) {
+			header('Content-Type: text/html');
+			$this->cakeError('error404');
+		}
+
+		if (is_null($name)) {
+			$name = $id;
+		}
+
+		if (is_array($mimeType)) {
+			$this->mimeType = array_merge($this->mimeType, $mimeType);
+		}
+
+		if (isset($extension) && isset($this->mimeType[strtolower($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) {
+				$contentTypes = array('application/octet-stream');
+				$agent = env('HTTP_USER_AGENT');
+
+				if (preg_match('%Opera(/| )([0-9].[0-9]{1,2})%', $agent)) {
+					$contentTypes[0] = 'application/octetstream';
+				} else if (preg_match('/MSIE ([0-9].[0-9]{1,2})/', $agent)) {
+					$contentTypes[0] = 'application/force-download';
+					array_merge($contentTypes, array(
+						'application/octet-stream',
+						'application/download'
+					));
+				}
+				foreach($contentTypes as $contentType) {
+					$this->_header('Content-Type: ' . $contentType);
+				}
+				$this->_header(array(
+					'Content-Disposition: attachment; filename="' . $name . '.' . $extension . '";',
+					'Expires: 0',
+					'Accept-Ranges: bytes',
+					'Cache-Control: private' => false,
+					'Pragma: private'));
+
+				$httpRange = env('HTTP_RANGE');
+				if (isset($httpRange)) {
+					list($toss, $range) = explode('=', $httpRange);
+
+					$size = $fileSize - 1;
+					$length = $fileSize - $range;
+
+					$this->_header(array(
+						'HTTP/1.1 206 Partial Content',
+						'Content-Length: ' . $length,
+						'Content-Range: bytes ' . $range . $size . '/' . $fileSize));
+
+					fseek($handle, $range);
+				} else {
+					$this->_header('Content-Length: ' . $fileSize);
+				}
+			} else {
+				$this->_header('Date: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
+				if ($cache) {
+					if (!is_numeric($cache)) {
+						$cache = strtotime($cache) - time();
+					}
+					$this->_header(array(
+						'Cache-Control: max-age=' . $cache,
+						'Expires: ' . gmdate('D, d M Y H:i:s', time() + $cache) . ' GMT',
+						'Pragma: cache'));
+				} else {
+					$this->_header(array(
+						'Cache-Control: must-revalidate, post-check=0, pre-check=0',
+						'Pragma: no-cache'));
+				}
+				$this->_header(array(
+					'Last-Modified: ' . $modified,
+					'Content-Type: ' . $this->mimeType[strtolower($extension)],
+					'Content-Length: ' . $fileSize));
+			}
+			$this->_output();
+			$this->_clearBuffer();
+
+			while (!feof($handle)) {
+				if (!$this->_isActive()) {
+					fclose($handle);
+					return false;
+				}
+				set_time_limit(0);
+				$buffer = fread($handle, $chunkSize);
+				echo $buffer;
+				$this->_flushBuffer();
+			}
+			fclose($handle);
+			return;
+		}
+		return false;
+	}
+
+/**
+ * Method to set headers
+ * @param mixed $header
+ * @param boolean $boolean
+ * @access protected
+ */
+	function _header($header, $boolean = true) {
+		if (is_array($header)) {
+			foreach ($header as $string => $boolean) {
+				if (is_numeric($string)) {
+					$this->_headers[] = array($boolean => true);
+				} else {
+					$this->_headers[] = array($string => $boolean);
+				}
+			}
+			return;
+		}
+		$this->_headers[] = array($header => $boolean);
+		return;
+	}
+
+/**
+ * Method to output headers
+ * @access protected
+ */
+	function _output() {
+		foreach ($this->_headers as $key => $value) {
+			$header = key($value);
+			header($header, $value[$header]);
+		}
+	}
+
+/**
+ * Returns true if connection is still active
+ * @return boolean
+ * @access protected
+ */
+	function _isActive() {
+		return connection_status() == 0 && !connection_aborted();
+	}
+
+/**
+ * Clears the contents of the topmost output buffer and discards them
+ * @return boolean
+ * @access protected
+ */
+	function _clearBuffer() {
+		return @ob_end_clean();
+	}
+
+/**
+ * Flushes the contents of the output buffer
+ * @access protected
+ */
+	function _flushBuffer() {
+		@flush();
+		@ob_flush();
+	}
+}

Added: trunk/src/Web/cake/libs/view/pages/home.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/pages/home.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/pages/home.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,188 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.pages
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+if (Configure::read() == 0):
+	$this->cakeError('error404');
+endif;
+?>
+<iframe src="http://cakephp.org/bake-banner" width="830" height="160" style="overflow:hidden; border:none;">
+	<p>For updates and important announcements, visit http://cakefest.org</p>
+</iframe>
+<h2><?php echo sprintf(__('Release Notes for CakePHP %s.', true), Configure::version()); ?></h2>
+<a href="http://cakephp.org/changelogs/1.3.14"><?php __('Read the changelog'); ?> </a>
+<?php
+if (Configure::read() > 0):
+	Debugger::checkSecurityKeys();
+endif;
+?>
+<div id="url-rewriting-warning" style="background-color:#e32; color:#fff; padding:3px; margin: 20px 0">
+	<?php __('URL rewriting is not properly configured on your server. '); ?>
+	<ol style="padding-left:20px">
+		<li>
+			<a target="_blank" href="http://book.cakephp.org/view/917/Apache-and-mod_rewrite-and-htaccess" style="color:#fff;">
+				<?php __('Help me configure it')?>
+			</a>
+		</li>
+		<li>
+			<a target="_blank" href="http://book.cakephp.org/view/931/CakePHP-Core-Configuration-Variables" style="color:#fff;">
+				<?php __('I don\'t / can\'t use URL rewriting')?>
+			</a>
+		</li>
+	</ol>
+</div>
+<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">';
+					printf(__('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
+	App::import('Core', 'Validation');
+	if (!Validation::alphaNumeric('cakephp')) {
+		echo '<p><span class="notice">';
+		__('PCRE has not been compiled with Unicode support.');
+		echo '<br/>';
+		__('Recompile PCRE with Unicode support by adding <code>--enable-unicode-properties</code> when configuring');
+		echo '</span></p>';
+	}
+?>
+<?php
+if (isset($filePresent)):
+	if (!class_exists('ConnectionManager')) {
+		require LIBS . 'model' . DS . 'connection_manager.php';
+	}
+	$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>
+	<?php
+		echo $this->Html->link(
+			sprintf('<strong>%s</strong> %s', __('New', true), __('CakePHP 1.3 Docs', true)),
+			'http://book.cakephp.org/view/875/x1-3-Collection',
+			array('target' => '_blank', 'escape' => false)
+		);
+	?>
+</p>
+<p>
+	<?php
+		echo $this->Html->link(
+			__('The 15 min Blog Tutorial', true),
+			'http://book.cakephp.org/view/1528/Blog',
+			array('target' => '_blank', 'escape' => false)
+		);
+	?>
+</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>
+
+<ul>
+	<li><a href="http://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="http://github.com/cakephp/"><?php __('CakePHP Code'); ?> </a>
+	<ul><li><?php __('For the Development of CakePHP Git repository, Downloads'); ?></li></ul></li>
+	<li><a href="http://cakephp.lighthouseapp.com/"><?php __('CakePHP Lighthouse'); ?> </a>
+	<ul><li><?php __('CakePHP Tickets, Wiki pages, Roadmap'); ?></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>

Added: trunk/src/Web/cake/libs/view/scaffolds/edit.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/scaffolds/edit.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/scaffolds/edit.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,47 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.scaffolds
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<div class="<?php echo $pluralVar;?> form">
+<?php
+	echo $this->Form->create();
+	echo $this->Form->inputs($scaffoldFields, array('created', 'modified', 'updated'));
+	echo $this->Form->end(__('Submit', true));
+?>
+</div>
+<div class="actions">
+	<h3><?php __('Actions'); ?></h3>
+	<ul>
+<?php if ($this->action != 'add'):?>
+		<li><?php echo $this->Html->link(__('Delete', true), array('action' => 'delete', $this->Form->value($modelClass.'.'.$primaryKey)), null, __('Are you sure you want to delete', true).' #' . $this->Form->value($modelClass.'.'.$primaryKey)); ?></li>
+<?php endif;?>
+		<li><?php echo $this->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>" . $this->Html->link(sprintf(__('List %s', true), Inflector::humanize($_details['controller'])), array('controller' => $_details['controller'], 'action' =>'index')) . "</li>\n";
+					echo "\t\t<li>" . $this->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

Added: trunk/src/Web/cake/libs/view/scaffolds/index.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/scaffolds/index.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/scaffolds/index.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,93 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console.libs.templates.views
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<div class="<?php echo $pluralVar;?> index">
+<h2><?php echo $pluralHumanName;?></h2>
+<table cellpadding="0" cellspacing="0">
+<tr>
+<?php foreach ($scaffoldFields as $_field):?>
+	<th><?php echo $this->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" . $this->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" . $this->Html->link(__('View', true), array('action' => 'view', ${$singularVar}[$modelClass][$primaryKey])) . "\n";
+		echo "\t\t\t" . $this->Html->link(__('Edit', true), array('action' => 'edit', ${$singularVar}[$modelClass][$primaryKey])) . "\n";
+		echo "\t\t\t" . $this->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>
+	<p><?php
+	echo $this->Paginator->counter(array(
+		'format' => __('Page %page% of %pages%, showing %current% records out of %count% total, starting on record %start%, ending on %end%', true)
+	));
+	?></p>
+	<div class="paging">
+	<?php echo "\t" . $this->Paginator->prev('<< ' . __('previous', true), array(), null, array('class' => 'disabled')) . "\n";?>
+	 | <?php echo $this->Paginator->numbers() . "\n"?>
+	<?php echo "\t ". $this->Paginator->next(__('next', true) .' >>', array(), null, array('class' => 'disabled')) . "\n";?>
+	</div>
+</div>
+<div class="actions">
+	<h3><?php __('Actions'); ?></h3>
+	<ul>
+		<li><?php echo $this->Html->link(sprintf(__('New %s', true), $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>" . $this->Html->link(sprintf(__('List %s', true), Inflector::humanize($_details['controller'])), array('controller' => $_details['controller'], 'action' => 'index')) . "</li>\n";
+					echo "\t\t<li>" . $this->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

Added: trunk/src/Web/cake/libs/view/scaffolds/view.ctp
===================================================================
--- trunk/src/Web/cake/libs/view/scaffolds/view.ctp	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/scaffolds/view.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,159 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.scaffolds
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<div class="<?php echo $pluralVar;?> view">
+<h2><?php printf(__("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" . $this->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">
+	<h3><?php __('Actions'); ?></h3>
+	<ul>
+<?php
+	echo "\t\t<li>" .$this->Html->link(sprintf(__('Edit %s', true), $singularHumanName),   array('action' => 'edit', ${$singularVar}[$modelClass][$primaryKey])). " </li>\n";
+	echo "\t\t<li>" .$this->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>" .$this->Html->link(sprintf(__('List %s', true), $pluralHumanName), array('action' => 'index')). " </li>\n";
+	echo "\t\t<li>" .$this->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>" . $this->Html->link(sprintf(__('List %s', true), Inflector::humanize($_details['controller'])), array('controller' => $_details['controller'], 'action' => 'index')) . "</li>\n";
+				echo "\t\t<li>" . $this->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 printf(__("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 $this->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 printf(__("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]);
+		if (isset($_details['with'])) {
+			$index = array_search($_details['with'], $otherFields);
+			unset($otherFields[$index]);
+		}
+		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" . $this->Html->link(__('View', true), array('controller' => $_details['controller'], 'action' => 'view', ${$otherSingularVar}[$_details['primaryKey']])). "\n";
+			echo "\t\t\t\t" . $this->Html->link(__('Edit', true), array('controller' => $_details['controller'], 'action' => 'edit', ${$otherSingularVar}[$_details['primaryKey']])). "\n";
+			echo "\t\t\t\t" . $this->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 $this->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

Added: trunk/src/Web/cake/libs/view/theme.php
===================================================================
--- trunk/src/Web/cake/libs/view/theme.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/theme.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,74 @@
+<?php
+/**
+ * A custom view class that is used for themeing
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Theme view class
+ *
+ * Allows the creation of multiple themes to be used in an app. Theme views are regular view files
+ * that can provide unique HTML and static assets.  If theme views are not found for the current view
+ * the default app view files will be used. You can set `$this->theme` and `$this->view = 'Theme'` 
+ * in your Controller to use the ThemeView.
+ *
+ * Example of theme path with `$this->theme = 'super_hot';` Would be `app/views/themed/super_hot/posts`
+ *
+ * @package       cake
+ * @subpackage    cake.cake.libs.view
+ */
+class ThemeView extends View {
+/**
+ * Constructor for ThemeView sets $this->theme.
+ *
+ * @param Controller $controller Controller object to be rendered.
+ * @param boolean $register Should the view be registered in the registry.
+ */
+	function __construct(&$controller, $register = true) {
+		parent::__construct($controller, $register);
+		$this->theme =& $controller->theme;
+	}
+
+/**
+ * Return all possible paths to find view files in order
+ *
+ * @param string $plugin The name of the plugin views are being found for.
+ * @param boolean $cached Set to true to force dir scan.
+ * @return array paths
+ * @access protected
+ * @todo Make theme path building respect $cached parameter.
+ */
+	function _paths($plugin = null, $cached = true) {
+		$paths = parent::_paths($plugin, $cached);
+		$themePaths = array();
+
+		if (!empty($this->theme)) {
+			$count = count($paths);
+			for ($i = 0; $i < $count; $i++) {
+				if (strpos($paths[$i], DS . 'plugins' . DS) === false
+					&& strpos($paths[$i], DS . 'libs' . DS . 'view') === false) {
+						if ($plugin) {
+							$themePaths[] = $paths[$i] . 'themed'. DS . $this->theme . DS . 'plugins' . DS . $plugin . DS;
+						}
+						$themePaths[] = $paths[$i] . 'themed'. DS . $this->theme . DS;
+					}
+			}
+			$paths = array_merge($themePaths, $paths);
+		}
+		return $paths;
+	}
+}

Added: trunk/src/Web/cake/libs/view/view.php
===================================================================
--- trunk/src/Web/cake/libs/view/view.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/view/view.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,993 @@
+<?php
+/**
+ * Methods for displaying presentation data in the view.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Included libraries.
+ */
+App::import('Core', 'ClassRegistry');
+App::import('View', 'Helper', false);
+
+/**
+ * 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;
+
+/**
+ * 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
+ * @access public
+ */
+	var $ext = '.ctp';
+
+/**
+ * Sub-directory for this view file.
+ *
+ * @var string
+ * @access public
+ */
+	var $subDir = null;
+
+/**
+ * Theme name.
+ *
+ * @var string
+ * @access public
+ */
+	var $theme = 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
+ * @access public
+ */
+	var $validationErrors = array();
+
+/**
+ * True when the view has been rendered.
+ *
+ * @var boolean
+ * @access public
+ */
+	var $hasRendered = false;
+
+/**
+ * Array of loaded view helpers.
+ *
+ * @var array
+ * @access public
+ */
+	var $loaded = array();
+
+/**
+ * True if in scope of model-specific region
+ *
+ * @var boolean
+ * @access public
+ */
+	var $modelScope = false;
+
+/**
+ * Name of current model this view context is attached to
+ *
+ * @var string
+ * @access public
+ */
+	var $model = null;
+
+/**
+ * Name of association model this view context is attached to
+ *
+ * @var string
+ * @access public
+ */
+	var $association = null;
+
+/**
+ * Name of current model field this view context is attached to
+ *
+ * @var string
+ * @access public
+ */
+	var $field = null;
+
+/**
+ * Suffix of current field this view context is attached to
+ *
+ * @var string
+ * @access public
+ */
+	var $fieldSuffix = null;
+
+/**
+ * The current model ID this view context is attached to
+ *
+ * @var mixed
+ * @access public
+ */
+	var $modelId = null;
+
+/**
+ * List of generated DOM UUIDs
+ *
+ * @var array
+ * @access public
+ */
+	var $uuids = array();
+
+/**
+ * Holds View output.
+ *
+ * @var string
+ * @access public
+ */
+	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', '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
+ * @access private
+ */
+	var $__paths = array();
+
+/**
+ * Constructor
+ *
+ * @param Controller $controller A controller object to pull View::__passedArgs from.
+ * @param boolean $register Should the View instance be registered in the ClassRegistry
+ * @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.
+ *
+ * ### Special params
+ *
+ * - `cache` - enable caching for this element accepts boolean or strtotime compatible string.
+ *   Can also be an array. If `cache` is an array,
+ *   `time` is used to specify duration of cache.
+ *   `key` can be used to create unique cache files.
+ * - `plugin` - Load an element from a specific plugin.
+ *
+ * @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)
+ * @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']);
+			} else {
+				$key = implode('_', array_keys($params));
+				if ($params['cache'] !== true) {
+					$expires = $params['cache'];
+				}
+			}
+
+			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);
+		$exts = $this->_getExtensions();
+		foreach ($exts as $ext) {
+			foreach ($paths as $path) {
+				if (file_exists($path . 'elements' . DS . $name . $ext)) {
+					$file = $path . 'elements' . DS . $name . $ext;
+					break;
+				}
+			}
+			if ($file) {
+				break;
+			}
+		}
+
+		if (is_file($file)) {
+			$vars = array_merge($this->viewVars, $params);
+			foreach ($this->loaded as $name => $helper) {
+				if (!isset($vars[$name])) {
+					$vars[$name] =& $this->loaded[$name];
+				}
+			}
+			$element = $this->_render($file, $vars, $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
+ * @access public
+ */
+	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)) {
+			$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']) ||
+					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);
+			trigger_error(sprintf(__("Error in view %s, got: <blockquote>%s</blockquote>", true), $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` - A backwards compatible place holder, you should set this value if you want more control.
+ * - `content_for_layout` - contains rendered view file
+ * - `scripts_for_layout` - contains scripts added to header
+ *
+ * @param string $content_for_layout Content to render in a view, wrapped by the surrounding layout.
+ * @return mixed Rendered output, or false on error
+ * @access public
+ */
+	function renderLayout($content_for_layout, $layout = null) {
+		$layoutFileName = $this->_getLayoutFileName($layout);
+		if (empty($layoutFileName)) {
+			return $this->output;
+		}
+
+		$dataForLayout = array_merge($this->viewVars, array(
+			'content_for_layout' => $content_for_layout,
+			'scripts_for_layout' => implode("\n\t", $this->__scripts),
+		));
+
+		if (!isset($dataForLayout['title_for_layout'])) {
+			$dataForLayout['title_for_layout'] = Inflector::humanize($this->viewPath);
+		}
+
+		if (empty($this->loaded) && !empty($this->helpers)) {
+			$loadHelpers = true;
+		} else {
+			$loadHelpers = false;
+			$dataForLayout = array_merge($dataForLayout, $this->loaded);
+		}
+
+		$this->_triggerHelpers('beforeLayout');
+		$this->output = $this->_render($layoutFileName, $dataForLayout, $loadHelpers, true);
+
+		if ($this->output === false) {
+			$this->output = $this->_render($layoutFileName, $dataForLayout);
+			trigger_error(sprintf(__("Error in layout %s, got: <blockquote>%s</blockquote>", true), $layoutFileName, $this->output), E_USER_ERROR);
+			return false;
+		}
+
+		$this->_triggerHelpers('afterLayout');
+
+		return $this->output;
+	}
+
+/**
+ * Fire a callback on all loaded Helpers. All helpers must implement this method,
+ * it is not checked before being called.  You can add additional helper callbacks in AppHelper.
+ *
+ * @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. Works in concert with CacheHelper and Dispatcher to
+ * render cached view files.
+ *
+ * @param string $filename the cache file to include
+ * @param string $timeStart the page render start time
+ * @return boolean Success of rendering the cached file.
+ * @access public
+ */
+	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');
+				}
+				$commentLength = strlen('<!--cachetime:' . $match['1'] . '-->');
+				echo substr($out, $commentLength);
+				return true;
+			}
+		}
+	}
+
+/**
+ * Returns a list of variables available in the current View context
+ *
+ * @return array Array of the set view variable names.
+ * @access public
+ */
+	function getVars() {
+		return array_keys($this->viewVars);
+	}
+
+/**
+ * Returns the contents of the given View variable(s)
+ *
+ * @param string $var The view var you want the contents of.
+ * @return mixed The content of the named var if its set, otherwise null.
+ * @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 Either the key name for the script, or the script content. Name can be used to
+ *   update/replace a script element.
+ * @param string $content The content of the script being added, optional.
+ * @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
+ * @access public
+ */
+	function entity() {
+		$assoc = ($this->association) ? $this->association : $this->model;
+		if (!empty($this->entityPath)) {
+			$path = explode('.', $this->entityPath);
+			$count = count($path);
+			if (
+				($count == 1 && !empty($this->association)) ||
+				($count == 1 && $this->model != $this->entityPath) ||
+				($count == 1 && empty($this->association) && !empty($this->field)) ||
+				($count  == 2 && !empty($this->fieldSuffix)) ||
+				is_numeric($path[0]) && !empty($assoc)
+			) {
+				array_unshift($path, $assoc);
+			}
+			return Set::filter($path);
+		}
+		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 void
+ * @access public
+ */
+	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;
+		}
+		$this->viewVars = $data + $this->viewVars;
+	}
+
+/**
+ * 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
+ * @access public
+ */
+	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
+ * @param boolean $loadHelpers Boolean to indicate that helpers should be loaded.
+ * @param boolean $cached Whether or not to trigger the creation of a cache file.
+ * @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);
+			$helpers = array_keys($loadedHelpers);
+			$helperNames = array_map(array('Inflector', 'variable'), $helpers);
+
+			for ($i = count($helpers) - 1; $i >= 0; $i--) {
+				$name = $helperNames[$i];
+				$helper =& $loadedHelpers[$helpers[$i]];
+
+				if (!isset($___dataForView[$name])) {
+					${$name} =& $helper;
+				}
+				$this->loaded[$helperNames[$i]] =& $helper;
+				$this->{$helpers[$i]} =& $helper;
+			}
+			$this->_triggerHelpers('beforeRender');
+			unset($name, $loadedHelpers, $helpers, $i, $helperNames, $helper);
+		}
+
+		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->viewVars = $this->viewVars;
+				$out = $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 Array containing the loaded helpers.
+ * @access protected
+ */
+	function &_loadHelpers(&$loaded, $helpers, $parent = null) {
+		foreach ($helpers as $i => $helper) {
+			$options = array();
+
+			if (!is_int($i)) {
+				$options = $helper;
+				$helper = $i;
+			}
+			list($plugin, $helper) = pluginSplit($helper, true, $this->plugin);
+			$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', 'theme', '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 $name 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(Inflector::underscore($this->plugin));
+
+		$exts = $this->_getExtensions();
+		foreach ($exts as $ext) {
+			foreach ($paths as $path) {
+				if (file_exists($path . $name . $ext)) {
+					return $path . $name . $ext;
+				}
+			}
+		}
+		$defaultPath = $paths[0];
+
+		if ($this->plugin) {
+			$pluginPaths = App::path('plugins');
+			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.
+ *
+ * @param string $name The name of the layout to find.
+ * @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(Inflector::underscore($this->plugin));
+		$file = 'layouts' . DS . $subDir . $name;
+
+		$exts = $this->_getExtensions();
+		foreach ($exts as $ext) {
+			foreach ($paths as $path) {
+				if (file_exists($path . $file . $ext)) {
+					return $path . $file . $ext;
+				}
+			}
+		}
+		return $this->_missingView($paths[0] . $file . $this->ext, 'missingLayout');
+	}
+
+
+/**
+ * Get the extensions that view files can use.
+ *
+ * @return array Array of extensions view files use.
+ * @access protected
+ */
+	function _getExtensions() {
+		$exts = array($this->ext);
+		if ($this->ext !== '.ctp') {
+			array_push($exts, '.ctp');
+		}
+		return $exts;
+	}
+
+/**
+ * Return a misssing view error message
+ *
+ * @param string $viewFileName the filename that should exist
+ * @return false
+ * @access protected
+ */
+	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 Optional plugin name to scan for view files.
+ * @param boolean $cached Set to true to force a refresh of view paths.
+ * @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 = App::path('views');
+		$corePaths = array_flip(App::core('views'));
+
+		if (!empty($plugin)) {
+			$count = count($viewPaths);
+			for ($i = 0; $i < $count; $i++) {
+				if (!isset($corePaths[$viewPaths[$i]])) {
+					$paths[] = $viewPaths[$i] . 'plugins' . DS . $plugin . DS;
+				}
+			}
+			$paths[] = App::pluginPath($plugin) . 'views' . DS;
+		}
+		$this->__paths = array_merge($paths, $viewPaths);
+		return $this->__paths;
+	}
+}

Added: trunk/src/Web/cake/libs/xml.php
===================================================================
--- trunk/src/Web/cake/libs/xml.php	                        (rev 0)
+++ trunk/src/Web/cake/libs/xml.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,1460 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP v .0.10.3.1400
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+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('format' => 'attributes');
+
+		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) && $options['slug'] !== false) {
+			$name = Inflector::slug(Inflector::underscore($name));
+		}
+
+		if (!empty($name)) {
+			$node =& $this->createElement($name);
+		} else {
+			$node =& $this;
+		}
+
+		$namespace = array();
+		$attributes = array();
+		$children = array();
+		$chldObjs = array();
+
+		if (is_object($object)) {
+			$chldObjs = get_object_vars($object);
+		} elseif (is_array($object)) {
+			$chldObjs = $object;
+		} elseif (!empty($object) || $object === 0 || $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']]);
+		}
+
+		$n = $name;
+		if (isset($chldObjs['_name_'])) {
+			$n = null;
+			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']))) {
+					if (!is_numeric($key)) {
+						$n = $key;
+					}
+					if (is_array($val)) {
+						foreach ($val as $n2 => $obj2) {
+							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) {
+							if ($options['slug'] == true) {
+								$key = Inflector::slug(Inflector::underscore($key));
+							}
+							$tmp =& $node->createElement($key);
+							if (!empty($val) || $val === 0 || $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.', true));
+				$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) && !empty($this->children)) {
+			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 (!empty($this->namespaces) > 0) {
+				foreach ($this->namespaces as $key => $val) {
+					$val = str_replace('"', '\"', $val);
+					$d .= ' xmlns:' . $key . '="' . $val . '"';
+				}
+			}
+
+			$parent =& $this->parent();
+			if ($parent->name === '#document' && !empty($parent->namespaces)) {
+				foreach ($parent->namespaces as $key => $val) {
+					$val = str_replace('"', '\"', $val);
+					$d .= ' xmlns:' . $key . '="' . $val . '"';
+				}
+			}
+
+			if (is_array($this->attributes) && !empty($this->attributes)) {
+				foreach ($this->attributes as $key => $val) {
+					if (is_bool($val) && $val === false) {
+						$val = 0;
+					}
+					$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;
+
+		foreach ($this->children as $child) {
+			$key = $camelize ? Inflector::camelize($child->name) : $child->name;
+
+			$leaf = false;
+			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 (count($child->children) == 1) {
+					$leaf = true;
+				}
+			} elseif (count($child->children) === 0 && $child->value == '') {
+				$value = $child->attributes;
+				if (empty($value)) {
+					$leaf = true;
+				}
+			} else {
+				$value = $child->toArray($camelize);
+			}
+
+			if (isset($out[$key])) {
+				if(!isset($out[$key][0]) || !is_array($out[$key]) || !is_int(key($out[$key]))) {
+					$out[$key] = array($out[$key]);
+				} 
+				$out[$key][] = $value;
+			} elseif (isset($out[$child->name])) {
+				$t = $out[$child->name];
+				unset($out[$child->name]);
+				$out[$key] = array($t);
+				$out[$key][] = $value;
+			} elseif ($leaf) {
+				$out[$child->name] = $value;
+			} else {
+				$out[$key] = $value;
+			}
+		}
+		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 protected
+ */
+	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.
+ *
+ * ### Options
+ * - 'root': The name of the root element, defaults to '#document'
+ * - 'version': The XML version, defaults to '1.0'
+ * - 'encoding': Document encoding, defaults to 'UTF-8'
+ * - 'namespaces': An array of namespaces (as strings) used in this document
+ * - 'format': Specifies the format this document converts to when parsed or
+ *    rendered out as text, either 'attributes' or 'tags', defaults to 'attributes'
+ * - 'tags': An array specifying any tag-specific formatting options, indexed
+ *    by tag name.  See XmlNode::normalize().
+ * - 'slug':  A boolean to indicate whether or not you want the string version of the XML document
+ *   to have its tags run through Inflector::slug().  Defaults to true
+ *
+ * @param mixed $input The content with which this XML document should be initialized.  Can be a
+ *    string, array or object.  If a string is specified, it may be a literal XML
+ *    document, or a URL or file path to read from.
+ * @param array $options Options to set up with, for valid options see above:
+ * @see XmlNode::normalize()
+ */
+	function __construct($input = null, $options = array()) {
+		$defaults = array(
+			'root' => '#document', 'tags' => array(), 'namespaces' => array(),
+			'version' => '1.0', 'encoding' => 'UTF-8', 'format' => 'attributes',
+			'slug' => true
+		);
+		$options = array_merge($defaults, Xml::options(), $options);
+
+		foreach (array('version', 'encoding', 'namespaces') as $key) {
+			$this->{$key} = $options[$key];
+		}
+		$this->__tags = $options['tags'];
+		parent::__construct('#document');
+
+		if ($options['root'] !== '#document') {
+			$Root =& $this->createNode($options['root']);
+		} else {
+			$Root =& $this;
+		}
+
+		if (!empty($input)) {
+			if (is_string($input)) {
+				$Root->load($input);
+			} elseif (is_array($input) || is_object($input)) {
+				$Root->append($input, $options);
+			}
+		}
+	}
+
+/**
+ * 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', true));
+			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->__rawData = trim($this->__rawData);
+		$this->__header = trim(str_replace(
+			array('<' . '?', '?' . '>'),
+			array('', ''),
+			substr($this->__rawData, 0, strpos($this->__rawData, '?' . '>'))
+		));
+
+		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('tag' => null, 'value' => null, 'attributes' => array());
+			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;
+			}
+		}
+		xml_parser_free($this->__parser);
+		$this->__parser = null;
+		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 an 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() {
+		$this->_killParent(true);
+	}
+
+/**
+ * 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];
+	}
+}

Added: trunk/src/Web/cake/tests/cases/basics.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/basics.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/basics.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,828 @@
+<?php
+/**
+ * BasicsTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+require_once CAKE . 'basics.php';
+App::import('Core', 'Folder');
+
+/**
+ * BasicsTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases
+ */
+class BasicsTest extends CakeTestCase {
+
+/**
+ * setUp method
+ *
+ * @return void
+ * @access public
+ */
+	function setUp() {
+		App::build(array(
+			'locales' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'locale' . DS)
+		));
+		$this->_language = Configure::read('Config.language');
+	}
+
+/**
+ * tearDown method
+ *
+ * @return void
+ * @access public
+ */
+	function tearDown() {
+		App::build();
+		Configure::write('Config.language', $this->_language);
+	}
+
+/**
+ * test the array_diff_key compatibility function.
+ *
+ * @return void
+ * @access public
+ */
+	function testArrayDiffKey() {
+		$one = array('one' => 1, 'two' => 2, 'three' => 3);
+		$two = array('one' => 'one', 'two' => 'two');
+		$result = array_diff_key($one, $two);
+		$expected = array('three' => 3);
+		$this->assertEqual($result, $expected);
+
+		$one = array('one' => array('value', 'value-two'), 'two' => 2, 'three' => 3);
+		$two = array('two' => 'two');
+		$result = array_diff_key($one, $two);
+		$expected = array('one' => array('value', 'value-two'), 'three' => 3);
+		$this->assertEqual($result, $expected);
+
+		$one = array('one' => null, 'two' => 2, 'three' => '', 'four' => 0);
+		$two = array('two' => 'two');
+		$result = array_diff_key($one, $two);
+		$expected = array('one' => null, 'three' => '', 'four' => 0);
+		$this->assertEqual($result, $expected);
+
+		$one = array('minYear' => null, 'maxYear' => null, 'separator' => '-', 'interval' => 1, 'monthNames' => true);
+		$two = array('minYear' => null, 'maxYear' => null, 'separator' => '-', 'interval' => 1, 'monthNames' => true);
+		$result = array_diff_key($one, $two);
+		$this->assertEqual($result, array());
+
+	}
+/**
+ * testHttpBase method
+ *
+ * @return void
+ * @access public
+ */
+	function testEnv() {
+		$this->skipIf(!function_exists('ini_get') || ini_get('safe_mode') === '1', '%s safe mode is on');
+
+		$__SERVER = $_SERVER;
+		$__ENV = $_ENV;
+
+		$_SERVER['HTTP_HOST'] = 'localhost';
+		$this->assertEqual(env('HTTP_BASE'), '.localhost');
+
+		$_SERVER['HTTP_HOST'] = 'com.ar';
+		$this->assertEqual(env('HTTP_BASE'), '.com.ar');
+
+		$_SERVER['HTTP_HOST'] = 'example.ar';
+		$this->assertEqual(env('HTTP_BASE'), '.example.ar');
+
+		$_SERVER['HTTP_HOST'] = 'example.com';
+		$this->assertEqual(env('HTTP_BASE'), '.example.com');
+
+		$_SERVER['HTTP_HOST'] = 'www.example.com';
+		$this->assertEqual(env('HTTP_BASE'), '.example.com');
+
+		$_SERVER['HTTP_HOST'] = 'subdomain.example.com';
+		$this->assertEqual(env('HTTP_BASE'), '.example.com');
+
+		$_SERVER['HTTP_HOST'] = 'example.com.ar';
+		$this->assertEqual(env('HTTP_BASE'), '.example.com.ar');
+
+		$_SERVER['HTTP_HOST'] = 'www.example.com.ar';
+		$this->assertEqual(env('HTTP_BASE'), '.example.com.ar');
+
+		$_SERVER['HTTP_HOST'] = 'subdomain.example.com.ar';
+		$this->assertEqual(env('HTTP_BASE'), '.example.com.ar');
+
+		$_SERVER['HTTP_HOST'] = 'double.subdomain.example.com';
+		$this->assertEqual(env('HTTP_BASE'), '.subdomain.example.com');
+
+		$_SERVER['HTTP_HOST'] = 'double.subdomain.example.com.ar';
+		$this->assertEqual(env('HTTP_BASE'), '.subdomain.example.com.ar');
+
+		$_SERVER = $_ENV = array();
+
+		$_SERVER['SCRIPT_NAME'] = '/a/test/test.php';
+		$this->assertEqual(env('SCRIPT_NAME'), '/a/test/test.php');
+
+		$_SERVER = $_ENV = array();
+
+		$_ENV['CGI_MODE'] = 'BINARY';
+		$_ENV['SCRIPT_URL'] = '/a/test/test.php';
+		$this->assertEqual(env('SCRIPT_NAME'), '/a/test/test.php');
+
+		$_SERVER = $_ENV = array();
+
+		$this->assertFalse(env('HTTPS'));
+
+		$_SERVER['HTTPS'] = 'on';
+		$this->assertTrue(env('HTTPS'));
+
+		$_SERVER['HTTPS'] = '1';
+		$this->assertTrue(env('HTTPS'));
+
+		$_SERVER['HTTPS'] = 'I am not empty';
+		$this->assertTrue(env('HTTPS'));
+
+		$_SERVER['HTTPS'] = 1;
+		$this->assertTrue(env('HTTPS'));
+
+		$_SERVER['HTTPS'] = 'off';
+		$this->assertFalse(env('HTTPS'));
+
+		$_SERVER['HTTPS'] = false;
+		$this->assertFalse(env('HTTPS'));
+
+		$_SERVER['HTTPS'] = '';
+		$this->assertFalse(env('HTTPS'));
+
+		$_SERVER = array();
+
+		$_ENV['SCRIPT_URI'] = 'https://domain.test/a/test.php';
+		$this->assertTrue(env('HTTPS'));
+
+		$_ENV['SCRIPT_URI'] = 'http://domain.test/a/test.php';
+		$this->assertFalse(env('HTTPS'));
+
+		$_SERVER = $_ENV = array();
+
+		$this->assertFalse(env('TEST_ME'));
+
+		$_ENV['TEST_ME'] = 'a';
+		$this->assertEqual(env('TEST_ME'), 'a');
+
+		$_SERVER['TEST_ME'] = 'b';
+		$this->assertEqual(env('TEST_ME'), 'b');
+
+		unset($_ENV['TEST_ME']);
+		$this->assertEqual(env('TEST_ME'), 'b');
+
+		$_SERVER = $__SERVER;
+		$_ENV = $__ENV;
+	}
+
+/**
+ * test uses()
+ *
+ * @return void
+ * @access public
+ * @deprecated
+ */
+	function testUses() {
+		$this->skipIf(class_exists('Security') || class_exists('Sanitize'), '%s Security and/or Sanitize class already loaded');
+
+		$this->assertFalse(class_exists('Security'));
+		$this->assertFalse(class_exists('Sanitize'));
+
+		uses('Security', 'Sanitize');
+
+		$this->assertTrue(class_exists('Security'));
+		$this->assertTrue(class_exists('Sanitize'));
+	}
+
+/**
+ * Test h()
+ *
+ * @return void
+ * @access public
+ */
+	function testH() {
+		$string = '<foo>';
+		$result = h($string);
+		$this->assertEqual('&lt;foo&gt;', $result);
+
+		$in = array('this & that', '<p>Which one</p>');
+		$result = h($in);
+		$expected = array('this &amp; that', '&lt;p&gt;Which one&lt;/p&gt;');
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * Test a()
+ *
+ * @return void
+ * @access public
+ */
+	function testA() {
+		$result = a('this', 'that', 'bar');
+		$this->assertEqual(array('this', 'that', 'bar'), $result);
+	}
+
+/**
+ * Test aa()
+ *
+ * @return void
+ * @access public
+ */
+	function testAa() {
+		$result = aa('a', 'b', 'c', 'd');
+		$expected = array('a' => 'b', 'c' => 'd');
+		$this->assertEqual($expected, $result);
+
+		$result = aa('a', 'b', 'c', 'd', 'e');
+		$expected = array('a' => 'b', 'c' => 'd', 'e' => null);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * Test am()
+ *
+ * @return void
+ * @access public
+ */
+	function testAm() {
+		$result = am(array('one', 'two'), 2, 3, 4);
+		$expected = array('one', 'two', 2, 3, 4);
+		$this->assertEqual($result, $expected);
+
+		$result = am(array('one' => array(2, 3), 'two' => array('foo')), array('one' => array(4, 5)));
+		$expected = array('one' => array(4, 5),'two' => array('foo'));
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test cache()
+ *
+ * @return void
+ * @access public
+ */
+	function testCache() {
+		$_cacheDisable = Configure::read('Cache.disable');
+		if ($this->skipIf($_cacheDisable, 'Cache is disabled, skipping cache() tests. %s')) {
+			return;
+		}
+
+		Configure::write('Cache.disable', true);
+		$result = cache('basics_test', 'simple cache write');
+		$this->assertNull($result);
+
+		$result = cache('basics_test');
+		$this->assertNull($result);
+
+		Configure::write('Cache.disable', false);
+		$result = cache('basics_test', 'simple cache write');
+		$this->assertTrue($result);
+		$this->assertTrue(file_exists(CACHE . 'basics_test'));
+
+		$result = cache('basics_test');
+		$this->assertEqual($result, 'simple cache write');
+		@unlink(CACHE . 'basics_test');
+
+		cache('basics_test', 'expired', '+1 second');
+		sleep(2);
+		$result = cache('basics_test', null, '+1 second');
+		$this->assertNull($result);
+
+		Configure::write('Cache.disable', $_cacheDisable);
+	}
+
+/**
+ * test clearCache()
+ *
+ * @return void
+ * @access public
+ */
+	function testClearCache() {
+		$cacheOff = Configure::read('Cache.disable');
+		if ($this->skipIf($cacheOff, 'Cache is disabled, skipping clearCache() tests. %s')) {
+			return;
+		}
+
+		cache('views' . DS . 'basics_test.cache', 'simple cache write');
+		$this->assertTrue(file_exists(CACHE . 'views' . DS . 'basics_test.cache'));
+
+		cache('views' . DS . 'basics_test_2.cache', 'simple cache write 2');
+		$this->assertTrue(file_exists(CACHE . 'views' . DS . 'basics_test_2.cache'));
+
+		cache('views' . DS . 'basics_test_3.cache', 'simple cache write 3');
+		$this->assertTrue(file_exists(CACHE . 'views' . DS . 'basics_test_3.cache'));
+
+		$result = clearCache(array('basics_test', 'basics_test_2'), 'views', '.cache');
+		$this->assertTrue($result);
+		$this->assertFalse(file_exists(CACHE . 'views' . DS . 'basics_test.cache'));
+		$this->assertFalse(file_exists(CACHE . 'views' . DS . 'basics_test.cache'));
+		$this->assertTrue(file_exists(CACHE . 'views' . DS . 'basics_test_3.cache'));
+
+		$result = clearCache(null, 'views', '.cache');
+		$this->assertTrue($result);
+		$this->assertFalse(file_exists(CACHE . 'views' . DS . 'basics_test_3.cache'));
+
+		// Different path from views and with prefix
+		cache('models' . DS . 'basics_test.cache', 'simple cache write');
+		$this->assertTrue(file_exists(CACHE . 'models' . DS . 'basics_test.cache'));
+
+		cache('models' . DS . 'basics_test_2.cache', 'simple cache write 2');
+		$this->assertTrue(file_exists(CACHE . 'models' . DS . 'basics_test_2.cache'));
+
+		cache('models' . DS . 'basics_test_3.cache', 'simple cache write 3');
+		$this->assertTrue(file_exists(CACHE . 'models' . DS . 'basics_test_3.cache'));
+
+		$result = clearCache('basics', 'models', '.cache');
+		$this->assertTrue($result);
+		$this->assertFalse(file_exists(CACHE . 'models' . DS . 'basics_test.cache'));
+		$this->assertFalse(file_exists(CACHE . 'models' . DS . 'basics_test_2.cache'));
+		$this->assertFalse(file_exists(CACHE . 'models' . DS . 'basics_test_3.cache'));
+
+		// checking if empty files were not removed
+		$emptyExists = file_exists(CACHE . 'views' . DS . 'empty');
+		if (!$emptyExists) {
+			cache('views' . DS . 'empty', '');
+		}
+		cache('views' . DS . 'basics_test.php', 'simple cache write');
+		$this->assertTrue(file_exists(CACHE . 'views' . DS . 'basics_test.php'));
+		$this->assertTrue(file_exists(CACHE . 'views' . DS . 'empty'));
+
+		$result = clearCache();
+		$this->assertTrue($result);
+		$this->assertTrue(file_exists(CACHE . 'views' . DS . 'empty'));
+		$this->assertFalse(file_exists(CACHE . 'views' . DS . 'basics_test.php'));
+		if (!$emptyExists) {
+			unlink(CACHE . 'views' . DS . 'empty');
+		}
+	}
+
+/**
+ * test __()
+ *
+ * @return void
+ * @access public
+ */
+	function test__() {
+		Configure::write('Config.language', 'rule_1_po');
+
+		$result = __('Plural Rule 1', true);
+		$expected = 'Plural Rule 1 (translated)';
+		$this->assertEqual($result, $expected);
+
+		$result = __('Plural Rule 1 (from core)', true);
+		$expected = 'Plural Rule 1 (from core translated)';
+		$this->assertEqual($result, $expected);
+
+		ob_start();
+			__('Plural Rule 1 (from core)');
+		$result = ob_get_clean();
+		$expected = 'Plural Rule 1 (from core translated)';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test __n()
+ *
+ * @return void
+ * @access public
+ */
+	function test__n() {
+		Configure::write('Config.language', 'rule_1_po');
+
+		$result = __n('%d = 1', '%d = 0 or > 1', 0, true);
+		$expected = '%d = 0 or > 1 (translated)';
+		$this->assertEqual($result, $expected);
+
+		$result = __n('%d = 1', '%d = 0 or > 1', 1, true);
+		$expected = '%d = 1 (translated)';
+		$this->assertEqual($result, $expected);
+
+		$result = __n('%d = 1 (from core)', '%d = 0 or > 1 (from core)', 2, true);
+		$expected = '%d = 0 or > 1 (from core translated)';
+		$this->assertEqual($result, $expected);
+
+		ob_start();
+			__n('%d = 1 (from core)', '%d = 0 or > 1 (from core)', 2);
+		$result = ob_get_clean();
+		$expected = '%d = 0 or > 1 (from core translated)';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test __d()
+ *
+ * @return void
+ * @access public
+ */
+	function test__d() {
+		Configure::write('Config.language', 'rule_1_po');
+
+		$result = __d('default', 'Plural Rule 1', true);
+		$expected = 'Plural Rule 1 (translated)';
+		$this->assertEqual($result, $expected);
+
+		$result = __d('core', 'Plural Rule 1', true);
+		$expected = 'Plural Rule 1';
+		$this->assertEqual($result, $expected);
+
+		$result = __d('core', 'Plural Rule 1 (from core)', true);
+		$expected = 'Plural Rule 1 (from core translated)';
+		$this->assertEqual($result, $expected);
+
+		ob_start();
+			__d('core', 'Plural Rule 1 (from core)');
+		$result = ob_get_clean();
+		$expected = 'Plural Rule 1 (from core translated)';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test __dn()
+ *
+ * @return void
+ * @access public
+ */
+	function test__dn() {
+		Configure::write('Config.language', 'rule_1_po');
+
+		$result = __dn('default', '%d = 1', '%d = 0 or > 1', 0, true);
+		$expected = '%d = 0 or > 1 (translated)';
+		$this->assertEqual($result, $expected);
+
+		$result = __dn('core', '%d = 1', '%d = 0 or > 1', 0, true);
+		$expected = '%d = 0 or > 1';
+		$this->assertEqual($result, $expected);
+
+		$result = __dn('core', '%d = 1 (from core)', '%d = 0 or > 1 (from core)', 0, true);
+		$expected = '%d = 0 or > 1 (from core translated)';
+		$this->assertEqual($result, $expected);
+
+		$result = __dn('default', '%d = 1', '%d = 0 or > 1', 1, true);
+		$expected = '%d = 1 (translated)';
+		$this->assertEqual($result, $expected);
+
+		ob_start();
+			__dn('core', '%d = 1 (from core)', '%d = 0 or > 1 (from core)', 2);
+		$result = ob_get_clean();
+		$expected = '%d = 0 or > 1 (from core translated)';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test __c()
+ *
+ * @return void
+ * @access public
+ */
+	function test__c() {
+		Configure::write('Config.language', 'rule_1_po');
+
+		$result = __c('Plural Rule 1', 6, true);
+		$expected = 'Plural Rule 1 (translated)';
+		$this->assertEqual($result, $expected);
+
+		$result = __c('Plural Rule 1 (from core)', 6, true);
+		$expected = 'Plural Rule 1 (from core translated)';
+		$this->assertEqual($result, $expected);
+
+		ob_start();
+			__c('Plural Rule 1 (from core)', 6);
+		$result = ob_get_clean();
+		$expected = 'Plural Rule 1 (from core translated)';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test __dc()
+ *
+ * @return void
+ * @access public
+ */
+	function test__dc() {
+		Configure::write('Config.language', 'rule_1_po');
+
+		$result = __dc('default', 'Plural Rule 1', 6, true);
+		$expected = 'Plural Rule 1 (translated)';
+		$this->assertEqual($result, $expected);
+
+		$result = __dc('default', 'Plural Rule 1 (from core)', 6, true);
+		$expected = 'Plural Rule 1 (from core translated)';
+		$this->assertEqual($result, $expected);
+
+		$result = __dc('core', 'Plural Rule 1', 6, true);
+		$expected = 'Plural Rule 1';
+		$this->assertEqual($result, $expected);
+
+		$result = __dc('core', 'Plural Rule 1 (from core)', 6, true);
+		$expected = 'Plural Rule 1 (from core translated)';
+		$this->assertEqual($result, $expected);
+
+		ob_start();
+			__dc('default', 'Plural Rule 1 (from core)', 6);
+		$result = ob_get_clean();
+		$expected = 'Plural Rule 1 (from core translated)';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test __dcn()
+ *
+ * @return void
+ * @access public
+ */
+	function test__dcn() {
+		Configure::write('Config.language', 'rule_1_po');
+
+		$result = __dcn('default', '%d = 1', '%d = 0 or > 1', 0, 6, true);
+		$expected = '%d = 0 or > 1 (translated)';
+		$this->assertEqual($result, $expected);
+
+		$result = __dcn('default', '%d = 1 (from core)', '%d = 0 or > 1 (from core)', 1, 6, true);
+		$expected = '%d = 1 (from core translated)';
+		$this->assertEqual($result, $expected);
+
+		$result = __dcn('core', '%d = 1', '%d = 0 or > 1', 0, 6, true);
+		$expected = '%d = 0 or > 1';
+		$this->assertEqual($result, $expected);
+
+		ob_start();
+			__dcn('default', '%d = 1 (from core)', '%d = 0 or > 1 (from core)', 1, 6);
+		$result = ob_get_clean();
+		$expected = '%d = 1 (from core translated)';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test LogError()
+ *
+ * @return void
+ * @access public
+ */
+	function testLogError() {
+		@unlink(LOGS . 'error.log');
+
+		LogError('Testing LogError() basic function');
+		LogError("Testing with\nmulti-line\nstring");
+
+		$result = file_get_contents(LOGS . 'error.log');
+		$this->assertPattern('/Error: Testing LogError\(\) basic function/', $result);
+		$this->assertNoPattern("/Error: Testing with\nmulti-line\nstring/", $result);
+		$this->assertPattern('/Error: Testing with multi-line string/', $result);
+	}
+
+/**
+ * test fileExistsInPath()
+ *
+ * @return void
+ * @access public
+ */
+	function testFileExistsInPath() {
+		$this->skipUnless(function_exists('ini_set'), '%s ini_set function not available');
+
+		$_includePath = ini_get('include_path');
+
+		$path = TMP . 'basics_test';
+		$folder1 = $path . DS . 'folder1';
+		$folder2 = $path . DS . 'folder2';
+		$file1 = $path . DS . 'file1.php';
+		$file2 = $folder1 . DS . 'file2.php';
+		$file3 = $folder1 . DS . 'file3.php';
+		$file4 = $folder2 . DS . 'file4.php';
+
+		new Folder($path, true);
+		new Folder($folder1, true);
+		new Folder($folder2, true);
+		touch($file1);
+		touch($file2);
+		touch($file3);
+		touch($file4);
+
+		ini_set('include_path', $path . PATH_SEPARATOR . $folder1);
+
+		$this->assertEqual(fileExistsInPath('file1.php'), $file1);
+		$this->assertEqual(fileExistsInPath('file2.php'), $file2);
+		$this->assertEqual(fileExistsInPath('folder1' . DS . 'file2.php'), $file2);
+		$this->assertEqual(fileExistsInPath($file2), $file2);
+		$this->assertEqual(fileExistsInPath('file3.php'), $file3);
+		$this->assertEqual(fileExistsInPath($file4), $file4);
+
+		$this->assertFalse(fileExistsInPath('file1'));
+		$this->assertFalse(fileExistsInPath('file4.php'));
+
+		$Folder = new Folder($path);
+		$Folder->delete();
+
+		ini_set('include_path', $_includePath);
+	}
+
+/**
+ * test convertSlash()
+ *
+ * @return void
+ * @access public
+ */
+	function testConvertSlash() {
+		$result = convertSlash('\path\to\location\\');
+		$expected = '\path\to\location\\';
+		$this->assertEqual($result, $expected);
+
+		$result = convertSlash('/path/to/location/');
+		$expected = 'path_to_location';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test debug()
+ *
+ * @return void
+ * @access public
+ */
+	function testDebug() {
+		ob_start();
+			debug('this-is-a-test');
+		$result = ob_get_clean();
+		$pattern = '/.*\>(.+?tests(\/|\\\)cases(\/|\\\)basics\.test\.php|';
+		$pattern .= preg_quote(substr(__FILE__, 1), '/') . ')';
+		$pattern .= '.*line.*' . (__LINE__ - 4) . '.*this-is-a-test.*/s';
+		$this->assertPattern($pattern, $result);
+
+		ob_start();
+			debug('<div>this-is-a-test</div>', true);
+		$result = ob_get_clean();
+		$pattern = '/.*\>(.+?tests(\/|\\\)cases(\/|\\\)basics\.test\.php|';
+		$pattern .= preg_quote(substr(__FILE__, 1), '/') . ')';
+		$pattern .=	'.*line.*' . (__LINE__ - 4) . '.*&lt;div&gt;this-is-a-test&lt;\/div&gt;.*/s';
+		$this->assertPattern($pattern, $result);
+	}
+
+/**
+ * test pr()
+ *
+ * @return void
+ * @access public
+ */
+	function testPr() {
+		ob_start();
+			pr('this is a test');
+		$result = ob_get_clean();
+		$expected = "<pre>this is a test</pre>";
+		$this->assertEqual($result, $expected);
+
+		ob_start();
+			pr(array('this' => 'is', 'a' => 'test'));
+		$result = ob_get_clean();
+		$expected = "<pre>Array\n(\n    [this] => is\n    [a] => test\n)\n</pre>";
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test params()
+ *
+ * @return void
+ * @access public
+ */
+	function testParams() {
+		$this->assertNull(params('weekend'));
+		$this->assertNull(params(array()));
+		$this->assertEqual(params(array('weekend')), array('weekend'));
+
+		$nested = array(array('weekend'));
+		$this->assertEqual(params($nested), array('weekend'));
+
+		$multiple = array(array('weekend'), 'jean-luc', 'godard');
+		$this->assertEqual(params($multiple), $multiple);
+	}
+
+/**
+ * test stripslashes_deep()
+ *
+ * @return void
+ * @access public
+ */
+	function testStripslashesDeep() {
+		$this->skipIf(ini_get('magic_quotes_sybase') === '1', '%s magic_quotes_sybase is on');
+
+		$this->assertEqual(stripslashes_deep("tes\'t"), "tes't");
+		$this->assertEqual(stripslashes_deep('tes\\' . chr(0) .'t'), 'tes' . chr(0) .'t');
+		$this->assertEqual(stripslashes_deep('tes\"t'), 'tes"t');
+		$this->assertEqual(stripslashes_deep("tes\'t"), "tes't");
+		$this->assertEqual(stripslashes_deep('te\\st'), 'test');
+
+		$nested = array(
+			'a' => "tes\'t",
+			'b' => 'tes\\' . chr(0) .'t',
+			'c' => array(
+				'd' => 'tes\"t',
+				'e' => "te\'s\'t",
+				array('f' => "tes\'t")
+				),
+			'g' => 'te\\st'
+			);
+		$expected = array(
+			'a' => "tes't",
+			'b' => 'tes' . chr(0) .'t',
+			'c' => array(
+				'd' => 'tes"t',
+				'e' => "te's't",
+				array('f' => "tes't")
+				),
+			'g' => 'test'
+			);
+		$this->assertEqual(stripslashes_deep($nested), $expected);
+	}
+
+/**
+ * test stripslashes_deep() with magic_quotes_sybase on
+ *
+ * @return void
+ * @access public
+ */
+	function testStripslashesDeepSybase() {
+		$this->skipUnless(ini_get('magic_quotes_sybase') === '1', '%s magic_quotes_sybase is off');
+
+		$this->assertEqual(stripslashes_deep("tes\'t"), "tes\'t");
+
+		$nested = array(
+			'a' => "tes't",
+			'b' => "tes''t",
+			'c' => array(
+				'd' => "tes'''t",
+				'e' => "tes''''t",
+				array('f' => "tes''t")
+				),
+			'g' => "te'''''st"
+			);
+		$expected = array(
+			'a' => "tes't",
+			'b' => "tes't",
+			'c' => array(
+				'd' => "tes''t",
+				'e' => "tes''t",
+				array('f' => "tes't")
+				),
+			'g' => "te'''st"
+			);
+		$this->assertEqual(stripslashes_deep($nested), $expected);
+	}
+
+/**
+ * test ife()
+ *
+ * @return void
+ * @access public
+ */
+	function testIfe() {
+		$this->assertEqual(ife(true, 'a', 'b'), 'a');
+		$this->assertEqual(ife(' ', 'a', 'b'), 'a');
+		$this->assertEqual(ife('test', 'a', 'b'), 'a');
+		$this->assertEqual(ife(23, 'a', 'b'), 'a');
+		$this->assertEqual(ife(array('t' => 'est'), 'a', 'b'), 'a');
+
+		$this->assertEqual(ife(false, 'a', 'b'), 'b');
+		$this->assertEqual(ife(null, 'a', 'b'), 'b');
+		$this->assertEqual(ife('', 'a', 'b'), 'b');
+		$this->assertEqual(ife(0, 'a', 'b'), 'b');
+		$this->assertEqual(ife(array(), 'a', 'b'), 'b');
+	}
+
+/**
+ * test pluginSplit
+ *
+ * @return void
+ */
+	function testPluginSplit() {
+		$result = pluginSplit('Something.else');
+		$this->assertEqual($result, array('Something', 'else'));
+
+		$result = pluginSplit('Something.else.more.dots');
+		$this->assertEqual($result, array('Something', 'else.more.dots'));
+
+		$result = pluginSplit('Somethingelse');
+		$this->assertEqual($result, array(null, 'Somethingelse'));
+
+		$result = pluginSplit('Something.else', true);
+		$this->assertEqual($result, array('Something.', 'else'));
+
+		$result = pluginSplit('Something.else.more.dots', true);
+		$this->assertEqual($result, array('Something.', 'else.more.dots'));
+
+		$result = pluginSplit('Post', false, 'Blog');
+		$this->assertEqual($result, array('Blog', 'Post'));
+
+		$result = pluginSplit('Blog.Post', false, 'Ultimate');
+		$this->assertEqual($result, array('Blog', 'Post'));
+	}
+}

Added: trunk/src/Web/cake/tests/cases/console/cake.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/console/cake.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/console/cake.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,956 @@
+<?php
+/**
+ * ShellDispatcherTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.console
+ * @since         CakePHP(tm) v 1.2.0.5432
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+if (!defined('DISABLE_AUTO_DISPATCH')) {
+	define('DISABLE_AUTO_DISPATCH', true);
+}
+
+if (!class_exists('ShellDispatcher')) {
+	ob_start();
+	$argv = false;
+	require CAKE . 'console' .  DS . 'cake.php';
+	ob_end_clean();
+}
+
+require_once CONSOLE_LIBS . 'shell.php';
+
+/**
+ * TestShellDispatcher class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.console
+ */
+class TestShellDispatcher extends ShellDispatcher {
+
+/**
+ * params property
+ *
+ * @var array
+ * @access public
+ */
+	var $params = array();
+
+/**
+ * stdout property
+ *
+ * @var string
+ * @access public
+ */
+	var $stdout = '';
+
+/**
+ * stderr property
+ *
+ * @var string
+ * @access public
+ */
+	var $stderr = '';
+
+/**
+ * stopped property
+ *
+ * @var string
+ * @access public
+ */
+	var $stopped = null;
+
+/**
+ * TestShell
+ *
+ * @var mixed
+ * @access public
+ */
+	var $TestShell;
+
+/**
+ * _initEnvironment method
+ *
+ * @return void
+ * @access protected
+ */
+	function _initEnvironment() {
+	}
+
+/**
+ * stderr method
+ *
+ * @return void
+ * @access public
+ */
+	function stderr($string) {
+		$this->stderr .= rtrim($string, ' ');
+	}
+
+/**
+ * stdout method
+ *
+ * @return void
+ * @access public
+ */
+	function stdout($string, $newline = true) {
+		if ($newline) {
+			$this->stdout .= rtrim($string, ' ') . "\n";
+		} else {
+			$this->stdout .= rtrim($string, ' ');
+		}
+	}
+
+/**
+ * clear method
+ *
+ * @return void
+ * @access public
+ */
+	function clear() {
+
+	}
+
+/**
+ * _stop method
+ *
+ * @return void
+ * @access protected
+ */
+	function _stop($status = 0) {
+		$this->stopped = 'Stopped with status: ' . $status;
+		return $status;
+	}
+
+/**
+ * getShell
+ *
+ * @param mixed $plugin
+ * @return mixed
+ * @access public
+ */
+	function getShell($plugin = null) {
+		return $this->_getShell($plugin);
+	}
+
+/**
+ * _getShell
+ *
+ * @param mixed $plugin
+ * @return mixed
+ * @access protected
+ */
+	function _getShell($plugin = null) {
+		if (isset($this->TestShell)) {
+			return $this->TestShell;
+		}
+		return parent::_getShell($plugin);
+	}
+}
+
+/**
+ * ShellDispatcherTest
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class ShellDispatcherTest extends CakeTestCase {
+
+/**
+ * setUp method
+ *
+ * @return void
+ * @access public
+ */
+	function setUp() {
+		App::build(array(
+			'plugins' => array(
+				TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS
+			),
+			'shells' => array(
+				CORE_PATH ? CONSOLE_LIBS : ROOT . DS . CONSOLE_LIBS,
+				TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'vendors' . DS . 'shells' . DS
+			)
+		), true);
+	}
+
+/**
+ * tearDown method
+ *
+ * @return void
+ * @access public
+ */
+	function tearDown() {
+		App::build();
+	}
+
+/**
+ * testParseParams method
+ *
+ * @return void
+ * @access public
+ */
+	function testParseParams() {
+		$Dispatcher =& new TestShellDispatcher();
+
+		$params = array(
+			'/cake/1.2.x.x/cake/console/cake.php',
+			'bake',
+			'-app',
+			'new',
+			'-working',
+			'/var/www/htdocs'
+		);
+		$expected = array(
+			'app' => 'new',
+			'webroot' => 'webroot',
+			'working' => '/var/www/htdocs/new',
+			'root' => '/var/www/htdocs'
+		);
+		$Dispatcher->parseParams($params);
+		$this->assertEqual($expected, $Dispatcher->params);
+
+		$params = array('cake.php');
+		$expected = array(
+			'app' => 'app',
+			'webroot' => 'webroot',
+			'working' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH . DS . 'app'),
+			'root' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH),
+		);
+		$Dispatcher->params = $Dispatcher->args = array();
+		$Dispatcher->parseParams($params);
+		$this->assertEqual($expected, $Dispatcher->params);
+
+		$params = array(
+			'cake.php',
+			'-app',
+			'new',
+		);
+		$expected = array(
+			'app' => 'new',
+			'webroot' => 'webroot',
+			'working' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH . DS . 'new'),
+			'root' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH)
+		);
+		$Dispatcher->params = $Dispatcher->args = array();
+		$Dispatcher->parseParams($params);
+		$this->assertEqual($expected, $Dispatcher->params);
+
+		$params = array(
+			'./cake.php',
+			'bake',
+			'-app',
+			'new',
+			'-working',
+			'/cake/1.2.x.x/cake/console'
+		);
+
+		$expected = array(
+			'app' => 'new',
+			'webroot' => 'webroot',
+			'working' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH . DS . 'new'),
+			'root' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH)
+		);
+
+		$Dispatcher->params = $Dispatcher->args = array();
+		$Dispatcher->parseParams($params);
+		$this->assertEqual($expected, $Dispatcher->params);
+
+		$params = array(
+			'./console/cake.php',
+			'bake',
+			'-app',
+			'new',
+			'-working',
+			'/cake/1.2.x.x/cake'
+		);
+		$expected = array(
+			'app' => 'new',
+			'webroot' => 'webroot',
+			'working' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH . DS . 'new'),
+			'root' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH)
+		);
+		$Dispatcher->params = $Dispatcher->args = array();
+		$Dispatcher->parseParams($params);
+		$this->assertEqual($expected, $Dispatcher->params);
+
+		$params = array(
+			'./console/cake.php',
+			'bake',
+			'-app',
+			'new',
+			'-dry',
+			'-working',
+			'/cake/1.2.x.x/cake'
+		);
+		$expected = array(
+			'app' => 'new',
+			'webroot' => 'webroot',
+			'working' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH . DS . 'new'),
+			'root' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH),
+			'dry' => 1
+		);
+		$Dispatcher->params = $Dispatcher->args = array();
+		$Dispatcher->parseParams($params);
+		$this->assertEqual($expected, $Dispatcher->params);
+
+		$params = array(
+			'./console/cake.php',
+			'-working',
+			'/cake/1.2.x.x/cake',
+			'schema',
+			'run',
+			'create',
+			'-dry',
+			'-f',
+			'-name',
+			'DbAcl'
+		);
+		$expected = array(
+			'app' => 'app',
+			'webroot' => 'webroot',
+			'working' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH . DS . 'app'),
+			'root' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH),
+			'dry' => 1,
+			'f' => 1,
+			'name' => 'DbAcl'
+		);
+		$Dispatcher->params = $Dispatcher->args = array();
+		$Dispatcher->parseParams($params);
+		$this->assertEqual($expected, $Dispatcher->params);
+
+		$expected = array('./console/cake.php', 'schema', 'run', 'create');
+		$this->assertEqual($expected, $Dispatcher->args);
+
+		$params = array(
+			'/cake/1.2.x.x/cake/console/cake.php',
+			'-working',
+			'/cake/1.2.x.x/app',
+			'schema',
+			'run',
+			'create',
+			'-dry',
+			'-name',
+			'DbAcl'
+		);
+		$expected = array(
+			'app' => 'app',
+			'webroot' => 'webroot',
+			'working' => '/cake/1.2.x.x/app',
+			'root' => '/cake/1.2.x.x',
+			'dry' => 1,
+			'name' => 'DbAcl'
+		);
+		$Dispatcher->params = $Dispatcher->args = array();
+		$Dispatcher->parseParams($params);
+		$this->assertEqual($expected, $Dispatcher->params);
+
+		$expected = array('/cake/1.2.x.x/cake/console/cake.php', 'schema', 'run', 'create');
+		$this->assertEqual($expected, $Dispatcher->args);
+		$params = array(
+			'cake.php',
+			'-working',
+			'C:/wamp/www/cake/app',
+			'bake',
+			'-app',
+			'C:/wamp/www/apps/cake/app',
+		);
+		$expected = array(
+			'app' => 'app',
+			'webroot' => 'webroot',
+			'working' => 'C:\wamp\www\apps\cake\app',
+			'root' => 'C:\wamp\www\apps\cake'
+		);
+
+		$Dispatcher->params = $Dispatcher->args = array();
+		$Dispatcher->parseParams($params);
+		$this->assertEqual($expected, $Dispatcher->params);
+
+		$params = array(
+			'cake.php',
+			'-working',
+			'C:\wamp\www\cake\app',
+			'bake',
+			'-app',
+			'C:\wamp\www\apps\cake\app',
+		);
+		$expected = array(
+			'app' => 'app',
+			'webroot' => 'webroot',
+			'working' => 'C:\wamp\www\apps\cake\app',
+			'root' => 'C:\wamp\www\apps\cake'
+		);
+		$Dispatcher->params = $Dispatcher->args = array();
+		$Dispatcher->parseParams($params);
+		$this->assertEqual($expected, $Dispatcher->params);
+
+		$params = array(
+			'cake.php',
+			'-working',
+			'C:\wamp\www\apps',
+			'bake',
+			'-app',
+			'cake\app',
+			'-url',
+			'http://example.com/some/url/with/a/path'
+		);
+		$expected = array(
+			'app' => 'app',
+			'webroot' => 'webroot',
+			'working' => 'C:\wamp\www\apps\cake\app',
+			'root' => 'C:\wamp\www\apps\cake',
+			'url' => 'http://example.com/some/url/with/a/path'
+		);
+		$Dispatcher->params = $Dispatcher->args = array();
+		$Dispatcher->parseParams($params);
+		$this->assertEqual($expected, $Dispatcher->params);
+
+		$params = array(
+			'/home/amelo/dev/cake-common/cake/console/cake.php',
+			'-root',
+			'/home/amelo/dev/lsbu-vacancy',
+			'-working',
+			'/home/amelo/dev/lsbu-vacancy',
+			'-app',
+			'app',
+		);
+		$expected = array(
+			'app' => 'app',
+			'webroot' => 'webroot',
+			'working' => '/home/amelo/dev/lsbu-vacancy/app',
+			'root' => '/home/amelo/dev/lsbu-vacancy',
+		);
+		$Dispatcher->params = $Dispatcher->args = array();
+		$Dispatcher->parseParams($params);
+		$this->assertEqual($expected, $Dispatcher->params);
+
+		$params = array(
+			'cake.php',
+			'-working',
+			'D:\www',
+			'bake',
+			'my_app',
+		);
+		$expected = array(
+			'working' => 'D:\www',
+			'app' => 'www',
+			'root' => 'D:',
+			'webroot' => 'webroot'
+		);
+
+		$Dispatcher->params = $Dispatcher->args = array();
+		$Dispatcher->parseParams($params);
+		$this->assertEqual($expected, $Dispatcher->params);
+		
+		$params = array(
+			'cake.php',
+			'-working',
+			'D:\ ',
+			'bake',
+			'my_app',
+		);
+		$expected = array(
+			'working' => '.',
+			'app' => 'D:',
+			'root' => '.',
+			'webroot' => 'webroot'
+		);
+		$Dispatcher->params = $Dispatcher->args = array();
+		$Dispatcher->parseParams($params);
+		$this->assertEqual($expected, $Dispatcher->params);
+	}
+
+/**
+ * testBuildPaths method
+ *
+ * @return void
+ * @access public
+ */
+	function testBuildPaths() {
+		$Dispatcher =& new TestShellDispatcher();
+
+		$result = $Dispatcher->shellPaths;
+
+		$expected = array(
+			TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS . 'test_plugin' . DS . 'vendors' . DS . 'shells' . DS,
+			TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS . 'test_plugin_two' . DS . 'vendors' . DS . 'shells' . DS,
+			APP . 'vendors' . DS . 'shells' . DS,
+			VENDORS . 'shells' . DS,
+			CORE_PATH ? CONSOLE_LIBS : ROOT . DS . CONSOLE_LIBS,
+			TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'vendors' . DS . 'shells' . DS,
+		);
+		$this->assertIdentical(array_diff($result, $expected), array());
+		$this->assertIdentical(array_diff($expected, $result), array());
+	}
+
+/**
+ * Verify loading of (plugin-) shells
+ *
+ * @return void
+ * @access public
+ */
+	function testGetShell() {
+		$this->skipIf(class_exists('SampleShell'), '%s SampleShell Class already loaded');
+		$this->skipIf(class_exists('ExampleShell'), '%s ExampleShell Class already loaded');
+
+		$Dispatcher =& new TestShellDispatcher();
+
+		$Dispatcher->shell = 'sample';
+		$Dispatcher->shellName = 'Sample';
+		$Dispatcher->shellClass = 'SampleShell';
+
+		$result = $Dispatcher->getShell();
+		$this->assertIsA($result, 'SampleShell');
+
+		$Dispatcher =& new TestShellDispatcher();
+
+		$Dispatcher->shell = 'example';
+		$Dispatcher->shellName = 'Example';
+		$Dispatcher->shellClass = 'ExampleShell';
+
+		$result = $Dispatcher->getShell('test_plugin');
+		$this->assertIsA($result, 'ExampleShell');
+	}
+
+/**
+ * Verify correct dispatch of Shell subclasses with a main method
+ *
+ * @return void
+ * @access public
+ */
+	function testDispatchShellWithMain() {
+		Mock::generate('Shell', 'MockWithMainShell', array('main', '_secret'));
+
+		$Dispatcher =& new TestShellDispatcher();
+
+		$Shell = new MockWithMainShell();
+		$Shell->setReturnValue('main', true);
+		$Shell->expectOnce('initialize');
+		$Shell->expectOnce('loadTasks');
+		$Shell->expectOnce('startup');
+		$Shell->expectOnce('main');
+		$Dispatcher->TestShell =& $Shell;
+
+		$Dispatcher->args = array('mock_with_main');
+		$result = $Dispatcher->dispatch();
+		$this->assertTrue($result);
+		$this->assertEqual($Dispatcher->args, array());
+
+		$Shell = new MockWithMainShell();
+		$Shell->setReturnValue('main', true);
+		$Shell->expectOnce('startup');
+		$Shell->expectOnce('main');
+		$Dispatcher->TestShell =& $Shell;
+
+		$Dispatcher->args = array('mock_with_main', 'initdb');
+		$result = $Dispatcher->dispatch();
+		$this->assertTrue($result);
+		$this->assertEqual($Dispatcher->args, array('initdb'));
+
+		$Shell = new MockWithMainShell();
+		$Shell->setReturnValue('main', true);
+		$Shell->expectOnce('startup');
+		$Shell->expectOnce('help');
+		$Dispatcher->TestShell =& $Shell;
+
+		$Dispatcher->args = array('mock_with_main', 'help');
+		$result = $Dispatcher->dispatch();
+		$this->assertNull($result);
+		$this->assertEqual($Dispatcher->args, array());
+
+		$Shell = new MockWithMainShell();
+		$Shell->setReturnValue('main', true);
+		$Shell->expectNever('hr');
+		$Shell->expectOnce('startup');
+		$Shell->expectOnce('main');
+		$Dispatcher->TestShell =& $Shell;
+
+		$Dispatcher->args = array('mock_with_main', 'hr');
+		$result = $Dispatcher->dispatch();
+		$this->assertTrue($result);
+		$this->assertEqual($Dispatcher->args, array('hr'));
+
+		$Shell = new MockWithMainShell();
+		$Shell->setReturnValue('main', true);
+		$Shell->expectOnce('startup');
+		$Shell->expectOnce('main');
+		$Dispatcher->TestShell =& $Shell;
+
+		$Dispatcher->args = array('mock_with_main', 'dispatch');
+		$result = $Dispatcher->dispatch();
+		$this->assertTrue($result);
+		$this->assertEqual($Dispatcher->args, array('dispatch'));
+
+		$Shell = new MockWithMainShell();
+		$Shell->setReturnValue('main', true);
+		$Shell->expectOnce('startup');
+		$Shell->expectOnce('main');
+		$Dispatcher->TestShell =& $Shell;
+
+		$Dispatcher->args = array('mock_with_main', 'idontexist');
+		$result = $Dispatcher->dispatch();
+		$this->assertTrue($result);
+		$this->assertEqual($Dispatcher->args, array('idontexist'));
+
+		$Shell = new MockWithMainShell();
+		$Shell->expectNever('startup');
+		$Shell->expectNever('main');
+		$Shell->expectNever('_secret');
+		$Dispatcher->TestShell =& $Shell;
+
+		$Dispatcher->args = array('mock_with_main', '_secret');
+		$result = $Dispatcher->dispatch();
+		$this->assertFalse($result);
+	}
+
+/**
+ * Verify correct dispatch of Shell subclasses without a main method
+ *
+ * @return void
+ * @access public
+ */
+	function testDispatchShellWithoutMain() {
+		Mock::generate('Shell', 'MockWithoutMainShell', array('initDb', '_secret'));
+
+		$Dispatcher =& new TestShellDispatcher();
+
+		$Shell = new MockWithoutMainShell();
+		$Shell->setReturnValue('initDb', true);
+		$Shell->expectOnce('initialize');
+		$Shell->expectOnce('loadTasks');
+		$Shell->expectNever('startup');
+		$Dispatcher->TestShell =& $Shell;
+
+		$Dispatcher->args = array('mock_without_main');
+		$result = $Dispatcher->dispatch();
+		$this->assertFalse($result);
+		$this->assertEqual($Dispatcher->args, array());
+
+		$Shell = new MockWithoutMainShell();
+		$Shell->setReturnValue('initDb', true);
+		$Shell->expectOnce('startup');
+		$Shell->expectOnce('initDb');
+		$Dispatcher->TestShell =& $Shell;
+
+		$Dispatcher->args = array('mock_without_main', 'initdb');
+		$result = $Dispatcher->dispatch();
+		$this->assertTrue($result);
+		$this->assertEqual($Dispatcher->args, array());
+
+		$Shell = new MockWithoutMainShell();
+		$Shell->setReturnValue('initDb', true);
+		$Shell->expectNever('startup');
+		$Shell->expectNever('hr');
+		$Dispatcher->TestShell =& $Shell;
+
+		$Dispatcher->args = array('mock_without_main', 'hr');
+		$result = $Dispatcher->dispatch();
+		$this->assertFalse($result);
+		$this->assertEqual($Dispatcher->args, array('hr'));
+
+		$Shell = new MockWithoutMainShell();
+		$Shell->setReturnValue('initDb', true);
+		$Shell->expectNever('startup');
+		$Dispatcher->TestShell =& $Shell;
+
+		$Dispatcher->args = array('mock_without_main', 'dispatch');
+		$result = $Dispatcher->dispatch();
+		$this->assertFalse($result);
+
+		$Shell = new MockWithoutMainShell();
+		$Shell->expectNever('startup');
+		$Dispatcher->TestShell =& $Shell;
+
+		$Dispatcher->args = array('mock_without_main', 'idontexist');
+		$result = $Dispatcher->dispatch();
+		$this->assertFalse($result);
+
+		$Shell = new MockWithoutMainShell();
+		$Shell->expectNever('startup');
+		$Shell->expectNever('_secret');
+		$Dispatcher->TestShell =& $Shell;
+
+		$Dispatcher->args = array('mock_without_main', '_secret');
+		$result = $Dispatcher->dispatch();
+		$this->assertFalse($result);
+	}
+
+/**
+ * Verify correct dispatch of custom classes with a main method
+ *
+ * @return void
+ * @access public
+ */
+	function testDispatchNotAShellWithMain() {
+		Mock::generate('Object', 'MockWithMainNotAShell',
+			array('main', 'initialize', 'loadTasks', 'startup', '_secret'));
+
+		$Dispatcher =& new TestShellDispatcher();
+
+		$Shell = new MockWithMainNotAShell();
+		$Shell->setReturnValue('main', true);
+		$Shell->expectNever('initialize');
+		$Shell->expectNever('loadTasks');
+		$Shell->expectOnce('startup');
+		$Shell->expectOnce('main');
+		$Dispatcher->TestShell =& $Shell;
+
+		$Dispatcher->args = array('mock_with_main_not_a');
+		$result = $Dispatcher->dispatch();
+		$this->assertTrue($result);
+		$this->assertEqual($Dispatcher->args, array());
+
+		$Shell = new MockWithMainNotAShell();
+		$Shell->setReturnValue('main', true);
+		$Shell->expectOnce('startup');
+		$Shell->expectOnce('main');
+		$Dispatcher->TestShell =& $Shell;
+
+		$Dispatcher->args = array('mock_with_main_not_a', 'initdb');
+		$result = $Dispatcher->dispatch();
+		$this->assertTrue($result);
+		$this->assertEqual($Dispatcher->args, array('initdb'));
+
+		$Shell = new MockWithMainNotAShell();
+		$Shell->setReturnValue('main', true);
+		$Shell->expectOnce('startup');
+		$Shell->expectOnce('main');
+		$Dispatcher->TestShell =& $Shell;
+
+		$Dispatcher->args = array('mock_with_main_not_a', 'hr');
+		$result = $Dispatcher->dispatch();
+		$this->assertTrue($result);
+		$this->assertEqual($Dispatcher->args, array('hr'));
+
+		$Shell = new MockWithMainNotAShell();
+		$Shell->setReturnValue('main', true);
+		$Shell->expectOnce('startup');
+		$Shell->expectOnce('main');
+		$Dispatcher->TestShell =& $Shell;
+
+		$Dispatcher->args = array('mock_with_main_not_a', 'dispatch');
+		$result = $Dispatcher->dispatch();
+		$this->assertTrue($result);
+		$this->assertEqual($Dispatcher->args, array('dispatch'));
+
+		$Shell = new MockWithMainNotAShell();
+		$Shell->setReturnValue('main', true);
+		$Shell->expectOnce('startup');
+		$Shell->expectOnce('main');
+		$Dispatcher->TestShell =& $Shell;
+
+		$Dispatcher->args = array('mock_with_main_not_a', 'idontexist');
+		$result = $Dispatcher->dispatch();
+		$this->assertTrue($result);
+		$this->assertEqual($Dispatcher->args, array('idontexist'));
+
+		$Shell = new MockWithMainNotAShell();
+		$Shell->expectNever('startup');
+		$Shell->expectNever('main');
+		$Shell->expectNever('_secret');
+		$Dispatcher->TestShell =& $Shell;
+
+		$Dispatcher->args = array('mock_with_main_not_a', '_secret');
+		$result = $Dispatcher->dispatch();
+		$this->assertFalse($result);
+	}
+
+/**
+ * Verify correct dispatch of custom classes without a main method
+ *
+ * @return void
+ * @access public
+ */
+	function testDispatchNotAShellWithoutMain() {
+		Mock::generate('Object', 'MockWithoutMainNotAShell',
+			array('initDb', 'initialize', 'loadTasks', 'startup', '_secret'));
+
+		$Dispatcher =& new TestShellDispatcher();
+
+		$Shell = new MockWithoutMainNotAShell();
+		$Shell->setReturnValue('initDb', true);
+		$Shell->expectNever('initialize');
+		$Shell->expectNever('loadTasks');
+		$Shell->expectNever('startup');
+		$Dispatcher->TestShell =& $Shell;
+
+		$Dispatcher->args = array('mock_without_main_not_a');
+		$result = $Dispatcher->dispatch();
+		$this->assertFalse($result);
+
+		$Shell = new MockWithoutMainNotAShell();
+		$Shell->setReturnValue('initDb', true);
+		$Shell->expectOnce('startup');
+		$Shell->expectOnce('initDb');
+		$Dispatcher->TestShell =& $Shell;
+
+		$Dispatcher->args = array('mock_without_main_not_a', 'initdb');
+		$result = $Dispatcher->dispatch();
+		$this->assertTrue($result);
+		$this->assertEqual($Dispatcher->args, array());
+
+		$Shell = new MockWithoutMainNotAShell();
+		$Shell->setReturnValue('initDb', true);
+		$Shell->expectNever('startup');
+		$Dispatcher->TestShell =& $Shell;
+
+		$Dispatcher->args = array('mock_without_main_not_a', 'hr');
+		$result = $Dispatcher->dispatch();
+		$this->assertFalse($result);
+
+		$Shell = new MockWithoutMainNotAShell();
+		$Shell->setReturnValue('initDb', true);
+		$Shell->expectNever('startup');
+		$Dispatcher->TestShell =& $Shell;
+
+		$Dispatcher->args = array('mock_without_main_not_a', 'dispatch');
+		$result = $Dispatcher->dispatch();
+		$this->assertFalse($result);
+
+		$Shell = new MockWithoutMainNotAShell();
+		$Shell->expectNever('startup');
+		$Dispatcher->TestShell =& $Shell;
+
+		$Dispatcher->args = array('mock_without_main_not_a', 'idontexist');
+		$result = $Dispatcher->dispatch();
+		$this->assertFalse($result);
+
+		$Shell = new MockWithoutMainNotAShell();
+		$Shell->expectNever('startup');
+		$Shell->expectNever('_secret');
+		$Dispatcher->TestShell =& $Shell;
+
+		$Dispatcher->args = array('mock_without_main_not_a', '_secret');
+		$result = $Dispatcher->dispatch();
+		$this->assertFalse($result);
+	}
+
+/**
+ * Verify that a task is called instead of the shell if the first arg equals
+ * the name of the task
+ *
+ * @return void
+ * @access public
+ */
+	function testDispatchTask() {
+		Mock::generate('Shell', 'MockWeekShell', array('main'));
+		Mock::generate('Shell', 'MockOnSundayTask', array('execute'));
+
+		$Dispatcher =& new TestShellDispatcher();
+
+		$Shell = new MockWeekShell();
+		$Shell->expectOnce('initialize');
+		$Shell->expectOnce('loadTasks');
+		$Shell->expectNever('startup');
+		$Shell->expectNever('main');
+
+		$Task = new MockOnSundayTask();
+		$Task->setReturnValue('execute', true);
+		$Task->expectOnce('initialize');
+		$Task->expectOnce('loadTasks');
+		$Task->expectOnce('startup');
+		$Task->expectOnce('execute');
+
+		$Shell->MockOnSunday =& $Task;
+		$Shell->taskNames = array('MockOnSunday');
+		$Dispatcher->TestShell =& $Shell;
+
+		$Dispatcher->args = array('mock_week', 'mock_on_sunday');
+		$result = $Dispatcher->dispatch();
+		$this->assertTrue($result);
+		$this->assertEqual($Dispatcher->args, array());
+
+		$Shell = new MockWeekShell();
+		$Task = new MockOnSundayTask();
+		$Task->expectNever('execute');
+		$Task->expectOnce('help');
+
+		$Shell->MockOnSunday =& $Task;
+		$Shell->taskNames = array('MockOnSunday');
+		$Dispatcher->TestShell =& $Shell;
+
+		$Dispatcher->args = array('mock_week', 'mock_on_sunday', 'help');
+		$result = $Dispatcher->dispatch();
+		$this->assertTrue($result);
+	}
+
+/**
+ * Verify shifting of arguments
+ *
+ * @return void
+ * @access public
+ */
+	function testShiftArgs() {
+		$Dispatcher =& new TestShellDispatcher();
+
+		$Dispatcher->args = array('a', 'b', 'c');
+		$this->assertEqual($Dispatcher->shiftArgs(), 'a');
+		$this->assertIdentical($Dispatcher->args, array('b', 'c'));
+
+		$Dispatcher->args = array('a' => 'b', 'c', 'd');
+		$this->assertEqual($Dispatcher->shiftArgs(), 'b');
+		$this->assertIdentical($Dispatcher->args, array('c', 'd'));
+
+		$Dispatcher->args = array('a', 'b' => 'c', 'd');
+		$this->assertEqual($Dispatcher->shiftArgs(), 'a');
+		$this->assertIdentical($Dispatcher->args, array('b' => 'c', 'd'));
+
+		$Dispatcher->args = array(0 => 'a',  2 => 'b', 30 => 'c');
+		$this->assertEqual($Dispatcher->shiftArgs(), 'a');
+		$this->assertIdentical($Dispatcher->args, array(0 => 'b', 1 => 'c'));
+
+		$Dispatcher->args = array();
+		$this->assertNull($Dispatcher->shiftArgs());
+		$this->assertIdentical($Dispatcher->args, array());
+	}
+
+/**
+ * testHelpCommand method
+ *
+ * @return void
+ * @access public
+ */
+	function testHelpCommand() {
+		$Dispatcher =& new TestShellDispatcher();
+
+		$expected = "/example \[.*TestPlugin, TestPluginTwo.*\]/";
+	 	$this->assertPattern($expected, $Dispatcher->stdout);
+
+		$expected = "/welcome \[.*TestPluginTwo.*\]/";
+	 	$this->assertPattern($expected, $Dispatcher->stdout);
+
+		$expected = "/acl \[.*CORE.*\]/";
+	 	$this->assertPattern($expected, $Dispatcher->stdout);
+
+		$expected = "/api \[.*CORE.*\]/";
+	 	$this->assertPattern($expected, $Dispatcher->stdout);
+
+		$expected = "/bake \[.*CORE.*\]/";
+	 	$this->assertPattern($expected, $Dispatcher->stdout);
+
+		$expected = "/console \[.*CORE.*\]/";
+	 	$this->assertPattern($expected, $Dispatcher->stdout);
+
+		$expected = "/i18n \[.*CORE.*\]/";
+	 	$this->assertPattern($expected, $Dispatcher->stdout);
+
+		$expected = "/schema \[.*CORE.*\]/";
+	 	$this->assertPattern($expected, $Dispatcher->stdout);
+
+		$expected = "/testsuite \[.*CORE.*\]/";
+	 	$this->assertPattern($expected, $Dispatcher->stdout);
+
+		$expected = "/sample \[.*test_app.*\]/";
+		$this->assertPattern($expected, $Dispatcher->stdout);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/console/libs/acl.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/console/libs/acl.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/console/libs/acl.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,346 @@
+<?php
+/**
+ * AclShell Test file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP :  Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://cakephp.org CakePHP Project
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.tasks
+ * @since         CakePHP v 1.2.0.7726
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Shell', 'Shell', false);
+
+if (!defined('DISABLE_AUTO_DISPATCH')) {
+	define('DISABLE_AUTO_DISPATCH', true);
+}
+
+if (!class_exists('ShellDispatcher')) {
+	ob_start();
+	$argv = false;
+	require CAKE . 'console' .  DS . 'cake.php';
+	ob_end_clean();
+}
+
+if (!class_exists('AclShell')) {
+	require CAKE . 'console' .  DS . 'libs' . DS . 'acl.php';
+}
+
+Mock::generatePartial(
+	'ShellDispatcher', 'TestAclShellMockShellDispatcher',
+	array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'dispatch')
+);
+Mock::generatePartial(
+	'AclShell', 'MockAclShell',
+	array('in', 'out', 'hr', 'createFile', 'error', 'err')
+);
+
+Mock::generate('AclComponent', 'MockAclShellAclComponent');
+
+/**
+ * AclShellTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.tasks
+ */
+class AclShellTest extends CakeTestCase {
+
+/**
+ * Fixtures
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array('core.aco', 'core.aro', 'core.aros_aco');
+
+/**
+ * configure Configure for testcase
+ *
+ * @return void
+ * @access public
+ */
+	function startCase() {
+		$this->_aclDb = Configure::read('Acl.database');
+		$this->_aclClass = Configure::read('Acl.classname');
+
+		Configure::write('Acl.database', 'test_suite');
+		Configure::write('Acl.classname', 'DbAcl');
+	}
+
+/**
+ * restore Environment settings
+ *
+ * @return void
+ * @access public
+ */
+	function endCase() {
+		Configure::write('Acl.database', $this->_aclDb);
+		Configure::write('Acl.classname', $this->_aclClass);
+	}
+
+/**
+ * startTest method
+ *
+ * @return void
+ * @access public
+ */
+	function startTest() {
+		$this->Dispatcher =& new TestAclShellMockShellDispatcher();
+		$this->Task =& new MockAclShell($this->Dispatcher);
+		$this->Task->Dispatch =& $this->Dispatcher;
+		$this->Task->params['datasource'] = 'test_suite';
+		$this->Task->Acl =& new AclComponent();
+		$controller = null;
+		$this->Task->Acl->startup($controller);
+	}
+
+/**
+ * endTest method
+ *
+ * @return void
+ * @access public
+ */
+	function endTest() {
+		ClassRegistry::flush();
+	}
+
+/**
+ * test that model.foreign_key output works when looking at acl rows
+ *
+ * @return void
+ * @access public
+ */
+	function testViewWithModelForeignKeyOutput() {
+		$this->Task->command = 'view';
+		$this->Task->startup();
+		$data = array(
+			'parent_id' => null,
+			'model' => 'MyModel',
+			'foreign_key' => 2,
+		);
+		$this->Task->Acl->Aro->create($data);
+		$this->Task->Acl->Aro->save();
+		$this->Task->args[0] = 'aro';
+
+		$this->Task->expectAt(0, 'out', array('Aro tree:'));
+		$this->Task->expectAt(1, 'out', array(new PatternExpectation('/\[1\] ROOT/')));
+		$this->Task->expectAt(3, 'out', array(new PatternExpectation('/\[3\] Gandalf/')));
+		$this->Task->expectAt(5, 'out', array(new PatternExpectation('/\[5\] MyModel.2/')));
+
+		$this->Task->view();
+	}
+
+/**
+ * test view with an argument
+ *
+ * @return void
+ * @access public
+ */
+	function testViewWithArgument() {
+		$this->Task->args = array('aro', 'admins');
+		$this->Task->expectAt(0, 'out', array('Aro tree:'));
+		$this->Task->expectAt(1, 'out', array('  [2] admins'));
+		$this->Task->expectAt(2, 'out', array('    [3] Gandalf'));
+		$this->Task->expectAt(3, 'out', array('    [4] Elrond'));
+		$this->Task->view();
+	}
+
+/**
+ * test the method that splits model.foreign key. and that it returns an array.
+ *
+ * @return void
+ * @access public
+ */
+	function testParsingModelAndForeignKey() {
+		$result = $this->Task->parseIdentifier('Model.foreignKey');
+		$expected = array('model' => 'Model', 'foreign_key' => 'foreignKey');
+
+		$result = $this->Task->parseIdentifier('mySuperUser');
+		$this->assertEqual($result, 'mySuperUser');
+
+		$result = $this->Task->parseIdentifier('111234');
+		$this->assertEqual($result, '111234');
+	}
+
+/**
+ * test creating aro/aco nodes
+ *
+ * @return void
+ * @access public
+ */
+	function testCreate() {
+		$this->Task->args = array('aro', 'root', 'User.1');
+		$this->Task->expectAt(0, 'out', array(new PatternExpectation('/created/'), '*'));
+		$this->Task->create();
+
+		$Aro =& ClassRegistry::init('Aro');
+		$Aro->cacheQueries = false;
+		$result = $Aro->read();
+		$this->assertEqual($result['Aro']['model'], 'User');
+		$this->assertEqual($result['Aro']['foreign_key'], 1);
+		$this->assertEqual($result['Aro']['parent_id'], null);
+		$id = $result['Aro']['id'];
+
+		$this->Task->args = array('aro', 'User.1', 'User.3');
+		$this->Task->expectAt(1, 'out', array(new PatternExpectation('/created/'), '*'));
+		$this->Task->create();
+
+		$Aro =& ClassRegistry::init('Aro');
+		$result = $Aro->read();
+		$this->assertEqual($result['Aro']['model'], 'User');
+		$this->assertEqual($result['Aro']['foreign_key'], 3);
+		$this->assertEqual($result['Aro']['parent_id'], $id);
+
+		$this->Task->args = array('aro', 'root', 'somealias');
+		$this->Task->expectAt(2, 'out', array(new PatternExpectation('/created/'), '*'));
+		$this->Task->create();
+
+		$Aro =& ClassRegistry::init('Aro');
+		$result = $Aro->read();
+		$this->assertEqual($result['Aro']['alias'], 'somealias');
+		$this->assertEqual($result['Aro']['model'], null);
+		$this->assertEqual($result['Aro']['foreign_key'], null);
+		$this->assertEqual($result['Aro']['parent_id'], null);
+	}
+
+/**
+ * test the delete method with different node types.
+ *
+ * @return void
+ * @access public
+ */
+	function testDelete() {
+		$this->Task->args = array('aro', 'AuthUser.1');
+		$this->Task->expectAt(0, 'out', array(new NoPatternExpectation('/not/'), true));
+		$this->Task->delete();
+
+		$Aro =& ClassRegistry::init('Aro');
+		$result = $Aro->read(null, 3);
+		$this->assertFalse($result);
+	}
+
+/**
+ * test setParent method.
+ *
+ * @return void
+ * @access public
+ */
+	function testSetParent() {
+		$this->Task->args = array('aro', 'AuthUser.2', 'root');
+		$this->Task->setParent();
+
+		$Aro =& ClassRegistry::init('Aro');
+		$result = $Aro->read(null, 4);
+		$this->assertEqual($result['Aro']['parent_id'], null);
+	}
+
+/**
+ * test grant
+ *
+ * @return void
+ * @access public
+ */
+	function testGrant() {
+		$this->Task->args = array('AuthUser.2', 'ROOT/Controller1', 'create');
+		$this->Task->expectAt(0, 'out', array(new PatternExpectation('/Permission granted/'), true));
+		$this->Task->grant();
+
+		$node = $this->Task->Acl->Aro->read(null, 4);
+		$this->assertFalse(empty($node['Aco'][0]));
+		$this->assertEqual($node['Aco'][0]['Permission']['_create'], 1);
+	}
+
+/**
+ * test deny
+ *
+ * @return void
+ * @access public
+ */
+	function testDeny() {
+		$this->Task->args = array('AuthUser.2', 'ROOT/Controller1', 'create');
+		$this->Task->expectAt(0, 'out', array(new PatternExpectation('/Permission denied/'), true));
+		$this->Task->deny();
+
+		$node = $this->Task->Acl->Aro->read(null, 4);
+		$this->assertFalse(empty($node['Aco'][0]));
+		$this->assertEqual($node['Aco'][0]['Permission']['_create'], -1);
+	}
+
+/**
+ * test checking allowed and denied perms
+ *
+ * @return void
+ * @access public
+ */
+	function testCheck() {
+		$this->Task->args = array('AuthUser.2', 'ROOT/Controller1', '*');
+		$this->Task->expectAt(0, 'out', array(new PatternExpectation('/not allowed/'), true));
+		$this->Task->check();
+
+		$this->Task->args = array('AuthUser.2', 'ROOT/Controller1', 'create');
+		$this->Task->expectAt(1, 'out', array(new PatternExpectation('/Permission granted/'), true));
+		$this->Task->grant();
+
+		$this->Task->args = array('AuthUser.2', 'ROOT/Controller1', 'create');
+		$this->Task->expectAt(2, 'out', array(new PatternExpectation('/is allowed/'), true));
+		$this->Task->check();
+
+		$this->Task->args = array('AuthUser.2', 'ROOT/Controller1', '*');
+		$this->Task->expectAt(3, 'out', array(new PatternExpectation('/not allowed/'), true));
+		$this->Task->check();
+	}
+
+/**
+ * test inherit and that it 0's the permission fields.
+ *
+ * @return void
+ * @access public
+ */
+	function testInherit() {
+		$this->Task->args = array('AuthUser.2', 'ROOT/Controller1', 'create');
+		$this->Task->expectAt(0, 'out', array(new PatternExpectation('/Permission granted/'), true));
+		$this->Task->grant();
+
+		$this->Task->args = array('AuthUser.2', 'ROOT/Controller1', 'all');
+		$this->Task->expectAt(1, 'out', array(new PatternExpectation('/permission inherited/i'), true));
+		$this->Task->inherit();
+
+		$node = $this->Task->Acl->Aro->read(null, 4);
+		$this->assertFalse(empty($node['Aco'][0]));
+		$this->assertEqual($node['Aco'][0]['Permission']['_create'], 0);
+	}
+
+/**
+ * test getting the path for an aro/aco
+ *
+ * @return void
+ * @access public
+ */
+	function testGetPath() {
+		$this->Task->args = array('aro', 'AuthUser.2');
+		$this->Task->expectAt(1, 'out', array('[1] ROOT'));
+		$this->Task->expectAt(2, 'out', array('  [2] admins'));
+		$this->Task->expectAt(3, 'out', array('    [4] Elrond'));
+		$this->Task->getPath();
+	}
+
+/**
+ * test that initdb makes the correct call.
+ *
+ * @return void
+ */
+	function testInitDb() {
+		$this->Task->Dispatch->expectOnce('dispatch');
+		$this->Task->initdb();
+
+		$this->assertEqual($this->Task->Dispatch->args, array('schema', 'create', 'DbAcl'));
+	}
+}

Added: trunk/src/Web/cake/tests/cases/console/libs/api.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/console/libs/api.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/console/libs/api.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,116 @@
+<?php
+/**
+ * ApiShellTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP :  Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://cakephp.org CakePHP Project
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.tasks
+ * @since         CakePHP v 1.2.0.7726
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Shell', 'Shell', false);
+
+if (!defined('DISABLE_AUTO_DISPATCH')) {
+	define('DISABLE_AUTO_DISPATCH', true);
+}
+
+if (!class_exists('ShellDispatcher')) {
+	ob_start();
+	$argv = false;
+	require CAKE . 'console' .  DS . 'cake.php';
+	ob_end_clean();
+}
+
+if (!class_exists('ApiShell')) {
+	require CAKE . 'console' .  DS . 'libs' . DS . 'api.php';
+}
+
+Mock::generatePartial(
+	'ShellDispatcher', 'ApiShellMockShellDispatcher',
+	array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment')
+);
+Mock::generatePartial(
+	'ApiShell', 'MockApiShell',
+	array('in', 'out', 'createFile', 'hr', '_stop')
+);
+
+/**
+ * ApiShellTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.tasks
+ */
+class ApiShellTest extends CakeTestCase {
+
+/**
+ * setUp method
+ *
+ * @return void
+ * @access public
+ */
+	function startTest() {
+		$this->Dispatcher =& new ApiShellMockShellDispatcher();
+		$this->Shell =& new MockApiShell($this->Dispatcher);
+		$this->Shell->Dispatch =& $this->Dispatcher;
+	}
+
+/**
+ * tearDown method
+ *
+ * @return void
+ * @access public
+ */
+	function endTest() {
+		ClassRegistry::flush();
+	}
+
+/**
+ * Test that method names are detected properly including those with no arguments.
+ *
+ * @return void
+ * @access public
+ */
+	function testMethodNameDetection () {
+		$this->Shell->setReturnValueAt(0, 'in', 'q');
+		$this->Shell->expectAt(0, 'out', array('Controller'));
+		$expected = array(
+			array(
+				'1. afterFilter()',
+				'2. beforeFilter()',
+				'3. beforeRender()',
+				'4. constructClasses()',
+				'5. disableCache()',
+				'6. flash($message, $url, $pause = 1, $layout = \'flash\')',
+				'7. header($status)',
+				'8. httpCodes($code = null)',
+				'9. isAuthorized()',
+				'10. loadModel($modelClass = null, $id = null)',
+				'11. paginate($object = null, $scope = array(), $whitelist = array())',
+				'12. postConditions($data = array(), $op = null, $bool = \'AND\', $exclusive = false)',
+				'13. redirect($url, $status = null, $exit = true)',
+				'14. referer($default = null, $local = false)',
+				'15. render($action = null, $layout = null, $file = null)',
+				'16. set($one, $two = null)',
+				'17. setAction($action)',
+				'18. shutdownProcess()', 
+				'19. startupProcess()',
+				'20. validate()',
+				'21. validateErrors()'
+			)
+		);
+		$this->Shell->expectAt(1, 'out', $expected);
+
+		$this->Shell->args = array('controller');
+		$this->Shell->paths['controller'] = CAKE_CORE_INCLUDE_PATH . DS . LIBS . 'controller' . DS;
+		$this->Shell->main();
+	}
+}

Added: trunk/src/Web/cake/tests/cases/console/libs/bake.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/console/libs/bake.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/console/libs/bake.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,130 @@
+<?php
+/**
+ * BakeShell Test Case
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.tasks
+ * @since         CakePHP(tm) v 1.3
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Shell', 'Shell', false);
+
+if (!defined('DISABLE_AUTO_DISPATCH')) {
+	define('DISABLE_AUTO_DISPATCH', true);
+}
+
+if (!class_exists('ShellDispatcher')) {
+	ob_start();
+	$argv = false;
+	require CAKE . 'console' .  DS . 'cake.php';
+	ob_end_clean();
+}
+
+require_once CAKE . 'console' .  DS . 'libs' . DS . 'bake.php';
+require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'model.php';
+require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'controller.php';
+require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'db_config.php';
+
+Mock::generatePartial(
+	'ShellDispatcher', 'BakeShellMockShellDispatcher',
+	array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment')
+);
+Mock::generatePartial(
+	'BakeShell', 'MockBakeShell',
+	array('in', 'hr', 'out', 'err', 'createFile', '_stop', '_checkUnitTest')
+);
+
+Mock::generate('DbConfigTask', 'BakeShellMockDbConfigTask');
+Mock::generate('ModelTask', 'BakeShellMockModelTask');
+Mock::generate('ControllerTask', 'BakeShellMockControllerTask');
+
+if (!class_exists('UsersController')) {
+	class UsersController extends Controller {
+		var $name = 'Users';
+	}
+}
+
+class BakeShellTestCase extends CakeTestCase {
+
+/**
+ * fixtures
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array('core.user');
+
+/**
+ * start test
+ *
+ * @return void
+ * @access public
+ */
+	function startTest() {
+		$this->Dispatch =& new BakeShellMockShellDispatcher();
+		$this->Shell =& new MockBakeShell();
+		$this->Shell->Dispatch =& $this->Dispatch;
+		$this->Shell->Dispatch->shellPaths = App::path('shells');
+	}
+
+/**
+ * endTest method
+ *
+ * @return void
+ * @access public
+ */
+	function endTest() {
+		unset($this->Dispatch, $this->Shell);
+	}
+
+/**
+ * test bake all
+ *
+ * @return void
+ * @access public
+ */
+	function testAllWithModelName() {
+		App::import('Model', 'User');
+		$userExists = class_exists('User');
+		if ($this->skipIf($userExists, 'User class exists, cannot test `bake all [param]`. %s')) {
+			return;
+		}
+		$this->Shell->Model =& new BakeShellMockModelTask();
+		$this->Shell->Controller =& new BakeShellMockControllerTask();
+		$this->Shell->View =& new BakeShellMockModelTask();
+		$this->Shell->DbConfig =& new BakeShellMockDbConfigTask();
+
+		$this->Shell->DbConfig->expectOnce('getConfig');
+		$this->Shell->DbConfig->setReturnValue('getConfig', 'test_suite');
+
+		$this->Shell->Model->setReturnValue('bake', true);
+		$this->Shell->Model->expectNever('getName');
+		$this->Shell->Model->expectOnce('bake');
+
+		$this->Shell->Controller->expectOnce('bake');
+		$this->Shell->Controller->setReturnValue('bake', true);
+
+		$this->Shell->View->expectOnce('execute');
+
+		$this->Shell->expectAt(0, 'out', array('Bake All'));
+		$this->Shell->expectAt(1, 'out', array('User Model was baked.'));
+		$this->Shell->expectAt(2, 'out', array('User Controller was baked.'));
+		$this->Shell->expectAt(3, 'out', array('User Views were baked.'));
+		$this->Shell->expectAt(4, 'out', array('Bake All complete'));
+
+		$this->Shell->params = array();
+		$this->Shell->args = array('User');
+		$this->Shell->all();
+	}
+}

Added: trunk/src/Web/cake/tests/cases/console/libs/schema.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/console/libs/schema.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/console/libs/schema.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,503 @@
+<?php
+/**
+ * SchemaShellTest Test file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://cakephp.org CakePHP Project
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.Shells
+ * @since         CakePHP v 1.3
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Shell', 'Shell', false);
+App::import('Model', 'CakeSchema', false);
+
+if (!defined('DISABLE_AUTO_DISPATCH')) {
+	define('DISABLE_AUTO_DISPATCH', true);
+}
+
+if (!class_exists('ShellDispatcher')) {
+	ob_start();
+	$argv = false;
+	require CAKE . 'console' .  DS . 'cake.php';
+	ob_end_clean();
+}
+
+if (!class_exists('SchemaShell')) {
+	require CAKE . 'console' .  DS . 'libs' . DS . 'schema.php';
+}
+
+Mock::generatePartial(
+	'ShellDispatcher', 'TestSchemaShellMockShellDispatcher',
+	array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment')
+);
+Mock::generatePartial(
+	'SchemaShell', 'MockSchemaShell',
+	array('in', 'out', 'hr', 'createFile', 'error', 'err', '_stop')
+);
+
+Mock::generate('CakeSchema', 'MockSchemaCakeSchema');
+
+/**
+ * Test for Schema database management
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class SchemaShellTestSchema extends CakeSchema {
+
+/**
+ * name property
+ *
+ * @var string 'MyApp'
+ * @access public
+ */
+	var $name = 'SchemaShellTest';
+
+/**
+ * connection property
+ *
+ * @var string 'test_suite'
+ * @access public
+ */
+	var $connection = 'test_suite';
+
+/**
+ * comments property
+ *
+ * @var array
+ * @access public
+ */
+	var $comments = array(
+		'id' => array('type' => 'integer', 'null' => false, 'default' => 0, 'key' => 'primary'),
+		'post_id' => array('type' => 'integer', 'null' => false, 'default' => 0),
+		'user_id' => array('type' => 'integer', 'null' => false),
+		'title' => array('type' => 'string', 'null' => false, 'length' => 100),
+		'comment' => array('type' => 'text', 'null' => false, 'default' => null),
+		'published' => array('type' => 'string', 'null' => true, 'default' => 'N', 'length' => 1),
+		'created' => array('type' => 'datetime', 'null' => true, 'default' => null),
+		'updated' => array('type' => 'datetime', 'null' => true, 'default' => null),
+		'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => true)),
+	);
+
+/**
+ * posts property
+ *
+ * @var array
+ * @access public
+ */
+	var $articles = array(
+		'id' => array('type' => 'integer', 'null' => false, 'default' => 0, 'key' => 'primary'),
+		'user_id' => array('type' => 'integer', 'null' => true, 'default' => ''),
+		'title' => array('type' => 'string', 'null' => false, 'default' => 'Title'),
+		'body' => array('type' => 'text', 'null' => true, 'default' => null),
+		'summary' => array('type' => 'text', 'null' => true),
+		'published' => array('type' => 'string', 'null' => true, 'default' => 'Y', 'length' => 1),
+		'created' => array('type' => 'datetime', 'null' => true, 'default' => null),
+		'updated' => array('type' => 'datetime', 'null' => true, 'default' => null),
+		'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => true)),
+	);
+}
+
+/**
+ * SchemaShellTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.Shells
+ */
+class SchemaShellTest extends CakeTestCase {
+
+/**
+ * Fixtures
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array('core.article', 'core.user', 'core.post', 'core.auth_user', 'core.author', 
+		'core.comment', 'core.test_plugin_comment'
+	);
+
+/**
+ * startTest method
+ *
+ * @return void
+ * @access public
+ */
+	function startTest() {
+		$this->Dispatcher =& new TestSchemaShellMockShellDispatcher();
+		$this->Shell =& new MockSchemaShell($this->Dispatcher);
+		$this->Shell->Dispatch =& $this->Dispatcher;
+	}
+
+/**
+ * endTest method
+ *
+ * @return void
+ * @access public
+ */
+	function endTest() {
+		ClassRegistry::flush();
+	}
+
+/**
+ * test startup method
+ *
+ * @return void
+ * @access public
+ */
+	function testStartup() {
+		$this->Shell->startup();
+		$this->assertTrue(isset($this->Shell->Schema));
+		$this->assertTrue(is_a($this->Shell->Schema, 'CakeSchema'));
+		$this->assertEqual(strtolower($this->Shell->Schema->name), strtolower(APP_DIR));
+		$this->assertEqual($this->Shell->Schema->file, 'schema.php');
+
+		unset($this->Shell->Schema);
+		$this->Shell->params = array(
+			'name' => 'TestSchema'
+		);
+		$this->Shell->startup();
+		$this->assertEqual($this->Shell->Schema->name, 'TestSchema');
+		$this->assertEqual($this->Shell->Schema->file, 'test_schema.php');
+		$this->assertEqual($this->Shell->Schema->connection, 'default');
+		$this->assertEqual($this->Shell->Schema->path, APP . 'config' . DS . 'schema');
+
+		unset($this->Shell->Schema);
+		$this->Shell->params = array(
+			'file' => 'other_file.php',
+			'connection' => 'test_suite',
+			'path' => '/test/path'
+		);
+		$this->Shell->startup();
+		$this->assertEqual(strtolower($this->Shell->Schema->name), strtolower(APP_DIR));
+		$this->assertEqual($this->Shell->Schema->file, 'other_file.php');
+		$this->assertEqual($this->Shell->Schema->connection, 'test_suite');
+		$this->assertEqual($this->Shell->Schema->path, '/test/path');
+	}
+
+/**
+ * Test View - and that it dumps the schema file to stdout
+ *
+ * @return void
+ * @access public
+ */
+	function testView() {
+		$this->Shell->startup();
+		$this->Shell->Schema->path = APP . 'config' . DS . 'schema';
+		$this->Shell->params['file'] = 'i18n.php';
+		$this->Shell->expectOnce('_stop');
+		$this->Shell->expectOnce('out');
+		$this->Shell->view();
+	}
+
+/**
+ * test that view() can find plugin schema files.
+ *
+ * @return void
+ * @access public
+ */
+	function testViewWithPlugins() {
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)
+		));
+		$this->Shell->args = array('TestPlugin.schema');
+		$this->Shell->startup();
+		$this->Shell->expectCallCount('_stop', 2);
+		$this->Shell->expectCallCount('out', 2);
+		$this->Shell->view();
+
+		$this->Shell->args = array();
+		$this->Shell->params = array('plugin' => 'TestPlugin');
+		$this->Shell->startup();
+		$this->Shell->view();
+
+		App::build();
+	}
+
+/**
+ * test dump() with sql file generation
+ *
+ * @return void
+ * @access public
+ */
+	function testDumpWithFileWriting() {
+		$this->Shell->params = array(
+			'name' => 'i18n',
+			'write' => TMP . 'tests' . DS . 'i18n.sql'
+		);
+		$this->Shell->expectOnce('_stop');
+		$this->Shell->startup();
+		$this->Shell->dump();
+
+		$sql =& new File(TMP . 'tests' . DS . 'i18n.sql');
+		$contents = $sql->read();
+		$this->assertPattern('/DROP TABLE/', $contents);
+		$this->assertPattern('/CREATE TABLE `i18n`/', $contents);
+		$this->assertPattern('/id/', $contents);
+		$this->assertPattern('/model/', $contents);
+		$this->assertPattern('/field/', $contents);
+		$this->assertPattern('/locale/', $contents);
+		$this->assertPattern('/foreign_key/', $contents);
+		$this->assertPattern('/content/', $contents);
+
+		$sql->delete();
+	}
+
+/**
+ * test that dump() can find and work with plugin schema files.
+ *
+ * @return void
+ * @access public
+ */
+	function testDumpFileWritingWithPlugins() {
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)
+		));
+		$this->Shell->args = array('TestPlugin.TestPluginApp');
+		$this->Shell->params = array(
+			'connection' => 'test_suite',
+			'write' => TMP . 'tests' . DS . 'dump_test.sql'
+		);
+		$this->Shell->startup();
+		$this->Shell->expectOnce('_stop');
+		$this->Shell->dump();
+
+		$file =& new File(TMP . 'tests' . DS . 'dump_test.sql');
+		$contents = $file->read();
+
+		$this->assertPattern('/CREATE TABLE `acos`/', $contents);
+		$this->assertPattern('/id/', $contents);
+		$this->assertPattern('/model/', $contents);
+
+		$file->delete();
+		App::build();
+	}
+
+/**
+ * test generate with snapshot generation
+ *
+ * @return void
+ * @access public
+ */
+	function testGenerateSnaphot() {
+		$this->Shell->path = TMP;
+		$this->Shell->params['file'] = 'schema.php';
+		$this->Shell->args = array('snapshot');
+		$this->Shell->Schema =& new MockSchemaCakeSchema();
+		$this->Shell->Schema->setReturnValue('read', array('schema data'));
+		$this->Shell->Schema->setReturnValue('write', true);
+
+		$this->Shell->Schema->expectOnce('read');
+		$this->Shell->Schema->expectOnce('write', array(array('schema data', 'file' => 'schema_1.php')));
+
+		$this->Shell->generate();
+	}
+
+/**
+ * test generate without a snapshot.
+ *
+ * @return void
+ * @access public
+ */
+	function testGenerateNoOverwrite() {
+		touch(TMP . 'schema.php');
+		$this->Shell->params['file'] = 'schema.php';
+		$this->Shell->args = array();
+
+		$this->Shell->setReturnValue('in', 'q');
+		$this->Shell->Schema =& new MockSchemaCakeSchema();
+		$this->Shell->Schema->path = TMP;
+		$this->Shell->Schema->expectNever('read');
+
+		$result = $this->Shell->generate();
+		unlink(TMP . 'schema.php');
+	}
+
+/**
+ * test generate with overwriting of the schema files.
+ *
+ * @return void
+ * @access public
+ */
+	function testGenerateOverwrite() {
+		touch(TMP . 'schema.php');
+		$this->Shell->params['file'] = 'schema.php';
+		$this->Shell->args = array();
+
+		$this->Shell->setReturnValue('in', 'o');
+		$this->Shell->expectAt(1, 'out', array(new PatternExpectation('/Schema file:\s[a-z\.]+\sgenerated/')));
+		$this->Shell->Schema =& new MockSchemaCakeSchema();
+		$this->Shell->Schema->path = TMP;
+		$this->Shell->Schema->setReturnValue('read', array('schema data'));
+		$this->Shell->Schema->setReturnValue('write', true);
+
+		$this->Shell->Schema->expectOnce('read');
+		$this->Shell->Schema->expectOnce('write', array(array('schema data', 'file' => 'schema.php')));
+
+		$this->Shell->generate();
+		unlink(TMP . 'schema.php');
+	}
+
+/**
+ * test that generate() can read plugin dirs and generate schema files for the models
+ * in a plugin.
+ *
+ * @return void
+ * @access public
+ */
+	function testGenerateWithPlugins() {
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)
+		));
+		$this->Shell->params = array(
+			'plugin' => 'TestPlugin',
+			'connection' => 'test_suite'
+		);
+		$this->Shell->startup();
+		$this->Shell->Schema->path = TMP . 'tests' . DS;
+
+		$this->Shell->generate();
+		$file =& new File(TMP . 'tests' . DS . 'schema.php');
+		$contents = $file->read();
+
+		$this->assertPattern('/class TestPluginSchema/', $contents);
+		$this->assertPattern('/var \$posts/', $contents);
+		$this->assertPattern('/var \$auth_users/', $contents);
+		$this->assertPattern('/var \$authors/', $contents);
+		$this->assertPattern('/var \$test_plugin_comments/', $contents);
+		$this->assertNoPattern('/var \$users/', $contents);
+		$this->assertNoPattern('/var \$articles/', $contents);
+
+		$file->delete();
+		App::build();
+	}
+
+/**
+ * Test schema run create with no table args.
+ *
+ * @return void
+ * @access public
+ */
+	function testCreateNoArgs() {
+		$this->Shell->params = array(
+			'connection' => 'test_suite',
+			'path' => APP . 'config' . DS . 'sql'
+		);
+		$this->Shell->args = array('i18n');
+		$this->Shell->startup();
+		$this->Shell->setReturnValue('in', 'y');
+		$this->Shell->create();
+
+		$db =& ConnectionManager::getDataSource('test_suite');
+		$sources = $db->listSources();
+		$this->assertTrue(in_array($db->config['prefix'] . 'i18n', $sources));
+
+		$schema =& new i18nSchema();
+		$db->execute($db->dropSchema($schema));
+	}
+
+/**
+ * Test schema run create with no table args.
+ *
+ * @return void
+ * @access public
+ */
+	function testCreateWithTableArgs() {
+		$this->Shell->params = array(
+			'connection' => 'test_suite',
+			'name' => 'DbAcl',
+			'path' => APP . 'config' . DS . 'schema'
+		);
+		$this->Shell->args = array('DbAcl', 'acos');
+		$this->Shell->startup();
+		$this->Shell->setReturnValue('in', 'y');
+		$this->Shell->create();
+
+		$db =& ConnectionManager::getDataSource('test_suite');
+		$sources = $db->listSources();
+		$this->assertTrue(in_array($db->config['prefix'] . 'acos', $sources));
+		$this->assertFalse(in_array($db->config['prefix'] . 'aros', $sources));
+		$this->assertFalse(in_array('aros_acos', $sources));
+
+		$db->execute('DROP TABLE ' . $db->config['prefix'] . 'acos');
+	}
+
+/**
+ * test run update with a table arg.
+ *
+ * @return void
+ * @access public
+ */
+	function testUpdateWithTable() {
+		$this->Shell->params = array(
+			'connection' => 'test_suite',
+			'f' => true
+		);
+		$this->Shell->args = array('SchemaShellTest', 'articles');
+		$this->Shell->startup();
+		$this->Shell->setReturnValue('in', 'y');
+		$this->Shell->update();
+
+		$article =& new Model(array('name' => 'Article', 'ds' => 'test_suite'));
+		$fields = $article->schema();
+		$this->assertTrue(isset($fields['summary']));
+
+		$this->_fixtures['core.article']->drop($this->db);
+		$this->_fixtures['core.article']->create($this->db);
+	}
+
+/**
+ * test that the plugin param creates the correct path in the schema object.
+ *
+ * @return void
+ * @access public
+ */
+	function testPluginParam() {
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)
+		));
+		$this->Shell->params = array(
+			'plugin' => 'TestPlugin',
+			'connection' => 'test_suite'
+		);
+		$this->Shell->startup();
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS . 'test_plugin' . DS . 'config' . DS . 'schema';
+		$this->assertEqual($this->Shell->Schema->path, $expected);
+		
+		App::build();
+	}
+
+/**
+ * test that using Plugin.name with write.
+ *
+ * @return void
+ * @access public
+ */
+	function testPluginDotSyntaxWithCreate() {
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)
+		));
+		$this->Shell->params = array(
+			'connection' => 'test_suite'
+		);
+		$this->Shell->args = array('TestPlugin.TestPluginApp');
+		$this->Shell->startup();
+		$this->Shell->setReturnValue('in', 'y');
+		$this->Shell->create();
+
+		$db =& ConnectionManager::getDataSource('test_suite');
+		$sources = $db->listSources();
+		$this->assertTrue(in_array($db->config['prefix'] . 'acos', $sources));
+
+		$db->execute('DROP TABLE ' . $db->config['prefix'] . 'acos');
+		App::build();
+	}
+}

Added: trunk/src/Web/cake/tests/cases/console/libs/shell.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/console/libs/shell.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/console/libs/shell.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,513 @@
+<?php
+/**
+ * ShellTest file
+ *
+ * Test Case for Shell
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP :  Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://cakephp.org CakePHP Project
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs
+ * @since         CakePHP v 1.2.0.7726
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Core', 'Folder');
+App::import('Shell', 'Shell', false);
+
+
+if (!defined('DISABLE_AUTO_DISPATCH')) {
+	define('DISABLE_AUTO_DISPATCH', true);
+}
+
+if (!class_exists('ShellDispatcher')) {
+	ob_start();
+	$argv = false;
+	require CAKE . 'console' .  DS . 'cake.php';
+	ob_end_clean();
+}
+
+Mock::generatePartial('ShellDispatcher', 'TestShellMockShellDispatcher', array(
+	'getInput', 'stdout', 'stderr', '_stop', '_initEnvironment'
+));
+
+/**
+ * TestShell class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs
+ */
+class TestShell extends Shell {
+
+/**
+ * name property
+ *
+ * @var name
+ * @access public
+ */
+	var $name = 'TestShell';
+/**
+ * stopped property
+ *
+ * @var integer
+ * @access public
+ */
+	var $stopped;
+
+/**
+ * stop method
+ *
+ * @param integer $status
+ * @return void
+ * @access protected
+ */
+	function _stop($status = 0) {
+		$this->stopped = $status;
+	}
+}
+
+/**
+ * TestAppleTask class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs
+ */
+class TestAppleTask extends Shell {
+}
+
+/**
+ * TestBananaTask class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs
+ */
+class TestBananaTask extends Shell {
+}
+
+/**
+ * ShellTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs
+ */
+class ShellTest extends CakeTestCase {
+
+/**
+ * Fixtures used in this test case
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array(
+		'core.post', 'core.comment', 'core.article', 'core.user',
+		'core.tag', 'core.articles_tag', 'core.attachment'
+	);
+
+/**
+ * setUp method
+ *
+ * @return void
+ * @access public
+ */
+	function setUp() {
+		$this->Dispatcher =& new TestShellMockShellDispatcher();
+		$this->Shell =& new TestShell($this->Dispatcher);
+	}
+
+/**
+ * tearDown method
+ *
+ * @return void
+ * @access public
+ */
+	function tearDown() {
+		ClassRegistry::flush();
+	}
+
+/**
+ * testConstruct method
+ *
+ * @return void
+ * @access public
+ */
+	function testConstruct() {
+		$this->assertIsA($this->Shell->Dispatch, 'TestShellMockShellDispatcher');
+		$this->assertEqual($this->Shell->name, 'TestShell');
+		$this->assertEqual($this->Shell->alias, 'TestShell');
+	}
+
+/**
+ * testInitialize method
+ *
+ * @return void
+ * @access public
+ */
+	function testInitialize() {
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS),
+			'models' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'models' . DS)
+		), true);
+
+		$this->Shell->uses = array('TestPlugin.TestPluginPost');
+		$this->Shell->initialize();
+
+		$this->assertTrue(isset($this->Shell->TestPluginPost));
+		$this->assertIsA($this->Shell->TestPluginPost, 'TestPluginPost');
+		$this->assertEqual($this->Shell->modelClass, 'TestPluginPost');
+
+		$this->Shell->uses = array('Comment');
+		$this->Shell->initialize();
+		$this->assertTrue(isset($this->Shell->Comment));
+		$this->assertIsA($this->Shell->Comment, 'Comment');
+		$this->assertEqual($this->Shell->modelClass, 'Comment');
+
+		$this->Shell->uses = true;
+		$this->Shell->initialize();
+		$this->assertTrue(isset($this->Shell->AppModel));
+		$this->assertIsA($this->Shell->AppModel, 'AppModel');
+
+		App::build();
+	}
+
+/**
+ * testIn method
+ *
+ * @return void
+ * @access public
+ */
+	function testIn() {
+		$this->Shell->Dispatch->setReturnValueAt(0, 'getInput', 'n');
+		$this->Shell->Dispatch->expectAt(0, 'getInput', array('Just a test?', array('y', 'n'), 'n'));
+		$result = $this->Shell->in('Just a test?', array('y', 'n'), 'n');
+		$this->assertEqual($result, 'n');
+
+		$this->Shell->Dispatch->setReturnValueAt(1, 'getInput', 'Y');
+		$this->Shell->Dispatch->expectAt(1, 'getInput', array('Just a test?', array('y', 'n'), 'n'));
+		$result = $this->Shell->in('Just a test?', array('y', 'n'), 'n');
+		$this->assertEqual($result, 'Y');
+
+		$this->Shell->Dispatch->setReturnValueAt(2, 'getInput', 'y');
+		$this->Shell->Dispatch->expectAt(2, 'getInput', array('Just a test?', 'y,n', 'n'));
+		$result = $this->Shell->in('Just a test?', 'y,n', 'n');
+		$this->assertEqual($result, 'y');
+
+		$this->Shell->Dispatch->setReturnValueAt(3, 'getInput', 'y');
+		$this->Shell->Dispatch->expectAt(3, 'getInput', array('Just a test?', 'y/n', 'n'));
+		$result = $this->Shell->in('Just a test?', 'y/n', 'n');
+		$this->assertEqual($result, 'y');
+
+		$this->Shell->Dispatch->setReturnValueAt(4, 'getInput', 'y');
+		$this->Shell->Dispatch->expectAt(4, 'getInput', array('Just a test?', 'y', 'y'));
+		$result = $this->Shell->in('Just a test?', 'y', 'y');
+		$this->assertEqual($result, 'y');
+
+		$this->Shell->Dispatch->setReturnValueAt(5, 'getInput', 'y');
+		$this->Shell->Dispatch->expectAt(5, 'getInput', array('Just a test?', array(0, 1, 2), 0));
+		$result = $this->Shell->in('Just a test?', array(0, 1, 2), 0);
+		$this->assertEqual($result, 0);
+	}
+
+/**
+ * Test in() when not interactive
+ *
+ * @return void
+ */
+	function testInNonInteractive() {
+		$this->Shell->interactive = false;
+
+		$result = $this->Shell->in('Just a test?', 'y/n', 'n');
+		$this->assertEqual($result, 'n');
+	}
+
+/**
+ * testOut method
+ *
+ * @return void
+ * @access public
+ */
+	function testOut() {
+		$this->Shell->Dispatch->expectAt(0, 'stdout', array("Just a test\n", false));
+		$this->Shell->out('Just a test');
+
+		$this->Shell->Dispatch->expectAt(1, 'stdout', array("Just\na\ntest\n", false));
+		$this->Shell->out(array('Just', 'a', 'test'));
+
+		$this->Shell->Dispatch->expectAt(2, 'stdout', array("Just\na\ntest\n\n", false));
+		$this->Shell->out(array('Just', 'a', 'test'), 2);
+
+		$this->Shell->Dispatch->expectAt(3, 'stdout', array("\n", false));
+		$this->Shell->out();
+	}
+
+/**
+ * testErr method
+ *
+ * @return void
+ * @access public
+ */
+	function testErr() {
+		$this->Shell->Dispatch->expectAt(0, 'stderr', array("Just a test\n"));
+		$this->Shell->err('Just a test');
+
+		$this->Shell->Dispatch->expectAt(1, 'stderr', array("Just\na\ntest\n"));
+		$this->Shell->err(array('Just', 'a', 'test'));
+
+		$this->Shell->Dispatch->expectAt(2, 'stderr', array("Just\na\ntest\n\n"));
+		$this->Shell->err(array('Just', 'a', 'test'), 2);
+
+		$this->Shell->Dispatch->expectAt(3, 'stderr', array("\n"));
+		$this->Shell->err();
+	}
+
+/**
+ * testNl
+ *
+ * @return void
+ * @access public
+ */
+	function testNl() {
+		$this->assertEqual($this->Shell->nl(), "\n");
+		$this->assertEqual($this->Shell->nl(true), "\n");
+		$this->assertEqual($this->Shell->nl(false), "");
+		$this->assertEqual($this->Shell->nl(2), "\n\n");
+		$this->assertEqual($this->Shell->nl(1), "\n");
+	}
+
+/**
+ * testHr
+ *
+ * @return void
+ * @access public
+ */
+	function testHr() {
+		$bar = '---------------------------------------------------------------';
+
+		$this->Shell->Dispatch->expectAt(0, 'stdout', array('', false));
+		$this->Shell->Dispatch->expectAt(1, 'stdout', array($bar . "\n", false));
+		$this->Shell->Dispatch->expectAt(2, 'stdout', array('', false));
+		$this->Shell->hr();
+
+		$this->Shell->Dispatch->expectAt(3, 'stdout', array("\n", false));
+		$this->Shell->Dispatch->expectAt(4, 'stdout', array($bar . "\n", false));
+		$this->Shell->Dispatch->expectAt(5, 'stdout', array("\n", false));
+		$this->Shell->hr(true);
+
+		$this->Shell->Dispatch->expectAt(3, 'stdout', array("\n\n", false));
+		$this->Shell->Dispatch->expectAt(4, 'stdout', array($bar . "\n", false));
+		$this->Shell->Dispatch->expectAt(5, 'stdout', array("\n\n", false));
+		$this->Shell->hr(2);
+	}
+
+/**
+ * testError
+ *
+ * @return void
+ * @access public
+ */
+	function testError() {
+		$this->Shell->Dispatch->expectAt(0, 'stderr', array("Error: Foo Not Found\n"));
+		$this->Shell->error('Foo Not Found');
+		$this->assertIdentical($this->Shell->stopped, 1);
+
+		$this->Shell->stopped = null;
+
+		$this->Shell->Dispatch->expectAt(1, 'stderr', array("Error: Foo Not Found\n"));
+		$this->Shell->Dispatch->expectAt(2, 'stderr', array("Searched all...\n"));
+		$this->Shell->error('Foo Not Found', 'Searched all...');
+		$this->assertIdentical($this->Shell->stopped, 1);
+	}
+
+/**
+ * testLoadTasks method
+ *
+ * @return void
+ * @access public
+ */
+	function testLoadTasks() {
+		$this->assertTrue($this->Shell->loadTasks());
+
+		$this->Shell->tasks = null;
+		$this->assertTrue($this->Shell->loadTasks());
+
+		$this->Shell->tasks = false;
+		$this->assertTrue($this->Shell->loadTasks());
+
+		$this->Shell->tasks = true;
+		$this->assertTrue($this->Shell->loadTasks());
+
+		$this->Shell->tasks = array();
+		$this->assertTrue($this->Shell->loadTasks());
+
+		// Fatal Error
+		// $this->Shell->tasks = 'TestIDontExist';
+		// $this->assertFalse($this->Shell->loadTasks());
+		// $this->assertFalse(isset($this->Shell->TestIDontExist));
+
+		$this->Shell->tasks = 'TestApple';
+		$this->assertTrue($this->Shell->loadTasks());
+		$this->assertIsA($this->Shell->TestApple, 'TestAppleTask');
+
+		$this->Shell->tasks = 'TestBanana';
+		$this->assertTrue($this->Shell->loadTasks());
+		$this->assertIsA($this->Shell->TestApple, 'TestAppleTask');
+		$this->assertIsA($this->Shell->TestBanana, 'TestBananaTask');
+
+		unset($this->Shell->ShellTestApple, $this->Shell->TestBanana);
+
+		$this->Shell->tasks = array('TestApple', 'TestBanana');
+		$this->assertTrue($this->Shell->loadTasks());
+		$this->assertIsA($this->Shell->TestApple, 'TestAppleTask');
+		$this->assertIsA($this->Shell->TestBanana, 'TestBananaTask');
+	}
+
+/**
+ * testShortPath method
+ *
+ * @return void
+ * @access public
+ */
+	function testShortPath() {
+		$path = $expected = DS . 'tmp' . DS . 'ab' . DS . 'cd';
+		$this->assertEqual($this->Shell->shortPath($path), $expected);
+
+		$path = $expected = DS . 'tmp' . DS . 'ab' . DS . 'cd' . DS ;
+		$this->assertEqual($this->Shell->shortPath($path), $expected);
+
+		$path = $expected = DS . 'tmp' . DS . 'ab' . DS . 'index.php';
+		$this->assertEqual($this->Shell->shortPath($path), $expected);
+
+		// Shell::shortPath needs Folder::realpath
+		// $path = DS . 'tmp' . DS . 'ab' . DS . '..' . DS . 'cd';
+		// $expected = DS . 'tmp' . DS . 'cd';
+		// $this->assertEqual($this->Shell->shortPath($path), $expected);
+
+		$path = DS . 'tmp' . DS . 'ab' . DS . DS . 'cd';
+		$expected = DS . 'tmp' . DS . 'ab' . DS . 'cd';
+		$this->assertEqual($this->Shell->shortPath($path), $expected);
+
+		$path = 'tmp' . DS . 'ab';
+		$expected = 'tmp' . DS . 'ab';
+		$this->assertEqual($this->Shell->shortPath($path), $expected);
+
+		$path = 'tmp' . DS . 'ab';
+		$expected = 'tmp' . DS . 'ab';
+		$this->assertEqual($this->Shell->shortPath($path), $expected);
+
+		$path = APP;
+		$expected = DS . basename(APP) . DS;
+		$this->assertEqual($this->Shell->shortPath($path), $expected);
+
+		$path = APP . 'index.php';
+		$expected = DS . basename(APP) . DS . 'index.php';
+		$this->assertEqual($this->Shell->shortPath($path), $expected);
+	}
+
+/**
+ * testCreateFile method
+ *
+ * @return void
+ * @access public
+ */
+	function testCreateFile() {
+		$this->skipIf(DIRECTORY_SEPARATOR === '\\', '%s Not supported on Windows');
+
+		$path = TMP . 'shell_test';
+		$file = $path . DS . 'file1.php';
+
+		new Folder($path, true);
+
+		$this->Shell->interactive = false;
+
+		$contents = "<?php\necho 'test';\n\$te = 'st';\n?>";
+		$result = $this->Shell->createFile($file, $contents);
+		$this->assertTrue($result);
+		$this->assertTrue(file_exists($file));
+		$this->assertEqual(file_get_contents($file), $contents);
+
+		$contents = "<?php\necho 'another test';\n\$te = 'st';\n?>";
+		$result = $this->Shell->createFile($file, $contents);
+		$this->assertTrue($result);
+		$this->assertTrue(file_exists($file));
+		$this->assertEqual(file_get_contents($file), $contents);
+
+		$this->Shell->interactive = true;
+
+		$this->Shell->Dispatch->setReturnValueAt(0, 'getInput', 'n');
+		$this->Shell->Dispatch->expectAt(1, 'stdout', array('File exists, overwrite?', '*'));
+
+		$contents = "<?php\necho 'yet another test';\n\$te = 'st';\n?>";
+		$result = $this->Shell->createFile($file, $contents);
+		$this->assertFalse($result);
+		$this->assertTrue(file_exists($file));
+		$this->assertNotEqual(file_get_contents($file), $contents);
+
+		$this->Shell->Dispatch->setReturnValueAt(1, 'getInput', 'y');
+		$this->Shell->Dispatch->expectAt(3, 'stdout', array('File exists, overwrite?', '*'));
+
+		$result = $this->Shell->createFile($file, $contents);
+		$this->assertTrue($result);
+		$this->assertTrue(file_exists($file));
+		$this->assertEqual(file_get_contents($file), $contents);
+
+		$Folder = new Folder($path);
+		$Folder->delete();
+	}
+
+/**
+ * testCreateFileWindows method
+ *
+ * @return void
+ * @access public
+ */
+	function testCreateFileWindows() {
+		$this->skipUnless(DIRECTORY_SEPARATOR === '\\', 'testCreateFileWindows supported on Windows only');
+
+		$path = TMP . 'shell_test';
+		$file = $path . DS . 'file1.php';
+
+		new Folder($path, true);
+
+		$this->Shell->interactive = false;
+
+		$contents = "<?php\necho 'test';\r\n\$te = 'st';\r\n?>";
+		$result = $this->Shell->createFile($file, $contents);
+		$this->assertTrue($result);
+		$this->assertTrue(file_exists($file));
+		$this->assertEqual(file_get_contents($file), $contents);
+
+		$contents = "<?php\necho 'another test';\r\n\$te = 'st';\r\n?>";
+		$result = $this->Shell->createFile($file, $contents);
+		$this->assertTrue($result);
+		$this->assertTrue(file_exists($file));
+		$this->assertEqual(file_get_contents($file), $contents);
+
+		$this->Shell->interactive = true;
+
+		$this->Shell->Dispatch->setReturnValueAt(0, 'getInput', 'n');
+		$this->Shell->Dispatch->expectAt(1, 'stdout', array('File exists, overwrite?'));
+
+		$contents = "<?php\necho 'yet another test';\r\n\$te = 'st';\r\n?>";
+		$result = $this->Shell->createFile($file, $contents);
+		$this->assertFalse($result);
+		$this->assertTrue(file_exists($file));
+		$this->assertNotEqual(file_get_contents($file), $contents);
+
+		$this->Shell->Dispatch->setReturnValueAt(1, 'getInput', 'y');
+		$this->Shell->Dispatch->expectAt(3, 'stdout', array('File exists, overwrite?'));
+
+		$result = $this->Shell->createFile($file, $contents);
+		$this->assertTrue($result);
+		$this->assertTrue(file_exists($file));
+		$this->assertEqual(file_get_contents($file), $contents);
+
+		$Folder = new Folder($path);
+		$Folder->delete();
+	}
+}

Added: trunk/src/Web/cake/tests/cases/console/libs/tasks/controller.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/console/libs/tasks/controller.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/console/libs/tasks/controller.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,633 @@
+<?php
+/**
+ * ControllerTask Test Case
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.tasks
+ * @since         CakePHP(tm) v 1.3
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Core', 'ClassRegistry');
+App::import('View', 'Helper', false);
+App::import('Shell', 'Shell', false);
+
+if (!defined('DISABLE_AUTO_DISPATCH')) {
+	define('DISABLE_AUTO_DISPATCH', true);
+}
+
+if (!class_exists('ShellDispatcher')) {
+	ob_start();
+	$argv = false;
+	require CAKE . 'console' .  DS . 'cake.php';
+	ob_end_clean();
+}
+
+require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'project.php';
+require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'controller.php';
+require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'model.php';
+require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'template.php';
+require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'test.php';
+
+Mock::generatePartial(
+	'ShellDispatcher', 'TestControllerTaskMockShellDispatcher',
+	array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment')
+);
+
+Mock::generatePartial(
+	'ControllerTask', 'MockControllerTask',
+	array('in', 'hr', 'out', 'err', 'createFile', '_stop', '_checkUnitTest')
+);
+
+Mock::generatePartial(
+	'ModelTask', 'ControllerMockModelTask',
+	array('in', 'out', 'err', 'createFile', '_stop', '_checkUnitTest')
+);
+
+Mock::generatePartial(
+	'ProjectTask', 'ControllerMockProjectTask',
+	array('in', 'out', 'err', 'createFile', '_stop', '_checkUnitTest', 'getPrefix')
+);
+
+Mock::generate('TestTask', 'ControllerMockTestTask');
+
+$imported = App::import('Model', 'Article');
+$imported = $imported || App::import('Model', 'Comment');
+$imported = $imported || App::import('Model', 'Tag');
+
+if (!$imported) {
+	define('ARTICLE_MODEL_CREATED', true);
+	App::import('Core', 'Model');
+
+	class Article extends Model {
+		var $name = 'Article';
+		var $hasMany = array('Comment');
+		var $hasAndBelongsToMany = array('Tag');
+	}
+
+}
+
+/**
+ * ControllerTaskTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.tasks
+ */
+class ControllerTaskTest extends CakeTestCase {
+
+/**
+ * fixtures
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array('core.article', 'core.comment', 'core.articles_tag', 'core.tag');
+
+/**
+ * startTest method
+ *
+ * @return void
+ * @access public
+ */
+	function startTest() {
+		$this->Dispatcher =& new TestControllerTaskMockShellDispatcher();
+		$this->Task =& new MockControllerTask($this->Dispatcher);
+		$this->Task->name = 'ControllerTask';
+		$this->Task->Dispatch =& $this->Dispatcher;
+		$this->Task->Dispatch->shellPaths = App::path('shells');
+		$this->Task->Template =& new TemplateTask($this->Task->Dispatch);
+		$this->Task->Template->params['theme'] = 'default';
+		$this->Task->Model =& new ControllerMockModelTask($this->Task->Dispatch);
+		$this->Task->Project =& new ControllerMockProjectTask($this->Task->Dispatch);
+		$this->Task->Test =& new ControllerMockTestTask();
+	}
+
+/**
+ * endTest method
+ *
+ * @return void
+ * @access public
+ */
+	function endTest() {
+		unset($this->Task, $this->Dispatcher);
+		ClassRegistry::flush();
+	}
+
+/**
+ * test ListAll
+ *
+ * @return void
+ * @access public
+ */
+	function testListAll() {
+		$this->Task->connection = 'test_suite';
+		$this->Task->interactive = true;
+		$this->Task->expectAt(1, 'out', array('1. Articles'));
+		$this->Task->expectAt(2, 'out', array('2. ArticlesTags'));
+		$this->Task->expectAt(3, 'out', array('3. Comments'));
+		$this->Task->expectAt(4, 'out', array('4. Tags'));
+
+		$expected = array('Articles', 'ArticlesTags', 'Comments', 'Tags');
+		$result = $this->Task->listAll('test_suite');
+		$this->assertEqual($result, $expected);
+
+		$this->Task->expectAt(6, 'out', array('1. Articles'));
+		$this->Task->expectAt(7, 'out', array('2. ArticlesTags'));
+		$this->Task->expectAt(8, 'out', array('4. Comments'));
+		$this->Task->expectAt(9, 'out', array('5. Tags'));
+
+		$this->Task->interactive = false;
+		$result = $this->Task->listAll();
+
+		$expected = array('articles', 'articles_tags', 'comments', 'tags');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * Test that getName interacts with the user and returns the controller name.
+ *
+ * @return void
+ * @access public
+ */
+	function testGetName() {
+		$this->Task->interactive = true;
+		$this->Task->setReturnValue('in', 1);
+
+		$this->Task->setReturnValueAt(0, 'in', 'q');
+		$this->Task->expectOnce('_stop');
+		$this->Task->getName('test_suite');
+
+		$this->Task->setReturnValueAt(1, 'in', 1);
+		$result = $this->Task->getName('test_suite');
+		$expected = 'Articles';
+		$this->assertEqual($result, $expected);
+
+		$this->Task->setReturnValueAt(2, 'in', 3);
+		$result = $this->Task->getName('test_suite');
+		$expected = 'Comments';
+		$this->assertEqual($result, $expected);
+
+		$this->Task->setReturnValueAt(3, 'in', 10);
+		$result = $this->Task->getName('test_suite');
+		$this->Task->expectOnce('err');
+	}
+
+/**
+ * test helper interactions
+ *
+ * @return void
+ * @access public
+ */
+	function testDoHelpers() {
+		$this->Task->setReturnValue('in', 'n');
+		$result = $this->Task->doHelpers();
+		$this->assertEqual($result, array());
+
+		$this->Task->setReturnValueAt(1, 'in', 'y');
+		$this->Task->setReturnValueAt(2, 'in', ' Javascript, Ajax, CustomOne  ');
+		$result = $this->Task->doHelpers();
+		$expected = array('Javascript', 'Ajax', 'CustomOne');
+		$this->assertEqual($result, $expected);
+
+		$this->Task->setReturnValueAt(3, 'in', 'y');
+		$this->Task->setReturnValueAt(4, 'in', ' Javascript, Ajax, CustomOne, , ');
+		$result = $this->Task->doHelpers();
+		$expected = array('Javascript', 'Ajax', 'CustomOne');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test component interactions
+ *
+ * @return void
+ * @access public
+ */
+	function testDoComponents() {
+		$this->Task->setReturnValue('in', 'n');
+		$result = $this->Task->doComponents();
+		$this->assertEqual($result, array());
+
+		$this->Task->setReturnValueAt(1, 'in', 'y');
+		$this->Task->setReturnValueAt(2, 'in', ' RequestHandler, Security  ');
+		$result = $this->Task->doComponents();
+		$expected = array('RequestHandler', 'Security');
+		$this->assertEqual($result, $expected);
+
+		$this->Task->setReturnValueAt(3, 'in', 'y');
+		$this->Task->setReturnValueAt(4, 'in', ' RequestHandler, Security, , ');
+		$result = $this->Task->doComponents();
+		$expected = array('RequestHandler', 'Security');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test Confirming controller user interaction
+ *
+ * @return void
+ * @access public
+ */
+	function testConfirmController() {
+		$controller = 'Posts';
+		$scaffold = false;
+		$helpers = array('Ajax', 'Time');
+		$components = array('Acl', 'Auth');
+		$uses = array('Comment', 'User');
+
+		$this->Task->expectAt(2, 'out', array("Controller Name:\n\t$controller"));
+		$this->Task->expectAt(3, 'out', array("Helpers:\n\tAjax, Time"));
+		$this->Task->expectAt(4, 'out', array("Components:\n\tAcl, Auth"));
+		$this->Task->confirmController($controller, $scaffold, $helpers, $components);
+	}
+
+/**
+ * test the bake method
+ *
+ * @return void
+ * @access public
+ */
+	function testBake() {
+		$helpers = array('Ajax', 'Time');
+		$components = array('Acl', 'Auth');
+		$this->Task->setReturnValue('createFile', true);
+
+		$result = $this->Task->bake('Articles', '--actions--', $helpers, $components);
+		$this->assertPattern('/class ArticlesController extends AppController/', $result);
+		$this->assertPattern('/\$components \= array\(\'Acl\', \'Auth\'\)/', $result);
+		$this->assertPattern('/\$helpers \= array\(\'Ajax\', \'Time\'\)/', $result);
+		$this->assertPattern('/\-\-actions\-\-/', $result);
+
+		$result = $this->Task->bake('Articles', 'scaffold', $helpers, $components);
+		$this->assertPattern('/class ArticlesController extends AppController/', $result);
+		$this->assertPattern('/var \$scaffold/', $result);
+		$this->assertNoPattern('/helpers/', $result);
+		$this->assertNoPattern('/components/', $result);
+
+		$result = $this->Task->bake('Articles', '--actions--', array(), array());
+		$this->assertPattern('/class ArticlesController extends AppController/', $result);
+		$this->assertNoPattern('/components/', $result);
+		$this->assertNoPattern('/helpers/', $result);
+		$this->assertPattern('/\-\-actions\-\-/', $result);
+	}
+
+/**
+ * test bake() with a -plugin param
+ *
+ * @return void
+ * @access public
+ */
+	function testBakeWithPlugin() {
+		$this->Task->plugin = 'ControllerTest';
+		$helpers = array('Ajax', 'Time');
+		$components = array('Acl', 'Auth');
+		$uses = array('Comment', 'User');
+
+		$path = APP . 'plugins' . DS . 'controller_test' . DS . 'controllers' . DS . 'articles_controller.php';
+		$this->Task->expectAt(0, 'createFile', array($path, '*'));
+		$this->Task->bake('Articles', '--actions--', array(), array(), array());
+
+		$this->Task->plugin = 'controllerTest';
+		$path = APP . 'plugins' . DS . 'controller_test' . DS . 'controllers' . DS . 'articles_controller.php';
+		$this->Task->expectAt(1, 'createFile', array(
+			$path, new PatternExpectation('/ArticlesController extends ControllerTestAppController/')));
+		$this->Task->bake('Articles', '--actions--', array(), array(), array());
+
+		$this->assertEqual($this->Task->Template->templateVars['plugin'], 'ControllerTest');
+	}
+
+/**
+ * test that bakeActions is creating the correct controller Code. (Using sessions)
+ *
+ * @return void
+ * @access public
+ */
+	function testBakeActionsUsingSessions() {
+		$skip = $this->skipIf(!defined('ARTICLE_MODEL_CREATED'),
+			'Testing bakeActions requires Article, Comment & Tag Model to be undefined. %s');
+		if ($skip) {
+			return;
+		}
+		$result = $this->Task->bakeActions('Articles', null, true);
+
+		$this->assertTrue(strpos($result, 'function index() {') !== false);
+		$this->assertTrue(strpos($result, '$this->Article->recursive = 0;') !== false);
+		$this->assertTrue(strpos($result, "\$this->set('articles', \$this->paginate());") !== false);
+
+		$this->assertTrue(strpos($result, 'function view($id = null)') !== false);
+		$this->assertTrue(strpos($result, "\$this->Session->setFlash(__('Invalid article', true));") !== false);
+		$this->assertTrue(strpos($result, "\$this->set('article', \$this->Article->read(null, \$id)") !== false);
+
+		$this->assertTrue(strpos($result, 'function add()') !== false);
+		$this->assertTrue(strpos($result, 'if (!empty($this->data))') !== false);
+		$this->assertTrue(strpos($result, 'if ($this->Article->save($this->data))') !== false);
+		$this->assertTrue(strpos($result, "\$this->Session->setFlash(__('The article has been saved', true));") !== false);
+
+		$this->assertTrue(strpos($result, 'function edit($id = null)') !== false);
+		$this->assertTrue(strpos($result, "\$this->Session->setFlash(__('The article could not be saved. Please, try again.', true));") !== false);
+
+		$this->assertTrue(strpos($result, 'function delete($id = null)') !== false);
+		$this->assertTrue(strpos($result, 'if ($this->Article->delete($id))') !== false);
+		$this->assertTrue(strpos($result, "\$this->Session->setFlash(__('Article deleted', true));") !== false);
+
+		$result = $this->Task->bakeActions('Articles', 'admin_', true);
+
+		$this->assertTrue(strpos($result, 'function admin_index() {') !== false);
+		$this->assertTrue(strpos($result, 'function admin_add()') !== false);
+		$this->assertTrue(strpos($result, 'function admin_view($id = null)') !== false);
+		$this->assertTrue(strpos($result, 'function admin_edit($id = null)') !== false);
+		$this->assertTrue(strpos($result, 'function admin_delete($id = null)') !== false);
+	}
+
+/**
+ * Test baking with Controller::flash() or no sessions.
+ *
+ * @return void
+ * @access public
+ */
+	function testBakeActionsWithNoSessions() {
+		$skip = $this->skipIf(!defined('ARTICLE_MODEL_CREATED'),
+			'Testing bakeActions requires Article, Tag, Comment Models to be undefined. %s');
+		if ($skip) {
+			return;
+		}
+		$result = $this->Task->bakeActions('Articles', null, false);
+
+		$this->assertTrue(strpos($result, 'function index() {') !== false);
+		$this->assertTrue(strpos($result, '$this->Article->recursive = 0;') !== false);
+		$this->assertTrue(strpos($result, "\$this->set('articles', \$this->paginate());") !== false);
+
+		$this->assertTrue(strpos($result, 'function view($id = null)') !== false);
+		$this->assertTrue(strpos($result, "\$this->flash(__('Invalid article', true), array('action' => 'index'))") !== false);
+		$this->assertTrue(strpos($result, "\$this->set('article', \$this->Article->read(null, \$id)") !== false);
+
+		$this->assertTrue(strpos($result, 'function add()') !== false);
+		$this->assertTrue(strpos($result, 'if (!empty($this->data))') !== false);
+		$this->assertTrue(strpos($result, 'if ($this->Article->save($this->data))') !== false);
+		$this->assertTrue(strpos($result, "\$this->flash(__('The article has been saved.', true), array('action' => 'index'))") !== false);
+
+		$this->assertTrue(strpos($result, 'function edit($id = null)') !== false);
+		$this->assertTrue(strpos($result, "\$this->Article->Tag->find('list')") !== false);
+		$this->assertTrue(strpos($result, "\$this->set(compact('tags'))") !== false);
+
+		$this->assertTrue(strpos($result, 'function delete($id = null)') !== false);
+		$this->assertTrue(strpos($result, 'if ($this->Article->delete($id))') !== false);
+		$this->assertTrue(strpos($result, "\$this->flash(__('Article deleted', true), array('action' => 'index'))") !== false);
+	}
+
+/**
+ * test baking a test
+ *
+ * @return void
+ * @access public
+ */
+	function testBakeTest() {
+		$this->Task->plugin = 'ControllerTest';
+		$this->Task->connection = 'test_suite';
+		$this->Task->interactive = false;
+
+		$this->Task->Test->expectOnce('bake', array('Controller', 'Articles'));
+		$this->Task->bakeTest('Articles');
+
+		$this->assertEqual($this->Task->plugin, $this->Task->Test->plugin);
+		$this->assertEqual($this->Task->connection, $this->Task->Test->connection);
+		$this->assertEqual($this->Task->interactive, $this->Task->Test->interactive);
+	}
+
+/**
+ * test Interactive mode.
+ *
+ * @return void
+ * @access public
+ */
+	function testInteractive() {
+		$this->Task->connection = 'test_suite';
+		$this->Task->path = '/my/path';
+		$this->Task->setReturnValue('in', '1');
+		$this->Task->setReturnValueAt(1, 'in', 'y'); // build interactive
+		$this->Task->setReturnValueAt(2, 'in', 'n'); // build no scaffolds
+		$this->Task->setReturnValueAt(3, 'in', 'y'); // build normal methods
+		$this->Task->setReturnValueAt(4, 'in', 'n'); // build admin methods
+		$this->Task->setReturnValueAt(5, 'in', 'n'); // helpers?
+		$this->Task->setReturnValueAt(6, 'in', 'n'); // components?
+		$this->Task->setReturnValueAt(7, 'in', 'y'); // use sessions
+		$this->Task->setReturnValueAt(8, 'in', 'y'); // looks good
+
+		$this->Task->execute();
+
+		$filename = '/my/path/articles_controller.php';
+		$this->Task->expectAt(0, 'createFile', array($filename, new PatternExpectation('/class ArticlesController/')));
+	}
+
+/**
+ * test Interactive mode.
+ *
+ * @return void
+ * @access public
+ */
+	function testInteractiveAdminMethodsNotInteractive() {
+		$this->Task->connection = 'test_suite';
+		$this->Task->interactive = true;
+		$this->Task->path = '/my/path';
+		$this->Task->setReturnValue('in', '1');
+		$this->Task->setReturnValueAt(1, 'in', 'y'); // build interactive
+		$this->Task->setReturnValueAt(2, 'in', 'n'); // build no scaffolds
+		$this->Task->setReturnValueAt(3, 'in', 'y'); // build normal methods
+		$this->Task->setReturnValueAt(4, 'in', 'y'); // build admin methods
+		$this->Task->setReturnValueAt(5, 'in', 'n'); // helpers?
+		$this->Task->setReturnValueAt(6, 'in', 'n'); // components?
+		$this->Task->setReturnValueAt(7, 'in', 'y'); // use sessions
+		$this->Task->setReturnValueAt(8, 'in', 'y'); // looks good
+		$this->Task->setReturnValue('createFile', true);
+		$this->Task->Project->setReturnValue('getPrefix', 'admin_');
+
+		$result = $this->Task->execute();
+		$this->assertPattern('/admin_index/', $result);
+
+		$filename = '/my/path/articles_controller.php';
+		$this->Task->expectAt(0, 'createFile', array($filename, new PatternExpectation('/class ArticlesController/')));
+	}
+
+/**
+ * test that execute runs all when the first arg == all
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteIntoAll() {
+		$skip = $this->skipIf(!defined('ARTICLE_MODEL_CREATED'),
+			'Execute into all could not be run as an Article, Tag or Comment model was already loaded. %s');
+		if ($skip) {
+			return;
+		}
+		$this->Task->connection = 'test_suite';
+		$this->Task->path = '/my/path/';
+		$this->Task->args = array('all');
+
+		$this->Task->setReturnValue('createFile', true);
+		$this->Task->setReturnValue('_checkUnitTest', true);
+		$this->Task->Test->expectCallCount('bake', 1);
+
+		$filename = '/my/path/articles_controller.php';
+		$this->Task->expectAt(0, 'createFile', array($filename, new PatternExpectation('/class ArticlesController/')));
+
+		$this->Task->execute();
+	}
+
+/**
+ * test that `cake bake controller foos` works.
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteWithController() {
+		$skip = $this->skipIf(!defined('ARTICLE_MODEL_CREATED'),
+			'Execute with scaffold param requires no Article, Tag or Comment model to be defined. %s');
+		if ($skip) {
+			return;
+		}
+		$this->Task->connection = 'test_suite';
+		$this->Task->path = '/my/path/';
+		$this->Task->args = array('Articles');
+
+		$filename = '/my/path/articles_controller.php';
+		$this->Task->expectAt(0, 'createFile', array(
+			$filename, new PatternExpectation('/\$scaffold/')
+		));
+
+		$this->Task->execute();
+	}
+
+/**
+ * test that both plural and singular forms work for controller baking.
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteWithControllerNameVariations() {
+		$skip = $this->skipIf(!defined('ARTICLE_MODEL_CREATED'),
+			'Execute with scaffold param requires no Article, Tag or Comment model to be defined. %s');
+		if ($skip) {
+			return;
+		}
+		$this->Task->connection = 'test_suite';
+		$this->Task->path = '/my/path/';
+		$this->Task->args = array('Articles');
+
+		$filename = '/my/path/articles_controller.php';
+		$this->Task->expectAt(0, 'createFile', array(
+			$filename, new PatternExpectation('/\$scaffold/')
+		));
+
+		$this->Task->execute();
+
+		$this->Task->args = array('Article');
+		$filename = '/my/path/articles_controller.php';
+		$this->Task->expectAt(1, 'createFile', array(
+			$filename, new PatternExpectation('/class ArticlesController/')
+		));
+		$this->Task->execute();
+
+		$this->Task->args = array('article');
+		$filename = '/my/path/articles_controller.php';
+		$this->Task->expectAt(2, 'createFile', array(
+			$filename, new PatternExpectation('/class ArticlesController/')
+		));
+
+		$this->Task->args = array('articles');
+		$filename = '/my/path/articles_controller.php';
+		$this->Task->expectAt(3, 'createFile', array(
+			$filename, new PatternExpectation('/class ArticlesController/')
+		));
+		$this->Task->execute();
+
+		$this->Task->args = array('Articles');
+		$filename = '/my/path/articles_controller.php';
+		$this->Task->expectAt(4, 'createFile', array(
+			$filename, new PatternExpectation('/class ArticlesController/')
+		));
+		$this->Task->execute();
+		$this->Task->execute();
+	}
+
+/**
+ * test that `cake bake controller foo scaffold` works.
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteWithPublicParam() {
+		$skip = $this->skipIf(!defined('ARTICLE_MODEL_CREATED'),
+			'Execute with scaffold param requires no Article, Tag or Comment model to be defined. %s');
+		if ($skip) {
+			return;
+		}
+		$this->Task->connection = 'test_suite';
+		$this->Task->path = '/my/path/';
+		$this->Task->args = array('Articles', 'public');
+
+		$filename = '/my/path/articles_controller.php';
+		$this->Task->expectAt(0, 'createFile', array(
+			$filename, new NoPatternExpectation('/var \$scaffold/')
+		));
+
+		$this->Task->execute();
+	}
+
+/**
+ * test that `cake bake controller foos both` works.
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteWithControllerAndBoth() {
+		$skip = $this->skipIf(!defined('ARTICLE_MODEL_CREATED'),
+			'Execute with scaffold param requires no Article, Tag or Comment model to be defined. %s');
+		if ($skip) {
+			return;
+		}
+		$this->Task->Project->setReturnValue('getPrefix', 'admin_');
+		$this->Task->connection = 'test_suite';
+		$this->Task->path = '/my/path/';
+		$this->Task->args = array('Articles', 'public', 'admin');
+
+		$filename = '/my/path/articles_controller.php';
+		$this->Task->expectAt(0, 'createFile', array(
+			$filename, new PatternExpectation('/admin_index/')
+		));
+
+		$this->Task->execute();
+	}
+
+/**
+ * test that `cake bake controller foos admin` works.
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteWithControllerAndAdmin() {
+		$skip = $this->skipIf(!defined('ARTICLE_MODEL_CREATED'),
+			'Execute with scaffold param requires no Article, Tag or Comment model to be defined. %s');
+		if ($skip) {
+			return;
+		}
+		$this->Task->Project->setReturnValue('getPrefix', 'admin_');
+		$this->Task->connection = 'test_suite';
+		$this->Task->path = '/my/path/';
+		$this->Task->args = array('Articles', 'admin');
+
+		$filename = '/my/path/articles_controller.php';
+		$this->Task->expectAt(0, 'createFile', array(
+			$filename, new PatternExpectation('/admin_index/')
+		));
+
+		$this->Task->execute();
+	}
+}

Added: trunk/src/Web/cake/tests/cases/console/libs/tasks/db_config.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/console/libs/tasks/db_config.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/console/libs/tasks/db_config.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,155 @@
+<?php
+/**
+ * DBConfigTask Test Case
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.tasks
+ * @since         CakePHP(tm) v 1.3
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Shell', 'Shell', false);
+
+if (!defined('DISABLE_AUTO_DISPATCH')) {
+	define('DISABLE_AUTO_DISPATCH', true);
+}
+
+if (!class_exists('ShellDispatcher')) {
+	ob_start();
+	$argv = false;
+	require CAKE . 'console' .  DS . 'cake.php';
+	ob_end_clean();
+}
+
+require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'db_config.php';
+//require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'template.php';
+
+Mock::generatePartial(
+	'ShellDispatcher', 'TestDbConfigTaskMockShellDispatcher',
+	array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment')
+);
+
+Mock::generatePartial(
+	'DbConfigTask', 'MockDbConfigTask',
+	array('in', 'hr', 'out', 'err', 'createFile', '_stop', '_checkUnitTest')
+);
+
+class TEST_DATABASE_CONFIG {
+	var $default = array(
+		'driver' => 'mysql',
+		'persistent' => false,
+		'host' => 'localhost',
+		'login' => 'user',
+		'password' => 'password',
+		'database' => 'database_name',
+		'prefix' => '',
+	);
+
+	var $otherOne = array(
+		'driver' => 'mysql',
+		'persistent' => false,
+		'host' => 'localhost',
+		'login' => 'user',
+		'password' => 'password',
+		'database' => 'other_one',
+		'prefix' => '',
+	);
+}
+
+/**
+ * DbConfigTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.tasks
+ */
+class DbConfigTaskTest extends CakeTestCase {
+
+/**
+ * startTest method
+ *
+ * @return void
+ * @access public
+ */
+	function startTest() {
+		$this->Dispatcher =& new TestDbConfigTaskMockShellDispatcher();
+		$this->Task =& new MockDbConfigTask($this->Dispatcher);
+		$this->Task->Dispatch =& $this->Dispatcher;
+		$this->Task->Dispatch->shellPaths = App::path('shells');
+
+		$this->Task->params['working'] = rtrim(APP, DS);
+		$this->Task->databaseClassName = 'TEST_DATABASE_CONFIG';
+	}
+
+/**
+ * endTest method
+ *
+ * @return void
+ * @access public
+ */
+	function endTest() {
+		unset($this->Task, $this->Dispatcher);
+		ClassRegistry::flush();
+	}
+
+/**
+ * Test the getConfig method.
+ *
+ * @return void
+ * @access public
+ */
+	function testGetConfig() {
+		$this->Task->setReturnValueAt(0, 'in', 'otherOne');
+		$result = $this->Task->getConfig();
+		$this->assertEqual($result, 'otherOne');
+	}
+
+/**
+ * test that initialize sets the path up.
+ *
+ * @return void
+ * @access public
+ */
+	function testInitialize() {
+		$this->assertTrue(empty($this->Task->path));
+		$this->Task->initialize();
+		$this->assertFalse(empty($this->Task->path));
+		$this->assertEqual($this->Task->path, APP . 'config' . DS);
+
+	}
+
+/**
+ * test execute and by extension __interactive
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteIntoInteractive() {
+		$this->Task->initialize();
+
+		$this->Task->expectOnce('_stop');
+		$this->Task->setReturnValue('in', 'y');
+		$this->Task->setReturnValueAt(0, 'in', 'default');
+		$this->Task->setReturnValueAt(1, 'in', 'n');
+		$this->Task->setReturnValueAt(2, 'in', 'localhost');
+		$this->Task->setReturnValueAt(3, 'in', 'n');
+		$this->Task->setReturnValueAt(4, 'in', 'root');
+		$this->Task->setReturnValueAt(5, 'in', 'password');
+		$this->Task->setReturnValueAt(6, 'in', 'cake_test');
+		$this->Task->setReturnValueAt(7, 'in', 'n');
+		$this->Task->setReturnValueAt(8, 'in', 'y');
+		$this->Task->setReturnValueAt(9, 'in', 'y');
+		$this->Task->setReturnValueAt(10, 'in', 'y');
+		$this->Task->setReturnValueAt(11, 'in', 'n');
+
+		$result = $this->Task->execute();
+	}
+}

Added: trunk/src/Web/cake/tests/cases/console/libs/tasks/extract.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/console/libs/tasks/extract.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/console/libs/tasks/extract.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,196 @@
+<?php
+/**
+ * ExtractTaskTest file
+ *
+ * Test Case for i18n extraction shell task
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP :  Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://cakephp.org CakePHP Project
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.tasks
+ * @since         CakePHP v 1.2.0.7726
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Core', 'Folder');
+App::import('Shell', 'Shell', false);
+
+if (!defined('DISABLE_AUTO_DISPATCH')) {
+	define('DISABLE_AUTO_DISPATCH', true);
+}
+
+if (!class_exists('ShellDispatcher')) {
+	ob_start();
+	$argv = false;
+	require CAKE . 'console' .  DS . 'cake.php';
+	ob_end_clean();
+}
+
+require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'extract.php';
+
+
+Mock::generatePartial(
+	'ShellDispatcher', 'TestExtractTaskMockShellDispatcher',
+	array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment')
+);
+
+/**
+ * ExtractTaskTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.tasks
+ */
+class ExtractTaskTest extends CakeTestCase {
+
+/**
+ * setUp method
+ *
+ * @return void
+ * @access public
+ */
+	function setUp() {
+		$this->Dispatcher =& new TestExtractTaskMockShellDispatcher();
+		$this->Task =& new ExtractTask($this->Dispatcher);
+	}
+
+/**
+ * tearDown method
+ *
+ * @return void
+ * @access public
+ */
+	function tearDown() {
+		ClassRegistry::flush();
+	}
+
+/**
+ * testExecute method
+ *
+ * @return void
+ * @access public
+ */
+	function testExecute() {
+		$path = TMP . 'tests' . DS . 'extract_task_test';
+		new Folder($path . DS . 'locale', true);
+
+		$this->Task->interactive = false;
+
+		$this->Task->params['paths'] = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'pages';
+		$this->Task->params['output'] = $path . DS;
+		$this->Task->Dispatch->expectNever('stderr');
+		$this->Task->Dispatch->expectNever('_stop');
+		$this->Task->execute();
+		$this->assertTrue(file_exists($path . DS . 'default.pot'));
+		$result = file_get_contents($path . DS . 'default.pot');
+
+		$pattern = '/"Content-Type\: text\/plain; charset\=utf-8/';
+		$this->assertPattern($pattern, $result);
+		$pattern = '/"Content-Transfer-Encoding\: 8bit/';
+		$this->assertPattern($pattern, $result);
+		$pattern = '/"Plural-Forms\: nplurals\=INTEGER; plural\=EXPRESSION;/';
+		$this->assertPattern($pattern, $result);
+
+		// home.ctp
+		$pattern = '/msgid "Your tmp directory is writable."\nmsgstr ""\n/';
+		$this->assertPattern($pattern, $result);
+		$pattern = '/msgid "Your tmp directory is NOT writable."\nmsgstr ""\n/';
+		$this->assertPattern($pattern, $result);
+		$pattern = '/msgid "The %s is being used for caching. To change the config edit ';
+		$pattern .= 'APP\/config\/core.php "\nmsgstr ""\n/';
+		$this->assertPattern($pattern, $result);
+		$pattern = '/msgid "Your cache is NOT working. Please check ';
+		$pattern .= 'the settings in APP\/config\/core.php"\nmsgstr ""\n/';
+		$this->assertPattern($pattern, $result);
+		$pattern = '/msgid "Your database configuration file is present."\nmsgstr ""\n/';
+		$this->assertPattern($pattern, $result);
+		$pattern = '/msgid "Your database configuration file is NOT present."\nmsgstr ""\n/';
+		$this->assertPattern($pattern, $result);
+		$pattern = '/msgid "Rename config\/database.php.default to ';
+		$pattern .= 'config\/database.php"\nmsgstr ""\n/';
+		$this->assertPattern($pattern, $result);
+		$pattern = '/msgid "Cake is able to connect to the database."\nmsgstr ""\n/';
+		$this->assertPattern($pattern, $result);
+		$pattern = '/msgid "Cake is NOT able to connect to the database."\nmsgstr ""\n/';
+		$this->assertPattern($pattern, $result);
+		$pattern = '/msgid "Editing this Page"\nmsgstr ""\n/';
+		$this->assertPattern($pattern, $result);
+		$pattern = '/msgid "To change the content of this page, edit: %s.*To change its layout, ';
+		$pattern .= 'edit: %s.*You can also add some CSS styles for your pages at: %s"\nmsgstr ""/s';
+		$this->assertPattern($pattern, $result);
+
+		// extract.ctp
+		$pattern = '/\#: (\\\\|\/)extract\.ctp:6\n';
+		$pattern .= 'msgid "You have %d new message."\nmsgid_plural "You have %d new messages."/';
+		$this->assertPattern($pattern, $result);
+
+		$pattern = '/\#: (\\\\|\/)extract\.ctp:7\n';
+		$pattern .= 'msgid "You deleted %d message."\nmsgid_plural "You deleted %d messages."/';
+		$this->assertPattern($pattern, $result);
+
+		$pattern = '/\#: (\\\\|\/)extract\.ctp:14\n';
+		$pattern .= '\#: (\\\\|\/)home\.ctp:77\n';
+		$pattern .= 'msgid "Editing this Page"\nmsgstr ""/';
+		$this->assertPattern($pattern, $result);
+
+		$pattern = '/\#: (\\\\|\/)extract\.ctp:17\nmsgid "';
+		$pattern .= 'Hot features!';
+		$pattern .= '\\\n - No Configuration: Set-up the database and let the magic begin';
+		$pattern .= '\\\n - Extremely Simple: Just look at the name...It\'s Cake';
+		$pattern .= '\\\n - Active, Friendly Community: Join us #cakephp on IRC. We\'d love to help you get started';
+		$pattern .= '"\nmsgstr ""/';
+		$this->assertPattern($pattern, $result);
+
+		$pattern = '/\#: (\\\\|\/)extract\.ctp:26\n';
+		$pattern .= 'msgid "Found "/';
+		$this->assertNoPattern($pattern, $result);
+
+		// extract.ctp - reading the domain.pot
+		$result = file_get_contents($path . DS . 'domain.pot');
+
+		$pattern = '/msgid "You have %d new message."\nmsgid_plural "You have %d new messages."/';
+		$this->assertNoPattern($pattern, $result);
+		$pattern = '/msgid "You deleted %d message."\nmsgid_plural "You deleted %d messages."/';
+		$this->assertNoPattern($pattern, $result);
+
+		$pattern = '/msgid "You have %d new message \(domain\)."\nmsgid_plural "You have %d new messages \(domain\)."/';
+		$this->assertPattern($pattern, $result);
+		$pattern = '/msgid "You deleted %d message \(domain\)."\nmsgid_plural "You deleted %d messages \(domain\)."/';
+		$this->assertPattern($pattern, $result);
+
+		$Folder = new Folder($path);
+		$Folder->delete();
+	}
+
+/**
+ * test extract can read more than one path.
+ *
+ * @return void
+ */
+	function testExtractMultiplePaths() {
+		$path = TMP . 'tests' . DS . 'extract_task_test';
+		new Folder($path . DS . 'locale', true);
+
+		$this->Task->interactive = false;
+
+		$this->Task->params['paths'] = 
+			TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'pages,' .
+			TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'posts';
+	
+		$this->Task->params['output'] = $path . DS;
+		$this->Task->Dispatch->expectNever('stderr');
+		$this->Task->Dispatch->expectNever('_stop');
+		$this->Task->execute();
+
+		$result = file_get_contents($path . DS . 'default.pot');
+
+		$pattern = '/msgid "Add User"/';
+		$this->assertPattern($pattern, $result);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/console/libs/tasks/fixture.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/console/libs/tasks/fixture.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/console/libs/tasks/fixture.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,397 @@
+<?php
+/**
+ * FixtureTask Test case
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.tasks
+ * @since         CakePHP(tm) v 1.3
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Shell', 'Shell', false);
+
+if (!defined('DISABLE_AUTO_DISPATCH')) {
+	define('DISABLE_AUTO_DISPATCH', true);
+}
+
+if (!class_exists('ShellDispatcher')) {
+	ob_start();
+	$argv = false;
+	require CAKE . 'console' .  DS . 'cake.php';
+	ob_end_clean();
+}
+
+require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'template.php';
+require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'fixture.php';
+
+Mock::generatePartial(
+	'ShellDispatcher', 'TestFixtureTaskMockShellDispatcher',
+	array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment')
+);
+
+Mock::generatePartial(
+	'FixtureTask', 'MockFixtureTask',
+	array('in', 'out', 'err', 'createFile', '_stop')
+);
+
+Mock::generatePartial(
+	'Shell', 'MockFixtureModelTask',
+	array('in', 'out', 'err', 'createFile', '_stop', 'getName', 'getTable', 'listAll')
+);
+
+/**
+ * FixtureTaskTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.tasks
+ */
+class FixtureTaskTest extends CakeTestCase {
+
+/**
+ * fixtures
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array('core.article', 'core.comment', 'core.datatype', 'core.binary_test');
+
+/**
+ * startTest method
+ *
+ * @return void
+ * @access public
+ */
+	function startTest() {
+		$this->Dispatcher =& new TestFixtureTaskMockShellDispatcher();
+		$this->Task =& new MockFixtureTask();
+		$this->Task->Model =& new MockFixtureModelTask();
+		$this->Task->DbConfig =& new MockFixtureModelTask();
+		$this->Task->Dispatch =& $this->Dispatcher;
+		$this->Task->Template =& new TemplateTask($this->Task->Dispatch);
+		$this->Task->Dispatch->shellPaths = App::path('shells');
+		$this->Task->Template->initialize();
+	}
+
+/**
+ * endTest method
+ *
+ * @return void
+ * @access public
+ */
+	function endTest() {
+		unset($this->Task, $this->Dispatcher);
+		ClassRegistry::flush();
+	}
+
+/**
+ * test that initialize sets the path
+ *
+ * @return void
+ * @access public
+ */
+	function testConstruct() {
+		$this->Dispatch->params['working'] = DS . 'my' . DS . 'path';
+		$Task =& new FixtureTask($this->Dispatch);
+
+		$expected = DS . 'my' . DS . 'path' . DS . 'tests' . DS . 'fixtures' . DS;
+		$this->assertEqual($Task->path, $expected);
+	}
+
+/**
+ * test import option array generation
+ *
+ * @return void
+ * @access public
+ */
+	function testImportOptions() {
+		$this->Task->setReturnValueAt(0, 'in', 'y');
+		$this->Task->setReturnValueAt(1, 'in', 'y');
+
+		$result = $this->Task->importOptions('Article');
+		$expected = array('schema' => 'Article', 'records' => true);
+		$this->assertEqual($result, $expected);
+
+		$this->Task->setReturnValueAt(2, 'in', 'n');
+		$this->Task->setReturnValueAt(3, 'in', 'n');
+		$this->Task->setReturnValueAt(4, 'in', 'n');
+
+		$result = $this->Task->importOptions('Article');
+		$expected = array();
+		$this->assertEqual($result, $expected);
+
+		$this->Task->setReturnValueAt(5, 'in', 'n');
+		$this->Task->setReturnValueAt(6, 'in', 'n');
+		$this->Task->setReturnValueAt(7, 'in', 'y');
+		$result = $this->Task->importOptions('Article');
+		$expected = array('fromTable' => true);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that connection gets set to the import options when a different connection is used.
+ *
+ * @return void
+ */
+	function testImportOptionsAlternateConnection() {
+		$this->Task->connection = 'test_suite';
+		$result = $this->Task->bake('Article', false, array('schema' => 'Article'));
+		$this->assertPattern("/'connection' => 'test_suite'/", $result);
+	}
+
+/**
+ * test generating a fixture with database conditions.
+ *
+ * @return void
+ * @access public
+ */
+	function testImportRecordsFromDatabaseWithConditions() {
+		$this->Task->interactive = true;
+		$this->Task->setReturnValueAt(0, 'in', 'WHERE 1=1 LIMIT 10');
+		$this->Task->connection = 'test_suite';
+		$this->Task->path = '/my/path/';
+		$result = $this->Task->bake('Article', false, array('fromTable' => true, 'schema' => 'Article', 'records' => false));
+
+		$this->assertPattern('/class ArticleFixture extends CakeTestFixture/', $result);
+		$this->assertPattern('/var \$records/', $result);
+		$this->assertPattern('/var \$import/', $result);
+		$this->assertPattern("/'title' => 'First Article'/", $result, 'Missing import data %s');
+		$this->assertPattern('/Second Article/', $result, 'Missing import data %s');
+		$this->assertPattern('/Third Article/', $result, 'Missing import data %s');
+	}
+
+/**
+ * Ensure that fixture data doesn't get overly escaped.
+ *
+ * @return void
+ */
+	function testImportRecordsNoEscaping() {
+		$Article = ClassRegistry::init('Article');
+		$Article->updateAll(array('body' => "'Body \"value\"'"));
+
+		$this->Task->interactive = true;
+		$this->Task->setReturnValueAt(0, 'in', 'WHERE 1=1 LIMIT 10');
+		$this->Task->connection = 'test_suite';
+		$this->Task->path = '/my/path/';
+		$result = $this->Task->bake('Article', false, array(
+			'fromTable' => true, 
+			'schema' => 'Article',
+			'records' => false
+		));
+
+		$this->assertPattern("/'body' => 'Body \"value\"'/", $result, 'Data has escaping %s');
+	}
+
+/**
+ * test that execute passes runs bake depending with named model.
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteWithNamedModel() {
+		$this->Task->connection = 'test_suite';
+		$this->Task->path = '/my/path/';
+		$this->Task->args = array('article');
+		$filename = '/my/path/article_fixture.php';
+		$this->Task->expectAt(0, 'createFile', array($filename, new PatternExpectation('/class ArticleFixture/')));
+		$this->Task->execute();
+	}
+
+/**
+ * test that execute passes runs bake depending with named model.
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteWithNamedModelVariations() {
+		$this->Task->connection = 'test_suite';
+		$this->Task->path = '/my/path/';
+
+		$this->Task->args = array('article');
+		$filename = '/my/path/article_fixture.php';
+		$this->Task->expectAt(0, 'createFile', array($filename, new PatternExpectation('/class ArticleFixture/')));
+		$this->Task->execute();
+
+		$this->Task->args = array('articles');
+		$filename = '/my/path/article_fixture.php';
+		$this->Task->expectAt(1, 'createFile', array($filename, new PatternExpectation('/class ArticleFixture/')));
+		$this->Task->execute();
+
+		$this->Task->args = array('Articles');
+		$filename = '/my/path/article_fixture.php';
+		$this->Task->expectAt(2, 'createFile', array($filename, new PatternExpectation('/class ArticleFixture/')));
+		$this->Task->execute();
+
+		$this->Task->args = array('Article');
+		$filename = '/my/path/article_fixture.php';
+		$this->Task->expectAt(3, 'createFile', array($filename, new PatternExpectation('/class ArticleFixture/')));
+		$this->Task->execute();
+	}
+
+/**
+ * test that execute runs all() when args[0] = all
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteIntoAll() {
+		$this->Task->connection = 'test_suite';
+		$this->Task->path = '/my/path/';
+		$this->Task->args = array('all');
+		$this->Task->Model->setReturnValue('listAll', array('articles', 'comments'));
+
+		$filename = '/my/path/article_fixture.php';
+		$this->Task->expectAt(0, 'createFile', array($filename, new PatternExpectation('/class ArticleFixture/')));
+		$this->Task->execute();
+
+		$filename = '/my/path/comment_fixture.php';
+		$this->Task->expectAt(1, 'createFile', array($filename, new PatternExpectation('/class CommentFixture/')));
+		$this->Task->execute();
+	}
+
+/**
+ * test using all() with -count and -records
+ *
+ * @return void
+ * @access public
+ */
+	function testAllWithCountAndRecordsFlags() {
+		$this->Task->connection = 'test_suite';
+		$this->Task->path = '/my/path/';
+		$this->Task->args = array('all');
+		$this->Task->params = array('count' => 10, 'records' => true);
+		$this->Task->Model->setReturnValue('listAll', array('articles', 'comments'));
+
+		$filename = '/my/path/article_fixture.php';
+		$this->Task->expectAt(0, 'createFile', array($filename, new PatternExpectation('/title\' => \'Third Article\'/')));
+
+		$filename = '/my/path/comment_fixture.php';
+		$this->Task->expectAt(1, 'createFile', array($filename, new PatternExpectation('/comment\' => \'First Comment for First Article/')));
+		$this->Task->expectCallCount('createFile', 2);
+		$this->Task->all();
+	}
+
+/**
+ * test interactive mode of execute
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteInteractive() {
+		$this->Task->connection = 'test_suite';
+		$this->Task->path = '/my/path/';
+
+		$this->Task->setReturnValue('in', 'y');
+		$this->Task->Model->setReturnValue('getName', 'Article');
+		$this->Task->Model->setReturnValue('getTable', 'articles', array('Article'));
+
+		$filename = '/my/path/article_fixture.php';
+		$this->Task->expectAt(0, 'createFile', array($filename, new PatternExpectation('/class ArticleFixture/')));
+		$this->Task->execute();
+	}
+
+/**
+ * Test that bake works
+ *
+ * @return void
+ * @access public
+ */
+	function testBake() {
+		$this->Task->connection = 'test_suite';
+		$this->Task->path = '/my/path/';
+
+		$result = $this->Task->bake('Article');
+		$this->assertPattern('/class ArticleFixture extends CakeTestFixture/', $result);
+		$this->assertPattern('/var \$fields/', $result);
+		$this->assertPattern('/var \$records/', $result);
+		$this->assertNoPattern('/var \$import/', $result);
+
+		$result = $this->Task->bake('Article', 'comments');
+		$this->assertPattern('/class ArticleFixture extends CakeTestFixture/', $result);
+		$this->assertPattern('/var \$name \= \'Article\';/', $result);
+		$this->assertPattern('/var \$table \= \'comments\';/', $result);
+		$this->assertPattern('/var \$fields = array\(/', $result);
+
+		$result = $this->Task->bake('Article', 'comments', array('records' => true));
+		$this->assertPattern(
+			"/var \\\$import \= array\('records' \=\> true, 'connection' => 'test_suite'\);/",
+			$result
+		);
+		$this->assertNoPattern('/var \$records/', $result);
+
+		$result = $this->Task->bake('Article', 'comments', array('schema' => 'Article'));
+		$this->assertPattern(
+			"/var \\\$import \= array\('model' \=\> 'Article', 'connection' => 'test_suite'\);/",
+			$result
+		);
+		$this->assertNoPattern('/var \$fields/', $result);
+
+		$result = $this->Task->bake('Article', 'comments', array('schema' => 'Article', 'records' => true));
+		$this->assertPattern(
+			"/var \\\$import \= array\('model' \=\> 'Article'\, 'records' \=\> true, 'connection' => 'test_suite'\);/",
+			$result
+		);
+		$this->assertNoPattern('/var \$fields/', $result);
+		$this->assertNoPattern('/var \$records/', $result);
+	}
+
+/**
+ * test record generation with float and binary types
+ *
+ * @return void
+ * @access public
+ */
+	function testRecordGenerationForBinaryAndFloat() {
+		$this->Task->connection = 'test_suite';
+		$this->Task->path = '/my/path/';
+
+		$result = $this->Task->bake('Article', 'datatypes');
+		$this->assertPattern("/'float_field' => 1/", $result);
+		$this->assertPattern("/'bool' => 1/", $result);
+
+		$result = $this->Task->bake('Article', 'binary_tests');
+		$this->assertPattern("/'data' => 'Lorem ipsum dolor sit amet'/", $result);
+	}
+
+/**
+ * Test that file generation includes headers and correct path for plugins.
+ *
+ * @return void
+ * @access public
+ */
+	function testGenerateFixtureFile() {
+		$this->Task->connection = 'test_suite';
+		$this->Task->path = '/my/path/';
+		$filename = '/my/path/article_fixture.php';
+
+		$this->Task->expectAt(0, 'createFile', array($filename, new PatternExpectation('/Article/')));
+		$result = $this->Task->generateFixtureFile('Article', array());
+
+		$this->Task->expectAt(1, 'createFile', array($filename, new PatternExpectation('/\<\?php(.*)$/ms')));
+		$result = $this->Task->generateFixtureFile('Article', array());
+	}
+
+/**
+ * test generating files into plugins.
+ *
+ * @return void
+ * @access public
+ */
+	function testGeneratePluginFixtureFile() {
+		$this->Task->connection = 'test_suite';
+		$this->Task->path = '/my/path/';
+		$this->Task->plugin = 'TestFixture';
+		$filename = APP . 'plugins' . DS . 'test_fixture' . DS . 'tests' . DS . 'fixtures' . DS . 'article_fixture.php';
+
+		$this->Task->expectAt(0, 'createFile', array($filename, new PatternExpectation('/Article/')));
+		$result = $this->Task->generateFixtureFile('Article', array());
+	}
+}

Added: trunk/src/Web/cake/tests/cases/console/libs/tasks/model.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/console/libs/tasks/model.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/console/libs/tasks/model.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,919 @@
+<?php
+/**
+ * ModelTaskTest file
+ *
+ * Test Case for test generation shell task
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://cakephp.org CakePHP Project
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.tasks
+ * @since         CakePHP v 1.2.6
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Shell', 'Shell', false);
+
+if (!defined('DISABLE_AUTO_DISPATCH')) {
+	define('DISABLE_AUTO_DISPATCH', true);
+}
+
+if (!class_exists('ShellDispatcher')) {
+	ob_start();
+	$argv = false;
+	require CAKE . 'console' .  DS . 'cake.php';
+	ob_end_clean();
+}
+
+require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'model.php';
+require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'fixture.php';
+require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'template.php';
+
+Mock::generatePartial(
+	'ShellDispatcher', 'TestModelTaskMockShellDispatcher',
+	array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment')
+);
+Mock::generatePartial(
+	'ModelTask', 'MockModelTask',
+	array('in', 'out', 'hr', 'err', 'createFile', '_stop', '_checkUnitTest')
+);
+
+Mock::generate(
+	'Model', 'MockModelTaskModel'
+);
+
+Mock::generate(
+	'FixtureTask', 'MockModelTaskFixtureTask'
+);
+
+/**
+ * ModelTaskTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.tasks
+ */
+class ModelTaskTest extends CakeTestCase {
+
+/**
+ * fixtures
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array('core.article', 'core.comment', 'core.articles_tag', 'core.tag', 'core.category_thread');
+
+/**
+ * starTest method
+ *
+ * @return void
+ * @access public
+ */
+	function startTest() {
+		$this->Dispatcher =& new TestModelTaskMockShellDispatcher();
+		$this->Task =& new MockModelTask($this->Dispatcher);
+		$this->Task->name = 'ModelTask';
+		$this->Task->interactive = true;
+		$this->Task->Dispatch =& $this->Dispatcher;
+		$this->Task->Dispatch->shellPaths = App::path('shells');
+		$this->Task->Template =& new TemplateTask($this->Task->Dispatch);
+		$this->Task->Fixture =& new MockModelTaskFixtureTask();
+		$this->Task->Test =& new MockModelTaskFixtureTask();
+	}
+
+/**
+ * endTest method
+ *
+ * @return void
+ * @access public
+ */
+	function endTest() {
+		unset($this->Task, $this->Dispatcher);
+		ClassRegistry::flush();
+	}
+
+/**
+ * Test that listAll scans the database connection and lists all the tables in it.s
+ *
+ * @return void
+ * @access public
+ */
+	function testListAll() {
+		$this->Task->expectAt(1, 'out', array('1. Article'));
+		$this->Task->expectAt(2, 'out', array('2. ArticlesTag'));
+		$this->Task->expectAt(3, 'out', array('3. CategoryThread'));
+		$this->Task->expectAt(4, 'out', array('4. Comment'));
+		$this->Task->expectAt(5, 'out', array('5. Tag'));
+		$result = $this->Task->listAll('test_suite');
+		$expected = array('articles', 'articles_tags', 'category_threads', 'comments', 'tags');
+		$this->assertEqual($result, $expected);
+
+		$this->Task->expectAt(7, 'out', array('1. Article'));
+		$this->Task->expectAt(8, 'out', array('2. ArticlesTag'));
+		$this->Task->expectAt(9, 'out', array('3. CategoryThread'));
+		$this->Task->expectAt(10, 'out', array('4. Comment'));
+		$this->Task->expectAt(11, 'out', array('5. Tag'));
+
+		$this->Task->connection = 'test_suite';
+		$result = $this->Task->listAll();
+		$expected = array('articles', 'articles_tags', 'category_threads', 'comments', 'tags');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * Test that getName interacts with the user and returns the model name.
+ *
+ * @return void
+ * @access public
+ */
+	function testGetName() {
+		$this->Task->setReturnValue('in', 1);
+
+		$this->Task->setReturnValueAt(0, 'in', 'q');
+		$this->Task->expectOnce('_stop');
+		$this->Task->getName('test_suite');
+
+		$this->Task->setReturnValueAt(1, 'in', 1);
+		$result = $this->Task->getName('test_suite');
+		$expected = 'Article';
+		$this->assertEqual($result, $expected);
+
+		$this->Task->setReturnValueAt(2, 'in', 4);
+		$result = $this->Task->getName('test_suite');
+		$expected = 'Comment';
+		$this->assertEqual($result, $expected);
+
+		$this->Task->setReturnValueAt(3, 'in', 10);
+		$result = $this->Task->getName('test_suite');
+		$this->Task->expectOnce('err');
+	}
+
+/**
+ * Test table name interactions
+ *
+ * @return void
+ * @access public
+ */
+	function testGetTableName() {
+		$this->Task->setReturnValueAt(0, 'in', 'y');
+		$result = $this->Task->getTable('Article', 'test_suite');
+		$expected = 'articles';
+		$this->assertEqual($result, $expected);
+
+		$this->Task->setReturnValueAt(1, 'in', 'n');
+		$this->Task->setReturnValueAt(2, 'in', 'my_table');
+		$result = $this->Task->getTable('Article', 'test_suite');
+		$expected = 'my_table';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that initializing the validations works.
+ *
+ * @return void
+ * @access public
+ */
+	function testInitValidations() {
+		$result = $this->Task->initValidations();
+		$this->assertTrue(in_array('notempty', $result));
+	}
+
+/**
+ * test that individual field validation works, with interactive = false
+ * tests the guessing features of validation
+ *
+ * @return void
+ * @access public
+ */
+	function testFieldValidationGuessing() {
+		$this->Task->interactive = false;
+		$this->Task->initValidations();
+
+		$result = $this->Task->fieldValidation('text', array('type' => 'string', 'length' => 10, 'null' => false));
+		$expected = array('notempty' => 'notempty');
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Task->fieldValidation('text', array('type' => 'date', 'length' => 10, 'null' => false));
+		$expected = array('date' => 'date');
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Task->fieldValidation('text', array('type' => 'time', 'length' => 10, 'null' => false));
+		$expected = array('time' => 'time');
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Task->fieldValidation('email', array('type' => 'string', 'length' => 10, 'null' => false));
+		$expected = array('email' => 'email');
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Task->fieldValidation('test', array('type' => 'integer', 'length' => 10, 'null' => false));
+		$expected = array('numeric' => 'numeric');
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Task->fieldValidation('test', array('type' => 'boolean', 'length' => 10, 'null' => false));
+		$expected = array('boolean' => 'boolean');
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Task->fieldValidation('test', array('type' => 'string', 'length' => 36, 'null' => false));
+		$expected = array('uuid' => 'uuid');
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * test that interactive field validation works and returns multiple validators.
+ *
+ * @return void
+ * @access public
+ */
+	function testInteractiveFieldValidation() {
+		$this->Task->initValidations();
+		$this->Task->interactive = true;
+		$this->Task->setReturnValueAt(0, 'in', '19');
+		$this->Task->setReturnValueAt(1, 'in', 'y');
+		$this->Task->setReturnValueAt(2, 'in', '15');
+		$this->Task->setReturnValueAt(3, 'in', 'n');
+
+		$result = $this->Task->fieldValidation('text', array('type' => 'string', 'length' => 10, 'null' => false));
+		$expected = array('notempty' => 'notempty', 'maxlength' => 'maxlength');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that a bogus response doesn't cause errors to bubble up.
+ *
+ * @return void
+ */
+	function testInteractiveFieldValidationWithBogusResponse() {
+		$this->Task->initValidations();
+		$this->Task->interactive = true;
+		$this->Task->setReturnValueAt(0, 'in', '999999');
+		$this->Task->setReturnValueAt(1, 'in', '19');
+		$this->Task->setReturnValueAt(2, 'in', 'n');
+		$this->Task->expectAt(4, 'out', array(new PatternExpectation('/make a valid/')));
+
+		$result = $this->Task->fieldValidation('text', array('type' => 'string', 'length' => 10, 'null' => false));
+		$expected = array('notempty' => 'notempty');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that a regular expression can be used for validation.
+ *
+ * @return void
+ */
+	function testInteractiveFieldValidationWithRegexp() {
+		$this->Task->initValidations();
+		$this->Task->interactive = true;
+		$this->Task->setReturnValueAt(0, 'in', '/^[a-z]{0,9}$/');
+		$this->Task->setReturnValueAt(1, 'in', 'n');
+
+		$result = $this->Task->fieldValidation('text', array('type' => 'string', 'length' => 10, 'null' => false));
+		$expected = array('a_z_0_9' => '/^[a-z]{0,9}$/');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test the validation Generation routine
+ *
+ * @return void
+ * @access public
+ */
+	function testNonInteractiveDoValidation() {
+		$Model =& new MockModelTaskModel();
+		$Model->primaryKey = 'id';
+		$Model->setReturnValue('schema', array(
+			'id' => array(
+				'type' => 'integer',
+				'length' => 11,
+				'null' => false,
+				'key' => 'primary',
+			),
+			'name' => array(
+				'type' => 'string',
+				'length' => 20,
+				'null' => false,
+			),
+			'email' => array(
+				'type' => 'string',
+				'length' => 255,
+				'null' => false,
+			),
+			'some_date' => array(
+				'type' => 'date',
+				'length' => '',
+				'null' => false,
+			),
+			'some_time' => array(
+				'type' => 'time',
+				'length' => '',
+				'null' => false,
+			),
+			'created' => array(
+				'type' => 'datetime',
+				'length' => '',
+				'null' => false,
+			)
+		));
+		$this->Task->interactive = false;
+
+		$result = $this->Task->doValidation($Model);
+		$expected = array(
+			'name' => array(
+				'notempty' => 'notempty'
+			),
+			'email' => array(
+				'email' => 'email',
+			),
+			'some_date' => array(
+				'date' => 'date'
+			),
+			'some_time' => array(
+				'time' => 'time'
+			),
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that finding primary key works
+ *
+ * @return void
+ * @access public
+ */
+	function testFindPrimaryKey() {
+		$fields = array(
+			'one' => array(),
+			'two' => array(),
+			'key' => array('key' => 'primary')
+		);
+		$this->Task->expectAt(0, 'in', array('*', null, 'key'));
+		$this->Task->setReturnValue('in', 'my_field');
+		$result = $this->Task->findPrimaryKey($fields);
+		$expected = 'my_field';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test finding Display field
+ *
+ * @return void
+ * @access public
+ */
+	function testFindDisplayField() {
+		$fields = array('id' => array(), 'tagname' => array(), 'body' => array(),
+			'created' => array(), 'modified' => array());
+
+		$this->Task->setReturnValue('in', 'n');
+		$this->Task->setReturnValueAt(0, 'in', 'n');
+		$result = $this->Task->findDisplayField($fields);
+		$this->assertFalse($result);
+
+		$this->Task->setReturnValueAt(1, 'in', 'y');
+		$this->Task->setReturnValueAt(2, 'in', 2);
+		$result = $this->Task->findDisplayField($fields);
+		$this->assertEqual($result, 'tagname');
+	}
+
+/**
+ * test that belongsTo generation works.
+ *
+ * @return void
+ * @access public
+ */
+	function testBelongsToGeneration() {
+		$model = new Model(array('ds' => 'test_suite', 'name' => 'Comment'));
+		$result = $this->Task->findBelongsTo($model, array());
+		$expected = array(
+			'belongsTo' => array(
+				array(
+					'alias' => 'Article',
+					'className' => 'Article',
+					'foreignKey' => 'article_id',
+				),
+				array(
+					'alias' => 'User',
+					'className' => 'User',
+					'foreignKey' => 'user_id',
+				),
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$model = new Model(array('ds' => 'test_suite', 'name' => 'CategoryThread'));
+		$result = $this->Task->findBelongsTo($model, array());
+		$expected = array(
+			'belongsTo' => array(
+				array(
+					'alias' => 'ParentCategoryThread',
+					'className' => 'CategoryThread',
+					'foreignKey' => 'parent_id',
+				),
+			)
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that hasOne and/or hasMany relations are generated properly.
+ *
+ * @return void
+ * @access public
+ */
+	function testHasManyHasOneGeneration() {
+		$model = new Model(array('ds' => 'test_suite', 'name' => 'Article'));
+		$this->Task->connection = 'test_suite';
+		$this->Task->listAll();
+		$result = $this->Task->findHasOneAndMany($model, array());
+		$expected = array(
+			'hasMany' => array(
+				array(
+					'alias' => 'Comment',
+					'className' => 'Comment',
+					'foreignKey' => 'article_id',
+				),
+			),
+			'hasOne' => array(
+				array(
+					'alias' => 'Comment',
+					'className' => 'Comment',
+					'foreignKey' => 'article_id',
+				),
+			),
+		);
+		$this->assertEqual($result, $expected);
+
+		$model = new Model(array('ds' => 'test_suite', 'name' => 'CategoryThread'));
+		$result = $this->Task->findHasOneAndMany($model, array());
+		$expected = array(
+			'hasOne' => array(
+				array(
+					'alias' => 'ChildCategoryThread',
+					'className' => 'CategoryThread',
+					'foreignKey' => 'parent_id',
+				),
+			),
+			'hasMany' => array(
+				array(
+					'alias' => 'ChildCategoryThread',
+					'className' => 'CategoryThread',
+					'foreignKey' => 'parent_id',
+				),
+			)
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * Test that HABTM generation works
+ *
+ * @return void
+ * @access public
+ */
+	function testHasAndBelongsToManyGeneration() {
+		$model = new Model(array('ds' => 'test_suite', 'name' => 'Article'));
+		$this->Task->connection = 'test_suite';
+		$this->Task->listAll();
+		$result = $this->Task->findHasAndBelongsToMany($model, array());
+		$expected = array(
+			'hasAndBelongsToMany' => array(
+				array(
+					'alias' => 'Tag',
+					'className' => 'Tag',
+					'foreignKey' => 'article_id',
+					'joinTable' => 'articles_tags',
+					'associationForeignKey' => 'tag_id',
+				),
+			),
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test non interactive doAssociations
+ *
+ * @return void
+ * @access public
+ */
+	function testDoAssociationsNonInteractive() {
+		$this->Task->connection = 'test_suite';
+		$this->Task->interactive = false;
+		$model = new Model(array('ds' => 'test_suite', 'name' => 'Article'));
+		$result = $this->Task->doAssociations($model);
+		$expected = array(
+			'hasMany' => array(
+				array(
+					'alias' => 'Comment',
+					'className' => 'Comment',
+					'foreignKey' => 'article_id',
+				),
+			),
+			'hasAndBelongsToMany' => array(
+				array(
+					'alias' => 'Tag',
+					'className' => 'Tag',
+					'foreignKey' => 'article_id',
+					'joinTable' => 'articles_tags',
+					'associationForeignKey' => 'tag_id',
+				),
+			),
+		);
+	}
+
+/**
+ * Ensure that the fixutre object is correctly called.
+ *
+ * @return void
+ * @access public
+ */
+	function testBakeFixture() {
+		$this->Task->plugin = 'test_plugin';
+		$this->Task->interactive = true;
+		$this->Task->Fixture->expectAt(0, 'bake', array('Article', 'articles'));
+		$this->Task->bakeFixture('Article', 'articles');
+
+		$this->assertEqual($this->Task->plugin, $this->Task->Fixture->plugin);
+		$this->assertEqual($this->Task->connection, $this->Task->Fixture->connection);
+		$this->assertEqual($this->Task->interactive, $this->Task->Fixture->interactive);
+	}
+
+/**
+ * Ensure that the test object is correctly called.
+ *
+ * @return void
+ * @access public
+ */
+	function testBakeTest() {
+		$this->Task->plugin = 'test_plugin';
+		$this->Task->interactive = true;
+		$this->Task->Test->expectAt(0, 'bake', array('Model', 'Article'));
+		$this->Task->bakeTest('Article');
+
+		$this->assertEqual($this->Task->plugin, $this->Task->Test->plugin);
+		$this->assertEqual($this->Task->connection, $this->Task->Test->connection);
+		$this->assertEqual($this->Task->interactive, $this->Task->Test->interactive);
+	}
+
+/**
+ * test confirming of associations, and that when an association is hasMany
+ * a question for the hasOne is also not asked.
+ *
+ * @return void
+ * @access public
+ */
+	function testConfirmAssociations() {
+		$associations = array(
+			'hasOne' => array(
+				array(
+					'alias' => 'ChildCategoryThread',
+					'className' => 'CategoryThread',
+					'foreignKey' => 'parent_id',
+				),
+			),
+			'hasMany' => array(
+				array(
+					'alias' => 'ChildCategoryThread',
+					'className' => 'CategoryThread',
+					'foreignKey' => 'parent_id',
+				),
+			),
+			'belongsTo' => array(
+				array(
+					'alias' => 'User',
+					'className' => 'User',
+					'foreignKey' => 'user_id',
+				),
+			)
+		);
+		$model = new Model(array('ds' => 'test_suite', 'name' => 'CategoryThread'));
+		$this->Task->setReturnValueAt(0, 'in', 'y');
+		$result = $this->Task->confirmAssociations($model, $associations);
+		$this->assertTrue(empty($result['hasOne']));
+
+		$this->Task->setReturnValue('in', 'n');
+		$result = $this->Task->confirmAssociations($model, $associations);
+		$this->assertTrue(empty($result['hasMany']));
+		$this->assertTrue(empty($result['hasOne']));
+	}
+
+/**
+ * test that inOptions generates questions and only accepts a valid answer
+ *
+ * @return void
+ * @access public
+ */
+	function testInOptions() {
+		$options = array('one', 'two', 'three');
+		$this->Task->expectAt(0, 'out', array('1. one'));
+		$this->Task->expectAt(1, 'out', array('2. two'));
+		$this->Task->expectAt(2, 'out', array('3. three'));
+		$this->Task->setReturnValueAt(0, 'in', 10);
+
+		$this->Task->expectAt(3, 'out', array('1. one'));
+		$this->Task->expectAt(4, 'out', array('2. two'));
+		$this->Task->expectAt(5, 'out', array('3. three'));
+		$this->Task->setReturnValueAt(1, 'in', 2);
+		$result = $this->Task->inOptions($options, 'Pick a number');
+		$this->assertEqual($result, 1);
+	}
+
+/**
+ * test baking validation
+ *
+ * @return void
+ * @access public
+ */
+	function testBakeValidation() {
+		$validate = array(
+			'name' => array(
+				'notempty' => 'notempty'
+			),
+			'email' => array(
+				'email' => 'email',
+			),
+			'some_date' => array(
+				'date' => 'date'
+			),
+			'some_time' => array(
+				'time' => 'time'
+			)
+		);
+		$result = $this->Task->bake('Article', compact('validate'));
+		$this->assertPattern('/class Article extends AppModel \{/', $result);
+		$this->assertPattern('/\$name \= \'Article\'/', $result);
+		$this->assertPattern('/\$validate \= array\(/', $result);
+		$expected = <<< STRINGEND
+array(
+			'notempty' => array(
+				'rule' => array('notempty'),
+				//'message' => 'Your custom message here',
+				//'allowEmpty' => false,
+				//'required' => false,
+				//'last' => false, // Stop validation after this rule
+				//'on' => 'create', // Limit validation to 'create' or 'update' operations
+			),
+STRINGEND;
+		$this->assertPattern('/' . preg_quote(str_replace("\r\n", "\n", $expected), '/') . '/', $result);
+	}
+
+/**
+ * test baking relations
+ *
+ * @return void
+ * @access public
+ */
+	function testBakeRelations() {
+		$associations = array(
+			'belongsTo' => array(
+				array(
+					'alias' => 'SomethingElse',
+					'className' => 'SomethingElse',
+					'foreignKey' => 'something_else_id',
+				),
+				array(
+					'alias' => 'User',
+					'className' => 'User',
+					'foreignKey' => 'user_id',
+				),
+			),
+			'hasOne' => array(
+				array(
+					'alias' => 'OtherModel',
+					'className' => 'OtherModel',
+					'foreignKey' => 'other_model_id',
+				),
+			),
+			'hasMany' => array(
+				array(
+					'alias' => 'Comment',
+					'className' => 'Comment',
+					'foreignKey' => 'parent_id',
+				),
+			),
+			'hasAndBelongsToMany' => array(
+				array(
+					'alias' => 'Tag',
+					'className' => 'Tag',
+					'foreignKey' => 'article_id',
+					'joinTable' => 'articles_tags',
+					'associationForeignKey' => 'tag_id',
+				),
+			)
+		);
+		$result = $this->Task->bake('Article', compact('associations'));
+		$this->assertPattern('/\$hasAndBelongsToMany \= array\(/', $result);
+		$this->assertPattern('/\$hasMany \= array\(/', $result);
+		$this->assertPattern('/\$belongsTo \= array\(/', $result);
+		$this->assertPattern('/\$hasOne \= array\(/', $result);
+		$this->assertPattern('/Tag/', $result);
+		$this->assertPattern('/OtherModel/', $result);
+		$this->assertPattern('/SomethingElse/', $result);
+		$this->assertPattern('/Comment/', $result);
+	}
+
+/**
+ * test bake() with a -plugin param
+ *
+ * @return void
+ * @access public
+ */
+	function testBakeWithPlugin() {
+		$this->Task->plugin = 'ControllerTest';
+
+		$path = APP . 'plugins' . DS . 'controller_test' . DS . 'models' . DS . 'article.php';
+		$this->Task->expectAt(0, 'createFile', array($path, '*'));
+		$this->Task->bake('Article', array(), array());
+
+		$this->Task->plugin = 'controllerTest';
+
+		$path = APP . 'plugins' . DS . 'controller_test' . DS . 'models' . DS . 'article.php';
+		$this->Task->expectAt(1, 'createFile', array(
+		$path, new PatternExpectation('/Article extends ControllerTestAppModel/')));
+		$this->Task->bake('Article', array(), array());
+
+		$this->assertEqual(count(ClassRegistry::keys()), 0);
+		$this->assertEqual(count(ClassRegistry::mapKeys()), 0);
+	}
+
+/**
+ * test that execute passes runs bake depending with named model.
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteWithNamedModel() {
+		$this->Task->connection = 'test_suite';
+		$this->Task->path = '/my/path/';
+		$this->Task->args = array('article');
+		$filename = '/my/path/article.php';
+		$this->Task->setReturnValue('_checkUnitTest', 1);
+		$this->Task->expectAt(0, 'createFile', array($filename, new PatternExpectation('/class Article extends AppModel/')));
+		$this->Task->execute();
+
+		$this->assertEqual(count(ClassRegistry::keys()), 0);
+		$this->assertEqual(count(ClassRegistry::mapKeys()), 0);
+	}
+
+/**
+ * test that execute passes with different inflections of the same name.
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteWithNamedModelVariations() {
+		$this->Task->connection = 'test_suite';
+		$this->Task->path = '/my/path/';
+		$this->Task->setReturnValue('_checkUnitTest', 1);
+
+		$this->Task->args = array('article');
+		$filename = '/my/path/article.php';
+
+		$this->Task->expectAt(0, 'createFile', array($filename, new PatternExpectation('/class Article extends AppModel/')));
+		$this->Task->execute();
+
+		$this->Task->args = array('Articles');
+		$this->Task->expectAt(1, 'createFile', array($filename, new PatternExpectation('/class Article extends AppModel/')));
+		$this->Task->execute();
+
+		$this->Task->args = array('articles');
+		$this->Task->expectAt(2, 'createFile', array($filename, new PatternExpectation('/class Article extends AppModel/')));
+		$this->Task->execute();
+	}
+
+/**
+ * test that execute with a model name picks up hasMany associations.
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteWithNamedModelHasManyCreated() {
+		$this->Task->connection = 'test_suite';
+		$this->Task->path = '/my/path/';
+		$this->Task->args = array('article');
+		$filename = '/my/path/article.php';
+		$this->Task->setReturnValue('_checkUnitTest', 1);
+		$this->Task->expectAt(0, 'createFile', array($filename, new PatternExpectation("/'Comment' \=\> array\(/")));
+		$this->Task->execute();
+	}
+
+/**
+ * test that execute runs all() when args[0] = all
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteIntoAll() {
+		$this->Task->connection = 'test_suite';
+		$this->Task->path = '/my/path/';
+		$this->Task->args = array('all');
+		$this->Task->setReturnValue('_checkUnitTest', true);
+
+		$this->Task->Fixture->expectCallCount('bake', 5);
+		$this->Task->Test->expectCallCount('bake', 5);
+
+		$filename = '/my/path/article.php';
+		$this->Task->expectAt(0, 'createFile', array($filename, new PatternExpectation('/class Article/')));
+
+		$filename = '/my/path/articles_tag.php';
+		$this->Task->expectAt(1, 'createFile', array($filename, new PatternExpectation('/class ArticlesTag/')));
+
+		$filename = '/my/path/category_thread.php';
+		$this->Task->expectAt(2, 'createFile', array($filename, new PatternExpectation('/class CategoryThread/')));
+
+		$filename = '/my/path/comment.php';
+		$this->Task->expectAt(3, 'createFile', array($filename, new PatternExpectation('/class Comment/')));
+
+		$filename = '/my/path/tag.php';
+		$this->Task->expectAt(4, 'createFile', array($filename, new PatternExpectation('/class Tag/')));
+
+		$this->Task->execute();
+
+		$this->assertEqual(count(ClassRegistry::keys()), 0);
+		$this->assertEqual(count(ClassRegistry::mapKeys()), 0);
+	}
+
+/**
+ * test that skipTables changes how all() works.
+ *
+ * @return void
+ */
+	function testSkipTablesAndAll() {
+		$this->Task->connection = 'test_suite';
+		$this->Task->path = '/my/path/';
+		$this->Task->args = array('all');
+		$this->Task->setReturnValue('_checkUnitTest', true);
+		$this->Task->skipTables = array('tags');
+
+		$this->Task->Fixture->expectCallCount('bake', 4);
+		$this->Task->Test->expectCallCount('bake', 4);
+
+		$filename = '/my/path/article.php';
+		$this->Task->expectAt(0, 'createFile', array($filename, new PatternExpectation('/class Article/')));
+
+		$filename = '/my/path/articles_tag.php';
+		$this->Task->expectAt(1, 'createFile', array($filename, new PatternExpectation('/class ArticlesTag/')));
+
+		$filename = '/my/path/category_thread.php';
+		$this->Task->expectAt(2, 'createFile', array($filename, new PatternExpectation('/class CategoryThread/')));
+
+		$filename = '/my/path/comment.php';
+		$this->Task->expectAt(3, 'createFile', array($filename, new PatternExpectation('/class Comment/')));
+
+		$this->Task->execute();
+	}
+
+/**
+ * test the interactive side of bake.
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteIntoInteractive() {
+		$this->Task->connection = 'test_suite';
+		$this->Task->path = '/my/path/';
+		$this->Task->interactive = true;
+
+		$this->Task->setReturnValueAt(0, 'in', '1'); //choose article
+		$this->Task->setReturnValueAt(1, 'in', 'n'); //no validation
+		$this->Task->setReturnValueAt(2, 'in', 'y'); //yes to associations
+		$this->Task->setReturnValueAt(3, 'in', 'y'); //yes to comment relation
+		$this->Task->setReturnValueAt(4, 'in', 'y'); //yes to user relation
+		$this->Task->setReturnValueAt(5, 'in', 'y'); //yes to tag relation
+		$this->Task->setReturnValueAt(6, 'in', 'n'); //no to additional assocs
+		$this->Task->setReturnValueAt(7, 'in', 'y'); //yes to looksGood?
+		$this->Task->setReturnValue('_checkUnitTest', true);
+
+		$this->Task->Test->expectOnce('bake');
+		$this->Task->Fixture->expectOnce('bake');
+
+		$filename = '/my/path/article.php';
+		$this->Task->expectOnce('createFile');
+		$this->Task->expectAt(0, 'createFile', array($filename, new PatternExpectation('/class Article/')));
+		$this->Task->execute();
+
+		$this->assertEqual(count(ClassRegistry::keys()), 0);
+		$this->assertEqual(count(ClassRegistry::mapKeys()), 0);
+	}
+
+/**
+ * test using bake interactively with a table that does not exist.
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteWithNonExistantTableName() {
+		$this->Task->connection = 'test_suite';
+		$this->Task->path = '/my/path/';
+
+		$this->Task->expectOnce('_stop');
+		$this->Task->expectOnce('err');
+
+		$this->Task->setReturnValueAt(0, 'in', 'Foobar');
+		$this->Task->setReturnValueAt(1, 'in', 'y');
+		$this->Task->execute();
+	}
+}

Added: trunk/src/Web/cake/tests/cases/console/libs/tasks/plugin.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/console/libs/tasks/plugin.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/console/libs/tasks/plugin.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,263 @@
+<?php
+/**
+ * PluginTask Test file
+ *
+ * Test Case for plugin generation shell task
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://cakephp.org CakePHP Project
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.tasks
+ * @since         CakePHP v 1.3.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Shell', 'Shell', false);
+
+if (!defined('DISABLE_AUTO_DISPATCH')) {
+	define('DISABLE_AUTO_DISPATCH', true);
+}
+
+if (!class_exists('ShellDispatcher')) {
+	ob_start();
+	$argv = false;
+	require CAKE . 'console' .  DS . 'cake.php';
+	ob_end_clean();
+}
+
+require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'plugin.php';
+require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'model.php';
+
+Mock::generatePartial(
+	'ShellDispatcher', 'TestPluginTaskMockShellDispatcher',
+	array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment')
+);
+Mock::generatePartial(
+	'PluginTask', 'MockPluginTask',
+	array('in', '_stop', 'err', 'out', 'createFile')
+);
+
+Mock::generate('ModelTask', 'PluginTestMockModelTask');
+
+/**
+ * PluginTaskPlugin class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.tasks
+ */
+class PluginTaskTest extends CakeTestCase {
+
+/**
+ * startTest method
+ *
+ * @return void
+ * @access public
+ */
+	function startTest() {
+		$this->Dispatcher =& new TestPluginTaskMockShellDispatcher();
+		$this->Dispatcher->shellPaths = App::path('shells');
+		$this->Task =& new MockPluginTask($this->Dispatcher);
+		$this->Task->Dispatch =& $this->Dispatcher;
+		$this->Task->path = TMP . 'tests' . DS;
+	}
+
+/**
+ * startCase methods
+ *
+ * @return void
+ * @access public
+ */
+	function startCase() {
+		$this->_paths = $paths = App::path('plugins');
+		$this->_testPath = array_push($paths, TMP . 'tests' . DS);
+		App::build(array('plugins' => $paths));
+	}
+
+/**
+ * endCase
+ *
+ * @return void
+ * @access public
+ */
+	function endCase() {
+		App::build(array('plugins' => $this->_paths));
+	}
+
+/**
+ * endTest method
+ *
+ * @return void
+ * @access public
+ */
+	function endTest() {
+		ClassRegistry::flush();
+	}
+
+/**
+ * test bake()
+ *
+ * @return void
+ * @access public
+ */
+	function testBakeFoldersAndFiles() {
+		$this->Task->setReturnValueAt(0, 'in', $this->_testPath);
+		$this->Task->setReturnValueAt(1, 'in', 'y');
+		$this->Task->bake('BakeTestPlugin');
+
+		$path = $this->Task->path . 'bake_test_plugin';
+		$this->assertTrue(is_dir($path), 'No plugin dir %s');
+
+		$this->assertTrue(is_dir($path . DS . 'config'), 'No config dir %s');
+		$this->assertTrue(is_dir($path . DS . 'config' . DS . 'schema'), 'No schema dir %s');
+		$this->assertTrue(file_exists($path . DS . 'config' . DS . 'schema' . DS . 'empty'), 'No empty file %s');
+
+		$this->assertTrue(is_dir($path . DS . 'controllers'), 'No controllers dir %s');
+		$this->assertTrue(is_dir($path . DS . 'controllers' . DS .'components'), 'No components dir %s');
+		$this->assertTrue(file_exists($path . DS . 'controllers' . DS . 'components' . DS . 'empty'), 'No empty file %s');
+
+		$this->assertTrue(is_dir($path . DS . 'models'), 'No models dir %s');
+		$this->assertTrue(file_exists($path . DS . 'models' . DS . 'behaviors' . DS . 'empty'), 'No empty file %s');
+		$this->assertTrue(is_dir($path . DS . 'models' . DS . 'datasources'), 'No datasources dir %s');
+		$this->assertTrue(file_exists($path . DS . 'models' . DS . 'datasources' . DS . 'empty'), 'No empty file %s');
+
+		$this->assertTrue(is_dir($path . DS . 'views'), 'No views dir %s');
+		$this->assertTrue(is_dir($path . DS . 'views' . DS . 'helpers'), 'No helpers dir %s');
+		$this->assertTrue(file_exists($path . DS . 'views' . DS . 'helpers' . DS . 'empty'), 'No empty file %s');
+
+		$this->assertTrue(is_dir($path . DS . 'tests'), 'No tests dir %s');
+		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'cases'), 'No cases dir %s');
+
+		$this->assertTrue(
+			is_dir($path . DS . 'tests' . DS . 'cases' . DS . 'components'), 'No components cases dir %s'
+		);
+		$this->assertTrue(
+			file_exists($path . DS . 'tests' . DS . 'cases' . DS . 'components' . DS . 'empty'), 'No empty file %s'
+		);
+
+		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'cases' . DS . 'behaviors'), 'No behaviors cases dir %s');
+		$this->assertTrue(
+			file_exists($path . DS . 'tests' . DS . 'cases' . DS . 'behaviors' . DS . 'empty'), 'No empty file %s'
+		);
+
+		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'cases' . DS . 'helpers'), 'No helpers cases dir %s');
+		$this->assertTrue(
+			file_exists($path . DS . 'tests' . DS . 'cases' . DS . 'helpers' . DS . 'empty'), 'No empty file %s'
+		);
+
+		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'cases' . DS . 'models'), 'No models cases dir %s');
+		$this->assertTrue(
+			file_exists($path . DS . 'tests' . DS . 'cases' . DS . 'models' . DS . 'empty'), 'No empty file %s'
+		);
+
+		$this->assertTrue(
+			is_dir($path . DS . 'tests' . DS . 'cases' . DS . 'controllers'),
+			'No controllers cases dir %s'
+		);
+		$this->assertTrue(
+			file_exists($path . DS . 'tests' . DS . 'cases' . DS . 'controllers' . DS . 'empty'), 'No empty file %s'
+		);
+
+		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'groups'), 'No groups dir %s');
+		$this->assertTrue(file_exists($path . DS . 'tests' . DS . 'groups' . DS . 'empty'), 'No empty file %s');
+
+		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'fixtures'), 'No fixtures dir %s');
+		$this->assertTrue(file_exists($path . DS . 'tests' . DS . 'fixtures' . DS . 'empty'), 'No empty file %s');
+
+		$this->assertTrue(is_dir($path . DS . 'vendors'), 'No vendors dir %s');
+	
+		$this->assertTrue(is_dir($path . DS . 'vendors' . DS . 'shells'), 'No vendors shells dir %s');
+		$this->assertTrue(is_dir($path . DS . 'vendors' . DS . 'shells' . DS . 'tasks'), 'No vendors shells tasks dir %s');
+		$this->assertTrue(file_exists($path . DS . 'vendors' . DS . 'shells' . DS . 'tasks' . DS . 'empty'), 'No empty file %s');
+		$this->assertTrue(is_dir($path . DS . 'libs'), 'No libs dir %s');
+		$this->assertTrue(is_dir($path . DS . 'webroot'), 'No webroot dir %s');
+
+		$file = $path . DS . 'bake_test_plugin_app_controller.php';
+		$this->Task->expectAt(0, 'createFile', array($file, '*'), 'No AppController %s');
+
+		$file = $path . DS . 'bake_test_plugin_app_model.php';
+		$this->Task->expectAt(1, 'createFile', array($file, '*'), 'No AppModel %s');
+
+		$Folder =& new Folder($this->Task->path . 'bake_test_plugin');
+		$Folder->delete();
+	}
+
+/**
+ * test execute with no args, flowing into interactive,
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteWithNoArgs() {
+		$this->Task->setReturnValueAt(0, 'in', 'TestPlugin');
+		$this->Task->setReturnValueAt(1, 'in', '3');
+		$this->Task->setReturnValueAt(2, 'in', 'y');
+		$this->Task->setReturnValueAt(3, 'in', 'n');
+
+		$path = $this->Task->path . 'test_plugin';
+		$file = $path . DS . 'test_plugin_app_controller.php';
+		$this->Task->expectAt(0, 'createFile', array($file, '*'), 'No AppController %s');
+
+		$file = $path . DS . 'test_plugin_app_model.php';
+		$this->Task->expectAt(1, 'createFile', array($file, '*'), 'No AppModel %s');
+
+		$this->Task->args = array();
+		$this->Task->execute();
+
+		$Folder =& new Folder($path);
+		$Folder->delete();
+	}
+
+/**
+ * Test Execute
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteWithOneArg() {
+		$this->Task->setReturnValueAt(0, 'in', $this->_testPath);
+		$this->Task->setReturnValueAt(1, 'in', 'y');
+		$this->Task->Dispatch->args = array('BakeTestPlugin');
+		$this->Task->args =& $this->Task->Dispatch->args;
+
+		$path = $this->Task->path . 'bake_test_plugin';
+		$file = $path . DS . 'bake_test_plugin_app_controller.php';
+		$this->Task->expectAt(0, 'createFile', array($file, '*'), 'No AppController %s');
+
+		$file = $path . DS . 'bake_test_plugin_app_model.php';
+		$this->Task->expectAt(1, 'createFile', array($file, '*'), 'No AppModel %s');
+
+		$this->Task->execute();
+
+		$Folder =& new Folder($this->Task->path . 'bake_test_plugin');
+		$Folder->delete();
+	}
+
+/**
+ * test execute chaining into MVC parts
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteWithTwoArgs() {
+		$this->Task->Model =& new PluginTestMockModelTask();
+		$this->Task->setReturnValueAt(0, 'in', $this->_testPath);
+		$this->Task->setReturnValueAt(1, 'in', 'y');
+
+		$Folder =& new Folder($this->Task->path . 'bake_test_plugin', true);
+
+		$this->Task->Dispatch->args = array('BakeTestPlugin', 'model');
+		$this->Task->args =& $this->Task->Dispatch->args;
+
+		$this->Task->Model->expectOnce('loadTasks');
+		$this->Task->Model->expectOnce('execute');
+		$this->Task->execute();
+		$Folder->delete();
+	}
+}

Added: trunk/src/Web/cake/tests/cases/console/libs/tasks/project.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/console/libs/tasks/project.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/console/libs/tasks/project.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,301 @@
+<?php
+/**
+ * ProjectTask Test file
+ *
+ * Test Case for project generation shell task
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://cakephp.org CakePHP Project
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.tasks
+ * @since         CakePHP v 1.3.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Shell', 'Shell', false);
+
+if (!defined('DISABLE_AUTO_DISPATCH')) {
+	define('DISABLE_AUTO_DISPATCH', true);
+}
+
+if (!class_exists('ShellDispatcher')) {
+	ob_start();
+	$argv = false;
+	require CAKE . 'console' .  DS . 'cake.php';
+	ob_end_clean();
+}
+
+require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'project.php';
+
+Mock::generatePartial(
+	'ShellDispatcher', 'TestProjectTaskMockShellDispatcher',
+	array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment')
+);
+Mock::generatePartial(
+	'ProjectTask', 'MockProjectTask',
+	array('in', '_stop', 'err', 'out', 'createFile')
+);
+
+/**
+ * ProjectTask Test class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.tasks
+ */
+class ProjectTaskTest extends CakeTestCase {
+
+/**
+ * startTest method
+ *
+ * @return void
+ * @access public
+ */
+	function startTest() {
+		$this->Dispatcher =& new TestProjectTaskMockShellDispatcher();
+		$this->Dispatcher->shellPaths = App::path('shells');
+		$this->Task =& new MockProjectTask($this->Dispatcher);
+		$this->Task->Dispatch =& $this->Dispatcher;
+		$this->Task->path = TMP . 'tests' . DS;
+	}
+
+/**
+ * endTest method
+ *
+ * @return void
+ * @access public
+ */
+	function endTest() {
+		ClassRegistry::flush();
+
+		$Folder =& new Folder($this->Task->path . 'bake_test_app');
+		$Folder->delete();
+	}
+
+/**
+ * creates a test project that is used for testing project task.
+ *
+ * @return void
+ * @access protected
+ */
+	function _setupTestProject() {
+		$skel = CAKE_CORE_INCLUDE_PATH . DS . CAKE . 'console' . DS . 'templates' . DS . 'skel';
+		$this->Task->setReturnValueAt(0, 'in', 'y');
+		$this->Task->setReturnValueAt(1, 'in', 'n');
+		$this->Task->bake($this->Task->path . 'bake_test_app', $skel);
+	}
+
+/**
+ * test bake() method and directory creation.
+ *
+ * @return void
+ * @access public
+ */
+	function testBake() {
+		$this->_setupTestProject();
+
+		$path = $this->Task->path . 'bake_test_app';
+		$this->assertTrue(is_dir($path), 'No project dir %s');
+		$this->assertTrue(is_dir($path . DS . 'controllers'), 'No controllers dir %s');
+		$this->assertTrue(is_dir($path . DS . 'controllers' . DS .'components'), 'No components dir %s');
+		$this->assertTrue(is_dir($path . DS . 'models'), 'No models dir %s');
+		$this->assertTrue(is_dir($path . DS . 'views'), 'No views dir %s');
+		$this->assertTrue(is_dir($path . DS . 'views' . DS . 'helpers'), 'No helpers dir %s');
+		$this->assertTrue(is_dir($path . DS . 'tests'), 'No tests dir %s');
+		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'cases'), 'No cases dir %s');
+		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'groups'), 'No groups dir %s');
+		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'fixtures'), 'No fixtures dir %s');
+	}
+
+/**
+ * test bake() method with -empty flag,  directory creation and empty files.
+ *
+ * @return void
+ * @access public
+ */
+	function testBakeEmptyFlag() {
+		$this->Task->params['empty'] = true;
+		$this->_setupTestProject();
+		$path = $this->Task->path . 'bake_test_app';
+		$this->assertTrue(is_dir($path), 'No project dir %s');
+		$this->assertTrue(is_dir($path . DS . 'controllers'), 'No controllers dir %s');
+		$this->assertTrue(is_dir($path . DS . 'controllers' . DS .'components'), 'No components dir %s');
+		$this->assertTrue(is_dir($path . DS . 'models'), 'No models dir %s');
+		$this->assertTrue(is_dir($path . DS . 'views'), 'No views dir %s');
+		$this->assertTrue(is_dir($path . DS . 'views' . DS . 'helpers'), 'No helpers dir %s');
+		$this->assertTrue(is_dir($path . DS . 'tests'), 'No tests dir %s');
+		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'cases'), 'No cases dir %s');
+		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'groups'), 'No groups dir %s');
+		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'fixtures'), 'No fixtures dir %s');
+
+		$this->assertTrue(is_file($path . DS . 'controllers' . DS .'components' . DS . 'empty'), 'No empty file in dir %s');
+		$this->assertTrue(is_file($path . DS . 'locale' . DS . 'eng' . DS . 'LC_MESSAGES' . DS . 'empty'), 'No empty file in dir %s');
+		$this->assertTrue(is_file($path . DS . 'models' . DS . 'behaviors' . DS . 'empty'), 'No empty file in dir %s');
+		$this->assertTrue(is_file($path . DS . 'models' . DS . 'datasources' . DS . 'empty'), 'No empty file in dir %s');
+		$this->assertTrue(is_file($path . DS . 'plugins' . DS . 'empty'), 'No empty file in dir %s');
+		$this->assertTrue(is_file($path . DS . 'tests' . DS . 'cases' . DS . 'behaviors' . DS . 'empty'), 'No empty file in dir %s');
+		$this->assertTrue(is_file($path . DS . 'tests' . DS . 'cases' . DS . 'components' . DS . 'empty'), 'No empty file in dir %s');
+		$this->assertTrue(is_file($path . DS . 'tests' . DS . 'cases' . DS . 'controllers' . DS . 'empty'), 'No empty file in dir %s');
+		$this->assertTrue(is_file($path . DS . 'tests' . DS . 'cases' . DS . 'datasources' . DS . 'empty'), 'No empty file in dir %s');
+		$this->assertTrue(is_file($path . DS . 'tests' . DS . 'cases' . DS . 'helpers' . DS . 'empty'), 'No empty file in dir %s');
+		$this->assertTrue(is_file($path . DS . 'tests' . DS . 'cases' . DS . 'models' . DS . 'empty'), 'No empty file in dir %s');
+		$this->assertTrue(is_file($path . DS . 'tests' . DS . 'cases' . DS . 'shells' . DS . 'empty'), 'No empty file in dir %s');
+		$this->assertTrue(is_file($path . DS . 'tests' . DS . 'fixtures' . DS . 'empty'), 'No empty file in dir %s');
+		$this->assertTrue(is_file($path . DS . 'tests' . DS . 'groups' . DS . 'empty'), 'No empty file in dir %s');
+		$this->assertTrue(is_file($path . DS . 'vendors' . DS . 'shells' . DS . 'tasks' . DS . 'empty'), 'No empty file in dir %s');
+		$this->assertTrue(is_file($path . DS . 'views' . DS . 'errors' . DS . 'empty'), 'No empty file in dir %s');
+		$this->assertTrue(is_file($path . DS . 'views' . DS . 'helpers' . DS . 'empty'), 'No empty file in dir %s');
+		$this->assertTrue(is_file($path . DS . 'views' . DS . 'scaffolds' . DS . 'empty'), 'No empty file in dir %s');
+		$this->assertTrue(is_file($path . DS . 'webroot' . DS . 'js' . DS . 'empty'), 'No empty file in dir %s');
+	}
+
+/**
+ * test generation of Security.salt
+ *
+ * @return void
+ * @access public
+ */
+	function testSecuritySaltGeneration() {
+		$this->_setupTestProject();
+
+		$path = $this->Task->path . 'bake_test_app' . DS;
+		$result = $this->Task->securitySalt($path);
+		$this->assertTrue($result);
+
+		$file =& new File($path . 'config' . DS . 'core.php');
+		$contents = $file->read();
+		$this->assertNoPattern('/DYhG93b0qyJfIxfs2guVoUubWwvniR2G0FgaC9mi/', $contents, 'Default Salt left behind. %s');
+	}
+
+	/**
+	 * test generation of Security.cipherSeed
+	 *
+	 * @return void
+	 * @access public
+	 */
+		function testSecurityCipherSeedGeneration() {
+			$this->_setupTestProject();
+
+			$path = $this->Task->path . 'bake_test_app' . DS;
+			$result = $this->Task->securityCipherSeed($path);
+			$this->assertTrue($result);
+
+			$file =& new File($path . 'config' . DS . 'core.php');
+			$contents = $file->read();
+			$this->assertNoPattern('/76859309657453542496749683645/', $contents, 'Default CipherSeed left behind. %s');
+		}
+
+/**
+ * Test that index.php is generated correctly.
+ *
+ * @return void
+ * @access public
+ */
+	function testIndexPhpGeneration() {
+		$this->_setupTestProject();
+
+		$path = $this->Task->path . 'bake_test_app' . DS;
+		$this->Task->corePath($path);
+
+		$file =& new File($path . 'webroot' . DS . 'index.php');
+		$contents = $file->read();
+		$this->assertNoPattern('/define\(\'CAKE_CORE_INCLUDE_PATH\', \'ROOT/', $contents);
+
+		$file =& new File($path . 'webroot' . DS . 'test.php');
+		$contents = $file->read();
+		$this->assertNoPattern('/define\(\'CAKE_CORE_INCLUDE_PATH\', \'ROOT/', $contents);
+	}
+
+/**
+ * test getPrefix method, and that it returns Routing.prefix or writes to config file.
+ *
+ * @return void
+ * @access public
+ */
+	function testGetPrefix() {
+		Configure::write('Routing.prefixes', array('admin'));
+		$result = $this->Task->getPrefix();
+		$this->assertEqual($result, 'admin_');
+
+		Configure::write('Routing.prefixes', null);
+		$this->_setupTestProject();
+		$this->Task->configPath = $this->Task->path . 'bake_test_app' . DS . 'config' . DS;
+		$this->Task->setReturnValue('in', 'super_duper_admin');
+
+		$result = $this->Task->getPrefix();
+		$this->assertEqual($result, 'super_duper_admin_');
+
+		$file =& new File($this->Task->configPath . 'core.php');
+		$file->delete();
+	}
+
+/**
+ * test cakeAdmin() writing core.php
+ *
+ * @return void
+ * @access public
+ */
+	function testCakeAdmin() {
+		$file =& new File(CONFIGS . 'core.php');
+		$contents = $file->read();;
+		$file =& new File(TMP . 'tests' . DS . 'core.php');
+		$file->write($contents);
+
+		Configure::write('Routing.prefixes', null);
+		$this->Task->configPath = TMP . 'tests' . DS;
+		$result = $this->Task->cakeAdmin('my_prefix');
+		$this->assertTrue($result);
+
+		$this->assertEqual(Configure::read('Routing.prefixes'), array('my_prefix'));
+		$file->delete();
+	}
+
+/**
+ * test getting the prefix with more than one prefix setup
+ *
+ * @return void
+ * @access public
+ */
+	function testGetPrefixWithMultiplePrefixes() {
+		Configure::write('Routing.prefixes', array('admin', 'ninja', 'shinobi'));
+		$this->_setupTestProject();
+		$this->Task->configPath = $this->Task->path . 'bake_test_app' . DS . 'config' . DS;
+		$this->Task->setReturnValue('in', 2);
+
+		$result = $this->Task->getPrefix();
+		$this->assertEqual($result, 'ninja_');
+	}
+
+/**
+ * Test execute method with one param to destination folder.
+ *
+ * @return void
+ * @access public
+ */
+	function testExecute() {
+		$this->Task->params['skel'] = CAKE_CORE_INCLUDE_PATH . DS . CAKE . DS . 'console' . DS. 'templates' . DS . 'skel';
+		$this->Task->params['working'] = TMP . 'tests' . DS;
+
+		$path = $this->Task->path . 'bake_test_app';
+		$this->Task->setReturnValue('in', 'y');
+		$this->Task->setReturnValueAt(0, 'in', $path);
+
+		$this->Task->execute();
+		$this->assertTrue(is_dir($path), 'No project dir %s');
+		$this->assertTrue(is_dir($path . DS . 'controllers'), 'No controllers dir %s');
+		$this->assertTrue(is_dir($path . DS . 'controllers' . DS .'components'), 'No components dir %s');
+		$this->assertTrue(is_dir($path . DS . 'models'), 'No models dir %s');
+		$this->assertTrue(is_dir($path . DS . 'views'), 'No views dir %s');
+		$this->assertTrue(is_dir($path . DS . 'views' . DS . 'helpers'), 'No helpers dir %s');
+		$this->assertTrue(is_dir($path . DS . 'tests'), 'No tests dir %s');
+		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'cases'), 'No cases dir %s');
+		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'groups'), 'No groups dir %s');
+		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'fixtures'), 'No fixtures dir %s');
+	}
+}

Added: trunk/src/Web/cake/tests/cases/console/libs/tasks/template.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/console/libs/tasks/template.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/console/libs/tasks/template.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,189 @@
+<?php
+/**
+ * TemplateTask file
+ *
+ * Test Case for TemplateTask generation shell task
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.tasks
+ * @since         CakePHP(tm) v 1.3
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Shell', 'Shell', false);
+
+if (!defined('DISABLE_AUTO_DISPATCH')) {
+	define('DISABLE_AUTO_DISPATCH', true);
+}
+
+if (!class_exists('ShellDispatcher')) {
+	ob_start();
+	$argv = false;
+	require CAKE . 'console' .  DS . 'cake.php';
+	ob_end_clean();
+}
+
+require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'template.php';
+
+Mock::generatePartial(
+	'ShellDispatcher', 'TestTemplateTaskMockShellDispatcher',
+	array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment')
+);
+
+Mock::generatePartial(
+	'TemplateTask', 'MockTemplateTask',
+	array('in', 'out', 'err', 'createFile', '_stop')
+);
+
+/**
+ * TemplateTaskTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.tasks
+ */
+class TemplateTaskTest extends CakeTestCase {
+
+/**
+ * startTest method
+ *
+ * @return void
+ * @access public
+ */
+	function startTest() {
+		$this->Dispatcher =& new TestTemplateTaskMockShellDispatcher();
+		$this->Task =& new MockTemplateTask($this->Dispatcher);
+		$this->Task->Dispatch =& $this->Dispatcher;
+		$this->Task->Dispatch->shellPaths = App::path('shells');
+	}
+
+/**
+ * endTest method
+ *
+ * @return void
+ * @access public
+ */
+	function endTest() {
+		unset($this->Task, $this->Dispatcher);
+		ClassRegistry::flush();
+	}
+
+/**
+ * test that set sets variables
+ *
+ * @return void
+ * @access public
+ */
+	function testSet() {
+		$this->Task->set('one', 'two');
+		$this->assertTrue(isset($this->Task->templateVars['one']));
+		$this->assertEqual($this->Task->templateVars['one'], 'two');
+
+		$this->Task->set(array('one' => 'three', 'four' => 'five'));
+		$this->assertTrue(isset($this->Task->templateVars['one']));
+		$this->assertEqual($this->Task->templateVars['one'], 'three');
+		$this->assertTrue(isset($this->Task->templateVars['four']));
+		$this->assertEqual($this->Task->templateVars['four'], 'five');
+		
+		$this->Task->templateVars = array();
+		$this->Task->set(array(3 => 'three', 4 => 'four'));
+		$this->Task->set(array(1 => 'one', 2 => 'two'));
+		$expected = array(3 => 'three', 4 => 'four', 1 => 'one', 2 => 'two');
+		$this->assertEqual($this->Task->templateVars, $expected);
+	}
+
+/**
+ * test finding themes installed in
+ *
+ * @return void
+ * @access public
+ */
+	function testFindingInstalledThemesForBake() {
+		$consoleLibs = CAKE_CORE_INCLUDE_PATH . DS . CAKE . 'console' . DS;
+		$this->Task->Dispatch->shellPaths = array($consoleLibs);
+		$this->Task->initialize();
+		$this->assertEqual($this->Task->templatePaths, array('default' => $consoleLibs . 'templates' . DS . 'default' . DS));
+	}
+
+/**
+ * test getting the correct theme name.  Ensure that with only one theme, or a theme param
+ * that the user is not bugged.  If there are more, find and return the correct theme name
+ *
+ * @return void
+ * @access public
+ */
+	function testGetThemePath() {
+		$defaultTheme = CAKE_CORE_INCLUDE_PATH . DS . dirname(CONSOLE_LIBS) . 'templates' . DS . 'default' .DS;
+		$this->Task->templatePaths = array('default' => $defaultTheme);
+		$this->Task->expectCallCount('in', 1);
+
+		$result = $this->Task->getThemePath();
+		$this->assertEqual($result, $defaultTheme);
+
+		$this->Task->templatePaths = array('default' => $defaultTheme, 'other' => '/some/path');
+		$this->Task->params['theme'] = 'other';
+		$result = $this->Task->getThemePath();
+		$this->assertEqual($result, '/some/path');
+
+		$this->Task->params = array();
+		$this->Task->setReturnValueAt(0, 'in', '1');
+		$result = $this->Task->getThemePath();
+		$this->assertEqual($result, $defaultTheme);
+		$this->assertEqual($this->Dispatcher->params['theme'], 'default');
+	}
+
+/**
+ * test generate
+ *
+ * @return void
+ * @access public
+ */
+	function testGenerate() {
+		App::build(array(
+			'shells' => array(
+				TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS .  'test_app' . DS . 'vendors' . DS . 'shells' . DS
+			)
+		));
+		$this->Task->initialize();
+		$this->Task->setReturnValue('in', 1);
+		$result = $this->Task->generate('classes', 'test_object', array('test' => 'foo'));
+		$expected = "I got rendered\nfoo";
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test generate with a missing template in the chosen theme.
+ * ensure fallback to default works.
+ *
+ * @return void
+ * @access public
+ */
+	function testGenerateWithTemplateFallbacks() {
+		App::build(array(
+			'shells' => array(
+				TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS .  'test_app' . DS . 'vendors' . DS . 'shells' . DS,
+				CAKE_CORE_INCLUDE_PATH . DS . 'console' . DS
+			)
+		));
+		$this->Task->initialize();
+		$this->Task->params['theme'] = 'test';
+		$this->Task->set(array(
+			'model' => 'Article',
+			'table' => 'articles',
+			'import' => false,
+			'records' => false,
+			'schema' => ''
+		));
+		$result = $this->Task->generate('classes', 'fixture');
+		$this->assertPattern('/ArticleFixture extends CakeTestFixture/', $result);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/console/libs/tasks/test.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/console/libs/tasks/test.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/console/libs/tasks/test.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,646 @@
+<?php
+/**
+ * TestTaskTest file
+ *
+ * Test Case for test generation shell task
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP :  Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://cakephp.org CakePHP Project
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.tasks
+ * @since         CakePHP v 1.2.0.7726
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Shell', 'Shell', false);
+App::import('Controller', 'Controller', false);
+App::import('Model', 'Model', false);
+
+if (!defined('DISABLE_AUTO_DISPATCH')) {
+	define('DISABLE_AUTO_DISPATCH', true);
+}
+
+if (!class_exists('ShellDispatcher')) {
+	ob_start();
+	$argv = false;
+	require CAKE . 'console' .  DS . 'cake.php';
+	ob_end_clean();
+}
+
+require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'test.php';
+require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'template.php';
+
+
+Mock::generatePartial(
+	'ShellDispatcher', 'TestTestTaskMockShellDispatcher',
+	array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment')
+);
+Mock::generatePartial(
+	'TestTask', 'MockTestTask',
+	array('in', '_stop', 'err', 'out', 'hr', 'createFile', 'isLoadableClass')
+);
+
+/**
+ * Test Article model
+ *
+ * @package cake
+ * @subpackage cake.tests.cases.console.libs.tasks
+ */
+class TestTaskArticle extends Model {
+
+/**
+ * Model name
+ *
+ * @var string
+ * @access public
+ */
+	var $name = 'TestTaskArticle';
+
+/**
+ * Table name to use
+ *
+ * @var string
+ * @access public
+ */
+	var $useTable = 'articles';
+
+/**
+ * HasMany Associations
+ *
+ * @var array
+ * @access public
+ */
+	var $hasMany = array(
+		'Comment' => array(
+			'className' => 'TestTask.TestTaskComment',
+			'foreignKey' => 'article_id',
+		)
+	);
+
+/**
+ * Has and Belongs To Many Associations
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array(
+		'Tag' => array(
+			'className' => 'TestTaskTag',
+			'joinTable' => 'articles_tags',
+			'foreignKey' => 'article_id',
+			'associationForeignKey' => 'tag_id'
+		)
+	);
+
+/**
+ * Example public method
+ *
+ * @return void
+ * @access public
+ */
+	function doSomething() {
+	}
+
+/**
+ * Example Secondary public method
+ *
+ * @return void
+ * @access public
+ */
+	function doSomethingElse() {
+	}
+
+/**
+ * Example protected method
+ *
+ * @return void
+ * @access protected
+ */
+	function _innerMethod() {
+	}
+}
+
+/**
+ * Tag Testing Model
+ *
+ * @package cake
+ * @subpackage cake.tests.cases.console.libs.tasks
+ */
+class TestTaskTag extends Model {
+
+/**
+ * Model name
+ *
+ * @var string
+ * @access public
+ */
+	var $name = 'TestTaskTag';
+
+/**
+ * Table name
+ *
+ * @var string
+ * @access public
+ */
+	var $useTable = 'tags';
+
+/**
+ * Has and Belongs To Many Associations
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array(
+		'Article' => array(
+			'className' => 'TestTaskArticle',
+			'joinTable' => 'articles_tags',
+			'foreignKey' => 'tag_id',
+			'associationForeignKey' => 'article_id'
+		)
+	);
+}
+
+/**
+ * Simulated plugin
+ *
+ * @package cake
+ * @subpackage cake.tests.cases.console.libs.tasks
+ */
+class TestTaskAppModel extends Model {
+}
+
+/**
+ * Testing AppMode (TaskComment)
+ *
+ * @package cake
+ * @subpackage cake.tests.cases.console.libs.tasks
+ */
+class TestTaskComment extends TestTaskAppModel {
+
+/**
+ * Model name
+ *
+ * @var string
+ * @access public
+ */
+	var $name = 'TestTaskComment';
+
+/**
+ * Table name
+ *
+ * @var string
+ * @access public
+ */
+	var $useTable = 'comments';
+
+/**
+ * Belongs To Associations
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array(
+		'Article' => array(
+			'className' => 'TestTaskArticle',
+			'foreignKey' => 'article_id',
+		)
+	);
+}
+
+/**
+ * Test Task Comments Controller
+ *
+ * @package cake
+ * @subpackage cake.tests.cases.console.libs.tasks
+ */
+class TestTaskCommentsController extends Controller {
+
+/**
+ * Controller Name
+ *
+ * @var string
+ * @access public
+ */
+	var $name = 'TestTaskComments';
+
+/**
+ * Models to use
+ *
+ * @var array
+ * @access public
+ */
+	var $uses = array('TestTaskComment', 'TestTaskTag');
+}
+
+/**
+ * TestTaskTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.tasks
+ */
+class TestTaskTest extends CakeTestCase {
+
+/**
+ * Fixtures
+ *
+ * @var string
+ * @access public
+ */
+	var $fixtures = array('core.article', 'core.comment', 'core.articles_tag', 'core.tag');
+
+/**
+ * startTest method
+ *
+ * @return void
+ * @access public
+ */
+	function startTest() {
+		$this->Dispatcher =& new TestTestTaskMockShellDispatcher();
+		$this->Dispatcher->shellPaths = App::path('shells');
+		$this->Task =& new MockTestTask($this->Dispatcher);
+		$this->Task->name = 'TestTask';
+		$this->Task->Dispatch =& $this->Dispatcher;
+		$this->Task->Template =& new TemplateTask($this->Dispatcher);
+	}
+
+/**
+ * endTest method
+ *
+ * @return void
+ * @access public
+ */
+	function endTest() {
+		ClassRegistry::flush();
+		App::build();
+	}
+
+/**
+ * Test that file path generation doesn't continuously append paths.
+ *
+ * @return void
+ * @access public
+ */
+	function testFilePathGeneration() {
+		$file = TESTS . 'cases' . DS . 'models' . DS . 'my_class.test.php';
+
+		$this->Task->Dispatch->expectNever('stderr');
+		$this->Task->Dispatch->expectNever('_stop');
+
+		$this->Task->setReturnValue('in', 'y');
+		$this->Task->expectAt(0, 'createFile', array($file, '*'));
+		$this->Task->bake('Model', 'MyClass');
+
+		$this->Task->expectAt(1, 'createFile', array($file, '*'));
+		$this->Task->bake('Model', 'MyClass');
+
+		$file = TESTS . 'cases' . DS . 'controllers' . DS . 'comments_controller.test.php';
+		$this->Task->expectAt(2, 'createFile', array($file, '*'));
+		$this->Task->bake('Controller', 'Comments');
+	}
+
+/**
+ * Test that method introspection pulls all relevant non parent class
+ * methods into the test case.
+ *
+ * @return void
+ */
+	function testMethodIntrospection() {
+		$result = $this->Task->getTestableMethods('TestTaskArticle');
+		$expected = array('dosomething', 'dosomethingelse');
+		$this->assertEqual(array_map('strtolower', $result), $expected);
+	}
+
+/**
+ * test that the generation of fixtures works correctly.
+ *
+ * @return void
+ * @access public
+ */
+	function testFixtureArrayGenerationFromModel() {
+		$subject = ClassRegistry::init('TestTaskArticle');
+		$result = $this->Task->generateFixtureList($subject);
+		$expected = array('plugin.test_task.test_task_comment', 'app.articles_tags',
+			'app.test_task_article', 'app.test_task_tag');
+
+		$this->assertEqual(sort($result), sort($expected));
+	}
+
+/**
+ * test that the generation of fixtures works correctly.
+ *
+ * @return void
+ * @access public
+ */
+	function testFixtureArrayGenerationFromController() {
+		$subject = new TestTaskCommentsController();
+		$result = $this->Task->generateFixtureList($subject);
+		$expected = array('plugin.test_task.test_task_comment', 'app.articles_tags',
+			'app.test_task_article', 'app.test_task_tag');
+
+		$this->assertEqual(sort($result), sort($expected));
+	}
+
+/**
+ * test user interaction to get object type
+ *
+ * @return void
+ * @access public
+ */
+	function testGetObjectType() {
+		$this->Task->expectOnce('_stop');
+		$this->Task->setReturnValueAt(0, 'in', 'q');
+		$this->Task->getObjectType();
+
+		$this->Task->setReturnValueAt(1, 'in', 2);
+		$result = $this->Task->getObjectType();
+		$this->assertEqual($result, $this->Task->classTypes[1]);
+	}
+
+/**
+ * creating test subjects should clear the registry so the registry is always fresh
+ *
+ * @return void
+ * @access public
+ */
+	function testRegistryClearWhenBuildingTestObjects() {
+		ClassRegistry::flush();
+		$model = ClassRegistry::init('TestTaskComment');
+		$model->bindModel(array(
+			'belongsTo' => array(
+				'Random' => array(
+					'className' => 'TestTaskArticle',
+					'foreignKey' => 'article_id',
+				)
+			)
+		));
+		$keys = ClassRegistry::keys();
+		$this->assertTrue(in_array('random', $keys));
+		$object =& $this->Task->buildTestSubject('Model', 'TestTaskComment');
+
+		$keys = ClassRegistry::keys();
+		$this->assertFalse(in_array('random', $keys));
+	}
+
+/**
+ * test that getClassName returns the user choice as a classname.
+ *
+ * @return void
+ * @access public
+ */
+	function testGetClassName() {
+		$objects = App::objects('model');
+		$skip = $this->skipIf(empty($objects), 'No models in app, this test will fail. %s');
+		if ($skip) {
+			return;
+		}
+		$this->Task->setReturnValueAt(0, 'in', 'MyCustomClass');
+		$result = $this->Task->getClassName('Model');
+		$this->assertEqual($result, 'MyCustomClass');
+
+		$this->Task->setReturnValueAt(1, 'in', 1);
+		$result = $this->Task->getClassName('Model');
+		$options = App::objects('model');
+		$this->assertEqual($result, $options[0]);
+	}
+
+/**
+ * Test the user interaction for defining additional fixtures.
+ *
+ * @return void
+ * @access public
+ */
+	function testGetUserFixtures() {
+		$this->Task->setReturnValueAt(0, 'in', 'y');
+		$this->Task->setReturnValueAt(1, 'in', 'app.pizza, app.topping, app.side_dish');
+		$result = $this->Task->getUserFixtures();
+		$expected = array('app.pizza', 'app.topping', 'app.side_dish');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that resolving classnames works
+ *
+ * @return void
+ * @access public
+ */
+	function testGetRealClassname() {
+		$result = $this->Task->getRealClassname('Model', 'Post');
+		$this->assertEqual($result, 'Post');
+
+		$result = $this->Task->getRealClassname('Controller', 'Posts');
+		$this->assertEqual($result, 'PostsController');
+
+		$result = $this->Task->getRealClassname('Helper', 'Form');
+		$this->assertEqual($result, 'FormHelper');
+
+		$result = $this->Task->getRealClassname('Behavior', 'Containable');
+		$this->assertEqual($result, 'ContainableBehavior');
+
+		$result = $this->Task->getRealClassname('Component', 'Auth');
+		$this->assertEqual($result, 'AuthComponent');
+	}
+
+/**
+ * test baking files.  The conditionally run tests are known to fail in PHP4
+ * as PHP4 classnames are all lower case, breaking the plugin path inflection.
+ *
+ * @return void
+ * @access public
+ */
+	function testBakeModelTest() {
+		$this->Task->setReturnValue('createFile', true);
+		$this->Task->setReturnValue('isLoadableClass', true);
+
+		$result = $this->Task->bake('Model', 'TestTaskArticle');
+
+		$this->assertPattern('/App::import\(\'Model\', \'TestTaskArticle\'\)/', $result);
+		$this->assertPattern('/class TestTaskArticleTestCase extends CakeTestCase/', $result);
+
+		$this->assertPattern('/function startTest\(\)/', $result);
+		$this->assertPattern("/\\\$this->TestTaskArticle \=\& ClassRegistry::init\('TestTaskArticle'\)/", $result);
+
+		$this->assertPattern('/function endTest\(\)/', $result);
+		$this->assertPattern('/unset\(\$this->TestTaskArticle\)/', $result);
+
+		$this->assertPattern('/function testDoSomething\(\)/i', $result);
+		$this->assertPattern('/function testDoSomethingElse\(\)/i', $result);
+
+		$this->assertPattern("/'app\.test_task_article'/", $result);
+		if (PHP5) {
+			$this->assertPattern("/'plugin\.test_task\.test_task_comment'/", $result);
+		}
+		$this->assertPattern("/'app\.test_task_tag'/", $result);
+		$this->assertPattern("/'app\.articles_tag'/", $result);
+	}
+
+/**
+ * test baking controller test files, ensure that the stub class is generated.
+ * Conditional assertion is known to fail on PHP4 as classnames are all lower case
+ * causing issues with inflection of path name from classname.
+ *
+ * @return void
+ * @access public
+ */
+	function testBakeControllerTest() {
+		$this->Task->setReturnValue('createFile', true);
+		$this->Task->setReturnValue('isLoadableClass', true);
+
+		$result = $this->Task->bake('Controller', 'TestTaskComments');
+
+		$this->assertPattern('/App::import\(\'Controller\', \'TestTaskComments\'\)/', $result);
+		$this->assertPattern('/class TestTaskCommentsControllerTestCase extends CakeTestCase/', $result);
+
+		$this->assertPattern('/class TestTestTaskCommentsController extends TestTaskCommentsController/', $result);
+		$this->assertPattern('/var \$autoRender = false/', $result);
+		$this->assertPattern('/function redirect\(\$url, \$status = null, \$exit = true\)/', $result);
+
+		$this->assertPattern('/function startTest\(\)/', $result);
+		$this->assertPattern("/\\\$this->TestTaskComments \=\& new TestTestTaskCommentsController\(\)/", $result);
+		$this->assertPattern("/\\\$this->TestTaskComments->constructClasses\(\)/", $result);
+
+		$this->assertPattern('/function endTest\(\)/', $result);
+		$this->assertPattern('/unset\(\$this->TestTaskComments\)/', $result);
+
+		$this->assertPattern("/'app\.test_task_article'/", $result);
+		if (PHP5) {
+			$this->assertPattern("/'plugin\.test_task\.test_task_comment'/", $result);
+		}
+		$this->assertPattern("/'app\.test_task_tag'/", $result);
+		$this->assertPattern("/'app\.articles_tag'/", $result);
+	}
+
+/**
+ * test Constructor generation ensure that constructClasses is called for controllers
+ *
+ * @return void
+ * @access public
+ */
+	function testGenerateConstructor() {
+		$result = $this->Task->generateConstructor('controller', 'PostsController');
+		$expected = "new TestPostsController();\n\t\t\$this->Posts->constructClasses();\n";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Task->generateConstructor('model', 'Post');
+		$expected = "ClassRegistry::init('Post');\n";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Task->generateConstructor('helper', 'FormHelper');
+		$expected = "new FormHelper();\n";
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * Test that mock class generation works for the appropriate classes
+ *
+ * @return void
+ * @access public
+ */
+	function testMockClassGeneration() {
+		$result = $this->Task->hasMockClass('controller');
+		$this->assertTrue($result);
+	}
+
+/**
+ * test bake() with a -plugin param
+ *
+ * @return void
+ * @access public
+ */
+	function testBakeWithPlugin() {
+		$this->Task->plugin = 'TestTest';
+
+		$path = APP . 'plugins' . DS . 'test_test' . DS . 'tests' . DS . 'cases' . DS . 'helpers' . DS . 'form.test.php';
+		$this->Task->expectAt(0, 'createFile', array($path, '*'));
+		$this->Task->bake('Helper', 'Form');
+	}
+
+/**
+ * test interactive with plugins lists from the plugin
+ *
+ * @return void
+ */
+	function testInteractiveWithPlugin() {
+		$testApp = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS;
+		App::build(array(
+			'plugins' => array($testApp)
+		), true);
+
+		$this->Task->plugin = 'TestPlugin';
+		$path = $testApp . 'test_plugin' . DS . 'tests' . DS . 'cases' . DS . 'helpers' . DS . 'other_helper.test.php';
+		$this->Task->setReturnValueAt(0, 'in', 5); //helper
+		$this->Task->setReturnValueAt(1, 'in', 1); //OtherHelper
+		$this->Task->expectAt(0, 'createFile', array($path, '*'));
+		$this->Task->expectAt(9, 'out', array('1. OtherHelper'));
+		$this->Task->execute();
+	}
+
+/**
+ * Test filename generation for each type + plugins
+ *
+ * @return void
+ * @access public
+ */
+	function testTestCaseFileName() {
+		$this->Task->path = '/my/path/tests/';
+
+		$result = $this->Task->testCaseFileName('Model', 'Post');
+		$expected = $this->Task->path . 'cases' . DS . 'models' . DS . 'post.test.php';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Task->testCaseFileName('Helper', 'Form');
+		$expected = $this->Task->path . 'cases' . DS . 'helpers' . DS . 'form.test.php';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Task->testCaseFileName('Controller', 'Posts');
+		$expected = $this->Task->path . 'cases' . DS . 'controllers' . DS . 'posts_controller.test.php';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Task->testCaseFileName('Behavior', 'Containable');
+		$expected = $this->Task->path . 'cases' . DS . 'behaviors' . DS . 'containable.test.php';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Task->testCaseFileName('Component', 'Auth');
+		$expected = $this->Task->path . 'cases' . DS . 'components' . DS . 'auth.test.php';
+		$this->assertEqual($result, $expected);
+
+		$this->Task->plugin = 'TestTest';
+		$result = $this->Task->testCaseFileName('Model', 'Post');
+		$expected = APP . 'plugins' . DS . 'test_test' . DS . 'tests' . DS . 'cases' . DS . 'models' . DS . 'post.test.php';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test execute with a type defined
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteWithOneArg() {
+		$this->Task->args[0] = 'Model';
+		$this->Task->setReturnValueAt(0, 'in', 'TestTaskTag');
+		$this->Task->setReturnValue('isLoadableClass', true);
+		$this->Task->expectAt(0, 'createFile', array('*', new PatternExpectation('/class TestTaskTagTestCase extends CakeTestCase/')));
+		$this->Task->execute();
+	}
+
+/**
+ * test execute with type and class name defined
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteWithTwoArgs() {
+		$this->Task->args = array('Model', 'TestTaskTag');
+		$this->Task->setReturnValueAt(0, 'in', 'TestTaskTag');
+		$this->Task->setReturnValue('isLoadableClass', true);
+		$this->Task->expectAt(0, 'createFile', array('*', new PatternExpectation('/class TestTaskTagTestCase extends CakeTestCase/')));
+		$this->Task->execute();
+	}
+}

Added: trunk/src/Web/cake/tests/cases/console/libs/tasks/view.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/console/libs/tasks/view.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/console/libs/tasks/view.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,678 @@
+<?php
+/**
+ * ViewTask Test file
+ *
+ * Test Case for view generation shell task
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://cakephp.org CakePHP Project
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.tasks
+ * @since         CakePHP v 1.2.0.7726
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Shell', 'Shell', false);
+
+if (!defined('DISABLE_AUTO_DISPATCH')) {
+	define('DISABLE_AUTO_DISPATCH', true);
+}
+
+if (!class_exists('ShellDispatcher')) {
+	ob_start();
+	$argv = false;
+	require CAKE . 'console' .  DS . 'cake.php';
+	ob_end_clean();
+}
+
+require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'view.php';
+require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'controller.php';
+require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'template.php';
+require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'project.php';
+
+Mock::generatePartial(
+	'ShellDispatcher', 'TestViewTaskMockShellDispatcher',
+	array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment')
+);
+Mock::generatePartial(
+	'ViewTask', 'MockViewTask',
+	array('in', '_stop', 'err', 'out', 'createFile')
+);
+
+Mock::generate('ControllerTask', 'ViewTaskMockControllerTask');
+Mock::generate('ProjectTask', 'ViewTaskMockProjectTask');
+
+/**
+ * Test View Task Comment Model
+ *
+ * @package cake
+ * @subpackage cake.tests.cases.console.libs.tasks
+ */
+class ViewTaskComment extends Model {
+
+/**
+ * Model name
+ *
+ * @var string
+ * @access public
+ */
+	var $name = 'ViewTaskComment';
+
+/**
+ * Table name
+ *
+ * @var string
+ * @access public
+ */
+	var $useTable = 'comments';
+
+/**
+ * Belongs To Associations
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array(
+		'Article' => array(
+			'className' => 'ViewTaskArticle',
+			'foreignKey' => 'article_id'
+		)
+	);
+}
+
+/**
+ * Test View Task Article Model
+ *
+ * @package cake
+ * @subpackage cake.tests.cases.console.libs.tasks
+ */
+class ViewTaskArticle extends Model {
+
+/**
+ * Model name
+ *
+ * @var string
+ * @access public
+ */
+	var $name = 'ViewTaskArticle';
+
+/**
+ * Table name
+ *
+ * @var string
+ * @access public
+ */
+	var $useTable = 'articles';
+}
+
+/**
+ * Test View Task Comments Controller
+ *
+ * @package cake
+ * @subpackage cake.tests.cases.console.libs.tasks
+ */
+class ViewTaskCommentsController extends Controller {
+
+/**
+ * Controller name
+ *
+ * @var string
+ * @access public
+ */
+	var $name = 'ViewTaskComments';
+
+/**
+ * Testing public controller action
+ *
+ * @return void
+ * @access public
+ */
+	function index() {
+	}
+
+/**
+ * Testing public controller action
+ *
+ * @return void
+ * @access public
+ */
+	function add() {
+	}
+}
+
+/**
+ * Test View Task Articles Controller
+ *
+ * @package cake
+ * @subpackage cake.tests.cases.console.libs.tasks
+ */
+class ViewTaskArticlesController extends Controller {
+
+/**
+ * Controller name
+ *
+ * @var string
+ * @access public
+ */
+	var $name = 'ViewTaskArticles';
+
+/**
+ * Test public controller action
+ *
+ * @return void
+ * @access public
+ */
+	function index() {
+	}
+
+/**
+ * Test public controller action
+ *
+ * @return void
+ * @access public
+ */
+	function add() {
+	}
+
+/**
+ * Test admin prefixed controller action
+ *
+ * @return void
+ * @access public
+ */
+	function admin_index() {
+	}
+
+/**
+ * Test admin prefixed controller action
+ *
+ * @return void
+ * @access public
+ */
+	function admin_add() {
+	}
+
+/**
+ * Test admin prefixed controller action
+ *
+ * @return void
+ * @access public
+ */
+	function admin_view() {
+	}
+
+/**
+ * Test admin prefixed controller action
+ *
+ * @return void
+ * @access public
+ */
+	function admin_edit() {
+	}
+
+/**
+ * Test admin prefixed controller action
+ *
+ * @return void
+ * @access public
+ */
+	function admin_delete() {
+	}
+}
+
+/**
+ * ViewTaskTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.tasks
+ */
+class ViewTaskTest extends CakeTestCase {
+
+/**
+ * Fixtures
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array('core.article', 'core.comment', 'core.articles_tag', 'core.tag');
+
+/**
+ * startTest method
+ *
+ * Ensure that the default theme is used
+ *
+ * @return void
+ * @access public
+ */
+	function startTest() {
+		$this->Dispatcher =& new TestViewTaskMockShellDispatcher();
+		$this->Dispatcher->shellPaths = App::path('shells');
+		$this->Task =& new MockViewTask($this->Dispatcher);
+		$this->Task->name = 'ViewTask';
+		$this->Task->Dispatch =& $this->Dispatcher;
+		$this->Task->Template =& new TemplateTask($this->Dispatcher);
+		$this->Task->Controller =& new ViewTaskMockControllerTask();
+		$this->Task->Project =& new ViewTaskMockProjectTask();
+		$this->Task->DbConfig =& new ViewTaskMockProjectTask();
+		$this->Task->path = TMP;
+		$this->Task->Template->params['theme'] = 'default';
+		
+		$this->_routing = Configure::read('Routing');
+	}
+
+/**
+ * endTest method
+ *
+ * @return void
+ * @access public
+ */
+	function endTest() {
+		ClassRegistry::flush();
+		Configure::write('Routing', $this->_routing);
+	}
+
+/**
+ * Test getContent and parsing of Templates.
+ *
+ * @return void
+ * @access public
+ */
+	function testGetContent() {
+		$vars = array(
+			'modelClass' => 'TestViewModel',
+			'schema' => array(),
+			'primaryKey' => 'id',
+			'displayField' => 'name',
+			'singularVar' => 'testViewModel',
+			'pluralVar' => 'testViewModels',
+			'singularHumanName' => 'Test View Model',
+			'pluralHumanName' => 'Test View Models',
+			'fields' => array('id', 'name', 'body'),
+			'associations' => array()
+		);
+		$result = $this->Task->getContent('view', $vars);
+
+		$this->assertPattern('/Delete Test View Model/', $result);
+		$this->assertPattern('/Edit Test View Model/', $result);
+		$this->assertPattern('/List Test View Models/', $result);
+		$this->assertPattern('/New Test View Model/', $result);
+
+		$this->assertPattern('/testViewModel\[\'TestViewModel\'\]\[\'id\'\]/', $result);
+		$this->assertPattern('/testViewModel\[\'TestViewModel\'\]\[\'name\'\]/', $result);
+		$this->assertPattern('/testViewModel\[\'TestViewModel\'\]\[\'body\'\]/', $result);
+	}
+
+/**
+ * test getContent() using an admin_prefixed action.
+ *
+ * @return void
+ * @access public
+ */
+	function testGetContentWithAdminAction() {
+		$_back = Configure::read('Routing');
+		Configure::write('Routing.prefixes', array('admin'));
+		$vars = array(
+			'modelClass' => 'TestViewModel',
+			'schema' => array(),
+			'primaryKey' => 'id',
+			'displayField' => 'name',
+			'singularVar' => 'testViewModel',
+			'pluralVar' => 'testViewModels',
+			'singularHumanName' => 'Test View Model',
+			'pluralHumanName' => 'Test View Models',
+			'fields' => array('id', 'name', 'body'),
+			'associations' => array()
+		);
+		$result = $this->Task->getContent('admin_view', $vars);
+
+		$this->assertPattern('/Delete Test View Model/', $result);
+		$this->assertPattern('/Edit Test View Model/', $result);
+		$this->assertPattern('/List Test View Models/', $result);
+		$this->assertPattern('/New Test View Model/', $result);
+
+		$this->assertPattern('/testViewModel\[\'TestViewModel\'\]\[\'id\'\]/', $result);
+		$this->assertPattern('/testViewModel\[\'TestViewModel\'\]\[\'name\'\]/', $result);
+		$this->assertPattern('/testViewModel\[\'TestViewModel\'\]\[\'body\'\]/', $result);
+
+		$result = $this->Task->getContent('admin_add', $vars);
+		$this->assertPattern("/input\('name'\)/", $result);
+		$this->assertPattern("/input\('body'\)/", $result);
+		$this->assertPattern('/List Test View Models/', $result);
+
+		Configure::write('Routing', $_back);
+	}
+
+/**
+ * test Bake method
+ *
+ * @return void
+ * @access public
+ */
+	function testBake() {
+		$this->Task->controllerName = 'ViewTaskComments';
+		$this->Task->controllerPath = 'view_task_comments';
+
+		$this->Task->expectAt(0, 'createFile', array(
+			TMP . 'view_task_comments' . DS . 'view.ctp',
+			new PatternExpectation('/View Task Articles/')
+		));
+		$this->Task->bake('view', true);
+
+		$this->Task->expectAt(1, 'createFile', array(TMP . 'view_task_comments' . DS . 'edit.ctp', '*'));
+		$this->Task->bake('edit', true);
+
+		$this->Task->expectAt(2, 'createFile', array(
+			TMP . 'view_task_comments' . DS . 'index.ctp',
+			new PatternExpectation('/\$viewTaskComment\[\'Article\'\]\[\'title\'\]/')
+		));
+		$this->Task->bake('index', true);
+	}
+
+/**
+ * test that baking a view with no template doesn't make a file.
+ *
+ * @return void
+ */
+	function testBakeWithNoTemplate() {
+		$this->Task->controllerName = 'ViewTaskComments';
+		$this->Task->controllerPath = 'view_task_comments';
+
+		$this->Task->expectNever('createFile');
+		$this->Task->bake('delete', true);
+	}
+
+/**
+ * test bake() with a -plugin param
+ *
+ * @return void
+ * @access public
+ */
+	function testBakeWithPlugin() {
+		$this->Task->controllerName = 'ViewTaskComments';
+		$this->Task->controllerPath = 'view_task_comments';
+		$this->Task->plugin = 'TestTest';
+
+		$path = APP . 'plugins' . DS . 'test_test' . DS . 'views' . DS . 'view_task_comments' . DS  . 'view.ctp';
+		$this->Task->expectAt(0, 'createFile', array($path, '*'));
+		$this->Task->bake('view', true);
+	}
+
+/**
+ * test bake actions baking multiple actions.
+ *
+ * @return void
+ * @access public
+ */
+	function testBakeActions() {
+		$this->Task->controllerName = 'ViewTaskComments';
+		$this->Task->controllerPath = 'view_task_comments';
+
+		$this->Task->expectAt(0, 'createFile', array(
+			TMP . 'view_task_comments' . DS . 'view.ctp',
+			new PatternExpectation('/View Task Comments/')
+		));
+		$this->Task->expectAt(1, 'createFile', array(
+			TMP . 'view_task_comments' . DS . 'edit.ctp',
+			new PatternExpectation('/Edit View Task Comment/')
+		));
+		$this->Task->expectAt(2, 'createFile', array(
+			TMP . 'view_task_comments' . DS . 'index.ctp',
+			new PatternExpectation('/ViewTaskComment/')
+		));
+
+		$this->Task->bakeActions(array('view', 'edit', 'index'), array());
+	}
+
+/**
+ * test baking a customAction (non crud)
+ *
+ * @return void
+ * @access public
+ */
+	function testCustomAction() {
+		$this->Task->controllerName = 'ViewTaskComments';
+		$this->Task->controllerPath = 'view_task_comments';
+		$this->Task->params['app'] = APP;
+
+		$this->Task->setReturnValueAt(0, 'in', '');
+		$this->Task->setReturnValueAt(1, 'in', 'my_action');
+		$this->Task->setReturnValueAt(2, 'in', 'y');
+		$this->Task->expectAt(0, 'createFile', array(TMP . 'view_task_comments' . DS . 'my_action.ctp', '*'));
+
+		$this->Task->customAction();
+	}
+
+/**
+ * Test all()
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteIntoAll() {
+		$this->Task->args[0] = 'all';
+
+		$this->Task->Controller->setReturnValue('listAll', array('view_task_comments'));
+		$this->Task->Controller->expectOnce('listAll');
+
+		$this->Task->expectCallCount('createFile', 2);
+		$this->Task->expectAt(0, 'createFile', array(TMP . 'view_task_comments' . DS . 'index.ctp', '*'));
+		$this->Task->expectAt(1, 'createFile', array(TMP . 'view_task_comments' . DS . 'add.ctp', '*'));
+
+		$this->Task->execute();
+	}
+
+/**
+ * Test all() with action parameter
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteIntoAllWithActionName() {
+		$this->Task->args = array('all', 'index');
+
+		$this->Task->Controller->setReturnValue('listAll', array('view_task_comments'));
+		$this->Task->Controller->expectOnce('listAll');
+
+		$this->Task->expectCallCount('createFile', 1);
+		$this->Task->expectAt(0, 'createFile', array(TMP . 'view_task_comments' . DS . 'index.ctp', '*'));
+
+		$this->Task->execute();
+	}
+
+/**
+ * test `cake bake view $controller view`
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteWithActionParam() {
+		$this->Task->args[0] = 'ViewTaskComments';
+		$this->Task->args[1] = 'view';
+
+		$this->Task->expectCallCount('createFile', 1);
+		$this->Task->expectAt(0, 'createFile', array(TMP . 'view_task_comments' . DS . 'view.ctp', '*'));
+		$this->Task->execute();
+	}
+
+/**
+ * test `cake bake view $controller`
+ * Ensure that views are only baked for actions that exist in the controller.
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteWithController() {
+		$this->Task->args[0] = 'ViewTaskComments';
+
+		$this->Task->expectCallCount('createFile', 2);
+		$this->Task->expectAt(0, 'createFile', array(TMP . 'view_task_comments' . DS . 'index.ctp', '*'));
+		$this->Task->expectAt(1, 'createFile', array(TMP . 'view_task_comments' . DS . 'add.ctp', '*'));
+
+		$this->Task->execute();
+	}
+
+/**
+ * test that both plural and singular forms can be used for baking views.
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteWithControllerVariations() {
+		$this->Task->args = array('ViewTaskComments');
+
+		$this->Task->expectAt(0, 'createFile', array(TMP . 'view_task_comments' . DS . 'index.ctp', '*'));
+		$this->Task->expectAt(1, 'createFile', array(TMP . 'view_task_comments' . DS . 'add.ctp', '*'));
+		$this->Task->execute();
+		
+		$this->Task->args = array('ViewTaskComment');
+
+		$this->Task->expectAt(0, 'createFile', array(TMP . 'view_task_comments' . DS . 'index.ctp', '*'));
+		$this->Task->expectAt(1, 'createFile', array(TMP . 'view_task_comments' . DS . 'add.ctp', '*'));
+		$this->Task->execute();
+	}
+
+/**
+ * test `cake bake view $controller -admin`
+ * Which only bakes admin methods, not non-admin methods.
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteWithControllerAndAdminFlag() {
+		$_back = Configure::read('Routing');
+		Configure::write('Routing.prefixes', array('admin'));
+		$this->Task->args[0] = 'ViewTaskArticles';
+		$this->Task->params['admin'] = 1;
+		$this->Task->Project->setReturnValue('getPrefix', 'admin_');
+
+		$this->Task->expectCallCount('createFile', 4);
+		$this->Task->expectAt(0, 'createFile', array(TMP . 'view_task_articles' . DS . 'admin_index.ctp', '*'));
+		$this->Task->expectAt(1, 'createFile', array(TMP . 'view_task_articles' . DS . 'admin_add.ctp', '*'));
+		$this->Task->expectAt(2, 'createFile', array(TMP . 'view_task_articles' . DS . 'admin_view.ctp', '*'));
+		$this->Task->expectAt(3, 'createFile', array(TMP . 'view_task_articles' . DS . 'admin_edit.ctp', '*'));
+
+		$this->Task->execute();
+		Configure::write('Routing', $_back);
+	}
+
+/**
+ * test execute into interactive.
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteInteractive() {
+		$this->Task->connection = 'test_suite';
+		$this->Task->args = array();
+		$this->Task->params = array();
+
+		$this->Task->Controller->setReturnValue('getName', 'ViewTaskComments');
+		$this->Task->setReturnValue('in', 'y');
+		$this->Task->setReturnValueAt(0, 'in', 'y');
+		$this->Task->setReturnValueAt(1, 'in', 'y');
+		$this->Task->setReturnValueAt(2, 'in', 'n');
+
+		$this->Task->expectCallCount('createFile', 4);
+		$this->Task->expectAt(0, 'createFile', array(
+			TMP . 'view_task_comments' . DS . 'index.ctp',
+			new PatternExpectation('/ViewTaskComment/')
+		));
+		$this->Task->expectAt(1, 'createFile', array(
+			TMP . 'view_task_comments' . DS . 'view.ctp',
+			new PatternExpectation('/ViewTaskComment/')
+		));
+		$this->Task->expectAt(2, 'createFile', array(
+			TMP . 'view_task_comments' . DS . 'add.ctp',
+			new PatternExpectation('/Add View Task Comment/')
+		));
+		$this->Task->expectAt(3, 'createFile', array(
+			TMP . 'view_task_comments' . DS . 'edit.ctp',
+			new PatternExpectation('/Edit View Task Comment/')
+		));
+
+		$this->Task->execute();
+	}
+
+/**
+ * test `cake bake view posts index list`
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteWithAlternateTemplates() {
+		$this->Task->connection = 'test_suite';
+		$this->Task->args = array('ViewTaskComments', 'index', 'list');
+		$this->Task->params = array();
+
+		$this->Task->expectCallCount('createFile', 1);
+		$this->Task->expectAt(0, 'createFile', array(
+			TMP . 'view_task_comments' . DS . 'list.ctp',
+			new PatternExpectation('/ViewTaskComment/')
+		));
+		$this->Task->execute();
+	}
+
+/**
+ * test execute into interactive() with admin methods.
+ *
+ * @return void
+ * @access public
+ */
+	function testExecuteInteractiveWithAdmin() {
+		Configure::write('Routing.prefixes', array('admin'));
+		$this->Task->connection = 'test_suite';
+		$this->Task->args = array();
+
+		$this->Task->Controller->setReturnValue('getName', 'ViewTaskComments');
+		$this->Task->Project->setReturnValue('getPrefix', 'admin_');
+		$this->Task->setReturnValueAt(0, 'in', 'y');
+		$this->Task->setReturnValueAt(1, 'in', 'n');
+		$this->Task->setReturnValueAt(2, 'in', 'y');
+
+		$this->Task->expectCallCount('createFile', 4);
+		$this->Task->expectAt(0, 'createFile', array(
+			TMP . 'view_task_comments' . DS . 'admin_index.ctp',
+			new PatternExpectation('/ViewTaskComment/')
+		));
+		$this->Task->expectAt(1, 'createFile', array(
+			TMP . 'view_task_comments' . DS . 'admin_view.ctp',
+			new PatternExpectation('/ViewTaskComment/')
+		));
+		$this->Task->expectAt(2, 'createFile', array(
+			TMP . 'view_task_comments' . DS . 'admin_add.ctp',
+			new PatternExpectation('/Add View Task Comment/')
+		));
+		$this->Task->expectAt(3, 'createFile', array(
+			TMP . 'view_task_comments' . DS . 'admin_edit.ctp',
+			new PatternExpectation('/Edit View Task Comment/')
+		));
+
+		$this->Task->execute();
+	}
+
+/**
+ * test getting templates, make sure noTemplateActions works
+ *
+ * @return void
+ */
+	function testGetTemplate() {
+		$result = $this->Task->getTemplate('delete');
+		$this->assertFalse($result);
+
+		$result = $this->Task->getTemplate('add');
+		$this->assertEqual($result, 'form');
+
+		Configure::write('Routing.prefixes', array('admin'));
+
+		$result = $this->Task->getTemplate('admin_add');
+		$this->assertEqual($result, 'form');
+	}
+
+}

Added: trunk/src/Web/cake/tests/cases/dispatcher.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/dispatcher.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/dispatcher.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,2625 @@
+<?php
+/**
+ * DispatcherTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+require_once CAKE . 'dispatcher.php';
+
+if (!class_exists('AppController')) {
+	require_once LIBS . 'controller' . DS . 'app_controller.php';
+} elseif (!defined('APP_CONTROLLER_EXISTS')){
+	define('APP_CONTROLLER_EXISTS', true);
+}
+
+/**
+ * TestDispatcher class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases
+ */
+class TestDispatcher extends Dispatcher {
+
+/**
+ * invoke method
+ *
+ * @param mixed $controller
+ * @param mixed $params
+ * @param mixed $missingAction
+ * @return void
+ * @access protected
+ */
+	function _invoke(&$controller, $params) {
+		restore_error_handler();
+		if ($result = parent::_invoke($controller, $params)) {
+			if ($result[0] === 'missingAction') {
+				return $result;
+			}
+		}
+		set_error_handler('simpleTestErrorHandler');
+
+		return $controller;
+	}
+
+/**
+ * cakeError method
+ *
+ * @param mixed $filename
+ * @return void
+ * @access public
+ */
+	function cakeError($filename, $params) {
+		return array($filename, $params);
+	}
+
+/**
+ * _stop method
+ *
+ * @return void
+ * @access protected
+ */
+	function _stop() {
+		$this->stopped = true;
+		return true;
+	}
+}
+
+/**
+ * MyPluginAppController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases
+ */
+class MyPluginAppController extends AppController {
+}
+
+/**
+ * MyPluginController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases
+ */
+class MyPluginController extends MyPluginAppController {
+
+/**
+ * name property
+ *
+ * @var string 'MyPlugin'
+ * @access public
+ */
+	var $name = 'MyPlugin';
+
+/**
+ * uses property
+ *
+ * @var array
+ * @access public
+ */
+	var $uses = array();
+
+/**
+ * index method
+ *
+ * @return void
+ * @access public
+ */
+	function index() {
+		return true;
+	}
+
+/**
+ * add method
+ *
+ * @return void
+ * @access public
+ */
+	function add() {
+		return true;
+	}
+
+/**
+ * admin_add method
+ *
+ * @param mixed $id
+ * @return void
+ * @access public
+ */
+	function admin_add($id = null) {
+		return $id;
+	}
+}
+
+/**
+ * SomePagesController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases
+ */
+class SomePagesController extends AppController {
+
+/**
+ * name property
+ *
+ * @var string 'SomePages'
+ * @access public
+ */
+	var $name = 'SomePages';
+
+/**
+ * uses property
+ *
+ * @var array
+ * @access public
+ */
+	var $uses = array();
+
+/**
+ * display method
+ *
+ * @param mixed $page
+ * @return void
+ * @access public
+ */
+	function display($page = null) {
+		return $page;
+	}
+
+/**
+ * index method
+ *
+ * @return void
+ * @access public
+ */
+	function index() {
+		return true;
+	}
+
+/**
+ * protected method
+ *
+ * @return void
+ * @access protected
+ */
+	function _protected() {
+		return true;
+	}
+
+/**
+ * redirect method overriding
+ *
+ * @return void
+ * @access public
+ */
+	function redirect() {
+		echo 'this should not be accessible';
+	}
+}
+
+/**
+ * OtherPagesController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases
+ */
+class OtherPagesController extends MyPluginAppController {
+
+/**
+ * name property
+ *
+ * @var string 'OtherPages'
+ * @access public
+ */
+	var $name = 'OtherPages';
+
+/**
+ * uses property
+ *
+ * @var array
+ * @access public
+ */
+	var $uses = array();
+
+/**
+ * display method
+ *
+ * @param mixed $page
+ * @return void
+ * @access public
+ */
+	function display($page = null) {
+		return $page;
+	}
+
+/**
+ * index method
+ *
+ * @return void
+ * @access public
+ */
+	function index() {
+		return true;
+	}
+}
+
+/**
+ * TestDispatchPagesController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases
+ */
+class TestDispatchPagesController extends AppController {
+
+/**
+ * name property
+ *
+ * @var string 'TestDispatchPages'
+ * @access public
+ */
+	var $name = 'TestDispatchPages';
+
+/**
+ * uses property
+ *
+ * @var array
+ * @access public
+ */
+	var $uses = array();
+
+/**
+ * admin_index method
+ *
+ * @return void
+ * @access public
+ */
+	function admin_index() {
+		return true;
+	}
+
+/**
+ * camelCased method
+ *
+ * @return void
+ * @access public
+ */
+	function camelCased() {
+		return true;
+	}
+}
+
+/**
+ * ArticlesTestAppController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases
+ */
+class ArticlesTestAppController extends AppController {
+}
+
+/**
+ * ArticlesTestController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases
+ */
+class ArticlesTestController extends ArticlesTestAppController {
+
+/**
+ * name property
+ *
+ * @var string 'ArticlesTest'
+ * @access public
+ */
+	var $name = 'ArticlesTest';
+
+/**
+ * uses property
+ *
+ * @var array
+ * @access public
+ */
+	var $uses = array();
+
+/**
+ * admin_index method
+ *
+ * @return void
+ * @access public
+ */
+	function admin_index() {
+		return true;
+	}
+/**
+ * fake index method.
+ *
+ * @return void
+ */
+	function index() {
+		return true;
+	}
+}
+
+/**
+ * SomePostsController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases
+ */
+class SomePostsController extends AppController {
+
+/**
+ * name property
+ *
+ * @var string 'SomePosts'
+ * @access public
+ */
+	var $name = 'SomePosts';
+
+/**
+ * uses property
+ *
+ * @var array
+ * @access public
+ */
+	var $uses = array();
+
+/**
+ * autoRender property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $autoRender = false;
+
+/**
+ * beforeFilter method
+ *
+ * @return void
+ * @access public
+ */
+	function beforeFilter() {
+		if ($this->params['action'] == 'index') {
+			$this->params['action'] = 'view';
+		} else {
+			$this->params['action'] = 'change';
+		}
+		$this->params['pass'] = array('changed');
+	}
+
+/**
+ * index method
+ *
+ * @return void
+ * @access public
+ */
+	function index() {
+		return true;
+	}
+
+/**
+ * change method
+ *
+ * @return void
+ * @access public
+ */
+	function change() {
+		return true;
+	}
+}
+
+/**
+ * TestCachedPagesController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases
+ */
+class TestCachedPagesController extends AppController {
+
+/**
+ * name property
+ *
+ * @var string 'TestCachedPages'
+ * @access public
+ */
+	var $name = 'TestCachedPages';
+
+/**
+ * uses property
+ *
+ * @var array
+ * @access public
+ */
+	var $uses = array();
+
+/**
+ * helpers property
+ *
+ * @var array
+ * @access public
+ */
+	var $helpers = array('Cache');
+
+/**
+ * cacheAction property
+ *
+ * @var array
+ * @access public
+ */
+	var $cacheAction = array(
+		'index' => '+2 sec',
+		'test_nocache_tags' => '+2 sec',
+		'view' => '+2 sec'
+	);
+
+/**
+ * viewPath property
+ *
+ * @var string 'posts'
+ * @access public
+ */
+	var $viewPath = 'posts';
+
+/**
+ * index method
+ *
+ * @return void
+ * @access public
+ */
+	function index() {
+		$this->render();
+	}
+
+/**
+ * test_nocache_tags method
+ *
+ * @return void
+ * @access public
+ */
+	function test_nocache_tags() {
+		$this->render();
+	}
+
+/**
+ * view method
+ *
+ * @return void
+ * @access public
+ */
+	function view($id = null) {
+		$this->render('index');
+	}
+/**
+ * test cached forms / tests view object being registered
+ *
+ * @return void
+ */
+	function cache_form() {
+		$this->cacheAction = 10;
+		$this->helpers[] = 'Form';
+	}
+}
+
+/**
+ * TimesheetsController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases
+ */
+class TimesheetsController extends AppController {
+
+/**
+ * name property
+ *
+ * @var string 'Timesheets'
+ * @access public
+ */
+	var $name = 'Timesheets';
+
+/**
+ * uses property
+ *
+ * @var array
+ * @access public
+ */
+	var $uses = array();
+
+/**
+ * index method
+ *
+ * @return void
+ * @access public
+ */
+	function index() {
+		return true;
+	}
+}
+
+/**
+ * DispatcherTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases
+ */
+class DispatcherTest extends CakeTestCase {
+
+/**
+ * setUp method
+ *
+ * @return void
+ * @access public
+ */
+	function startTest() {
+		$this->_get = $_GET;
+		$_GET = array();
+		$this->_post = $_POST;
+		$this->_files = $_FILES;
+		$this->_server = $_SERVER;
+
+		$this->_app = Configure::read('App');
+		Configure::write('App.base', false);
+		Configure::write('App.baseUrl', false);
+		Configure::write('App.dir', 'app');
+		Configure::write('App.webroot', 'webroot');
+
+		$this->_cache = Configure::read('Cache');
+		Configure::write('Cache.disable', true);
+
+		$this->_debug = Configure::read('debug');
+
+		App::build(App::core());
+	}
+
+/**
+ * tearDown method
+ *
+ * @return void
+ * @access public
+ */
+	function endTest() {
+		$_GET = $this->_get;
+		$_POST = $this->_post;
+		$_FILES = $this->_files;
+		$_SERVER = $this->_server;
+		App::build();
+		Configure::write('App', $this->_app);
+		Configure::write('Cache', $this->_cache);
+		Configure::write('debug', $this->_debug);
+	}
+
+/**
+ * testParseParamsWithoutZerosAndEmptyPost method
+ *
+ * @return void
+ * @access public
+ */
+	function testParseParamsWithoutZerosAndEmptyPost() {
+		$Dispatcher =& new Dispatcher();
+		$test = $Dispatcher->parseParams("/testcontroller/testaction/params1/params2/params3");
+		$this->assertIdentical($test['controller'], 'testcontroller');
+		$this->assertIdentical($test['action'], 'testaction');
+		$this->assertIdentical($test['pass'][0], 'params1');
+		$this->assertIdentical($test['pass'][1], 'params2');
+		$this->assertIdentical($test['pass'][2], 'params3');
+		$this->assertFalse(!empty($test['form']));
+	}
+
+/**
+ * testParseParamsReturnsPostedData method
+ *
+ * @return void
+ * @access public
+ */
+	function testParseParamsReturnsPostedData() {
+		$_POST['testdata'] = "My Posted Content";
+		$Dispatcher =& new Dispatcher();
+		$test = $Dispatcher->parseParams("/");
+		$this->assertTrue($test['form'], "Parsed URL not returning post data");
+		$this->assertIdentical($test['form']['testdata'], "My Posted Content");
+	}
+
+/**
+ * testParseParamsWithSingleZero method
+ *
+ * @return void
+ * @access public
+ */
+	function testParseParamsWithSingleZero() {
+		$Dispatcher =& new Dispatcher();
+		$test = $Dispatcher->parseParams("/testcontroller/testaction/1/0/23");
+		$this->assertIdentical($test['controller'], 'testcontroller');
+		$this->assertIdentical($test['action'], 'testaction');
+		$this->assertIdentical($test['pass'][0], '1');
+		$this->assertPattern('/\\A(?:0)\\z/', $test['pass'][1]);
+		$this->assertIdentical($test['pass'][2], '23');
+	}
+
+/**
+ * testParseParamsWithManySingleZeros method
+ *
+ * @return void
+ * @access public
+ */
+	function testParseParamsWithManySingleZeros() {
+		$Dispatcher =& new Dispatcher();
+		$test = $Dispatcher->parseParams("/testcontroller/testaction/0/0/0/0/0/0");
+		$this->assertPattern('/\\A(?:0)\\z/', $test['pass'][0]);
+		$this->assertPattern('/\\A(?:0)\\z/', $test['pass'][1]);
+		$this->assertPattern('/\\A(?:0)\\z/', $test['pass'][2]);
+		$this->assertPattern('/\\A(?:0)\\z/', $test['pass'][3]);
+		$this->assertPattern('/\\A(?:0)\\z/', $test['pass'][4]);
+		$this->assertPattern('/\\A(?:0)\\z/', $test['pass'][5]);
+	}
+
+/**
+ * testParseParamsWithManyZerosInEachSectionOfUrl method
+ *
+ * @return void
+ * @access public
+ */
+	function testParseParamsWithManyZerosInEachSectionOfUrl() {
+		$Dispatcher =& new Dispatcher();
+		$test = $Dispatcher->parseParams("/testcontroller/testaction/000/0000/00000/000000/000000/0000000");
+		$this->assertPattern('/\\A(?:000)\\z/', $test['pass'][0]);
+		$this->assertPattern('/\\A(?:0000)\\z/', $test['pass'][1]);
+		$this->assertPattern('/\\A(?:00000)\\z/', $test['pass'][2]);
+		$this->assertPattern('/\\A(?:000000)\\z/', $test['pass'][3]);
+		$this->assertPattern('/\\A(?:000000)\\z/', $test['pass'][4]);
+		$this->assertPattern('/\\A(?:0000000)\\z/', $test['pass'][5]);
+	}
+
+/**
+ * testParseParamsWithMixedOneToManyZerosInEachSectionOfUrl method
+ *
+ * @return void
+ * @access public
+ */
+	function testParseParamsWithMixedOneToManyZerosInEachSectionOfUrl() {
+		$Dispatcher =& new Dispatcher();
+		$test = $Dispatcher->parseParams("/testcontroller/testaction/01/0403/04010/000002/000030/0000400");
+		$this->assertPattern('/\\A(?:01)\\z/', $test['pass'][0]);
+		$this->assertPattern('/\\A(?:0403)\\z/', $test['pass'][1]);
+		$this->assertPattern('/\\A(?:04010)\\z/', $test['pass'][2]);
+		$this->assertPattern('/\\A(?:000002)\\z/', $test['pass'][3]);
+		$this->assertPattern('/\\A(?:000030)\\z/', $test['pass'][4]);
+		$this->assertPattern('/\\A(?:0000400)\\z/', $test['pass'][5]);
+	}
+
+/**
+ * testQueryStringOnRoot method
+ *
+ * @return void
+ * @access public
+ */
+	function testQueryStringOnRoot() {
+		Router::reload();
+		Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home'));
+		Router::connect('/pages/*', array('controller' => 'pages', 'action' => 'display'));
+
+		$_GET = array('coffee' => 'life', 'sleep' => 'sissies');
+		$Dispatcher =& new Dispatcher();
+		$uri = 'posts/home/?coffee=life&sleep=sissies';
+		$result = $Dispatcher->parseParams($uri);
+		$this->assertPattern('/posts/', $result['controller']);
+		$this->assertPattern('/home/', $result['action']);
+		$this->assertTrue(isset($result['url']['sleep']));
+		$this->assertTrue(isset($result['url']['coffee']));
+
+		$Dispatcher =& new Dispatcher();
+		$uri = '/?coffee=life&sleep=sissy';
+		$result = $Dispatcher->parseParams($uri);
+		$this->assertPattern('/pages/', $result['controller']);
+		$this->assertPattern('/display/', $result['action']);
+		$this->assertTrue(isset($result['url']['sleep']));
+		$this->assertTrue(isset($result['url']['coffee']));
+		$this->assertEqual($result['url']['coffee'], 'life');
+	}
+
+/**
+ * testFileUploadArrayStructure method
+ *
+ * @return void
+ * @access public
+ */
+	function testFileUploadArrayStructure() {
+		$_FILES = array('data' => array('name' => array(
+			'File' => array(
+				array('data' => 'cake_mssql_patch.patch'),
+					array('data' => 'controller.diff'),
+					array('data' => ''),
+					array('data' => ''),
+				),
+				'Post' => array('attachment' => 'jquery-1.2.1.js'),
+			),
+			'type' => array(
+				'File' => array(
+					array('data' => ''),
+					array('data' => ''),
+					array('data' => ''),
+					array('data' => ''),
+				),
+				'Post' => array('attachment' => 'application/x-javascript'),
+			),
+			'tmp_name' => array(
+				'File' => array(
+					array('data' => '/private/var/tmp/phpy05Ywj'),
+					array('data' => '/private/var/tmp/php7MBztY'),
+					array('data' => ''),
+					array('data' => ''),
+				),
+				'Post' => array('attachment' => '/private/var/tmp/phpEwlrIo'),
+			),
+			'error' => array(
+				'File' => array(
+					array('data' => 0),
+					array('data' => 0),
+					array('data' => 4),
+					array('data' => 4)
+				),
+				'Post' => array('attachment' => 0)
+			),
+			'size' => array(
+				'File' => array(
+					array('data' => 6271),
+					array('data' => 350),
+					array('data' => 0),
+					array('data' => 0),
+				),
+				'Post' => array('attachment' => 80469)
+			),
+		));
+
+		$Dispatcher =& new Dispatcher();
+		$result = $Dispatcher->parseParams('/');
+
+		$expected = array(
+			'File' => array(
+				array('data' => array(
+					'name' => 'cake_mssql_patch.patch',
+					'type' => '',
+					'tmp_name' => '/private/var/tmp/phpy05Ywj',
+					'error' => 0,
+					'size' => 6271,
+				),
+			),
+			array('data' => array(
+				'name' => 'controller.diff',
+				'type' => '',
+				'tmp_name' => '/private/var/tmp/php7MBztY',
+				'error' => 0,
+				'size' => 350,
+			)),
+			array('data' => array(
+				'name' => '',
+				'type' => '',
+				'tmp_name' => '',
+				'error' => 4,
+				'size' => 0,
+			)),
+			array('data' => array(
+				'name' => '',
+				'type' => '',
+				'tmp_name' => '',
+				'error' => 4,
+				'size' => 0,
+			)),
+		),
+		'Post' => array('attachment' => array(
+			'name' => 'jquery-1.2.1.js',
+			'type' => 'application/x-javascript',
+			'tmp_name' => '/private/var/tmp/phpEwlrIo',
+			'error' => 0,
+			'size' => 80469,
+		)));
+		$this->assertEqual($result['data'], $expected);
+
+		$_FILES = array(
+			'data' => array(
+				'name' => array(
+					'Document' => array(
+						1 => array(
+							'birth_cert' => 'born on.txt',
+							'passport' => 'passport.txt',
+							'drivers_license' => 'ugly pic.jpg'
+						),
+						2 => array(
+							'birth_cert' => 'aunt betty.txt',
+							'passport' => 'betty-passport.txt',
+							'drivers_license' => 'betty-photo.jpg'
+						),
+					),
+				),
+				'type' => array(
+					'Document' => array(
+						1 => array(
+							'birth_cert' => 'application/octet-stream',
+							'passport' => 'application/octet-stream',
+							'drivers_license' => 'application/octet-stream',
+						),
+						2 => array(
+							'birth_cert' => 'application/octet-stream',
+							'passport' => 'application/octet-stream',
+							'drivers_license' => 'application/octet-stream',
+						)
+					)
+				),
+				'tmp_name' => array(
+					'Document' => array(
+						1 => array(
+							'birth_cert' => '/private/var/tmp/phpbsUWfH',
+							'passport' => '/private/var/tmp/php7f5zLt',
+ 							'drivers_license' => '/private/var/tmp/phpMXpZgT',
+						),
+						2 => array(
+							'birth_cert' => '/private/var/tmp/php5kHZt0',
+ 							'passport' => '/private/var/tmp/phpnYkOuM',
+ 							'drivers_license' => '/private/var/tmp/php9Rq0P3',
+						)
+					)
+				),
+				'error' => array(
+					'Document' => array(
+						1 => array(
+							'birth_cert' => 0,
+							'passport' => 0,
+ 							'drivers_license' => 0,
+						),
+						2 => array(
+							'birth_cert' => 0,
+ 							'passport' => 0,
+ 							'drivers_license' => 0,
+						)
+					)
+				),
+				'size' => array(
+					'Document' => array(
+						1 => array(
+							'birth_cert' => 123,
+							'passport' => 458,
+ 							'drivers_license' => 875,
+						),
+						2 => array(
+							'birth_cert' => 876,
+ 							'passport' => 976,
+ 							'drivers_license' => 9783,
+						)
+					)
+				)
+			)
+		);
+		$Dispatcher =& new Dispatcher();
+		$result = $Dispatcher->parseParams('/');
+		$expected = array(
+			'Document' => array(
+				1 => array(
+					'birth_cert' => array(
+						'name' => 'born on.txt',
+						'tmp_name' => '/private/var/tmp/phpbsUWfH',
+						'error' => 0,
+						'size' => 123,
+						'type' => 'application/octet-stream',
+					),
+					'passport' => array(
+						'name' => 'passport.txt',
+						'tmp_name' => '/private/var/tmp/php7f5zLt',
+						'error' => 0,
+						'size' => 458,
+						'type' => 'application/octet-stream',
+					),
+					'drivers_license' => array(
+						'name' => 'ugly pic.jpg',
+						'tmp_name' => '/private/var/tmp/phpMXpZgT',
+						'error' => 0,
+						'size' => 875,
+						'type' => 'application/octet-stream',
+					),
+				),
+				2 => array(
+					'birth_cert' => array(
+						'name' => 'aunt betty.txt',
+						'tmp_name' => '/private/var/tmp/php5kHZt0',
+						'error' => 0,
+						'size' => 876,
+						'type' => 'application/octet-stream',
+					),
+					'passport' => array(
+						'name' => 'betty-passport.txt',
+						'tmp_name' => '/private/var/tmp/phpnYkOuM',
+						'error' => 0,
+						'size' => 976,
+						'type' => 'application/octet-stream',
+					),
+					'drivers_license' => array(
+						'name' => 'betty-photo.jpg',
+						'tmp_name' => '/private/var/tmp/php9Rq0P3',
+						'error' => 0,
+						'size' => 9783,
+						'type' => 'application/octet-stream',
+					),
+				),
+			)
+		);
+		$this->assertEqual($result['data'], $expected);
+
+
+		$_FILES = array(
+			'data' => array(
+				'name' => array('birth_cert' => 'born on.txt'),
+				'type' => array('birth_cert' => 'application/octet-stream'),
+				'tmp_name' => array('birth_cert' => '/private/var/tmp/phpbsUWfH'),
+				'error' => array('birth_cert' => 0),
+				'size' => array('birth_cert' => 123)
+			)
+		);
+
+		$Dispatcher =& new Dispatcher();
+		$result = $Dispatcher->parseParams('/');
+
+		$expected = array(
+			'birth_cert' => array(
+				'name' => 'born on.txt',
+				'type' => 'application/octet-stream',
+				'tmp_name' => '/private/var/tmp/phpbsUWfH',
+				'error' => 0,
+				'size' => 123
+			)
+		);
+
+		$this->assertEqual($result['data'], $expected);
+	}
+
+/**
+ * testGetUrl method
+ *
+ * @return void
+ * @access public
+ */
+	function testGetUrl() {
+		$Dispatcher =& new Dispatcher();
+		$Dispatcher->base = '/app/webroot/index.php';
+		$uri = '/app/webroot/index.php/posts/add';
+		$result = $Dispatcher->getUrl($uri);
+		$expected = 'posts/add';
+		$this->assertEqual($expected, $result);
+
+		Configure::write('App.baseUrl', '/app/webroot/index.php');
+
+		$uri = '/posts/add';
+		$result = $Dispatcher->getUrl($uri);
+		$expected = 'posts/add';
+		$this->assertEqual($expected, $result);
+
+		$_GET['url'] = array();
+		Configure::write('App.base', '/control');
+		$Dispatcher =& new Dispatcher();
+		$Dispatcher->baseUrl();
+		$uri = '/control/students/browse';
+		$result = $Dispatcher->getUrl($uri);
+		$expected = 'students/browse';
+		$this->assertEqual($expected, $result);
+
+		$_GET['url'] = array();
+		$Dispatcher =& new Dispatcher();
+		$Dispatcher->base = '';
+		$uri = '/?/home';
+		$result = $Dispatcher->getUrl($uri);
+		$expected = '?/home';
+		$this->assertEqual($expected, $result);
+
+		$_GET['url'] = array();
+		$Dispatcher =& new Dispatcher();
+		$Dispatcher->base = '/shop';
+		$uri = '/shop/fr/pages/shop';
+		$result = $Dispatcher->getUrl($uri);
+		$expected = 'fr/pages/shop';
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * testBaseUrlAndWebrootWithModRewrite method
+ *
+ * @return void
+ * @access public
+ */
+	function testBaseUrlAndWebrootWithModRewrite() {
+		$Dispatcher =& new Dispatcher();
+
+		$Dispatcher->base = false;
+		$_SERVER['DOCUMENT_ROOT'] = '/cake/repo/branches';
+		$_SERVER['SCRIPT_FILENAME'] = '/cake/repo/branches/1.2.x.x/app/webroot/index.php';
+		$_SERVER['PHP_SELF'] = '/1.2.x.x/app/webroot/index.php';
+		$result = $Dispatcher->baseUrl();
+		$expected = '/1.2.x.x';
+		$this->assertEqual($expected, $result);
+		$expectedWebroot = '/1.2.x.x/';
+		$this->assertEqual($expectedWebroot, $Dispatcher->webroot);
+
+		$Dispatcher->base = false;
+		$_SERVER['DOCUMENT_ROOT'] = '/cake/repo/branches/1.2.x.x/app/webroot';
+		$_SERVER['SCRIPT_FILENAME'] = '/cake/repo/branches/1.2.x.x/app/webroot/index.php';
+		$_SERVER['PHP_SELF'] = '/index.php';
+		$result = $Dispatcher->baseUrl();
+		$expected = '';
+		$this->assertEqual($expected, $result);
+		$expectedWebroot = '/';
+		$this->assertEqual($expectedWebroot, $Dispatcher->webroot);
+
+		$Dispatcher->base = false;
+		$_SERVER['DOCUMENT_ROOT'] = '/cake/repo/branches/1.2.x.x/test/';
+		$_SERVER['SCRIPT_FILENAME'] = '/cake/repo/branches/1.2.x.x/test/webroot/index.php';
+		$_SERVER['PHP_SELF'] = '/webroot/index.php';
+		$result = $Dispatcher->baseUrl();
+		$expected = '';
+		$this->assertEqual($expected, $result);
+		$expectedWebroot = '/';
+		$this->assertEqual($expectedWebroot, $Dispatcher->webroot);
+
+		$Dispatcher->base = false;
+		$_SERVER['DOCUMENT_ROOT'] = '/some/apps/where';
+		$_SERVER['SCRIPT_FILENAME'] = '/some/apps/where/app/webroot/index.php';
+		$_SERVER['PHP_SELF'] = '/some/apps/where/app/webroot/index.php';
+		$result = $Dispatcher->baseUrl();
+		$expected = '/some/apps/where';
+		$this->assertEqual($expected, $result);
+		$expectedWebroot = '/some/apps/where/';
+		$this->assertEqual($expectedWebroot, $Dispatcher->webroot);
+
+
+		Configure::write('App.dir', 'auth');
+
+		$Dispatcher->base = false;
+		$_SERVER['DOCUMENT_ROOT'] = '/cake/repo/branches';
+		$_SERVER['SCRIPT_FILENAME'] = '/cake/repo/branches/demos/auth/webroot/index.php';
+		$_SERVER['PHP_SELF'] = '/demos/auth/webroot/index.php';
+
+		$result = $Dispatcher->baseUrl();
+		$expected = '/demos/auth';
+		$this->assertEqual($expected, $result);
+		$expectedWebroot = '/demos/auth/';
+		$this->assertEqual($expectedWebroot, $Dispatcher->webroot);
+
+		Configure::write('App.dir', 'code');
+
+		$Dispatcher->base = false;
+		$_SERVER['DOCUMENT_ROOT'] = '/Library/WebServer/Documents';
+		$_SERVER['SCRIPT_FILENAME'] = '/Library/WebServer/Documents/clients/PewterReport/code/webroot/index.php';
+		$_SERVER['PHP_SELF'] = '/clients/PewterReport/code/webroot/index.php';
+		$result = $Dispatcher->baseUrl();
+		$expected = '/clients/PewterReport/code';
+		$this->assertEqual($expected, $result);
+		$expectedWebroot = '/clients/PewterReport/code/';
+		$this->assertEqual($expectedWebroot, $Dispatcher->webroot);
+	}
+
+/**
+ * testBaseUrlwithModRewriteAlias method
+ *
+ * @return void
+ * @access public
+ */
+	function testBaseUrlwithModRewriteAlias() {
+		$_SERVER['DOCUMENT_ROOT'] = '/home/aplusnur/public_html';
+		$_SERVER['SCRIPT_FILENAME'] = '/home/aplusnur/cake2/app/webroot/index.php';
+		$_SERVER['PHP_SELF'] = '/control/index.php';
+
+		Configure::write('App.base', '/control');
+
+		$Dispatcher =& new Dispatcher();
+		$result = $Dispatcher->baseUrl();
+		$expected = '/control';
+		$this->assertEqual($expected, $result);
+		$expectedWebroot = '/control/';
+		$this->assertEqual($expectedWebroot, $Dispatcher->webroot);
+
+		Configure::write('App.base', false);
+		Configure::write('App.dir', 'affiliate');
+		Configure::write('App.webroot', 'newaffiliate');
+
+		$_SERVER['DOCUMENT_ROOT'] = '/var/www/abtravaff/html';
+		$_SERVER['SCRIPT_FILENAME'] = '/var/www/abtravaff/html/newaffiliate/index.php';
+		$_SERVER['PHP_SELF'] = '/newaffiliate/index.php';
+		$Dispatcher =& new Dispatcher();
+		$result = $Dispatcher->baseUrl();
+		$expected = '/newaffiliate';
+		$this->assertEqual($expected, $result);
+		$expectedWebroot = '/newaffiliate/';
+		$this->assertEqual($expectedWebroot, $Dispatcher->webroot);
+	}
+
+/**
+ * testBaseUrlAndWebrootWithBaseUrl method
+ *
+ * @return void
+ * @access public
+ */
+	function testBaseUrlAndWebrootWithBaseUrl() {
+		$Dispatcher =& new Dispatcher();
+
+		Configure::write('App.dir', 'app');
+
+		Configure::write('App.baseUrl', '/app/webroot/index.php');
+		$result = $Dispatcher->baseUrl();
+		$expected = '/app/webroot/index.php';
+		$this->assertEqual($expected, $result);
+		$expectedWebroot = '/app/webroot/';
+		$this->assertEqual($expectedWebroot, $Dispatcher->webroot);
+
+		Configure::write('App.baseUrl', '/app/webroot/test.php');
+		$result = $Dispatcher->baseUrl();
+		$expected = '/app/webroot/test.php';
+		$this->assertEqual($expected, $result);
+		$expectedWebroot = '/app/webroot/';
+		$this->assertEqual($expectedWebroot, $Dispatcher->webroot);
+
+		Configure::write('App.baseUrl', '/app/index.php');
+		$result = $Dispatcher->baseUrl();
+		$expected = '/app/index.php';
+		$this->assertEqual($expected, $result);
+		$expectedWebroot = '/app/webroot/';
+		$this->assertEqual($expectedWebroot, $Dispatcher->webroot);
+
+		Configure::write('App.baseUrl', '/index.php');
+		$result = $Dispatcher->baseUrl();
+		$expected = '/index.php';
+		$this->assertEqual($expected, $result);
+		$expectedWebroot = '/app/webroot/';
+		$this->assertEqual($expectedWebroot, $Dispatcher->webroot);
+
+		Configure::write('App.baseUrl', '/CakeBB/app/webroot/index.php');
+		$result = $Dispatcher->baseUrl();
+		$expected = '/CakeBB/app/webroot/index.php';
+		$this->assertEqual($expected, $result);
+		$expectedWebroot = '/CakeBB/app/webroot/';
+		$this->assertEqual($expectedWebroot, $Dispatcher->webroot);
+
+		Configure::write('App.baseUrl', '/CakeBB/app/index.php');
+		$result = $Dispatcher->baseUrl();
+		$expected = '/CakeBB/app/index.php';
+		$this->assertEqual($expected, $result);
+		$expectedWebroot = '/CakeBB/app/webroot/';
+		$this->assertEqual($expectedWebroot, $Dispatcher->webroot);
+
+		Configure::write('App.baseUrl', '/CakeBB/index.php');
+		$result = $Dispatcher->baseUrl();
+		$expected = '/CakeBB/index.php';
+		$this->assertEqual($expected, $result);
+		$expectedWebroot = '/CakeBB/app/webroot/';
+		$this->assertEqual($expectedWebroot, $Dispatcher->webroot);
+
+		Configure::write('App.baseUrl', '/dbhauser/index.php');
+		$_SERVER['DOCUMENT_ROOT'] = '/kunden/homepages/4/d181710652/htdocs/joomla';
+		$_SERVER['SCRIPT_FILENAME'] = '/kunden/homepages/4/d181710652/htdocs/joomla/dbhauser/index.php';
+		$result = $Dispatcher->baseUrl();
+		$expected = '/dbhauser/index.php';
+		$this->assertEqual($expected, $result);
+		$expectedWebroot = '/dbhauser/app/webroot/';
+		$this->assertEqual($expectedWebroot, $Dispatcher->webroot);
+	}
+
+/**
+ * Check that a sub-directory containing app|webroot doesn't get mishandled when re-writing is off.
+ *
+ * @return void
+ */
+	function testBaseUrlWithAppAndWebrootInDirname() {
+		Configure::write('App.baseUrl', '/approval/index.php');
+		$_SERVER['DOCUMENT_ROOT'] = '/Users/markstory/Sites/';
+		$_SERVER['SCRIPT_FILENAME'] = '/Users/markstory/Sites/approval/index.php';
+		$Dispatcher =& new Dispatcher();
+		$result = $Dispatcher->baseUrl();
+
+		$this->assertEqual('/approval/index.php', $result);
+		$this->assertEqual('/approval/app/webroot/', $Dispatcher->webroot);
+
+		Configure::write('App.baseUrl', '/webrootable/index.php');
+		$_SERVER['DOCUMENT_ROOT'] = '/Users/markstory/Sites/';
+		$_SERVER['SCRIPT_FILENAME'] = '/Users/markstory/Sites/webrootable/index.php';
+		$Dispatcher =& new Dispatcher();
+		$result = $Dispatcher->baseUrl();
+
+		$this->assertEqual('/webrootable/index.php', $result);
+		$this->assertEqual('/webrootable/app/webroot/', $Dispatcher->webroot);
+	}
+
+/**
+ * test baseUrl with no rewrite and using the top level index.php.
+ *
+ * @return void
+ */
+	function testBaseUrlNoRewriteTopLevelIndex() {
+		$Dispatcher =& new Dispatcher();
+
+		Configure::write('App.baseUrl', '/index.php');
+		$_SERVER['DOCUMENT_ROOT'] = '/Users/markstory/Sites/cake_dev';
+		$_SERVER['SCRIPT_FILENAME'] = '/Users/markstory/Sites/cake_dev/index.php';
+
+		$result = $Dispatcher->baseUrl();
+		$this->assertEqual('/index.php', $result);
+		$this->assertEqual('/app/webroot/', $Dispatcher->webroot);
+		$this->assertEqual('', $Dispatcher->base);
+	}
+
+/**
+ * test baseUrl with no rewrite, and using the app/webroot/index.php file as is normal with virtual hosts.
+ *
+ * @return void
+ */
+	function testBaseUrlNoRewriteWebrootIndex() {
+		$Dispatcher =& new Dispatcher();
+
+		Configure::write('App.baseUrl', '/index.php');
+		$_SERVER['DOCUMENT_ROOT'] = '/Users/markstory/Sites/cake_dev/app/webroot';
+		$_SERVER['SCRIPT_FILENAME'] = '/Users/markstory/Sites/cake_dev/app/webroot/index.php';
+
+		$result = $Dispatcher->baseUrl();
+		$this->assertEqual('/index.php', $result);
+		$this->assertEqual('/', $Dispatcher->webroot);
+		$this->assertEqual('', $Dispatcher->base);
+	}
+
+/**
+ * testBaseUrlAndWebrootWithBase method
+ *
+ * @return void
+ * @access public
+ */
+	function testBaseUrlAndWebrootWithBase() {
+		$Dispatcher =& new Dispatcher();
+		$Dispatcher->base = '/app';
+		$result = $Dispatcher->baseUrl();
+		$expected = '/app';
+		$this->assertEqual($expected, $result);
+		$expectedWebroot = '/app/';
+		$this->assertEqual($expectedWebroot, $Dispatcher->webroot);
+
+		$Dispatcher->base = '';
+		$result = $Dispatcher->baseUrl();
+		$expected = '';
+		$this->assertEqual($expected, $result);
+		$expectedWebroot = '/';
+		$this->assertEqual($expectedWebroot, $Dispatcher->webroot);
+
+		Configure::write('App.dir', 'testbed');
+		$Dispatcher->base = '/cake/testbed/webroot';
+		$result = $Dispatcher->baseUrl();
+		$expected = '/cake/testbed/webroot';
+		$this->assertEqual($expected, $result);
+		$expectedWebroot = '/cake/testbed/webroot/';
+		$this->assertEqual($expectedWebroot, $Dispatcher->webroot);
+	}
+
+/**
+ * testMissingController method
+ *
+ * @return void
+ * @access public
+ */
+	function testMissingController() {
+		$Dispatcher =& new TestDispatcher();
+		Configure::write('App.baseUrl', '/index.php');
+		$url = 'some_controller/home/param:value/param2:value2';
+		$controller = $Dispatcher->dispatch($url, array('return' => 1));
+		$expected = array('missingController', array(array(
+			'className' => 'SomeControllerController',
+			'webroot' => '/app/webroot/',
+			'url' => 'some_controller/home/param:value/param2:value2',
+			'base' => '/index.php'
+		)));
+		$this->assertEqual($expected, $controller);
+	}
+
+/**
+ * testPrivate method
+ *
+ * @return void
+ * @access public
+ */
+	function testPrivate() {
+		$Dispatcher =& new TestDispatcher();
+		Configure::write('App.baseUrl','/index.php');
+		$url = 'some_pages/_protected/param:value/param2:value2';
+
+		$controller = $Dispatcher->dispatch($url, array('return' => 1));
+
+		$expected = array('privateAction', array(array(
+			'className' => 'SomePagesController',
+			'action' => '_protected',
+			'webroot' => '/app/webroot/',
+			'url' => 'some_pages/_protected/param:value/param2:value2',
+			'base' => '/index.php'
+		)));
+		$this->assertEqual($controller, $expected);
+	}
+
+/**
+ * testMissingAction method
+ *
+ * @return void
+ * @access public
+ */
+	function testMissingAction() {
+		$Dispatcher =& new TestDispatcher();
+		Configure::write('App.baseUrl', '/index.php');
+		$url = 'some_pages/home/param:value/param2:value2';
+
+		$controller = $Dispatcher->dispatch($url, array('return'=> 1));
+
+		$expected = array('missingAction', array(array(
+			'className' => 'SomePagesController',
+			'action' => 'home',
+			'webroot' => '/app/webroot/',
+			'url' => '/index.php/some_pages/home/param:value/param2:value2',
+			'base' => '/index.php'
+		)));
+		$this->assertEqual($expected, $controller);
+
+		$Dispatcher =& new TestDispatcher();
+		Configure::write('App.baseUrl','/index.php');
+		$url = 'some_pages/redirect/param:value/param2:value2';
+
+		$controller = $Dispatcher->dispatch($url, array('return'=> 1));
+
+		$expected = array('missingAction', array(array(
+			'className' => 'SomePagesController',
+			'action' => 'redirect',
+			'webroot' => '/app/webroot/',
+			'url' => '/index.php/some_pages/redirect/param:value/param2:value2',
+			'base' => '/index.php'
+		)));
+		$this->assertEqual($expected, $controller);
+	}
+
+/**
+ * testDispatch method
+ *
+ * @return void
+ * @access public
+ */
+	function testDispatch() {
+		$Dispatcher =& new TestDispatcher();
+		Configure::write('App.baseUrl','/index.php');
+		$url = 'pages/home/param:value/param2:value2';
+
+		$controller = $Dispatcher->dispatch($url, array('return' => 1));
+		$expected = 'Pages';
+		$this->assertEqual($expected, $controller->name);
+
+		$expected = array('0' => 'home', 'param' => 'value', 'param2' => 'value2');
+		$this->assertIdentical($expected, $controller->passedArgs);
+
+		Configure::write('App.baseUrl','/pages/index.php');
+
+		$url = 'pages/home';
+		$controller = $Dispatcher->dispatch($url, array('return' => 1));
+
+		$expected = 'Pages';
+		$this->assertEqual($expected, $controller->name);
+
+		$url = 'pages/home/';
+		$controller = $Dispatcher->dispatch($url, array('return' => 1));
+		$this->assertNull($controller->plugin);
+		$this->assertNull($Dispatcher->params['plugin']);
+
+		$expected = 'Pages';
+		$this->assertEqual($expected, $controller->name);
+
+		unset($Dispatcher);
+
+		$Dispatcher =& new TestDispatcher();
+		Configure::write('App.baseUrl','/timesheets/index.php');
+
+		$url = 'timesheets';
+		$controller = $Dispatcher->dispatch($url, array('return' => 1));
+
+		$expected = 'Timesheets';
+		$this->assertEqual($expected, $controller->name);
+
+		$url = 'timesheets/';
+		$controller = $Dispatcher->dispatch($url, array('return' => 1));
+
+		$this->assertEqual('Timesheets', $controller->name);
+		$this->assertEqual('/timesheets/index.php', $Dispatcher->base);
+
+
+		$url = 'test_dispatch_pages/camelCased';
+		$controller = $Dispatcher->dispatch($url, array('return' => 1));
+		$this->assertEqual('TestDispatchPages', $controller->name);
+
+		$url = 'test_dispatch_pages/camelCased/something. .';
+		$controller = $Dispatcher->dispatch($url, array('return' => 1));
+		$this->assertEqual($controller->params['pass'][0], 'something. .', 'Period was chopped off. %s');
+
+	}
+
+/**
+ * testDispatchWithArray method
+ *
+ * @return void
+ * @access public
+ */
+	function testDispatchWithArray() {
+		$Dispatcher =& new TestDispatcher();
+		$url = 'pages/home/param:value/param2:value2';
+
+		$url = array('controller' => 'pages', 'action' => 'display');
+		$controller = $Dispatcher->dispatch($url, array(
+			'pass' => array('home'),
+			'named' => array('param' => 'value', 'param2' => 'value2'),
+			'return' => 1
+		));
+		$expected = 'Pages';
+		$this->assertEqual($expected, $controller->name);
+
+		$expected = array('0' => 'home', 'param' => 'value', 'param2' => 'value2');
+		$this->assertIdentical($expected, $controller->passedArgs);
+
+		$this->assertEqual($Dispatcher->base . '/pages/display/home/param:value/param2:value2', $Dispatcher->here);
+	}
+
+/**
+ * test that a garbage url doesn't cause errors.
+ *
+ * @return void
+ */
+	function testDispatchWithGarbageUrl() {
+		Configure::write('App.baseUrl', '/index.php');
+
+		$Dispatcher =& new TestDispatcher();
+		$url = 'http://google.com';
+		$result = $Dispatcher->dispatch($url);
+		$expected = array('missingController', array(array(
+			'className' => 'Controller',
+			'webroot' => '/app/webroot/',
+			'url' => 'http://google.com',
+			'base' => '/index.php'
+		)));
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * testAdminDispatch method
+ *
+ * @return void
+ * @access public
+ */
+	function testAdminDispatch() {
+		$_POST = array();
+		$Dispatcher =& new TestDispatcher();
+		Configure::write('Routing.prefixes', array('admin'));
+		Configure::write('App.baseUrl','/cake/repo/branches/1.2.x.x/index.php');
+		$url = 'admin/test_dispatch_pages/index/param:value/param2:value2';
+
+		Router::reload();
+		$Router =& Router::getInstance();
+		$controller = $Dispatcher->dispatch($url, array('return' => 1));
+
+		$this->assertEqual($controller->name, 'TestDispatchPages');
+
+		$this->assertIdentical($controller->passedArgs, array('param' => 'value', 'param2' => 'value2'));
+		$this->assertTrue($controller->params['admin']);
+
+		$expected = '/cake/repo/branches/1.2.x.x/index.php/admin/test_dispatch_pages/index/param:value/param2:value2';
+		$this->assertIdentical($expected, $controller->here);
+
+		$expected = '/cake/repo/branches/1.2.x.x/index.php';
+		$this->assertIdentical($expected, $controller->base);
+	}
+
+/**
+ * testPluginDispatch method
+ *
+ * @return void
+ * @access public
+ */
+	function testPluginDispatch() {
+		$_POST = array();
+		$_SERVER['PHP_SELF'] = '/cake/repo/branches/1.2.x.x/index.php';
+
+		Router::reload();
+		$Dispatcher =& new TestDispatcher();
+		Router::connect(
+			'/my_plugin/:controller/*',
+			array('plugin' => 'my_plugin', 'controller' => 'pages', 'action' => 'display')
+		);
+
+		$Dispatcher->base = false;
+		$url = 'my_plugin/some_pages/home/param:value/param2:value2';
+		$controller = $Dispatcher->dispatch($url, array('return' => 1));
+
+		$result = $Dispatcher->parseParams($url);
+		$expected = array(
+			'pass' => array('home'),
+			'named' => array('param'=> 'value', 'param2'=> 'value2'), 'plugin'=> 'my_plugin',
+			'controller'=> 'some_pages', 'action'=> 'display', 'form'=> null,
+			'url'=> array('url'=> 'my_plugin/some_pages/home/param:value/param2:value2'),
+		);
+		ksort($expected);
+		ksort($result);
+
+		$this->assertEqual($expected, $result);
+
+		$this->assertIdentical($controller->plugin, 'my_plugin');
+		$this->assertIdentical($controller->name, 'SomePages');
+		$this->assertIdentical($controller->params['controller'], 'some_pages');
+		$this->assertIdentical($controller->passedArgs, array('0' => 'home', 'param'=>'value', 'param2'=>'value2'));
+
+		$expected = '/cake/repo/branches/1.2.x.x/my_plugin/some_pages/home/param:value/param2:value2';
+		$this->assertIdentical($expected, $controller->here);
+
+		$expected = '/cake/repo/branches/1.2.x.x';
+		$this->assertIdentical($expected, $controller->base);
+	}
+
+/**
+ * testAutomaticPluginDispatch method
+ *
+ * @return void
+ * @access public
+ */
+	function testAutomaticPluginDispatch() {
+		$_POST = array();
+		$_SERVER['PHP_SELF'] = '/cake/repo/branches/1.2.x.x/index.php';
+
+		Router::reload();
+		$Dispatcher =& new TestDispatcher();
+		Router::connect(
+			'/my_plugin/:controller/:action/*',
+			array('plugin' => 'my_plugin', 'controller' => 'pages', 'action' => 'display')
+		);
+
+		$Dispatcher->base = false;
+
+		$url = 'my_plugin/other_pages/index/param:value/param2:value2';
+		$controller = $Dispatcher->dispatch($url, array('return' => 1));
+
+		$this->assertIdentical($controller->plugin, 'my_plugin');
+		$this->assertIdentical($controller->name, 'OtherPages');
+		$this->assertIdentical($controller->action, 'index');
+		$this->assertIdentical($controller->passedArgs, array('param' => 'value', 'param2' => 'value2'));
+
+		$expected = '/cake/repo/branches/1.2.x.x/my_plugin/other_pages/index/param:value/param2:value2';
+		$this->assertIdentical($expected, $controller->here);
+
+		$expected = '/cake/repo/branches/1.2.x.x';
+		$this->assertIdentical($expected, $controller->base);
+	}
+
+/**
+ * testAutomaticPluginControllerDispatch method
+ *
+ * @return void
+ * @access public
+ */
+	function testAutomaticPluginControllerDispatch() {
+		$_POST = array();
+		$_SERVER['PHP_SELF'] = '/cake/repo/branches/1.2.x.x/index.php';
+
+		$plugins = App::objects('plugin');
+		$plugins[] = 'MyPlugin';
+		$plugins[] = 'ArticlesTest';
+
+		$app = App::getInstance();
+		$app->__objects['plugin'] = $plugins;
+
+		Router::reload();
+		$Dispatcher =& new TestDispatcher();
+		$Dispatcher->base = false;
+
+		$url = 'my_plugin/my_plugin/add/param:value/param2:value2';
+
+		$controller = $Dispatcher->dispatch($url, array('return' => 1));
+
+		$this->assertIdentical($controller->plugin, 'my_plugin');
+		$this->assertIdentical($controller->name, 'MyPlugin');
+		$this->assertIdentical($controller->action, 'add');
+		$this->assertEqual($controller->params['named'], array('param' => 'value', 'param2' => 'value2'));
+
+
+		Router::reload();
+		$Dispatcher =& new TestDispatcher();
+		$Dispatcher->base = false;
+
+		// Simulates the Route for a real plugin, installed in APP/plugins
+		Router::connect('/my_plugin/:controller/:action/*', array('plugin' => 'my_plugin'));
+
+		$plugin = 'MyPlugin';
+		$pluginUrl = Inflector::underscore($plugin);
+
+		$url = $pluginUrl;
+		$controller = $Dispatcher->dispatch($url, array('return' => 1));
+		$this->assertIdentical($controller->plugin, 'my_plugin');
+		$this->assertIdentical($controller->name, 'MyPlugin');
+		$this->assertIdentical($controller->action, 'index');
+
+		$expected = $pluginUrl;
+		$this->assertEqual($controller->params['controller'], $expected);
+
+
+		Configure::write('Routing.prefixes', array('admin'));
+
+		Router::reload();
+		$Dispatcher =& new TestDispatcher();
+		$Dispatcher->base = false;
+
+		$url = 'admin/my_plugin/my_plugin/add/5/param:value/param2:value2';
+		$controller = $Dispatcher->dispatch($url, array('return' => 1));
+
+		$this->assertEqual($controller->params['plugin'], 'my_plugin');
+		$this->assertEqual($controller->params['controller'], 'my_plugin');
+		$this->assertEqual($controller->params['action'], 'admin_add');
+		$this->assertEqual($controller->params['pass'], array(5));
+		$this->assertEqual($controller->params['named'], array('param' => 'value', 'param2' => 'value2'));
+		$this->assertIdentical($controller->plugin, 'my_plugin');
+		$this->assertIdentical($controller->name, 'MyPlugin');
+		$this->assertIdentical($controller->action, 'admin_add');
+
+		$expected = array(0 => 5, 'param'=>'value', 'param2'=>'value2');
+		$this->assertEqual($controller->passedArgs, $expected);
+
+		Configure::write('Routing.prefixes', array('admin'));
+		Router::reload();
+
+		$Dispatcher =& new TestDispatcher();
+		$Dispatcher->base = false;
+
+		$controller = $Dispatcher->dispatch('admin/articles_test', array('return' => 1));
+		$this->assertIdentical($controller->plugin, 'articles_test');
+		$this->assertIdentical($controller->name, 'ArticlesTest');
+		$this->assertIdentical($controller->action, 'admin_index');
+
+		$expected = array(
+			'pass'=> array(),
+			'named' => array(),
+			'controller' => 'articles_test',
+			'plugin' => 'articles_test',
+			'action' => 'admin_index',
+			'prefix' => 'admin',
+			'admin' =>  true,
+			'form' => array(),
+			'url' => array('url' => 'admin/articles_test'),
+			'return' => 1
+		);
+		$this->assertEqual($controller->params, $expected);
+	}
+
+/**
+ * test Plugin dispatching without controller name and using
+ * plugin short form instead.
+ *
+ * @return void
+ * @access public
+ */
+	function testAutomaticPluginDispatchWithShortAccess() {
+		$_POST = array();
+		$_SERVER['PHP_SELF'] = '/cake/repo/branches/1.2.x.x/index.php';
+		$plugins = App::objects('plugin');
+		$plugins[] = 'MyPlugin';
+
+		$app = App::getInstance();
+		$app->__objects['plugin'] = $plugins;
+
+		Router::reload();
+
+		$Dispatcher =& new TestDispatcher();
+		$Dispatcher->base = false;
+
+		$url = 'my_plugin/';
+		$controller = $Dispatcher->dispatch($url, array('return' => 1));
+		$this->assertEqual($controller->params['controller'], 'my_plugin');
+		$this->assertEqual($controller->params['plugin'], 'my_plugin');
+		$this->assertEqual($controller->params['action'], 'index');
+		$this->assertFalse(isset($controller->params['pass'][0]));
+	}
+
+/**
+ * test plugin shortcut urls with controllers that need to be loaded,
+ * the above test uses a controller that has already been included.
+ *
+ * @return void
+ */
+	function testPluginShortCutUrlsWithControllerThatNeedsToBeLoaded() {
+		$loaded = class_exists('TestPluginController', false);
+		if ($this->skipIf($loaded, 'TestPluginController already loaded, this test will always pass, skipping %s')) {
+			return true;
+		}
+		Router::reload();
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)
+		), true);
+		App::objects('plugin', null, false);
+
+		$Dispatcher =& new TestDispatcher();
+		$Dispatcher->base = false;
+
+		$url = 'test_plugin/';
+		$controller = $Dispatcher->dispatch($url, array('return' => 1));
+		$this->assertEqual($controller->params['controller'], 'test_plugin');
+		$this->assertEqual($controller->params['plugin'], 'test_plugin');
+		$this->assertEqual($controller->params['action'], 'index');
+		$this->assertFalse(isset($controller->params['pass'][0]));
+
+		$url = '/test_plugin/tests/index';
+		$controller = $Dispatcher->dispatch($url, array('return' => 1));
+		$this->assertEqual($controller->params['controller'], 'tests');
+		$this->assertEqual($controller->params['plugin'], 'test_plugin');
+		$this->assertEqual($controller->params['action'], 'index');
+		$this->assertFalse(isset($controller->params['pass'][0]));
+
+		$url = '/test_plugin/tests/index/some_param';
+		$controller = $Dispatcher->dispatch($url, array('return' => 1));
+		$this->assertEqual($controller->params['controller'], 'tests');
+		$this->assertEqual($controller->params['plugin'], 'test_plugin');
+		$this->assertEqual($controller->params['action'], 'index');
+		$this->assertEqual($controller->params['pass'][0], 'some_param');
+
+		App::build();
+	}
+
+/**
+ * testAutomaticPluginControllerMissingActionDispatch method
+ *
+ * @return void
+ * @access public
+ */
+	function testAutomaticPluginControllerMissingActionDispatch() {
+		$_POST = array();
+		$_SERVER['PHP_SELF'] = '/cake/repo/branches/1.2.x.x/index.php';
+
+		Router::reload();
+		$Dispatcher =& new TestDispatcher();
+		$Dispatcher->base = false;
+
+		$url = 'my_plugin/not_here/param:value/param2:value2';
+		$controller = $Dispatcher->dispatch($url, array('return'=> 1));
+
+		$expected = array('missingAction', array(array(
+			'className' => 'MyPluginController',
+			'action' => 'not_here',
+			'webroot' => '/cake/repo/branches/1.2.x.x/',
+			'url' => '/cake/repo/branches/1.2.x.x/my_plugin/not_here/param:value/param2:value2',
+			'base' => '/cake/repo/branches/1.2.x.x'
+		)));
+		$this->assertIdentical($expected, $controller);
+
+		Router::reload();
+		$Dispatcher =& new TestDispatcher();
+		$Dispatcher->base = false;
+
+		$url = 'my_plugin/param:value/param2:value2';
+		$controller = $Dispatcher->dispatch($url, array('return'=> 1));
+
+		$expected = array('missingAction', array(array(
+			'className' => 'MyPluginController',
+			'action' => 'param:value',
+			'webroot' => '/cake/repo/branches/1.2.x.x/',
+			'url' => '/cake/repo/branches/1.2.x.x/my_plugin/param:value/param2:value2',
+			'base' => '/cake/repo/branches/1.2.x.x'
+		)));
+		$this->assertIdentical($expected, $controller);
+	}
+
+/**
+ * testPrefixProtection method
+ *
+ * @return void
+ * @access public
+ */
+	function testPrefixProtection() {
+		$_POST = array();
+		$_SERVER['PHP_SELF'] = '/cake/repo/branches/1.2.x.x/index.php';
+
+		Router::reload();
+		Router::connect('/admin/:controller/:action/*', array('prefix'=>'admin'), array('controller', 'action'));
+
+		$Dispatcher =& new TestDispatcher();
+		$Dispatcher->base = false;
+
+		$url = 'test_dispatch_pages/admin_index/param:value/param2:value2';
+		$controller = $Dispatcher->dispatch($url, array('return' => 1));
+
+		$expected = array('privateAction', array(array(
+			'className' => 'TestDispatchPagesController',
+			'action' => 'admin_index',
+			'webroot' => '/cake/repo/branches/1.2.x.x/',
+			'url' => 'test_dispatch_pages/admin_index/param:value/param2:value2',
+			'base' => '/cake/repo/branches/1.2.x.x'
+		)));
+		$this->assertIdentical($expected, $controller);
+	}
+
+/**
+ * Test dispatching into the TestPlugin in the test_app
+ *
+ * @return void
+ * @access public
+ */
+	function testTestPluginDispatch() {
+		$Dispatcher =& new TestDispatcher();
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)
+		));
+		App::objects('plugin', null, false);
+		Router::reload();
+		Router::parse('/');
+
+		$url = '/test_plugin/tests/index';
+		$result = $Dispatcher->dispatch($url, array('return' => 1));
+		$this->assertTrue(class_exists('TestsController'));
+		$this->assertTrue(class_exists('TestPluginAppController'));
+		$this->assertTrue(class_exists('OtherComponentComponent'));
+		$this->assertTrue(class_exists('PluginsComponentComponent'));
+
+		$this->assertEqual($result->params['controller'], 'tests');
+		$this->assertEqual($result->params['plugin'], 'test_plugin');
+		$this->assertEqual($result->params['action'], 'index');
+
+		App::build();
+	}
+
+/**
+ * testChangingParamsFromBeforeFilter method
+ *
+ * @return void
+ * @access public
+ */
+	function testChangingParamsFromBeforeFilter() {
+		$_SERVER['PHP_SELF'] = '/cake/repo/branches/1.2.x.x/index.php';
+		$Dispatcher =& new TestDispatcher();
+		$url = 'some_posts/index/param:value/param2:value2';
+		$controller = $Dispatcher->dispatch($url, array('return' => 1));
+
+		$expected = array('missingAction', array(array(
+			'className' => 'SomePostsController',
+			'action' => 'view',
+			'webroot' => '/cake/repo/branches/1.2.x.x/',
+			'url' => '/cake/repo/branches/1.2.x.x/some_posts/index/param:value/param2:value2',
+			'base' => '/cake/repo/branches/1.2.x.x'
+		)));
+		$this->assertEqual($expected, $controller);
+
+		$url = 'some_posts/something_else/param:value/param2:value2';
+		$controller = $Dispatcher->dispatch($url, array('return' => 1));
+
+		$expected = 'SomePosts';
+		$this->assertEqual($expected, $controller->name);
+
+		$expected = 'change';
+		$this->assertEqual($expected, $controller->action);
+
+		$expected = array('changed');
+		$this->assertIdentical($expected, $controller->params['pass']);
+	}
+
+/**
+ * testStaticAssets method
+ *
+ * @return void
+ * @access public
+ */
+	function testAssets() {
+		Router::reload();
+		$Configure =& Configure::getInstance();
+		$Configure->__objects = null;
+
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS),
+			'vendors' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'vendors'. DS),
+			'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS)
+		));
+
+		$Dispatcher =& new TestDispatcher();
+		$debug = Configure::read('debug');
+		Configure::write('debug', 0);
+
+		ob_start();
+		$Dispatcher->dispatch('theme/test_theme/../webroot/css/test_asset.css');
+		$result = ob_get_clean();
+		$this->assertFalse($result);
+
+		ob_start();
+		$Dispatcher->dispatch('theme/test_theme/pdfs');
+		$result = ob_get_clean();
+		$this->assertFalse($result);
+
+		ob_start();
+		$Dispatcher->dispatch('theme/test_theme/flash/theme_test.swf');
+		$result = ob_get_clean();
+		$file = file_get_contents(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'themed' . DS . 'test_theme' . DS . 'webroot' . DS . 'flash' . DS . 'theme_test.swf');
+		$this->assertEqual($file, $result);
+		$this->assertEqual('this is just a test to load swf file from the theme.', $result);
+
+		ob_start();
+		$Dispatcher->dispatch('theme/test_theme/pdfs/theme_test.pdf');
+		$result = ob_get_clean();
+		$file = file_get_contents(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'themed' . DS . 'test_theme' . DS . 'webroot' . DS . 'pdfs' . DS . 'theme_test.pdf');
+		$this->assertEqual($file, $result);
+		$this->assertEqual('this is just a test to load pdf file from the theme.', $result);
+
+		ob_start();
+		$Dispatcher->dispatch('theme/test_theme/img/test.jpg');
+		$result = ob_get_clean();
+		$file = file_get_contents(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'themed' . DS . 'test_theme' . DS . 'webroot' . DS . 'img' . DS . 'test.jpg');
+		$this->assertEqual($file, $result);
+
+		$Dispatcher->params = $Dispatcher->parseParams('theme/test_theme/css/test_asset.css');
+		ob_start();
+		$Dispatcher->asset('theme/test_theme/css/test_asset.css');
+		$result = ob_get_clean();
+		$this->assertEqual('this is the test asset css file', $result);
+
+		$Dispatcher->params = $Dispatcher->parseParams('theme/test_theme/js/theme.js');
+		ob_start();
+		$Dispatcher->asset('theme/test_theme/js/theme.js');
+		$result = ob_get_clean();
+		$this->assertEqual('root theme js file', $result);
+
+		$Dispatcher->params = $Dispatcher->parseParams('theme/test_theme/js/one/theme_one.js');
+		ob_start();
+		$Dispatcher->asset('theme/test_theme/js/one/theme_one.js');
+		$result = ob_get_clean();
+		$this->assertEqual('nested theme js file', $result);
+
+		ob_start();
+		$Dispatcher->asset('test_plugin/root.js');
+		$result = ob_get_clean();
+		$expected = file_get_contents(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS . 'test_plugin' . DS . 'webroot' . DS . 'root.js');
+		$this->assertEqual($result, $expected);
+
+		ob_start();
+		$Dispatcher->dispatch('test_plugin/flash/plugin_test.swf');
+		$result = ob_get_clean();
+		$file = file_get_contents(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS . 'test_plugin' . DS . 'webroot' . DS . 'flash' . DS . 'plugin_test.swf');
+		$this->assertEqual($file, $result);
+		$this->assertEqual('this is just a test to load swf file from the plugin.', $result);
+
+		ob_start();
+		$Dispatcher->dispatch('test_plugin/pdfs/plugin_test.pdf');
+		$result = ob_get_clean();
+		$file = file_get_contents(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS . 'test_plugin' . DS . 'webroot' . DS . 'pdfs' . DS . 'plugin_test.pdf');
+		$this->assertEqual($file, $result);
+		 $this->assertEqual('this is just a test to load pdf file from the plugin.', $result);
+
+		ob_start();
+		$Dispatcher->asset('test_plugin/js/test_plugin/test.js');
+		$result = ob_get_clean();
+		$this->assertEqual('alert("Test App");', $result);
+
+		$Dispatcher->params = $Dispatcher->parseParams('test_plugin/js/test_plugin/test.js');
+		ob_start();
+		$Dispatcher->asset('test_plugin/js/test_plugin/test.js');
+		$result = ob_get_clean();
+		$this->assertEqual('alert("Test App");', $result);
+
+		$Dispatcher->params = $Dispatcher->parseParams('test_plugin/css/test_plugin_asset.css');
+		ob_start();
+		$Dispatcher->asset('test_plugin/css/test_plugin_asset.css');
+		$result = ob_get_clean();
+		$this->assertEqual('this is the test plugin asset css file', $result);
+
+		$Dispatcher->params = $Dispatcher->parseParams('test_plugin/img/cake.icon.gif');
+		ob_start();
+		$Dispatcher->asset('test_plugin/img/cake.icon.gif');
+		$result = ob_get_clean();
+		$file = file_get_contents(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS . 'test_plugin' .DS . 'webroot' . DS . 'img' . DS . 'cake.icon.gif');
+		$this->assertEqual($file, $result);
+
+		$Dispatcher->params = $Dispatcher->parseParams('plugin_js/js/plugin_js.js');
+		ob_start();
+		$Dispatcher->asset('plugin_js/js/plugin_js.js');
+		$result = ob_get_clean();
+		$expected = "alert('win sauce');";
+		$this->assertEqual($result, $expected);
+
+		$Dispatcher->params = $Dispatcher->parseParams('plugin_js/js/one/plugin_one.js');
+		ob_start();
+		$Dispatcher->asset('plugin_js/js/one/plugin_one.js');
+		$result = ob_get_clean();
+		$expected = "alert('plugin one nested js file');";
+		$this->assertEqual($result, $expected);
+		Configure::write('debug', $debug);
+		//reset the header content-type without page can render as plain text.
+		header('Content-type: text/html');
+
+		$Dispatcher->params = $Dispatcher->parseParams('test_plugin/css/theme_one.htc');
+		ob_start();
+		$Dispatcher->asset('test_plugin/css/unknown.extension');
+		$result = ob_get_clean();
+		$this->assertEqual('Testing a file with unknown extension to mime mapping.', $result);
+		header('Content-type: text/html');
+
+		$Dispatcher->params = $Dispatcher->parseParams('test_plugin/css/theme_one.htc');
+		ob_start();
+		$Dispatcher->asset('test_plugin/css/theme_one.htc');
+		$result = ob_get_clean();
+		$this->assertEqual('htc file', $result);
+		header('Content-type: text/html');
+	}
+
+/**
+ * test that missing asset processors trigger a 404 with no response body.
+ *
+ * @return void
+ */
+	function testMissingAssetProcessor404() {
+		$Dispatcher =& new TestDispatcher();
+		Configure::write('Asset.filter', array(
+			'js' => '',
+			'css' => null
+		));
+		$this->assertNoErrors();
+
+		ob_start();
+		$Dispatcher->asset('ccss/cake.generic.css');
+		$result = ob_get_clean();
+		$this->assertTrue($Dispatcher->stopped);
+
+		header('HTTP/1.1 200 Ok');
+	}
+
+/**
+ * test that asset filters work for theme and plugin assets
+ *
+ * @return void
+ */
+	function testAssetFilterForThemeAndPlugins() {
+		$Dispatcher =& new TestDispatcher();
+		Configure::write('Asset.filter', array(
+			'js' => '',
+			'css' => ''
+		));
+		$Dispatcher->asset('theme/test_theme/ccss/cake.generic.css');
+		$this->assertTrue($Dispatcher->stopped);
+
+		$Dispatcher->stopped = false;
+		$Dispatcher->asset('theme/test_theme/cjs/debug_kit.js');
+		$this->assertTrue($Dispatcher->stopped);
+
+		$Dispatcher->stopped = false;
+		$Dispatcher->asset('test_plugin/ccss/cake.generic.css');
+		$this->assertTrue($Dispatcher->stopped);
+
+		$Dispatcher->stopped = false;
+		$Dispatcher->asset('test_plugin/cjs/debug_kit.js');
+		$this->assertTrue($Dispatcher->stopped);
+
+		$Dispatcher->stopped = false;
+		$Dispatcher->asset('css/ccss/debug_kit.css');
+		$this->assertFalse($Dispatcher->stopped);
+
+		$Dispatcher->stopped = false;
+		$Dispatcher->asset('js/cjs/debug_kit.js');
+		$this->assertFalse($Dispatcher->stopped);
+	}
+/**
+ * testFullPageCachingDispatch method
+ *
+ * @return void
+ * @access public
+ */
+	function testFullPageCachingDispatch() {
+		Configure::write('Cache.disable', false);
+		Configure::write('Cache.check', true);
+		Configure::write('debug', 2);
+
+		$_POST = array();
+		$_SERVER['PHP_SELF'] = '/';
+
+		Router::reload();
+		Router::connect('/', array('controller' => 'test_cached_pages', 'action' => 'index'));
+
+		App::build(array(
+			'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS),
+		), true);
+
+		$dispatcher =& new TestDispatcher();
+		$dispatcher->base = false;
+
+		$url = '/';
+
+		ob_start();
+		$dispatcher->dispatch($url);
+		$out = ob_get_clean();
+
+		ob_start();
+		$dispatcher->cached($url);
+		$cached = ob_get_clean();
+
+		$result = str_replace(array("\t", "\r\n", "\n"), "", $out);
+		$cached = preg_replace('/<!--+[^<>]+-->/', '', $cached);
+		$expected =  str_replace(array("\t", "\r\n", "\n"), "", $cached);
+
+		$this->assertEqual($result, $expected);
+
+		$filename = $this->__cachePath($dispatcher->here);
+		unlink($filename);
+
+		$dispatcher->base = false;
+		$url = 'test_cached_pages/index';
+
+		ob_start();
+		$dispatcher->dispatch($url);
+		$out = ob_get_clean();
+
+		ob_start();
+		$dispatcher->cached($url);
+		$cached = ob_get_clean();
+
+		$result = str_replace(array("\t", "\r\n", "\n"), "", $out);
+		$cached = preg_replace('/<!--+[^<>]+-->/', '', $cached);
+		$expected =  str_replace(array("\t", "\r\n", "\n"), "", $cached);
+
+		$this->assertEqual($result, $expected);
+		$filename = $this->__cachePath($dispatcher->here);
+		unlink($filename);
+
+		$url = 'TestCachedPages/index';
+
+		ob_start();
+		$dispatcher->dispatch($url);
+		$out = ob_get_clean();
+
+		ob_start();
+		$dispatcher->cached($url);
+		$cached = ob_get_clean();
+
+		$result = str_replace(array("\t", "\r\n", "\n"), "", $out);
+		$cached = preg_replace('/<!--+[^<>]+-->/', '', $cached);
+		$expected =  str_replace(array("\t", "\r\n", "\n"), "", $cached);
+
+		$this->assertEqual($result, $expected);
+		$filename = $this->__cachePath($dispatcher->here);
+		unlink($filename);
+
+		$url = 'TestCachedPages/test_nocache_tags';
+
+		ob_start();
+		$dispatcher->dispatch($url);
+		$out = ob_get_clean();
+
+		ob_start();
+		$dispatcher->cached($url);
+		$cached = ob_get_clean();
+
+		$result = str_replace(array("\t", "\r\n", "\n"), "", $out);
+		$cached = preg_replace('/<!--+[^<>]+-->/', '', $cached);
+		$expected =  str_replace(array("\t", "\r\n", "\n"), "", $cached);
+
+		$this->assertEqual($result, $expected);
+		$filename = $this->__cachePath($dispatcher->here);
+		unlink($filename);
+
+		$url = 'test_cached_pages/view/param/param';
+
+		ob_start();
+		$dispatcher->dispatch($url);
+		$out = ob_get_clean();
+
+		ob_start();
+		$dispatcher->cached($url);
+		$cached = ob_get_clean();
+
+		$result = str_replace(array("\t", "\r\n", "\n"), "", $out);
+		$cached = preg_replace('/<!--+[^<>]+-->/', '', $cached);
+		$expected =  str_replace(array("\t", "\r\n", "\n"), "", $cached);
+
+		$this->assertEqual($result, $expected);
+		$filename = $this->__cachePath($dispatcher->here);
+		unlink($filename);
+
+		$url = 'test_cached_pages/view/foo:bar/value:goo';
+
+		ob_start();
+		$dispatcher->dispatch($url);
+		$out = ob_get_clean();
+
+		ob_start();
+		$dispatcher->cached($url);
+		$cached = ob_get_clean();
+
+		$result = str_replace(array("\t", "\r\n", "\n"), "", $out);
+		$cached = preg_replace('/<!--+[^<>]+-->/', '', $cached);
+		$expected =  str_replace(array("\t", "\r\n", "\n"), "", $cached);
+
+		$this->assertEqual($result, $expected);
+		$filename = $this->__cachePath($dispatcher->here);
+		$this->assertTrue(file_exists($filename));
+		unlink($filename);
+	}
+
+/**
+ * test that cached() registers a view and un-registers it.  Tests
+ * that helpers using ClassRegistry::getObject('view'); don't fail
+ *
+ * @return void
+ */
+	function testCachedRegisteringViewObject() {
+		Configure::write('Cache.disable', false);
+		Configure::write('Cache.check', true);
+		Configure::write('debug', 2);
+
+		$_POST = array();
+		$_SERVER['PHP_SELF'] = '/';
+
+		Router::reload();
+		App::build(array(
+			'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS)
+		));
+
+		$dispatcher =& new TestDispatcher();
+		$dispatcher->base = false;
+
+		$url = 'test_cached_pages/cache_form';
+		ob_start();
+		$dispatcher->dispatch($url);
+		$out = ob_get_clean();
+
+		ClassRegistry::flush();
+
+		ob_start();
+		$dispatcher->cached($url);
+		$cached = ob_get_clean();
+
+		$result = str_replace(array("\t", "\r\n", "\n"), "", $out);
+		$cached = preg_replace('/<!--+[^<>]+-->/', '', $cached);
+		$expected =  str_replace(array("\t", "\r\n", "\n"), "", $cached);
+
+		$this->assertEqual($result, $expected);
+		$filename = $this->__cachePath($dispatcher->here);
+		@unlink($filename);
+		ClassRegistry::flush();
+	}
+
+/**
+ * testHttpMethodOverrides method
+ *
+ * @return void
+ * @access public
+ */
+	function testHttpMethodOverrides() {
+		Router::reload();
+		Router::mapResources('Posts');
+
+		$_SERVER['REQUEST_METHOD'] = 'POST';
+		$dispatcher =& new Dispatcher();
+		$dispatcher->base = false;
+
+		$result = $dispatcher->parseParams('/posts');
+		$expected = array('pass' => array(), 'named' => array(), 'plugin' => null, 'controller' => 'posts', 'action' => 'add', '[method]' => 'POST', 'form' => array(), 'url' => array());
+		$this->assertEqual($result, $expected);
+
+		$_SERVER['REQUEST_METHOD'] = 'GET';
+		$_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] = 'PUT';
+
+		$result = $dispatcher->parseParams('/posts/5');
+		$expected = array('pass' => array('5'), 'named' => array(), 'id' => '5', 'plugin' => null, 'controller' => 'posts', 'action' => 'edit', '[method]' => 'PUT', 'form' => array(), 'url' => array());
+		$this->assertEqual($result, $expected);
+
+		unset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']);
+		$_SERVER['REQUEST_METHOD'] = 'GET';
+
+		$result = $dispatcher->parseParams('/posts/5');
+		$expected = array('pass' => array('5'), 'named' => array(), 'id' => '5', 'plugin' => null, 'controller' => 'posts', 'action' => 'view', '[method]' => 'GET', 'form' => array(), 'url' => array());
+		$this->assertEqual($result, $expected);
+
+		$_POST['_method'] = 'PUT';
+
+		$result = $dispatcher->parseParams('/posts/5');
+		$expected = array('pass' => array('5'), 'named' => array(), 'id' => '5', 'plugin' => null, 'controller' => 'posts', 'action' => 'edit', '[method]' => 'PUT', 'form' => array(), 'url' => array());
+		$this->assertEqual($result, $expected);
+
+		$_POST['_method'] = 'POST';
+		$_POST['data'] = array('Post' => array('title' => 'New Post'));
+		$_POST['extra'] = 'data';
+		$_SERVER = array();
+
+		$result = $dispatcher->parseParams('/posts');
+		$expected = array(
+			'pass' => array(), 'named' => array(), 'plugin' => null, 'controller' => 'posts', 'action' => 'add',
+			'[method]' => 'POST', 'form' => array('extra' => 'data'), 'data' => array('Post' => array('title' => 'New Post')),
+			'url' => array()
+		);
+		$this->assertEqual($result, $expected);
+
+		unset($_POST['_method']);
+	}
+
+/**
+ * Tests that invalid characters cannot be injected into the application base path.
+ *
+ * @return void
+ * @access public
+ */
+	function testBasePathInjection() {
+		$self = $_SERVER['PHP_SELF'];
+		$_SERVER['PHP_SELF'] = urldecode(
+			"/index.php/%22%3E%3Ch1%20onclick=%22alert('xss');%22%3Eheya%3C/h1%3E"
+		);
+
+		$dispatcher =& new Dispatcher();
+		$result = $dispatcher->baseUrl();
+		$expected = '/index.php/h1 onclick=alert(xss);heya';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testEnvironmentDetection method
+ *
+ * @return void
+ * @access public
+ */
+	function testEnvironmentDetection() {
+		$dispatcher =& new Dispatcher();
+
+		$environments = array(
+			'IIS' => array(
+				'No rewrite base path' => array(
+					'App' => array('base' => false, 'baseUrl' => '/index.php?', 'server' => 'IIS'),
+					'SERVER' => array('HTTPS' => 'off', 'SCRIPT_NAME' => '/index.php', 'PATH_TRANSLATED' => 'C:\\Inetpub\\wwwroot', 'QUERY_STRING' => '', 'REMOTE_ADDR' => '127.0.0.1', 'REMOTE_HOST' => '127.0.0.1', 'REQUEST_METHOD' => 'GET', 'SERVER_NAME' => 'localhost', 'SERVER_PORT' => '80', 'SERVER_PROTOCOL' => 'HTTP/1.1', 'APPL_PHYSICAL_PATH' => 'C:\\Inetpub\\wwwroot\\', 'REQUEST_URI' => '/index.php', 'URL' => '/index.php', 'SCRIPT_FILENAME' => 'C:\\Inetpub\\wwwroot\\index.php', 'ORIG_PATH_INFO' => '/index.php', 'PATH_INFO' => '', 'ORIG_PATH_TRANSLATED' => 'C:\\Inetpub\\wwwroot\\index.php', 'DOCUMENT_ROOT' => 'C:\\Inetpub\\wwwroot', 'PHP_SELF' => '/index.php', 'HTTP_HOST' => 'localhost', 'argv' => array(), 'argc' => 0),
+					'reload' => true,
+					'path' => ''
+				),
+				'No rewrite with path' => array(
+					'SERVER' => array('QUERY_STRING' => '/posts/add', 'REQUEST_URI' => '/index.php?/posts/add', 'URL' => '/index.php?/posts/add', 'argv' => array('/posts/add'), 'argc' => 1),
+					'reload' => false,
+					'path' => '/posts/add'
+				),
+				'No rewrite sub dir 1' => array(
+					'GET' => array(),
+					'SERVER' => array('QUERY_STRING' => '',  'REQUEST_URI' => '/index.php', 'URL' => '/index.php', 'SCRIPT_FILENAME' => 'C:\\Inetpub\\wwwroot\\index.php', 'ORIG_PATH_INFO' => '/index.php', 'PATH_INFO' => '', 'ORIG_PATH_TRANSLATED' => 'C:\\Inetpub\\wwwroot\\index.php', 'DOCUMENT_ROOT' => 'C:\\Inetpub\\wwwroot', 'PHP_SELF' => '/index.php', 'argv' => array(), 'argc' => 0),
+					'reload' => false,
+					'path' => ''
+				),
+				'No rewrite sub dir 1 with path' => array(
+					'GET' => array('/posts/add' => ''),
+					'SERVER' => array('QUERY_STRING' => '/posts/add', 'REQUEST_URI' => '/index.php?/posts/add', 'URL' => '/index.php?/posts/add', 'SCRIPT_FILENAME' => 'C:\\Inetpub\\wwwroot\\index.php', 'argv' => array('/posts/add'), 'argc' => 1),
+					'reload' => false,
+					'path' => '/posts/add'
+				),
+				'No rewrite sub dir 2' => array(
+					'App' => array('base' => false, 'baseUrl' => '/site/index.php?', 'dir' => 'app', 'webroot' => 'webroot', 'server' => 'IIS'),
+					'GET' => array(),
+					'POST' => array(),
+					'SERVER' => array('SCRIPT_NAME' => '/site/index.php', 'PATH_TRANSLATED' => 'C:\\Inetpub\\wwwroot', 'QUERY_STRING' => '', 'REQUEST_URI' => '/site/index.php', 'URL' => '/site/index.php', 'SCRIPT_FILENAME' => 'C:\\Inetpub\\wwwroot\\site\\index.php', 'DOCUMENT_ROOT' => 'C:\\Inetpub\\wwwroot', 'PHP_SELF' => '/site/index.php', 'argv' => array(), 'argc' => 0),
+					'reload' => false,
+					'path' => ''
+				),
+				'No rewrite sub dir 2 with path' => array(
+					'GET' => array('/posts/add' => ''),
+					'SERVER' => array('SCRIPT_NAME' => '/site/index.php', 'PATH_TRANSLATED' => 'C:\\Inetpub\\wwwroot', 'QUERY_STRING' => '/posts/add', 'REQUEST_URI' => '/site/index.php?/posts/add', 'URL' => '/site/index.php?/posts/add', 'ORIG_PATH_TRANSLATED' => 'C:\\Inetpub\\wwwroot\\site\\index.php', 'DOCUMENT_ROOT' => 'C:\\Inetpub\\wwwroot', 'PHP_SELF' => '/site/index.php', 'argv' => array('/posts/add'), 'argc' => 1),
+					'reload' => false,
+					'path' => '/posts/add'
+				)
+			),
+			'Apache' => array(
+				'No rewrite base path' => array(
+					'App' => array('base' => false, 'baseUrl' => '/index.php', 'dir' => 'app', 'webroot' => 'webroot'),
+					'SERVER' => array(
+						'SERVER_NAME' => 'localhost',
+						'SERVER_ADDR' => '::1',
+						'SERVER_PORT' => '80',
+						'REMOTE_ADDR' => '::1',
+						'DOCUMENT_ROOT' => '/Library/WebServer/Documents/officespace/app/webroot',
+						'SCRIPT_FILENAME' => '/Library/WebServer/Documents/site/app/webroot/index.php',
+						'QUERY_STRING' => '',
+						'REQUEST_URI' => '/',
+						'SCRIPT_NAME' => '/index.php',
+						'PHP_SELF' => '/index.php',
+						'argv' => array(),
+						'argc' => 0
+					),
+					'reload' => true,
+					'path' => ''
+				),
+				'No rewrite with path' => array(
+					'SERVER' => array(
+						'HTTP_HOST' => 'localhost', 
+						'DOCUMENT_ROOT' => '/Library/WebServer/Documents/officespace/app/webroot',
+						'SCRIPT_FILENAME' => '/Library/WebServer/Documents/officespace/app/webroot/index.php',
+						'QUERY_STRING' => '',
+						'REQUEST_URI' => '/index.php/posts/add',
+						'SCRIPT_NAME' => '/index.php',
+						'PATH_INFO' => '/posts/add',
+						'PHP_SELF' => '/index.php/posts/add', 
+						'argv' => array(),
+						'argc' => 0),
+					'reload' => false,
+					'path' => '/posts/add'
+				),
+				'GET Request at base domain' => array(
+					'App' => array('base' => false, 'baseUrl' => null, 'dir' => 'app', 'webroot' => 'webroot'),
+					'SERVER'	=> array(
+						'HTTP_HOST' => 'cake.1.2',
+						'SERVER_NAME' => 'cake.1.2',
+						'SERVER_ADDR' => '127.0.0.1',
+						'SERVER_PORT' => '80',
+						'REMOTE_ADDR' => '127.0.0.1',
+						'DOCUMENT_ROOT' => '/Volumes/Home/htdocs/cake/repo/branches/1.2.x.x/app/webroot',
+						'SCRIPT_FILENAME' => '/Volumes/Home/htdocs/cake/repo/branches/1.2.x.x/app/webroot/index.php',
+						'REMOTE_PORT' => '53550',
+						'QUERY_STRING' => 'a=b',
+						'REQUEST_URI' => '/?a=b',
+						'SCRIPT_NAME' => '/index.php',
+						'PHP_SELF' => '/index.php'
+					),
+					'GET' => array('a' => 'b'),
+					'POST' => array(),
+					'reload' => true,
+					'path' => '',
+					'urlParams' => array('a' => 'b'),
+					'environment' => array('CGI_MODE' => false)
+				),
+				'New CGI no mod_rewrite' => array(
+					'App' => array('base' => false, 'baseUrl' => '/limesurvey20/index.php', 'dir' => 'app', 'webroot' => 'webroot'),
+					'SERVER' => array(
+						'DOCUMENT_ROOT' => '/home/.sites/110/site313/web',
+						'PATH_INFO' => '/installations',
+						'PATH_TRANSLATED' => '/home/.sites/110/site313/web/limesurvey20/index.php',
+						'PHPRC' => '/home/.sites/110/site313',
+						'QUERY_STRING' => '',
+						'REQUEST_URI' => '/limesurvey20/index.php/installations',
+						'SCRIPT_FILENAME' => '/home/.sites/110/site313/web/limesurvey20/index.php',
+						'SCRIPT_NAME' => '/limesurvey20/index.php',
+						'SCRIPT_URI' => 'http://www.gisdat-umfragen.at/limesurvey20/index.php/installations',
+						'PHP_SELF' => '/limesurvey20/index.php/installations',
+						'CGI_MODE' => true
+					),
+					'GET' => array(),
+					'POST' => array(),
+					'reload' => true,
+					'path' => '/installations',
+					'urlParams' => array(),
+					'environment' => array('CGI_MODE' => true)
+				)
+			)
+		);
+		$backup = $this->__backupEnvironment();
+
+		foreach ($environments as $name => $env) {
+			foreach ($env as $descrip => $settings) {
+				if ($settings['reload']) {
+					$this->__reloadEnvironment();
+				}
+				$this->__loadEnvironment($settings);
+				$this->assertEqual($dispatcher->uri(), $settings['path'], "%s on environment: {$name}, on setting: {$descrip}");
+
+				if (isset($settings['urlParams'])) {
+					$this->assertEqual($_GET, $settings['urlParams'], "%s on environment: {$name}, on setting: {$descrip}");
+				}
+				if (isset($settings['environment'])) {
+					foreach ($settings['environment'] as $key => $val) {
+						$this->assertEqual(env($key), $val, "%s on key {$key} on environment: {$name}, on setting: {$descrip}");
+					}
+				}
+			}
+		}
+		$this->__loadEnvironment(array_merge(array('reload' => true), $backup));
+	}
+
+/**
+ * Tests that the Dispatcher does not return an empty action
+ *
+ * @return void
+ * @access public
+ */
+	function testTrailingSlash() {
+		$_POST = array();
+		$_SERVER['PHP_SELF'] = '/cake/repo/branches/1.2.x.x/index.php';
+
+		Router::reload();
+		$Dispatcher =& new TestDispatcher();
+		Router::connect('/myalias/:action/*', array('controller' => 'my_controller', 'action' => null));
+
+		$Dispatcher->base = false;
+		$url = 'myalias/'; //Fails
+		$controller = $Dispatcher->dispatch($url, array('return' => 1));
+		$result = $Dispatcher->parseParams($url);
+		$this->assertEqual('index', $result['action']);
+
+		$url = 'myalias'; //Passes
+		$controller = $Dispatcher->dispatch($url, array('return' => 1));
+		$result = $Dispatcher->parseParams($url);
+		$this->assertEqual('index', $result['action']);
+	}
+
+/**
+ * backupEnvironment method
+ *
+ * @return void
+ * @access private
+ */
+	function __backupEnvironment() {
+		return array(
+			'App'	=> Configure::read('App'),
+			'GET'	=> $_GET,
+			'POST'	=> $_POST,
+			'SERVER'=> $_SERVER
+		);
+	}
+
+/**
+ * reloadEnvironment method
+ *
+ * @return void
+ * @access private
+ */
+	function __reloadEnvironment() {
+		foreach ($_GET as $key => $val) {
+			unset($_GET[$key]);
+		}
+		foreach ($_POST as $key => $val) {
+			unset($_POST[$key]);
+		}
+		foreach ($_SERVER as $key => $val) {
+			unset($_SERVER[$key]);
+		}
+		Configure::write('App', array());
+	}
+
+/**
+ * loadEnvironment method
+ *
+ * @param mixed $env
+ * @return void
+ * @access private
+ */
+	function __loadEnvironment($env) {
+		if ($env['reload']) {
+			$this->__reloadEnvironment();
+		}
+
+		if (isset($env['App'])) {
+			Configure::write('App', $env['App']);
+		}
+
+		if (isset($env['GET'])) {
+			foreach ($env['GET'] as $key => $val) {
+				$_GET[$key] = $val;
+			}
+		}
+
+		if (isset($env['POST'])) {
+			foreach ($env['POST'] as $key => $val) {
+				$_POST[$key] = $val;
+			}
+		}
+
+		if (isset($env['SERVER'])) {
+			foreach ($env['SERVER'] as $key => $val) {
+				$_SERVER[$key] = $val;
+			}
+		}
+	}
+
+/**
+ * cachePath method
+ *
+ * @param mixed $her
+ * @return string
+ * @access private
+ */
+	function __cachePath($here) {
+		$path = $here;
+		if ($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';
+		}
+		return $filename;
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/cache/apc.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/cache/apc.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/cache/apc.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,210 @@
+<?php
+/**
+ * ApcEngineTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.cache
+ * @since         CakePHP(tm) v 1.2.0.5434
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+if (!class_exists('Cache')) {
+	require LIBS . 'cache.php';
+}
+
+/**
+ * ApcEngineTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.cache
+ */
+class ApcEngineTest extends CakeTestCase {
+
+/**
+ * skip method
+ *
+ * @access public
+ * @return void
+ */
+	function skip() {
+		$skip = true;
+		if (function_exists('apc_store')) {
+			$skip = false;
+		}
+		$this->skipIf($skip, '%s Apc is not installed or configured properly');
+	}
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		$this->_cacheDisable = Configure::read('Cache.disable');
+		Configure::write('Cache.disable', false);
+		Cache::config('apc', array('engine' => 'Apc', 'prefix' => 'cake_'));
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		Configure::write('Cache.disable', $this->_cacheDisable);
+		Cache::drop('apc');
+		Cache::config('default');
+	}
+
+/**
+ * testReadAndWriteCache method
+ *
+ * @access public
+ * @return void
+ */
+	function testReadAndWriteCache() {
+		Cache::set(array('duration' => 1));
+
+		$result = Cache::read('test');
+		$expecting = '';
+		$this->assertEqual($result, $expecting);
+
+		$data = 'this is a test of the emergency broadcasting system';
+		$result = Cache::write('test', $data);
+		$this->assertTrue($result);
+
+		$result = Cache::read('test');
+		$expecting = $data;
+		$this->assertEqual($result, $expecting);
+
+		Cache::delete('test');
+	}
+
+/**
+ * Writing cache entries with duration = 0 (forever) should work.
+ *
+ * @return void
+ */
+	function testReadWriteDurationZero() {
+		Cache::config('apc', array('engine' => 'Apc', 'duration' => 0, 'prefix' => 'cake_'));
+		Cache::write('zero', 'Should save', 'apc');
+		sleep(1);
+
+		$result = Cache::read('zero', 'apc');
+		$this->assertEqual('Should save', $result);
+	}
+
+/**
+ * testExpiry method
+ *
+ * @access public
+ * @return void
+ */
+	function testExpiry() {
+		Cache::set(array('duration' => 1));
+
+		$result = Cache::read('test');
+		$this->assertFalse($result);
+
+		$data = 'this is a test of the emergency broadcasting system';
+		$result = Cache::write('other_test', $data);
+		$this->assertTrue($result);
+
+		sleep(2);
+		$result = Cache::read('other_test');
+		$this->assertFalse($result);
+
+		Cache::set(array('duration' =>  1));
+
+		$data = 'this is a test of the emergency broadcasting system';
+		$result = Cache::write('other_test', $data);
+		$this->assertTrue($result);
+
+		sleep(2);
+		$result = Cache::read('other_test');
+		$this->assertFalse($result);
+
+		sleep(2);
+		$result = Cache::read('other_test');
+		$this->assertFalse($result);
+	}
+
+/**
+ * testDeleteCache method
+ *
+ * @access public
+ * @return void
+ */
+	function testDeleteCache() {
+		$data = 'this is a test of the emergency broadcasting system';
+		$result = Cache::write('delete_test', $data);
+		$this->assertTrue($result);
+
+		$result = Cache::delete('delete_test');
+		$this->assertTrue($result);
+	}
+
+/**
+ * testDecrement method
+ *
+ * @access public
+ * @return void
+ */
+	function testDecrement() {
+		if ($this->skipIf(!function_exists('apc_dec'), 'No apc_dec() function, cannot test decrement() %s')) {
+			return;
+		}
+		$result = Cache::write('test_decrement', 5);
+		$this->assertTrue($result);
+
+		$result = Cache::decrement('test_decrement');
+		$this->assertEqual(4, $result);
+
+		$result = Cache::read('test_decrement');
+		$this->assertEqual(4, $result);
+
+		$result = Cache::decrement('test_decrement', 2);
+		$this->assertEqual(2, $result);
+
+		$result = Cache::read('test_decrement');
+		$this->assertEqual(2, $result);
+		
+	}
+
+/**
+ * testIncrement method
+ *
+ * @access public
+ * @return void
+ */
+	function testIncrement() {
+		if ($this->skipIf(!function_exists('apc_inc'), 'No apc_inc() function, cannot test increment() %s')) {
+			return;
+		}
+		$result = Cache::write('test_increment', 5);
+		$this->assertTrue($result);
+
+		$result = Cache::increment('test_increment');
+		$this->assertEqual(6, $result);
+
+		$result = Cache::read('test_increment');
+		$this->assertEqual(6, $result);
+
+		$result = Cache::increment('test_increment', 2);
+		$this->assertEqual(8, $result);
+
+		$result = Cache::read('test_increment');
+		$this->assertEqual(8, $result);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/cache/file.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/cache/file.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/cache/file.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,404 @@
+<?php
+/**
+ * FileEngineTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.cache
+ * @since         CakePHP(tm) v 1.2.0.5434
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+if (!class_exists('Cache')) {
+	require LIBS . 'cache.php';
+}
+
+/**
+ * FileEngineTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.cache
+ */
+class FileEngineTest extends CakeTestCase {
+
+/**
+ * config property
+ *
+ * @var array
+ * @access public
+ */
+	var $config = array();
+
+/**
+ * startCase method
+ *
+ * @access public
+ * @return void
+ */
+	function startCase() {
+		$this->_cacheDisable = Configure::read('Cache.disable');
+		$this->_cacheConfig = Cache::config('default');
+		Configure::write('Cache.disable', false);
+		Cache::config('default', array('engine' => 'File', 'path' => CACHE));
+	}
+
+/**
+ * endCase method
+ *
+ * @access public
+ * @return void
+ */
+	function endCase() {
+		Configure::write('Cache.disable', $this->_cacheDisable);
+		Cache::config('default', $this->_cacheConfig['settings']);
+	}
+
+/**
+ * testCacheDirChange method
+ *
+ * @access public
+ * @return void
+ */
+	function testCacheDirChange() {
+		$result = Cache::config('sessions', array('engine'=> 'File', 'path' => TMP . 'sessions'));
+		$this->assertEqual($result['settings'], Cache::settings('sessions'));
+
+		$result = Cache::config('sessions', array('engine'=> 'File', 'path' => TMP . 'tests'));
+		$this->assertEqual($result['settings'], Cache::settings('sessions'));
+		$this->assertNotEqual($result['settings'], Cache::settings('default'));
+	}
+
+/**
+ * testReadAndWriteCache method
+ *
+ * @access public
+ * @return void
+ */
+	function testReadAndWriteCache() {
+		Cache::config('default');
+
+		$result = Cache::write(null, 'here');
+		$this->assertFalse($result);
+
+		Cache::set(array('duration' => 1));
+
+		$result = Cache::read('test');
+		$expecting = '';
+		$this->assertEqual($result, $expecting);
+
+		$data = 'this is a test of the emergency broadcasting system';
+		$result = Cache::write('test', $data);
+		$this->assertTrue(file_exists(CACHE . 'cake_test'));
+
+		$result = Cache::read('test');
+		$expecting = $data;
+		$this->assertEqual($result, $expecting);
+
+		Cache::delete('test');
+	}
+
+/**
+ * testExpiry method
+ *
+ * @access public
+ * @return void
+ */
+	function testExpiry() {
+		Cache::set(array('duration' => 1));
+
+		$result = Cache::read('test');
+		$this->assertFalse($result);
+
+		$data = 'this is a test of the emergency broadcasting system';
+		$result = Cache::write('other_test', $data);
+		$this->assertTrue($result);
+
+		sleep(2);
+		$result = Cache::read('other_test');
+		$this->assertFalse($result);
+
+		Cache::set(array('duration' =>  "+1 second"));
+
+		$data = 'this is a test of the emergency broadcasting system';
+		$result = Cache::write('other_test', $data);
+		$this->assertTrue($result);
+
+		sleep(2);
+		$result = Cache::read('other_test');
+		$this->assertFalse($result);
+	}
+
+/**
+ * testDeleteCache method
+ *
+ * @access public
+ * @return void
+ */
+	function testDeleteCache() {
+		$data = 'this is a test of the emergency broadcasting system';
+		$result = Cache::write('delete_test', $data);
+		$this->assertTrue($result);
+
+		$result = Cache::delete('delete_test');
+		$this->assertTrue($result);
+		$this->assertFalse(file_exists(TMP . 'tests' . DS . 'delete_test'));
+
+		$result = Cache::delete('delete_test');
+		$this->assertFalse($result);
+	}
+
+/**
+ * testSerialize method
+ *
+ * @access public
+ * @return void
+ */
+	function testSerialize() {
+		Cache::config('default', array('engine' => 'File', 'serialize' => true));
+		$data = 'this is a test of the emergency broadcasting system';
+		$write = Cache::write('serialize_test', $data);
+		$this->assertTrue($write);
+
+		Cache::config('default', array('serialize' => false));
+		$read = Cache::read('serialize_test');
+
+		$newread = Cache::read('serialize_test');
+
+		$delete = Cache::delete('serialize_test');
+
+		$this->assertIdentical($read, serialize($data));
+
+		$this->assertIdentical(unserialize($newread), $data);
+	}
+
+/**
+ * testClear method
+ *
+ * @access public
+ * @return void
+ */
+	function testClear() {
+		Cache::config('default', array('engine' => 'File', 'duration' => 1));
+		$data = 'this is a test of the emergency broadcasting system';
+		$write = Cache::write('serialize_test1', $data);
+		$write = Cache::write('serialize_test2', $data);
+		$write = Cache::write('serialize_test3', $data);
+		$this->assertTrue(file_exists(CACHE . 'cake_serialize_test1'));
+		$this->assertTrue(file_exists(CACHE . 'cake_serialize_test2'));
+		$this->assertTrue(file_exists(CACHE . 'cake_serialize_test3'));
+		sleep(2);
+		$result = Cache::clear(true);
+		$this->assertTrue($result);
+		$this->assertFalse(file_exists(CACHE . 'cake_serialize_test1'));
+		$this->assertFalse(file_exists(CACHE . 'cake_serialize_test2'));
+		$this->assertFalse(file_exists(CACHE . 'cake_serialize_test3'));
+
+		$data = 'this is a test of the emergency broadcasting system';
+		$write = Cache::write('serialize_test1', $data);
+		$write = Cache::write('serialize_test2', $data);
+		$write = Cache::write('serialize_test3', $data);
+		$this->assertTrue(file_exists(CACHE . 'cake_serialize_test1'));
+		$this->assertTrue(file_exists(CACHE . 'cake_serialize_test2'));
+		$this->assertTrue(file_exists(CACHE . 'cake_serialize_test3'));
+
+		$result = Cache::clear();
+		$this->assertTrue($result);
+		$this->assertFalse(file_exists(CACHE . 'cake_serialize_test1'));
+		$this->assertFalse(file_exists(CACHE . 'cake_serialize_test2'));
+		$this->assertFalse(file_exists(CACHE . 'cake_serialize_test3'));
+
+		Cache::config('default', array('engine' => 'File', 'path' => CACHE . 'views'));
+
+		$data = 'this is a test of the emergency broadcasting system';
+		$write = Cache::write('controller_view_1', $data);
+		$write = Cache::write('controller_view_2', $data);
+		$write = Cache::write('controller_view_3', $data);
+		$write = Cache::write('controller_view_10', $data);
+		$write = Cache::write('controller_view_11', $data);
+		$write = Cache::write('controller_view_12', $data);
+		$this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_1'));
+		$this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_2'));
+		$this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_3'));
+		$this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_10'));
+		$this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_11'));
+		$this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_12'));
+
+		clearCache('controller_view_1', 'views', '');
+		$this->assertFalse(file_exists(CACHE . 'views'. DS . 'cake_controller_view_1'));
+		$this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_2'));
+		$this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_3'));
+		$this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_10'));
+		$this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_11'));
+		$this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_12'));
+
+		clearCache('controller_view', 'views', '');
+		$this->assertFalse(file_exists(CACHE . 'views'. DS . 'cake_controller_view_1'));
+		$this->assertFalse(file_exists(CACHE . 'views'. DS . 'cake_controller_view_2'));
+		$this->assertFalse(file_exists(CACHE . 'views'. DS . 'cake_controller_view_3'));
+		$this->assertFalse(file_exists(CACHE . 'views'. DS . 'cake_controller_view_10'));
+		$this->assertFalse(file_exists(CACHE . 'views'. DS . 'cake_controller_view_11'));
+		$this->assertFalse(file_exists(CACHE . 'views'. DS . 'cake_controller_view_12'));
+
+		$write = Cache::write('controller_view_1', $data);
+		$write = Cache::write('controller_view_2', $data);
+		$write = Cache::write('controller_view_3', $data);
+		$write = Cache::write('controller_view_10', $data);
+		$write = Cache::write('controller_view_11', $data);
+		$write = Cache::write('controller_view_12', $data);
+		$this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_1'));
+		$this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_2'));
+		$this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_3'));
+		$this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_10'));
+		$this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_11'));
+		$this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_12'));
+
+		clearCache(array('controller_view_2', 'controller_view_11', 'controller_view_12'), 'views', '');
+		$this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_1'));
+		$this->assertFalse(file_exists(CACHE . 'views'. DS . 'cake_controller_view_2'));
+		$this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_3'));
+		$this->assertTrue(file_exists(CACHE . 'views'. DS . 'cake_controller_view_10'));
+		$this->assertFalse(file_exists(CACHE . 'views'. DS . 'cake_controller_view_11'));
+		$this->assertFalse(file_exists(CACHE . 'views'. DS . 'cake_controller_view_12'));
+
+		clearCache('controller_view');
+
+		Cache::config('default', array('engine' => 'File', 'path' => CACHE));
+	}
+
+/**
+ * test that clear() doesn't wipe files not in the current engine's prefix.
+ *
+ * @return void
+ */
+	function testClearWithPrefixes() {
+		$FileOne =& new FileEngine();
+		$FileOne->init(array(
+			'prefix' => 'prefix_one_',
+			'duration' => DAY
+		));
+		$FileTwo =& new FileEngine();
+		$FileTwo->init(array(
+			'prefix' => 'prefix_two_',
+			'duration' => DAY
+		));
+
+		$data1 = $data2 = $expected = 'content to cache';
+		$FileOne->write('key_one', $data1, DAY);
+		$FileTwo->write('key_two', $data2, DAY);
+
+		$this->assertEqual($FileOne->read('key_one'), $expected);
+		$this->assertEqual($FileTwo->read('key_two'), $expected);
+
+		$FileOne->clear(false);
+		$this->assertEqual($FileTwo->read('key_two'), $expected, 'secondary config was cleared by accident.');
+	}
+
+/**
+ * testKeyPath method
+ *
+ * @access public
+ * @return void
+ */
+	function testKeyPath() {
+		$result = Cache::write('views.countries.something', 'here');
+		$this->assertTrue($result);
+		$this->assertTrue(file_exists(CACHE . 'cake_views_countries_something'));
+
+		$result = Cache::read('views.countries.something');
+		$this->assertEqual($result, 'here');
+
+		$result = Cache::clear();
+		$this->assertTrue($result);
+	}
+
+/**
+ * testRemoveWindowsSlashesFromCache method
+ *
+ * @access public
+ * @return void
+ */
+	function testRemoveWindowsSlashesFromCache() {
+		Cache::config('windows_test', array('engine' => 'File', 'isWindows' => true, 'prefix' => null, 'path' => TMP));
+
+		$expected = array (
+			'C:\dev\prj2\sites\cake\libs' => array(
+				0 => 'C:\dev\prj2\sites\cake\libs', 1 => 'C:\dev\prj2\sites\cake\libs\view',
+				2 => 'C:\dev\prj2\sites\cake\libs\view\scaffolds', 3 => 'C:\dev\prj2\sites\cake\libs\view\pages',
+				4 => 'C:\dev\prj2\sites\cake\libs\view\layouts', 5 => 'C:\dev\prj2\sites\cake\libs\view\layouts\xml',
+				6 => 'C:\dev\prj2\sites\cake\libs\view\layouts\rss', 7 => 'C:\dev\prj2\sites\cake\libs\view\layouts\js',
+				8 => 'C:\dev\prj2\sites\cake\libs\view\layouts\email', 9 => 'C:\dev\prj2\sites\cake\libs\view\layouts\email\text',
+				10 => 'C:\dev\prj2\sites\cake\libs\view\layouts\email\html', 11 => 'C:\dev\prj2\sites\cake\libs\view\helpers',
+				12 => 'C:\dev\prj2\sites\cake\libs\view\errors', 13 => 'C:\dev\prj2\sites\cake\libs\view\elements',
+				14 => 'C:\dev\prj2\sites\cake\libs\view\elements\email', 15 => 'C:\dev\prj2\sites\cake\libs\view\elements\email\text',
+				16 => 'C:\dev\prj2\sites\cake\libs\view\elements\email\html', 17 => 'C:\dev\prj2\sites\cake\libs\model',
+				18 => 'C:\dev\prj2\sites\cake\libs\model\datasources', 19 => 'C:\dev\prj2\sites\cake\libs\model\datasources\dbo',
+				20 => 'C:\dev\prj2\sites\cake\libs\model\behaviors', 21 => 'C:\dev\prj2\sites\cake\libs\controller',
+				22 => 'C:\dev\prj2\sites\cake\libs\controller\components', 23 => 'C:\dev\prj2\sites\cake\libs\cache'),
+			'C:\dev\prj2\sites\main_site\vendors' => array(
+				0 => 'C:\dev\prj2\sites\main_site\vendors', 1 => 'C:\dev\prj2\sites\main_site\vendors\shells',
+				2 => 'C:\dev\prj2\sites\main_site\vendors\shells\templates', 3 => 'C:\dev\prj2\sites\main_site\vendors\shells\templates\cdc_project',
+				4 => 'C:\dev\prj2\sites\main_site\vendors\shells\tasks', 5 => 'C:\dev\prj2\sites\main_site\vendors\js',
+				6 => 'C:\dev\prj2\sites\main_site\vendors\css'),
+			'C:\dev\prj2\sites\vendors' => array(
+				0 => 'C:\dev\prj2\sites\vendors', 1 => 'C:\dev\prj2\sites\vendors\simpletest',
+				2 => 'C:\dev\prj2\sites\vendors\simpletest\test', 3 => 'C:\dev\prj2\sites\vendors\simpletest\test\support',
+				4 => 'C:\dev\prj2\sites\vendors\simpletest\test\support\collector', 5 => 'C:\dev\prj2\sites\vendors\simpletest\extensions',
+				6 => 'C:\dev\prj2\sites\vendors\simpletest\extensions\testdox', 7 => 'C:\dev\prj2\sites\vendors\simpletest\docs',
+				8 => 'C:\dev\prj2\sites\vendors\simpletest\docs\fr', 9 => 'C:\dev\prj2\sites\vendors\simpletest\docs\en'),
+			'C:\dev\prj2\sites\main_site\views\helpers' => array(
+				0 => 'C:\dev\prj2\sites\main_site\views\helpers')
+		);
+
+		Cache::write('test_dir_map', $expected, 'windows_test');
+		$data = Cache::read('test_dir_map', 'windows_test');
+		Cache::delete('test_dir_map', 'windows_test');
+		$this->assertEqual($expected, $data);
+
+		Cache::drop('windows_test');
+	}
+
+/**
+ * testWriteQuotedString method
+ *
+ * @access public
+ * @return void
+ */
+	function testWriteQuotedString() {
+		Cache::config('default', array('engine' => 'File', 'path' => TMP . 'tests'));
+		Cache::write('App.doubleQuoteTest', '"this is a quoted string"');
+		$this->assertIdentical(Cache::read('App.doubleQuoteTest'), '"this is a quoted string"');
+		Cache::write('App.singleQuoteTest', "'this is a quoted string'");
+		$this->assertIdentical(Cache::read('App.singleQuoteTest'), "'this is a quoted string'");
+
+		Cache::config('default', array('isWindows' => true, 'path' => TMP . 'tests'));
+		$this->assertIdentical(Cache::read('App.doubleQuoteTest'), '"this is a quoted string"');
+		Cache::write('App.singleQuoteTest', "'this is a quoted string'");
+		$this->assertIdentical(Cache::read('App.singleQuoteTest'), "'this is a quoted string'");
+	}
+
+/**
+ * check that FileEngine generates an error when a configured Path does not exist.
+ *
+ * @return void
+ */
+	function testErrorWhenPathDoesNotExist() {
+		if ($this->skipIf(is_dir(TMP . 'tests' . DS . 'file_failure'), 'Cannot run test directory exists. %s')) {
+			return;
+		}
+		$this->expectError();
+		Cache::config('failure', array(
+			'engine' => 'File',
+			'path' => TMP . 'tests' . DS . 'file_failure'
+		));
+
+		Cache::drop('failure');
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/cache/memcache.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/cache/memcache.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/cache/memcache.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,401 @@
+<?php
+/**
+ * MemcacheEngineTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.cache
+ * @since         CakePHP(tm) v 1.2.0.5434
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+if (!class_exists('Cache')) {
+	require LIBS . 'cache.php';
+}
+App::import('Core', 'cache/Memcache');
+
+
+class TestMemcacheEngine extends MemcacheEngine {
+/**
+ * public accessor to _parseServerString
+ *
+ * @param string $server 
+ * @return array
+ */
+	function parseServerString($server) {
+		return $this->_parseServerString($server);
+	}
+	
+	function setMemcache(&$memcache) {
+		$this->__Memcache = $memcache;
+	}
+}
+
+Mock::generate('Memcache', 'MemcacheMockMemcache');
+
+/**
+ * MemcacheEngineTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.cache
+ */
+class MemcacheEngineTest extends CakeTestCase {
+
+/**
+ * skip method
+ *
+ * @access public
+ * @return void
+ */
+	function skip() {
+		$skip = true;
+		if (class_exists('Memcache')) {
+			$skip = false;
+		}
+		$this->skipIf($skip, '%s Memcache is not installed or configured properly.');
+	}
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		$this->_cacheDisable = Configure::read('Cache.disable');
+		Configure::write('Cache.disable', false);
+		Cache::config('memcache', array(
+			'engine' => 'Memcache',
+			'prefix' => 'cake_',
+			'duration' => 3600
+		));
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		Configure::write('Cache.disable', $this->_cacheDisable);
+		Cache::drop('memcache');
+		Cache::config('default');
+	}
+
+/**
+ * testSettings method
+ *
+ * @access public
+ * @return void
+ */
+	function testSettings() {
+		$settings = Cache::settings();
+		unset($settings['serialize'], $settings['path']);
+		$expecting = array(
+			'prefix' => 'cake_',
+			'duration'=> 3600,
+			'probability' => 100,
+			'servers' => array('127.0.0.1'),
+			'compress' => false,
+			'engine' => 'Memcache',
+			'persistent' => true,
+		);
+		$this->assertEqual($settings, $expecting);
+	}
+
+/**
+ * testSettings method
+ *
+ * @access public
+ * @return void
+ */
+	function testMultipleServers() {
+		$servers = array('127.0.0.1:11211', '127.0.0.1:11222');
+		$available = true;
+		$Memcache =& new Memcache();
+
+		foreach($servers as $server) {
+			list($host, $port) = explode(':', $server);
+			if (!@$Memcache->connect($host, $port)) {
+				$available = false;
+			}
+		}
+
+		if ($this->skipIf(!$available, '%s Need memcache servers at ' . implode(', ', $servers) . ' to run this test')) {
+			return;
+		}
+		$Memcache =& new MemcacheEngine();
+		$Memcache->init(array('engine' => 'Memcache', 'servers' => $servers));
+
+		$servers = array_keys($Memcache->__Memcache->getExtendedStats());
+		$settings = $Memcache->settings();
+		$this->assertEqual($servers, $settings['servers']);
+		Cache::drop('dual_server');
+	}
+
+/**
+ * testConnect method
+ *
+ * @access public
+ * @return void
+ */
+	function testConnect() {
+		$Memcache =& new MemcacheEngine();
+		$Memcache->init(Cache::settings('memcache'));
+		$result = $Memcache->connect('127.0.0.1');
+		$this->assertTrue($result);
+	}
+
+/**
+ * test connecting to an ipv6 server.
+ *
+ * @return void
+ */
+	function testConnectIpv6() {
+		$Memcache =& new MemcacheEngine();
+		$result = $Memcache->init(array(
+			'prefix' => 'cake_',
+			'duration' => 200,
+			'engine' => 'Memcache',
+			'servers' => array(
+				'[::1]:11211'
+			)
+		));
+		$this->assertTrue($result);
+	}
+
+/**
+ * test non latin domains.
+ *
+ * @return void
+ */
+	function testParseServerStringNonLatin() {
+		$Memcache =& new TestMemcacheEngine();
+		$result = $Memcache->parseServerString('schülervz.net:13211');
+		$this->assertEqual($result, array('schülervz.net', '13211'));
+
+		$result = $Memcache->parseServerString('sülül:1111');
+		$this->assertEqual($result, array('sülül', '1111'));
+	}
+
+/**
+ * test unix sockets.
+ *
+ * @return void
+ */
+    function testParseServerStringUnix() {
+        $Memcache =& new TestMemcacheEngine();
+        $result = $Memcache->parseServerString('unix:///path/to/memcached.sock');
+        $this->assertEqual($result, array('unix:///path/to/memcached.sock', 0));
+    }
+
+/**
+ * testReadAndWriteCache method
+ *
+ * @access public
+ * @return void
+ */
+	function testReadAndWriteCache() {
+		Cache::set(array('duration' => 1));
+
+		$result = Cache::read('test');
+		$expecting = '';
+		$this->assertEqual($result, $expecting);
+
+		$data = 'this is a test of the emergency broadcasting system';
+		$result = Cache::write('test', $data);
+		$this->assertTrue($result);
+
+		$result = Cache::read('test');
+		$expecting = $data;
+		$this->assertEqual($result, $expecting);
+
+		Cache::delete('test');
+	}
+
+/**
+ * testExpiry method
+ *
+ * @access public
+ * @return void
+ */
+	function testExpiry() {
+		Cache::set(array('duration' => 1));
+
+		$result = Cache::read('test');
+		$this->assertFalse($result);
+
+		$data = 'this is a test of the emergency broadcasting system';
+		$result = Cache::write('other_test', $data);
+		$this->assertTrue($result);
+
+		sleep(2);
+		$result = Cache::read('other_test');
+		$this->assertFalse($result);
+
+		Cache::set(array('duration' =>  "+1 second"));
+
+		$data = 'this is a test of the emergency broadcasting system';
+		$result = Cache::write('other_test', $data);
+		$this->assertTrue($result);
+
+		sleep(2);
+		$result = Cache::read('other_test');
+		$this->assertFalse($result);
+
+		Cache::config('memcache', array('duration' => '+1 second'));
+		sleep(2);
+
+		$result = Cache::read('other_test');
+		$this->assertFalse($result);
+
+		Cache::config('memcache', array('duration' => '+29 days'));
+		$data = 'this is a test of the emergency broadcasting system';
+		$result = Cache::write('long_expiry_test', $data);
+		$this->assertTrue($result);
+
+		sleep(2);
+		$result = Cache::read('long_expiry_test');
+		$expecting = $data;
+		$this->assertEqual($result, $expecting);
+
+		Cache::config('memcache', array('duration' => 3600));
+	}
+
+/**
+ * testDeleteCache method
+ *
+ * @access public
+ * @return void
+ */
+	function testDeleteCache() {
+		$data = 'this is a test of the emergency broadcasting system';
+		$result = Cache::write('delete_test', $data);
+		$this->assertTrue($result);
+
+		$result = Cache::delete('delete_test');
+		$this->assertTrue($result);
+	}
+
+/**
+ * testDecrement method
+ *
+ * @access public
+ * @return void
+ */
+	function testDecrement() {
+		$result = Cache::write('test_decrement', 5);
+		$this->assertTrue($result);
+
+		$result = Cache::decrement('test_decrement');
+		$this->assertEqual(4, $result);
+
+		$result = Cache::read('test_decrement');
+		$this->assertEqual(4, $result);
+
+		$result = Cache::decrement('test_decrement', 2);
+		$this->assertEqual(2, $result);
+
+		$result = Cache::read('test_decrement');
+		$this->assertEqual(2, $result);
+	}
+
+/**
+ * testIncrement method
+ *
+ * @access public
+ * @return void
+ */
+	function testIncrement() {
+		$result = Cache::write('test_increment', 5);
+		$this->assertTrue($result);
+
+		$result = Cache::increment('test_increment');
+		$this->assertEqual(6, $result);
+
+		$result = Cache::read('test_increment');
+		$this->assertEqual(6, $result);
+
+		$result = Cache::increment('test_increment', 2);
+		$this->assertEqual(8, $result);
+
+		$result = Cache::read('test_increment');
+		$this->assertEqual(8, $result);
+	}
+
+/**
+ * test that configurations don't conflict, when a file engine is declared after a memcache one.
+ *
+ * @return void
+ */
+	function testConfigurationConflict() {
+		Cache::config('long_memcache', array(
+		  'engine' => 'Memcache',
+		  'duration'=> '+2 seconds',
+		  'servers' => array('127.0.0.1:11211'),
+		));
+		Cache::config('short_memcache', array(
+		  'engine' => 'Memcache',
+		  'duration'=> '+1 seconds',
+		  'servers' => array('127.0.0.1:11211'),
+		));
+		Cache::config('some_file', array('engine' => 'File'));
+
+		$this->assertTrue(Cache::write('duration_test', 'yay', 'long_memcache'));
+		$this->assertTrue(Cache::write('short_duration_test', 'boo', 'short_memcache'));
+
+		$this->assertEqual(Cache::read('duration_test', 'long_memcache'), 'yay', 'Value was not read %s');
+		$this->assertEqual(Cache::read('short_duration_test', 'short_memcache'), 'boo', 'Value was not read %s');
+
+		sleep(1);
+		$this->assertEqual(Cache::read('duration_test', 'long_memcache'), 'yay', 'Value was not read %s');
+
+		sleep(2);
+		$this->assertFalse(Cache::read('short_duration_test', 'short_memcache'), 'Cache was not invalidated %s');
+		$this->assertFalse(Cache::read('duration_test', 'long_memcache'), 'Value did not expire %s');
+
+		Cache::delete('duration_test', 'long_memcache');
+		Cache::delete('short_duration_test', 'short_memcache');
+	}
+
+/**
+ * test that a 0 duration can succesfully write.
+ *
+ * @return void
+ */
+	function testZeroDuration() {
+		Cache::config('memcache', array('duration' => 0));
+		$result = Cache::write('test_key', 'written!', 'memcache');
+
+		$this->assertTrue($result, 'Could not write with duration 0');
+		$result = Cache::read('test_key', 'memcache');
+		$this->assertEqual($result, 'written!');
+	}
+
+/**
+ * test that durations greater than 30 days never expire
+ *
+ * @return void
+ */
+	function testLongDurationEqualToZero() {
+		$memcache =& new TestMemcacheEngine();
+		$memcache->settings['compress'] = false;
+
+		$mock = new MemcacheMockMemcache();
+		$memcache->setMemcache($mock);
+		$mock->expectAt(0, 'set', array('key', 'value', false, 0));
+
+		$value = 'value';
+		$memcache->write('key', $value, 50 * DAY);
+	}
+
+}

Added: trunk/src/Web/cake/tests/cases/libs/cache/xcache.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/cache/xcache.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/cache/xcache.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,222 @@
+<?php
+/**
+ * XcacheEngineTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.cache
+ * @since         CakePHP(tm) v 1.2.0.5434
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+if (!class_exists('Cache')) {
+	require LIBS . 'cache.php';
+}
+
+/**
+ * XcacheEngineTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.cache
+ */
+class XcacheEngineTest extends UnitTestCase {
+
+/**
+ * skip method
+ *
+ * @access public
+ * @return void
+ */
+	function skip() {
+		$skip = true;
+		if (function_exists('xcache_set')) {
+			$skip = false;
+		}
+		$this->skipIf($skip, '%s Xcache is not installed or configured properly');
+	}
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		$this->_cacheDisable = Configure::read('Cache.disable');
+		Configure::write('Cache.disable', false);
+		Cache::config('xcache', array('engine' => 'Xcache', 'prefix' => 'cake_'));
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		Configure::write('Cache.disable', $this->_cacheDisable);
+		Cache::config('default');
+	}
+
+/**
+ * testSettings method
+ *
+ * @access public
+ * @return void
+ */
+	function testSettings() {
+		$settings = Cache::settings();
+		$expecting = array(
+			'prefix' => 'cake_',
+			'duration'=> 3600,
+			'probability' => 100,
+			'engine' => 'Xcache',
+		);
+		$this->assertTrue(isset($settings['PHP_AUTH_USER']));
+		$this->assertTrue(isset($settings['PHP_AUTH_PW']));
+
+		unset($settings['PHP_AUTH_USER'], $settings['PHP_AUTH_PW']);
+		$this->assertEqual($settings, $expecting);
+	}
+
+/**
+ * testReadAndWriteCache method
+ *
+ * @access public
+ * @return void
+ */
+	function testReadAndWriteCache() {
+		Cache::set(array('duration' => 1));
+
+		$result = Cache::read('test');
+		$expecting = '';
+		$this->assertEqual($result, $expecting);
+
+		$data = 'this is a test of the emergency broadcasting system';
+		$result = Cache::write('test', $data);
+		$this->assertTrue($result);
+
+		$result = Cache::read('test');
+		$expecting = $data;
+		$this->assertEqual($result, $expecting);
+
+		Cache::delete('test');
+	}
+
+/**
+ * testExpiry method
+ *
+ * @access public
+ * @return void
+ */
+	function testExpiry() {
+		Cache::set(array('duration' => 1));
+		$result = Cache::read('test');
+		$this->assertFalse($result);
+
+		$data = 'this is a test of the emergency broadcasting system';
+		$result = Cache::write('other_test', $data);
+		$this->assertTrue($result);
+
+		sleep(2);
+		$result = Cache::read('other_test');
+		$this->assertFalse($result);
+
+		Cache::set(array('duration' =>  "+1 second"));
+
+		$data = 'this is a test of the emergency broadcasting system';
+		$result = Cache::write('other_test', $data);
+		$this->assertTrue($result);
+
+		sleep(2);
+		$result = Cache::read('other_test');
+		$this->assertFalse($result);
+	}
+
+/**
+ * testDeleteCache method
+ *
+ * @access public
+ * @return void
+ */
+	function testDeleteCache() {
+		$data = 'this is a test of the emergency broadcasting system';
+		$result = Cache::write('delete_test', $data);
+		$this->assertTrue($result);
+
+		$result = Cache::delete('delete_test');
+		$this->assertTrue($result);
+	}
+
+/**
+ * testClearCache method
+ *
+ * @access public
+ * @return void
+ */
+	function testClearCache() {
+		$data = 'this is a test of the emergency broadcasting system';
+		$result = Cache::write('clear_test_1', $data);
+		$this->assertTrue($result);
+
+		$result = Cache::write('clear_test_2', $data);
+		$this->assertTrue($result);
+
+		$result = Cache::clear();
+		$this->assertTrue($result);
+	}
+
+/**
+ * testDecrement method
+ *
+ * @access public
+ * @return void
+ */
+	function testDecrement() {
+		$result = Cache::write('test_decrement', 5);
+		$this->assertTrue($result);
+
+		$result = Cache::decrement('test_decrement');
+		$this->assertEqual(4, $result);
+
+		$result = Cache::read('test_decrement');
+		$this->assertEqual(4, $result);
+
+		$result = Cache::decrement('test_decrement', 2);
+		$this->assertEqual(2, $result);
+
+		$result = Cache::read('test_decrement');
+		$this->assertEqual(2, $result);
+	}
+
+/**
+ * testIncrement method
+ *
+ * @access public
+ * @return void
+ */
+	function testIncrement() {
+		$result = Cache::write('test_increment', 5);
+		$this->assertTrue($result);
+
+		$result = Cache::increment('test_increment');
+		$this->assertEqual(6, $result);
+
+		$result = Cache::read('test_increment');
+		$this->assertEqual(6, $result);
+
+		$result = Cache::increment('test_increment', 2);
+		$this->assertEqual(8, $result);
+
+		$result = Cache::read('test_increment');
+		$this->assertEqual(8, $result);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/cache.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/cache.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/cache.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,372 @@
+<?php
+/**
+ * CacheTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.5432
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+if (!class_exists('Cache')) {
+	require LIBS . 'cache.php';
+}
+
+/**
+ * CacheTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class CacheTest extends CakeTestCase {
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		$this->_cacheDisable = Configure::read('Cache.disable');
+		Configure::write('Cache.disable', false);
+
+		$this->_defaultCacheConfig = Cache::config('default');
+		Cache::config('default', array('engine' => 'File', 'path' => TMP . 'tests'));
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		Configure::write('Cache.disable', $this->_cacheDisable);
+		Cache::config('default', $this->_defaultCacheConfig['settings']);
+	}
+
+/**
+ * testConfig method
+ *
+ * @access public
+ * @return void
+ */
+	function testConfig() {
+		$settings = array('engine' => 'File', 'path' => TMP . 'tests', 'prefix' => 'cake_test_');
+		$results = Cache::config('new', $settings);
+		$this->assertEqual($results, Cache::config('new'));
+		$this->assertTrue(isset($results['engine']));
+		$this->assertTrue(isset($results['settings']));
+	}
+
+/**
+ * Check that no fatal errors are issued doing normal things when Cache.disable is true.
+ *
+ * @return void
+ */
+	function testNonFatalErrorsWithCachedisable() {
+		Configure::write('Cache.disable', true);
+		Cache::config('test', array('engine' => 'File', 'path' => TMP, 'prefix' => 'error_test_'));
+
+		Cache::write('no_save', 'Noooo!', 'test');
+		Cache::read('no_save', 'test');
+		Cache::delete('no_save', 'test');
+		Cache::set('duration', '+10 minutes');
+
+		Configure::write('Cache.disable', false);
+	}
+
+/**
+ * test configuring CacheEngines in App/libs
+ *
+ * @return void
+ */
+	function testConfigWithLibAndPluginEngines() {
+		App::build(array(
+			'libs' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'libs' . DS),
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)
+		), true);
+
+		$settings = array('engine' => 'TestAppCache', 'path' => TMP, 'prefix' => 'cake_test_');
+		$result = Cache::config('libEngine', $settings);
+		$this->assertEqual($result, Cache::config('libEngine'));
+
+		$settings = array('engine' => 'TestPlugin.TestPluginCache', 'path' => TMP, 'prefix' => 'cake_test_');
+		$result = Cache::config('pluginLibEngine', $settings);
+		$this->assertEqual($result, Cache::config('pluginLibEngine'));
+
+		Cache::drop('libEngine');
+		Cache::drop('pluginLibEngine');
+
+		App::build();
+	}
+
+/**
+ * testInvalidConfig method
+ *
+ * Test that the cache class doesn't cause fatal errors with a partial path
+ *
+ * @access public
+ * @return void
+ */
+	function testInvaidConfig() {
+		$this->expectError();
+		Cache::config('invalid', array(
+			'engine' => 'File',
+			'duration' => '+1 year',
+			'prefix' => 'testing_invalid_',
+			'path' => 'data/',
+			'serialize' => true,
+			'random' => 'wii'
+		));
+		$read = Cache::read('Test', 'invalid');
+		$this->assertEqual($read, null);
+	}
+
+/**
+ * testConfigChange method
+ *
+ * @access public
+ * @return void
+ */
+	function testConfigChange() {
+		$_cacheConfigSessions = Cache::config('sessions');
+		$_cacheConfigTests = Cache::config('tests');
+
+		$result = Cache::config('sessions', array('engine'=> 'File', 'path' => TMP . 'sessions'));
+		$this->assertEqual($result['settings'], Cache::settings('sessions'));
+
+		$result = Cache::config('tests', array('engine'=> 'File', 'path' => TMP . 'tests'));
+		$this->assertEqual($result['settings'], Cache::settings('tests'));
+
+		Cache::config('sessions', $_cacheConfigSessions['settings']);
+		Cache::config('tests', $_cacheConfigTests['settings']);
+	}
+
+/**
+ * test that calling config() sets the 'default' configuration up.
+ *
+ * @return void
+ */
+	function testConfigSettingDefaultConfigKey() {
+		Cache::config('test_name', array('engine' => 'File', 'prefix' => 'test_name_'));
+
+		Cache::config('test_name');
+		Cache::write('value_one', 'I am cached');
+		$result = Cache::read('value_one');
+		$this->assertEqual($result, 'I am cached');
+
+		Cache::config('default');
+		$result = Cache::read('value_one');
+		$this->assertEqual($result, null);
+
+		Cache::write('value_one', 'I am in default config!');
+		$result = Cache::read('value_one');
+		$this->assertEqual($result, 'I am in default config!');
+
+		Cache::config('test_name');
+		$result = Cache::read('value_one');
+		$this->assertEqual($result, 'I am cached');
+
+		Cache::delete('value_one');
+		Cache::config('default');
+		Cache::delete('value_one');
+	}
+
+/**
+ * testWritingWithConfig method
+ *
+ * @access public
+ * @return void
+ */
+	function testWritingWithConfig() {
+		$_cacheConfigSessions = Cache::config('sessions');
+
+		Cache::write('test_somthing', 'this is the test data', 'tests');
+
+		$expected = array(
+			'path' => TMP . 'sessions',
+			'prefix' => 'cake_',
+			'lock' => false,
+			'serialize' => true,
+			'duration' => 3600,
+			'probability' => 100,
+			'engine' => 'File',
+			'isWindows' => DIRECTORY_SEPARATOR == '\\'
+		);
+		$this->assertEqual($expected, Cache::settings('sessions'));
+
+		Cache::config('sessions', $_cacheConfigSessions['settings']);
+	}
+
+/**
+ * test that configured returns an array of the currently configured cache
+ * settings
+ *
+ * @return void
+ */
+	function testConfigured() {
+		$result = Cache::configured();
+		$this->assertTrue(in_array('_cake_core_', $result));
+		$this->assertTrue(in_array('default', $result));
+	}
+
+/**
+ * testInitSettings method
+ *
+ * @access public
+ * @return void
+ */
+	function testInitSettings() {
+		Cache::config('for_test', array('engine' => 'File', 'path' => TMP . 'tests'));
+
+		$settings = Cache::settings();
+		$expecting = array(
+			'engine' => 'File',
+			'duration'=> 3600,
+			'probability' => 100,
+			'path'=> TMP . 'tests',
+			'prefix'=> 'cake_',
+			'lock' => false,
+			'serialize'=> true,
+			'isWindows' => DIRECTORY_SEPARATOR == '\\'
+		);
+		$this->assertEqual($settings, $expecting);
+	}
+
+/**
+ * test that drop removes cache configs, and that further attempts to use that config
+ * do not work.
+ *
+ * @return void
+ */
+	function testDrop() {
+		App::build(array(
+			'libs' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'libs' . DS),
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)
+		), true);
+
+		$result = Cache::drop('some_config_that_does_not_exist');
+		$this->assertFalse($result);
+
+		$_testsConfig = Cache::config('tests');
+		$result = Cache::drop('tests');
+		$this->assertTrue($result);
+
+		Cache::config('unconfigTest', array(
+			'engine' => 'TestAppCache'
+		));
+		$this->assertTrue(Cache::isInitialized('unconfigTest'));
+
+		$this->assertTrue(Cache::drop('unconfigTest'));
+		$this->assertFalse(Cache::isInitialized('TestAppCache'));
+
+		Cache::config('tests', $_testsConfig);
+		App::build();
+	}
+
+/**
+ * testWriteEmptyValues method
+ *
+ * @access public
+ * @return void
+ */
+	function testWriteEmptyValues() {
+		Cache::write('App.falseTest', false);
+		$this->assertIdentical(Cache::read('App.falseTest'), false);
+
+		Cache::write('App.trueTest', true);
+		$this->assertIdentical(Cache::read('App.trueTest'), true);
+
+		Cache::write('App.nullTest', null);
+		$this->assertIdentical(Cache::read('App.nullTest'), null);
+
+		Cache::write('App.zeroTest', 0);
+		$this->assertIdentical(Cache::read('App.zeroTest'), 0);
+
+		Cache::write('App.zeroTest2', '0');
+		$this->assertIdentical(Cache::read('App.zeroTest2'), '0');
+	}
+
+/**
+ * testCacheDisable method
+ *
+ * Check that the "Cache.disable" configuration and a change to it
+ * (even after a cache config has been setup) is taken into account.
+ *
+ * @link https://trac.cakephp.org/ticket/6236
+ * @access public
+ * @return void
+ */
+	function testCacheDisable() {
+		Configure::write('Cache.disable', false);
+		Cache::config('test_cache_disable_1', array('engine'=> 'File', 'path' => TMP . 'tests'));
+
+		$this->assertTrue(Cache::write('key_1', 'hello'));
+		$this->assertIdentical(Cache::read('key_1'), 'hello');
+
+		Configure::write('Cache.disable', true);
+
+		$this->assertFalse(Cache::write('key_2', 'hello'));
+		$this->assertFalse(Cache::read('key_2'));
+
+		Configure::write('Cache.disable', false);
+
+		$this->assertTrue(Cache::write('key_3', 'hello'));
+		$this->assertIdentical(Cache::read('key_3'), 'hello');
+
+		Configure::write('Cache.disable', true);
+		Cache::config('test_cache_disable_2', array('engine'=> 'File', 'path' => TMP . 'tests'));
+
+		$this->assertFalse(Cache::write('key_4', 'hello'));
+		$this->assertFalse(Cache::read('key_4'));
+
+		Configure::write('Cache.disable', false);
+
+		$this->assertTrue(Cache::write('key_5', 'hello'));
+		$this->assertIdentical(Cache::read('key_5'), 'hello');
+
+		Configure::write('Cache.disable', true);
+
+		$this->assertFalse(Cache::write('key_6', 'hello'));
+		$this->assertFalse(Cache::read('key_6'));
+	}
+
+/**
+ * testSet method
+ *
+ * @access public
+ * @return void
+ */
+	function testSet() {
+		$_cacheSet = Cache::set();
+
+		Cache::set(array('duration' => '+1 year'));
+		$data = Cache::read('test_cache');
+		$this->assertFalse($data);
+
+		$data = 'this is just a simple test of the cache system';
+		$write = Cache::write('test_cache', $data);
+		$this->assertTrue($write);
+
+		Cache::set(array('duration' => '+1 year'));
+		$data = Cache::read('test_cache');
+		$this->assertEqual($data, 'this is just a simple test of the cache system');
+
+		Cache::delete('test_cache');
+
+		$global = Cache::settings();
+
+		Cache::set($_cacheSet);
+	}
+
+}

Added: trunk/src/Web/cake/tests/cases/libs/cake_log.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/cake_log.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/cake_log.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,183 @@
+<?php
+/**
+ * CakeLogTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.5432
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', 'Log');
+App::import('Core', 'log/FileLog');
+
+/**
+ * CakeLogTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class CakeLogTest extends CakeTestCase {
+
+/**
+ * Start test callback, clears all streams enabled.
+ *
+ * @return void
+ */
+	function startTest() {
+		$streams = CakeLog::configured();
+		foreach ($streams as $stream) {
+			CakeLog::drop($stream);
+		}
+	}
+
+/**
+ * test importing loggers from app/libs and plugins.
+ *
+ * @return void
+ */
+	function testImportingLoggers() {
+		App::build(array(
+			'libs' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'libs' . DS),
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)
+		), true);
+
+		$result = CakeLog::config('libtest', array(
+			'engine' => 'TestAppLog'
+		));
+		$this->assertTrue($result);
+		$this->assertEqual(CakeLog::configured(), array('libtest'));
+
+		$result = CakeLog::config('plugintest', array(
+			'engine' => 'TestPlugin.TestPluginLog'
+		));
+		$this->assertTrue($result);
+		$this->assertEqual(CakeLog::configured(), array('libtest', 'plugintest'));
+
+		App::build();
+	}
+
+/**
+ * test all the errors from failed logger imports
+ *
+ * @return void
+ */
+	function testImportingLoggerFailure() {
+		$this->expectError('Missing logger classname');
+		CakeLog::config('fail', array());
+
+		$this->expectError('Could not load logger class born to fail');
+		CakeLog::config('fail', array('engine' => 'born to fail'));
+
+		$this->expectError('logger class stdClass does not implement a write method.');
+		CakeLog::config('fail', array('engine' => 'stdClass'));
+	}
+
+/**
+ * Test that CakeLog autoconfigures itself to use a FileLogger with the LOGS dir.
+ * When no streams are there.
+ *
+ * @return void
+ */
+	function testAutoConfig() {
+		@unlink(LOGS . 'error.log');
+		CakeLog::write(LOG_WARNING, 'Test warning');
+		$this->assertTrue(file_exists(LOGS . 'error.log'));
+
+		$result = CakeLog::configured();
+		$this->assertEqual($result, array('default'));
+		unlink(LOGS . 'error.log');
+	}
+
+/**
+ * test configuring log streams
+ *
+ * @return void
+ */
+	function testConfig() {
+		CakeLog::config('file', array(
+			'engine' => 'FileLog',
+			'path' => LOGS
+		));
+		$result = CakeLog::configured();
+		$this->assertEqual($result, array('file'));
+
+		@unlink(LOGS . 'error.log');
+		CakeLog::write(LOG_WARNING, 'Test warning');
+		$this->assertTrue(file_exists(LOGS . 'error.log'));
+
+		$result = file_get_contents(LOGS . 'error.log');
+		$this->assertPattern('/^2[0-9]{3}-[0-9]+-[0-9]+ [0-9]+:[0-9]+:[0-9]+ Warning: Test warning/', $result);
+		unlink(LOGS . 'error.log');
+	}
+
+/**
+ * explict tests for drop()
+ *
+ * @return void
+ **/
+	function testDrop() {
+		CakeLog::config('file', array(
+			'engine' => 'FileLog',
+			'path' => LOGS
+		));
+		$result = CakeLog::configured();
+		$this->assertEqual($result, array('file'));
+
+		CakeLog::drop('file');
+		$result = CakeLog::configured();
+		$this->assertEqual($result, array());
+	}
+
+/**
+ * testLogFileWriting method
+ *
+ * @access public
+ * @return void
+ */
+	function testLogFileWriting() {
+		@unlink(LOGS . 'error.log');
+		$result = CakeLog::write(LOG_WARNING, 'Test warning');
+		$this->assertTrue($result);
+		$this->assertTrue(file_exists(LOGS . 'error.log'));
+		unlink(LOGS . 'error.log');
+
+		CakeLog::write(LOG_WARNING, 'Test warning 1');
+		CakeLog::write(LOG_WARNING, 'Test warning 2');
+		$result = file_get_contents(LOGS . 'error.log');
+		$this->assertPattern('/^2[0-9]{3}-[0-9]+-[0-9]+ [0-9]+:[0-9]+:[0-9]+ Warning: Test warning 1/', $result);
+		$this->assertPattern('/2[0-9]{3}-[0-9]+-[0-9]+ [0-9]+:[0-9]+:[0-9]+ Warning: Test warning 2$/', $result);
+		unlink(LOGS . 'error.log');
+	}
+
+/**
+ * Test logging with the error handler.
+ *
+ * @return void
+ */
+	function testLoggingWithErrorHandling() {
+		@unlink(LOGS . 'debug.log');
+		Configure::write('log', E_ALL & ~E_DEPRECATED & ~E_STRICT);
+		Configure::write('debug', 0);
+
+		set_error_handler(array('CakeLog', 'handleError'));
+		$out .= '';
+
+		$result = file(LOGS . 'debug.log');
+		$this->assertEqual(count($result), 1);
+		$this->assertPattern(
+			'/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} Notice: Notice \(8\): Undefined variable:\s+out in \[.+ line \d+\]$/',
+			$result[0]
+		);
+		@unlink(LOGS . 'debug.log');
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/cake_session.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/cake_session.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/cake_session.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,508 @@
+<?php
+/**
+ * SessionTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+if (!class_exists('CakeSession')) {
+	App::import('Core', 'CakeSession');
+}
+
+/**
+ * CakeSessionTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class CakeSessionTest extends CakeTestCase {
+
+/**
+ * Fixtures used in the SessionTest
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array('core.session');
+
+/**
+ * startCase method
+ *
+ * @access public
+ * @return void
+ */
+	function startCase() {
+		// Make sure garbage colector will be called
+		$this->__gc_divisor = ini_get('session.gc_divisor');
+		ini_set('session.gc_divisor', '1');
+	}
+
+/**
+ * endCase method
+ *
+ * @access public
+ * @return void
+ */
+	function endCase() {
+		// Revert to the default setting
+		ini_set('session.gc_divisor', $this->__gc_divisor);
+	}
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		$this->Session =& new CakeSession();
+		$this->Session->start();
+		$this->Session->_checkValid();
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+    function tearDown() {
+        unset($_SESSION);
+		session_destroy();
+    }
+
+/**
+ * testSessionPath
+ *
+ * @access public
+ * @return void
+ */
+	function testSessionPath() {
+		$Session = new CakeSession('/index.php');
+		$this->assertEqual('/', $Session->path);
+
+		$Session = new CakeSession('/sub_dir/index.php');
+		$this->assertEqual('/sub_dir/', $Session->path);
+
+		$Session = new CakeSession('');
+		$this->assertEqual('/', $Session->path, 'Session path is empty, with "" as $base needs to be / %s');
+	}
+
+/**
+ * testCheck method
+ *
+ * @access public
+ * @return void
+ */
+	function testCheck() {
+		$this->Session->write('SessionTestCase', 'value');
+		$this->assertTrue($this->Session->check('SessionTestCase'));
+
+		$this->assertFalse($this->Session->check('NotExistingSessionTestCase'), false);
+	}
+
+/**
+ * testSimpleRead method
+ *
+ * @access public
+ * @return void
+ */
+	function testSimpleRead() {
+		$this->Session->write('testing', '1,2,3');
+		$result = $this->Session->read('testing');
+		$this->assertEqual($result, '1,2,3');
+
+		$this->Session->write('testing', array('1' => 'one', '2' => 'two','3' => 'three'));
+		$result = $this->Session->read('testing.1');
+		$this->assertEqual($result, 'one');
+
+		$result = $this->Session->read('testing');
+		$this->assertEqual($result, array('1' => 'one', '2' => 'two', '3' => 'three'));
+
+		$result = $this->Session->read();
+		$this->assertTrue(isset($result['testing']));
+		$this->assertTrue(isset($result['Config']));
+		$this->assertTrue(isset($result['Config']['userAgent']));
+
+		$this->Session->write('This.is.a.deep.array.my.friend', 'value');
+		$result = $this->Session->read('This.is.a.deep.array.my.friend');
+		$this->assertEqual('value', $result);
+	}
+
+/**
+ * testId method
+ *
+ * @access public
+ * @return void
+ */
+	function testId() {
+		$expected = session_id();
+		$result = $this->Session->id();
+		$this->assertEqual($result, $expected);
+
+		$this->Session->id('MySessionId');
+		$result = $this->Session->id();
+		$this->assertEqual($result, 'MySessionId');
+	}
+
+/**
+ * testStarted method
+ *
+ * @access public
+ * @return void
+ */
+	function testStarted() {
+		$this->assertTrue($this->Session->started());
+
+		unset($_SESSION);
+		$_SESSION = null;
+		$this->assertFalse($this->Session->started());
+		$this->assertTrue($this->Session->start());
+
+		$session = new CakeSession(null, false);
+		$this->assertTrue($session->started());
+		unset($session);
+	}
+
+/**
+ * testError method
+ *
+ * @access public
+ * @return void
+ */
+	function testError() {
+		$this->Session->read('Does.not.exist');
+		$result = $this->Session->error();
+		$this->assertEqual($result, "Does.not.exist doesn't exist");
+
+		$this->Session->delete('Failing.delete');
+		$result = $this->Session->error();
+		$this->assertEqual($result, "Failing.delete doesn't exist");
+	}
+
+/**
+ * testDel method
+ *
+ * @access public
+ * @return void
+ */
+	function testDelete() {
+		$this->assertTrue($this->Session->write('Delete.me', 'Clearing out'));
+		$this->assertTrue($this->Session->delete('Delete.me'));
+		$this->assertFalse($this->Session->check('Delete.me'));
+		$this->assertTrue($this->Session->check('Delete'));
+
+		$this->assertTrue($this->Session->write('Clearing.sale', 'everything must go'));
+		$this->assertTrue($this->Session->delete('Clearing'));
+		$this->assertFalse($this->Session->check('Clearing.sale'));
+		$this->assertFalse($this->Session->check('Clearing'));
+	}
+
+/**
+ * testWatchVar method
+ *
+ * @access public
+ * @return void
+ */
+	function testWatchVar() {
+		$this->assertFalse($this->Session->watch(null));
+
+		$this->Session->write('Watching', "I'm watching you");
+		$this->Session->watch('Watching');
+		$this->expectError('Writing session key {Watching}: "They found us!"');
+		$this->Session->write('Watching', 'They found us!');
+
+		$this->expectError('Deleting session key {Watching}');
+		$this->Session->delete('Watching');
+
+		$this->assertFalse($this->Session->watch('Invalid.key'));
+	}
+
+/**
+ * testIgnore method
+ *
+ * @access public
+ * @return void
+ */
+	function testIgnore() {
+		$this->Session->write('Watching', "I'm watching you");
+		$this->Session->watch('Watching');
+		$this->Session->ignore('Watching');
+		$this->assertTrue($this->Session->write('Watching', 'They found us!'));
+	}
+
+/**
+ * testDestroy method
+ *
+ * @access public
+ * @return void
+ */
+	function testDestroy() {
+		$this->Session->write('bulletProof', 'invicible');
+		$id = $this->Session->id();
+		$this->Session->destroy();
+		$this->assertFalse($this->Session->check('bulletProof'));
+		$this->assertNotEqual($id, $this->Session->id());
+		$this->assertTrue($this->Session->started());
+
+		$this->Session->cookieLifeTime = 'test';
+		$this->Session->destroy();
+		$this->assertNotEqual('test', $this->Session->cookieLifeTime);
+	}
+
+/**
+ * testCheckingSavedEmpty method
+ *
+ * @access public
+ * @return void
+ */
+	function testCheckingSavedEmpty() {
+		$this->assertTrue($this->Session->write('SessionTestCase', 0));
+		$this->assertTrue($this->Session->check('SessionTestCase'));
+
+		$this->assertTrue($this->Session->write('SessionTestCase', '0'));
+		$this->assertTrue($this->Session->check('SessionTestCase'));
+
+		$this->assertTrue($this->Session->write('SessionTestCase', false));
+		$this->assertTrue($this->Session->check('SessionTestCase'));
+
+		$this->assertTrue($this->Session->write('SessionTestCase', null));
+		$this->assertFalse($this->Session->check('SessionTestCase'));
+	}
+
+/**
+ * testCheckKeyWithSpaces method
+ *
+ * @access public
+ * @return void
+ */
+	function testCheckKeyWithSpaces() {
+		$this->assertTrue($this->Session->write('Session Test', "test"));
+		$this->assertEqual($this->Session->check('Session Test'), 'test');
+		$this->Session->delete('Session Test');
+
+		$this->assertTrue($this->Session->write('Session Test.Test Case', "test"));
+		$this->assertTrue($this->Session->check('Session Test.Test Case'));
+	}
+
+/**
+ * test key exploitation
+ *
+ * @return void
+ */
+	function testKeyExploit() {
+		$key = "a'] = 1; phpinfo(); \$_SESSION['a";
+		$result = $this->Session->write($key, 'haxored');
+		$this->assertTrue($result);
+
+		$result = $this->Session->read($key);
+		$this->assertEqual($result, 'haxored');
+	}
+
+/**
+ * testReadingSavedEmpty method
+ *
+ * @access public
+ * @return void
+ */
+	function testReadingSavedEmpty() {
+		$this->Session->write('SessionTestCase', 0);
+		$this->assertEqual($this->Session->read('SessionTestCase'), 0);
+
+		$this->Session->write('SessionTestCase', '0');
+		$this->assertEqual($this->Session->read('SessionTestCase'), '0');
+		$this->assertFalse($this->Session->read('SessionTestCase') === 0);
+
+		$this->Session->write('SessionTestCase', false);
+		$this->assertFalse($this->Session->read('SessionTestCase'));
+
+		$this->Session->write('SessionTestCase', null);
+		$this->assertEqual($this->Session->read('SessionTestCase'), null);
+	}
+
+/**
+ * testCheckUserAgentFalse method
+ *
+ * @access public
+ * @return void
+ */
+	function testCheckUserAgentFalse() {
+		Configure::write('Session.checkAgent', false);
+		$this->Session->_userAgent = md5('http://randomdomainname.com' . Configure::read('Security.salt'));
+		$this->assertTrue($this->Session->valid());
+	}
+
+/**
+ * testCheckUserAgentTrue method
+ *
+ * @access public
+ * @return void
+ */
+	function testCheckUserAgentTrue() {
+		Configure::write('Session.checkAgent', true);
+		$this->Session->_userAgent = md5('http://randomdomainname.com' . Configure::read('Security.salt'));
+		$this->assertFalse($this->Session->valid());
+	}
+
+/**
+ * testReadAndWriteWithDatabaseStorage method
+ *
+ * @access public
+ * @return void
+ */
+	function testReadAndWriteWithCakeStorage() {
+		unset($_SESSION);
+		session_destroy();
+		ini_set('session.save_handler', 'files');
+		Configure::write('Session.save', 'cake');
+		$this->setUp();
+
+		$this->Session->write('SessionTestCase', 0);
+		$this->assertEqual($this->Session->read('SessionTestCase'), 0);
+
+		$this->Session->write('SessionTestCase', '0');
+		$this->assertEqual($this->Session->read('SessionTestCase'), '0');
+		$this->assertFalse($this->Session->read('SessionTestCase') === 0);
+
+		$this->Session->write('SessionTestCase', false);
+		$this->assertFalse($this->Session->read('SessionTestCase'));
+
+		$this->Session->write('SessionTestCase', null);
+		$this->assertEqual($this->Session->read('SessionTestCase'), null);
+
+		$this->Session->write('SessionTestCase', 'This is a Test');
+		$this->assertEqual($this->Session->read('SessionTestCase'), 'This is a Test');
+
+		$this->Session->write('SessionTestCase', 'This is a Test');
+		$this->Session->write('SessionTestCase', 'This was updated');
+		$this->assertEqual($this->Session->read('SessionTestCase'), 'This was updated');
+
+		$this->Session->destroy();
+		$this->assertFalse($this->Session->read('SessionTestCase'));
+	}
+
+/**
+ * testReadAndWriteWithDatabaseStorage method
+ *
+ * @access public
+ * @return void
+ */
+	function testReadAndWriteWithCacheStorage() {
+		unset($_SESSION);
+		session_destroy();
+		ini_set('session.save_handler', 'files');
+		Configure::write('Session.save', 'cache');
+		$this->setUp();
+
+		$this->Session->write('SessionTestCase', 0);
+		$this->assertEqual($this->Session->read('SessionTestCase'), 0);
+
+		$this->Session->write('SessionTestCase', '0');
+		$this->assertEqual($this->Session->read('SessionTestCase'), '0');
+		$this->assertFalse($this->Session->read('SessionTestCase') === 0);
+
+		$this->Session->write('SessionTestCase', false);
+		$this->assertFalse($this->Session->read('SessionTestCase'));
+
+		$this->Session->write('SessionTestCase', null);
+		$this->assertEqual($this->Session->read('SessionTestCase'), null);
+
+		$this->Session->write('SessionTestCase', 'This is a Test');
+		$this->assertEqual($this->Session->read('SessionTestCase'), 'This is a Test');
+
+		$this->Session->write('SessionTestCase', 'This is a Test');
+		$this->Session->write('SessionTestCase', 'This was updated');
+		$this->assertEqual($this->Session->read('SessionTestCase'), 'This was updated');
+
+		$this->Session->destroy();
+		$this->assertFalse($this->Session->read('SessionTestCase'));
+	}
+
+/**
+ * testReadAndWriteWithDatabaseStorage method
+ *
+ * @access public
+ * @return void
+ */
+	function testReadAndWriteWithDatabaseStorage() {
+		unset($_SESSION);
+		session_destroy();
+		Configure::write('Session.table', 'sessions');
+		Configure::write('Session.model', 'Session');
+		Configure::write('Session.database', 'test_suite');
+		Configure::write('Session.save', 'database');
+		$this->setUp();
+
+		$this->Session->write('SessionTestCase', 0);
+		$this->assertEqual($this->Session->read('SessionTestCase'), 0);
+
+		$this->Session->write('SessionTestCase', '0');
+		$this->assertEqual($this->Session->read('SessionTestCase'), '0');
+		$this->assertFalse($this->Session->read('SessionTestCase') === 0);
+
+		$this->Session->write('SessionTestCase', false);
+		$this->assertFalse($this->Session->read('SessionTestCase'));
+
+		$this->Session->write('SessionTestCase', null);
+		$this->assertEqual($this->Session->read('SessionTestCase'), null);
+
+		$this->Session->write('SessionTestCase', 'This is a Test');
+		$this->assertEqual($this->Session->read('SessionTestCase'), 'This is a Test');
+
+        $this->Session->write('SessionTestCase', 'Some additional data');
+        $this->assertEqual($this->Session->read('SessionTestCase'), 'Some additional data');
+
+		$this->Session->destroy();
+		$this->assertFalse($this->Session->read('SessionTestCase'));
+		session_write_close();
+
+		unset($_SESSION);
+		ini_set('session.save_handler', 'files');
+		Configure::write('Session.save', 'php');
+		$this->setUp();
+	}
+
+/**
+ * testReadAndWriteWithDatabaseStorage method
+ *
+ * @access public
+ * @return void
+ */
+	function testDatabaseStorageEmptySessionId() {
+		unset($_SESSION);
+		session_destroy();
+		Configure::write('Session.table', 'sessions');
+		Configure::write('Session.model', 'Session');
+		Configure::write('Session.database', 'test_suite');
+		Configure::write('Session.save', 'database');
+		$this->setUp();
+		$id = $this->Session->id();
+
+		$this->Session->id = '';
+		session_id('');
+
+		$this->Session->write('SessionTestCase', 'This is a Test');
+		$this->assertEqual($this->Session->read('SessionTestCase'), 'This is a Test');
+
+		session_write_close();
+
+		unset($_SESSION);
+		ini_set('session.save_handler', 'files');
+		Configure::write('Session.save', 'php');
+		session_id($id);
+		$this->setUp();
+	}
+
+}

Added: trunk/src/Web/cake/tests/cases/libs/cake_socket.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/cake_socket.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/cake_socket.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,192 @@
+<?php
+/**
+ * SocketTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', 'CakeSocket');
+
+/**
+ * SocketTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class CakeSocketTest extends CakeTestCase {
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		$this->Socket = new CakeSocket();
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		unset($this->Socket);
+	}
+
+/**
+ * testConstruct method
+ *
+ * @access public
+ * @return void
+ */
+	function testConstruct() {
+		$this->Socket->__construct();
+		$baseConfig = $this->Socket->_baseConfig;
+		$this->assertIdentical($baseConfig, array(
+			'persistent'	=> false,
+			'host'			=> 'localhost',
+			'protocol'		=> 'tcp',
+			'port'			=> 80,
+			'timeout'		=> 30
+		));
+
+		$this->Socket->reset();
+		$this->Socket->__construct(array('host' => 'foo-bar'));
+		$baseConfig['host'] = 'foo-bar';
+		$baseConfig['protocol'] = getprotobyname($baseConfig['protocol']);
+		$this->assertIdentical($this->Socket->config, $baseConfig);
+
+		$this->Socket = new CakeSocket(array('host' => 'www.cakephp.org', 'port' => 23, 'protocol' => 'udp'));
+		$baseConfig = $this->Socket->_baseConfig;
+
+		$baseConfig['host'] = 'www.cakephp.org';
+		$baseConfig['port'] = 23;
+		$baseConfig['protocol'] = 17;
+
+		$this->assertIdentical($this->Socket->config, $baseConfig);
+	}
+
+/**
+ * testSocketConnection method
+ *
+ * @access public
+ * @return void
+ */
+	function testSocketConnection() {
+		$this->assertFalse($this->Socket->connected);
+		$this->Socket->disconnect();
+		$this->assertFalse($this->Socket->connected);
+		$this->Socket->connect();
+		$this->assertTrue($this->Socket->connected);
+		$this->Socket->connect();
+		$this->assertTrue($this->Socket->connected);
+
+		$this->Socket->disconnect();
+		$config = array('persistent' => true);
+		$this->Socket = new CakeSocket($config);
+		$this->Socket->connect();
+		$this->assertTrue($this->Socket->connected);
+	}
+
+/**
+ * testSocketHost method
+ *
+ * @access public
+ * @return void
+ */
+	function testSocketHost() {
+		$this->Socket = new CakeSocket();
+		$this->Socket->connect();
+		$this->assertEqual($this->Socket->address(), '127.0.0.1');
+		$this->assertEqual(gethostbyaddr('127.0.0.1'), $this->Socket->host());
+		$this->assertEqual($this->Socket->lastError(), null);
+		$this->assertTrue(in_array('127.0.0.1', $this->Socket->addresses()));
+
+		$this->Socket = new CakeSocket(array('host' => '127.0.0.1'));
+		$this->Socket->connect();
+		$this->assertEqual($this->Socket->address(), '127.0.0.1');
+		$this->assertEqual(gethostbyaddr('127.0.0.1'), $this->Socket->host());
+		$this->assertEqual($this->Socket->lastError(), null);
+		$this->assertTrue(in_array('127.0.0.1', $this->Socket->addresses()));
+	}
+
+/**
+ * testSocketWriting method
+ *
+ * @access public
+ * @return void
+ */
+	function testSocketWriting() {
+		$request = "GET / HTTP/1.1\r\nConnection: close\r\n\r\n";
+		$this->assertTrue($this->Socket->write($request));
+	}
+
+/**
+ * testSocketReading method
+ *
+ * @access public
+ * @return void
+ */
+	function testSocketReading() {
+		$this->Socket = new CakeSocket(array('timeout' => 5));
+		$this->Socket->connect();
+		$this->assertEqual($this->Socket->read(26), null);
+
+		$config = array('host' => 'www.cakephp.org', 'timeout' => 1);
+		$this->Socket = new CakeSocket($config);
+		$this->assertTrue($this->Socket->connect());
+		$this->assertFalse($this->Socket->read(1024 * 1024));
+		$this->assertEqual($this->Socket->lastError(), '2: ' . __('Connection timed out', true));
+
+		$config = array('host' => 'www.cakephp.org', 'timeout' => 30);
+		$this->Socket = new CakeSocket($config);
+		$this->assertTrue($this->Socket->connect());
+		$this->assertEqual($this->Socket->read(26), null);
+		$this->assertEqual($this->Socket->lastError(), null);
+	}
+
+/**
+ * testLastError method
+ *
+ * @access public
+ * @return void
+ */
+	function testLastError() {
+		$this->Socket = new CakeSocket();
+		$this->Socket->setLastError(4, 'some error here');
+		$this->assertEqual($this->Socket->lastError(), '4: some error here');
+	}
+
+/**
+ * testReset method
+ *
+ * @access public
+ * @return void
+ */
+	function testReset() {
+		$config = array(
+			'persistent'	=> true,
+			'host'			=> '127.0.0.1',
+			'protocol'		=> 'udp',
+			'port'			=> 80,
+			'timeout'		=> 20
+		);
+		$anotherSocket = new CakeSocket($config);
+		$anotherSocket->reset();
+		$this->assertEqual(array(), $anotherSocket->config);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/cake_test_case.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/cake_test_case.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/cake_test_case.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,502 @@
+<?php
+/**
+ * CakeTestCaseTest file
+ *
+ * Test Case for CakeTestCase class
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://cakephp.org CakePHP Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.
+ * @since         CakePHP v 1.2.0.4487
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Core', 'CakeTestCase');
+
+if (!class_exists('AppController')) {
+	require_once LIBS . 'controller' . DS . 'app_controller.php';
+} elseif (!defined('APP_CONTROLLER_EXISTS')) {
+	define('APP_CONTROLLER_EXISTS', true);
+}
+
+Mock::generate('CakeHtmlReporter');
+Mock::generate('CakeTestCase', 'CakeDispatcherMockTestCase');
+
+SimpleTest::ignore('SubjectCakeTestCase');
+SimpleTest::ignore('CakeDispatcherMockTestCase');
+
+/**
+ * SubjectCakeTestCase
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class SubjectCakeTestCase extends CakeTestCase {
+
+/**
+ * Feed a Mocked Reporter to the subject case
+ * prevents its pass/fails from affecting the real test
+ *
+ * @param string $reporter
+ * @access public
+ * @return void
+ */
+	function setReporter(&$reporter) {
+		$this->_reporter = &$reporter;
+	}
+
+/**
+ * testDummy method
+ *
+ * @return void
+ * @access public
+ */
+	function testDummy() {
+	}
+}
+
+/**
+ * CakeTestCaseTest
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class CakeTestCaseTest extends CakeTestCase {
+
+/**
+ * setUp
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		$this->_debug = Configure::read('debug');
+		$this->Case =& new SubjectCakeTestCase();
+		$reporter =& new MockCakeHtmlReporter();
+		$this->Case->setReporter($reporter);
+		$this->Reporter = $reporter;
+	}
+
+/**
+ * tearDown
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		Configure::write('debug', $this->_debug);
+		unset($this->Case);
+		unset($this->Reporter);
+	}
+
+/**
+ * endTest
+ *
+ * @access public
+ * @return void
+ */
+	function endTest() {
+		App::build();
+	}
+
+/**
+ * testAssertGoodTags
+ *
+ * @access public
+ * @return void
+ */
+	function testAssertGoodTags() {
+		$this->Reporter->expectAtLeastOnce('paintPass');
+		$this->Reporter->expectNever('paintFail');
+
+		$input = '<p>Text</p>';
+		$pattern = array(
+			'<p',
+			'Text',
+			'/p',
+		);
+		$this->assertTrue($this->Case->assertTags($input, $pattern));
+
+		$input = '<a href="/test.html" class="active">My link</a>';
+		$pattern = array(
+			'a' => array('href' => '/test.html', 'class' => 'active'),
+			'My link',
+			'/a'
+		);
+		$this->assertTrue($this->Case->assertTags($input, $pattern));
+
+		$pattern = array(
+			'a' => array('class' => 'active', 'href' => '/test.html'),
+			'My link',
+			'/a'
+		);
+		$this->assertTrue($this->Case->assertTags($input, $pattern), 'Attributes in wrong order. %s');
+
+		$input = "<a    href=\"/test.html\"\t\n\tclass=\"active\"\tid=\"primary\">\t<span>My link</span></a>";
+		$pattern = array(
+			'a' => array('id' => 'primary', 'href' => '/test.html', 'class' => 'active'),
+			'<span',
+			'My link',
+			'/span',
+			'/a'
+		);
+		$this->assertTrue($this->Case->assertTags($input, $pattern), 'Whitespace consumption %s');
+
+		$input = '<p class="info"><a href="/test.html" class="active"><strong onClick="alert(\'hey\');">My link</strong></a></p>';
+		$pattern = array(
+			'p' => array('class' => 'info'),
+			'a' => array('class' => 'active', 'href' => '/test.html' ),
+			'strong' => array('onClick' => 'alert(\'hey\');'),
+			'My link',
+			'/strong',
+			'/a',
+			'/p'
+		);
+		$this->assertTrue($this->Case->assertTags($input, $pattern));
+	}
+
+/**
+ * test that assertTags knows how to handle correct quoting.
+ *
+ * @return void
+ */
+	function testAssertTagsQuotes() {
+		$input = '<a href="/test.html" class="active">My link</a>';
+		$pattern = array(
+			'a' => array('href' => '/test.html', 'class' => 'active'),
+			'My link',
+			'/a'
+		);
+		$this->assertTrue($this->Case->assertTags($input, $pattern), 'Double quoted attributes %s');
+
+		$input = "<a href='/test.html' class='active'>My link</a>";
+		$pattern = array(
+			'a' => array('href' => '/test.html', 'class' => 'active'),
+			'My link',
+			'/a'
+		);
+		$this->assertTrue($this->Case->assertTags($input, $pattern), 'Single quoted attributes %s');
+
+		$input = "<a href='/test.html' class='active'>My link</a>";
+		$pattern = array(
+			'a' => array('href' => 'preg:/.*\.html/', 'class' => 'active'),
+			'My link',
+			'/a'
+		);
+		$this->assertTrue($this->Case->assertTags($input, $pattern), 'Single quoted attributes %s');
+	}
+
+/**
+ * testNumericValuesInExpectationForAssertTags
+ *
+ * @access public
+ * @return void
+ */
+	function testNumericValuesInExpectationForAssertTags() {
+		$value = 220985;
+
+		$input = '<p><strong>' . $value . '</strong></p>';
+		$pattern = array(
+			'<p',
+				'<strong',
+					$value,
+				'/strong',
+			'/p'
+		);
+		$this->assertTrue($this->Case->assertTags($input, $pattern));
+
+		$input = '<p><strong>' . $value . '</strong></p><p><strong>' . $value . '</strong></p>';
+		$pattern = array(
+			'<p',
+				'<strong',
+					$value,
+				'/strong',
+			'/p',
+			'<p',
+				'<strong',
+					$value,
+				'/strong',
+			'/p',
+		);
+		$this->assertTrue($this->Case->assertTags($input, $pattern));
+
+		$input = '<p><strong>' . $value . '</strong></p><p id="' . $value . '"><strong>' . $value . '</strong></p>';
+		$pattern = array(
+			'<p',
+				'<strong',
+					$value,
+				'/strong',
+			'/p',
+			'p' => array('id' => $value),
+				'<strong',
+					$value,
+				'/strong',
+			'/p',
+		);
+		$this->assertTrue($this->Case->assertTags($input, $pattern));
+	}
+
+ /**
+ * testBadAssertTags
+ *
+ * @access public
+ * @return void
+ */
+	function testBadAssertTags() {
+		$this->Reporter->expectAtLeastOnce('paintFail');
+		$this->Reporter->expectNever('paintPass');
+
+		$input = '<a href="/test.html" class="active">My link</a>';
+		$pattern = array(
+			'a' => array('hRef' => '/test.html', 'clAss' => 'active'),
+			'My link',
+			'/a'
+		);
+		$this->assertFalse($this->Case->assertTags($input, $pattern));
+
+		$input = '<a href="/test.html" class="active">My link</a>';
+		$pattern = array(
+			'<a' => array('href' => '/test.html', 'class' => 'active'),
+			'My link',
+			'/a'
+		);
+		$this->assertFalse($this->Case->assertTags($input, $pattern));
+	}
+
+/**
+ * testBefore
+ *
+ * @access public
+ * @return void
+ */
+	function testBefore() {
+		$this->Case->before('testDummy');
+		$this->assertFalse(isset($this->Case->db));
+
+		$this->Case->fixtures = array('core.post');
+		$this->Case->before('start');
+		$this->assertTrue(isset($this->Case->db));
+		$this->assertTrue(isset($this->Case->_fixtures['core.post']));
+		$this->assertTrue(is_a($this->Case->_fixtures['core.post'], 'CakeTestFixture'));
+		$this->assertEqual($this->Case->_fixtureClassMap['Post'], 'core.post');
+	}
+
+/**
+ * testAfter
+ *
+ * @access public
+ * @return void
+ */
+	function testAfter() {
+		$this->Case->after('testDummy');
+		$this->assertFalse($this->Case->__truncated);
+
+		$this->Case->fixtures = array('core.post');
+		$this->Case->before('start');
+		$this->Case->start();
+		$this->Case->after('testDummy');
+		$this->assertTrue($this->Case->__truncated);
+	}
+
+/**
+ * testLoadFixtures
+ *
+ * @access public
+ * @return void
+ */
+	function testLoadFixtures() {
+		$this->Case->fixtures = array('core.post');
+		$this->Case->autoFixtures = false;
+		$this->Case->before('start');
+		$this->expectError();
+		$this->Case->loadFixtures('Wrong!');
+		$this->Case->end();
+	}
+
+/**
+ * testGetTests Method
+ *
+ * @return void
+ * @access public
+ */
+	function testGetTests() {
+		$result = $this->Case->getTests();
+		$this->assertEqual(array_slice($result, 0, 2), array('start', 'startCase'));
+		$this->assertEqual(array_slice($result, -2), array('endCase', 'end'));
+	}
+
+/**
+ * TestTestAction
+ *
+ * @access public
+ * @return void
+ */
+	function testTestAction() {
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS),
+			'models' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'models' . DS),
+			'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS),
+			'controllers' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'controllers' . DS)
+		), true);
+
+		$result = $this->Case->testAction('/tests_apps/index', array('return' => 'view'));
+		$this->assertPattern('/^\s*This is the TestsAppsController index view\s*$/i', $result);
+
+		$result = $this->Case->testAction('/tests_apps/index', array('return' => 'contents'));
+		$this->assertPattern('/\bThis is the TestsAppsController index view\b/i', $result);
+		$this->assertPattern('/<html/', $result);
+		$this->assertPattern('/<\/html>/', $result);
+
+		$result = $this->Case->testAction('/tests_apps/some_method', array('return' => 'result'));
+		$this->assertEqual($result, 5);
+
+		$result = $this->Case->testAction('/tests_apps/set_action', array('return' => 'vars'));
+		$this->assertEqual($result, array('var' => 'string'));
+
+		$db =& ConnectionManager::getDataSource('test_suite');
+		$fixture =& new PostFixture();
+		$fixture->create($db);
+
+		$result = $this->Case->testAction('/tests_apps_posts/add', array('return' => 'vars'));
+		$this->assertTrue(array_key_exists('posts', $result));
+		$this->assertEqual(count($result['posts']), 1);
+
+		$result = $this->Case->testAction('/tests_apps_posts/url_var/var1:value1/var2:val2', array(
+			'return' => 'vars',
+			'method' => 'get',
+		));
+		$this->assertTrue(isset($result['params']['url']['url']));
+		$this->assertEqual(array_keys($result['params']['named']), array('var1', 'var2'));
+
+		$result = $this->Case->testAction('/tests_apps_posts/url_var/gogo/val2', array(
+			'return' => 'vars',
+			'method' => 'get',
+		));
+		$this->assertEqual($result['params']['pass'], array('gogo', 'val2'));
+
+
+		$result = $this->Case->testAction('/tests_apps_posts/url_var', array(
+			'return' => 'vars',
+			'method' => 'get',
+			'data' => array(
+				'red' => 'health',
+				'blue' => 'mana'
+			)
+		));
+		$this->assertTrue(isset($result['params']['url']['red']));
+		$this->assertTrue(isset($result['params']['url']['blue']));
+		$this->assertTrue(isset($result['params']['url']['url']));
+
+		$result = $this->Case->testAction('/tests_apps_posts/post_var', array(
+			'return' => 'vars',
+			'method' => 'post',
+			'data' => array(
+				'name' => 'is jonas',
+				'pork' => 'and beans',
+			)
+		));
+		$this->assertEqual(array_keys($result['data']), array('name', 'pork'));
+		$fixture->drop($db);
+
+		$db =& ConnectionManager::getDataSource('test_suite');
+		$_backPrefix = $db->config['prefix'];
+		$db->config['prefix'] = 'cake_testaction_test_suite_';
+
+		$config = $db->config;
+		$config['prefix'] = 'cake_testcase_test_';
+
+		ConnectionManager::create('cake_test_case', $config);
+		$db2 =& ConnectionManager::getDataSource('cake_test_case');
+
+		$fixture =& new PostFixture($db2);
+		$fixture->create($db2);
+		$fixture->insert($db2);
+
+		$result = $this->Case->testAction('/tests_apps_posts/fixtured', array(
+			'return' => 'vars',
+			'fixturize' => true,
+			'connection' => 'cake_test_case',
+		));
+		$this->assertTrue(isset($result['posts']));
+		$this->assertEqual(count($result['posts']), 3);
+		$tables = $db2->listSources();
+		$this->assertFalse(in_array('cake_testaction_test_suite_posts', $tables));
+
+		$fixture->drop($db2);
+
+		$db =& ConnectionManager::getDataSource('test_suite');
+
+		//test that drop tables behaves as exepected with testAction
+		$db =& ConnectionManager::getDataSource('test_suite');
+		$_backPrefix = $db->config['prefix'];
+		$db->config['prefix'] = 'cake_testaction_test_suite_';
+
+		$config = $db->config;
+		$config['prefix'] = 'cake_testcase_test_';
+
+		ConnectionManager::create('cake_test_case', $config);
+		$db =& ConnectionManager::getDataSource('cake_test_case');
+		$fixture =& new PostFixture($db);
+		$fixture->create($db);
+		$fixture->insert($db);
+
+		$this->Case->dropTables = false;
+		$result = $this->Case->testAction('/tests_apps_posts/fixtured', array(
+			'return' => 'vars',
+			'fixturize' => true,
+			'connection' => 'cake_test_case',
+		));
+
+		$tables = $db->listSources();
+		$this->assertTrue(in_array('cake_testaction_test_suite_posts', $tables));
+
+		$fixture->drop($db);
+		$db =& ConnectionManager::getDataSource('test_suite');
+		$db->config['prefix'] = $_backPrefix;
+		$fixture->drop($db);
+	}
+
+/**
+ * testSkipIf
+ *
+ * @return void
+ */
+	function testSkipIf() {
+		$this->assertTrue($this->Case->skipIf(true));
+		$this->assertFalse($this->Case->skipIf(false));
+	}
+
+/**
+ * testTestDispatcher
+ *
+ * @access public
+ * @return void
+ */
+	function testTestDispatcher() {
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS),
+			'models' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'models' . DS),
+			'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS),
+			'controllers' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'controllers' . DS)
+		), true);
+
+		$Dispatcher =& new CakeTestDispatcher();
+		$Case =& new CakeDispatcherMockTestCase();
+
+		$Case->expectOnce('startController');
+		$Case->expectOnce('endController');
+
+		$Dispatcher->testCase($Case);
+		$this->assertTrue(isset($Dispatcher->testCase));
+
+		$return = $Dispatcher->dispatch('/tests_apps/index', array('autoRender' => 0, 'return' => 1, 'requested' => 1));
+	}
+}
\ No newline at end of file

Added: trunk/src/Web/cake/tests/cases/libs/cake_test_fixture.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/cake_test_fixture.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/cake_test_fixture.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,504 @@
+<?php
+/**
+ * CakeTestFixture file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.libs
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Datasource', 'DboSource', false);
+
+/**
+ * CakeTestFixtureTestFixture class
+ *
+ * @package       cake
+ * @subpackage    cake.cake.tests.cases.libs
+ */
+class CakeTestFixtureTestFixture extends CakeTestFixture {
+
+/**
+ * name Property
+ *
+ * @var string
+ */
+	var $name = 'FixtureTest';
+
+/**
+ * table property
+ *
+ * @var string
+ */
+	var $table = 'fixture_tests';
+
+/**
+ * Fields array
+ *
+ * @var array
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer',  'key' => 'primary'),
+		'name' => array('type' => 'string', 'length' => '255'),
+		'created' => array('type' => 'datetime')
+	);
+
+/**
+ * Records property
+ *
+ * @var array
+ */
+	var $records = array(
+		array('name' => 'Gandalf', 'created' => '2009-04-28 19:20:00'),
+		array('name' => 'Captain Picard', 'created' => '2009-04-28 19:20:00'),
+		array('name' => 'Chewbacca', 'created' => '2009-04-28 19:20:00')
+	);
+}
+
+/**
+ * StringFieldsTestFixture class
+ *
+ * @package       cake
+ * @subpackage    cake.cake.tests.cases.libs
+ */
+class StringsTestFixture extends CakeTestFixture {
+
+/**
+ * name Property
+ *
+ * @var string
+ */
+	var $name = 'Strings';
+
+/**
+ * table property
+ *
+ * @var string
+ */
+	var $table = 'strings';
+
+/**
+ * Fields array
+ *
+ * @var array
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer',  'key' => 'primary'),
+		'name' => array('type' => 'string', 'length' => '255'),
+		'email' => array('type' => 'string', 'length' => '255'),
+        'age' => array('type' => 'integer', 'default' => 10)
+	);
+
+/**
+ * Records property
+ *
+ * @var array
+ */
+	var $records = array(
+        array('name' => 'John Doe', 'email' => 'john.****@email*****', 'age' => 20),
+        array('email' => 'jane.****@email*****', 'name' => 'Jane Doe', 'age' => 30),
+        array('name' => 'Mark Doe', 'email' => 'mark.****@email*****')
+	);
+}
+
+
+/**
+ * CakeTestFixtureImportFixture class
+ *
+ * @package       cake
+ * @subpackage    cake.cake.tests.cases.libs
+ */
+class CakeTestFixtureImportFixture extends CakeTestFixture {
+
+/**
+ * Name property
+ *
+ * @var string
+ */
+	var $name = 'ImportFixture';
+
+/**
+ * Import property
+ *
+ * @var mixed
+ */
+	var $import = array('table' => 'fixture_tests', 'connection' => 'test_suite');
+}
+
+/**
+ * CakeTestFixtureDefaultImportFixture class
+ *
+ * @package       cake
+ * @subpackage    cake.cake.tests.cases.libs
+ */
+class CakeTestFixtureDefaultImportFixture extends CakeTestFixture {
+
+/**
+ * Name property
+ *
+ * @var string
+ */
+	var $name = 'ImportFixture';
+}
+
+/**
+ * FixtureImportTestModel class
+ *
+ * @package       default
+ * @subpackage    cake.cake.tests.cases.libs.
+ */
+class FixtureImportTestModel extends Model {
+	var $name = 'FixtureImport';
+	var $useTable = 'fixture_tests';
+	var $useDbConfig = 'test_suite';
+}
+
+class FixturePrefixTest extends Model {
+	var $name = 'FixturePrefix';
+	var $useTable = '_tests';
+	var $tablePrefix = 'fixture';
+	var $useDbConfig = 'test_suite';
+}
+
+Mock::generate('DboSource', 'BaseFixtureMockDboSource');
+
+class FixtureMockDboSource extends BaseFixtureMockDboSource {
+	var $insertMulti;
+
+	function value($string) {
+		return is_string($string) ? '\'' . $string . '\'' : $string;
+	}
+
+	function insertMulti($table, $fields, $values) {
+		$this->insertMulti = compact('table', 'fields', 'values');
+		return true;
+	}
+}
+
+/**
+ * Test case for CakeTestFixture
+ *
+ * @package       cake
+ * @subpackage    cake.cake.tests.cases.libs
+ */
+class CakeTestFixtureTest extends CakeTestCase {
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		$this->criticDb =& new FixtureMockDboSource();
+		$this->criticDb->fullDebug = true;
+	}
+
+/**
+ * tearDown
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		unset($this->criticDb);
+	}
+
+/**
+ * testInit
+ *
+ * @access public
+ * @return void
+ */
+	function testInit() {
+		$Fixture =& new CakeTestFixtureTestFixture();
+		unset($Fixture->table);
+		$Fixture->init();
+		$this->assertEqual($Fixture->table, 'fixture_tests');
+		$this->assertEqual($Fixture->primaryKey, 'id');
+
+		$Fixture =& new CakeTestFixtureTestFixture();
+		$Fixture->primaryKey = 'my_random_key';
+		$Fixture->init();
+		$this->assertEqual($Fixture->primaryKey, 'my_random_key');
+	}
+
+/**
+ * test that init() correctly sets the fixture table when the connection or model have prefixes defined.
+ *
+ * @return void
+ */
+	function testInitDbPrefix() {
+		$this->_initDb();
+		$Source =& new CakeTestFixtureTestFixture();
+		$Source->create($this->db);
+		$Source->insert($this->db);
+
+		$Fixture =& new CakeTestFixtureImportFixture();
+		$expected = array('id', 'name', 'created');
+		$this->assertEqual(array_keys($Fixture->fields), $expected);
+
+		$db =& ConnectionManager::getDataSource('test_suite');
+		$config = $db->config;
+		$config['prefix'] = 'fixture_test_suite_';
+		ConnectionManager::create('fixture_test_suite', $config);
+
+		$Fixture->fields = $Fixture->records = null;
+		$Fixture->import = array('table' => 'fixture_tests', 'connection' => 'test_suite', 'records' => true);
+		$Fixture->init();
+		$this->assertEqual(count($Fixture->records), count($Source->records));
+
+		$Fixture =& new CakeTestFixtureImportFixture();
+		$Fixture->fields = $Fixture->records = $Fixture->table = null;
+		$Fixture->import = array('model' => 'FixtureImportTestModel', 'connection' => 'test_suite');
+		$Fixture->init();
+		$this->assertEqual(array_keys($Fixture->fields), array('id', 'name', 'created'));
+		$this->assertEqual($Fixture->table, 'fixture_tests');
+
+		$keys = array_flip(ClassRegistry::keys());
+		$this->assertFalse(array_key_exists('fixtureimporttestmodel', $keys));
+
+		$Source->drop($this->db);
+	}
+
+/**
+ * test that fixtures don't duplicate the test db prefix.
+ *
+ * @return void
+ */
+	function testInitDbPrefixDuplication() {
+		$this->_initDb();
+		$backPrefix = $this->db->config['prefix'];
+		$this->db->config['prefix'] = 'cake_fixture_test_';
+
+		$Source =& new CakeTestFixtureTestFixture();
+		$Source->create($this->db);
+		$Source->insert($this->db);
+
+		$Fixture =& new CakeTestFixtureImportFixture();
+		$Fixture->fields = $Fixture->records = $Fixture->table = null;
+		$Fixture->import = array('model' => 'FixtureImportTestModel', 'connection' => 'test_suite');
+
+		$Fixture->init();
+		$this->assertEqual(array_keys($Fixture->fields), array('id', 'name', 'created'));
+		$this->assertEqual($Fixture->table, 'fixture_tests');
+
+		$Source->drop($this->db);
+		$this->db->config['prefix'] = $backPrefix;
+	}
+
+/**
+ * test init with a model that has a tablePrefix declared.
+ *
+ * @return void
+ */
+	function testInitModelTablePrefix() {
+		$this->_initDb();
+		$hasPrefix = !empty($this->db->config['prefix']);
+		if ($this->skipIf($hasPrefix, 'Cannot run this test, you have a database connection prefix.')) {
+			return;
+		}
+		$Source =& new CakeTestFixtureTestFixture();
+		$Source->create($this->db);
+		$Source->insert($this->db);
+
+		$Fixture =& new CakeTestFixtureImportFixture();
+		unset($Fixture->table);
+		$Fixture->fields = $Fixture->records = null;
+		$Fixture->import = array('model' => 'FixturePrefixTest', 'connection' => 'test_suite', 'records' => false);
+		$Fixture->init();
+		$this->assertEqual($Fixture->table, 'fixture_tests');
+
+		$keys = array_flip(ClassRegistry::keys());
+		$this->assertFalse(array_key_exists('fixtureimporttestmodel', $keys));
+
+		$Source->drop($this->db);
+	}
+
+/**
+ * testImport
+ *
+ * @access public
+ * @return void
+ */
+	function testImport() {
+		$this->_initDb();
+
+		$defaultDb =& ConnectionManager::getDataSource('default');
+		$testSuiteDb =& ConnectionManager::getDataSource('test_suite');
+		$defaultConfig = $defaultDb->config;
+		$testSuiteConfig = $testSuiteDb->config;
+		ConnectionManager::create('new_test_suite', array_merge($testSuiteConfig, array('prefix' => 'new_' . $testSuiteConfig['prefix'])));
+		$newTestSuiteDb =& ConnectionManager::getDataSource('new_test_suite');
+
+		$Source =& new CakeTestFixtureTestFixture();
+		$Source->create($newTestSuiteDb);
+		$Source->insert($newTestSuiteDb);
+
+		$defaultDb->config = $newTestSuiteDb->config;
+
+		$Fixture =& new CakeTestFixtureDefaultImportFixture();
+		$Fixture->fields = $Fixture->records = null;
+		$Fixture->import = array('model' => 'FixtureImportTestModel', 'connection' => 'new_test_suite');
+		$Fixture->init();
+		$this->assertEqual(array_keys($Fixture->fields), array('id', 'name', 'created'));
+
+		$defaultDb->config = $defaultConfig;
+
+		$keys = array_flip(ClassRegistry::keys());
+		$this->assertFalse(array_key_exists('fixtureimporttestmodel', $keys));
+
+		$Source->drop($newTestSuiteDb);
+	}
+
+/**
+ * test that importing with records works.  Make sure to try with postgres as its
+ * handling of aliases is a workaround at best.
+ *
+ * @return void
+ */
+	function testImportWithRecords() {
+		$this->_initDb();
+
+		$defaultDb =& ConnectionManager::getDataSource('default');
+		$testSuiteDb =& ConnectionManager::getDataSource('test_suite');
+		$defaultConfig = $defaultDb->config;
+		$testSuiteConfig = $testSuiteDb->config;
+		ConnectionManager::create('new_test_suite', array_merge($testSuiteConfig, array('prefix' => 'new_' . $testSuiteConfig['prefix'])));
+		$newTestSuiteDb =& ConnectionManager::getDataSource('new_test_suite');
+
+		$Source =& new CakeTestFixtureTestFixture();
+		$Source->create($newTestSuiteDb);
+		$Source->insert($newTestSuiteDb);
+
+		$defaultDb->config = $newTestSuiteDb->config;
+
+		$Fixture =& new CakeTestFixtureDefaultImportFixture();
+		$Fixture->fields = $Fixture->records = null;
+		$Fixture->import = array(
+			'model' => 'FixtureImportTestModel', 'connection' => 'new_test_suite', 'records' => true
+		);
+		$Fixture->init();
+		$this->assertEqual(array_keys($Fixture->fields), array('id', 'name', 'created'));
+		$this->assertFalse(empty($Fixture->records[0]), 'No records loaded on importing fixture.');
+		$this->assertTrue(isset($Fixture->records[0]['name']), 'No name loaded for first record');
+
+		$defaultDb->config = $defaultConfig;
+
+		$Source->drop($newTestSuiteDb);
+	}
+
+/**
+ * test create method
+ *
+ * @access public
+ * @return void
+ */
+	function testCreate() {
+		$Fixture =& new CakeTestFixtureTestFixture();
+		$this->criticDb->expectAtLeastOnce('execute');
+		$this->criticDb->expectAtLeastOnce('createSchema');
+		$return = $Fixture->create($this->criticDb);
+		$this->assertTrue($this->criticDb->fullDebug);
+		$this->assertTrue($return);
+
+		unset($Fixture->fields);
+		$return = $Fixture->create($this->criticDb);
+		$this->assertFalse($return);
+	}
+
+/**
+ * test the insert method
+ *
+ * @access public
+ * @return void
+ */
+	function testInsert() {
+		$Fixture =& new CakeTestFixtureTestFixture();
+
+		$this->criticDb->insertMulti = array();
+		$return = $Fixture->insert($this->criticDb);
+		$this->assertTrue(!empty($this->criticDb->insertMulti));
+		$this->assertTrue($this->criticDb->fullDebug);
+		$this->assertTrue($return);
+		$this->assertEqual('fixture_tests', $this->criticDb->insertMulti['table']);
+		$this->assertEqual(array('name', 'created'), $this->criticDb->insertMulti['fields']);
+		$expected = array(
+			'(\'Gandalf\', \'2009-04-28 19:20:00\')',
+			'(\'Captain Picard\', \'2009-04-28 19:20:00\')',
+			'(\'Chewbacca\', \'2009-04-28 19:20:00\')'
+		);
+		$this->assertEqual($expected, $this->criticDb->insertMulti['values']);
+	}
+
+/**
+ * test the insert method
+ *
+ * @access public
+ * @return void
+ */
+	function testInsertStrings() {
+		$Fixture =& new StringsTestFixture();
+
+		$this->criticDb->insertMulti = array();
+		$return = $Fixture->insert($this->criticDb);
+		$this->assertTrue(!empty($this->criticDb->insertMulti));
+		$this->assertTrue($this->criticDb->fullDebug);
+		$this->assertTrue($return);
+		$this->assertEqual('strings', $this->criticDb->insertMulti['table']);
+		$this->assertEqual(array('name', 'email', 'age'), $this->criticDb->insertMulti['fields']);
+		$expected = array(
+			'(\'John Doe\', \'john.****@email*****\', 20)',
+			'(\'Jane Doe\', \'jane.****@email*****\', 30)',
+			'(\'Mark Doe\', \'mark.****@email*****\', NULL)',
+		);
+		$this->assertEqual($expected, $this->criticDb->insertMulti['values']);
+	}
+
+/**
+ * Test the drop method
+ *
+ * @access public
+ * @return void
+ */
+	function testDrop() {
+		$Fixture =& new CakeTestFixtureTestFixture();
+		$this->criticDb->setReturnValueAt(0, 'execute', true);
+		$this->criticDb->expectAtLeastOnce('execute');
+		$this->criticDb->expectAtLeastOnce('dropSchema');
+
+		$return = $Fixture->drop($this->criticDb);
+		$this->assertTrue($this->criticDb->fullDebug);
+		$this->assertTrue($return);
+
+		$this->criticDb->setReturnValueAt(1, 'execute', false);
+		$return = $Fixture->drop($this->criticDb);
+		$this->assertFalse($return);
+
+		unset($Fixture->fields);
+		$return = $Fixture->drop($this->criticDb);
+		$this->assertFalse($return);
+	}
+
+/**
+ * Test the truncate method.
+ *
+ * @access public
+ * @return void
+ */
+	function testTruncate() {
+		$Fixture =& new CakeTestFixtureTestFixture();
+		$this->criticDb->expectAtLeastOnce('truncate');
+		$Fixture->truncate($this->criticDb);
+		$this->assertTrue($this->criticDb->fullDebug);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/class_registry.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/class_registry.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/class_registry.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,325 @@
+<?php
+/**
+ * ClassRegistryTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.5432
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', 'ClassRegistry');
+
+/**
+ * ClassRegisterModel class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class ClassRegisterModel extends CakeTestModel {
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+}
+
+/**
+ * RegisterArticle class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class RegisterArticle extends ClassRegisterModel {
+
+/**
+ * name property
+ *
+ * @var string 'RegisterArticle'
+ * @access public
+ */
+	var $name = 'RegisterArticle';
+}
+
+/**
+ * RegisterArticleFeatured class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class RegisterArticleFeatured extends ClassRegisterModel {
+
+/**
+ * name property
+ *
+ * @var string 'RegisterArticleFeatured'
+ * @access public
+ */
+	var $name = 'RegisterArticleFeatured';
+}
+
+/**
+ * RegisterArticleTag class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class RegisterArticleTag extends ClassRegisterModel {
+
+/**
+ * name property
+ *
+ * @var string 'RegisterArticleTag'
+ * @access public
+ */
+	var $name = 'RegisterArticleTag';
+}
+
+/**
+ * RegistryPluginAppModel class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class RegistryPluginAppModel extends ClassRegisterModel {
+
+/**
+ * tablePrefix property
+ *
+ * @var string 'something_'
+ * @access public
+ */
+	var $tablePrefix = 'something_';
+}
+
+/**
+ * TestRegistryPluginModel class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class TestRegistryPluginModel extends RegistryPluginAppModel {
+
+/**
+ * name property
+ *
+ * @var string 'TestRegistryPluginModel'
+ * @access public
+ */
+	var $name = 'TestRegistryPluginModel';
+}
+
+/**
+ * RegisterCategory class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class RegisterCategory extends ClassRegisterModel {
+
+/**
+ * name property
+ *
+ * @var string 'RegisterCategory'
+ * @access public
+ */
+	var $name = 'RegisterCategory';
+}
+
+/**
+ * ClassRegistryTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class ClassRegistryTest extends CakeTestCase {
+
+/**
+ * testAddModel method
+ *
+ * @access public
+ * @return void
+ */
+	function testAddModel() {
+		if (PHP5) {
+			$Tag = ClassRegistry::init('RegisterArticleTag');
+		} else {
+			$Tag =& ClassRegistry::init('RegisterArticleTag');
+		}
+		$this->assertTrue(is_a($Tag, 'RegisterArticleTag'));
+
+		$TagCopy = ClassRegistry::isKeySet('RegisterArticleTag');
+		$this->assertTrue($TagCopy);
+
+		$Tag->name = 'SomeNewName';
+
+		if (PHP5) {
+			$TagCopy = ClassRegistry::getObject('RegisterArticleTag');
+		} else {
+			$TagCopy =& ClassRegistry::getObject('RegisterArticleTag');
+		}
+
+		$this->assertTrue(is_a($TagCopy, 'RegisterArticleTag'));
+		$this->assertIdentical($Tag, $TagCopy);
+
+		if (PHP5) {
+			$NewTag = ClassRegistry::init(array('class' => 'RegisterArticleTag', 'alias' => 'NewTag'));
+		} else {
+			$NewTag =& ClassRegistry::init(array('class' => 'RegisterArticleTag', 'alias' => 'NewTag'));
+		}
+		$this->assertTrue(is_a($Tag, 'RegisterArticleTag'));
+
+		if (PHP5) {
+			$NewTagCopy = ClassRegistry::init(array('class' => 'RegisterArticleTag', 'alias' => 'NewTag'));
+		} else {
+			$NewTagCopy =& ClassRegistry::init(array('class' => 'RegisterArticleTag', 'alias' => 'NewTag'));
+		}
+
+		$this->assertNotIdentical($Tag, $NewTag);
+		$this->assertIdentical($NewTag, $NewTagCopy);
+
+		$NewTag->name = 'SomeOtherName';
+		$this->assertNotIdentical($Tag, $NewTag);
+		$this->assertIdentical($NewTag, $NewTagCopy);
+
+		$Tag->name = 'SomeOtherName';
+		$this->assertNotIdentical($Tag, $NewTag);
+
+		$this->assertTrue($TagCopy->name === 'SomeOtherName');
+
+		if (PHP5) {
+			$User = ClassRegistry::init(array('class' => 'RegisterUser', 'alias' => 'User', 'table' => false));
+		} else {
+			$User =& ClassRegistry::init(array('class' => 'RegisterUser', 'alias' => 'User', 'table' => false));
+		}
+		$this->assertTrue(is_a($User, 'AppModel'));
+
+		if (PHP5) {
+			$UserCopy = ClassRegistry::init(array('class' => 'RegisterUser', 'alias' => 'User', 'table' => false));
+		} else {
+			$UserCopy =& ClassRegistry::init(array('class' => 'RegisterUser', 'alias' => 'User', 'table' => false));
+		}
+		$this->assertTrue(is_a($UserCopy, 'AppModel'));
+		$this->assertIdentical($User, $UserCopy);
+
+		if (PHP5) {
+			$Category = ClassRegistry::init(array('class' => 'RegisterCategory'));
+		} else {
+			$Category =& ClassRegistry::init(array('class' => 'RegisterCategory'));
+		}
+		$this->assertTrue(is_a($Category, 'RegisterCategory'));
+
+		if (PHP5) {
+			$ParentCategory = ClassRegistry::init(array('class' => 'RegisterCategory', 'alias' => 'ParentCategory'));
+		} else {
+			$ParentCategory =& ClassRegistry::init(array('class' => 'RegisterCategory', 'alias' => 'ParentCategory'));
+		}
+		$this->assertTrue(is_a($ParentCategory, 'RegisterCategory'));
+		$this->assertNotIdentical($Category, $ParentCategory);
+
+		$this->assertNotEqual($Category->alias, $ParentCategory->alias);
+		$this->assertEqual('RegisterCategory', $Category->alias);
+		$this->assertEqual('ParentCategory', $ParentCategory->alias);
+	}
+
+/**
+ * testClassRegistryFlush method
+ *
+ * @access public
+ * @return void
+ */
+	function testClassRegistryFlush() {
+		$ArticleTag = ClassRegistry::getObject('RegisterArticleTag');
+		$this->assertTrue(is_a($ArticleTag, 'RegisterArticleTag'));
+		ClassRegistry::flush();
+
+		$NoArticleTag = ClassRegistry::isKeySet('RegisterArticleTag');
+		$this->assertFalse($NoArticleTag);
+		$this->assertTrue(is_a($ArticleTag, 'RegisterArticleTag'));
+	}
+
+/**
+ * testAddMultipleModels method
+ *
+ * @access public
+ * @return void
+ */
+	function testAddMultipleModels() {
+		$Article = ClassRegistry::isKeySet('Article');
+		$this->assertFalse($Article);
+
+		$Featured = ClassRegistry::isKeySet('Featured');
+		$this->assertFalse($Featured);
+
+		$Tag = ClassRegistry::isKeySet('Tag');
+		$this->assertFalse($Tag);
+
+		$models = array(array('class' => 'RegisterArticle', 'alias' => 'Article'),
+				array('class' => 'RegisterArticleFeatured', 'alias' => 'Featured'),
+				array('class' => 'RegisterArticleTag', 'alias' => 'Tag'));
+
+		$added = ClassRegistry::init($models);
+		$this->assertTrue($added);
+
+		$Article = ClassRegistry::isKeySet('Article');
+		$this->assertTrue($Article);
+
+		$Featured = ClassRegistry::isKeySet('Featured');
+		$this->assertTrue($Featured);
+
+		$Tag = ClassRegistry::isKeySet('Tag');
+		$this->assertTrue($Tag);
+
+		$Article = ClassRegistry::getObject('Article');
+		$this->assertTrue(is_a($Article, 'RegisterArticle'));
+
+		$Featured = ClassRegistry::getObject('Featured');
+		$this->assertTrue(is_a($Featured, 'RegisterArticleFeatured'));
+
+		$Tag = ClassRegistry::getObject('Tag');
+		$this->assertTrue(is_a($Tag, 'RegisterArticleTag'));
+	}
+
+/**
+ * testPluginAppModel method
+ *
+ * @access public
+ * @return void
+ */
+	function testPluginAppModel() {
+		$TestRegistryPluginModel = ClassRegistry::isKeySet('TestRegistryPluginModel');
+		$this->assertFalse($TestRegistryPluginModel);
+
+		$TestRegistryPluginModel = ClassRegistry::init('RegistryPlugin.TestRegistryPluginModel');
+		$this->assertTrue(is_a($TestRegistryPluginModel, 'TestRegistryPluginModel'));
+
+		$this->assertEqual($TestRegistryPluginModel->tablePrefix, 'something_');
+
+		if (PHP5) {
+			$PluginUser = ClassRegistry::init(array('class' => 'RegistryPlugin.RegisterUser', 'alias' => 'RegistryPluginUser', 'table' => false));
+		} else {
+			$PluginUser =& ClassRegistry::init(array('class' => 'RegistryPlugin.RegisterUser', 'alias' => 'RegistryPluginUser', 'table' => false));
+		}
+		$this->assertTrue(is_a($PluginUser, 'RegistryPluginAppModel'));
+
+		if (PHP5) {
+			$PluginUserCopy = ClassRegistry::getObject('RegistryPluginUser');
+		} else {
+			$PluginUserCopy =& ClassRegistry::getObject('RegistryPluginUser');
+		}
+		$this->assertTrue(is_a($PluginUserCopy, 'RegistryPluginAppModel'));
+		$this->assertIdentical($PluginUser, $PluginUserCopy);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/code_coverage_manager.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/code_coverage_manager.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/code_coverage_manager.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,516 @@
+<?php
+/**
+ * CodeCoverageManagerTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+require_once CAKE . 'tests' . DS . 'lib' . DS . 'code_coverage_manager.php';
+require_once CAKE . 'tests' . DS . 'lib' . DS . 'reporter' . DS . 'cake_cli_reporter.php';
+
+/**
+ * CodeCoverageManagerTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class CodeCoverageManagerTest extends CakeTestCase {
+
+/**
+ * Skip if XDebug not installed
+ *
+ * @access public
+ */
+	function skip() {
+		$this->skipIf(!extension_loaded('xdebug'), '%s XDebug not installed');
+	}
+
+/**
+ * startTest Method
+ * Store reference of $_GET to restore later.
+ *
+ * @return void
+ */
+	function startCase() {
+		$this->_get = $_GET;
+	}
+
+/**
+ * End Case - restore GET vars.
+ *
+ * @return void
+ */
+	function endCase() {
+		$_GET = $this->_get;
+	}
+
+/**
+ * testNoTestCaseSupplied method
+ *
+ * @access public
+ * @return void
+ */
+	function testNoTestCaseSupplied() {
+		if ($this->skipIf(PHP_SAPI == 'cli', 'Is cli, cannot run this test %s')) {
+			return;
+		}
+		$reporter =& new CakeHtmlReporter(null, array('group' => false, 'app' => false, 'plugin' => false));
+
+		CodeCoverageManager::init(substr(md5(microtime()), 0, 5), $reporter);
+		CodeCoverageManager::report(false);
+		$this->assertError();
+
+		CodeCoverageManager::init('tests' . DS . 'lib' . DS . basename(__FILE__), $reporter);
+		CodeCoverageManager::report(false);
+		$this->assertError();
+	}
+
+/**
+ * Test that test cases don't cause errors
+ *
+ * @return void
+ */
+	function testNoTestCaseSuppliedNoErrors() {
+		if ($this->skipIf(PHP_SAPI == 'cli', 'Is cli, cannot run this test %s')) {
+			return;
+		}
+		$reporter =& new CakeHtmlReporter(null, array('group' => false, 'app' => false, 'plugin' => false));
+		$path = LIBS;
+		if (strpos(LIBS, ROOT) === false) {
+			$path = ROOT.DS.LIBS;
+		}
+		App::import('Core', 'Folder');
+		$folder = new Folder();
+		$folder->cd($path);
+		$contents = $folder->read();
+
+		$contents[1] = array_filter($contents[1], array(&$this, '_basenameFilter'));
+
+		foreach ($contents[1] as $file) {
+			CodeCoverageManager::init('libs' . DS . $file, $reporter);
+			CodeCoverageManager::report(false);
+			$this->assertNoErrors('libs' . DS . $file);
+		}
+	}
+
+/**
+ * Remove file names that don't share a basename with the current file.
+ *
+ * @return void
+ */
+	function _basenameFilter($var) {
+		return ($var != basename(__FILE__));
+	}
+
+/**
+ * testGetTestObjectFileNameFromTestCaseFile method
+ *
+ * @access public
+ * @return void
+ */
+	function testGetTestObjectFileNameFromTestCaseFile() {
+		$manager =& CodeCoverageManager::getInstance();
+		$manager->reporter = new CakeHtmlReporter();
+
+		$expected = $manager->__testObjectFileFromCaseFile('models/some_file.test.php', true);
+		$this->assertIdentical(APP.'models'.DS.'some_file.php', $expected);
+
+		$expected = $manager->__testObjectFileFromCaseFile('models/datasources/some_file.test.php', true);
+		$this->assertIdentical(APP.'models'.DS.'datasources'.DS.'some_file.php', $expected);
+
+		$expected = $manager->__testObjectFileFromCaseFile('controllers/some_file.test.php', true);
+		$this->assertIdentical(APP.'controllers'.DS.'some_file.php', $expected);
+
+		$expected = $manager->__testObjectFileFromCaseFile('views/some_file.test.php', true);
+		$this->assertIdentical(APP.'views'.DS.'some_file.php', $expected);
+
+		$expected = $manager->__testObjectFileFromCaseFile('behaviors/some_file.test.php', true);
+		$this->assertIdentical(APP.'models'.DS.'behaviors'.DS.'some_file.php', $expected);
+
+		$expected = $manager->__testObjectFileFromCaseFile('components/some_file.test.php', true);
+		$this->assertIdentical(APP.'controllers'.DS.'components'.DS.'some_file.php', $expected);
+
+		$expected = $manager->__testObjectFileFromCaseFile('helpers/some_file.test.php', true);
+		$this->assertIdentical(APP.'views'.DS.'helpers'.DS.'some_file.php', $expected);
+
+		$manager->pluginTest = 'bugs';
+		$expected = $manager->__testObjectFileFromCaseFile('models/some_file.test.php', false);
+		$this->assertIdentical(APP.'plugins'.DS.'bugs'.DS.'models'.DS.'some_file.php', $expected);
+
+		$manager->pluginTest = false;
+		$manager->reporter = new CakeCliReporter;
+		$expected = $manager->__testObjectFileFromCaseFile('libs/set.test.php', false);
+		$this->assertIdentical(ROOT.DS.'cake'.DS.'libs'.DS.'set.php', $expected);
+	}
+
+/**
+ * testOfHtmlDiffReport method
+ *
+ * @access public
+ * @return void
+ */
+	function testOfHtmlDiffReport() {
+		$manager =& CodeCoverageManager::getInstance();
+		$code = <<<PHP
+/**
+ * Set class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+		class Set extends Object {
+
+/**
+		 * Value of the Set object.
+		 *
+		 * @var array
+		 * @access public
+		 */
+			var \$value = array();
+
+/**
+		 * Constructor. Defaults to an empty array.
+		 *
+		 * @access public
+		 */
+			function __construct() {
+				if (func_num_args() == 1 && is_array(func_get_arg(0))) {
+					\$this->value = func_get_arg(0);
+				} else {
+					\$this->value = func_get_args();
+				}
+			}
+
+/**
+		 * Returns the contents of the Set object
+		 *
+		 * @return array
+		 * @access public
+		 */
+			function &get() {
+				return \$this->value;
+			}
+
+/**
+		 * 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
+		 */
+			function merge(\$arr1, \$arr2 = null) {
+				\$args = func_get_args();
+
+				if (isset(\$this) && is_a(\$this, 'set')) {
+					\$backtrace = debug_backtrace();
+					\$previousCall = strtolower(\$backtrace[1]['class'].'::'.\$backtrace[1]['function']);
+					if (\$previousCall != 'set::merge') {
+						\$r =& \$this->value;
+						array_unshift(\$args, null);
+					}
+				}
+				if (!isset(\$r)) {
+					\$r = (array)current(\$args);
+				}
+
+				while ((\$arg = next(\$args)) !== false) {
+					if (is_a(\$arg, 'set')) {
+						\$arg = \$arg->get();
+					}
+
+					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)) {
+
+						} else {
+							\$r[\$key] = \$val;
+						}
+					}
+				}
+				return \$r;
+			}
+PHP;
+
+		$testObjectFile = explode("\n", $code);
+		$coverageData = array(
+			0 => 1,
+			1 => 1,
+			2 => -2,
+			3 => -2,
+			4 => -2,
+			5 => -2,
+			6 => -2,
+			7 => -2,
+			8 => -1,
+			9 => -2,
+			10 => -2,
+			11 => -2,
+			12 => -2,
+			13 => -2,
+			14 => 1,
+			15 => 1,
+			16 => -1,
+			17 => 1,
+			18 => 1,
+			19 => -1,
+			20 => 1,
+			21 => -2,
+			22 => -2,
+			23 => -2,
+			24 => -2,
+			25 => -2,
+			26 => -2,
+			27 => 1,
+			28 => -1,
+			29 => 1,
+			30 => 1,
+			31 => -2,
+			32 => -2,
+			33 => -2,
+			34 => -2,
+			35 => -2,
+			36 => -2,
+			37 => -2,
+			38 => -2,
+			39 => -2,
+			40 => -2,
+			41 => -2,
+			42 => -2,
+			43 => -1,
+			44 => -2,
+			45 => -2,
+			46 => -2,
+			47 => -2,
+			48 => 1,
+			49 => 1,
+			50 => -1,
+			51 => 1,
+			52 => 1,
+			53 => -2,
+			54 => -2,
+			55 => 1,
+			56 => 1,
+			57 => 1,
+			58 => 1,
+			59 => -1,
+			60 => 1,
+			61 => 1,
+			62 => -2,
+			63 => -2,
+			64 => 1,
+			65 => -2,
+			66 => 1,
+			67 => -1,
+			68 => -2,
+			69 => -1,
+			70 => -1,
+			71 => 1,
+			72 => -2,
+		);
+		$expected = array(
+			0 => 'ignored',
+			1 => 'ignored',
+			2 => 'ignored',
+			3 => 'ignored',
+			4 => 'ignored',
+			5 => 'ignored show start realstart',
+			6 => 'ignored show',
+			7 => 'ignored show',
+			8 => 'uncovered show',
+			9 => 'ignored show',
+			10 => 'ignored show',
+			11 => 'ignored show end',
+			12 => 'ignored',
+			13 => 'ignored show start',
+			14 => 'covered show',
+			15 => 'covered show',
+			16 => 'uncovered show',
+			17 => 'covered show show',
+			18 => 'covered show show',
+			19 => 'uncovered show',
+			20 => 'covered show',
+			21 => 'ignored show',
+			22 => 'ignored show end',
+			23 => 'ignored',
+			24 => 'ignored',
+			25 => 'ignored show start',
+			26 => 'ignored show',
+			27 => 'covered show',
+			28 => 'uncovered show',
+			29 => 'covered show',
+			30 => 'covered show',
+			31 => 'ignored show end',
+			32 => 'ignored',
+			33 => 'ignored',
+			34 => 'ignored',
+			35 => 'ignored',
+			36 => 'ignored',
+			37 => 'ignored',
+			38 => 'ignored',
+			39 => 'ignored',
+			40 => 'ignored show start',
+			41 => 'ignored show',
+			42 => 'ignored show',
+			43 => 'uncovered show',
+			41 => 'ignored show',
+			42 => 'ignored show',
+			43 => 'uncovered show',
+			44 => 'ignored show',
+			45 => 'ignored show',
+			46 => 'ignored show',
+			47 => 'ignored show',
+			48 => 'covered show',
+			49 => 'covered show',
+			50 => 'uncovered show',
+			51 => 'covered show',
+			52 => 'covered show',
+			53 => 'ignored show end',
+			54 => 'ignored',
+			55 => 'covered',
+			56 => 'covered show start',
+			57 => 'covered show',
+			58 => 'covered show',
+			59 => 'uncovered show',
+			60 => 'covered show',
+			61 => 'covered show',
+			62 => 'ignored show end',
+			63 => 'ignored',
+			64 => 'covered show start',
+			65 => 'ignored show',
+			66 => 'covered show show',
+			67 => 'uncovered show',
+			68 => 'ignored show',
+			69 => 'uncovered show',
+			70 => 'uncovered show',
+			71 => 'covered show',
+			72 => 'ignored show',
+			73 => 'ignored show end end',
+		);
+		$execCodeLines = range(0, 72);
+		$result = explode("</div>", $report = $manager->reportCaseHtmlDiff($testObjectFile, $coverageData, $execCodeLines, 3));
+
+		foreach ($result as $line) {
+			preg_match('/<span class="line-num">(.*?)<\/span>/', $line, $matches);
+			if (!isset($matches[1])) {
+				continue;
+			}
+
+			$num = $matches[1];
+			$class = $expected[$num];
+			$pattern = '/<div class="code-line '.$class.'">/';
+			$this->assertPattern($pattern, $line, $num.': '.$line." fails");
+		}
+	}
+
+/**
+ * testArrayStrrpos method
+ *
+ * @access public
+ * @return void
+ */
+	function testArrayStrrpos() {
+		$manager =& CodeCoverageManager::getInstance();
+
+		$a = array(
+			'apples',
+			'bananas',
+			'oranges'
+		);
+		$this->assertEqual(1, $manager->__array_strpos($a, 'ba', true));
+		$this->assertEqual(2, $manager->__array_strpos($a, 'range', true));
+		$this->assertEqual(0, $manager->__array_strpos($a, 'pp', true));
+		$this->assertFalse($manager->__array_strpos('', 'ba', true));
+		$this->assertFalse($manager->__array_strpos(false, 'ba', true));
+		$this->assertFalse($manager->__array_strpos(array(), 'ba', true));
+
+		$a = array(
+			'rang',
+			'orange',
+			'oranges'
+		);
+		$this->assertEqual(0, $manager->__array_strpos($a, 'rang'));
+		$this->assertEqual(2, $manager->__array_strpos($a, 'rang', true));
+		$this->assertEqual(1, $manager->__array_strpos($a, 'orange', false));
+		$this->assertEqual(1, $manager->__array_strpos($a, 'orange'));
+		$this->assertEqual(2, $manager->__array_strpos($a, 'orange', true));
+	}
+
+/**
+ * testGetExecutableLines method
+ *
+ * @access public
+ * @return void
+ */
+	function testGetExecutableLines() {
+		$manager =& CodeCoverageManager::getInstance();
+		$code = <<<HTML
+			\$manager =& CodeCoverageManager::getInstance();
+HTML;
+		$result = $manager->__getExecutableLines($code);
+		foreach ($result as $line) {
+			$this->assertNotIdentical($line, '');
+		}
+
+		$code = <<<HTML
+		{
+		}
+		<?php?>
+		?>
+		<?
+}
+{{}}
+(())
+		@codeCoverageIgnoreStart
+		some
+		more
+		code
+		here
+		@codeCoverageIgnoreEnd
+HTML;
+		$result = $manager->__getExecutableLines($code);
+		foreach ($result as $line) {
+			$this->assertIdentical(trim($line), '');
+		}
+	}
+
+/**
+ * testCalculateCodeCoverage method
+ *
+ * @access public
+ * @return void
+ */
+	function testCalculateCodeCoverage() {
+		$manager =& CodeCoverageManager::getInstance();
+		$data = array(
+			'25' => array(100, 25),
+			'50' => array(100, 50),
+			'0' => array(0, 0),
+			'0' => array(100, 0),
+			'100' => array(100, 100),
+		);
+		foreach ($data as $coverage => $lines) {
+			$this->assertEqual($coverage, $manager->__calcCoverage($lines[0], $lines[1]));
+		}
+
+		$manager->__calcCoverage(100, 1000);
+		$this->assertError();
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/configure.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/configure.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/configure.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,833 @@
+<?php
+/**
+ * ConfigureTest file
+ *
+ * Holds several tests
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.5432
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', 'Configure');
+
+/**
+ * ConfigureTest
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class ConfigureTest extends CakeTestCase {
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		$this->_cacheDisable = Configure::read('Cache.disable');
+		$this->_debug = Configure::read('debug');
+
+		Configure::write('Cache.disable', true);
+	}
+
+/**
+ * endTest
+ *
+ * @access public
+ * @return void
+ */
+	function endTest() {
+		App::build();
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		if (file_exists(TMP . 'cache' . DS . 'persistent' . DS . 'cake_core_core_paths')) {
+			unlink(TMP . 'cache' . DS . 'persistent' . DS . 'cake_core_core_paths');
+		}
+		if (file_exists(TMP . 'cache' . DS . 'persistent' . DS . 'cake_core_dir_map')) {
+			unlink(TMP . 'cache' . DS . 'persistent' . DS . 'cake_core_dir_map');
+		}
+		if (file_exists(TMP . 'cache' . DS . 'persistent' . DS . 'cake_core_file_map')) {
+			unlink(TMP . 'cache' . DS . 'persistent' . DS . 'cake_core_file_map');
+		}
+		if (file_exists(TMP . 'cache' . DS . 'persistent' . DS . 'cake_core_object_map')) {
+			unlink(TMP . 'cache' . DS . 'persistent' . DS . 'cake_core_object_map');
+		}
+		if (file_exists(TMP . 'cache' . DS . 'persistent' . DS . 'test.config.php')) {
+			unlink(TMP . 'cache' . DS . 'persistent' . DS . 'test.config.php');
+		}
+		if (file_exists(TMP . 'cache' . DS . 'persistent' . DS . 'test.php')) {
+			unlink(TMP . 'cache' . DS . 'persistent' . DS . 'test.php');
+		}
+		Configure::write('debug', $this->_debug);
+		Configure::write('Cache.disable', $this->_cacheDisable);
+	}
+
+/**
+ * testRead method
+ *
+ * @access public
+ * @return void
+ */
+	function testRead() {
+		$expected = 'ok';
+		Configure::write('level1.level2.level3_1', $expected);
+		Configure::write('level1.level2.level3_2', 'something_else');
+		$result = Configure::read('level1.level2.level3_1');
+		$this->assertEqual($expected, $result);
+
+		$result = Configure::read('level1.level2.level3_2');
+		$this->assertEqual($result, 'something_else');
+
+		$result = Configure::read('debug');
+		$this->assertTrue($result >= 0);
+	}
+
+/**
+ * testWrite method
+ *
+ * @access public
+ * @return void
+ */
+	function testWrite() {
+		$writeResult = Configure::write('SomeName.someKey', 'myvalue');
+		$this->assertTrue($writeResult);
+		$result = Configure::read('SomeName.someKey');
+		$this->assertEqual($result, 'myvalue');
+
+		$writeResult = Configure::write('SomeName.someKey', null);
+		$this->assertTrue($writeResult);
+		$result = Configure::read('SomeName.someKey');
+		$this->assertEqual($result, null);
+
+		$expected = array('One' => array('Two' => array('Three' => array('Four' => array('Five' => 'cool')))));
+		$writeResult = Configure::write('Key', $expected);
+		$this->assertTrue($writeResult);
+
+		$result = Configure::read('Key');
+		$this->assertEqual($expected, $result);
+
+		$result = Configure::read('Key.One');
+		$this->assertEqual($expected['One'], $result);
+
+		$result = Configure::read('Key.One.Two');
+		$this->assertEqual($expected['One']['Two'], $result);
+
+		$result = Configure::read('Key.One.Two.Three.Four.Five');
+		$this->assertEqual('cool', $result);
+	}
+
+/**
+ * testSetErrorReporting Level
+ *
+ * @return void
+ */
+	function testSetErrorReportingLevel() {
+		Configure::write('log', false);
+
+		Configure::write('debug', 0);
+		$result = ini_get('error_reporting');
+		$this->assertEqual($result, 0);
+
+		Configure::write('debug', 2);
+		$result = ini_get('error_reporting');
+		$this->assertEqual($result, E_ALL & ~E_DEPRECATED & ~E_STRICT);
+
+		$result = ini_get('display_errors');
+		$this->assertEqual($result, 1);
+
+		Configure::write('debug', 0);
+		$result = ini_get('error_reporting');
+		$this->assertEqual($result, 0);
+	}
+
+/**
+ * test that log and debug configure values interact well.
+ *
+ * @return void
+ */
+	function testInteractionOfDebugAndLog() {
+		Configure::write('log', false);
+
+		Configure::write('debug', 0);
+		$this->assertEqual(ini_get('error_reporting'), 0);
+		$this->assertEqual(ini_get('display_errors'), 0);
+
+		Configure::write('log', E_WARNING);
+		Configure::write('debug', 0);
+		$this->assertEqual(ini_get('error_reporting'), E_WARNING);
+		$this->assertEqual(ini_get('display_errors'), 0);
+
+		Configure::write('debug', 2);
+		$this->assertEqual(ini_get('error_reporting'), E_ALL & ~E_DEPRECATED & ~E_STRICT);
+		$this->assertEqual(ini_get('display_errors'), 1);
+
+		Configure::write('debug', 0);
+		Configure::write('log', false);
+		$this->assertEqual(ini_get('error_reporting'), 0);
+		$this->assertEqual(ini_get('display_errors'), 0);
+	}
+
+/**
+ * testDelete method
+ *
+ * @access public
+ * @return void
+ */
+	function testDelete() {
+		Configure::write('SomeName.someKey', 'myvalue');
+		$result = Configure::read('SomeName.someKey');
+		$this->assertEqual($result, 'myvalue');
+
+		Configure::delete('SomeName.someKey');
+		$result = Configure::read('SomeName.someKey');
+		$this->assertTrue($result === null);
+
+		Configure::write('SomeName', array('someKey' => 'myvalue', 'otherKey' => 'otherValue'));
+
+		$result = Configure::read('SomeName.someKey');
+		$this->assertEqual($result, 'myvalue');
+
+		$result = Configure::read('SomeName.otherKey');
+		$this->assertEqual($result, 'otherValue');
+
+		Configure::delete('SomeName');
+
+		$result = Configure::read('SomeName.someKey');
+		$this->assertTrue($result === null);
+
+		$result = Configure::read('SomeName.otherKey');
+		$this->assertTrue($result === null);
+	}
+
+/**
+ * testLoad method
+ *
+ * @access public
+ * @return void
+ */
+	function testLoad() {
+		$result = Configure::load('non_existing_configuration_file');
+		$this->assertFalse($result);
+
+		$result = Configure::load('config');
+		$this->assertTrue($result);
+
+		$result = Configure::load('../../index');
+		$this->assertFalse($result);
+	}
+
+/**
+ * testLoad method
+ *
+ * @access public
+ * @return void
+ */
+	function testLoadPlugin() {
+		App::build(array('plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)), true);
+		$result = Configure::load('test_plugin.load');
+		$this->assertTrue($result);
+		$expected = '/test_app/plugins/test_plugin/config/load.php';
+		$config = Configure::read('plugin_load');
+		$this->assertEqual($config, $expected);
+
+		$result = Configure::load('test_plugin.more.load');
+		$this->assertTrue($result);
+		$expected = '/test_app/plugins/test_plugin/config/more.load.php';
+		$config = Configure::read('plugin_more_load');
+		$this->assertEqual($config, $expected);
+	}
+
+/**
+ * testStore method
+ *
+ * @access public
+ * @return void
+ */
+	function testStoreAndLoad() {
+		Configure::write('Cache.disable', false);
+
+		$expected = array('data' => 'value with backslash \, \'singlequote\' and "doublequotes"');
+		Configure::store('SomeExample', 'test', $expected);
+
+		Configure::load('test');
+		$config = Configure::read('SomeExample');
+		$this->assertEqual($config, $expected);
+
+		$expected = array(
+			'data' => array('first' => 'value with backslash \, \'singlequote\' and "doublequotes"', 'second' => 'value2'),
+			'data2' => 'value'
+		);
+		Configure::store('AnotherExample', 'test_config', $expected);
+
+		Configure::load('test_config');
+		$config = Configure::read('AnotherExample');
+		$this->assertEqual($config, $expected);
+	}
+
+/**
+ * testVersion method
+ *
+ * @access public
+ * @return void
+ */
+	function testVersion() {
+		$result = Configure::version();
+		$this->assertTrue(version_compare($result, '1.2', '>='));
+	}
+}
+
+/**
+ * AppImportTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class AppImportTest extends CakeTestCase {
+
+/**
+ * testBuild method
+ *
+ * @access public
+ * @return void
+ */
+	function testBuild() {
+		$old = App::path('models');
+		$expected = array(
+			APP . 'models' . DS,
+			APP,
+			ROOT . DS . LIBS . 'model' . DS
+		);
+		$this->assertEqual($expected, $old);
+
+		App::build(array('models' => array('/path/to/models/')));
+
+		$new = App::path('models');
+
+		$expected = array(
+			'/path/to/models/',
+			APP . 'models' . DS,
+			APP,
+			ROOT . DS . LIBS . 'model' . DS
+		);
+		$this->assertEqual($expected, $new);
+
+		App::build(); //reset defaults
+		$defaults = App::path('models');
+		$this->assertEqual($old, $defaults);
+	}
+
+/**
+ * testBuildWithReset method
+ *
+ * @access public
+ * @return void
+ */
+	function testBuildWithReset() {
+		$old = App::path('models');
+		$expected = array(
+			APP . 'models' . DS,
+			APP,
+			ROOT . DS . LIBS . 'model' . DS
+		);
+		$this->assertEqual($expected, $old);
+
+		App::build(array('models' => array('/path/to/models/')), true);
+
+		$new = App::path('models');
+
+		$expected = array(
+			'/path/to/models/'
+		);
+		$this->assertEqual($expected, $new);
+
+		App::build(); //reset defaults
+		$defaults = App::path('models');
+		$this->assertEqual($old, $defaults);
+	}
+
+/**
+ * testCore method
+ *
+ * @access public
+ * @return void
+ */
+	function testCore() {
+		$model = App::core('models');
+		$this->assertEqual(array(ROOT . DS . LIBS . 'model' . DS), $model);
+
+		$view = App::core('views');
+		$this->assertEqual(array(ROOT . DS . LIBS . 'view' . DS), $view);
+
+		$controller = App::core('controllers');
+		$this->assertEqual(array(ROOT . DS . LIBS . 'controller' . DS), $controller);
+
+	}
+
+/**
+ * testListObjects method
+ *
+ * @access public
+ * @return void
+ */
+	function testListObjects() {
+		$result = App::objects('class', TEST_CAKE_CORE_INCLUDE_PATH . 'libs');
+		$this->assertTrue(in_array('Xml', $result));
+		$this->assertTrue(in_array('Cache', $result));
+		$this->assertTrue(in_array('HttpSocket', $result));
+
+		$result = App::objects('behavior');
+		$this->assertTrue(in_array('Tree', $result));
+
+		$result = App::objects('controller');
+		$this->assertTrue(in_array('Pages', $result));
+
+		$result = App::objects('component');
+		$this->assertTrue(in_array('Auth', $result));
+
+		$result = App::objects('view');
+		$this->assertTrue(in_array('Media', $result));
+
+		$result = App::objects('helper');
+		$this->assertTrue(in_array('Html', $result));
+
+		$result = App::objects('model');
+		$notExpected = array('AppModel', 'ModelBehavior', 'ConnectionManager',  'DbAcl', 'Model', 'CakeSchema');
+		foreach ($notExpected as $class) {
+			$this->assertFalse(in_array($class, $result));
+		}
+
+		$result = App::objects('file');
+		$this->assertFalse($result);
+
+		$result = App::objects('file', 'non_existing_configure');
+		$expected = array();
+		$this->assertEqual($result, $expected);
+
+		$result = App::objects('NonExistingType');
+		$this->assertFalse($result);
+
+		App::build(array(
+			'plugins' => array(
+				TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'libs' . DS
+			)
+		));
+		$result = App::objects('plugin', null, false);
+		$this->assertTrue(in_array('Cache', $result));
+		$this->assertTrue(in_array('Log', $result));
+
+		App::build();
+	}
+
+/**
+ * test that pluginPath can find paths for plugins.
+ *
+ * @return void
+ */
+	function testPluginPath() {
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)
+		));
+		$path = App::pluginPath('test_plugin');
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS . 'test_plugin' . DS;
+		$this->assertEqual($path, $expected);
+
+		$path = App::pluginPath('TestPlugin');
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS . 'test_plugin' . DS;
+		$this->assertEqual($path, $expected);
+
+		$path = App::pluginPath('TestPluginTwo');
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS . 'test_plugin_two' . DS;
+		$this->assertEqual($path, $expected);
+		App::build();
+	}
+
+/**
+ * test that pluginPath can find paths for plugins.
+ *
+ * @return void
+ */
+	function testThemePath() {
+		App::build(array(
+			'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS)
+		));
+		$path = App::themePath('test_theme');
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'themed' . DS . 'test_theme' . DS;
+		$this->assertEqual($path, $expected);
+
+		$path = App::themePath('TestTheme');
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'themed' . DS . 'test_theme' . DS;
+		$this->assertEqual($path, $expected);
+
+		App::build();
+	}
+
+/**
+ * testClassLoading method
+ *
+ * @access public
+ * @return void
+ */
+	function testClassLoading() {
+		$file = App::import();
+		$this->assertTrue($file);
+
+		$file = App::import('Model', 'Model', false);
+		$this->assertTrue($file);
+		$this->assertTrue(class_exists('Model'));
+
+		$file = App::import('Controller', 'Controller', false);
+		$this->assertTrue($file);
+		$this->assertTrue(class_exists('Controller'));
+
+		$file = App::import('Component', 'Component', false);
+		$this->assertTrue($file);
+		$this->assertTrue(class_exists('Component'));
+
+		$file = App::import('Shell', 'Shell', false);
+		$this->assertTrue($file);
+		$this->assertTrue(class_exists('Shell'));
+
+		$file = App::import('Model', 'SomeRandomModelThatDoesNotExist', false);
+		$this->assertFalse($file);
+
+		$file = App::import('Model', 'AppModel', false);
+		$this->assertTrue($file);
+		$this->assertTrue(class_exists('AppModel'));
+
+		$file = App::import('WrongType', null, true, array(), '');
+		$this->assertTrue($file);
+
+		$file = App::import('Model', 'NonExistingPlugin.NonExistingModel', false);
+		$this->assertFalse($file);
+
+		$file = App::import('Core', 'NonExistingPlugin.NonExistingModel', false);
+		$this->assertFalse($file);
+
+		$file = App::import('Model', array('NonExistingPlugin.NonExistingModel'), false);
+		$this->assertFalse($file);
+
+		$file = App::import('Core', array('NonExistingPlugin.NonExistingModel'), false);
+		$this->assertFalse($file);
+
+		$file = App::import('Core', array('NonExistingPlugin.NonExistingModel.AnotherChild'), false);
+		$this->assertFalse($file);
+
+		if (!class_exists('AppController')) {
+			$classes = array_flip(get_declared_classes());
+
+			if (PHP5) {
+				$this->assertFalse(isset($classes['PagesController']));
+				$this->assertFalse(isset($classes['AppController']));
+			} else {
+				$this->assertFalse(isset($classes['pagescontroller']));
+				$this->assertFalse(isset($classes['appcontroller']));
+			}
+
+			$file = App::import('Controller', 'Pages');
+			$this->assertTrue($file);
+			$this->assertTrue(class_exists('PagesController'));
+
+			$classes = array_flip(get_declared_classes());
+
+			if (PHP5) {
+				$this->assertTrue(isset($classes['PagesController']));
+				$this->assertTrue(isset($classes['AppController']));
+			} else {
+				$this->assertTrue(isset($classes['pagescontroller']));
+				$this->assertTrue(isset($classes['appcontroller']));
+			}
+
+			$file = App::import('Behavior', 'Containable');
+			$this->assertTrue($file);
+			$this->assertTrue(class_exists('ContainableBehavior'));
+
+			$file = App::import('Component', 'RequestHandler');
+			$this->assertTrue($file);
+			$this->assertTrue(class_exists('RequestHandlerComponent'));
+
+			$file = App::import('Helper', 'Form');
+			$this->assertTrue($file);
+			$this->assertTrue(class_exists('FormHelper'));
+
+			$file = App::import('Model', 'NonExistingModel');
+			$this->assertFalse($file);
+
+			$file = App::import('Datasource', 'DboSource');
+			$this->assertTrue($file);
+			$this->assertTrue(class_exists('DboSource'));
+		}
+		App::build();
+	}
+
+/**
+ * test import() with plugins
+ *
+ * @return void
+ */
+	function testPluginImporting() {
+		App::build(array(
+			'libs' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'libs' . DS),
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)
+		));
+
+		$result = App::import('Controller', 'TestPlugin.Tests');
+		$this->assertTrue($result);
+		$this->assertTrue(class_exists('TestPluginAppController'));
+		$this->assertTrue(class_exists('TestsController'));
+
+		$result = App::import('Lib', 'TestPlugin.TestPluginLibrary');
+		$this->assertTrue($result);
+		$this->assertTrue(class_exists('TestPluginLibrary'));
+
+		$result = App::import('Lib', 'Library');
+		$this->assertTrue($result);
+		$this->assertTrue(class_exists('Library'));
+
+		$result = App::import('Helper', 'TestPlugin.OtherHelper');
+		$this->assertTrue($result);
+		$this->assertTrue(class_exists('OtherHelperHelper'));
+		
+		$result = App::import('Helper', 'TestPlugin.TestPluginApp');
+		$this->assertTrue($result);
+		$this->assertTrue(class_exists('TestPluginAppHelper'));
+
+		$result = App::import('Datasource', 'TestPlugin.TestSource');
+		$this->assertTrue($result);
+		$this->assertTrue(class_exists('TestSource'));
+		
+		App::build();
+	}
+
+/**
+ * test that building helper paths actually works.
+ *
+ * @return void
+ * @link http://cakephp.lighthouseapp.com/projects/42648/tickets/410
+ */
+	function testImportingHelpersFromAlternatePaths() {
+		App::build();
+		$this->assertFalse(class_exists('BananaHelper'), 'BananaHelper exists, cannot test importing it.');
+		App::import('Helper', 'Banana');
+		$this->assertFalse(class_exists('BananaHelper'), 'BananaHelper was not found because the path does not exist.');
+
+		App::build(array(
+			'helpers' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'helpers' . DS)
+		));
+		App::build(array('vendors' => array(TEST_CAKE_CORE_INCLUDE_PATH)));
+		$this->assertFalse(class_exists('BananaHelper'), 'BananaHelper exists, cannot test importing it.');
+		App::import('Helper', 'Banana');
+		$this->assertTrue(class_exists('BananaHelper'), 'BananaHelper was not loaded.');
+
+		App::build();
+	}
+
+/**
+ * testFileLoading method
+ *
+ * @access public
+ * @return void
+ */
+	function testFileLoading () {
+		$file = App::import('File', 'RealFile', false, array(), TEST_CAKE_CORE_INCLUDE_PATH  . 'config' . DS . 'config.php');
+		$this->assertTrue($file);
+
+		$file = App::import('File', 'NoFile', false, array(), TEST_CAKE_CORE_INCLUDE_PATH  . 'config' . DS . 'cake' . DS . 'config.php');
+		$this->assertFalse($file);
+	}
+	// import($type = null, $name = null, $parent = true, $file = null, $search = array(), $return = false) {
+
+/**
+ * testFileLoadingWithArray method
+ *
+ * @access public
+ * @return void
+ */
+	function testFileLoadingWithArray() {
+		$type = array('type' => 'File', 'name' => 'SomeName', 'parent' => false,
+				'file' => TEST_CAKE_CORE_INCLUDE_PATH  . DS . 'config' . DS . 'config.php');
+		$file = App::import($type);
+		$this->assertTrue($file);
+
+		$type = array('type' => 'File', 'name' => 'NoFile', 'parent' => false,
+				'file' => TEST_CAKE_CORE_INCLUDE_PATH  . 'config' . DS . 'cake' . DS . 'config.php');
+		$file = App::import($type);
+		$this->assertFalse($file);
+	}
+
+/**
+ * testFileLoadingReturnValue method
+ *
+ * @access public
+ * @return void
+ */
+	function testFileLoadingReturnValue () {
+		$file = App::import('File', 'Name', false, array(), TEST_CAKE_CORE_INCLUDE_PATH  . 'config' . DS . 'config.php', true);
+		$this->assertTrue($file);
+
+		$this->assertTrue(isset($file['Cake.version']));
+
+		$type = array('type' => 'File', 'name' => 'OtherName', 'parent' => false,
+				'file' => TEST_CAKE_CORE_INCLUDE_PATH  . 'config' . DS . 'config.php', 'return' => true);
+		$file = App::import($type);
+		$this->assertTrue($file);
+
+		$this->assertTrue(isset($file['Cake.version']));
+	}
+
+/**
+ * testLoadingWithSearch method
+ *
+ * @access public
+ * @return void
+ */
+	function testLoadingWithSearch () {
+		$file = App::import('File', 'NewName', false, array(TEST_CAKE_CORE_INCLUDE_PATH ), 'config.php');
+		$this->assertTrue($file);
+
+		$file = App::import('File', 'AnotherNewName', false, array(LIBS), 'config.php');
+		$this->assertFalse($file);
+	}
+
+/**
+ * testLoadingWithSearchArray method
+ *
+ * @access public
+ * @return void
+ */
+	function testLoadingWithSearchArray () {
+		$type = array('type' => 'File', 'name' => 'RandomName', 'parent' => false, 'file' => 'config.php', 'search' => array(TEST_CAKE_CORE_INCLUDE_PATH ));
+		$file = App::import($type);
+		$this->assertTrue($file);
+
+		$type = array('type' => 'File', 'name' => 'AnotherRandomName', 'parent' => false, 'file' => 'config.php', 'search' => array(LIBS));
+		$file = App::import($type);
+		$this->assertFalse($file);
+	}
+
+/**
+ * testMultipleLoading method
+ *
+ * @access public
+ * @return void
+ */
+	function testMultipleLoading() {
+		$toLoad = array('I18n', 'CakeSocket');
+
+		$classes = array_flip(get_declared_classes());
+		$this->assertFalse(isset($classes['i18n']));
+		$this->assertFalse(isset($classes['CakeSocket']));
+
+		$load = App::import($toLoad);
+		$this->assertTrue($load);
+
+		$classes = array_flip(get_declared_classes());
+
+		if (PHP5) {
+			$this->assertTrue(isset($classes['I18n']));
+		} else {
+			$this->assertTrue(isset($classes['i18n']));
+		}
+
+		$load = App::import(array('I18n', 'SomeNotFoundClass', 'CakeSocket'));
+		$this->assertFalse($load);
+
+		$load = App::import($toLoad);
+		$this->assertTrue($load);
+	}
+
+/**
+ * This test only works if you have plugins/my_plugin set up.
+ * plugins/my_plugin/models/my_plugin.php and other_model.php
+ */
+
+/*
+	function testMultipleLoadingByType() {
+		$classes = array_flip(get_declared_classes());
+		$this->assertFalse(isset($classes['OtherPlugin']));
+		$this->assertFalse(isset($classes['MyPlugin']));
+
+
+		$load = App::import('Model', array('MyPlugin.OtherPlugin', 'MyPlugin.MyPlugin'));
+		$this->assertTrue($load);
+
+		$classes = array_flip(get_declared_classes());
+		$this->assertTrue(isset($classes['OtherPlugin']));
+		$this->assertTrue(isset($classes['MyPlugin']));
+	}
+*/
+	function testLoadingVendor() {
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS),
+			'vendors' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'vendors'. DS),
+		), true);
+
+		ob_start();
+		$result = App::import('Vendor', 'TestPlugin.TestPluginAsset', array('ext' => 'css'));
+		$text = ob_get_clean();
+		$this->assertTrue($result);
+		$this->assertEqual($text, 'this is the test plugin asset css file');
+
+		ob_start();
+		$result = App::import('Vendor', 'TestAsset', array('ext' => 'css'));
+		$text = ob_get_clean();
+		$this->assertTrue($result);
+		$this->assertEqual($text, 'this is the test asset css file');
+
+		$result = App::import('Vendor', 'TestPlugin.SamplePlugin');
+		$this->assertTrue($result);
+		$this->assertTrue(class_exists('SamplePluginClassTestName'));
+
+		$result = App::import('Vendor', 'ConfigureTestVendorSample');
+		$this->assertTrue($result);
+		$this->assertTrue(class_exists('ConfigureTestVendorSample'));
+
+		ob_start();
+		$result = App::import('Vendor', 'SomeName', array('file' => 'some.name.php'));
+		$text = ob_get_clean();
+		$this->assertTrue($result);
+		$this->assertEqual($text, 'This is a file with dot in file name');
+
+		ob_start();
+		$result = App::import('Vendor', 'TestHello', array('file' => 'Test'.DS.'hello.php'));
+		$text = ob_get_clean();
+		$this->assertTrue($result);
+		$this->assertEqual($text, 'This is the hello.php file in Test directory');
+
+		ob_start();
+		$result = App::import('Vendor', 'MyTest', array('file' => 'Test'.DS.'MyTest.php'));
+		$text = ob_get_clean();
+		$this->assertTrue($result);
+		$this->assertEqual($text, 'This is the MyTest.php file');
+
+		ob_start();
+		$result = App::import('Vendor', 'Welcome');
+		$text = ob_get_clean();
+		$this->assertTrue($result);
+		$this->assertEqual($text, 'This is the welcome.php file in vendors directory');
+
+		ob_start();
+		$result = App::import('Vendor', 'TestPlugin.Welcome');
+		$text = ob_get_clean();
+		$this->assertTrue($result);
+		$this->assertEqual($text, 'This is the welcome.php file in test_plugin/vendors directory');
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/controller/component.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/controller/component.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/controller/component.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,595 @@
+<?php
+/**
+ * ComponentTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ * @since         CakePHP(tm) v 1.2.0.5436
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Controller', 'Controller', false);
+App::import('Controller', 'Component', false);
+
+if (!class_exists('AppController')) {
+
+/**
+ * AppController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+	class AppController extends Controller {
+
+/**
+ * name property
+ *
+ * @var string 'App'
+ * @access public
+ */
+		var $name = 'App';
+
+/**
+ * uses property
+ *
+ * @var array
+ * @access public
+ */
+		var $uses = array();
+
+/**
+ * helpers property
+ *
+ * @var array
+ * @access public
+ */
+		var $helpers = array();
+
+/**
+ * components property
+ *
+ * @var array
+ * @access public
+ */
+		var $components = array('Orange' => array('colour' => 'blood orange'));
+	}
+} elseif (!defined('APP_CONTROLLER_EXISTS')){
+	define('APP_CONTROLLER_EXISTS', true);
+}
+
+/**
+ * ParamTestComponent
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class ParamTestComponent extends Object {
+
+/**
+ * name property
+ *
+ * @var string 'ParamTest'
+ * @access public
+ */
+	var $name = 'ParamTest';
+
+/**
+ * components property
+ *
+ * @var array
+ * @access public
+ */
+	var $components = array('Banana' => array('config' => 'value'));
+
+/**
+ * initialize method
+ *
+ * @param mixed $controller
+ * @param mixed $settings
+ * @access public
+ * @return void
+ */
+	function initialize(&$controller, $settings) {
+		foreach ($settings as $key => $value) {
+			if (is_numeric($key)) {
+				$this->{$value} = true;
+			} else {
+				$this->{$key} = $value;
+			}
+		}
+	}
+}
+
+/**
+ * ComponentTestController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class ComponentTestController extends AppController {
+
+/**
+ * name property
+ *
+ * @var string 'ComponentTest'
+ * @access public
+ */
+	var $name = 'ComponentTest';
+
+/**
+ * uses property
+ *
+ * @var array
+ * @access public
+ */
+	var $uses = array();
+}
+
+/**
+ * AppleComponent class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class AppleComponent extends Object {
+
+/**
+ * components property
+ *
+ * @var array
+ * @access public
+ */
+	var $components = array('Orange');
+
+/**
+ * testName property
+ *
+ * @var mixed null
+ * @access public
+ */
+	var $testName = null;
+
+/**
+ * startup method
+ *
+ * @param mixed $controller
+ * @access public
+ * @return void
+ */
+	function startup(&$controller) {
+		$this->testName = $controller->name;
+	}
+}
+
+/**
+ * OrangeComponent class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class OrangeComponent extends Object {
+
+/**
+ * components property
+ *
+ * @var array
+ * @access public
+ */
+	var $components = array('Banana');
+
+/**
+ * initialize method
+ *
+ * @param mixed $controller
+ * @access public
+ * @return void
+ */
+	function initialize(&$controller, $settings) {
+		$this->Controller = $controller;
+		$this->Banana->testField = 'OrangeField';
+		$this->settings = $settings;
+	}
+
+/**
+ * startup method
+ *
+ * @param Controller $controller
+ * @return string
+ * @access public
+ */
+	function startup(&$controller) {
+		$controller->foo = 'pass';
+	}
+}
+
+/**
+ * BananaComponent class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class BananaComponent extends Object {
+
+/**
+ * testField property
+ *
+ * @var string 'BananaField'
+ * @access public
+ */
+	var $testField = 'BananaField';
+
+/**
+ * components property
+ *
+ * @var array
+ * @access public
+ */
+	var $components = array('Apple');
+
+/**
+ * startup method
+ *
+ * @param Controller $controller
+ * @return string
+ * @access public
+ */
+	function startup(&$controller) {
+		$controller->bar = 'fail';
+	}
+}
+
+/**
+ * MutuallyReferencingOneComponent class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class MutuallyReferencingOneComponent extends Object {
+
+/**
+ * components property
+ *
+ * @var array
+ * @access public
+ */
+	var $components = array('MutuallyReferencingTwo');
+}
+
+/**
+ * MutuallyReferencingTwoComponent class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class MutuallyReferencingTwoComponent extends Object {
+
+/**
+ * components property
+ *
+ * @var array
+ * @access public
+ */
+	var $components = array('MutuallyReferencingOne');
+}
+
+/**
+ * SomethingWithEmailComponent class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class SomethingWithEmailComponent extends Object {
+
+/**
+ * components property
+ *
+ * @var array
+ * @access public
+ */
+	var $components = array('Email');
+}
+
+Mock::generate('Object', 'ComponentMockComponent', array('startup', 'beforeFilter', 'beforeRender', 'other'));
+
+/**
+ * ComponentTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class ComponentTest extends CakeTestCase {
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		$this->_pluginPaths = App::path('plugins');
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)
+		));
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		App::build();
+		ClassRegistry::flush();
+	}
+
+/**
+ * testLoadComponents method
+ *
+ * @access public
+ * @return void
+ */
+	function testLoadComponents() {
+		$Controller =& new ComponentTestController();
+		$Controller->components = array('RequestHandler');
+
+		$Component =& new Component();
+		$Component->init($Controller);
+
+		$this->assertTrue(is_a($Controller->RequestHandler, 'RequestHandlerComponent'));
+
+		$Controller =& new ComponentTestController();
+		$Controller->plugin = 'test_plugin';
+		$Controller->components = array('RequestHandler', 'TestPluginComponent');
+
+		$Component =& new Component();
+		$Component->init($Controller);
+
+		$this->assertTrue(is_a($Controller->RequestHandler, 'RequestHandlerComponent'));
+		$this->assertTrue(is_a($Controller->TestPluginComponent, 'TestPluginComponentComponent'));
+		$this->assertTrue(is_a(
+			$Controller->TestPluginComponent->TestPluginOtherComponent,
+			'TestPluginOtherComponentComponent'
+		));
+		$this->assertFalse(isset($Controller->TestPluginOtherComponent));
+
+		$Controller =& new ComponentTestController();
+		$Controller->components = array('Security');
+
+		$Component =& new Component();
+		$Component->init($Controller);
+
+		$this->assertTrue(is_a($Controller->Security, 'SecurityComponent'));
+		$this->assertTrue(is_a($Controller->Security->Session, 'SessionComponent'));
+
+		$Controller =& new ComponentTestController();
+		$Controller->components = array('Security', 'Cookie', 'RequestHandler');
+
+		$Component =& new Component();
+		$Component->init($Controller);
+
+		$this->assertTrue(is_a($Controller->Security, 'SecurityComponent'));
+		$this->assertTrue(is_a($Controller->Security->RequestHandler, 'RequestHandlerComponent'));
+		$this->assertTrue(is_a($Controller->RequestHandler, 'RequestHandlerComponent'));
+		$this->assertTrue(is_a($Controller->Cookie, 'CookieComponent'));
+	}
+
+/**
+ * test component loading
+ *
+ * @return void
+ */
+	function testNestedComponentLoading() {
+		$Controller =& new ComponentTestController();
+		$Controller->components = array('Apple');
+		$Controller->uses = false;
+		$Controller->constructClasses();
+		$Controller->Component->initialize($Controller);
+
+		$this->assertTrue(is_a($Controller->Apple, 'AppleComponent'));
+		$this->assertTrue(is_a($Controller->Apple->Orange, 'OrangeComponent'));
+		$this->assertTrue(is_a($Controller->Apple->Orange->Banana, 'BananaComponent'));
+		$this->assertTrue(is_a($Controller->Apple->Orange->Controller, 'ComponentTestController'));
+		$this->assertTrue(empty($Controller->Apple->Session));
+		$this->assertTrue(empty($Controller->Apple->Orange->Session));
+	}
+
+/**
+ * Tests Component::startup() and only running callbacks for components directly attached to
+ * the controller.
+ *
+ * @return void
+ */
+	function testComponentStartup() {
+		$Controller =& new ComponentTestController();
+		$Controller->components = array('Apple');
+		$Controller->uses = false;
+		$Controller->constructClasses();
+		$Controller->Component->initialize($Controller);
+		$Controller->beforeFilter();
+		$Controller->Component->startup($Controller);
+
+		$this->assertTrue(is_a($Controller->Apple, 'AppleComponent'));
+		$this->assertEqual($Controller->Apple->testName, 'ComponentTest');
+
+		$expected = !(defined('APP_CONTROLLER_EXISTS') && APP_CONTROLLER_EXISTS);
+		$this->assertEqual(isset($Controller->foo), $expected);
+		$this->assertFalse(isset($Controller->bar));
+	}
+
+/**
+ * test that triggerCallbacks fires methods on all the components, and can trigger any method.
+ *
+ * @return void
+ */
+	function testTriggerCallback() {
+		$Controller =& new ComponentTestController();
+		$Controller->components = array('ComponentMock');
+		$Controller->uses = null;
+		$Controller->constructClasses();
+
+		$Controller->ComponentMock->expectOnce('beforeRender');
+		$Controller->Component->triggerCallback('beforeRender', $Controller);
+
+		$Controller->ComponentMock->expectNever('beforeFilter');
+		$Controller->ComponentMock->enabled = false;
+		$Controller->Component->triggerCallback('beforeFilter', $Controller);
+	}
+
+/**
+ * test a component being used more than once.
+ *
+ * @return void
+ */
+	function testMultipleComponentInitialize() {
+		$Controller =& new ComponentTestController();
+		$Controller->uses = false;
+		$Controller->components = array('Orange', 'Banana');
+		$Controller->constructClasses();
+		$Controller->Component->initialize($Controller);
+
+		$this->assertEqual($Controller->Banana->testField, 'OrangeField');
+		$this->assertEqual($Controller->Orange->Banana->testField, 'OrangeField');
+	}
+
+/**
+ * Test Component declarations with Parameters
+ * tests merging of component parameters and merging / construction of components.
+ *
+ * @return void
+ */
+	function testComponentsWithParams() {
+		if ($this->skipIf(defined('APP_CONTROLLER_EXISTS'), '%s Need a non-existent AppController')) {
+			return;
+		}
+
+		$Controller =& new ComponentTestController();
+		$Controller->components = array('ParamTest' => array('test' => 'value', 'flag'), 'Apple');
+		$Controller->uses = false;
+		$Controller->constructClasses();
+		$Controller->Component->initialize($Controller);
+
+		$this->assertTrue(is_a($Controller->ParamTest, 'ParamTestComponent'));
+		$this->assertTrue(is_a($Controller->ParamTest->Banana, 'BananaComponent'));
+		$this->assertTrue(is_a($Controller->Orange, 'OrangeComponent'));
+		$this->assertFalse(isset($Controller->Session));
+		$this->assertEqual($Controller->Orange->settings, array('colour' => 'blood orange'));
+		$this->assertEqual($Controller->ParamTest->test, 'value');
+		$this->assertEqual($Controller->ParamTest->flag, true);
+
+		//Settings are merged from app controller and current controller.
+		$Controller =& new ComponentTestController();
+		$Controller->components = array(
+			'ParamTest' => array('test' => 'value'),
+			'Orange' => array('ripeness' => 'perfect')
+		);
+		$Controller->constructClasses();
+		$Controller->Component->initialize($Controller);
+
+		$expected = array('colour' => 'blood orange', 'ripeness' => 'perfect');
+		$this->assertEqual($Controller->Orange->settings, $expected);
+		$this->assertEqual($Controller->ParamTest->test, 'value');
+	}
+
+/**
+ * Ensure that settings are not duplicated when passed into component initialize.
+ *
+ * @return void
+ */
+	function testComponentParamsNoDuplication() {
+		if ($this->skipIf(defined('APP_CONTROLLER_EXISTS'), '%s Need a non-existent AppController')) {
+			return;
+		}
+		$Controller =& new ComponentTestController();
+		$Controller->components = array('Orange' => array('setting' => array('itemx')));
+		$Controller->uses = false;
+
+		$Controller->constructClasses();
+		$Controller->Component->initialize($Controller);
+		$expected = array('setting' => array('itemx'), 'colour' => 'blood orange');
+		$this->assertEqual($Controller->Orange->settings, $expected, 'Params duplication has occured %s');
+	}
+
+/**
+ * Test mutually referencing components.
+ *
+ * @return void
+ */
+	function testMutuallyReferencingComponents() {
+		$Controller =& new ComponentTestController();
+		$Controller->components = array('MutuallyReferencingOne');
+		$Controller->uses = false;
+		$Controller->constructClasses();
+		$Controller->Component->initialize($Controller);
+
+		$this->assertTrue(is_a(
+			$Controller->MutuallyReferencingOne,
+			'MutuallyReferencingOneComponent'
+		));
+		$this->assertTrue(is_a(
+			$Controller->MutuallyReferencingOne->MutuallyReferencingTwo,
+			'MutuallyReferencingTwoComponent'
+		));
+		$this->assertTrue(is_a(
+			$Controller->MutuallyReferencingOne->MutuallyReferencingTwo->MutuallyReferencingOne,
+			'MutuallyReferencingOneComponent'
+		));
+	}
+
+/**
+ * Test mutually referencing components.
+ *
+ * @return void
+ */
+	function testSomethingReferencingEmailComponent() {
+		$Controller =& new ComponentTestController();
+		$Controller->components = array('SomethingWithEmail');
+		$Controller->uses = false;
+		$Controller->constructClasses();
+		$Controller->Component->initialize($Controller);
+		$Controller->beforeFilter();
+		$Controller->Component->startup($Controller);
+
+		$this->assertTrue(is_a(
+			$Controller->SomethingWithEmail,
+			'SomethingWithEmailComponent'
+		));
+		$this->assertTrue(is_a(
+			$Controller->SomethingWithEmail->Email,
+			'EmailComponent'
+		));
+		$this->assertTrue(is_a(
+			$Controller->SomethingWithEmail->Email->Controller,
+			'ComponentTestController'
+		));
+	}
+
+/**
+ * Test that SessionComponent doesn't get added if its already in the components array.
+ *
+ * @return void
+ * @access public
+ */
+	function testDoubleLoadingOfSessionComponent() {
+		if ($this->skipIf(defined('APP_CONTROLLER_EXISTS'), '%s Need a non-existent AppController')) {
+			return;
+		}
+
+		$Controller =& new ComponentTestController();
+		$Controller->uses = false;
+		$Controller->components = array('Session');
+		$Controller->constructClasses();
+
+		$this->assertEqual($Controller->components, array('Session' => '', 'Orange' => array('colour' => 'blood orange')));
+	}
+
+}

Added: trunk/src/Web/cake/tests/cases/libs/controller/components/acl.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/controller/components/acl.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/controller/components/acl.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,646 @@
+<?php
+/**
+ * AclComponentTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ * @since         CakePHP(tm) v 1.2.0.5435
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) {
+	define('CAKEPHP_UNIT_TEST_EXECUTION', 1);
+}
+App::import(array('controller' .DS . 'components' . DS . 'acl', 'model' . DS . 'db_acl'));
+
+/**
+ * AclNodeTwoTestBase class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ */
+class AclNodeTwoTestBase extends AclNode {
+
+/**
+ * useDbConfig property
+ *
+ * @var string 'test_suite'
+ * @access public
+ */
+	var $useDbConfig = 'test_suite';
+
+/**
+ * cacheSources property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $cacheSources = false;
+}
+
+/**
+ * AroTwoTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ */
+class AroTwoTest extends AclNodeTwoTestBase {
+
+/**
+ * name property
+ *
+ * @var string 'AroTwoTest'
+ * @access public
+ */
+	var $name = 'AroTwoTest';
+
+/**
+ * useTable property
+ *
+ * @var string 'aro_twos'
+ * @access public
+ */
+	var $useTable = 'aro_twos';
+
+/**
+ * hasAndBelongsToMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('AcoTwoTest' => array('with' => 'PermissionTwoTest'));
+}
+
+/**
+ * AcoTwoTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ */
+class AcoTwoTest extends AclNodeTwoTestBase {
+
+/**
+ * name property
+ *
+ * @var string 'AcoTwoTest'
+ * @access public
+ */
+	var $name = 'AcoTwoTest';
+
+/**
+ * useTable property
+ *
+ * @var string 'aco_twos'
+ * @access public
+ */
+	var $useTable = 'aco_twos';
+
+/**
+ * hasAndBelongsToMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('AroTwoTest' => array('with' => 'PermissionTwoTest'));
+}
+
+/**
+ * PermissionTwoTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ */
+class PermissionTwoTest extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'PermissionTwoTest'
+ * @access public
+ */
+	var $name = 'PermissionTwoTest';
+
+/**
+ * useTable property
+ *
+ * @var string 'aros_aco_twos'
+ * @access public
+ */
+	var $useTable = 'aros_aco_twos';
+
+/**
+ * cacheQueries property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $cacheQueries = false;
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('AroTwoTest' => array('foreignKey' => 'aro_id'), 'AcoTwoTest' => array('foreignKey' => 'aco_id'));
+
+/**
+ * actsAs property
+ *
+ * @var mixed null
+ * @access public
+ */
+	var $actsAs = null;
+}
+
+/**
+ * DbAclTwoTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ */
+class DbAclTwoTest extends DbAcl {
+
+/**
+ * construct method
+ *
+ * @access private
+ * @return void
+ */
+	function __construct() {
+		$this->Aro =& new AroTwoTest();
+		$this->Aro->Permission =& new PermissionTwoTest();
+		$this->Aco =& new AcoTwoTest();
+		$this->Aro->Permission =& new PermissionTwoTest();
+	}
+}
+
+/**
+ * IniAclTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ */
+class IniAclTest extends IniAcl {
+}
+
+/**
+ * ACL Component Text case
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ */
+class AclComponentTest extends CakeTestCase {
+
+/**
+ * fixtures property
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array('core.aro_two', 'core.aco_two', 'core.aros_aco_two');
+
+/**
+ * startTest method
+ *
+ * @access public
+ * @return void
+ */
+	function startTest() {
+		$this->Acl =& new AclComponent();
+	}
+
+/**
+ * before method
+ *
+ * @param mixed $method
+ * @access public
+ * @return void
+ */
+	function before($method) {
+		Configure::write('Acl.classname', 'DbAclTwoTest');
+		Configure::write('Acl.database', 'test_suite');
+		parent::before($method);
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		unset($this->Acl);
+	}
+
+/**
+ * testAclCreate method
+ *
+ * @access public
+ * @return void
+ */
+	function testAclCreate() {
+		$this->Acl->Aro->create(array('alias' => 'Chotchkey'));
+		$this->assertTrue($this->Acl->Aro->save());
+
+		$parent = $this->Acl->Aro->id;
+
+		$this->Acl->Aro->create(array('parent_id' => $parent, 'alias' => 'Joanna'));
+		$this->assertTrue($this->Acl->Aro->save());
+
+		$this->Acl->Aro->create(array('parent_id' => $parent, 'alias' => 'Stapler'));
+		$this->assertTrue($this->Acl->Aro->save());
+
+		$root = $this->Acl->Aco->node('ROOT');
+		$parent = $root[0]['AcoTwoTest']['id'];
+
+		$this->Acl->Aco->create(array('parent_id' => $parent, 'alias' => 'Drinks'));
+		$this->assertTrue($this->Acl->Aco->save());
+
+		$this->Acl->Aco->create(array('parent_id' => $parent, 'alias' => 'PiecesOfFlair'));
+		$this->assertTrue($this->Acl->Aco->save());
+	}
+
+/**
+ * testAclCreateWithParent method
+ *
+ * @access public
+ * @return void
+ */
+	function testAclCreateWithParent() {
+		$parent = $this->Acl->Aro->findByAlias('Peter', null, null, -1);
+		$this->Acl->Aro->create();
+		$this->Acl->Aro->save(array(
+			'alias' => 'Subordinate',
+			'model' => 'User',
+			'foreign_key' => 7,
+			'parent_id' => $parent['AroTwoTest']['id']
+		));
+		$result = $this->Acl->Aro->findByAlias('Subordinate', null, null, -1);
+		$this->assertEqual($result['AroTwoTest']['lft'], 16);
+		$this->assertEqual($result['AroTwoTest']['rght'], 17);
+	}
+
+/**
+ * testDbAclAllow method
+ *
+ * @access public
+ * @return void
+ */
+	function testDbAclAllow() {
+		$this->assertFalse($this->Acl->check('Micheal', 'tpsReports', 'read'));
+		$this->assertTrue($this->Acl->allow('Micheal', 'tpsReports', array('read', 'delete', 'update')));
+		$this->assertTrue($this->Acl->check('Micheal', 'tpsReports', 'update'));
+		$this->assertTrue($this->Acl->check('Micheal', 'tpsReports', 'read'));
+		$this->assertTrue($this->Acl->check('Micheal', 'tpsReports', 'delete'));
+
+		$this->assertFalse($this->Acl->check('Micheal', 'tpsReports', 'create'));
+		$this->assertTrue($this->Acl->allow('Micheal', 'ROOT/tpsReports', 'create'));
+		$this->assertTrue($this->Acl->check('Micheal', 'tpsReports', 'create'));
+		$this->assertTrue($this->Acl->check('Micheal', 'tpsReports', 'delete'));
+		$this->assertTrue($this->Acl->allow('Micheal', 'printers', 'create'));
+		// Michael no longer has his delete permission for tpsReports!
+		$this->assertTrue($this->Acl->check('Micheal', 'tpsReports', 'delete'));
+		$this->assertTrue($this->Acl->check('Micheal', 'printers', 'create'));
+
+		$this->assertFalse($this->Acl->check('root/users/Samir', 'ROOT/tpsReports/view'));
+		$this->assertTrue($this->Acl->allow('root/users/Samir', 'ROOT/tpsReports/view', '*'));
+		$this->assertTrue($this->Acl->check('Samir', 'view', 'read'));
+		$this->assertTrue($this->Acl->check('root/users/Samir', 'ROOT/tpsReports/view', 'update'));
+
+		$this->assertFalse($this->Acl->check('root/users/Samir', 'ROOT/tpsReports/update','*'));
+		$this->assertTrue($this->Acl->allow('root/users/Samir', 'ROOT/tpsReports/update', '*'));
+		$this->assertTrue($this->Acl->check('Samir', 'update', 'read'));
+		$this->assertTrue($this->Acl->check('root/users/Samir', 'ROOT/tpsReports/update', 'update'));
+		// Samir should still have his tpsReports/view permissions, but does not
+		$this->assertTrue($this->Acl->check('root/users/Samir', 'ROOT/tpsReports/view', 'update'));
+
+		$this->expectError('DbAcl::allow() - Invalid node');
+		$this->assertFalse($this->Acl->allow('Lumbergh', 'ROOT/tpsReports/DoesNotExist', 'create'));
+
+		$this->expectError('DbAcl::allow() - Invalid node');
+		$this->assertFalse($this->Acl->allow('Homer', 'tpsReports', 'create'));
+	}
+
+/**
+ * testDbAclCheck method
+ *
+ * @access public
+ * @return void
+ */
+	function testDbAclCheck() {
+		$this->assertTrue($this->Acl->check('Samir', 'print', 'read'));
+		$this->assertTrue($this->Acl->check('Lumbergh', 'current', 'read'));
+		$this->assertFalse($this->Acl->check('Milton', 'smash', 'read'));
+		$this->assertFalse($this->Acl->check('Milton', 'current', 'update'));
+
+		$this->expectError("DbAcl::check() - Failed ARO/ACO node lookup in permissions check.  Node references:\nAro: WRONG\nAco: tpsReports");
+		$this->assertFalse($this->Acl->check('WRONG', 'tpsReports', 'read'));
+
+		$this->expectError("ACO permissions key foobar does not exist in DbAcl::check()");
+		$this->assertFalse($this->Acl->check('Lumbergh', 'smash', 'foobar'));
+
+		$this->expectError("DbAcl::check() - Failed ARO/ACO node lookup in permissions check.  Node references:\nAro: users\nAco: NonExistant");
+		$this->assertFalse($this->Acl->check('users', 'NonExistant', 'read'));
+
+		$this->assertFalse($this->Acl->check(null, 'printers', 'create'));
+		$this->assertFalse($this->Acl->check('managers', null, 'read'));
+
+		$this->assertTrue($this->Acl->check('Bobs', 'ROOT/tpsReports/view/current', 'read'));
+		$this->assertFalse($this->Acl->check('Samir', 'ROOT/tpsReports/update', 'read'));
+
+		$this->assertFalse($this->Acl->check('root/users/Milton', 'smash', 'delete'));
+	}
+
+/**
+ * testDbAclCascadingDeny function
+ *
+ * Setup the acl permissions such that Bobs inherits from admin.
+ * deny Admin delete access to a specific resource, check the permisssions are inherited.
+ *
+ * @access public
+ * @return void
+ */
+	function testDbAclCascadingDeny() {
+		$this->Acl->inherit('Bobs', 'ROOT', '*');
+		$this->assertTrue($this->Acl->check('admin', 'tpsReports', 'delete'));
+		$this->assertTrue($this->Acl->check('Bobs', 'tpsReports', 'delete'));
+		$this->Acl->deny('admin', 'tpsReports', 'delete');
+		$this->assertFalse($this->Acl->check('admin', 'tpsReports', 'delete'));
+		$this->assertFalse($this->Acl->check('Bobs', 'tpsReports', 'delete'));
+	}
+
+/**
+ * testDbAclDeny method
+ *
+ * @access public
+ * @return void
+ */
+	function testDbAclDeny() {
+		$this->assertTrue($this->Acl->check('Micheal', 'smash', 'delete'));
+		$this->Acl->deny('Micheal', 'smash', 'delete');
+		$this->assertFalse($this->Acl->check('Micheal', 'smash', 'delete'));
+		$this->assertTrue($this->Acl->check('Micheal', 'smash', 'read'));
+		$this->assertTrue($this->Acl->check('Micheal', 'smash', 'create'));
+		$this->assertTrue($this->Acl->check('Micheal', 'smash', 'update'));
+		$this->assertFalse($this->Acl->check('Micheal', 'smash', '*'));
+
+		$this->assertTrue($this->Acl->check('Samir', 'refill', '*'));
+		$this->Acl->deny('Samir', 'refill', '*');
+		$this->assertFalse($this->Acl->check('Samir', 'refill', 'create'));
+		$this->assertFalse($this->Acl->check('Samir', 'refill', 'update'));
+		$this->assertFalse($this->Acl->check('Samir', 'refill', 'read'));
+		$this->assertFalse($this->Acl->check('Samir', 'refill', 'delete'));
+
+		$result = $this->Acl->Aro->Permission->find('all', array('conditions' => array('AroTwoTest.alias' => 'Samir')));
+		$expected = '-1';
+		$this->assertEqual($result[0]['PermissionTwoTest']['_delete'], $expected);
+
+		$this->expectError('DbAcl::allow() - Invalid node');
+		$this->assertFalse($this->Acl->deny('Lumbergh', 'ROOT/tpsReports/DoesNotExist', 'create'));
+	}
+
+/**
+ * testAclNodeLookup method
+ *
+ * @access public
+ * @return void
+ */
+	function testAclNodeLookup() {
+		$result = $this->Acl->Aro->node('root/users/Samir');
+		$expected = array(
+			array('AroTwoTest' => array('id' => '7', 'parent_id' => '4', 'model' => 'User', 'foreign_key' => 3, 'alias' => 'Samir')),
+			array('AroTwoTest' => array('id' => '4', 'parent_id' => '1', 'model' => 'Group', 'foreign_key' => 3, 'alias' => 'users')),
+			array('AroTwoTest' => array('id' => '1', 'parent_id' => null, 'model' => null, 'foreign_key' => null, 'alias' => 'root'))
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Acl->Aco->node('ROOT/tpsReports/view/current');
+		$expected = array(
+			array('AcoTwoTest' => array('id' => '4', 'parent_id' => '3', 'model' => null, 'foreign_key' => null, 'alias' => 'current')),
+			array('AcoTwoTest' => array('id' => '3', 'parent_id' => '2', 'model' => null, 'foreign_key' => null, 'alias' => 'view')),
+			array('AcoTwoTest' => array('id' => '2', 'parent_id' => '1', 'model' => null, 'foreign_key' => null, 'alias' => 'tpsReports')),
+			array('AcoTwoTest' => array('id' => '1', 'parent_id' => null, 'model' => null, 'foreign_key' => null, 'alias' => 'ROOT')),
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testDbInherit method
+ *
+ * @access public
+ * @return void
+ */
+	function testDbInherit() {
+		//parent doesn't have access inherit should still deny
+		$this->assertFalse($this->Acl->check('Milton', 'smash', 'delete'));
+		$this->Acl->inherit('Milton', 'smash', 'delete');
+		$this->assertFalse($this->Acl->check('Milton', 'smash', 'delete'));
+
+		//inherit parent
+		$this->assertFalse($this->Acl->check('Milton', 'smash', 'read'));
+		$this->Acl->inherit('Milton', 'smash', 'read');
+		$this->assertTrue($this->Acl->check('Milton', 'smash', 'read'));
+	}
+
+/**
+ * testDbGrant method
+ *
+ * @access public
+ * @return void
+ */
+	function testDbGrant() {
+		$this->assertFalse($this->Acl->check('Samir', 'tpsReports', 'create'));
+		$this->Acl->grant('Samir', 'tpsReports', 'create');
+		$this->assertTrue($this->Acl->check('Samir', 'tpsReports', 'create'));
+
+		$this->assertFalse($this->Acl->check('Micheal', 'view', 'read'));
+		$this->Acl->grant('Micheal', 'view', array('read', 'create', 'update'));
+		$this->assertTrue($this->Acl->check('Micheal', 'view', 'read'));
+		$this->assertTrue($this->Acl->check('Micheal', 'view', 'create'));
+		$this->assertTrue($this->Acl->check('Micheal', 'view', 'update'));
+		$this->assertFalse($this->Acl->check('Micheal', 'view', 'delete'));
+
+		$this->expectError('DbAcl::allow() - Invalid node');
+		$this->assertFalse($this->Acl->grant('Peter', 'ROOT/tpsReports/DoesNotExist', 'create'));
+	}
+
+/**
+ * testDbRevoke method
+ *
+ * @access public
+ * @return void
+ */
+	function testDbRevoke() {
+		$this->assertTrue($this->Acl->check('Bobs', 'tpsReports', 'read'));
+		$this->Acl->revoke('Bobs', 'tpsReports', 'read');
+		$this->assertFalse($this->Acl->check('Bobs', 'tpsReports', 'read'));
+
+		$this->assertTrue($this->Acl->check('users', 'printers', 'read'));
+		$this->Acl->revoke('users', 'printers', 'read');
+		$this->assertFalse($this->Acl->check('users', 'printers', 'read'));
+		$this->assertFalse($this->Acl->check('Samir', 'printers', 'read'));
+		$this->assertFalse($this->Acl->check('Peter', 'printers', 'read'));
+
+		$this->expectError('DbAcl::allow() - Invalid node');
+		$this->assertFalse($this->Acl->deny('Bobs', 'ROOT/printers/DoesNotExist', 'create'));
+	}
+
+/**
+ * testStartup method
+ *
+ * @access public
+ * @return void
+ */
+	function testStartup() {
+		$controller = new Controller();
+		$this->assertTrue($this->Acl->startup($controller));
+	}
+
+/**
+ * testIniReadConfigFile
+ *
+ * @access public
+ * @return void
+ */
+	function testIniReadConfigFile() {
+		Configure::write('Acl.classname', 'IniAclTest');
+		unset($this->Acl);
+		$this->Acl = new AclComponent();
+		$iniFile = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'config'. DS . 'acl.ini.php';
+		$result = $this->Acl->_Instance->readConfigFile($iniFile);
+		$expected = array(
+			'admin' => array(
+				'groups' => 'administrators',
+				'allow' => '',
+				'deny' => 'ads',
+			),
+			'paul' => array(
+				'groups' => 'users',
+				'allow' =>'',
+				'deny' => '',
+			),
+			'jenny' => array(
+				'groups' => 'users',
+				'allow' => 'ads',
+				'deny' => 'images, files',
+			),
+			'nobody' => array(
+				'groups' => 'anonymous',
+				'allow' => '',
+				'deny' => '',
+			),
+			'administrators' => array(
+				'deny' => '',
+				'allow' => 'posts, comments, images, files, stats, ads',
+			),
+			'users' => array(
+				'allow' => 'posts, comments, images, files',
+				'deny' => 'stats, ads',
+			),
+			'anonymous' => array(
+				'allow' => '',
+				'deny' => 'posts, comments, images, files, stats, ads',
+			),
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testIniCheck method
+ *
+ * @access public
+ * @return void
+ */
+	function testIniCheck() {
+		Configure::write('Acl.classname', 'IniAclTest');
+		unset($this->Acl);
+		$iniFile = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'config'. DS . 'acl.ini.php';
+
+		$this->Acl = new AclComponent();
+		$this->Acl->_Instance->config= $this->Acl->_Instance->readConfigFile($iniFile);
+
+		$this->assertFalse($this->Acl->check('admin', 'ads'));
+		$this->assertTrue($this->Acl->check('admin', 'posts'));
+
+		$this->assertTrue($this->Acl->check('jenny', 'posts'));
+		$this->assertTrue($this->Acl->check('jenny', 'ads'));
+
+		$this->assertTrue($this->Acl->check('paul', 'posts'));
+		$this->assertFalse($this->Acl->check('paul', 'ads'));
+
+		$this->assertFalse($this->Acl->check('nobody', 'comments'));
+	}
+
+/**
+ * debug function - to help editing/creating test cases for the ACL component
+ *
+ * To check the overal ACL status at any time call $this->__debug();
+ * Generates a list of the current aro and aco structures and a grid dump of the permissions that are defined
+ * Only designed to work with the db based ACL
+ *
+ * @param bool $treesToo
+ * @access private
+ * @return void
+ */
+	function __debug ($printTreesToo = false) {
+		$this->Acl->Aro->displayField = 'alias';
+		$this->Acl->Aco->displayField = 'alias';
+		$aros = $this->Acl->Aro->find('list', array('order' => 'lft'));
+		$acos = $this->Acl->Aco->find('list', array('order' => 'lft'));
+		$rights = array('*', 'create', 'read', 'update', 'delete');
+		$permissions['Aros v Acos >'] = $acos;
+		foreach ($aros as $aro) {
+			$row = array();
+			foreach ($acos as $aco) {
+				$perms = '';
+				foreach ($rights as $right) {
+					if ($this->Acl->check($aro, $aco, $right)) {
+						if ($right == '*') {
+							$perms .= '****';
+							break;
+						}
+						$perms .= $right[0];
+					} elseif ($right != '*') {
+						$perms .= ' ';
+					}
+				}
+				$row[] = $perms;
+			}
+			$permissions[$aro] = $row;
+		}
+		foreach ($permissions as $key => $values) {
+			array_unshift($values, $key);
+			$values = array_map(array(&$this, '__pad'), $values);
+			$permissions[$key] = implode (' ', $values);
+		}
+		$permisssions = array_map(array(&$this, '__pad'), $permissions);
+		array_unshift($permissions, 'Current Permissions :');
+		if ($printTreesToo) {
+			debug (array('aros' => $this->Acl->Aro->generateTreeList(), 'acos' => $this->Acl->Aco->generateTreeList()));
+		}
+		debug (implode("\r\n", $permissions));
+	}
+
+/**
+ * pad function
+ * Used by debug to format strings used in the data dump
+ *
+ * @param string $string
+ * @param int $len
+ * @access private
+ * @return void
+ */
+	function __pad($string = '', $len = 14) {
+		return str_pad($string, $len);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/controller/components/auth.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/controller/components/auth.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/controller/components/auth.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,1646 @@
+<?php
+/**
+ * AuthComponentTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.cases.libs.controller.components
+ * @since         CakePHP(tm) v 1.2.0.5347
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Component', array('Auth', 'Acl'));
+App::import('Model', 'DbAcl');
+App::import('Core', 'Xml');
+
+Mock::generate('AclComponent', 'AuthTestMockAclComponent');
+
+/**
+* TestAuthComponent class
+*
+* @package       cake
+* @subpackage    cake.tests.cases.libs.controller.components
+*/
+class TestAuthComponent extends AuthComponent {
+
+/**
+ * testStop property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $testStop = false;
+
+/**
+ * Sets default login state
+ *
+ * @var bool true
+ * @access protected
+ */
+	var $_loggedIn = true;
+
+/**
+ * stop method
+ *
+ * @access public
+ * @return void
+ */
+	function _stop() {
+		$this->testStop = true;
+	}
+}
+
+/**
+* AuthUser class
+*
+* @package       cake
+* @subpackage    cake.tests.cases.libs.controller.components
+*/
+class AuthUser extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'AuthUser'
+ * @access public
+ */
+	var $name = 'AuthUser';
+
+/**
+ * useDbConfig property
+ *
+ * @var string 'test_suite'
+ * @access public
+ */
+	var $useDbConfig = 'test_suite';
+
+/**
+ * parentNode method
+ *
+ * @access public
+ * @return void
+ */
+	function parentNode() {
+		return true;
+	}
+
+/**
+ * bindNode method
+ *
+ * @param mixed $object
+ * @access public
+ * @return void
+ */
+	function bindNode($object) {
+		return 'Roles/Admin';
+	}
+
+/**
+ * isAuthorized method
+ *
+ * @param mixed $user
+ * @param mixed $controller
+ * @param mixed $action
+ * @access public
+ * @return void
+ */
+	function isAuthorized($user, $controller = null, $action = null) {
+		if (!empty($user)) {
+			return true;
+		}
+		return false;
+	}
+}
+
+/**
+ * AuthUserCustomField class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ */
+class AuthUserCustomField extends AuthUser {
+
+/**
+ * name property
+ *
+ * @var string 'AuthUser'
+ * @access public
+ */
+	var $name = 'AuthUserCustomField';
+}
+
+/**
+* UuidUser class
+*
+* @package       cake
+* @subpackage    cake.tests.cases.libs.controller.components
+*/
+class UuidUser extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'AuthUser'
+ * @access public
+ */
+	var $name = 'UuidUser';
+
+/**
+ * useDbConfig property
+ *
+ * @var string 'test_suite'
+ * @access public
+ */
+	var $useDbConfig = 'test_suite';
+
+/**
+ * useTable property
+ *
+ * @var string 'uuid'
+ * @access public
+ */
+	var $useTable = 'uuids';
+
+/**
+ * parentNode method
+ *
+ * @access public
+ * @return void
+ */
+	function parentNode() {
+		return true;
+	}
+
+/**
+ * bindNode method
+ *
+ * @param mixed $object
+ * @access public
+ * @return void
+ */
+	function bindNode($object) {
+		return 'Roles/Admin';
+	}
+
+/**
+ * isAuthorized method
+ *
+ * @param mixed $user
+ * @param mixed $controller
+ * @param mixed $action
+ * @access public
+ * @return void
+ */
+	function isAuthorized($user, $controller = null, $action = null) {
+		if (!empty($user)) {
+			return true;
+		}
+		return false;
+	}
+}
+
+/**
+* AuthTestController class
+*
+* @package       cake
+* @subpackage    cake.tests.cases.libs.controller.components
+*/
+class AuthTestController extends Controller {
+
+/**
+ * name property
+ *
+ * @var string 'AuthTest'
+ * @access public
+ */
+	var $name = 'AuthTest';
+
+/**
+ * uses property
+ *
+ * @var array
+ * @access public
+ */
+	var $uses = array('AuthUser');
+
+/**
+ * components property
+ *
+ * @var array
+ * @access public
+ */
+	var $components = array('Session', 'Auth', 'Acl');
+
+/**
+ * testUrl property
+ *
+ * @var mixed null
+ * @access public
+ */
+	var $testUrl = null;
+
+/**
+ * construct method
+ *
+ * @access private
+ * @return void
+ */
+	function __construct() {
+		$this->params = Router::parse('/auth_test');
+		Router::setRequestInfo(array($this->params, array('base' => null, 'here' => '/auth_test', 'webroot' => '/', 'passedArgs' => array(), 'argSeparator' => ':', 'namedArgs' => array())));
+		parent::__construct();
+	}
+
+/**
+ * beforeFilter method
+ *
+ * @access public
+ * @return void
+ */
+	function beforeFilter() {
+		$this->Auth->userModel = 'AuthUser';
+	}
+
+/**
+ * login method
+ *
+ * @access public
+ * @return void
+ */
+	function login() {
+	}
+
+/**
+ * admin_login method
+ *
+ * @access public
+ * @return void
+ */
+	function admin_login() {
+	}
+
+/**
+ * logout method
+ *
+ * @access public
+ * @return void
+ */
+	function logout() {
+		// $this->redirect($this->Auth->logout());
+	}
+
+/**
+ * add method
+ *
+ * @access public
+ * @return void
+ */
+	function add() {
+		echo "add";
+	}
+
+/**
+ * add method
+ *
+ * @access public
+ * @return void
+ */
+	function camelCase() {
+		echo "camelCase";
+	}
+
+/**
+ * redirect method
+ *
+ * @param mixed $url
+ * @param mixed $status
+ * @param mixed $exit
+ * @access public
+ * @return void
+ */
+	function redirect($url, $status = null, $exit = true) {
+		$this->testUrl = Router::url($url);
+		return false;
+	}
+
+/**
+ * isAuthorized method
+ *
+ * @access public
+ * @return void
+ */
+	function isAuthorized() {
+		if (isset($this->params['testControllerAuth'])) {
+			return false;
+		}
+		return true;
+	}
+
+/**
+ * Mock delete method
+ *
+ * @param mixed $url
+ * @param mixed $status
+ * @param mixed $exit
+ * @access public
+ * @return void
+ */
+	function delete($id = null) {
+		if ($this->TestAuth->testStop !== true && $id !== null) {
+			echo 'Deleted Record: ' . var_export($id, true);
+		}
+	}
+}
+
+/**
+ * AjaxAuthController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ */
+class AjaxAuthController extends Controller {
+
+/**
+ * name property
+ *
+ * @var string 'AjaxAuth'
+ * @access public
+ */
+	var $name = 'AjaxAuth';
+
+/**
+ * components property
+ *
+ * @var array
+ * @access public
+ */
+	var $components = array('Session', 'TestAuth');
+
+/**
+ * uses property
+ *
+ * @var array
+ * @access public
+ */
+	var $uses = array();
+
+/**
+ * testUrl property
+ *
+ * @var mixed null
+ * @access public
+ */
+	var $testUrl = null;
+
+/**
+ * beforeFilter method
+ *
+ * @access public
+ * @return void
+ */
+	function beforeFilter() {
+		$this->TestAuth->ajaxLogin = 'test_element';
+		$this->TestAuth->userModel = 'AuthUser';
+		$this->TestAuth->RequestHandler->ajaxLayout = 'ajax2';
+	}
+
+/**
+ * add method
+ *
+ * @access public
+ * @return void
+ */
+	function add() {
+		if ($this->TestAuth->testStop !== true) {
+			echo 'Added Record';
+		}
+	}
+
+/**
+ * redirect method
+ *
+ * @param mixed $url
+ * @param mixed $status
+ * @param mixed $exit
+ * @access public
+ * @return void
+ */
+	function redirect($url, $status = null, $exit = true) {
+		$this->testUrl = Router::url($url);
+		return false;
+	}
+}
+
+/**
+* AuthTest class
+*
+* @package       cake
+* @subpackage    cake.tests.cases.libs.controller.components
+*/
+class AuthTest extends CakeTestCase {
+
+/**
+ * name property
+ *
+ * @var string 'Auth'
+ * @access public
+ */
+	var $name = 'Auth';
+
+/**
+ * fixtures property
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array('core.uuid', 'core.auth_user', 'core.auth_user_custom_field', 'core.aro', 'core.aco', 'core.aros_aco', 'core.aco_action');
+
+/**
+ * initialized property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $initialized = false;
+
+/**
+ * startTest method
+ *
+ * @access public
+ * @return void
+ */
+	function startTest() {
+		$this->_server = $_SERVER;
+		$this->_env = $_ENV;
+
+		$this->_securitySalt = Configure::read('Security.salt');
+		Configure::write('Security.salt', 'JfIxfs2guVoUubWDYhG93b0qyJfIxfs2guwvniR2G0FgaC9mi');
+
+		$this->_acl = Configure::read('Acl');
+		Configure::write('Acl.database', 'test_suite');
+		Configure::write('Acl.classname', 'DbAcl');
+
+		$this->Controller =& new AuthTestController();
+		$this->Controller->Component->init($this->Controller);
+		$this->Controller->Component->initialize($this->Controller);
+		$this->Controller->beforeFilter();
+
+		ClassRegistry::addObject('view', new View($this->Controller));
+
+		$this->Controller->Session->delete('Auth');
+		$this->Controller->Session->delete('Message.auth');
+
+		Router::reload();
+
+		$this->initialized = true;
+	}
+
+/**
+ * endTest method
+ *
+ * @access public
+ * @return void
+ */
+	function endTest() {
+		$_SERVER = $this->_server;
+		$_ENV = $this->_env;
+		Configure::write('Acl', $this->_acl);
+		Configure::write('Security.salt', $this->_securitySalt);
+
+		$this->Controller->Session->delete('Auth');
+		$this->Controller->Session->delete('Message.auth');
+		ClassRegistry::flush();
+		unset($this->Controller, $this->AuthUser);
+	}
+
+/**
+ * testNoAuth method
+ *
+ * @access public
+ * @return void
+ */
+	function testNoAuth() {
+		$this->assertFalse($this->Controller->Auth->isAuthorized());
+	}
+
+/**
+ * testIsErrorOrTests
+ *
+ * @access public
+ * @return void
+ */
+	function testIsErrorOrTests() {
+		$this->Controller->Auth->initialize($this->Controller);
+
+		$this->Controller->name = 'CakeError';
+		$this->assertTrue($this->Controller->Auth->startup($this->Controller));
+
+		$this->Controller->name = 'Post';
+		$this->Controller->params['action'] = 'thisdoesnotexist';
+		$this->assertTrue($this->Controller->Auth->startup($this->Controller));
+
+		$this->Controller->scaffold = null;
+		$this->Controller->params['action'] = 'index';
+		$this->assertFalse($this->Controller->Auth->startup($this->Controller));
+	}
+
+/**
+ * testIdentify method
+ *
+ * @access public
+ * @return void
+ */
+	function testIdentify() {
+		$this->AuthUser =& new AuthUser();
+		$user['id'] = 1;
+		$user['username'] = 'mariano';
+		$user['password'] = Security::hash(Configure::read('Security.salt') . 'cake');
+		$this->AuthUser->save($user, false);
+
+		$this->Controller->Auth->initialize($this->Controller);
+		$this->Controller->Auth->userModel = 'AuthUser';
+		$this->Controller->Auth->startup($this->Controller);
+		$this->assertTrue($this->Controller->Auth->identify($user));
+	}
+
+/**
+ * testIdentifyWithConditions method
+ *
+ * @access public
+ * @return void
+ */
+	function testIdentifyWithConditions() {
+		$this->AuthUser =& new AuthUser();
+		$user['id'] = 1;
+		$user['username'] = 'mariano';
+		$user['password'] = Security::hash(Configure::read('Security.salt') . 'cake');
+		$this->AuthUser->save($user, false);
+
+		$this->Controller->Auth->initialize($this->Controller);
+		$this->Controller->Auth->startup($this->Controller);
+		$this->Controller->Auth->userModel = 'AuthUser';
+
+		$this->assertFalse($this->Controller->Auth->identify($user, array('AuthUser.id >' => 2)));
+
+		$this->Controller->Auth->userScope = array('id >' => 2);
+		$this->assertFalse($this->Controller->Auth->identify($user));
+		$this->assertTrue($this->Controller->Auth->identify($user, false));
+	}
+
+/**
+ * testLogin method
+ *
+ * @access public
+ * @return void
+ */
+	function testLogin() {
+		$this->AuthUser =& new AuthUser();
+		$user['id'] = 1;
+		$user['username'] = 'mariano';
+		$user['password'] = Security::hash(Configure::read('Security.salt') . 'cake');
+		$this->AuthUser->save($user, false);
+
+		$authUser = $this->AuthUser->find();
+
+		$this->Controller->data['AuthUser']['username'] = $authUser['AuthUser']['username'];
+		$this->Controller->data['AuthUser']['password'] = 'cake';
+
+		$this->Controller->params = Router::parse('auth_test/login');
+		$this->Controller->params['url']['url'] = 'auth_test/login';
+
+		$this->Controller->Auth->initialize($this->Controller);
+
+		$this->Controller->Auth->loginAction = 'auth_test/login';
+		$this->Controller->Auth->userModel = 'AuthUser';
+
+		$this->Controller->Auth->startup($this->Controller);
+		$user = $this->Controller->Auth->user();
+		$expected = array('AuthUser' => array(
+			'id' => 1, 'username' => 'mariano', 'created' => '2007-03-17 01:16:23', 'updated' => date('Y-m-d H:i:s')
+		));
+		$this->assertEqual($user, $expected);
+		$this->Controller->Session->delete('Auth');
+
+		$this->Controller->data['AuthUser']['username'] = 'blah';
+		$this->Controller->data['AuthUser']['password'] = '';
+
+		$this->Controller->Auth->startup($this->Controller);
+
+		$user = $this->Controller->Auth->user();
+		$this->assertFalse($user);
+		$this->Controller->Session->delete('Auth');
+
+		$this->Controller->data['AuthUser']['username'] = 'now() or 1=1 --';
+		$this->Controller->data['AuthUser']['password'] = '';
+
+		$this->Controller->Auth->startup($this->Controller);
+
+		$user = $this->Controller->Auth->user();
+		$this->assertFalse($user);
+		$this->Controller->Session->delete('Auth');
+
+		$this->Controller->data['AuthUser']['username'] = 'now() or 1=1 # something';
+		$this->Controller->data['AuthUser']['password'] = '';
+
+		$this->Controller->Auth->startup($this->Controller);
+
+		$user = $this->Controller->Auth->user();
+		$this->assertFalse($user);
+		$this->Controller->Session->delete('Auth');
+
+		$this->Controller->Auth->userModel = 'UuidUser';
+		$this->Controller->Auth->login('47c36f9c-bc00-4d17-9626-4e183ca6822b');
+
+		$user = $this->Controller->Auth->user();
+		$expected = array('UuidUser' => array(
+			'id' => '47c36f9c-bc00-4d17-9626-4e183ca6822b', 'title' => 'Unique record 1', 'count' => 2, 'created' => '2008-03-13 01:16:23', 'updated' => '2008-03-13 01:18:31'
+		));
+		$this->assertEqual($user, $expected);
+		$this->Controller->Session->delete('Auth');
+	}
+
+/**
+ * test that being redirected to the login page, with no post data does
+ * not set the session value.  Saving the session value in this circumstance
+ * can cause the user to be redirected to an already public page.
+ *
+ * @return void
+ */
+	function testLoginActionNotSettingAuthRedirect() {
+		$_referer = $_SERVER['HTTP_REFERER'];
+		$_SERVER['HTTP_REFERER'] = '/pages/display/about';
+
+		$this->Controller->data = array();
+		$this->Controller->params = Router::parse('auth_test/login');
+		$this->Controller->params['url']['url'] = 'auth_test/login';
+		$this->Controller->Session->delete('Auth');
+
+		$this->Controller->Auth->loginRedirect = '/users/dashboard';
+		$this->Controller->Auth->loginAction = 'auth_test/login';
+		$this->Controller->Auth->userModel = 'AuthUser';
+
+		$this->Controller->Auth->startup($this->Controller);
+		$redirect = $this->Controller->Session->read('Auth.redirect');
+		$this->assertNull($redirect);
+	}
+
+/**
+ * testAuthorizeFalse method
+ *
+ * @access public
+ * @return void
+ */
+	function testAuthorizeFalse() {
+		$this->AuthUser =& new AuthUser();
+		$user = $this->AuthUser->find();
+		$this->Controller->Session->write('Auth', $user);
+		$this->Controller->Auth->userModel = 'AuthUser';
+		$this->Controller->Auth->authorize = false;
+		$this->Controller->params = Router::parse('auth_test/add');
+		$result = $this->Controller->Auth->startup($this->Controller);
+		$this->assertTrue($result);
+
+		$this->Controller->Session->delete('Auth');
+		$result = $this->Controller->Auth->startup($this->Controller);
+		$this->assertFalse($result);
+		$this->assertTrue($this->Controller->Session->check('Message.auth'));
+
+		$this->Controller->params = Router::parse('auth_test/camelCase');
+		$result = $this->Controller->Auth->startup($this->Controller);
+		$this->assertFalse($result);
+	}
+
+/**
+ * testAuthorizeController method
+ *
+ * @access public
+ * @return void
+ */
+	function testAuthorizeController() {
+		$this->AuthUser =& new AuthUser();
+		$user = $this->AuthUser->find();
+		$this->Controller->Session->write('Auth', $user);
+		$this->Controller->Auth->userModel = 'AuthUser';
+		$this->Controller->Auth->authorize = 'controller';
+		$this->Controller->params = Router::parse('auth_test/add');
+		$result = $this->Controller->Auth->startup($this->Controller);
+		$this->assertTrue($result);
+
+		$this->Controller->params['testControllerAuth'] = 1;
+		$result = $this->Controller->Auth->startup($this->Controller);
+		$this->assertTrue($this->Controller->Session->check('Message.auth'));
+		$this->assertFalse($result);
+
+		$this->Controller->Session->delete('Auth');
+	}
+
+/**
+ * testAuthorizeModel method
+ *
+ * @access public
+ * @return void
+ */
+	function testAuthorizeModel() {
+		$this->AuthUser =& new AuthUser();
+		$user = $this->AuthUser->find();
+		$this->Controller->Session->write('Auth', $user);
+
+		$this->Controller->params['controller'] = 'auth_test';
+		$this->Controller->params['action'] = 'add';
+		$this->Controller->Auth->userModel = 'AuthUser';
+		$this->Controller->Auth->initialize($this->Controller);
+		$this->Controller->Auth->authorize = array('model'=>'AuthUser');
+		$result = $this->Controller->Auth->startup($this->Controller);
+		$this->assertTrue($result);
+
+		$this->Controller->Session->delete('Auth');
+		$this->Controller->Auth->startup($this->Controller);
+		$this->assertTrue($this->Controller->Session->check('Message.auth'));
+		$result = $this->Controller->Auth->isAuthorized();
+		$this->assertFalse($result);
+	}
+
+/**
+ * testAuthorizeCrud method
+ *
+ * @access public
+ * @return void
+ */
+	function testAuthorizeCrud() {
+		$this->AuthUser =& new AuthUser();
+		$user = $this->AuthUser->find();
+		$this->Controller->Session->write('Auth', $user);
+
+		$this->Controller->params['controller'] = 'auth_test';
+		$this->Controller->params['action'] = 'add';
+
+		$this->Controller->Acl->name = 'DbAclTest';
+
+		$this->Controller->Acl->Aro->id = null;
+		$this->Controller->Acl->Aro->create(array('alias' => 'Roles'));
+		$result = $this->Controller->Acl->Aro->save();
+		$this->assertTrue($result);
+
+		$parent = $this->Controller->Acl->Aro->id;
+
+		$this->Controller->Acl->Aro->create(array('parent_id' => $parent, 'alias' => 'Admin'));
+		$result = $this->Controller->Acl->Aro->save();
+		$this->assertTrue($result);
+
+		$parent = $this->Controller->Acl->Aro->id;
+
+		$this->Controller->Acl->Aro->create(array(
+			'model' => 'AuthUser', 'parent_id' => $parent, 'foreign_key' => 1, 'alias'=> 'mariano'
+		));
+		$result = $this->Controller->Acl->Aro->save();
+		$this->assertTrue($result);
+
+		$this->Controller->Acl->Aco->create(array('alias' => 'Root'));
+		$result = $this->Controller->Acl->Aco->save();
+		$this->assertTrue($result);
+
+		$parent = $this->Controller->Acl->Aco->id;
+
+		$this->Controller->Acl->Aco->create(array('parent_id' => $parent, 'alias' => 'AuthTest'));
+		$result = $this->Controller->Acl->Aco->save();
+		$this->assertTrue($result);
+
+		$this->Controller->Acl->allow('Roles/Admin', 'Root');
+		$this->Controller->Acl->allow('Roles/Admin', 'Root/AuthTest');
+
+		$this->Controller->Auth->initialize($this->Controller);
+
+		$this->Controller->Auth->userModel = 'AuthUser';
+		$this->Controller->Auth->authorize = 'crud';
+		$this->Controller->Auth->actionPath = 'Root/';
+
+		$this->Controller->Auth->startup($this->Controller);
+		$this->assertTrue($this->Controller->Auth->isAuthorized());
+
+		$this->Controller->Session->delete('Auth');
+		$this->Controller->Auth->startup($this->Controller);
+		$this->assertTrue($this->Controller->Session->check('Message.auth'));
+	}
+
+/**
+ * test authorize = 'actions' setting.
+ *
+ * @return void
+ */
+	function testAuthorizeActions() {
+		$this->AuthUser =& new AuthUser();
+		$user = $this->AuthUser->find();
+		$this->Controller->Session->write('Auth', $user);
+		$this->Controller->params['controller'] = 'auth_test';
+		$this->Controller->params['action'] = 'add';
+
+		$this->Controller->Acl =& new AuthTestMockAclComponent();
+		$this->Controller->Acl->setReturnValue('check', true);
+
+		$this->Controller->Auth->initialize($this->Controller);
+
+		$this->Controller->Auth->userModel = 'AuthUser';
+		$this->Controller->Auth->authorize = 'actions';
+		$this->Controller->Auth->actionPath = 'Root/';
+
+		$this->Controller->Acl->expectAt(0, 'check', array(
+			$user, 'Root/AuthTest/add'
+		));
+
+		$this->Controller->Auth->startup($this->Controller);
+		$this->assertTrue($this->Controller->Auth->isAuthorized());
+	}
+
+/**
+ * Tests that deny always takes precedence over allow
+ *
+ * @access public
+ * @return void
+ */
+	function testAllowDenyAll() {
+		$this->Controller->Auth->initialize($this->Controller);
+
+		$this->Controller->Auth->allow('*');
+		$this->Controller->Auth->deny('add', 'camelcase');
+
+		$this->Controller->params['action'] = 'delete';
+		$this->assertTrue($this->Controller->Auth->startup($this->Controller));
+
+		$this->Controller->params['action'] = 'add';
+		$this->assertFalse($this->Controller->Auth->startup($this->Controller));
+
+		$this->Controller->params['action'] = 'Add';
+		$this->assertFalse($this->Controller->Auth->startup($this->Controller));
+
+		$this->Controller->params['action'] = 'camelCase';
+		$this->assertFalse($this->Controller->Auth->startup($this->Controller));
+
+		$this->Controller->Auth->allow('*');
+		$this->Controller->Auth->deny(array('add', 'camelcase'));
+
+		$this->Controller->params['action'] = 'camelCase';
+		$this->assertFalse($this->Controller->Auth->startup($this->Controller));
+	}
+
+/**
+ * test the action() method
+ *
+ * @return void
+ */
+	function testActionMethod() {
+		$this->Controller->params['controller'] = 'auth_test';
+		$this->Controller->params['action'] = 'add';
+
+		$this->Controller->Auth->initialize($this->Controller);
+		$this->Controller->Auth->actionPath = 'ROOT/';
+
+		$result = $this->Controller->Auth->action();
+		$this->assertEqual($result, 'ROOT/AuthTest/add');
+
+		$result = $this->Controller->Auth->action(':controller');
+		$this->assertEqual($result, 'ROOT/AuthTest');
+
+		$result = $this->Controller->Auth->action(':controller');
+		$this->assertEqual($result, 'ROOT/AuthTest');
+
+		$this->Controller->params['plugin'] = 'test_plugin';
+		$this->Controller->params['controller'] = 'auth_test';
+		$this->Controller->params['action'] = 'add';
+		$this->Controller->Auth->initialize($this->Controller);
+		$result = $this->Controller->Auth->action();
+		$this->assertEqual($result, 'ROOT/TestPlugin/AuthTest/add');
+	}
+
+/**
+ * test that deny() converts camel case inputs to lowercase.
+ *
+ * @return void
+ */
+	function testDenyWithCamelCaseMethods() {
+		$this->Controller->Auth->initialize($this->Controller);
+		$this->Controller->Auth->allow('*');
+		$this->Controller->Auth->deny('add', 'camelCase');
+
+		$url = '/auth_test/camelCase';
+		$this->Controller->params = Router::parse($url);
+		$this->Controller->params['url']['url'] = Router::normalize($url);
+
+		$this->assertFalse($this->Controller->Auth->startup($this->Controller));
+	}
+
+/**
+ * test that allow() and allowedActions work with camelCase method names.
+ *
+ * @return void
+ */
+	function testAllowedActionsWithCamelCaseMethods() {
+		$url = '/auth_test/camelCase';
+		$this->Controller->params = Router::parse($url);
+		$this->Controller->params['url']['url'] = Router::normalize($url);
+		$this->Controller->Auth->initialize($this->Controller);
+		$this->Controller->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login');
+		$this->Controller->Auth->userModel = 'AuthUser';
+		$this->Controller->Auth->allow('*');
+		$result = $this->Controller->Auth->startup($this->Controller);
+		$this->assertTrue($result, 'startup() should return true, as action is allowed. %s');
+
+		$url = '/auth_test/camelCase';
+		$this->Controller->params = Router::parse($url);
+		$this->Controller->params['url']['url'] = Router::normalize($url);
+		$this->Controller->Auth->initialize($this->Controller);
+		$this->Controller->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login');
+		$this->Controller->Auth->userModel = 'AuthUser';
+		$this->Controller->Auth->allowedActions = array('delete', 'camelCase', 'add');
+		$result = $this->Controller->Auth->startup($this->Controller);
+		$this->assertTrue($result, 'startup() should return true, as action is allowed. %s');
+
+		$this->Controller->Auth->allowedActions = array('delete', 'add');
+		$result = $this->Controller->Auth->startup($this->Controller);
+		$this->assertFalse($result, 'startup() should return false, as action is not allowed. %s');
+
+		$url = '/auth_test/delete';
+		$this->Controller->params = Router::parse($url);
+		$this->Controller->params['url']['url'] = Router::normalize($url);
+		$this->Controller->Auth->initialize($this->Controller);
+		$this->Controller->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login');
+		$this->Controller->Auth->userModel = 'AuthUser';
+
+		$this->Controller->Auth->allow(array('delete', 'add'));
+		$result = $this->Controller->Auth->startup($this->Controller);
+		$this->assertTrue($result, 'startup() should return true, as action is allowed. %s');
+	}
+
+	function testAllowedActionsSetWithAllowMethod() {
+		$url = '/auth_test/action_name';
+		$this->Controller->params = Router::parse($url);
+		$this->Controller->params['url']['url'] = Router::normalize($url);
+		$this->Controller->Auth->initialize($this->Controller);
+		$this->Controller->Auth->allow('action_name', 'anotherAction');
+		$this->assertEqual($this->Controller->Auth->allowedActions, array('action_name', 'anotheraction'));
+	}
+
+/**
+ * testLoginRedirect method
+ *
+ * @access public
+ * @return void
+ */
+	function testLoginRedirect() {
+		if (isset($_SERVER['HTTP_REFERER'])) {
+			$backup = $_SERVER['HTTP_REFERER'];
+		} else {
+			$backup = null;
+		}
+
+		$_SERVER['HTTP_REFERER'] = false;
+
+		$this->Controller->Session->write('Auth', array(
+			'AuthUser' => array('id' => '1', 'username' => 'nate')
+		));
+
+		$this->Controller->params = Router::parse('users/login');
+		$this->Controller->params['url']['url'] = 'users/login';
+		$this->Controller->Auth->initialize($this->Controller);
+
+		$this->Controller->Auth->userModel = 'AuthUser';
+		$this->Controller->Auth->loginRedirect = array(
+			'controller' => 'pages', 'action' => 'display', 'welcome'
+		);
+		$this->Controller->Auth->startup($this->Controller);
+		$expected = Router::normalize($this->Controller->Auth->loginRedirect);
+		$this->assertEqual($expected, $this->Controller->Auth->redirect());
+
+		$this->Controller->Session->delete('Auth');
+
+		//empty referer no session
+		$_SERVER['HTTP_REFERER'] = false;
+		$_ENV['HTTP_REFERER'] = false;
+		putenv('HTTP_REFERER=');
+		$url = '/posts/view/1';
+
+		$this->Controller->Session->write('Auth', array(
+			'AuthUser' => array('id' => '1', 'username' => 'nate'))
+		);
+		$this->Controller->testUrl = null;
+		$this->Controller->params = Router::parse($url);
+		array_push($this->Controller->methods, 'view', 'edit', 'index');
+
+		$this->Controller->Auth->initialize($this->Controller);
+		$this->Controller->Auth->authorize = 'controller';
+		$this->Controller->params['testControllerAuth'] = true;
+
+		$this->Controller->Auth->loginAction = array(
+			'controller' => 'AuthTest', 'action' => 'login'
+		);
+		$this->Controller->Auth->userModel = 'AuthUser';
+		$this->Controller->Auth->startup($this->Controller);
+		$expected = Router::normalize('/');
+		$this->assertEqual($expected, $this->Controller->testUrl);
+
+
+		$this->Controller->Session->delete('Auth');
+		$_SERVER['HTTP_REFERER'] = Router::url('/admin/', true);
+
+		$this->Controller->Session->write('Auth', array(
+			'AuthUser' => array('id'=>'1', 'username'=>'nate'))
+		);
+		$this->Controller->params['url']['url'] = 'auth_test/login';
+		$this->Controller->Auth->initialize($this->Controller);
+		$this->Controller->Auth->loginAction = 'auth_test/login';
+		$this->Controller->Auth->userModel = 'AuthUser';
+		$this->Controller->Auth->loginRedirect = false;
+		$this->Controller->Auth->startup($this->Controller);
+		$expected = Router::normalize('/admin');
+		$this->assertEqual($expected, $this->Controller->Auth->redirect());
+
+		//Ticket #4750
+		//named params
+		$this->Controller->Session->delete('Auth');
+		$url = '/posts/index/year:2008/month:feb';
+		$this->Controller->params = Router::parse($url);
+		$this->Controller->params['url']['url'] = Router::normalize($url);
+		$this->Controller->Auth->initialize($this->Controller);
+		$this->Controller->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login');
+		$this->Controller->Auth->userModel = 'AuthUser';
+		$this->Controller->Auth->startup($this->Controller);
+		$expected = Router::normalize('posts/index/year:2008/month:feb');
+		$this->assertEqual($expected, $this->Controller->Session->read('Auth.redirect'));
+
+		//passed args
+		$this->Controller->Session->delete('Auth');
+		$url = '/posts/view/1';
+		$this->Controller->params = Router::parse($url);
+		$this->Controller->params['url']['url'] = Router::normalize($url);
+		$this->Controller->Auth->initialize($this->Controller);
+		$this->Controller->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login');
+		$this->Controller->Auth->userModel = 'AuthUser';
+		$this->Controller->Auth->startup($this->Controller);
+		$expected = Router::normalize('posts/view/1');
+		$this->assertEqual($expected, $this->Controller->Session->read('Auth.redirect'));
+
+        // QueryString parameters
+		$_back = $_GET;
+		$_GET = array(
+			'url' => '/posts/index/29',
+			'print' => 'true',
+			'refer' => 'menu'
+		);
+		$this->Controller->Session->delete('Auth');
+		$url = '/posts/index/29?print=true&refer=menu';
+		$this->Controller->params = Dispatcher::parseParams($url);
+		$this->Controller->Auth->initialize($this->Controller);
+		$this->Controller->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login');
+		$this->Controller->Auth->userModel = 'AuthUser';
+		$this->Controller->Auth->startup($this->Controller);
+		$expected = Router::normalize('posts/index/29?print=true&refer=menu');
+		$this->assertEqual($expected, $this->Controller->Session->read('Auth.redirect'));
+
+		$_GET = array(
+			'url' => '/posts/index/29',
+			'print' => 'true',
+			'refer' => 'menu',
+			'ext' => 'html'
+		);
+		$this->Controller->Session->delete('Auth');
+		$url = '/posts/index/29?print=true&refer=menu';
+		$this->Controller->params = Dispatcher::parseParams($url);
+		$this->Controller->Auth->initialize($this->Controller);
+		$this->Controller->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login');
+		$this->Controller->Auth->userModel = 'AuthUser';
+		$this->Controller->Auth->startup($this->Controller);
+		$expected = Router::normalize('posts/index/29?print=true&refer=menu');
+		$this->assertEqual($expected, $this->Controller->Session->read('Auth.redirect'));
+		$_GET = $_back;
+
+		//external authed action
+		$_SERVER['HTTP_REFERER'] = 'http://webmail.example.com/view/message';
+		$this->Controller->Session->delete('Auth');
+		$url = '/posts/edit/1';
+		$this->Controller->params = Router::parse($url);
+		$this->Controller->params['url']['url'] = Router::normalize($url);
+		$this->Controller->Auth->initialize($this->Controller);
+		$this->Controller->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login');
+		$this->Controller->Auth->userModel = 'AuthUser';
+		$this->Controller->Auth->startup($this->Controller);
+		$expected = Router::normalize('/posts/edit/1');
+		$this->assertEqual($expected, $this->Controller->Session->read('Auth.redirect'));
+
+		//external direct login link
+		$_SERVER['HTTP_REFERER'] = 'http://webmail.example.com/view/message';
+		$this->Controller->Session->delete('Auth');
+		$url = '/AuthTest/login';
+		$this->Controller->params = Router::parse($url);
+		$this->Controller->params['url']['url'] = Router::normalize($url);
+		$this->Controller->Auth->initialize($this->Controller);
+		$this->Controller->Auth->loginAction = array('controller' => 'AuthTest', 'action' => 'login');
+		$this->Controller->Auth->userModel = 'AuthUser';
+		$this->Controller->Auth->startup($this->Controller);
+		$expected = Router::normalize('/');
+		$this->assertEqual($expected, $this->Controller->Session->read('Auth.redirect'));
+
+		$_SERVER['HTTP_REFERER'] = $backup;
+		$this->Controller->Session->delete('Auth');
+	}
+
+/**
+ * Ensure that no redirect is performed when a 404 is reached
+ * And the user doesn't have a session.
+ *
+ * @return void
+ */
+	function testNoRedirectOn404() {
+		$this->Controller->Session->delete('Auth');
+		$this->Controller->Auth->initialize($this->Controller);
+		$this->Controller->params = Router::parse('auth_test/something_totally_wrong');
+		$result = $this->Controller->Auth->startup($this->Controller);
+		$this->assertTrue($result, 'Auth redirected a missing action %s');
+	}
+
+/**
+ * testEmptyUsernameOrPassword method
+ *
+ * @access public
+ * @return void
+ */
+	function testEmptyUsernameOrPassword() {
+		$this->AuthUser =& new AuthUser();
+		$user['id'] = 1;
+		$user['username'] = 'mariano';
+		$user['password'] = Security::hash(Configure::read('Security.salt') . 'cake');
+		$this->AuthUser->save($user, false);
+
+		$authUser = $this->AuthUser->find();
+
+		$this->Controller->data['AuthUser']['username'] = '';
+		$this->Controller->data['AuthUser']['password'] = '';
+
+		$this->Controller->params = Router::parse('auth_test/login');
+		$this->Controller->params['url']['url'] = 'auth_test/login';
+		$this->Controller->Auth->initialize($this->Controller);
+		$this->Controller->Auth->loginAction = 'auth_test/login';
+		$this->Controller->Auth->userModel = 'AuthUser';
+
+		$this->Controller->Auth->startup($this->Controller);
+		$user = $this->Controller->Auth->user();
+		$this->assertTrue($this->Controller->Session->check('Message.auth'));
+		$this->assertEqual($user, false);
+		$this->Controller->Session->delete('Auth');
+	}
+
+/**
+ * testInjection method
+ *
+ * @access public
+ * @return void
+ */
+	function testInjection() {
+		$this->AuthUser =& new AuthUser();
+		$this->AuthUser->id = 2;
+		$this->AuthUser->saveField('password', Security::hash(Configure::read('Security.salt') . 'cake'));
+
+		$this->Controller->data['AuthUser']['username'] = 'nate';
+		$this->Controller->data['AuthUser']['password'] = 'cake';
+		$this->Controller->params = Router::parse('auth_test/login');
+		$this->Controller->params['url']['url'] = 'auth_test/login';
+		$this->Controller->Auth->initialize($this->Controller);
+
+		$this->Controller->Auth->loginAction = 'auth_test/login';
+		$this->Controller->Auth->userModel = 'AuthUser';
+		$this->Controller->Auth->startup($this->Controller);
+		$this->assertTrue(is_array($this->Controller->Auth->user()));
+
+		$this->Controller->Session->delete($this->Controller->Auth->sessionKey);
+
+		$this->Controller->data['AuthUser']['username'] = 'nate';
+		$this->Controller->data['AuthUser']['password'] = 'cake1';
+		$this->Controller->params['url']['url'] = 'auth_test/login';
+		$this->Controller->Auth->initialize($this->Controller);
+
+		$this->Controller->Auth->loginAction = 'auth_test/login';
+		$this->Controller->Auth->userModel = 'AuthUser';
+		$this->Controller->Auth->startup($this->Controller);
+		$this->assertTrue(is_null($this->Controller->Auth->user()));
+
+		$this->Controller->Session->delete($this->Controller->Auth->sessionKey);
+
+		$this->Controller->data['AuthUser']['username'] = '> n';
+		$this->Controller->data['AuthUser']['password'] = 'cake';
+		$this->Controller->Auth->initialize($this->Controller);
+
+		$this->Controller->Auth->startup($this->Controller);
+		$this->assertTrue(is_null($this->Controller->Auth->user()));
+
+		unset($this->Controller->data['AuthUser']['password']);
+		$this->Controller->data['AuthUser']['username'] = "1'1";
+		$this->Controller->Auth->initialize($this->Controller);
+
+		$this->Controller->Auth->startup($this->Controller);
+		$this->assertTrue(is_null($this->Controller->Auth->user()));
+
+		unset($this->Controller->data['AuthUser']['username']);
+		$this->Controller->data['AuthUser']['password'] = "1'1";
+		$this->Controller->Auth->initialize($this->Controller);
+
+		$this->Controller->Auth->startup($this->Controller);
+		$this->assertTrue(is_null($this->Controller->Auth->user()));
+	}
+
+/**
+ * test Hashing of passwords
+ *
+ * @return void
+ */
+	function testHashPasswords() {
+		$this->Controller->Auth->userModel = 'AuthUser';
+
+		$data['AuthUser']['password'] = 'superSecret';
+		$data['AuthUser']['username'] = 'super****@daily*****';
+		$return = $this->Controller->Auth->hashPasswords($data);
+		$expected = $data;
+		$expected['AuthUser']['password'] = Security::hash($expected['AuthUser']['password'], null, true);
+		$this->assertEqual($return, $expected);
+
+		$data['Wrong']['password'] = 'superSecret';
+		$data['Wrong']['username'] = 'super****@daily*****';
+		$data['AuthUser']['password'] = 'IcantTellYou';
+		$return = $this->Controller->Auth->hashPasswords($data);
+		$expected = $data;
+		$expected['AuthUser']['password'] = Security::hash($expected['AuthUser']['password'], null, true);
+		$this->assertEqual($return, $expected);
+
+		if (PHP5) {
+			$xml = array(
+				'User' => array(
+					'username' => 'batma****@batca*****',
+					'password' => 'bruceWayne',
+				)
+			);
+			$data =& new Xml($xml);
+			$return = $this->Controller->Auth->hashPasswords($data);
+			$expected = $data;
+			$this->assertEqual($return, $expected);
+		}
+	}
+
+/**
+ * testCustomRoute method
+ *
+ * @access public
+ * @return void
+ */
+	function testCustomRoute() {
+		Router::reload();
+		Router::connect(
+			'/:lang/:controller/:action/*',
+			array('lang' => null),
+			array('lang' => '[a-z]{2,3}')
+		);
+
+		$url = '/en/users/login';
+		$this->Controller->params = Router::parse($url);
+		Router::setRequestInfo(array($this->Controller->passedArgs, array(
+			'base' => null, 'here' => $url, 'webroot' => '/', 'passedArgs' => array(),
+			'argSeparator' => ':', 'namedArgs' => array()
+		)));
+
+		$this->AuthUser =& new AuthUser();
+		$user = array(
+			'id' => 1, 'username' => 'felix',
+			'password' => Security::hash(Configure::read('Security.salt') . 'cake'
+		));
+		$user = $this->AuthUser->save($user, false);
+
+		$this->Controller->data['AuthUser'] = array('username' => 'felix', 'password' => 'cake');
+		$this->Controller->params['url']['url'] = substr($url, 1);
+		$this->Controller->Auth->initialize($this->Controller);
+		$this->Controller->Auth->loginAction = array('lang' => 'en', 'controller' => 'users', 'action' => 'login');
+		$this->Controller->Auth->userModel = 'AuthUser';
+
+		$this->Controller->Auth->startup($this->Controller);
+		$user = $this->Controller->Auth->user();
+		$this->assertTrue(!!$user);
+
+		$this->Controller->Session->delete('Auth');
+		Router::reload();
+		Router::connect('/', array('controller' => 'people', 'action' => 'login'));
+		$url = '/';
+		$this->Controller->params = Router::parse($url);
+		Router::setRequestInfo(array($this->Controller->passedArgs, array(
+			'base' => null, 'here' => $url, 'webroot' => '/', 'passedArgs' => array(),
+			'argSeparator' => ':', 'namedArgs' => array()
+		)));
+		$this->Controller->data['AuthUser'] = array('username' => 'felix', 'password' => 'cake');
+		$this->Controller->params['url']['url'] = substr($url, 1);
+		$this->Controller->Auth->initialize($this->Controller);
+		$this->Controller->Auth->loginAction = array('controller' => 'people', 'action' => 'login');
+		$this->Controller->Auth->userModel = 'AuthUser';
+
+		$this->Controller->Auth->startup($this->Controller);
+		$user = $this->Controller->Auth->user();
+		$this->assertTrue(!!$user);
+	}
+
+/**
+ * testCustomField method
+ *
+ * @access public
+ * @return void
+ */
+	function testCustomField() {
+		Router::reload();
+
+		$this->AuthUserCustomField =& new AuthUserCustomField();
+		$user = array(
+			'id' => 1, 'email' => 'harki****@examp*****',
+			'password' => Security::hash(Configure::read('Security.salt') . 'cake'
+		));
+		$user = $this->AuthUserCustomField->save($user, false);
+
+		Router::connect('/', array('controller' => 'people', 'action' => 'login'));
+		$url = '/';
+		$this->Controller->params = Router::parse($url);
+		Router::setRequestInfo(array($this->Controller->passedArgs, array(
+			'base' => null, 'here' => $url, 'webroot' => '/', 'passedArgs' => array(),
+			'argSeparator' => ':', 'namedArgs' => array()
+		)));
+		$this->Controller->data['AuthUserCustomField'] = array('email' => 'harki****@examp*****', 'password' => 'cake');
+		$this->Controller->params['url']['url'] = substr($url, 1);
+		$this->Controller->Auth->initialize($this->Controller);
+        $this->Controller->Auth->fields = array('username' => 'email', 'password' => 'password');
+		$this->Controller->Auth->loginAction = array('controller' => 'people', 'action' => 'login');
+		$this->Controller->Auth->userModel = 'AuthUserCustomField';
+
+		$this->Controller->Auth->startup($this->Controller);
+		$user = $this->Controller->Auth->user();
+		$this->assertTrue(!!$user);
+    }
+
+/**
+ * testAdminRoute method
+ *
+ * @access public
+ * @return void
+ */
+	function testAdminRoute() {
+		$prefixes = Configure::read('Routing.prefixes');
+		Configure::write('Routing.prefixes', array('admin'));
+		Router::reload();
+
+		$url = '/admin/auth_test/add';
+		$this->Controller->params = Router::parse($url);
+		$this->Controller->params['url']['url'] = ltrim($url, '/');
+		Router::setRequestInfo(array(
+			array(
+				'pass' => array(), 'action' => 'add', 'plugin' => null,
+				'controller' => 'auth_test', 'admin' => true,
+				'url' => array('url' => $this->Controller->params['url']['url'])
+			),
+			array(
+				'base' => null, 'here' => $url,
+				'webroot' => '/', 'passedArgs' => array(),
+			)
+		));
+		$this->Controller->Auth->initialize($this->Controller);
+
+		$this->Controller->Auth->loginAction = array(
+			'admin' => true, 'controller' => 'auth_test', 'action' => 'login'
+		);
+		$this->Controller->Auth->userModel = 'AuthUser';
+
+		$this->Controller->Auth->startup($this->Controller);
+		$this->assertEqual($this->Controller->testUrl, '/admin/auth_test/login');
+
+		Configure::write('Routing.prefixes', $prefixes);
+	}
+
+/**
+ * testPluginModel method
+ *
+ * @access public
+ * @return void
+ */
+	function testPluginModel() {
+		// Adding plugins
+		Cache::delete('object_map', '_cake_core_');
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS),
+			'models' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'models' . DS)
+		), true);
+		App::objects('plugin', null, false);
+
+		$PluginModel =& ClassRegistry::init('TestPlugin.TestPluginAuthUser');
+		$user['id'] = 1;
+		$user['username'] = 'gwoo';
+		$user['password'] = Security::hash(Configure::read('Security.salt') . 'cake');
+		$PluginModel->save($user, false);
+
+		$authUser = $PluginModel->find();
+
+		$this->Controller->data['TestPluginAuthUser']['username'] = $authUser['TestPluginAuthUser']['username'];
+		$this->Controller->data['TestPluginAuthUser']['password'] = 'cake';
+
+		$this->Controller->params = Router::parse('auth_test/login');
+		$this->Controller->params['url']['url'] = 'auth_test/login';
+
+		$this->Controller->Auth->initialize($this->Controller);
+
+		$this->Controller->Auth->loginAction = 'auth_test/login';
+		$this->Controller->Auth->userModel = 'TestPlugin.TestPluginAuthUser';
+
+		$this->Controller->Auth->startup($this->Controller);
+		$user = $this->Controller->Auth->user();
+		$expected = array('TestPluginAuthUser' => array(
+			'id' => 1, 'username' => 'gwoo', 'created' => '2007-03-17 01:16:23', 'updated' => date('Y-m-d H:i:s')
+		));
+		$this->assertEqual($user, $expected);
+		$sessionKey = $this->Controller->Auth->sessionKey;
+		$this->assertEqual('Auth.TestPluginAuthUser', $sessionKey);
+		
+		$this->Controller->Auth->loginAction = null;
+		$this->Controller->Auth->__setDefaults();
+		$loginAction = $this->Controller->Auth->loginAction;
+		$expected = array(
+		    'controller'	=> 'test_plugin_auth_users',
+		    'action'		=> 'login',
+		    'plugin'		=> 'test_plugin'
+		);
+		$this->assertEqual($loginAction, $expected);
+
+		// Reverting changes
+		Cache::delete('object_map', '_cake_core_');
+		App::build();
+		App::objects('plugin', null, false);
+	}
+
+/**
+ * testAjaxLogin method
+ *
+ * @access public
+ * @return void
+ */
+	function testAjaxLogin() {
+		App::build(array('views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS)));
+		$_SERVER['HTTP_X_REQUESTED_WITH'] = "XMLHttpRequest";
+
+		if (!class_exists('dispatcher')) {
+			require CAKE . 'dispatcher.php';
+		}
+
+		ob_start();
+		$Dispatcher =& new Dispatcher();
+		$Dispatcher->dispatch('/ajax_auth/add', array('return' => 1));
+		$result = ob_get_clean();
+		$this->assertEqual("Ajax!\nthis is the test element", str_replace("\r\n", "\n", $result));
+		unset($_SERVER['HTTP_X_REQUESTED_WITH']);
+	}
+
+/**
+ * testLoginActionRedirect method
+ *
+ * @access public
+ * @return void
+ */
+	function testLoginActionRedirect() {
+		$admin = Configure::read('Routing.admin');
+		Configure::write('Routing.admin', 'admin');
+		Router::reload();
+
+		$url = '/admin/auth_test/login';
+		$this->Controller->params = Router::parse($url);
+		$this->Controller->params['url']['url'] = ltrim($url, '/');
+		Router::setRequestInfo(array(
+			array(
+				'pass' => array(), 'action' => 'admin_login', 'plugin' => null, 'controller' => 'auth_test',
+				'admin' => true, 'url' => array('url' => $this->Controller->params['url']['url']),
+			),
+			array(
+				'base' => null, 'here' => $url,
+				'webroot' => '/', 'passedArgs' => array(),
+			)
+		));
+
+		$this->Controller->Auth->initialize($this->Controller);
+
+		$this->Controller->Auth->loginAction = array('admin' => true, 'controller' => 'auth_test', 'action' => 'login');
+		$this->Controller->Auth->userModel = 'AuthUser';
+
+		$this->Controller->Auth->startup($this->Controller);
+
+		$this->assertNull($this->Controller->testUrl);
+
+		Configure::write('Routing.admin', $admin);
+	}
+
+/**
+ * Tests that shutdown destroys the redirect session var
+ *
+ * @access public
+ * @return void
+ */
+	function testShutDown() {
+		$this->Controller->Session->write('Auth.redirect', 'foo');
+		$this->Controller->Auth->_loggedIn = true;
+		$this->Controller->Auth->shutdown($this->Controller);
+		$this->assertFalse($this->Controller->Session->read('Auth.redirect'));
+	}
+
+/**
+ * test the initialize callback and its interactions with Router::prefixes()
+ *
+ * @return void
+ */
+	function testInitializeAndRoutingPrefixes() {
+		$restore = Configure::read('Routing');
+		Configure::write('Routing.prefixes', array('admin', 'super_user'));
+		Router::reload();
+		$this->Controller->Auth->initialize($this->Controller);
+
+		$this->assertTrue(isset($this->Controller->Auth->actionMap['delete']));
+		$this->assertTrue(isset($this->Controller->Auth->actionMap['view']));
+		$this->assertTrue(isset($this->Controller->Auth->actionMap['add']));
+		$this->assertTrue(isset($this->Controller->Auth->actionMap['admin_view']));
+		$this->assertTrue(isset($this->Controller->Auth->actionMap['super_user_delete']));
+
+		Configure::write('Routing', $restore);
+	}
+
+/**
+ * test $settings in Controller::$components
+ *
+ * @access public
+ * @return void
+ */
+	function testComponentSettings() {
+		$this->Controller =& new AuthTestController();
+		$this->Controller->components = array(
+			'Auth' => array(
+				'fields' => array('username' => 'email', 'password' => 'password'),
+				'loginAction' => array('controller' => 'people', 'action' => 'login'),
+				'userModel' => 'AuthUserCustomField',
+				'sessionKey' => 'AltAuth.AuthUserCustomField'
+			),
+			'Session'
+		);
+		$this->Controller->Component->init($this->Controller);
+		$this->Controller->Component->initialize($this->Controller);
+		Router::reload();
+
+		$this->AuthUserCustomField =& new AuthUserCustomField();
+		$user = array(
+			'id' => 1, 'email' => 'harki****@examp*****',
+			'password' => Security::hash(Configure::read('Security.salt') . 'cake'
+		));
+		$user = $this->AuthUserCustomField->save($user, false);
+
+		Router::connect('/', array('controller' => 'people', 'action' => 'login'));
+		$url = '/';
+		$this->Controller->params = Router::parse($url);
+		Router::setRequestInfo(array($this->Controller->passedArgs, array(
+			'base' => null, 'here' => $url, 'webroot' => '/', 'passedArgs' => array(),
+			'argSeparator' => ':', 'namedArgs' => array()
+		)));
+		$this->Controller->data['AuthUserCustomField'] = array('email' => 'harki****@examp*****', 'password' => 'cake');
+		$this->Controller->params['url']['url'] = substr($url, 1);
+		$this->Controller->Auth->startup($this->Controller);
+
+		$user = $this->Controller->Auth->user();
+		$this->assertTrue(!!$user);
+
+		$expected = array(
+			'fields' => array('username' => 'email', 'password' => 'password'),
+			'loginAction' => array('controller' => 'people', 'action' => 'login'),
+			'logoutRedirect' => array('controller' => 'people', 'action' => 'login'),
+			'userModel' => 'AuthUserCustomField',
+			'sessionKey' => 'AltAuth.AuthUserCustomField'
+		);
+		$this->assertEqual($expected['fields'], $this->Controller->Auth->fields);
+		$this->assertEqual($expected['loginAction'], $this->Controller->Auth->loginAction);
+		$this->assertEqual($expected['logoutRedirect'], $this->Controller->Auth->logoutRedirect);
+		$this->assertEqual($expected['userModel'], $this->Controller->Auth->userModel);
+		$this->assertEqual($expected['sessionKey'], $this->Controller->Auth->sessionKey);
+	}
+
+/**
+ * test that logout deletes the session variables. and returns the correct url
+ *
+ * @return void
+ */
+	function testLogout() {
+		$this->Controller->Session->write('Auth.User.id', '1');
+		$this->Controller->Session->write('Auth.redirect', '/users/login');
+		$this->Controller->Auth->logoutRedirect = '/';
+		$result = $this->Controller->Auth->logout();
+
+		$this->assertEqual($result, '/');
+		$this->assertNull($this->Controller->Session->read('Auth.AuthUser'));
+		$this->assertNull($this->Controller->Session->read('Auth.redirect'));
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/controller/components/cookie.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/controller/components/cookie.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/controller/components/cookie.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,530 @@
+<?php
+/**
+ * CookieComponentTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ * @since         CakePHP(tm) v 1.2.0.5435
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Controller', array('Component', 'Controller'), false);
+App::import('Component', 'Cookie');
+
+/**
+ * CookieComponentTestController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ */
+class CookieComponentTestController extends Controller {
+
+/**
+ * components property
+ *
+ * @var array
+ * @access public
+ */
+	var $components = array('Cookie');
+
+/**
+ * beforeFilter method
+ *
+ * @access public
+ * @return void
+ */
+	function beforeFilter() {
+		$this->Cookie->name = 'CakeTestCookie';
+		$this->Cookie->time = 10;
+		$this->Cookie->path = '/';
+		$this->Cookie->domain = '';
+		$this->Cookie->secure = false;
+		$this->Cookie->key = 'somerandomhaskey';
+	}
+}
+
+/**
+ * CookieComponentTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ */
+class CookieComponentTest extends CakeTestCase {
+
+/**
+ * Controller property
+ *
+ * @var CookieComponentTestController
+ * @access public
+ */
+	var $Controller;
+
+/**
+ * start
+ *
+ * @access public
+ * @return void
+ */
+	function start() {
+		$this->Controller = new CookieComponentTestController();
+		$this->Controller->constructClasses();
+		$this->Controller->Component->initialize($this->Controller);
+		$this->Controller->beforeFilter();
+		$this->Controller->Component->startup($this->Controller);
+		$this->Controller->Cookie->destroy();
+	}
+
+/**
+ * end
+ *
+ * @access public
+ * @return void
+ */
+	function end() {
+		$this->Controller->Cookie->destroy();
+	}
+
+/**
+ * test that initialize sets settings from components array
+ *
+ * @return void
+ */
+	function testInitialize() {
+		$settings = array(
+			'time' => '5 days',
+			'path' => '/'
+		);
+		$this->Controller->Cookie->initialize($this->Controller, $settings);
+		$this->assertEqual($this->Controller->Cookie->time, $settings['time']);
+		$this->assertEqual($this->Controller->Cookie->path, $settings['path']);
+	}
+
+/**
+ * testCookieName
+ *
+ * @access public
+ * @return void
+ */
+	function testCookieName() {
+		$this->assertEqual($this->Controller->Cookie->name, 'CakeTestCookie');
+	}
+
+/**
+ * testSettingEncryptedCookieData
+ *
+ * @access public
+ * @return void
+ */
+	function testSettingEncryptedCookieData() {
+		$this->Controller->Cookie->write('Encrytped_array', array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!'));
+		$this->Controller->Cookie->write('Encrytped_multi_cookies.name', 'CakePHP');
+		$this->Controller->Cookie->write('Encrytped_multi_cookies.version', '1.2.0.x');
+		$this->Controller->Cookie->write('Encrytped_multi_cookies.tag', 'CakePHP Rocks!');
+	}
+
+/**
+ * testReadEncryptedCookieData
+ *
+ * @access public
+ * @return void
+ */
+	function testReadEncryptedCookieData() {
+		$data = $this->Controller->Cookie->read('Encrytped_array');
+		$expected = array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!');
+		$this->assertEqual($data, $expected);
+
+		$data = $this->Controller->Cookie->read('Encrytped_multi_cookies');
+		$expected = array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!');
+		$this->assertEqual($data, $expected);
+	}
+
+/**
+ * testSettingPlainCookieData
+ *
+ * @access public
+ * @return void
+ */
+	function testSettingPlainCookieData() {
+		$this->Controller->Cookie->write('Plain_array', array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!'), false);
+		$this->Controller->Cookie->write('Plain_multi_cookies.name', 'CakePHP', false);
+		$this->Controller->Cookie->write('Plain_multi_cookies.version', '1.2.0.x', false);
+		$this->Controller->Cookie->write('Plain_multi_cookies.tag', 'CakePHP Rocks!', false);
+	}
+
+/**
+ * testReadPlainCookieData
+ *
+ * @access public
+ * @return void
+ */
+	function testReadPlainCookieData() {
+		$data = $this->Controller->Cookie->read('Plain_array');
+		$expected = array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!');
+		$this->assertEqual($data, $expected);
+
+		$data = $this->Controller->Cookie->read('Plain_multi_cookies');
+		$expected = array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!');
+		$this->assertEqual($data, $expected);
+	}
+
+/**
+ * testWritePlainCookieArray
+ *
+ * @access public
+ * @return void
+ */
+	function testWritePlainCookieArray() {
+		$this->Controller->Cookie->write(array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' => 'CakePHP Rocks!'), null, false);
+
+		$this->assertEqual($this->Controller->Cookie->read('name'), 'CakePHP');
+		$this->assertEqual($this->Controller->Cookie->read('version'), '1.2.0.x');
+		$this->assertEqual($this->Controller->Cookie->read('tag'), 'CakePHP Rocks!');
+
+		$this->Controller->Cookie->delete('name');
+		$this->Controller->Cookie->delete('version');
+		$this->Controller->Cookie->delete('tag');
+	}
+
+/**
+ * testReadingCookieValue
+ *
+ * @access public
+ * @return void
+ */
+	function testReadingCookieValue() {
+		$data = $this->Controller->Cookie->read();
+		$expected = array(
+			'Encrytped_array' => array(
+				'name' => 'CakePHP',
+				'version' => '1.2.0.x',
+				'tag' => 'CakePHP Rocks!'),
+			'Encrytped_multi_cookies' => array(
+				'name' => 'CakePHP',
+				'version' => '1.2.0.x',
+				'tag' => 'CakePHP Rocks!'),
+			'Plain_array' => array(
+				'name' => 'CakePHP',
+				'version' => '1.2.0.x',
+				'tag' => 'CakePHP Rocks!'),
+			'Plain_multi_cookies' => array(
+				'name' => 'CakePHP',
+				'version' => '1.2.0.x',
+				'tag' => 'CakePHP Rocks!'));
+		$this->assertEqual($data, $expected);
+	}
+
+/**
+ * testDeleteCookieValue
+ *
+ * @access public
+ * @return void
+ */
+	function testDeleteCookieValue() {
+		$this->Controller->Cookie->delete('Encrytped_multi_cookies.name');
+		$data = $this->Controller->Cookie->read('Encrytped_multi_cookies');
+		$expected = array('version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!');
+		$this->assertEqual($data, $expected);
+
+		$this->Controller->Cookie->delete('Encrytped_array');
+		$data = $this->Controller->Cookie->read('Encrytped_array');
+		$expected = array();
+		$this->assertEqual($data, $expected);
+
+		$this->Controller->Cookie->delete('Plain_multi_cookies.name');
+		$data = $this->Controller->Cookie->read('Plain_multi_cookies');
+		$expected = array('version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!');
+		$this->assertEqual($data, $expected);
+
+		$this->Controller->Cookie->delete('Plain_array');
+		$data = $this->Controller->Cookie->read('Plain_array');
+		$expected = array();
+		$this->assertEqual($data, $expected);
+	}
+
+/**
+ * testSettingCookiesWithArray
+ *
+ * @access public
+ * @return void
+ */
+	function testSettingCookiesWithArray() {
+		$this->Controller->Cookie->destroy();
+
+		$this->Controller->Cookie->write(array('Encrytped_array' => array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!')));
+		$this->Controller->Cookie->write(array('Encrytped_multi_cookies.name' => 'CakePHP'));
+		$this->Controller->Cookie->write(array('Encrytped_multi_cookies.version' => '1.2.0.x'));
+		$this->Controller->Cookie->write(array('Encrytped_multi_cookies.tag' => 'CakePHP Rocks!'));
+
+		$this->Controller->Cookie->write(array('Plain_array' => array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!')), null, false);
+		$this->Controller->Cookie->write(array('Plain_multi_cookies.name' => 'CakePHP'), null, false);
+		$this->Controller->Cookie->write(array('Plain_multi_cookies.version' => '1.2.0.x'), null, false);
+		$this->Controller->Cookie->write(array('Plain_multi_cookies.tag' => 'CakePHP Rocks!'), null, false);
+	}
+
+/**
+ * testReadingCookieArray
+ *
+ * @access public
+ * @return void
+ */
+	function testReadingCookieArray() {
+		$data = $this->Controller->Cookie->read('Encrytped_array.name');
+		$expected = 'CakePHP';
+		$this->assertEqual($data, $expected);
+
+		$data = $this->Controller->Cookie->read('Encrytped_array.version');
+		$expected = '1.2.0.x';
+		$this->assertEqual($data, $expected);
+
+		$data = $this->Controller->Cookie->read('Encrytped_array.tag');
+		$expected = 'CakePHP Rocks!';
+		$this->assertEqual($data, $expected);
+
+		$data = $this->Controller->Cookie->read('Encrytped_multi_cookies.name');
+		$expected = 'CakePHP';
+		$this->assertEqual($data, $expected);
+
+		$data = $this->Controller->Cookie->read('Encrytped_multi_cookies.version');
+		$expected = '1.2.0.x';
+		$this->assertEqual($data, $expected);
+
+		$data = $this->Controller->Cookie->read('Encrytped_multi_cookies.tag');
+		$expected = 'CakePHP Rocks!';
+		$this->assertEqual($data, $expected);
+
+		$data = $this->Controller->Cookie->read('Plain_array.name');
+		$expected = 'CakePHP';
+		$this->assertEqual($data, $expected);
+
+		$data = $this->Controller->Cookie->read('Plain_array.version');
+		$expected = '1.2.0.x';
+		$this->assertEqual($data, $expected);
+
+		$data = $this->Controller->Cookie->read('Plain_array.tag');
+		$expected = 'CakePHP Rocks!';
+		$this->assertEqual($data, $expected);
+
+		$data = $this->Controller->Cookie->read('Plain_multi_cookies.name');
+		$expected = 'CakePHP';
+		$this->assertEqual($data, $expected);
+
+		$data = $this->Controller->Cookie->read('Plain_multi_cookies.version');
+		$expected = '1.2.0.x';
+		$this->assertEqual($data, $expected);
+
+		$data = $this->Controller->Cookie->read('Plain_multi_cookies.tag');
+		$expected = 'CakePHP Rocks!';
+		$this->assertEqual($data, $expected);
+	}
+
+/**
+ * testReadingCookieDataOnStartup
+ *
+ * @access public
+ * @return void
+ */
+	function testReadingCookieDataOnStartup() {
+		$this->Controller->Cookie->destroy();
+
+		$data = $this->Controller->Cookie->read('Encrytped_array');
+		$expected = array();
+		$this->assertEqual($data, $expected);
+
+		$data = $this->Controller->Cookie->read('Encrytped_multi_cookies');
+		$expected = array();
+		$this->assertEqual($data, $expected);
+
+		$data = $this->Controller->Cookie->read('Plain_array');
+		$expected = array();
+		$this->assertEqual($data, $expected);
+
+		$data = $this->Controller->Cookie->read('Plain_multi_cookies');
+		$expected = array();
+		$this->assertEqual($data, $expected);
+
+		$_COOKIE['CakeTestCookie'] = array(
+				'Encrytped_array' => $this->__encrypt(array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!')),
+				'Encrytped_multi_cookies' => array(
+						'name' => $this->__encrypt('CakePHP'),
+						'version' => $this->__encrypt('1.2.0.x'),
+						'tag' => $this->__encrypt('CakePHP Rocks!')),
+				'Plain_array' => 'name|CakePHP,version|1.2.0.x,tag|CakePHP Rocks!',
+				'Plain_multi_cookies' => array(
+						'name' => 'CakePHP',
+						'version' => '1.2.0.x',
+						'tag' => 'CakePHP Rocks!'));
+		$this->Controller->Cookie->startup();
+
+		$data = $this->Controller->Cookie->read('Encrytped_array');
+		$expected = array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!');
+		$this->assertEqual($data, $expected);
+
+		$data = $this->Controller->Cookie->read('Encrytped_multi_cookies');
+		$expected = array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!');
+		$this->assertEqual($data, $expected);
+
+		$data = $this->Controller->Cookie->read('Plain_array');
+		$expected = array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!');
+		$this->assertEqual($data, $expected);
+
+		$data = $this->Controller->Cookie->read('Plain_multi_cookies');
+		$expected = array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!');
+		$this->assertEqual($data, $expected);
+		$this->Controller->Cookie->destroy();
+		unset($_COOKIE['CakeTestCookie']);
+	}
+
+/**
+ * testReadingCookieDataWithoutStartup
+ *
+ * @access public
+ * @return void
+ */
+	function testReadingCookieDataWithoutStartup() {
+		$data = $this->Controller->Cookie->read('Encrytped_array');
+		$expected = array();
+		$this->assertEqual($data, $expected);
+
+		$data = $this->Controller->Cookie->read('Encrytped_multi_cookies');
+		$expected = array();
+		$this->assertEqual($data, $expected);
+
+		$data = $this->Controller->Cookie->read('Plain_array');
+		$expected = array();
+		$this->assertEqual($data, $expected);
+
+		$data = $this->Controller->Cookie->read('Plain_multi_cookies');
+		$expected = array();
+		$this->assertEqual($data, $expected);
+
+		$_COOKIE['CakeTestCookie'] = array(
+				'Encrytped_array' => $this->__encrypt(array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!')),
+				'Encrytped_multi_cookies' => array(
+						'name' => $this->__encrypt('CakePHP'),
+						'version' => $this->__encrypt('1.2.0.x'),
+						'tag' => $this->__encrypt('CakePHP Rocks!')),
+				'Plain_array' => 'name|CakePHP,version|1.2.0.x,tag|CakePHP Rocks!',
+				'Plain_multi_cookies' => array(
+						'name' => 'CakePHP',
+						'version' => '1.2.0.x',
+						'tag' => 'CakePHP Rocks!'));
+
+		$data = $this->Controller->Cookie->read('Encrytped_array');
+		$expected = array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!');
+		$this->assertEqual($data, $expected);
+
+		$data = $this->Controller->Cookie->read('Encrytped_multi_cookies');
+		$expected = array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!');
+		$this->assertEqual($data, $expected);
+
+		$data = $this->Controller->Cookie->read('Plain_array');
+		$expected = array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!');
+		$this->assertEqual($data, $expected);
+
+		$data = $this->Controller->Cookie->read('Plain_multi_cookies');
+		$expected = array('name' => 'CakePHP', 'version' => '1.2.0.x', 'tag' =>'CakePHP Rocks!');
+		$this->assertEqual($data, $expected);
+		$this->Controller->Cookie->destroy();
+		unset($_COOKIE['CakeTestCookie']);
+	}
+
+
+/**
+ * test that no error is issued for non array data.
+ *
+ * @return void
+ */
+	function testNoErrorOnNonArrayData() {
+		$this->Controller->Cookie->destroy();
+		$_COOKIE['CakeTestCookie'] = 'kaboom';
+
+		$this->assertNull($this->Controller->Cookie->read('value'));
+	}
+
+/**
+ * test that deleting a top level keys kills the child elements too.
+ *
+ * @return void
+ */
+	function testDeleteRemovesChildren() {
+		$_COOKIE['CakeTestCookie'] = array(
+			'User' => array('email' => 'examp****@examp*****', 'name' => 'mark'),
+			'other' => 'value'
+		);
+		$this->Controller->Cookie->startup();
+		$this->assertEqual('mark', $this->Controller->Cookie->read('User.name'));
+
+		$this->Controller->Cookie->delete('User');
+		$this->assertFalse($this->Controller->Cookie->read('User.email'));
+		$this->Controller->Cookie->destroy();
+	}
+
+/**
+ * Test deleting recursively with keys that don't exist.
+ *
+ * @return void
+ */
+	function testDeleteChildrenNotExist() {
+		$this->assertNull($this->Controller->Cookie->delete('NotFound'));
+		$this->assertNull($this->Controller->Cookie->delete('Not.Found'));
+	}
+
+/**
+ * Test that 1.3 can read 2.0 format codes if json_encode exists.
+ *
+ * @return void
+ */
+	function testForwardsCompatibility() {
+		if ($this->skipIf(!function_exists('json_decode'), 'no json_decode, skipping.')) {
+			return;
+		}
+		$_COOKIE['CakeTestCookie'] = array(
+			'JSON' => '{"name":"value"}',
+			'Empty' => '',
+			'String' => '{"somewhat:"broken"}'
+		);
+		$this->Controller->Cookie->startup($this->Controller);
+		$this->assertEqual(array('name' => 'value'), $this->Controller->Cookie->read('JSON'));
+		$this->assertEqual('value', $this->Controller->Cookie->read('JSON.name'));
+		$this->assertEqual('', $this->Controller->Cookie->read('Empty'));
+		$this->assertEqual('{"somewhat:"broken"}', $this->Controller->Cookie->read('String'));
+	}
+
+/**
+ * encrypt method
+ *
+ * @param mixed $value
+ * @return string
+ * @access private
+ */
+	function __encrypt($value) {
+		if (is_array($value)) {
+			$value = $this->__implode($value);
+		}
+		return "Q2FrZQ==." . base64_encode(Security::cipher($value, $this->Controller->Cookie->key));
+	}
+
+/**
+ * implode method
+ *
+ * @param array $value
+ * @return string
+ * @access private
+ */
+	function __implode($array) {
+		$string = '';
+		foreach ($array as $key => $value) {
+			$string .= ',' . $key . '|' . $value;
+		}
+		return substr($string, 1);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/controller/components/email.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/controller/components/email.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/controller/components/email.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,1331 @@
+<?php
+/**
+ * EmailComponentTest file
+ *
+ * Series of tests for email component.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.cases.libs.controller.components
+ * @since         CakePHP(tm) v 1.2.0.5347
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Component', 'Email');
+App::import('Core', 'CakeSocket');
+
+Mock::generate('CakeSocket', 'MockEmailSocket');
+
+/**
+ * EmailTestComponent class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ */
+class EmailTestComponent extends EmailComponent {
+
+	var $smtpSend = '';
+/**
+ * smtpSend method override for testing
+ *
+ * @access public
+ * @return mixed
+ */
+	function smtpSend($data, $code = '250') {
+		return parent::_smtpSend($data, $code);
+	}
+
+/**
+ * undocumented function
+ *
+ * @return void
+ */
+	function _smtpSend($data, $code = '250') {
+		if ($this->_debug) {
+			$this->smtpSend .= $data . "\n";
+			return true;
+		}
+		return parent::_smtpSend($data, $code);
+	}
+
+/**
+ * Convenience setter method for testing.
+ *
+ * @access public
+ * @return void
+ */
+	function setConnectionSocket(&$socket) {
+		$this->__smtpConnection = $socket;
+	}
+
+/**
+ * Allows mocks to be used with tests.
+ *
+ * @param array $config 
+ * @return void
+ */
+	function _getSocket($config) {
+		if (empty($this->__smtpConnection)) {
+			parent::_getSocket($config);
+		}
+	}
+
+/**
+ * Convenience getter method for testing.
+ *
+ * @access public
+ * @return mixed
+ */
+	function getConnectionSocket() {
+		return $this->__smtpConnection;
+	}
+
+/**
+ * Convenience setter for testing.
+ *
+ * @access public
+ * @return void
+ */
+	function setHeaders($headers) {
+		$this->__header += $headers;
+	}
+
+/**
+ * Convenience getter for testing.
+ *
+ * @access public
+ * @return array
+ */
+	function getHeaders() {
+		return $this->__header;
+	}
+
+/**
+ * Convenience setter for testing.
+ *
+ * @access public
+ * @return void
+ */
+	function setBoundary() {
+		$this->__createBoundary();
+	}
+
+/**
+ * Convenience getter for testing.
+ *
+ * @access public
+ * @return string
+ */
+	function getBoundary() {
+		return $this->__boundary;
+	}
+
+/**
+ * Convenience getter for testing.
+ *
+ * @access public
+ * @return string
+ */
+	function getMessage() {
+		return $this->__message;
+	}
+
+/**
+ * Convenience getter for testing.
+ *
+ * @access protected
+ * @return string
+ */
+	function _getMessage() {
+		return $this->__message;
+	}
+
+/**
+ * Convenience method for testing.
+ *
+ * @access public
+ * @return string
+ */
+	function strip($content, $message = false) {
+		return parent::_strip($content, $message);
+	}
+
+/**
+ * Wrapper for testing.
+ *
+ * @return void
+ */
+	function formatAddress($string, $smtp = false) {
+		return parent::_formatAddress($string, $smtp);
+	}
+}
+
+/**
+ * EmailTestController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ */
+class EmailTestController extends Controller {
+
+/**
+ * name property
+ *
+ * @var string 'EmailTest'
+ * @access public
+ */
+	var $name = 'EmailTest';
+
+/**
+ * uses property
+ *
+ * @var mixed null
+ * @access public
+ */
+	var $uses = null;
+
+/**
+ * components property
+ *
+ * @var array
+ * @access public
+ */
+	var $components = array('Session', 'EmailTest');
+
+/**
+ * pageTitle property
+ *
+ * @var string
+ * @access public
+ */
+	var $pageTitle = 'EmailTest';
+}
+
+/**
+ * EmailTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ */
+class EmailComponentTest extends CakeTestCase {
+
+/**
+ * Controller property
+ *
+ * @var EmailTestController
+ * @access public
+ */
+	var $Controller;
+
+/**
+ * name property
+ *
+ * @var string 'Email'
+ * @access public
+ */
+	var $name = 'Email';
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		$this->_appEncoding = Configure::read('App.encoding');
+		Configure::write('App.encoding', 'UTF-8');
+
+		$this->Controller =& new EmailTestController();
+
+		restore_error_handler();
+		@$this->Controller->Component->init($this->Controller);
+		set_error_handler('simpleTestErrorHandler');
+
+		$this->Controller->EmailTest->initialize($this->Controller, array());
+		ClassRegistry::addObject('view', new View($this->Controller));
+
+		App::build(array(
+			'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS)
+		));
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		Configure::write('App.encoding', $this->_appEncoding);
+		App::build();
+		$this->Controller->Session->delete('Message');
+		restore_error_handler();
+		ClassRegistry::flush();
+	}
+
+/**
+ * osFix method
+ *
+ * @param string $string
+ * @access private
+ * @return string
+ */
+	function __osFix($string) {
+		return str_replace(array("\r\n", "\r"), "\n", $string);
+	}
+
+/**
+ * testSmtpConfig method
+ *
+ * @access public
+ * @return void
+ */
+	function testSmtpConfig() {
+		if ($this->skipIf(!@fsockopen('localhost', 25), '%s No SMTP server running on localhost')) {
+			return;
+		}
+		$this->Controller->EmailTest->delivery = 'smtp';
+		$this->Controller->EmailTest->smtpOptions = array();
+		$this->Controller->EmailTest->send('anything');
+		$config = array(
+			'host' => 'localhost',
+			'port' => 25,
+			'protocol' => 'smtp',
+			'timeout' => 30
+		);
+		$this->assertEqual($config, $this->Controller->EmailTest->smtpOptions);
+
+		$this->Controller->EmailTest->smtpOptions = array('port' => 80);
+		$this->Controller->EmailTest->send('anything');
+		$config['port'] = 80;
+		$this->assertEqual($config, $this->Controller->EmailTest->smtpOptions);
+	}
+
+/**
+ * testBadSmtpSend method
+ *
+ * @access public
+ * @return void
+ */
+	function testBadSmtpSend() {
+		if ($this->skipIf(!@fsockopen('localhost', 25), '%s No SMTP server running on localhost')) {
+			return;
+		}
+		$this->Controller->EmailTest->smtpOptions['host'] = 'blah';
+		$this->Controller->EmailTest->delivery = 'smtp';
+		$this->assertFalse($this->Controller->EmailTest->send('Should not work'));
+	}
+
+/**
+ * testSmtpSend method
+ *
+ * @access public
+ * @return void
+ */
+	function testSmtpSend() {
+		if ($this->skipIf(!@fsockopen('localhost', 25), '%s No SMTP server running on localhost')) {
+			return;
+		}
+
+		$this->Controller->EmailTest->to = 'postmaster @ localhost';
+		$this->Controller->EmailTest->from = 'norep****@examp*****';
+		$this->Controller->EmailTest->subject = 'Cake SMTP test';
+		$this->Controller->EmailTest->replyTo = 'norep****@examp*****';
+		$this->Controller->EmailTest->template = null;
+
+		$this->Controller->EmailTest->delivery = 'smtp';
+		$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+
+		$this->Controller->EmailTest->_debug = true;
+		$this->Controller->EmailTest->sendAs = 'text';
+		$expect = <<<TEMPDOC
+<pre>Host: localhost
+Port: 25
+Timeout: 30
+To: postmaster @ localhost
+From: norep****@examp*****
+Subject: Cake SMTP test
+Header:
+
+To: postmaster @ localhost
+From: norep****@examp*****
+Reply-To: norep****@examp*****
+Subject: Cake SMTP test
+X-Mailer: CakePHP Email Component
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 7bitParameters:
+
+Message:
+
+This is the body of the message
+
+</pre>
+TEMPDOC;
+		$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+		$this->assertEqual($this->Controller->Session->read('Message.email.message'), $this->__osFix($expect));
+	}
+
+/**
+ * testSmtpEhlo method
+ *
+ * @access public
+ * @return void
+ */
+	function testSmtpEhlo() {
+		if ($this->skipIf(!@fsockopen('localhost', 25), '%s No SMTP server running on localhost')) {
+			return;
+		}
+
+		$connection =& new CakeSocket(array('protocol'=>'smtp', 'host' => 'localhost', 'port' => 25));
+		$this->Controller->EmailTest->setConnectionSocket($connection);
+		$this->Controller->EmailTest->smtpOptions['timeout'] = 10;
+		$this->assertTrue($connection->connect());
+		$this->assertTrue($this->Controller->EmailTest->smtpSend(null, '220') !== false);
+		$this->skipIf($this->Controller->EmailTest->smtpSend('EHLO locahost', '250') === false, '%s do not support EHLO.');
+		$connection->disconnect();
+
+		$this->Controller->EmailTest->to = 'postmaster @ localhost';
+		$this->Controller->EmailTest->from = 'norep****@examp*****';
+		$this->Controller->EmailTest->subject = 'Cake SMTP test';
+		$this->Controller->EmailTest->replyTo = 'norep****@examp*****';
+		$this->Controller->EmailTest->template = null;
+
+		$this->Controller->EmailTest->delivery = 'smtp';
+		$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+
+		$this->Controller->EmailTest->_debug = true;
+		$this->Controller->EmailTest->sendAs = 'text';
+		$expect = <<<TEMPDOC
+<pre>Host: localhost
+Port: 25
+Timeout: 30
+To: postmaster @ localhost
+From: norep****@examp*****
+Subject: Cake SMTP test
+Header:
+
+To: postmaster @ localhost
+From: norep****@examp*****
+Reply-To: norep****@examp*****
+Subject: Cake SMTP test
+X-Mailer: CakePHP Email Component
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 7bitParameters:
+
+Message:
+
+This is the body of the message
+
+</pre>
+TEMPDOC;
+		$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+		$this->assertEqual($this->Controller->Session->read('Message.email.message'), $this->__osFix($expect));
+	}
+
+/**
+ * testSmtpSendMultipleTo method
+ *
+ * @access public
+ * @return void
+ */
+	function testSmtpSendMultipleTo() {
+		if ($this->skipIf(!@fsockopen('localhost', 25), '%s No SMTP server running on localhost')) {
+			return;
+		}
+		$this->Controller->EmailTest->reset();
+		$this->Controller->EmailTest->to = array('postmaster @ localhost', 'root @ localhost');
+		$this->Controller->EmailTest->from = 'norep****@examp*****';
+		$this->Controller->EmailTest->subject = 'Cake SMTP multiple To test';
+		$this->Controller->EmailTest->replyTo = 'norep****@examp*****';
+		$this->Controller->EmailTest->template = null;
+		$this->Controller->EmailTest->_debug = true;
+		$this->Controller->EmailTest->sendAs = 'text';
+		$this->Controller->EmailTest->delivery = 'smtp';
+		
+		$socket = new MockEmailSocket();
+		$socket->setReturnValue('connect', true);
+		$this->Controller->EmailTest->setConnectionSocket($socket);
+
+		$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+
+		$this->assertPattern('/EHLO localhost\n/', $this->Controller->EmailTest->smtpSend);
+		$this->assertPattern('/MAIL FROM: <noreply @ example\.com>\n/', $this->Controller->EmailTest->smtpSend);
+		$this->assertPattern('/RCPT TO: <postmaster @ localhost>\n/', $this->Controller->EmailTest->smtpSend);
+		$this->assertPattern('/RCPT TO: <root @ localhost>\n/', $this->Controller->EmailTest->smtpSend);
+		$this->assertPattern(
+			'/To: postmaster @ localhost, root @ localhost[\n\r]/', 
+			$this->Controller->EmailTest->smtpSend
+		);
+	}
+
+/**
+ * test sending smtp from a host using a port.
+ *
+ * @return void
+ */
+	function testSmtpSendHostWithPort() {
+		$bkp = env('HTTP_HOST');
+		$_SERVER['HTTP_HOST'] = 'localhost:8080';
+
+		$this->Controller->EmailTest->reset();
+		$this->Controller->EmailTest->to = array('root @ localhost');
+		$this->Controller->EmailTest->from = 'norep****@examp*****';
+		$this->Controller->EmailTest->subject = 'Cake SMTP host test';
+		$this->Controller->EmailTest->replyTo = 'norep****@examp*****';
+		$this->Controller->EmailTest->template = null;
+		$this->Controller->EmailTest->delivery = 'smtp';
+		$this->Controller->EmailTest->sendAs = 'text';
+		$this->Controller->EmailTest->_debug = true;
+
+		$socket = new MockEmailSocket();
+		$socket->setReturnValue('connect', true);
+
+		$this->Controller->EmailTest->setConnectionSocket($socket);
+		$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+
+		$this->assertPattern('/EHLO localhost\n/', $this->Controller->EmailTest->smtpSend);
+
+		$_SERVER['HTTP_HOST'] = $bkp;
+	}
+
+/**
+ * testAuthenticatedSmtpSend method
+ *
+ * @access public
+ * @return void
+ */
+	function testAuthenticatedSmtpSend() {
+		if ($this->skipIf(!@fsockopen('localhost', 25), '%s No SMTP server running on localhost')) {
+			return;
+		}
+
+		$this->Controller->EmailTest->to = 'postmaster @ localhost';
+		$this->Controller->EmailTest->from = 'norep****@examp*****';
+		$this->Controller->EmailTest->subject = 'Cake SMTP test';
+		$this->Controller->EmailTest->replyTo = 'norep****@examp*****';
+		$this->Controller->EmailTest->template = null;
+		$this->Controller->EmailTest->smtpOptions['username'] = 'test';
+		$this->Controller->EmailTest->smtpOptions['password'] = 'testing';
+
+		$this->Controller->EmailTest->delivery = 'smtp';
+		$result = $this->Controller->EmailTest->send('This is the body of the message');
+		$code = substr($this->Controller->EmailTest->smtpError, 0, 3);
+		$this->skipIf(!$code, '%s Authentication not enabled on server');
+
+		$this->assertFalse($result);
+		$this->assertEqual($code, '535');
+	}
+
+/**
+ * testSendFormats method
+ *
+ * @access public
+ * @return void
+ */
+	function testSendFormats() {
+		$this->Controller->EmailTest->to = 'postmaster @ localhost';
+		$this->Controller->EmailTest->from = 'norep****@examp*****';
+		$this->Controller->EmailTest->subject = 'Cake SMTP test';
+		$this->Controller->EmailTest->replyTo = 'norep****@examp*****';
+		$this->Controller->EmailTest->template = null;
+		$this->Controller->EmailTest->delivery = 'debug';
+		$this->Controller->EmailTest->messageId = false;
+
+		$date = date(DATE_RFC2822);
+		$message = <<<MSGBLOC
+<pre>To: postmaster @ localhost
+From: norep****@examp*****
+Subject: Cake SMTP test
+Header:
+
+From: norep****@examp*****
+Reply-To: norep****@examp*****
+Date: $date
+X-Mailer: CakePHP Email Component
+Content-Type: {CONTENTTYPE}
+Content-Transfer-Encoding: 7bitParameters:
+
+Message:
+
+This is the body of the message
+
+</pre>
+MSGBLOC;
+
+		$this->Controller->EmailTest->sendAs = 'text';
+		$expect = str_replace('{CONTENTTYPE}', 'text/plain; charset=UTF-8', $message);
+		$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+		$this->assertEqual($this->Controller->Session->read('Message.email.message'), $this->__osFix($expect));
+
+		$this->Controller->EmailTest->sendAs = 'html';
+		$expect = str_replace('{CONTENTTYPE}', 'text/html; charset=UTF-8', $message);
+		$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+		$this->assertEqual($this->Controller->Session->read('Message.email.message'), $this->__osFix($expect));
+
+		// TODO: better test for format of message sent?
+		$this->Controller->EmailTest->sendAs = 'both';
+		$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+		$boundary = $this->Controller->EmailTest->getBoundary();
+
+		$expect = str_replace('{CONTENTTYPE}', 'multipart/alternative; boundary="alt-' . $boundary . '"', $message);
+		$this->assertEqual($this->Controller->Session->read('Message.email.message'), $this->__osFix($expect));
+	}
+
+/**
+ * testTemplates method
+ *
+ * @access public
+ * @return void
+ */
+	function testTemplates() {
+		ClassRegistry::flush();
+
+		$this->Controller->EmailTest->to = 'postmaster @ localhost';
+		$this->Controller->EmailTest->from = 'norep****@examp*****';
+		$this->Controller->EmailTest->subject = 'Cake SMTP test';
+		$this->Controller->EmailTest->replyTo = 'norep****@examp*****';
+
+		$this->Controller->EmailTest->delivery = 'debug';
+		$this->Controller->EmailTest->messageId = false;
+
+		$date = date(DATE_RFC2822);
+		$header = <<<HEADBLOC
+To: postmaster @ localhost
+From: norep****@examp*****
+Subject: Cake SMTP test
+Header:
+
+From: norep****@examp*****
+Reply-To: norep****@examp*****
+Date: $date
+X-Mailer: CakePHP Email Component
+Content-Type: {CONTENTTYPE}
+Content-Transfer-Encoding: 7bitParameters:
+
+Message:
+
+
+HEADBLOC;
+
+		$this->Controller->EmailTest->layout = 'default';
+		$this->Controller->EmailTest->template = 'default';
+
+		$text = <<<TEXTBLOC
+
+This is the body of the message
+
+This email was sent using the CakePHP Framework, http://cakephp.org.
+TEXTBLOC;
+
+		$html = <<<HTMLBLOC
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
+
+<html>
+<head>
+	<title>Email Test</title>
+</head>
+
+<body>
+	<p> This is the body of the message</p><p> </p>
+	<p>This email was sent using the <a href="http://cakephp.org">CakePHP Framework</a></p>
+</body>
+</html>
+HTMLBLOC;
+
+		$this->Controller->EmailTest->sendAs = 'text';
+		$expect = '<pre>' . str_replace('{CONTENTTYPE}', 'text/plain; charset=UTF-8', $header) . $text . "\n" . '</pre>';
+		$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+		$this->assertEqual($this->Controller->Session->read('Message.email.message'), $this->__osFix($expect));
+
+		$this->Controller->EmailTest->sendAs = 'html';
+		$expect = '<pre>' . str_replace('{CONTENTTYPE}', 'text/html; charset=UTF-8', $header) . $html . "\n" . '</pre>';
+		$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+		$this->assertEqual($this->Controller->Session->read('Message.email.message'), $this->__osFix($expect));
+
+		$this->Controller->EmailTest->sendAs = 'both';
+		$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+		$boundary = $this->Controller->EmailTest->getBoundary();
+
+		$expect = str_replace('{CONTENTTYPE}', 'multipart/alternative; boundary="alt-' . $boundary . '"', $header);
+		$expect .= '--alt-' . $boundary . "\n" . 'Content-Type: text/plain; charset=UTF-8' . "\n" . 'Content-Transfer-Encoding: 7bit' . "\n\n" . $text . "\n\n";
+		$expect .= '--alt-' . $boundary . "\n" . 'Content-Type: text/html; charset=UTF-8' . "\n" . 'Content-Transfer-Encoding: 7bit' . "\n\n" . $html . "\n\n";
+		$expect = '<pre>' . $expect . "--alt-$boundary--" . "\n\n" . '</pre>';
+		$this->assertEqual($this->Controller->Session->read('Message.email.message'), $this->__osFix($expect));
+
+		$this->Controller->EmailTest->reset();
+		$this->Controller->EmailTest->to = 'postmaster @ localhost';
+		$this->Controller->EmailTest->from = 'norep****@examp*****';
+		$this->Controller->EmailTest->subject = 'Cake SMTP test';
+		$this->Controller->EmailTest->replyTo = 'norep****@examp*****';
+
+		$this->Controller->EmailTest->delivery = 'debug';
+		$this->Controller->EmailTest->messageId = false;
+
+		$html = <<<HTMLBLOC
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
+
+<html>
+<head>
+	<title>Email Test</title>
+</head>
+
+<body>
+	<p> This is the body of the message</p><p> </p>
+	<p>This email was sent using the CakePHP Framework</p>
+</body>
+</html>
+
+HTMLBLOC;
+
+		$this->Controller->EmailTest->sendAs = 'html';
+		$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message', 'default', 'thin'));
+
+		$boundary = $this->Controller->EmailTest->getBoundary();
+		$expect = str_replace('{CONTENTTYPE}', 'text/html; charset=UTF-8', $header) . $html;
+		$expect = '<pre>' . $expect . '</pre>';
+		$this->assertEqual($this->Controller->Session->read('Message.email.message'), $this->__osFix($expect));
+		
+		$result = ClassRegistry::getObject('view');
+		$this->assertFalse($result);
+	}
+
+/**
+ * test that elements used in email templates get helpers.
+ *
+ * @return void
+ */
+	function testTemplateNestedElements() {
+		$this->Controller->EmailTest->to = 'postmaster @ localhost';
+		$this->Controller->EmailTest->from = 'norep****@examp*****';
+		$this->Controller->EmailTest->subject = 'Cake SMTP test';
+		$this->Controller->EmailTest->replyTo = 'norep****@examp*****';
+
+		$this->Controller->EmailTest->delivery = 'debug';
+		$this->Controller->EmailTest->messageId = false;
+		$this->Controller->EmailTest->layout = 'default';
+		$this->Controller->EmailTest->template = 'nested_element';
+		$this->Controller->EmailTest->sendAs = 'html';
+		$this->Controller->helpers = array('Html');
+
+		$this->Controller->EmailTest->send();
+		$result = $this->Controller->Session->read('Message.email.message');
+		$this->assertPattern('/Test/', $result);
+		$this->assertPattern('/http\:\/\/example\.com/', $result);
+	}
+
+/**
+ * testSmtpSendSocket method
+ *
+ * @access public
+ * @return void
+ */
+	function testSmtpSendSocket() {
+		if ($this->skipIf(!@fsockopen('localhost', 25), '%s No SMTP server running on localhost')) {
+			return;
+		}
+
+		$this->Controller->EmailTest->smtpOptions['timeout'] = 10;
+		$socket =& new CakeSocket(array_merge(array('protocol'=>'smtp'), $this->Controller->EmailTest->smtpOptions));
+		$this->Controller->EmailTest->setConnectionSocket($socket);
+
+		$this->assertTrue($this->Controller->EmailTest->getConnectionSocket());
+
+		$response = $this->Controller->EmailTest->smtpSend('HELO', '250');
+		$this->assertPattern('/501 Syntax: HELO hostname/', $this->Controller->EmailTest->smtpError);
+
+		$this->Controller->EmailTest->reset();
+		$response = $this->Controller->EmailTest->smtpSend('HELO somehostname', '250');
+		$this->assertNoPattern('/501 Syntax: HELO hostname/', $this->Controller->EmailTest->smtpError);
+	}
+
+/**
+ * testSendDebug method
+ *
+ * @access public
+ * @return void
+ */
+	function testSendDebug() {
+		$this->Controller->EmailTest->to = 'postmaster @ localhost';
+		$this->Controller->EmailTest->from = 'norep****@examp*****';
+		$this->Controller->EmailTest->cc = 'cc****@examp*****';
+		$this->Controller->EmailTest->bcc = 'bcc****@examp*****';
+		$this->Controller->EmailTest->subject = 'Cake Debug Test';
+		$this->Controller->EmailTest->replyTo = 'norep****@examp*****';
+		$this->Controller->EmailTest->template = null;
+
+		$this->Controller->EmailTest->delivery = 'debug';
+		$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+		$result = $this->Controller->Session->read('Message.email.message');
+
+		$this->assertPattern('/To: postmaster @ localhost\n/', $result);
+		$this->assertPattern('/Subject: Cake Debug Test\n/', $result);
+		$this->assertPattern('/Reply-To: norep****@examp*****\n/', $result);
+		$this->assertPattern('/From: norep****@examp*****\n/', $result);
+		$this->assertPattern('/Cc: cc****@examp*****\n/', $result);
+		$this->assertPattern('/Bcc: bcc****@examp*****\n/', $result);
+		$this->assertPattern('/Date: ' . preg_quote(date(DATE_RFC2822)) . '\n/', $result);
+		$this->assertPattern('/X-Mailer: CakePHP Email Component\n/', $result);
+		$this->assertPattern('/Content-Type: text\/plain; charset=UTF-8\n/', $result);
+		$this->assertPattern('/Content-Transfer-Encoding: 7bitParameters:\n/', $result);
+		$this->assertPattern('/This is the body of the message/', $result);
+	}
+
+/**
+ * test send with delivery = debug and not using sessions.
+ *
+ * @return void
+ */
+	function testSendDebugWithNoSessions() {
+		$session =& $this->Controller->Session;
+		unset($this->Controller->Session);
+		$this->Controller->EmailTest->to = 'postmaster @ localhost';
+		$this->Controller->EmailTest->from = 'norep****@examp*****';
+		$this->Controller->EmailTest->subject = 'Cake Debug Test';
+		$this->Controller->EmailTest->replyTo = 'norep****@examp*****';
+		$this->Controller->EmailTest->template = null;
+
+		$this->Controller->EmailTest->delivery = 'debug';
+		$result = $this->Controller->EmailTest->send('This is the body of the message');
+
+		$this->assertPattern('/To: postmaster @ localhost\n/', $result);
+		$this->assertPattern('/Subject: Cake Debug Test\n/', $result);
+		$this->assertPattern('/Reply-To: norep****@examp*****\n/', $result);
+		$this->assertPattern('/From: norep****@examp*****\n/', $result);
+		$this->assertPattern('/Date: ' . preg_quote(date(DATE_RFC2822)) . '\n/', $result);
+		$this->assertPattern('/X-Mailer: CakePHP Email Component\n/', $result);
+		$this->assertPattern('/Content-Type: text\/plain; charset=UTF-8\n/', $result);
+		$this->assertPattern('/Content-Transfer-Encoding: 7bitParameters:\n/', $result);
+		$this->assertPattern('/This is the body of the message/', $result);
+		$this->Controller->Session = $session;
+	}
+
+/**
+ * testMessageRetrievalWithoutTemplate method
+ *
+ * @access public
+ * @return void
+ */
+	function testMessageRetrievalWithoutTemplate() {
+		App::build(array(
+			'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS)
+		));
+
+		$this->Controller->EmailTest->to = 'postmaster @ localhost';
+		$this->Controller->EmailTest->from = 'norep****@examp*****';
+		$this->Controller->EmailTest->subject = 'Cake Debug Test';
+		$this->Controller->EmailTest->replyTo = 'norep****@examp*****';
+		$this->Controller->EmailTest->layout = 'default';
+		$this->Controller->EmailTest->template = null;
+
+		$this->Controller->EmailTest->delivery = 'debug';
+
+		$text = $html = 'This is the body of the message';
+
+		$this->Controller->EmailTest->sendAs = 'both';
+		$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+		$this->assertEqual($this->Controller->EmailTest->textMessage, $this->__osFix($text));
+		$this->assertEqual($this->Controller->EmailTest->htmlMessage, $this->__osFix($html));
+
+		$this->Controller->EmailTest->sendAs = 'text';
+		$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+		$this->assertEqual($this->Controller->EmailTest->textMessage, $this->__osFix($text));
+		$this->assertNull($this->Controller->EmailTest->htmlMessage);
+
+		$this->Controller->EmailTest->sendAs = 'html';
+		$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+		$this->assertNull($this->Controller->EmailTest->textMessage);
+		$this->assertEqual($this->Controller->EmailTest->htmlMessage, $this->__osFix($html));
+	}
+
+/**
+ * testMessageRetrievalWithTemplate method
+ *
+ * @access public
+ * @return void
+ */
+	function testMessageRetrievalWithTemplate() {
+		App::build(array(
+			'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS)
+		));
+
+		$this->Controller->set('value', 22091985);
+		$this->Controller->set('title_for_layout', 'EmailTest');
+
+		$this->Controller->EmailTest->to = 'postmaster @ localhost';
+		$this->Controller->EmailTest->from = 'norep****@examp*****';
+		$this->Controller->EmailTest->subject = 'Cake Debug Test';
+		$this->Controller->EmailTest->replyTo = 'norep****@examp*****';
+		$this->Controller->EmailTest->layout = 'default';
+		$this->Controller->EmailTest->template = 'custom';
+
+		$this->Controller->EmailTest->delivery = 'debug';
+
+		$text = <<<TEXTBLOC
+
+Here is your value: 22091985
+This email was sent using the CakePHP Framework, http://cakephp.org.
+TEXTBLOC;
+
+		$html = <<<HTMLBLOC
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
+
+<html>
+<head>
+	<title>EmailTest</title>
+</head>
+
+<body>
+	<p>Here is your value: <b>22091985</b></p>
+	<p>This email was sent using the <a href="http://cakephp.org">CakePHP Framework</a></p>
+</body>
+</html>
+HTMLBLOC;
+
+		$this->Controller->EmailTest->sendAs = 'both';
+		$this->assertTrue($this->Controller->EmailTest->send());
+		$this->assertEqual($this->Controller->EmailTest->textMessage, $this->__osFix($text));
+		$this->assertEqual($this->Controller->EmailTest->htmlMessage, $this->__osFix($html));
+
+		$this->Controller->EmailTest->sendAs = 'text';
+		$this->assertTrue($this->Controller->EmailTest->send());
+		$this->assertEqual($this->Controller->EmailTest->textMessage, $this->__osFix($text));
+		$this->assertNull($this->Controller->EmailTest->htmlMessage);
+
+		$this->Controller->EmailTest->sendAs = 'html';
+		$this->assertTrue($this->Controller->EmailTest->send());
+		$this->assertNull($this->Controller->EmailTest->textMessage);
+		$this->assertEqual($this->Controller->EmailTest->htmlMessage, $this->__osFix($html));
+	}
+
+/**
+ * testContentArray method
+ *
+ * @access public
+ * @return void
+ */
+	function testSendContentArray() {
+		$this->Controller->EmailTest->to = 'postmaster @ localhost';
+		$this->Controller->EmailTest->from = 'norep****@examp*****';
+		$this->Controller->EmailTest->subject = 'Cake Debug Test';
+		$this->Controller->EmailTest->replyTo = 'norep****@examp*****';
+		$this->Controller->EmailTest->template = null;
+		$this->Controller->EmailTest->delivery = 'debug';
+
+		$content = array('First line', 'Second line', 'Third line');
+		$this->assertTrue($this->Controller->EmailTest->send($content));
+		$result = $this->Controller->Session->read('Message.email.message');
+
+		$this->assertPattern('/To: postmaster @ localhost\n/', $result);
+		$this->assertPattern('/Subject: Cake Debug Test\n/', $result);
+		$this->assertPattern('/Reply-To: norep****@examp*****\n/', $result);
+		$this->assertPattern('/From: norep****@examp*****\n/', $result);
+		$this->assertPattern('/X-Mailer: CakePHP Email Component\n/', $result);
+		$this->assertPattern('/Content-Type: text\/plain; charset=UTF-8\n/', $result);
+		$this->assertPattern('/Content-Transfer-Encoding: 7bitParameters:\n/', $result);
+		$this->assertPattern('/First line\n/', $result);
+		$this->assertPattern('/Second line\n/', $result);
+		$this->assertPattern('/Third line\n/', $result);
+	}
+
+/**
+ * test setting a custom date.
+ *
+ * @return void
+ */
+	function testDateProperty() {
+		$this->Controller->EmailTest->to = 'postmaster @ localhost';
+		$this->Controller->EmailTest->from = 'norep****@examp*****';
+		$this->Controller->EmailTest->subject = 'Cake Debug Test';
+		$this->Controller->EmailTest->date = 'Today!';
+		$this->Controller->EmailTest->template = null;
+		$this->Controller->EmailTest->delivery = 'debug';
+
+		$this->assertTrue($this->Controller->EmailTest->send('test message'));
+		$result = $this->Controller->Session->read('Message.email.message');
+		$this->assertPattern('/Date: Today!\n/', $result);
+	}
+
+/**
+ * testContentStripping method
+ *
+ * @access public
+ * @return void
+ */
+	function testContentStripping() {
+		$content = "Previous content\n--alt-\nContent-TypeContent-Type:: text/html; charsetcharset==utf-8\nContent-Transfer-Encoding: 7bit";
+		$content .= "\n\n<p>My own html content</p>";
+
+		$result = $this->Controller->EmailTest->strip($content, true);
+		$expected = "Previous content\n--alt-\n text/html; utf-8\n 7bit\n\n<p>My own html content</p>";
+		$this->assertEqual($result, $expected);
+
+		$content = '<p>Some HTML content with an <a href="mailto:test****@examp*****">email link</a>';
+		$result  = $this->Controller->EmailTest->strip($content, true);
+		$expected = $content;
+		$this->assertEqual($result, $expected);
+
+		$content  = '<p>Some HTML content with an ';
+		$content .= '<a href="mailto:test****@examp*****,test2****@examp*****">email link</a>';
+		$result  = $this->Controller->EmailTest->strip($content, true);
+		$expected = $content;
+		$this->assertEqual($result, $expected);
+
+		$content = 'This is a test email to: you and whomever you forward it to';
+		$result  = $this->Controller->EmailTest->strip($content, true);
+		$expected = $content;
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that the _encode() will set mb_internal_encoding.
+ *
+ * @return void
+ */
+	function test_encodeSettingInternalCharset() {
+		$skip = !function_exists('mb_internal_encoding');
+		if ($this->skipIf($skip, 'Missing mb_* functions, cannot run test.')) {
+			return;
+		}
+		mb_internal_encoding('ISO-8859-1');
+
+		$this->Controller->charset = 'UTF-8';
+		$this->Controller->EmailTest->to = 'postmaster @ localhost';
+		$this->Controller->EmailTest->from = 'norep****@examp*****';
+		$this->Controller->EmailTest->subject = 'هذه رسالة بعنوان طويل مرسل للمستلم';
+		$this->Controller->EmailTest->replyTo = 'norep****@examp*****';
+		$this->Controller->EmailTest->template = null;
+		$this->Controller->EmailTest->delivery = 'debug';
+
+		$this->Controller->EmailTest->sendAs = 'text';
+		$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+
+		$subject = '=?UTF-8?B?2YfYsNmHINix2LPYp9mE2Kkg2KjYudmG2YjYp9mGINi32YjZitmEINmF2LE=?=' . "\r\n" . ' =?UTF-8?B?2LPZhCDZhNmE2YXYs9iq2YTZhQ==?=';
+
+		preg_match('/Subject: (.*)Header:/s', $this->Controller->Session->read('Message.email.message'), $matches);
+		$this->assertEqual(trim($matches[1]), $subject);
+
+		$result = mb_internal_encoding();
+		$this->assertEqual($result, 'ISO-8859-1');
+	}
+
+/**
+ * testMultibyte method
+ *
+ * @access public
+ * @return void
+ */
+	function testMultibyte() {
+		$this->Controller->charset = 'UTF-8';
+		$this->Controller->EmailTest->to = 'postmaster @ localhost';
+		$this->Controller->EmailTest->from = 'norep****@examp*****';
+		$this->Controller->EmailTest->subject = 'هذه رسالة بعنوان طويل مرسل للمستلم';
+		$this->Controller->EmailTest->replyTo = 'norep****@examp*****';
+		$this->Controller->EmailTest->template = null;
+		$this->Controller->EmailTest->delivery = 'debug';
+
+		$subject = '=?UTF-8?B?2YfYsNmHINix2LPYp9mE2Kkg2KjYudmG2YjYp9mGINi32YjZitmEINmF2LE=?=' . "\r\n" . ' =?UTF-8?B?2LPZhCDZhNmE2YXYs9iq2YTZhQ==?=';
+
+		$this->Controller->EmailTest->sendAs = 'text';
+		$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+		preg_match('/Subject: (.*)Header:/s', $this->Controller->Session->read('Message.email.message'), $matches);
+		$this->assertEqual(trim($matches[1]), $subject);
+
+		$this->Controller->EmailTest->sendAs = 'html';
+		$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+		preg_match('/Subject: (.*)Header:/s', $this->Controller->Session->read('Message.email.message'), $matches);
+		$this->assertEqual(trim($matches[1]), $subject);
+
+		$this->Controller->EmailTest->sendAs = 'both';
+		$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+		preg_match('/Subject: (.*)Header:/s', $this->Controller->Session->read('Message.email.message'), $matches);
+		$this->assertEqual(trim($matches[1]), $subject);
+	}
+
+/**
+ * undocumented function
+ *
+ * @return void
+ * @access public
+ */
+	function testSendWithAttachments() {
+		$this->Controller->EmailTest->to = 'postmaster @ localhost';
+		$this->Controller->EmailTest->from = 'norep****@examp*****';
+		$this->Controller->EmailTest->subject = 'Attachment Test';
+		$this->Controller->EmailTest->replyTo = 'norep****@examp*****';
+		$this->Controller->EmailTest->template = null;
+		$this->Controller->EmailTest->delivery = 'debug';
+		$this->Controller->EmailTest->attachments = array(
+			__FILE__,
+			'some-name.php' => __FILE__
+		);
+		$body = '<p>This is the body of the message</p>';
+
+		$this->Controller->EmailTest->sendAs = 'text';
+		$this->assertTrue($this->Controller->EmailTest->send($body));
+		$msg = $this->Controller->Session->read('Message.email.message');
+		$this->assertPattern('/' . preg_quote('Content-Disposition: attachment; filename="email.test.php"') . '/', $msg);
+		$this->assertPattern('/' . preg_quote('Content-Disposition: attachment; filename="some-name.php"') . '/', $msg);
+	}
+
+/**
+ * testSendAsIsNotIgnoredIfAttachmentsPresent method
+ *
+ * @return void
+ * @access public
+ */
+	function testSendAsIsNotIgnoredIfAttachmentsPresent() {
+		$this->Controller->EmailTest->to = 'postmaster @ localhost';
+		$this->Controller->EmailTest->from = 'norep****@examp*****';
+		$this->Controller->EmailTest->subject = 'Attachment Test';
+		$this->Controller->EmailTest->replyTo = 'norep****@examp*****';
+		$this->Controller->EmailTest->template = null;
+		$this->Controller->EmailTest->delivery = 'debug';
+		$this->Controller->EmailTest->attachments = array(__FILE__);
+		$body = '<p>This is the body of the message</p>';
+
+		$this->Controller->EmailTest->sendAs = 'html';
+		$this->assertTrue($this->Controller->EmailTest->send($body));
+		$msg = $this->Controller->Session->read('Message.email.message');
+		$this->assertNoPattern('/text\/plain/', $msg);
+		$this->assertPattern('/text\/html/', $msg);
+
+		$this->Controller->EmailTest->sendAs = 'text';
+		$this->assertTrue($this->Controller->EmailTest->send($body));
+		$msg = $this->Controller->Session->read('Message.email.message');
+		$this->assertPattern('/text\/plain/', $msg);
+		$this->assertNoPattern('/text\/html/', $msg);
+
+		$this->Controller->EmailTest->sendAs = 'both';
+		$this->assertTrue($this->Controller->EmailTest->send($body));
+		$msg = $this->Controller->Session->read('Message.email.message');
+
+		$this->assertNoPattern('/text\/plain/', $msg);
+		$this->assertNoPattern('/text\/html/', $msg);
+		$this->assertPattern('/multipart\/alternative/', $msg);
+	}
+
+/**
+ * testNoDoubleNewlinesInHeaders function
+ *
+ * @return void
+ * @access public
+ */
+	function testNoDoubleNewlinesInHeaders() {
+		$this->Controller->EmailTest->to = 'postmaster @ localhost';
+		$this->Controller->EmailTest->from = 'norep****@examp*****';
+		$this->Controller->EmailTest->subject = 'Attachment Test';
+		$this->Controller->EmailTest->replyTo = 'norep****@examp*****';
+		$this->Controller->EmailTest->template = null;
+		$this->Controller->EmailTest->delivery = 'debug';
+		$body = '<p>This is the body of the message</p>';
+
+		$this->Controller->EmailTest->sendAs = 'both';
+		$this->assertTrue($this->Controller->EmailTest->send($body));
+		$msg = $this->Controller->Session->read('Message.email.message');
+
+		$this->assertNoPattern('/\n\nContent-Transfer-Encoding/', $msg);
+		$this->assertPattern('/\nContent-Transfer-Encoding/', $msg);
+	}
+
+/**
+ * testReset method
+ *
+ * @access public
+ * @return void
+ */
+	function testReset() {
+		$this->Controller->EmailTest->template = 'test_template';
+		$this->Controller->EmailTest->to = 'test.****@examp*****';
+		$this->Controller->EmailTest->from = 'test.****@examp*****';
+		$this->Controller->EmailTest->replyTo = 'test.****@examp*****';
+		$this->Controller->EmailTest->return = 'test.****@examp*****';
+		$this->Controller->EmailTest->cc = array('cc1****@examp*****', 'cc2****@examp*****');
+		$this->Controller->EmailTest->bcc = array('bcc1****@examp*****', 'bcc2****@examp*****');
+		$this->Controller->EmailTest->date = 'Today!';
+		$this->Controller->EmailTest->subject = 'Test subject';
+		$this->Controller->EmailTest->additionalParams = 'X-additional-header';
+		$this->Controller->EmailTest->delivery = 'smtp';
+		$this->Controller->EmailTest->smtpOptions['host'] = 'blah';
+		$this->Controller->EmailTest->smtpOptions['timeout'] = 0.5;
+		$this->Controller->EmailTest->attachments = array('attachment1', 'attachment2');
+		$this->Controller->EmailTest->textMessage = 'This is the body of the message';
+		$this->Controller->EmailTest->htmlMessage = 'This is the body of the message';
+		$this->Controller->EmailTest->messageId = false;
+
+		$this->assertFalse($this->Controller->EmailTest->send('Should not work'));
+
+		$this->Controller->EmailTest->reset();
+
+		$this->assertNull($this->Controller->EmailTest->template);
+		$this->assertIdentical($this->Controller->EmailTest->to, array());
+		$this->assertNull($this->Controller->EmailTest->from);
+		$this->assertNull($this->Controller->EmailTest->replyTo);
+		$this->assertNull($this->Controller->EmailTest->return);
+		$this->assertIdentical($this->Controller->EmailTest->cc, array());
+		$this->assertIdentical($this->Controller->EmailTest->bcc, array());
+		$this->assertNull($this->Controller->EmailTest->date);
+		$this->assertNull($this->Controller->EmailTest->subject);
+		$this->assertNull($this->Controller->EmailTest->additionalParams);
+		$this->assertIdentical($this->Controller->EmailTest->getHeaders(), array());
+		$this->assertNull($this->Controller->EmailTest->getBoundary());
+		$this->assertIdentical($this->Controller->EmailTest->getMessage(), array());
+		$this->assertNull($this->Controller->EmailTest->smtpError);
+		$this->assertIdentical($this->Controller->EmailTest->attachments, array());
+		$this->assertNull($this->Controller->EmailTest->textMessage);
+		$this->assertTrue($this->Controller->EmailTest->messageId);
+		$this->assertEqual('mail', $this->Controller->EmailTest->delivery);
+	}
+
+	function testPluginCustomViewClass() {
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS),
+			'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS)
+		));
+
+		$this->Controller->view = 'TestPlugin.Email';
+
+		$this->Controller->EmailTest->to = 'postmaster @ localhost';
+		$this->Controller->EmailTest->from = 'norep****@examp*****';
+		$this->Controller->EmailTest->subject = 'CustomViewClass test';
+		$this->Controller->EmailTest->delivery = 'debug';
+		$body = 'Body of message';
+
+		$this->assertTrue($this->Controller->EmailTest->send($body));
+		$result = $this->Controller->Session->read('Message.email.message');
+
+		$this->assertPattern('/Body of message/', $result);
+
+	}
+
+/**
+ * testStartup method
+ *
+ * @access public
+ * @return void
+ */
+	function testStartup() {
+		$this->assertNull($this->Controller->EmailTest->startup($this->Controller));
+	}
+
+/**
+ * testMessageId method
+ *
+ * @access public
+ * @return void
+ */
+	function testMessageId() {
+		$this->Controller->EmailTest->to = 'postmaster @ localhost';
+		$this->Controller->EmailTest->from = 'norep****@examp*****';
+		$this->Controller->EmailTest->subject = 'Cake Debug Test';
+		$this->Controller->EmailTest->replyTo = 'norep****@examp*****';
+		$this->Controller->EmailTest->template = null;
+
+		$this->Controller->EmailTest->delivery = 'debug';
+		$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+		$result = $this->Controller->Session->read('Message.email.message');
+
+		$this->assertPattern('/Message-ID: \<[a-f0-9]{32}@' . env('HTTP_HOST') . '\>\n/', $result);
+
+		$this->Controller->EmailTest->messageId = '<22091985.998877 @ localhost>';
+
+		$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+		$result = $this->Controller->Session->read('Message.email.message');
+
+		$this->assertPattern('/Message-ID: <22091985.998877 @ localhost>\n/', $result);
+
+		$this->Controller->EmailTest->messageId = false;
+
+		$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+		$result = $this->Controller->Session->read('Message.email.message');
+
+		$this->assertNoPattern('/Message-ID:/', $result);
+	}
+
+/**
+ * testSendMessage method
+ *
+ * @access public
+ * @return void
+ */
+	function testSendMessage() {
+		$this->Controller->EmailTest->delivery = 'getMessage';
+		$this->Controller->EmailTest->lineLength = 70;
+
+		$text = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.';
+		$this->Controller->EmailTest->sendAs = 'text';
+		$result = $this->Controller->EmailTest->send($text);
+		$expected = array(
+			'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do',
+			'eiusmod tempor incididunt ut labore et dolore magna aliqua.',
+			'',
+			''
+		);
+		$this->assertEqual($expected, $result);
+
+		$text = 'Lorem ipsum dolor sit amet, <b>consectetur</b> adipisicing elit, sed do <span>eiusmod tempor</span> incididunt ut labore et dolore magna aliqua.';
+		$this->Controller->EmailTest->sendAs = 'html';
+		$result = $this->Controller->EmailTest->send($text);
+		$expected = array(
+			$text,
+			'',
+			''
+		);
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * Test that _formatName doesn't jack up email addresses with alias parts.
+ *
+ * @return void
+ */
+	function testFormatAddressAliases() {
+		$result = $this->Controller->EmailTest->formatAddress('email****@examp*****');
+		$this->assertEqual($result, 'email****@examp*****');
+
+		$result = $this->Controller->EmailTest->formatAddress('alias <email****@examp*****>');
+		$this->assertEqual($result, 'alias <email****@examp*****>');
+		
+		$result = $this->Controller->EmailTest->formatAddress('alias<email****@examp*****>');
+		$this->assertEqual($result, 'alias <email****@examp*****>');
+
+		$result = $this->Controller->EmailTest->formatAddress('email****@examp*****');
+		$this->assertEqual($result, 'email****@examp*****');
+
+		$result = $this->Controller->EmailTest->formatAddress('<email****@examp*****>');
+		$this->assertEqual($result, '<email****@examp*****>');
+
+		$result = $this->Controller->EmailTest->formatAddress('email****@examp*****', true);
+		$this->assertEqual($result, '<email****@examp*****>');
+
+		$result = $this->Controller->EmailTest->formatAddress('<email****@examp*****>', true);
+		$this->assertEqual($result, '<email****@examp*****>');
+
+		$result = $this->Controller->EmailTest->formatAddress('alias name <email****@examp*****>', true);
+		$this->assertEqual($result, '<email****@examp*****>');
+	}
+
+/**
+ * test formatting addresses with multibyte chars
+ *
+ * @return void
+ */
+	function testFormatAddressMultibyte() {
+		$this->Controller->EmailTest->charset = 'UTF-8';
+		$result = $this->Controller->EmailTest->formatAddress('ÄÖÜTest <email****@domai*****>');
+		$this->assertEqual($result, '=?UTF-8?B?w4TDlsOcVGVzdA==?= <email****@domai*****>');
+		
+		$result = $this->Controller->EmailTest->formatAddress('ÄÖÜTest<email****@domai*****>');
+		$this->assertEqual($result, '=?UTF-8?B?w4TDlsOcVGVzdA==?= <email****@domai*****>');
+
+		$result = $this->Controller->EmailTest->formatAddress('ÄÖÜTest <email****@domai*****>', true);
+		$this->assertEqual($result, '<email****@domai*****>');
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/controller/components/request_handler.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/controller/components/request_handler.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/controller/components/request_handler.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,734 @@
+<?php
+/**
+ * RequestHandlerComponentTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ * @since         CakePHP(tm) v 1.2.0.5435
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Controller', 'Controller', false);
+App::import('Component', array('RequestHandler'));
+
+Mock::generatePartial('RequestHandlerComponent', 'NoStopRequestHandler', array('_stop', '_header'));
+Mock::generatePartial('Controller', 'RequestHandlerMockController', array('header'));
+
+/**
+ * RequestHandlerTestController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ */
+class RequestHandlerTestController extends Controller {
+
+/**
+ * name property
+ *
+ * @var string
+ * @access public
+ */
+	var $name = 'RequestHandlerTest';
+
+/**
+ * uses property
+ *
+ * @var mixed null
+ * @access public
+ */
+	var $uses = null;
+
+/**
+ * construct method
+ *
+ * @param array $params
+ * @access private
+ * @return void
+ */
+	function __construct($params = array()) {
+		foreach ($params as $key => $val) {
+			$this->{$key} = $val;
+		}
+		parent::__construct();
+	}
+
+/**
+ * test method for ajax redirection
+ *
+ * @return void
+ */
+	function destination() {
+		$this->viewPath = 'posts';
+		$this->render('index');
+	}
+/**
+ * test method for ajax redirection + parameter parsing
+ *
+ * @return void
+ */
+	function param_method($one = null, $two = null) {
+		echo "one: $one two: $two";
+		$this->autoRender = false;
+	}
+
+/**
+ * test method for testing layout rendering when isAjax()
+ *
+ * @return void
+ */
+	function ajax2_layout() {
+		if ($this->autoLayout) {
+			$this->layout = 'ajax2';
+		}
+		$this->destination();
+	}
+}
+
+/**
+ * RequestHandlerTestDisabledController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ */
+class RequestHandlerTestDisabledController extends Controller {
+
+/**
+ * uses property
+ *
+ * @var mixed null
+ * @access public
+ */
+	var $uses = null;
+
+/**
+ * construct method
+ *
+ * @param array $params
+ * @access private
+ * @return void
+ */
+	function __construct($params = array()) {
+		foreach ($params as $key => $val) {
+			$this->{$key} = $val;
+		}
+		parent::__construct();
+	}
+
+/**
+ * beforeFilter method
+ *
+ * @return void
+ * @access public
+ */
+	function beforeFilter() {
+		$this->RequestHandler->enabled = false;
+	}
+}
+
+/**
+ * RequestHandlerComponentTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ */
+class RequestHandlerComponentTest extends CakeTestCase {
+
+/**
+ * Controller property
+ *
+ * @var RequestHandlerTestController
+ * @access public
+ */
+	var $Controller;
+
+/**
+ * RequestHandler property
+ *
+ * @var RequestHandlerComponent
+ * @access public
+ */
+	var $RequestHandler;
+
+/**
+ * startTest method
+ *
+ * @access public
+ * @return void
+ */
+	function startTest() {
+		$this->_init();
+	}
+
+/**
+ * init method
+ *
+ * @access protected
+ * @return void
+ */
+	function _init() {
+		$this->Controller = new RequestHandlerTestController(array('components' => array('RequestHandler')));
+		$this->Controller->constructClasses();
+		$this->RequestHandler =& $this->Controller->RequestHandler;
+	}
+
+/**
+ * endTest method
+ *
+ * @access public
+ * @return void
+ */
+	function endTest() {
+		unset($this->RequestHandler);
+		unset($this->Controller);
+		if (!headers_sent()) {
+			header('Content-type: text/html'); //reset content type.
+		}
+		App::build();
+	}
+
+/**
+ * testInitializeCallback method
+ *
+ * @access public
+ * @return void
+ */
+	function testInitializeCallback() {
+		$this->assertNull($this->RequestHandler->ext);
+
+		$this->_init();
+		$this->Controller->params['url']['ext'] = 'rss';
+		$this->RequestHandler->initialize($this->Controller);
+		$this->assertEqual($this->RequestHandler->ext, 'rss');
+
+		$settings = array(
+			'ajaxLayout' => 'test_ajax'
+		);
+		$this->RequestHandler->initialize($this->Controller, $settings);
+		$this->assertEqual($this->RequestHandler->ajaxLayout, 'test_ajax');
+	}
+
+/**
+ * testDisabling method
+ *
+ * @access public
+ * @return void
+ */
+	function testDisabling() {
+		$_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest';
+		$this->_init();
+		$this->Controller->Component->initialize($this->Controller);
+		$this->Controller->beforeFilter();
+		$this->Controller->Component->startup($this->Controller);
+		$this->assertEqual($this->Controller->params, array('isAjax' => true));
+
+		$this->Controller = new RequestHandlerTestDisabledController(array('components' => array('RequestHandler')));
+		$this->Controller->constructClasses();
+		$this->Controller->Component->initialize($this->Controller);
+		$this->Controller->beforeFilter();
+		$this->Controller->Component->startup($this->Controller);
+		$this->assertEqual($this->Controller->params, array());
+		unset($_SERVER['HTTP_X_REQUESTED_WITH']);
+	}
+
+/**
+ * testAutoResponseType method
+ *
+ * @access public
+ * @return void
+ */
+	function testAutoResponseType() {
+		$this->Controller->ext = '.thtml';
+		$this->Controller->params['url']['ext'] = 'rss';
+		$this->RequestHandler->initialize($this->Controller);
+		$this->RequestHandler->startup($this->Controller);
+		$this->assertEqual($this->Controller->ext, '.ctp');
+	}
+
+
+/**
+ * testAutoAjaxLayout method
+ *
+ * @access public
+ * @return void
+ */
+	function testAutoAjaxLayout() {
+		$_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest';
+		$this->RequestHandler->startup($this->Controller);
+		$this->assertTrue($this->Controller->layout, $this->RequestHandler->ajaxLayout);
+
+		$this->_init();
+		$this->Controller->params['url']['ext'] = 'js';
+		$this->RequestHandler->initialize($this->Controller);
+		$this->RequestHandler->startup($this->Controller);
+		$this->assertNotEqual($this->Controller->layout, 'ajax');
+
+		unset($_SERVER['HTTP_X_REQUESTED_WITH']);
+	}
+
+/**
+ * testStartupCallback method
+ *
+ * @access public
+ * @return void
+ */
+	function testStartupCallback() {
+		$_SERVER['REQUEST_METHOD'] = 'PUT';
+		$_SERVER['CONTENT_TYPE'] = 'application/xml';
+		$this->RequestHandler->startup($this->Controller);
+		$this->assertTrue(is_array($this->Controller->data));
+		$this->assertFalse(is_object($this->Controller->data));
+	}
+
+/**
+ * testStartupCallback with charset.
+ *
+ * @return void
+ */
+	function testStartupCallbackCharset() {
+		$_SERVER['REQUEST_METHOD'] = 'PUT';
+		$_SERVER['CONTENT_TYPE'] = 'application/xml; charset=UTF-8';
+		$this->RequestHandler->startup($this->Controller);
+		$this->assertTrue(is_array($this->Controller->data));
+		$this->assertFalse(is_object($this->Controller->data));
+	}
+
+/**
+ * testNonAjaxRedirect method
+ *
+ * @access public
+ * @return void
+ */
+	function testNonAjaxRedirect() {
+		$this->RequestHandler->initialize($this->Controller);
+		$this->RequestHandler->startup($this->Controller);
+		$this->assertNull($this->RequestHandler->beforeRedirect($this->Controller, '/'));
+	}
+
+/**
+ * testRenderAs method
+ *
+ * @access public
+ * @return void
+ */
+	function testRenderAs() {
+		$this->assertFalse(in_array('Xml', $this->Controller->helpers));
+		$this->RequestHandler->renderAs($this->Controller, 'xml');
+		$this->assertTrue(in_array('Xml', $this->Controller->helpers));
+
+		$this->Controller->viewPath = 'request_handler_test\\xml';
+		$this->RequestHandler->renderAs($this->Controller, 'js');
+		$this->assertEqual($this->Controller->viewPath, 'request_handler_test' . DS . 'js');
+	}
+
+/**
+ * test that respondAs works as expected.
+ *
+ * @return void
+ */
+	function testRespondAs() {
+		$RequestHandler = new NoStopRequestHandler();
+		$RequestHandler->expectAt(0, '_header', array('Content-Type: application/json'));
+		$RequestHandler->expectAt(1, '_header', array('Content-Type: text/xml'));
+
+		$result = $RequestHandler->respondAs('json');
+		$this->assertTrue($result);
+
+		$result = $RequestHandler->respondAs('text/xml');
+		$this->assertTrue($result);
+	}
+
+/**
+ * test that attachment headers work with respondAs
+ *
+ * @return void
+ */
+	function testRespondAsWithAttachment() {
+		$RequestHandler = new NoStopRequestHandler();
+		$RequestHandler->expectAt(0, '_header', array('Content-Disposition: attachment; filename="myfile.xml"'));
+		$RequestHandler->expectAt(1, '_header', array('Content-Type: text/xml'));
+
+		$result = $RequestHandler->respondAs('xml', array('attachment' => 'myfile.xml'));
+		$this->assertTrue($result);
+	}
+
+/**
+ * test that calling renderAs() more than once continues to work.
+ *
+ * @link #6466
+ * @return void
+ */
+	function testRenderAsCalledTwice() {
+		$this->RequestHandler->renderAs($this->Controller, 'xml');
+		$this->assertEqual($this->Controller->viewPath, 'request_handler_test' . DS . 'xml');
+		$this->assertEqual($this->Controller->layoutPath, 'xml');
+
+		$this->assertTrue(in_array('Xml', $this->Controller->helpers));
+
+		$this->RequestHandler->renderAs($this->Controller, 'js');
+		$this->assertEqual($this->Controller->viewPath, 'request_handler_test' . DS . 'js');
+		$this->assertEqual($this->Controller->layoutPath, 'js');
+		$this->assertTrue(in_array('Js', $this->Controller->helpers));
+	}
+
+/**
+ * testRequestClientTypes method
+ *
+ * @access public
+ * @return void
+ */
+	function testRequestClientTypes() {
+		$this->assertFalse($this->RequestHandler->isFlash());
+		$_SERVER['HTTP_USER_AGENT'] = 'Shockwave Flash';
+		$this->assertTrue($this->RequestHandler->isFlash());
+		unset($_SERVER['HTTP_USER_AGENT'], $_SERVER['HTTP_X_REQUESTED_WITH']);
+
+		$this->assertFalse($this->RequestHandler->isAjax());
+		$_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest';
+		$_SERVER['HTTP_X_PROTOTYPE_VERSION'] = '1.5';
+		$this->assertTrue($this->RequestHandler->isAjax());
+		$this->assertEqual($this->RequestHandler->getAjaxVersion(), '1.5');
+
+		unset($_SERVER['HTTP_X_REQUESTED_WITH'], $_SERVER['HTTP_X_PROTOTYPE_VERSION']);
+		$this->assertFalse($this->RequestHandler->isAjax());
+		$this->assertFalse($this->RequestHandler->getAjaxVersion());
+	}
+
+/**
+ * Tests the detection of various Flash versions
+ *
+ * @access public
+ * @return void
+ */
+	function testFlashDetection() {
+		$_agent = env('HTTP_USER_AGENT');
+		$_SERVER['HTTP_USER_AGENT'] = 'Shockwave Flash';
+		$this->assertTrue($this->RequestHandler->isFlash());
+
+		$_SERVER['HTTP_USER_AGENT'] = 'Adobe Flash';
+		$this->assertTrue($this->RequestHandler->isFlash());
+
+		$_SERVER['HTTP_USER_AGENT'] = 'Adobe Flash Player 9';
+		$this->assertTrue($this->RequestHandler->isFlash());
+
+		$_SERVER['HTTP_USER_AGENT'] = 'Adobe Flash Player 10';
+		$this->assertTrue($this->RequestHandler->isFlash());
+
+		$_SERVER['HTTP_USER_AGENT'] = 'Shock Flash';
+		$this->assertFalse($this->RequestHandler->isFlash());
+
+		$_SERVER['HTTP_USER_AGENT'] = $_agent;
+	}
+
+/**
+ * testRequestContentTypes method
+ *
+ * @access public
+ * @return void
+ */
+	function testRequestContentTypes() {
+		$_SERVER['REQUEST_METHOD'] = 'GET';
+		$this->assertNull($this->RequestHandler->requestedWith());
+
+		$_SERVER['REQUEST_METHOD'] = 'POST';
+		$_SERVER['CONTENT_TYPE'] = 'application/json';
+		$this->assertEqual($this->RequestHandler->requestedWith(), 'json');
+
+		$result = $this->RequestHandler->requestedWith(array('json', 'xml'));
+		$this->assertEqual($result, 'json');
+
+		$result =$this->RequestHandler->requestedWith(array('rss', 'atom'));
+		$this->assertFalse($result);
+
+		$_SERVER['HTTP_ACCEPT'] = 'text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,*/*';
+		$this->_init();
+		$this->assertTrue($this->RequestHandler->isXml());
+		$this->assertFalse($this->RequestHandler->isAtom());
+		$this->assertFalse($this->RequestHandler->isRSS());
+
+		$_SERVER['HTTP_ACCEPT'] = 'application/atom+xml,text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,*/*';
+		$this->_init();
+		$this->assertTrue($this->RequestHandler->isAtom());
+		$this->assertFalse($this->RequestHandler->isRSS());
+
+		$_SERVER['HTTP_ACCEPT'] = 'application/rss+xml,text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,*/*';
+		$this->_init();
+		$this->assertFalse($this->RequestHandler->isAtom());
+		$this->assertTrue($this->RequestHandler->isRSS());
+
+		$this->assertFalse($this->RequestHandler->isWap());
+		$_SERVER['HTTP_ACCEPT'] = 'text/vnd.wap.wml,text/html,text/plain,image/png,*/*';
+		$this->_init();
+		$this->assertTrue($this->RequestHandler->isWap());
+
+		$_SERVER['HTTP_ACCEPT'] = 'application/rss+xml ;q=0.9 ,  text/xml,  application/xml,application/xhtml+xml';
+		$this->_init();
+		$this->assertFalse($this->RequestHandler->isAtom());
+		$this->assertTrue($this->RequestHandler->isRSS());
+	}
+
+/**
+ * testResponseContentType method
+ *
+ * @access public
+ * @return void
+ */
+	function testResponseContentType() {
+		$this->assertNull($this->RequestHandler->responseType());
+		$this->assertTrue($this->RequestHandler->respondAs('atom'));
+		$this->assertEqual($this->RequestHandler->responseType(), 'atom');
+	}
+
+/**
+ * testMobileDeviceDetection method
+ *
+ * @access public
+ * @return void
+ */
+	function testMobileDeviceDetection() {
+		$this->assertFalse($this->RequestHandler->isMobile());
+
+		$_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A543a Safari/419.3';
+		$this->assertTrue($this->RequestHandler->isMobile());
+
+		$_SERVER['HTTP_USER_AGENT'] = 'Some imaginary UA';
+		$this->RequestHandler->mobileUA []= 'imaginary';
+		$this->assertTrue($this->RequestHandler->isMobile());
+		array_pop($this->RequestHandler->mobileUA);
+	}
+
+/**
+ * testRequestProperties method
+ *
+ * @access public
+ * @return void
+ */
+	function testRequestProperties() {
+		$_SERVER['HTTPS'] = 'on';
+		$this->assertTrue($this->RequestHandler->isSSL());
+
+		unset($_SERVER['HTTPS']);
+		$this->assertFalse($this->RequestHandler->isSSL());
+
+		$_ENV['SCRIPT_URI'] = 'https://localhost/';
+		$s = $_SERVER;
+		$_SERVER = array();
+		$this->assertTrue($this->RequestHandler->isSSL());
+		$_SERVER = $s;
+	}
+
+/**
+ * testRequestMethod method
+ *
+ * @access public
+ * @return void
+ */
+	function testRequestMethod() {
+		$_SERVER['REQUEST_METHOD'] = 'GET';
+		$this->assertTrue($this->RequestHandler->isGet());
+		$this->assertFalse($this->RequestHandler->isPost());
+		$this->assertFalse($this->RequestHandler->isPut());
+		$this->assertFalse($this->RequestHandler->isDelete());
+
+		$_SERVER['REQUEST_METHOD'] = 'POST';
+		$this->assertFalse($this->RequestHandler->isGet());
+		$this->assertTrue($this->RequestHandler->isPost());
+		$this->assertFalse($this->RequestHandler->isPut());
+		$this->assertFalse($this->RequestHandler->isDelete());
+
+		$_SERVER['REQUEST_METHOD'] = 'PUT';
+		$this->assertFalse($this->RequestHandler->isGet());
+		$this->assertFalse($this->RequestHandler->isPost());
+		$this->assertTrue($this->RequestHandler->isPut());
+		$this->assertFalse($this->RequestHandler->isDelete());
+
+		$_SERVER['REQUEST_METHOD'] = 'DELETE';
+		$this->assertFalse($this->RequestHandler->isGet());
+		$this->assertFalse($this->RequestHandler->isPost());
+		$this->assertFalse($this->RequestHandler->isPut());
+		$this->assertTrue($this->RequestHandler->isDelete());
+	}
+
+/**
+ * testClientContentPreference method
+ *
+ * @access public
+ * @return void
+ */
+	function testClientContentPreference() {
+		$_SERVER['HTTP_ACCEPT'] = 'text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,*/*';
+		$this->_init();
+		$this->assertNotEqual($this->RequestHandler->prefers(), 'rss');
+		$this->RequestHandler->ext = 'rss';
+		$this->assertEqual($this->RequestHandler->prefers(), 'rss');
+		$this->assertFalse($this->RequestHandler->prefers('xml'));
+		$this->assertEqual($this->RequestHandler->prefers(array('js', 'xml', 'xhtml')), 'xml');
+		$this->assertTrue($this->RequestHandler->accepts('xml'));
+
+		$_SERVER['HTTP_ACCEPT'] = 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5';
+		$this->_init();
+		$this->assertEqual($this->RequestHandler->prefers(), 'xml');
+		$this->assertEqual($this->RequestHandler->accepts(array('js', 'xml', 'html')), 'xml');
+		$this->assertFalse($this->RequestHandler->accepts(array('gif', 'jpeg', 'foo')));
+
+		$_SERVER['HTTP_ACCEPT'] = '*/*;q=0.5';
+		$this->_init();
+		$this->assertEqual($this->RequestHandler->prefers(), 'html');
+		$this->assertFalse($this->RequestHandler->prefers('rss'));
+		$this->assertFalse($this->RequestHandler->accepts('rss'));
+	}
+
+/**
+ * testCustomContent method
+ *
+ * @access public
+ * @return void
+ */
+	function testCustomContent() {
+		$_SERVER['HTTP_ACCEPT'] = 'text/x-mobile,text/html;q=0.9,text/plain;q=0.8,*/*;q=0.5';
+		$this->_init();
+		$this->RequestHandler->setContent('mobile', 'text/x-mobile');
+		$this->RequestHandler->startup($this->Controller);
+		$this->assertEqual($this->RequestHandler->prefers(), 'mobile');
+
+		$this->_init();
+		$this->RequestHandler->setContent(array('mobile' => 'text/x-mobile'));
+		$this->RequestHandler->startup($this->Controller);
+		$this->assertEqual($this->RequestHandler->prefers(), 'mobile');
+	}
+
+/**
+ * testClientProperties method
+ *
+ * @access public
+ * @return void
+ */
+	function testClientProperties() {
+		$_SERVER['HTTP_HOST'] = 'localhost:80';
+		$this->assertEqual($this->RequestHandler->getReferer(), 'localhost');
+		$_SERVER['HTTP_HOST'] = null;
+		$_SERVER['HTTP_X_FORWARDED_HOST'] = 'cakephp.org';
+		$this->assertEqual($this->RequestHandler->getReferer(), 'cakephp.org');
+
+		$_SERVER['HTTP_X_FORWARDED_FOR'] = '192.168.1.5, 10.0.1.1, proxy.com';
+		$_SERVER['HTTP_CLIENT_IP'] = '192.168.1.2';
+		$_SERVER['REMOTE_ADDR'] = '192.168.1.3';
+		$this->assertEqual($this->RequestHandler->getClientIP(false), '192.168.1.5');
+		$this->assertEqual($this->RequestHandler->getClientIP(), '192.168.1.2');
+
+		unset($_SERVER['HTTP_X_FORWARDED_FOR']);
+		$this->assertEqual($this->RequestHandler->getClientIP(), '192.168.1.2');
+
+		unset($_SERVER['HTTP_CLIENT_IP']);
+		$this->assertEqual($this->RequestHandler->getClientIP(), '192.168.1.3');
+
+		$_SERVER['HTTP_CLIENTADDRESS'] = '10.0.1.2, 10.0.1.1';
+		$this->assertEqual($this->RequestHandler->getClientIP(), '10.0.1.2');
+	}
+
+/**
+ * test that ajax requests involving redirects trigger requestAction instead.
+ *
+ * @return void
+ */
+	function testAjaxRedirectAsRequestAction() {
+		$_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest';
+		$this->_init();
+		App::build(array(
+			'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS)
+		), true);
+
+		$this->Controller->RequestHandler = new NoStopRequestHandler($this);
+		$this->Controller->RequestHandler->expectOnce('_stop');
+
+		ob_start();
+		$this->Controller->RequestHandler->beforeRedirect(
+			$this->Controller, array('controller' => 'request_handler_test', 'action' => 'destination')
+		);
+		$result = ob_get_clean();
+		$this->assertPattern('/posts index/', $result, 'RequestAction redirect failed.');
+
+		unset($_SERVER['HTTP_X_REQUESTED_WITH']);
+		App::build();
+	}
+
+/**
+ * test that ajax requests involving redirects don't force no layout
+ * this would cause the ajax layout to not be rendered.
+ *
+ * @return void
+ */
+	function testAjaxRedirectAsRequestActionStillRenderingLayout() {
+		$_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest';
+		$this->_init();
+		App::build(array(
+			'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS)
+		), true);
+
+		$this->Controller->RequestHandler = new NoStopRequestHandler($this);
+		$this->Controller->RequestHandler->expectOnce('_stop');
+
+		ob_start();
+		$this->Controller->RequestHandler->beforeRedirect(
+			$this->Controller, array('controller' => 'request_handler_test', 'action' => 'ajax2_layout')
+		);
+		$result = ob_get_clean();
+		$this->assertPattern('/posts index/', $result, 'RequestAction redirect failed.');
+		$this->assertPattern('/Ajax!/', $result, 'Layout was not rendered.');
+
+		unset($_SERVER['HTTP_X_REQUESTED_WITH']);
+		App::build();
+	}
+
+/**
+ * test that the beforeRedirect callback properly converts
+ * array urls into their correct string ones, and adds base => false so
+ * the correct urls are generated.
+ *
+ * @link http://cakephp.lighthouseapp.com/projects/42648-cakephp-1x/tickets/276
+ * @return void
+ */
+	function testBeforeRedirectCallbackWithArrayUrl() {
+		$_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest';
+
+		Router::setRequestInfo(array(
+			array('plugin' => null, 'controller' => 'accounts', 'action' => 'index', 'pass' => array(), 'named' => array(), 'form' => array(), 'url' => array('url' => 'accounts/')),
+			array('base' => '/officespace', 'here' => '/officespace/accounts/', 'webroot' => '/officespace/')
+		));
+
+		$RequestHandler =& new NoStopRequestHandler();
+
+		ob_start();
+		$RequestHandler->beforeRedirect(
+			$this->Controller,
+			array('controller' => 'request_handler_test', 'action' => 'param_method', 'first', 'second')
+		);
+		$result = ob_get_clean();
+		$this->assertEqual($result, 'one: first two: second');
+	}
+
+/**
+ * assure that beforeRedirect with a status code will correctly set the status header
+ *
+ * @return void
+ */
+	function testBeforeRedirectCallingHeader() {
+		$controller =& new RequestHandlerMockController();
+		$RequestHandler =& new NoStopRequestHandler();
+
+		$controller->expectOnce('header', array('HTTP/1.1 403 Forbidden'));
+
+		ob_start();
+		$RequestHandler->beforeRedirect($controller, 'request_handler_test/param_method/first/second', 403);
+		$result = ob_get_clean();
+	}
+
+}

Added: trunk/src/Web/cake/tests/cases/libs/controller/components/security.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/controller/components/security.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/controller/components/security.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,1305 @@
+<?php
+/**
+ * SecurityComponentTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ * @since         CakePHP(tm) v 1.2.0.5435
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Component', 'Security');
+
+/**
+* TestSecurityComponent
+*
+* @package       cake
+* @subpackage    cake.tests.cases.libs.controller.components
+*/
+class TestSecurityComponent extends SecurityComponent {
+
+/**
+ * validatePost method
+ *
+ * @param Controller $controller
+ * @return unknown
+ */
+	function validatePost(&$controller) {
+		return $this->_validatePost($controller);
+	}
+}
+
+/**
+* SecurityTestController
+*
+* @package       cake
+* @subpackage    cake.tests.cases.libs.controller.components
+*/
+class SecurityTestController extends Controller {
+
+/**
+ * name property
+ *
+ * @var string 'SecurityTest'
+ * @access public
+ */
+	var $name = 'SecurityTest';
+
+/**
+ * components property
+ *
+ * @var array
+ * @access public
+ */
+	var $components = array('Session', 'TestSecurity');
+
+/**
+ * failed property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $failed = false;
+
+/**
+ * Used for keeping track of headers in test
+ *
+ * @var array
+ * @access public
+ */
+	var $testHeaders = array();
+
+/**
+ * fail method
+ *
+ * @access public
+ * @return void
+ */
+	function fail() {
+		$this->failed = true;
+	}
+
+/**
+ * redirect method
+ *
+ * @param mixed $option
+ * @param mixed $code
+ * @param mixed $exit
+ * @access public
+ * @return void
+ */
+	function redirect($option, $code, $exit) {
+		return $code;
+	}
+
+/**
+ * Conveinence method for header()
+ *
+ * @param string $status
+ * @return void
+ * @access public
+ */
+	function header($status) {
+		$this->testHeaders[] = $status;
+	}
+}
+
+/**
+ * SecurityComponentTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ */
+class SecurityComponentTest extends CakeTestCase {
+
+/**
+ * Controller property
+ *
+ * @var SecurityTestController
+ * @access public
+ */
+	var $Controller;
+
+/**
+ * oldSalt property
+ *
+ * @var string
+ * @access public
+ */
+	var $oldSalt;
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function startTest() {
+		$this->Controller =& new SecurityTestController();
+		$this->Controller->Component->init($this->Controller);
+		$this->Controller->Security =& $this->Controller->TestSecurity;
+		$this->Controller->Security->blackHoleCallback = 'fail';
+		$this->oldSalt = Configure::read('Security.salt');
+		Configure::write('Security.salt', 'foo!');
+	}
+
+/**
+ * Tear-down method. Resets environment state.
+ *
+ * @access public
+ * @return void
+ */
+	function endTest() {
+		Configure::write('Security.salt', $this->oldSalt);
+		$this->Controller->Session->delete('_Token');
+		unset($this->Controller->Security);
+		unset($this->Controller->Component);
+		unset($this->Controller);
+	}
+
+/**
+ * test that initalize can set properties.
+ *
+ * @return void
+ */
+	function testInitialize() {
+		$settings = array(
+			'requirePost' => array('edit', 'update'),
+			'requireSecure' => array('update_account'),
+			'requireGet' => array('index'),
+			'validatePost' => false,
+			'loginUsers' => array(
+				'mark' => 'password'
+			),
+			'requireLogin' => array('login'),
+		);
+		$this->Controller->Security->initialize($this->Controller, $settings);
+		$this->assertEqual($this->Controller->Security->requirePost, $settings['requirePost']);
+		$this->assertEqual($this->Controller->Security->requireSecure, $settings['requireSecure']);
+		$this->assertEqual($this->Controller->Security->requireGet, $settings['requireGet']);
+		$this->assertEqual($this->Controller->Security->validatePost, $settings['validatePost']);
+		$this->assertEqual($this->Controller->Security->loginUsers, $settings['loginUsers']);
+		$this->assertEqual($this->Controller->Security->requireLogin, $settings['requireLogin']);
+	}
+
+/**
+ * testStartup method
+ *
+ * @access public
+ * @return void
+ */
+	function testStartup() {
+		$this->Controller->Security->startup($this->Controller);
+		$result = $this->Controller->params['_Token']['key'];
+		$this->assertNotNull($result);
+		$this->assertTrue($this->Controller->Session->check('_Token'));
+	}
+
+/**
+ * testRequirePostFail method
+ *
+ * @access public
+ * @return void
+ */
+	function testRequirePostFail() {
+		$_SERVER['REQUEST_METHOD'] = 'GET';
+		$this->Controller->action = 'posted';
+		$this->Controller->Security->requirePost(array('posted'));
+		$this->Controller->Security->startup($this->Controller);
+		$this->assertTrue($this->Controller->failed);
+	}
+
+/**
+ * testRequirePostSucceed method
+ *
+ * @access public
+ * @return void
+ */
+	function testRequirePostSucceed() {
+		$_SERVER['REQUEST_METHOD'] = 'POST';
+		$this->Controller->action = 'posted';
+		$this->Controller->Security->requirePost('posted');
+		$this->Controller->Security->startup($this->Controller);
+		$this->assertFalse($this->Controller->failed);
+	}
+
+/**
+ * testRequireSecureFail method
+ *
+ * @access public
+ * @return void
+ */
+	function testRequireSecureFail() {
+		$_SERVER['HTTPS'] = 'off';
+		$_SERVER['REQUEST_METHOD'] = 'POST';
+		$this->Controller->action = 'posted';
+		$this->Controller->Security->requireSecure(array('posted'));
+		$this->Controller->Security->startup($this->Controller);
+		$this->assertTrue($this->Controller->failed);
+	}
+
+/**
+ * testRequireSecureSucceed method
+ *
+ * @access public
+ * @return void
+ */
+	function testRequireSecureSucceed() {
+		$_SERVER['REQUEST_METHOD'] = 'Secure';
+		$this->Controller->action = 'posted';
+		$_SERVER['HTTPS'] = 'on';
+		$this->Controller->Security->requireSecure('posted');
+		$this->Controller->Security->startup($this->Controller);
+		$this->assertFalse($this->Controller->failed);
+	}
+
+/**
+ * testRequireAuthFail method
+ *
+ * @access public
+ * @return void
+ */
+	function testRequireAuthFail() {
+		$_SERVER['REQUEST_METHOD'] = 'AUTH';
+		$this->Controller->action = 'posted';
+		$this->Controller->data = array('username' => 'willy', 'password' => 'somePass');
+		$this->Controller->Security->requireAuth(array('posted'));
+		$this->Controller->Security->startup($this->Controller);
+		$this->assertTrue($this->Controller->failed);
+
+		$this->Controller->Session->write('_Token', serialize(array('allowedControllers' => array())));
+		$this->Controller->data = array('username' => 'willy', 'password' => 'somePass');
+		$this->Controller->action = 'posted';
+		$this->Controller->Security->requireAuth('posted');
+		$this->Controller->Security->startup($this->Controller);
+		$this->assertTrue($this->Controller->failed);
+
+		$this->Controller->Session->write('_Token', serialize(array(
+			'allowedControllers' => array('SecurityTest'), 'allowedActions' => array('posted2')
+		)));
+		$this->Controller->data = array('username' => 'willy', 'password' => 'somePass');
+		$this->Controller->action = 'posted';
+		$this->Controller->Security->requireAuth('posted');
+		$this->Controller->Security->startup($this->Controller);
+		$this->assertTrue($this->Controller->failed);
+	}
+
+/**
+ * testRequireAuthSucceed method
+ *
+ * @access public
+ * @return void
+ */
+	function testRequireAuthSucceed() {
+		$_SERVER['REQUEST_METHOD'] = 'AUTH';
+		$this->Controller->action = 'posted';
+		$this->Controller->Security->requireAuth('posted');
+		$this->Controller->Security->startup($this->Controller);
+		$this->assertFalse($this->Controller->failed);
+
+		$this->Controller->Security->Session->write('_Token', serialize(array(
+			'allowedControllers' => array('SecurityTest'), 'allowedActions' => array('posted')
+		)));
+		$this->Controller->params['controller'] = 'SecurityTest';
+		$this->Controller->params['action'] = 'posted';
+
+		$this->Controller->data = array(
+			'username' => 'willy', 'password' => 'somePass', '_Token' => ''
+		);
+		$this->Controller->action = 'posted';
+		$this->Controller->Security->requireAuth('posted');
+		$this->Controller->Security->startup($this->Controller);
+		$this->assertFalse($this->Controller->failed);
+	}
+
+/**
+ * testRequirePostSucceedWrongMethod method
+ *
+ * @access public
+ * @return void
+ */
+	function testRequirePostSucceedWrongMethod() {
+		$_SERVER['REQUEST_METHOD'] = 'GET';
+		$this->Controller->action = 'getted';
+		$this->Controller->Security->requirePost('posted');
+		$this->Controller->Security->startup($this->Controller);
+		$this->assertFalse($this->Controller->failed);
+	}
+
+/**
+ * testRequireGetFail method
+ *
+ * @access public
+ * @return void
+ */
+	function testRequireGetFail() {
+		$_SERVER['REQUEST_METHOD'] = 'POST';
+		$this->Controller->action = 'getted';
+		$this->Controller->Security->requireGet(array('getted'));
+		$this->Controller->Security->startup($this->Controller);
+		$this->assertTrue($this->Controller->failed);
+	}
+
+/**
+ * testRequireGetSucceed method
+ *
+ * @access public
+ * @return void
+ */
+	function testRequireGetSucceed() {
+		$_SERVER['REQUEST_METHOD'] = 'GET';
+		$this->Controller->action = 'getted';
+		$this->Controller->Security->requireGet('getted');
+		$this->Controller->Security->startup($this->Controller);
+		$this->assertFalse($this->Controller->failed);
+	}
+
+/**
+ * testRequireLogin method
+ *
+ * @access public
+ * @return void
+ */
+	function testRequireLogin() {
+		$this->Controller->action = 'posted';
+		$this->Controller->Security->requireLogin(
+			'posted',
+			array('type' => 'basic', 'users' => array('admin' => 'password'))
+		);
+		$_SERVER['PHP_AUTH_USER'] = 'admin';
+		$_SERVER['PHP_AUTH_PW'] = 'password';
+		$this->Controller->Security->startup($this->Controller);
+		$this->assertFalse($this->Controller->failed);
+
+
+		$this->Controller->action = 'posted';
+		$this->Controller->Security->requireLogin(
+			array('posted'),
+			array('type' => 'basic', 'users' => array('admin' => 'password'))
+		);
+		$_SERVER['PHP_AUTH_USER'] = 'admin2';
+		$_SERVER['PHP_AUTH_PW'] = 'password';
+		$this->Controller->Security->startup($this->Controller);
+		$this->assertTrue($this->Controller->failed);
+
+		$this->Controller->action = 'posted';
+		$this->Controller->Security->requireLogin(
+			'posted',
+			array('type' => 'basic', 'users' => array('admin' => 'password'))
+		);
+		$_SERVER['PHP_AUTH_USER'] = 'admin';
+		$_SERVER['PHP_AUTH_PW'] = 'password2';
+		$this->Controller->Security->startup($this->Controller);
+		$this->assertTrue($this->Controller->failed);
+	}
+
+/**
+ * testDigestAuth method
+ *
+ * @access public
+ * @return void
+ */
+	function testDigestAuth() {
+		$skip = $this->skipIf((version_compare(PHP_VERSION, '5.1') == -1) XOR (!function_exists('apache_request_headers')),
+			"%s Cannot run Digest Auth test for PHP versions < 5.1"
+		);
+
+		if ($skip) {
+			return;
+		}
+
+		$this->Controller->action = 'posted';
+		$_SERVER['PHP_AUTH_DIGEST'] = $digest = <<<DIGEST
+		Digest username="Mufasa",
+		realm="testr****@host*****",
+		nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
+		uri="/dir/index.html",
+		qop=auth,
+		nc=00000001,
+		cnonce="0a4f113b",
+		response="460d0d3c6867c2f1ab85b1ada1aece48",
+		opaque="5ccc069c403ebaf9f0171e9517f40e41"
+DIGEST;
+		$this->Controller->Security->requireLogin('posted', array(
+			'type' => 'digest', 'users' => array('Mufasa' => 'password'),
+			'realm' => 'testr****@host*****'
+		));
+		$this->Controller->Security->startup($this->Controller);
+		$this->assertFalse($this->Controller->failed);
+	}
+
+/**
+ * testRequireGetSucceedWrongMethod method
+ *
+ * @access public
+ * @return void
+ */
+	function testRequireGetSucceedWrongMethod() {
+		$_SERVER['REQUEST_METHOD'] = 'POST';
+		$this->Controller->action = 'posted';
+		$this->Controller->Security->requireGet('getted');
+		$this->Controller->Security->startup($this->Controller);
+		$this->assertFalse($this->Controller->failed);
+	}
+
+/**
+ * testRequirePutFail method
+ *
+ * @access public
+ * @return void
+ */
+	function testRequirePutFail() {
+		$_SERVER['REQUEST_METHOD'] = 'POST';
+		$this->Controller->action = 'putted';
+		$this->Controller->Security->requirePut(array('putted'));
+		$this->Controller->Security->startup($this->Controller);
+		$this->assertTrue($this->Controller->failed);
+	}
+
+/**
+ * testRequirePutSucceed method
+ *
+ * @access public
+ * @return void
+ */
+	function testRequirePutSucceed() {
+		$_SERVER['REQUEST_METHOD'] = 'PUT';
+		$this->Controller->action = 'putted';
+		$this->Controller->Security->requirePut('putted');
+		$this->Controller->Security->startup($this->Controller);
+		$this->assertFalse($this->Controller->failed);
+	}
+
+/**
+ * testRequirePutSucceedWrongMethod method
+ *
+ * @access public
+ * @return void
+ */
+	function testRequirePutSucceedWrongMethod() {
+		$_SERVER['REQUEST_METHOD'] = 'POST';
+		$this->Controller->action = 'posted';
+		$this->Controller->Security->requirePut('putted');
+		$this->Controller->Security->startup($this->Controller);
+		$this->assertFalse($this->Controller->failed);
+	}
+
+/**
+ * testRequireDeleteFail method
+ *
+ * @access public
+ * @return void
+ */
+	function testRequireDeleteFail() {
+		$_SERVER['REQUEST_METHOD'] = 'POST';
+		$this->Controller->action = 'deleted';
+		$this->Controller->Security->requireDelete(array('deleted', 'other_method'));
+		$this->Controller->Security->startup($this->Controller);
+		$this->assertTrue($this->Controller->failed);
+	}
+
+/**
+ * testRequireDeleteSucceed method
+ *
+ * @access public
+ * @return void
+ */
+	function testRequireDeleteSucceed() {
+		$_SERVER['REQUEST_METHOD'] = 'DELETE';
+		$this->Controller->action = 'deleted';
+		$this->Controller->Security->requireDelete('deleted');
+		$this->Controller->Security->startup($this->Controller);
+		$this->assertFalse($this->Controller->failed);
+	}
+
+/**
+ * testRequireDeleteSucceedWrongMethod method
+ *
+ * @access public
+ * @return void
+ */
+	function testRequireDeleteSucceedWrongMethod() {
+		$_SERVER['REQUEST_METHOD'] = 'POST';
+		$this->Controller->action = 'posted';
+		$this->Controller->Security->requireDelete('deleted');
+		$this->Controller->Security->startup($this->Controller);
+		$this->assertFalse($this->Controller->failed);
+	}
+
+/**
+ * testRequireLoginSettings method
+ *
+ * @access public
+ * @return void
+ */
+	function testRequireLoginSettings() {
+		$this->Controller->Security->requireLogin(
+			'add', 'edit',
+			array('type' => 'basic', 'users' => array('admin' => 'password'))
+		);
+		$this->assertEqual($this->Controller->Security->requireLogin, array('add', 'edit'));
+		$this->assertEqual($this->Controller->Security->loginUsers, array('admin' => 'password'));
+	}
+
+/**
+ * testRequireLoginAllActions method
+ *
+ * @access public
+ * @return void
+ */
+	function testRequireLoginAllActions() {
+		$this->Controller->Security->requireLogin(
+			array('type' => 'basic', 'users' => array('admin' => 'password'))
+		);
+		$this->assertEqual($this->Controller->Security->requireLogin, array('*'));
+		$this->assertEqual($this->Controller->Security->loginUsers, array('admin' => 'password'));
+	}
+
+/**
+ * Simple hash validation test
+ *
+ * @access public
+ * @return void
+ */
+	function testValidatePost() {
+		$this->Controller->Security->startup($this->Controller);
+		$key = $this->Controller->params['_Token']['key'];
+		$fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877%3AModel.valid';
+
+		$this->Controller->data = array(
+			'Model' => array('username' => 'nate', 'password' => 'foo', 'valid' => '0'),
+			'_Token' => compact('key', 'fields')
+		);
+		$this->assertTrue($this->Controller->Security->validatePost($this->Controller));
+	}
+
+/**
+ * Test that validatePost fails if you are missing the session information.
+ *
+ * @return void
+ */
+	function testValidatePostNoSession() {
+		$this->Controller->Security->startup($this->Controller);
+		$this->Controller->Session->delete('_Token');
+
+		$key = $this->Controller->params['_Token']['key'];
+		$fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877%3AModel.valid';
+
+		$this->Controller->data = array(
+			'Model' => array('username' => 'nate', 'password' => 'foo', 'valid' => '0'),
+			'_Token' => compact('key', 'fields')
+		);
+		$this->assertFalse($this->Controller->Security->validatePost($this->Controller));
+	}
+
+/**
+ * test that validatePost fails if any of its required fields are missing.
+ *
+ * @return void
+ */
+	function testValidatePostFormHacking() {
+		$this->Controller->Security->startup($this->Controller);
+		$key = $this->Controller->params['_Token']['key'];
+		$fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877%3AModel.valid';
+
+		$this->Controller->data = array(
+			'Model' => array('username' => 'nate', 'password' => 'foo', 'valid' => '0'),
+			'_Token' => compact('key')
+		);
+		$result = $this->Controller->Security->validatePost($this->Controller);
+		$this->assertFalse($result, 'validatePost passed when fields were missing. %s');
+
+		$this->Controller->data = array(
+			'Model' => array('username' => 'nate', 'password' => 'foo', 'valid' => '0'),
+			'_Token' => compact('fields')
+		);
+		$result = $this->Controller->Security->validatePost($this->Controller);
+		$this->assertFalse($result, 'validatePost passed when key was missing. %s');
+	}
+
+/**
+ * Test that objects can't be passed into the serialized string. This was a vector for RFI and LFI 
+ * attacks. Thanks to Felix Wilhelm
+ *
+ * @return void
+ */
+	function testValidatePostObjectDeserialize() {
+		$this->Controller->Security->startup($this->Controller);
+		$key = $this->Controller->params['_Token']['key'];
+		$fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877';
+
+		// a corrupted serialized object, so we can see if it ever gets to deserialize
+		$attack = 'O:3:"App":1:{s:5:"__map";a:1:{s:3:"foo";s:7:"Hacked!";s:1:"fail"}}';
+		$fields .= urlencode(':' . str_rot13($attack));
+
+		$this->Controller->data = array(
+			'Model' => array('username' => 'mark', 'password' => 'foo', 'valid' => '0'),
+			'_Token' => compact('key', 'fields')
+		);
+		$result = $this->Controller->Security->validatePost($this->Controller);
+		$this->assertFalse($result, 'validatePost passed when key was missing. %s');
+	}
+
+/**
+ * Tests validation of checkbox arrays
+ *
+ * @access public
+ * @return void
+ */
+	function testValidatePostArray() {
+		$this->Controller->Security->startup($this->Controller);
+		$key = $this->Controller->params['_Token']['key'];
+		$fields = 'f7d573650a295b94e0938d32b323fde775e5f32b%3A';
+
+		$this->Controller->data = array(
+			'Model' => array('multi_field' => array('1', '3')),
+			'_Token' => compact('key', 'fields')
+		);
+		$this->assertTrue($this->Controller->Security->validatePost($this->Controller));
+	}
+
+/**
+ * testValidatePostNoModel method
+ *
+ * @access public
+ * @return void
+ */
+	function testValidatePostNoModel() {
+		$this->Controller->Security->startup($this->Controller);
+		$key = $this->Controller->params['_Token']['key'];
+		$fields = '540ac9c60d323c22bafe997b72c0790f39a8bdef%3A';
+
+		$this->Controller->data = array(
+			'anything' => 'some_data',
+			'_Token' => compact('key', 'fields')
+		);
+
+		$result = $this->Controller->Security->validatePost($this->Controller);
+		$this->assertTrue($result);
+	}
+
+/**
+ * testValidatePostSimple method
+ *
+ * @access public
+ * @return void
+ */
+	function testValidatePostSimple() {
+		$this->Controller->Security->startup($this->Controller);
+		$key = $this->Controller->params['_Token']['key'];
+		$fields = '69f493434187b867ea14b901fdf58b55d27c935d%3A';
+
+		$this->Controller->data = $data = array(
+			'Model' => array('username' => '', 'password' => ''),
+			'_Token' => compact('key', 'fields')
+		);
+
+		$result = $this->Controller->Security->validatePost($this->Controller);
+		$this->assertTrue($result);
+	}
+
+/**
+ * Tests hash validation for multiple records, including locked fields
+ *
+ * @access public
+ * @return void
+ */
+	function testValidatePostComplex() {
+		$this->Controller->Security->startup($this->Controller);
+		$key = $this->Controller->params['_Token']['key'];
+		$fields = 'c9118120e680a7201b543f562e5301006ccfcbe2%3AAddresses.0.id%7CAddresses.1.id';
+
+		$this->Controller->data = array(
+			'Addresses' => array(
+				'0' => array(
+					'id' => '123456', 'title' => '', 'first_name' => '', 'last_name' => '',
+					'address' => '', 'city' => '', 'phone' => '', 'primary' => ''
+				),
+				'1' => array(
+					'id' => '654321', 'title' => '', 'first_name' => '', 'last_name' => '',
+					'address' => '', 'city' => '', 'phone' => '', 'primary' => ''
+				)
+			),
+			'_Token' => compact('key', 'fields')
+		);
+		$result = $this->Controller->Security->validatePost($this->Controller);
+		$this->assertTrue($result);
+	}
+
+/**
+ * test ValidatePost with multiple select elements.
+ *
+ * @return void
+ */
+	function testValidatePostMultipleSelect() {
+		$this->Controller->Security->startup($this->Controller);
+		$key = $this->Controller->params['_Token']['key'];
+		$fields = '422cde416475abc171568be690a98cad20e66079%3A';
+
+		$this->Controller->data = array(
+			'Tag' => array('Tag' => array(1, 2)),
+			'_Token' => compact('key', 'fields'),
+		);
+		$result = $this->Controller->Security->validatePost($this->Controller);
+		$this->assertTrue($result);
+
+		$this->Controller->data = array(
+			'Tag' => array('Tag' => array(1, 2, 3)),
+			'_Token' => compact('key', 'fields'),
+		);
+		$result = $this->Controller->Security->validatePost($this->Controller);
+		$this->assertTrue($result);
+
+		$this->Controller->data = array(
+			'Tag' => array('Tag' => array(1, 2, 3, 4)),
+			'_Token' => compact('key', 'fields'),
+		);
+		$result = $this->Controller->Security->validatePost($this->Controller);
+		$this->assertTrue($result);
+
+		$fields = '19464422eafe977ee729c59222af07f983010c5f%3A';
+		$this->Controller->data = array(
+			'User.password' => 'bar', 'User.name' => 'foo', 'User.is_valid' => '1',
+			'Tag' => array('Tag' => array(1)), '_Token' => compact('key', 'fields'),
+		);
+		$result = $this->Controller->Security->validatePost($this->Controller);
+		$this->assertTrue($result);
+	}
+
+/**
+ * testValidatePostCheckbox method
+ *
+ * First block tests un-checked checkbox
+ * Second block tests checked checkbox
+ *
+ * @access public
+ * @return void
+ */
+	function testValidatePostCheckbox() {
+		$this->Controller->Security->startup($this->Controller);
+		$key = $this->Controller->params['_Token']['key'];
+		$fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877%3AModel.valid';
+
+		$this->Controller->data = array(
+			'Model' => array('username' => '', 'password' => '', 'valid' => '0'),
+			'_Token' => compact('key', 'fields')
+		);
+
+		$result = $this->Controller->Security->validatePost($this->Controller);
+		$this->assertTrue($result);
+
+		$fields = '874439ca69f89b4c4a5f50fb9c36ff56a28f5d42%3A';
+
+		$this->Controller->data = array(
+			'Model' => array('username' => '', 'password' => '', 'valid' => '0'),
+			'_Token' => compact('key', 'fields')
+		);
+
+		$result = $this->Controller->Security->validatePost($this->Controller);
+		$this->assertTrue($result);
+
+
+		$this->Controller->data = array();
+		$this->Controller->Security->startup($this->Controller);
+		$key = $this->Controller->params['_Token']['key'];
+
+		$this->Controller->data = $data = array(
+			'Model' => array('username' => '', 'password' => '', 'valid' => '0'),
+			'_Token' => compact('key', 'fields')
+		);
+
+		$result = $this->Controller->Security->validatePost($this->Controller);
+		$this->assertTrue($result);
+	}
+
+/**
+ * testValidatePostHidden method
+ *
+ * @access public
+ * @return void
+ */
+	function testValidatePostHidden() {
+		$this->Controller->Security->startup($this->Controller);
+		$key = $this->Controller->params['_Token']['key'];
+		$fields = '51ccd8cb0997c7b3d4523ecde5a109318405ef8c%3AModel.hidden%7CModel.other_hidden';
+		$fields .= '';
+
+		$this->Controller->data = array(
+			'Model' => array(
+				'username' => '', 'password' => '', 'hidden' => '0',
+				'other_hidden' => 'some hidden value'
+			),
+			'_Token' => compact('key', 'fields')
+		);
+		$result = $this->Controller->Security->validatePost($this->Controller);
+		$this->assertTrue($result);
+	}
+
+/**
+ * testValidatePostWithDisabledFields method
+ *
+ * @access public
+ * @return void
+ */
+	function testValidatePostWithDisabledFields() {
+		$this->Controller->Security->disabledFields = array('Model.username', 'Model.password');
+		$this->Controller->Security->startup($this->Controller);
+		$key = $this->Controller->params['_Token']['key'];
+		$fields = 'ef1082968c449397bcd849f963636864383278b1%3AModel.hidden';
+
+		$this->Controller->data = array(
+			'Model' => array(
+				'username' => '', 'password' => '', 'hidden' => '0'
+			),
+			'_Token' => compact('fields', 'key')
+		);
+
+		$result = $this->Controller->Security->validatePost($this->Controller);
+		$this->assertTrue($result);
+	}
+
+/**
+ * testValidateHiddenMultipleModel method
+ *
+ * @access public
+ * @return void
+ */
+	function testValidateHiddenMultipleModel() {
+		$this->Controller->Security->startup($this->Controller);
+		$key = $this->Controller->params['_Token']['key'];
+		$fields = 'a2d01072dc4660eea9d15007025f35a7a5b58e18%3AModel.valid%7CModel2.valid%7CModel3.valid';
+
+		$this->Controller->data = array(
+			'Model' => array('username' => '', 'password' => '', 'valid' => '0'),
+			'Model2' => array('valid' => '0'),
+			'Model3' => array('valid' => '0'),
+			'_Token' => compact('key', 'fields')
+		);
+		$result = $this->Controller->Security->validatePost($this->Controller);
+		$this->assertTrue($result);
+	}
+
+/**
+ * testLoginValidation method
+ *
+ * @access public
+ * @return void
+ */
+	function testLoginValidation() {
+
+	}
+
+/**
+ * testValidateHasManyModel method
+ *
+ * @access public
+ * @return void
+ */
+	function testValidateHasManyModel() {
+		$this->Controller->Security->startup($this->Controller);
+		$key = $this->Controller->params['_Token']['key'];
+		$fields = '51e3b55a6edd82020b3f29c9ae200e14bbeb7ee5%3AModel.0.hidden%7CModel.0.valid';
+		$fields .= '%7CModel.1.hidden%7CModel.1.valid';
+
+		$this->Controller->data = array(
+			'Model' => array(
+				array(
+					'username' => 'username', 'password' => 'password',
+					'hidden' => 'value', 'valid' => '0'
+				),
+				array(
+					'username' => 'username', 'password' => 'password',
+					'hidden' => 'value', 'valid' => '0'
+				)
+			),
+			'_Token' => compact('key', 'fields')
+		);
+
+		$result = $this->Controller->Security->validatePost($this->Controller);
+		$this->assertTrue($result);
+	}
+
+/**
+ * testValidateHasManyRecordsPass method
+ *
+ * @access public
+ * @return void
+ */
+	function testValidateHasManyRecordsPass() {
+		$this->Controller->Security->startup($this->Controller);
+		$key = $this->Controller->params['_Token']['key'];
+		$fields = '7a203edb3d345bbf38fe0dccae960da8842e11d7%3AAddress.0.id%7CAddress.0.primary%7C';
+		$fields .= 'Address.1.id%7CAddress.1.primary';
+
+		$this->Controller->data = array(
+			'Address' => array(
+				0 => array(
+					'id' => '123',
+					'title' => 'home',
+					'first_name' => 'Bilbo',
+					'last_name' => 'Baggins',
+					'address' => '23 Bag end way',
+					'city' => 'the shire',
+					'phone' => 'N/A',
+					'primary' => '1',
+				),
+				1 => array(
+					'id' => '124',
+					'title' => 'home',
+					'first_name' => 'Frodo',
+					'last_name' => 'Baggins',
+					'address' => '50 Bag end way',
+					'city' => 'the shire',
+					'phone' => 'N/A',
+					'primary' => '1'
+				)
+			),
+			'_Token' => compact('key', 'fields')
+		);
+
+		$result = $this->Controller->Security->validatePost($this->Controller);
+		$this->assertTrue($result);
+	}
+
+/**
+ * testValidateHasManyRecords method
+ *
+ * validatePost should fail, hidden fields have been changed.
+ *
+ * @access public
+ * @return void
+ */
+	function testValidateHasManyRecordsFail() {
+		$this->Controller->Security->startup($this->Controller);
+		$key = $this->Controller->params['_Token']['key'];
+		$fields = '7a203edb3d345bbf38fe0dccae960da8842e11d7%3AAddress.0.id%7CAddress.0.primary%7C';
+		$fields .= 'Address.1.id%7CAddress.1.primary';
+
+		$this->Controller->data = array(
+			'Address' => array(
+				0 => array(
+					'id' => '123',
+					'title' => 'home',
+					'first_name' => 'Bilbo',
+					'last_name' => 'Baggins',
+					'address' => '23 Bag end way',
+					'city' => 'the shire',
+					'phone' => 'N/A',
+					'primary' => '5',
+				),
+				1 => array(
+					'id' => '124',
+					'title' => 'home',
+					'first_name' => 'Frodo',
+					'last_name' => 'Baggins',
+					'address' => '50 Bag end way',
+					'city' => 'the shire',
+					'phone' => 'N/A',
+					'primary' => '1'
+				)
+			),
+			'_Token' => compact('key', 'fields')
+		);
+
+		$result = $this->Controller->Security->validatePost($this->Controller);
+		$this->assertFalse($result);
+	}
+
+/**
+ * testLoginRequest method
+ *
+ * @access public
+ * @return void
+ */
+	function testLoginRequest() {
+		$this->Controller->Security->startup($this->Controller);
+		$realm = 'cakephp.org';
+		$options = array('realm' => $realm, 'type' => 'basic');
+		$result = $this->Controller->Security->loginRequest($options);
+		$expected = 'WWW-Authenticate: Basic realm="'.$realm.'"';
+		$this->assertEqual($result, $expected);
+
+		$this->Controller->Security->startup($this->Controller);
+		$options = array('realm' => $realm, 'type' => 'digest');
+		$result = $this->Controller->Security->loginRequest($options);
+		$this->assertPattern('/realm="'.$realm.'"/', $result);
+		$this->assertPattern('/qop="auth"/', $result);
+	}
+
+/**
+ * testGenerateDigestResponseHash method
+ *
+ * @access public
+ * @return void
+ */
+	function testGenerateDigestResponseHash() {
+		$this->Controller->Security->startup($this->Controller);
+		$realm = 'cakephp.org';
+		$loginData = array('realm' => $realm, 'users' => array('Willy Smith' => 'password'));
+		$this->Controller->Security->requireLogin($loginData);
+
+		$data = array(
+			'username' => 'Willy Smith',
+			'password' => 'password',
+			'nonce' => String::uuid(),
+			'nc' => 1,
+			'cnonce' => 1,
+			'realm' => $realm,
+			'uri' => 'path_to_identifier',
+			'qop' => 'testme'
+		);
+		$_SERVER['REQUEST_METHOD'] = 'POST';
+
+		$result = $this->Controller->Security->generateDigestResponseHash($data);
+		$expected = md5(
+			md5($data['username'] . ':' . $loginData['realm'] . ':' . $data['password']) . ':' .
+			$data['nonce'] . ':' . $data['nc'] . ':' . $data['cnonce'] . ':' . $data['qop'] . ':' .
+			md5(env('REQUEST_METHOD') . ':' . $data['uri'])
+		);
+		$this->assertIdentical($result, $expected);
+	}
+
+/**
+ * testLoginCredentials method
+ *
+ * @access public
+ * @return void
+ */
+	function testLoginCredentials() {
+		$this->Controller->Security->startup($this->Controller);
+		$_SERVER['PHP_AUTH_USER'] = $user = 'Willy Test';
+		$_SERVER['PHP_AUTH_PW'] = $pw = 'some password for the nice test';
+
+		$result = $this->Controller->Security->loginCredentials('basic');
+		$expected = array('username' => $user, 'password' => $pw);
+		$this->assertIdentical($result, $expected);
+
+		if (version_compare(PHP_VERSION, '5.1') != -1) {
+			$_SERVER['PHP_AUTH_DIGEST'] = $digest = <<<DIGEST
+				Digest username="Mufasa",
+				realm="testr****@host*****",
+				nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
+				uri="/dir/index.html",
+				qop=auth,
+				nc=00000001,
+				cnonce="0a4f113b",
+				response="6629fae49393a05397450978507c4ef1",
+				opaque="5ccc069c403ebaf9f0171e9517f40e41"
+DIGEST;
+			$expected = array(
+				'username' => 'Mufasa',
+				'realm' => 'testr****@host*****',
+				'nonce' => 'dcd98b7102dd2f0e8b11d0f600bfb0c093',
+				'uri' => '/dir/index.html',
+				'qop' => 'auth',
+				'nc' => '00000001',
+				'cnonce' => '0a4f113b',
+				'response' => '6629fae49393a05397450978507c4ef1',
+				'opaque' => '5ccc069c403ebaf9f0171e9517f40e41'
+			);
+			$result = $this->Controller->Security->loginCredentials('digest');
+			$this->assertIdentical($result, $expected);
+		}
+	}
+
+/**
+ * testParseDigestAuthData method
+ *
+ * @access public
+ * @return void
+ */
+	function testParseDigestAuthData() {
+		$this->Controller->Security->startup($this->Controller);
+		$digest = <<<DIGEST
+			Digest username="Mufasa",
+			realm="testr****@host*****",
+			nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
+			uri="/dir/index.html",
+			qop=auth,
+			nc=00000001,
+			cnonce="0a4f113b",
+			response="6629fae49393a05397450978507c4ef1",
+			opaque="5ccc069c403ebaf9f0171e9517f40e41"
+DIGEST;
+		$expected = array(
+			'username' => 'Mufasa',
+			'realm' => 'testr****@host*****',
+			'nonce' => 'dcd98b7102dd2f0e8b11d0f600bfb0c093',
+			'uri' => '/dir/index.html',
+			'qop' => 'auth',
+			'nc' => '00000001',
+			'cnonce' => '0a4f113b',
+			'response' => '6629fae49393a05397450978507c4ef1',
+			'opaque' => '5ccc069c403ebaf9f0171e9517f40e41'
+		);
+		$result = $this->Controller->Security->parseDigestAuthData($digest);
+		$this->assertIdentical($result, $expected);
+
+		$result = $this->Controller->Security->parseDigestAuthData('');
+		$this->assertNull($result);
+	}
+
+/**
+ * test parsing digest information with email addresses
+ *
+ * @return void
+ */
+	function testParseDigestAuthEmailAddress() {
+		$this->Controller->Security->startup($this->Controller);
+		$digest = <<<DIGEST
+			Digest username="mark****@examp*****",
+			realm="testr****@host*****",
+			nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
+			uri="/dir/index.html",
+			qop=auth,
+			nc=00000001,
+			cnonce="0a4f113b",
+			response="6629fae49393a05397450978507c4ef1",
+			opaque="5ccc069c403ebaf9f0171e9517f40e41"
+DIGEST;
+		$expected = array(
+			'username' => 'mark****@examp*****',
+			'realm' => 'testr****@host*****',
+			'nonce' => 'dcd98b7102dd2f0e8b11d0f600bfb0c093',
+			'uri' => '/dir/index.html',
+			'qop' => 'auth',
+			'nc' => '00000001',
+			'cnonce' => '0a4f113b',
+			'response' => '6629fae49393a05397450978507c4ef1',
+			'opaque' => '5ccc069c403ebaf9f0171e9517f40e41'
+		);
+		$result = $this->Controller->Security->parseDigestAuthData($digest);
+		$this->assertIdentical($result, $expected);
+	}
+
+/**
+ * testFormDisabledFields method
+ *
+ * @access public
+ * @return void
+ */
+	function testFormDisabledFields() {
+		$this->Controller->Security->startup($this->Controller);
+		$key = $this->Controller->params['_Token']['key'];
+		$fields = '11842060341b9d0fc3808b90ba29fdea7054d6ad%3An%3A0%3A%7B%7D';
+
+		$this->Controller->data = array(
+			'MyModel' => array('name' => 'some data'),
+			'_Token' => compact('key', 'fields')
+		);
+		$result = $this->Controller->Security->validatePost($this->Controller);
+		$this->assertFalse($result);
+
+		$this->Controller->Security->startup($this->Controller);
+		$this->Controller->Security->disabledFields = array('MyModel.name');
+		$key = $this->Controller->params['_Token']['key'];
+
+		$this->Controller->data = array(
+			'MyModel' => array('name' => 'some data'),
+			'_Token' => compact('key', 'fields')
+		);
+
+		$result = $this->Controller->Security->validatePost($this->Controller);
+		$this->assertTrue($result);
+	}
+
+/**
+ * testRadio method
+ *
+ * @access public
+ * @return void
+ */
+	function testRadio() {
+		$this->Controller->Security->startup($this->Controller);
+		$key = $this->Controller->params['_Token']['key'];
+		$fields = '575ef54ca4fc8cab468d6d898e9acd3a9671c17e%3An%3A0%3A%7B%7D';
+
+		$this->Controller->data = array(
+			'_Token' => compact('key', 'fields')
+		);
+		$result = $this->Controller->Security->validatePost($this->Controller);
+		$this->assertFalse($result);
+
+		$this->Controller->data = array(
+			'_Token' => compact('key', 'fields'),
+			'Test' => array('test' => '')
+		);
+		$result = $this->Controller->Security->validatePost($this->Controller);
+		$this->assertTrue($result);
+
+		$this->Controller->data = array(
+			'_Token' => compact('key', 'fields'),
+			'Test' => array('test' => '1')
+		);
+		$result = $this->Controller->Security->validatePost($this->Controller);
+		$this->assertTrue($result);
+
+		$this->Controller->data = array(
+			'_Token' => compact('key', 'fields'),
+			'Test' => array('test' => '2')
+		);
+		$result = $this->Controller->Security->validatePost($this->Controller);
+		$this->assertTrue($result);
+	}
+
+/**
+ * testInvalidAuthHeaders method
+ *
+ * @access public
+ * @return void
+ */
+	function testInvalidAuthHeaders() {
+		$this->Controller->Security->blackHoleCallback = null;
+		$_SERVER['PHP_AUTH_USER'] = 'admin';
+		$_SERVER['PHP_AUTH_PW'] = 'password';
+		$realm = 'cakephp.org';
+		$loginData = array('type' => 'basic', 'realm' => $realm);
+		$this->Controller->Security->requireLogin($loginData);
+		$this->Controller->Security->startup($this->Controller);
+
+		$expected = 'WWW-Authenticate: Basic realm="'.$realm.'"';
+		$this->assertEqual(count($this->Controller->testHeaders), 1);
+		$this->assertEqual(current($this->Controller->testHeaders), $expected);
+	}
+
+/**
+ * test that a requestAction's controller will have the _Token appended to
+ * the params.
+ *
+ * @return void
+ * @see http://cakephp.lighthouseapp.com/projects/42648/tickets/68
+ */
+	function testSettingTokenForRequestAction() {
+		$this->Controller->Security->startup($this->Controller);
+		$key = $this->Controller->params['_Token']['key'];
+
+		$this->Controller->params['requested'] = 1;
+		unset($this->Controller->params['_Token']);
+
+		$this->Controller->Security->startup($this->Controller);
+		$this->assertEqual($this->Controller->params['_Token']['key'], $key);
+	}
+
+/**
+ * test that blackhole doesn't delete the _Token session key so repeat data submissions
+ * stay blackholed.
+ *
+ * @link http://cakephp.lighthouseapp.com/projects/42648/tickets/214
+ * @return void
+ */
+	function testBlackHoleNotDeletingSessionInformation() {
+		$this->Controller->Security->startup($this->Controller);
+
+		$this->Controller->Security->blackHole($this->Controller, 'auth');
+		$this->assertTrue($this->Controller->Security->Session->check('_Token'), '_Token was deleted by blackHole %s');
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/controller/components/session.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/controller/components/session.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/controller/components/session.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,385 @@
+<?php
+/**
+ * SessionComponentTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ * @since         CakePHP(tm) v 1.2.0.5436
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Controller', 'Controller', false);
+App::import('Component', 'Session');
+
+/**
+ * SessionTestController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ */
+class SessionTestController extends Controller {
+
+/**
+ * uses property
+ *
+ * @var array
+ * @access public
+ */
+	var $uses = array();
+
+/**
+ * session_id method
+ *
+ * @return string
+ * @access public
+ */
+	function session_id() {
+		return $this->Session->id();
+	}
+}
+
+/**
+ * OrangeSessionTestController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ */
+class OrangeSessionTestController extends Controller {
+
+/**
+ * uses property
+ *
+ * @var array
+ * @access public
+ */
+	var $uses = array();
+
+/**
+ * session_id method
+ *
+ * @return string
+ * @access public
+ */
+	function session_id() {
+		return $this->Session->id();
+	}
+}
+
+/**
+ * SessionComponentTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ */
+class SessionComponentTest extends CakeTestCase {
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		$this->_session = Configure::read('Session');
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		Configure::write('Session', $this->_session);
+	}
+
+/**
+ * testSessionAutoStart method
+ *
+ * @access public
+ * @return void
+ */
+	function testSessionAutoStart() {
+		Configure::write('Session.start', false);
+		$Session =& new SessionComponent();
+		$this->assertFalse($Session->__active);
+		$this->assertFalse($Session->started());
+		$Session->startup(new SessionTestController());
+
+		Configure::write('Session.start', true);
+		$Session =& new SessionComponent();
+		$this->assertTrue($Session->__active);
+		$this->assertFalse($Session->started());
+		$Session->startup(new SessionTestController());
+		$this->assertTrue(isset($_SESSION));
+
+		$Object = new Object();
+		$Session =& new SessionComponent();
+		$Session->start();
+		$expected = $Session->id();
+
+		$result = $Object->requestAction('/session_test/session_id');
+		$this->assertEqual($result, $expected);
+
+		$result = $Object->requestAction('/orange_session_test/session_id');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testSessionActivate method
+ *
+ * @access public
+ * @return void
+ */
+	function testSessionActivate() {
+		$Session =& new SessionComponent();
+
+		$this->assertTrue($Session->__active);
+		$this->assertNull($Session->activate());
+		$this->assertTrue($Session->__active);
+
+		Configure::write('Session.start', false);
+		$Session =& new SessionComponent();
+		$this->assertFalse($Session->__active);
+		$this->assertNull($Session->activate());
+		$this->assertTrue($Session->__active);
+		Configure::write('Session.start', true);
+		$Session->destroy();
+	}
+
+/**
+ * testSessionValid method
+ *
+ * @access public
+ * @return void
+ */
+	function testSessionValid() {
+		$Session =& new SessionComponent();
+
+		$this->assertTrue($Session->valid());
+
+		$Session->_userAgent = 'rweerw';
+		$this->assertFalse($Session->valid());
+
+		Configure::write('Session.start', false);
+		$Session =& new SessionComponent();
+		$this->assertFalse($Session->__active);
+		$this->assertFalse($Session->valid());
+		Configure::write('Session.start', true);
+
+		$Session =& new SessionComponent();
+		$Session->time = $Session->read('Config.time') + 1;
+		$this->assertFalse($Session->valid());
+
+		Configure::write('Session.checkAgent', false);
+		$Session =& new SessionComponent();
+		$Session->time = $Session->read('Config.time') + 1;
+		$this->assertFalse($Session->valid());
+		Configure::write('Session.checkAgent', true);
+	}
+
+/**
+ * testSessionError method
+ *
+ * @access public
+ * @return void
+ */
+	function testSessionError() {
+		$Session =& new SessionComponent();
+
+		$this->assertFalse($Session->error());
+
+		Configure::write('Session.start', false);
+		$Session =& new SessionComponent();
+		$this->assertFalse($Session->__active);
+		$this->assertFalse($Session->error());
+		Configure::write('Session.start', true);
+	}
+
+/**
+ * testSessionReadWrite method
+ *
+ * @access public
+ * @return void
+ */
+	function testSessionReadWrite() {
+		$Session =& new SessionComponent();
+
+		$this->assertFalse($Session->read('Test'));
+
+		$this->assertTrue($Session->write('Test', 'some value'));
+		$this->assertEqual($Session->read('Test'), 'some value');
+		$this->assertFalse($Session->write('Test.key', 'some value'));
+		$Session->delete('Test');
+
+		$this->assertTrue($Session->write('Test.key.path', 'some value'));
+		$this->assertEqual($Session->read('Test.key.path'), 'some value');
+		$this->assertEqual($Session->read('Test.key'), array('path' => 'some value'));
+		$this->assertTrue($Session->write('Test.key.path2', 'another value'));
+		$this->assertEqual($Session->read('Test.key'), array('path' => 'some value', 'path2' => 'another value'));
+		$Session->delete('Test');
+
+		$array = array('key1' => 'val1', 'key2' => 'val2', 'key3' => 'val3');
+		$this->assertTrue($Session->write('Test', $array));
+		$this->assertEqual($Session->read('Test'), $array);
+		$Session->delete('Test');
+
+		$this->assertFalse($Session->write(array('Test'), 'some value'));
+		$this->assertTrue($Session->write(array('Test' => 'some value')));
+		$this->assertEqual($Session->read('Test'), 'some value');
+		$Session->delete('Test');
+
+		Configure::write('Session.start', false);
+		$Session =& new SessionComponent();
+		$this->assertFalse($Session->write('Test', 'some value'));
+		$Session->write('Test', 'some value');
+		$this->assertFalse($Session->read('Test'));
+		Configure::write('Session.start', true);
+	}
+
+/**
+ * testSessionDelete method
+ *
+ * @access public
+ * @return void
+ */
+	function testSessionDelete() {
+		$Session =& new SessionComponent();
+
+		$this->assertFalse($Session->delete('Test'));
+
+		$Session->write('Test', 'some value');
+		$this->assertTrue($Session->delete('Test'));
+
+		Configure::write('Session.start', false);
+		$Session =& new SessionComponent();
+		$Session->write('Test', 'some value');
+		$this->assertFalse($Session->delete('Test'));
+		Configure::write('Session.start', true);
+	}
+
+/**
+ * testSessionCheck method
+ *
+ * @access public
+ * @return void
+ */
+	function testSessionCheck() {
+		$Session =& new SessionComponent();
+
+		$this->assertFalse($Session->check('Test'));
+
+		$Session->write('Test', 'some value');
+		$this->assertTrue($Session->check('Test'));
+		$Session->delete('Test');
+
+		Configure::write('Session.start', false);
+		$Session =& new SessionComponent();
+		$Session->write('Test', 'some value');
+		$this->assertFalse($Session->check('Test'));
+		Configure::write('Session.start', true);
+	}
+
+/**
+ * testSessionFlash method
+ *
+ * @access public
+ * @return void
+ */
+	function testSessionFlash() {
+		$Session =& new SessionComponent();
+
+		$this->assertNull($Session->read('Message.flash'));
+
+		$Session->setFlash('This is a test message');
+		$this->assertEqual($Session->read('Message.flash'), array('message' => 'This is a test message', 'element' => 'default', 'params' => array()));
+
+		$Session->setFlash('This is a test message', 'test', array('name' => 'Joel Moss'));
+		$this->assertEqual($Session->read('Message.flash'), array('message' => 'This is a test message', 'element' => 'test', 'params' => array('name' => 'Joel Moss')));
+
+		$Session->setFlash('This is a test message', 'default', array(), 'myFlash');
+		$this->assertEqual($Session->read('Message.myFlash'), array('message' => 'This is a test message', 'element' => 'default', 'params' => array()));
+
+		$Session->setFlash('This is a test message', 'non_existing_layout');
+		$this->assertEqual($Session->read('Message.myFlash'), array('message' => 'This is a test message', 'element' => 'default', 'params' => array()));
+
+		$Session->delete('Message');
+	}
+
+/**
+ * testSessionId method
+ *
+ * @access public
+ * @return void
+ */
+	function testSessionId() {
+		unset($_SESSION);
+		$Session =& new SessionComponent();
+		$this->assertNull($Session->id());
+	}
+
+/**
+ * testSessionDestroy method
+ *
+ * @access public
+ * @return void
+ */
+	function testSessionDestroy() {
+		$Session =& new SessionComponent();
+
+		$Session->write('Test', 'some value');
+		$this->assertEqual($Session->read('Test'), 'some value');
+		$Session->destroy('Test');
+		$this->assertNull($Session->read('Test'));
+	}
+
+/**
+ * testSessionTimeout method
+ *
+ * @access public
+ * @return void
+ */
+	function testSessionTimeout() {
+		Configure::write('debug', 2);
+		Configure::write('Security.level', 'low');
+
+		session_destroy();
+		$Session =& new SessionComponent();
+		$Session->destroy();
+		$Session->write('Test', 'some value');
+		$this->assertEqual($Session->sessionTime, time() + (300 * Configure::read('Session.timeout')));
+		$this->assertEqual($_SESSION['Config']['timeout'], 10);
+		$this->assertEqual($_SESSION['Config']['time'], $Session->sessionTime);
+		$this->assertEqual($Session->time, time());
+		$this->assertEqual($_SESSION['Config']['time'], $Session->time + (300 * Configure::read('Session.timeout')));
+
+		Configure::write('Security.level', 'medium');
+		$Session =& new SessionComponent();
+		$Session->destroy();
+		$Session->write('Test', 'some value');
+		$this->assertEqual($Session->sessionTime, mktime() + (100 * Configure::read('Session.timeout')));
+		$this->assertEqual($_SESSION['Config']['timeout'], 10);
+		$this->assertEqual($_SESSION['Config']['time'], $Session->sessionTime);
+		$this->assertEqual($Session->time, time());
+		$this->assertEqual($_SESSION['Config']['time'], $Session->time + (Security::inactiveMins() *  Configure::read('Session.timeout')));
+
+		Configure::write('Security.level', 'high');
+		$Session =& new SessionComponent();
+		$Session->destroy();
+		$Session->write('Test', 'some value');
+		$this->assertEqual($Session->sessionTime, time() + (10 * Configure::read('Session.timeout')));
+		$this->assertEqual($_SESSION['Config']['timeout'], 10);
+		$this->assertEqual($_SESSION['Config']['time'], $Session->sessionTime);
+		$this->assertEqual($Session->time, time());
+		$this->assertEqual($_SESSION['Config']['time'], $Session->time + (Security::inactiveMins() * Configure::read('Session.timeout')));
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/controller/controller.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/controller/controller.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/controller/controller.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,1528 @@
+<?php
+/**
+ * ControllerTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP Project
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ * @since         CakePHP(tm) v 1.2.0.5436
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Controller', 'Controller', false);
+App::import('Component', 'Security');
+App::import('Component', 'Cookie');
+
+/**
+ * AppController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+if (!class_exists('AppController')) {
+	/**
+	 * AppController class
+	 *
+	 * @package       cake
+	 * @subpackage    cake.tests.cases.libs.controller
+	 */
+	class AppController extends Controller {
+	/**
+	 * helpers property
+	 *
+	 * @var array
+	 * @access public
+	 */
+		var $helpers = array('Html', 'Javascript');
+	/**
+	 * uses property
+	 *
+	 * @var array
+	 * @access public
+	 */
+		var $uses = array('ControllerPost');
+	/**
+	 * components property
+	 *
+	 * @var array
+	 * @access public
+	 */
+		var $components = array('Cookie');
+	}
+} elseif (!defined('APP_CONTROLLER_EXISTS')) {
+	define('APP_CONTROLLER_EXISTS', true);
+}
+
+/**
+ * ControllerPost class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class ControllerPost extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'ControllerPost'
+ * @access public
+ */
+	var $name = 'ControllerPost';
+
+/**
+ * useTable property
+ *
+ * @var string 'posts'
+ * @access public
+ */
+	var $useTable = 'posts';
+
+/**
+ * invalidFields property
+ *
+ * @var array
+ * @access public
+ */
+	var $invalidFields = array('name' => 'error_msg');
+
+/**
+ * lastQuery property
+ *
+ * @var mixed null
+ * @access public
+ */
+	var $lastQuery = null;
+
+/**
+ * beforeFind method
+ *
+ * @param mixed $query
+ * @access public
+ * @return void
+ */
+	function beforeFind($query) {
+		$this->lastQuery = $query;
+	}
+
+/**
+ * find method
+ *
+ * @param mixed $type
+ * @param array $options
+ * @access public
+ * @return void
+ */
+	function find($type, $options = array()) {
+		if ($type == 'popular') {
+			$conditions = array($this->name . '.' . $this->primaryKey .' > ' => '1');
+			$options = Set::merge($options, compact('conditions'));
+			return parent::find('all', $options);
+		}
+		return parent::find($type, $options);
+	}
+}
+
+/**
+ * ControllerPostsController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class ControllerCommentsController extends AppController {
+
+/**
+ * name property
+ *
+ * @var string 'ControllerPost'
+ * @access public
+ */
+	var $name = 'ControllerComments';
+}
+
+/**
+ * ControllerComment class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class ControllerComment extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'ControllerComment'
+ * @access public
+ */
+	var $name = 'Comment';
+
+/**
+ * useTable property
+ *
+ * @var string 'comments'
+ * @access public
+ */
+	var $useTable = 'comments';
+
+/**
+ * data property
+ *
+ * @var array
+ * @access public
+ */
+	var $data = array('name' => 'Some Name');
+
+/**
+ * alias property
+ *
+ * @var string 'ControllerComment'
+ * @access public
+ */
+	var $alias = 'ControllerComment';
+}
+
+/**
+ * ControllerAlias class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class ControllerAlias extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'ControllerAlias'
+ * @access public
+ */
+	var $name = 'ControllerAlias';
+
+/**
+ * alias property
+ *
+ * @var string 'ControllerSomeAlias'
+ * @access public
+ */
+	var $alias = 'ControllerSomeAlias';
+
+/**
+ * useTable property
+ *
+ * @var string 'posts'
+ * @access public
+ */
+	var $useTable = 'posts';
+}
+
+/**
+ * ControllerPaginateModel class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class ControllerPaginateModel extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string
+ * @access public
+ */
+	var $name = 'ControllerPaginateModel';
+
+/**
+ * useTable property
+ *
+ * @var string'
+ * @access public
+ */
+	var $useTable = 'comments';
+
+/**
+ * paginate method
+ *
+ * @return void
+ * @access public
+ */
+	function paginate($conditions, $fields, $order, $limit, $page, $recursive, $extra) {
+		$this->extra = $extra;
+	}
+
+/**
+ * paginateCount
+ *
+ * @access public
+ * @return void
+ */
+	function paginateCount($conditions, $recursive, $extra) {
+		$this->extraCount = $extra;
+	}
+}
+
+/**
+ * NameTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class NameTest extends CakeTestModel {
+
+/**
+ * name property
+ * @var string 'Name'
+ * @access public
+ */
+	var $name = 'Name';
+
+/**
+ * useTable property
+ * @var string 'names'
+ * @access public
+ */
+	var $useTable = 'comments';
+
+/**
+ * alias property
+ *
+ * @var string 'ControllerComment'
+ * @access public
+ */
+	var $alias = 'Name';
+}
+
+/**
+ * TestController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class TestController extends AppController {
+
+/**
+ * name property
+ * @var string 'Name'
+ * @access public
+ */
+	var $name = 'TestController';
+
+/**
+ * helpers property
+ *
+ * @var array
+ * @access public
+ */
+	var $helpers = array('Session', 'Xml');
+
+/**
+ * components property
+ *
+ * @var array
+ * @access public
+ */
+	var $components = array('Security');
+
+/**
+ * uses property
+ *
+ * @var array
+ * @access public
+ */
+	var $uses = array('ControllerComment', 'ControllerAlias');
+
+/**
+ * index method
+ *
+ * @param mixed $testId
+ * @param mixed $test2Id
+ * @access public
+ * @return void
+ */
+	function index($testId, $test2Id) {
+		$this->data['testId'] = $testId;
+		$this->data['test2Id'] = $test2Id;
+	}
+}
+
+/**
+ * TestComponent class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class TestComponent extends Object {
+
+/**
+ * beforeRedirect method
+ *
+ * @access public
+ * @return void
+ */
+	function beforeRedirect() {
+	}
+/**
+ * initialize method
+ *
+ * @access public
+ * @return void
+ */
+	function initialize(&$controller) {
+	}
+
+/**
+ * startup method
+ *
+ * @access public
+ * @return void
+ */
+	function startup(&$controller) {
+	}
+/**
+ * shutdown method
+ *
+ * @access public
+ * @return void
+ */
+	function shutdown(&$controller) {
+	}
+/**
+ * beforeRender callback
+ *
+ * @return void
+ */
+	function beforeRender(&$controller) {
+		if ($this->viewclass) {
+			$controller->view = $this->viewclass;
+		}
+	}
+}
+
+/**
+ * AnotherTestController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class AnotherTestController extends AppController {
+
+/**
+ * name property
+ * @var string 'Name'
+ * @access public
+ */
+	var $name = 'AnotherTest';
+/**
+ * uses property
+ *
+ * @var array
+ * @access public
+ */
+	var $uses = null;
+}
+
+/**
+ * ControllerTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class ControllerTest extends CakeTestCase {
+
+/**
+ * fixtures property
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array('core.post', 'core.comment', 'core.name');
+
+/**
+ * endTest
+ *
+ * @access public
+ * @return void
+ */
+	function endTest() {
+		App::build();
+	}
+
+/**
+ * testLoadModel method
+ *
+ * @access public
+ * @return void
+ */
+	function testLoadModel() {
+		$Controller =& new Controller();
+
+		$this->assertFalse(isset($Controller->ControllerPost));
+
+		$result = $Controller->loadModel('ControllerPost');
+		$this->assertTrue($result);
+		$this->assertTrue(is_a($Controller->ControllerPost, 'ControllerPost'));
+		$this->assertTrue(in_array('ControllerPost', $Controller->modelNames));
+
+		ClassRegistry::flush();
+		unset($Controller);
+	}
+
+/**
+ * testConstructClasses method
+ *
+ * @access public
+ * @return void
+ */
+	function testConstructClasses() {
+		$Controller =& new Controller();
+		$Controller->modelClass = 'ControllerPost';
+		$Controller->passedArgs[] = '1';
+		$Controller->constructClasses();
+		$this->assertEqual($Controller->ControllerPost->id, 1);
+
+		unset($Controller);
+
+		$Controller =& new Controller();
+		$Controller->uses = array('ControllerPost', 'ControllerComment');
+		$Controller->passedArgs[] = '1';
+		$Controller->constructClasses();
+		$this->assertTrue(is_a($Controller->ControllerPost, 'ControllerPost'));
+		$this->assertTrue(is_a($Controller->ControllerComment, 'ControllerComment'));
+
+		$this->assertEqual($Controller->ControllerComment->name, 'Comment');
+
+		unset($Controller);
+
+		App::build(array('plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)));
+
+		$Controller =& new Controller();
+		$Controller->uses = array('TestPlugin.TestPluginPost');
+		$Controller->constructClasses();
+
+		$this->assertEqual($Controller->modelClass, 'TestPluginPost');
+		$this->assertTrue(isset($Controller->TestPluginPost));
+		$this->assertTrue(is_a($Controller->TestPluginPost, 'TestPluginPost'));
+
+		unset($Controller);
+	}
+
+/**
+ * testAliasName method
+ *
+ * @access public
+ * @return void
+ */
+	function testAliasName() {
+		$Controller =& new Controller();
+		$Controller->uses = array('NameTest');
+		$Controller->constructClasses();
+
+		$this->assertEqual($Controller->NameTest->name, 'Name');
+		$this->assertEqual($Controller->NameTest->alias, 'Name');
+
+		unset($Controller);
+	}
+
+/**
+ * testPersistent method
+ *
+ * @access public
+ * @return void
+ */
+	function testPersistent() {
+		Configure::write('Cache.disable', false);
+		$Controller =& new Controller();
+		$Controller->modelClass = 'ControllerPost';
+		$Controller->persistModel = true;
+		$Controller->constructClasses();
+		$this->assertTrue(file_exists(CACHE . 'persistent' . DS .'controllerpost.php'));
+		$this->assertTrue(is_a($Controller->ControllerPost, 'ControllerPost'));
+		@unlink(CACHE . 'persistent' . DS . 'controllerpost.php');
+		@unlink(CACHE . 'persistent' . DS . 'controllerpostregistry.php');
+
+		unset($Controller);
+		Configure::write('Cache.disable', true);
+	}
+
+/**
+ * testPaginate method
+ *
+ * @access public
+ * @return void
+ */
+	function testPaginate() {
+		$Controller =& new Controller();
+		$Controller->uses = array('ControllerPost', 'ControllerComment');
+		$Controller->passedArgs[] = '1';
+		$Controller->params['url'] = array();
+		$Controller->constructClasses();
+
+		$results = Set::extract($Controller->paginate('ControllerPost'), '{n}.ControllerPost.id');
+		$this->assertEqual($results, array(1, 2, 3));
+
+		$results = Set::extract($Controller->paginate('ControllerComment'), '{n}.ControllerComment.id');
+		$this->assertEqual($results, array(1, 2, 3, 4, 5, 6));
+
+		$Controller->modelClass = null;
+
+		$Controller->uses[0] = 'Plugin.ControllerPost';
+		$results = Set::extract($Controller->paginate(), '{n}.ControllerPost.id');
+		$this->assertEqual($results, array(1, 2, 3));
+
+		$Controller->passedArgs = array('page' => '-1');
+		$results = Set::extract($Controller->paginate('ControllerPost'), '{n}.ControllerPost.id');
+		$this->assertEqual($Controller->params['paging']['ControllerPost']['page'], 1);
+		$this->assertEqual($results, array(1, 2, 3));
+
+		$Controller->passedArgs = array('sort' => 'ControllerPost.id', 'direction' => 'asc');
+		$results = Set::extract($Controller->paginate('ControllerPost'), '{n}.ControllerPost.id');
+		$this->assertEqual($Controller->params['paging']['ControllerPost']['page'], 1);
+		$this->assertEqual($results, array(1, 2, 3));
+
+		$Controller->passedArgs = array('sort' => 'ControllerPost.id', 'direction' => 'desc');
+		$results = Set::extract($Controller->paginate('ControllerPost'), '{n}.ControllerPost.id');
+		$this->assertEqual($Controller->params['paging']['ControllerPost']['page'], 1);
+		$this->assertEqual($results, array(3, 2, 1));
+
+		$Controller->passedArgs = array('sort' => 'id', 'direction' => 'desc');
+		$results = Set::extract($Controller->paginate('ControllerPost'), '{n}.ControllerPost.id');
+		$this->assertEqual($Controller->params['paging']['ControllerPost']['page'], 1);
+		$this->assertEqual($results, array(3, 2, 1));
+
+		$Controller->passedArgs = array('sort' => 'NotExisting.field', 'direction' => 'desc');
+		$results = Set::extract($Controller->paginate('ControllerPost'), '{n}.ControllerPost.id');
+		$this->assertEqual($Controller->params['paging']['ControllerPost']['page'], 1, 'Invalid field in query %s');
+		$this->assertEqual($results, array(1, 2, 3));
+
+		$Controller->passedArgs = array('sort' => 'ControllerPost.author_id', 'direction' => 'allYourBase');
+		$results = Set::extract($Controller->paginate('ControllerPost'), '{n}.ControllerPost.id');
+		$this->assertEqual($Controller->ControllerPost->lastQuery['order'][0], array('ControllerPost.author_id' => 'asc'));
+		$this->assertEqual($results, array(1, 3, 2));
+
+		$Controller->passedArgs = array('page' => '1 " onclick="alert(\'xss\');">');
+		$Controller->paginate = array('limit' => 1);
+		$Controller->paginate('ControllerPost');
+		$this->assertIdentical($Controller->params['paging']['ControllerPost']['page'], 1, 'XSS exploit opened %s');
+		$this->assertIdentical($Controller->params['paging']['ControllerPost']['options']['page'], 1, 'XSS exploit opened %s');
+
+		$Controller->passedArgs = array();
+		$Controller->paginate = array('limit' => 0);
+		$Controller->paginate('ControllerPost');
+		$this->assertIdentical($Controller->params['paging']['ControllerPost']['page'], 1);
+		$this->assertIdentical($Controller->params['paging']['ControllerPost']['pageCount'], 3);
+		$this->assertIdentical($Controller->params['paging']['ControllerPost']['prevPage'], false);
+		$this->assertIdentical($Controller->params['paging']['ControllerPost']['nextPage'], true);
+
+		$Controller->passedArgs = array();
+		$Controller->paginate = array('limit' => 'garbage!');
+		$Controller->paginate('ControllerPost');
+		$this->assertIdentical($Controller->params['paging']['ControllerPost']['page'], 1);
+		$this->assertIdentical($Controller->params['paging']['ControllerPost']['pageCount'], 3);
+		$this->assertIdentical($Controller->params['paging']['ControllerPost']['prevPage'], false);
+		$this->assertIdentical($Controller->params['paging']['ControllerPost']['nextPage'], true);
+
+		$Controller->passedArgs = array();
+		$Controller->paginate = array('limit' => '-1');
+		$Controller->paginate('ControllerPost');
+		$this->assertIdentical($Controller->params['paging']['ControllerPost']['page'], 1);
+		$this->assertIdentical($Controller->params['paging']['ControllerPost']['pageCount'], 3);
+		$this->assertIdentical($Controller->params['paging']['ControllerPost']['prevPage'], false);
+		$this->assertIdentical($Controller->params['paging']['ControllerPost']['nextPage'], true);
+	}
+
+/**
+ * testPaginateExtraParams method
+ *
+ * @access public
+ * @return void
+ */
+	function testPaginateExtraParams() {
+		$Controller =& new Controller();
+		$Controller->uses = array('ControllerPost', 'ControllerComment');
+		$Controller->passedArgs[] = '1';
+		$Controller->params['url'] = array();
+		$Controller->constructClasses();
+
+		$Controller->passedArgs = array('page' => '-1', 'contain' => array('ControllerComment'));
+		$result = $Controller->paginate('ControllerPost');
+		$this->assertEqual($Controller->params['paging']['ControllerPost']['page'], 1);
+		$this->assertEqual(Set::extract($result, '{n}.ControllerPost.id'), array(1, 2, 3));
+		$this->assertTrue(!isset($Controller->ControllerPost->lastQuery['contain']));
+
+		$Controller->passedArgs = array('page' => '-1');
+		$Controller->paginate = array('ControllerPost' => array('contain' => array('ControllerComment')));
+		$result = $Controller->paginate('ControllerPost');
+		$this->assertEqual($Controller->params['paging']['ControllerPost']['page'], 1);
+		$this->assertEqual(Set::extract($result, '{n}.ControllerPost.id'), array(1, 2, 3));
+		$this->assertTrue(isset($Controller->ControllerPost->lastQuery['contain']));
+
+		$Controller->paginate = array('ControllerPost' => array('popular', 'fields' => array('id', 'title')));
+		$result = $Controller->paginate('ControllerPost');
+		$this->assertEqual(Set::extract($result, '{n}.ControllerPost.id'), array(2, 3));
+		$this->assertEqual($Controller->ControllerPost->lastQuery['conditions'], array('ControllerPost.id > ' => '1'));
+
+		$Controller->passedArgs = array('limit' => 12);
+		$Controller->paginate = array('limit' => 30);
+		$result = $Controller->paginate('ControllerPost');
+		$paging = $Controller->params['paging']['ControllerPost'];
+
+		$this->assertEqual($Controller->ControllerPost->lastQuery['limit'], 12);
+		$this->assertEqual($paging['options']['limit'], 12);
+
+		$Controller =& new Controller();
+		$Controller->uses = array('ControllerPaginateModel');
+		$Controller->params['url'] = array();
+		$Controller->constructClasses();
+		$Controller->paginate = array(
+			'ControllerPaginateModel' => array('contain' => array('ControllerPaginateModel'), 'group' => 'Comment.author_id')
+		);
+		$result = $Controller->paginate('ControllerPaginateModel');
+		$expected = array('contain' => array('ControllerPaginateModel'), 'group' => 'Comment.author_id');
+		$this->assertEqual($Controller->ControllerPaginateModel->extra, $expected);
+		$this->assertEqual($Controller->ControllerPaginateModel->extraCount, $expected);
+
+		$Controller->paginate = array(
+			'ControllerPaginateModel' => array('foo', 'contain' => array('ControllerPaginateModel'), 'group' => 'Comment.author_id')
+		);
+		$Controller->paginate('ControllerPaginateModel');
+		$expected = array('contain' => array('ControllerPaginateModel'), 'group' => 'Comment.author_id', 'type' => 'foo');
+		$this->assertEqual($Controller->ControllerPaginateModel->extra, $expected);
+		$this->assertEqual($Controller->ControllerPaginateModel->extraCount, $expected);
+	}
+
+/**
+ * testPaginateFieldsDouble method
+ *
+ * @return void
+ * @access public
+ */
+	function testPaginateFieldsDouble(){
+		$Controller =& new Controller();
+		$Controller->uses = array('ControllerPost');
+		$Controller->params['url'] = array();
+		$Controller->constructClasses();
+
+		$Controller->paginate = array(
+			'fields' => array(
+				'ControllerPost.id',
+				'radians(180.0) as floatvalue'
+			),
+			'order' => array('ControllerPost.created'=>'DESC'),
+			'limit' => 1,
+			'page' => 1,
+			'recursive' => -1
+		);
+		$conditions = array();
+		$result = $Controller->paginate('ControllerPost',$conditions);
+		$expected = array(
+			array(
+				'ControllerPost' => array(
+					'id' => 3,
+				),
+				0 => array(
+					'floatvalue' => '3.14159265358979',
+				),
+			),
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+
+/**
+ * testPaginatePassedArgs method
+ *
+ * @return void
+ * @access public
+ */
+	function testPaginatePassedArgs() {
+		$Controller =& new Controller();
+		$Controller->uses = array('ControllerPost');
+		$Controller->passedArgs[] = array('1', '2', '3');
+		$Controller->params['url'] = array();
+		$Controller->constructClasses();
+
+		$Controller->paginate = array(
+			'fields' => array(),
+			'order' => '',
+			'limit' => 5,
+			'page' => 1,
+			'recursive' => -1
+		);
+		$conditions = array();
+		$Controller->paginate('ControllerPost',$conditions);
+
+		$expected = array(
+			'fields' => array(),
+			'order' => '',
+			'limit' => 5,
+			'page' => 1,
+			'recursive' => -1,
+			'conditions' => array()
+		);
+		$this->assertEqual($Controller->params['paging']['ControllerPost']['options'],$expected);
+	}
+
+/**
+ * Test that special paginate types are called and that the type param doesn't leak out into defaults or options.
+ *
+ * @return void
+ */
+	function testPaginateSpecialType() {
+		$Controller =& new Controller();
+		$Controller->uses = array('ControllerPost', 'ControllerComment');
+		$Controller->passedArgs[] = '1';
+		$Controller->params['url'] = array();
+		$Controller->constructClasses();
+
+		$Controller->paginate = array('ControllerPost' => array('popular', 'fields' => array('id', 'title')));
+		$result = $Controller->paginate('ControllerPost');
+
+		$this->assertEqual(Set::extract($result, '{n}.ControllerPost.id'), array(2, 3));
+		$this->assertEqual($Controller->ControllerPost->lastQuery['conditions'], array('ControllerPost.id > ' => '1'));
+		$this->assertFalse(isset($Controller->params['paging']['ControllerPost']['defaults'][0]));
+		$this->assertFalse(isset($Controller->params['paging']['ControllerPost']['options'][0]));
+	}
+
+/**
+ * testDefaultPaginateParams method
+ *
+ * @access public
+ * @return void
+ */
+	function testDefaultPaginateParams() {
+		$Controller =& new Controller();
+		$Controller->modelClass = 'ControllerPost';
+		$Controller->params['url'] = array();
+		$Controller->paginate = array('order' => 'ControllerPost.id DESC');
+		$Controller->constructClasses();
+		$results = Set::extract($Controller->paginate('ControllerPost'), '{n}.ControllerPost.id');
+		$this->assertEqual($Controller->params['paging']['ControllerPost']['defaults']['order'], 'ControllerPost.id DESC');
+		$this->assertEqual($Controller->params['paging']['ControllerPost']['options']['order'], 'ControllerPost.id DESC');
+		$this->assertEqual($results, array(3, 2, 1));
+	}
+
+/**
+ * test paginate() and virtualField interactions
+ *
+ * @return void
+ */
+	function testPaginateOrderVirtualField() {
+		$Controller =& new Controller();
+		$Controller->uses = array('ControllerPost', 'ControllerComment');
+		$Controller->params['url'] = array();
+		$Controller->constructClasses();
+		$Controller->ControllerPost->virtualFields = array(
+			'offset_test' => 'ControllerPost.id + 1'
+		);
+
+		$Controller->paginate = array(
+			'fields' => array('id', 'title', 'offset_test'),
+			'order' => array('offset_test' => 'DESC')
+		);
+		$result = $Controller->paginate('ControllerPost');
+		$this->assertEqual(Set::extract($result, '{n}.ControllerPost.offset_test'), array(4, 3, 2));
+
+		$Controller->passedArgs = array('sort' => 'offset_test', 'direction' => 'asc');
+		$result = $Controller->paginate('ControllerPost');
+		$this->assertEqual(Set::extract($result, '{n}.ControllerPost.offset_test'), array(2, 3, 4));
+	}
+
+/**
+ * test paginate() and virtualField overlapping with real fields.
+ *
+ * @return void
+ */
+	function testPaginateOrderVirtualFieldSharedWithRealField() {
+		$Controller =& new Controller();
+		$Controller->uses = array('ControllerPost', 'ControllerComment');
+		$Controller->params['url'] = array();
+		$Controller->constructClasses();
+		$Controller->ControllerComment->virtualFields = array(
+			'title' => 'ControllerComment.comment'
+		);
+		$Controller->ControllerComment->bindModel(array(
+			'belongsTo' => array(
+				'ControllerPost' => array(
+					'className' => 'ControllerPost',
+					'foreignKey' => 'article_id'
+				)
+			)
+		), false);
+
+		$Controller->paginate = array(
+			'fields' => array('ControllerComment.id', 'title', 'ControllerPost.title'),
+		);
+		$Controller->passedArgs = array('sort' => 'ControllerPost.title', 'dir' => 'asc');
+		$result = $Controller->paginate('ControllerComment');
+		$this->assertEqual(Set::extract($result, '{n}.ControllerComment.id'), array(1, 2, 3, 4, 5, 6));
+	}
+
+/**
+ * testFlash method
+ *
+ * @access public
+ * @return void
+ */
+	function testFlash() {
+		$Controller =& new Controller();
+		$Controller->flash('this should work', '/flash');
+		$result = $Controller->output;
+
+		$expected = '<!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; charset=utf-8" />
+		<title>this should work</title>
+		<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="/flash">this should work</a></p>
+		</body>
+		</html>';
+		$result = str_replace(array("\t", "\r\n", "\n"), "", $result);
+		$expected =  str_replace(array("\t", "\r\n", "\n"), "", $expected);
+		$this->assertEqual($result, $expected);
+
+		App::build(array('views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS)));
+		$Controller =& new Controller();
+		$Controller->flash('this should work', '/flash', 1, 'ajax2');
+		$result = $Controller->output;
+		$this->assertPattern('/Ajax!/', $result);
+		App::build();
+	}
+
+/**
+ * testControllerSet method
+ *
+ * @access public
+ * @return void
+ */
+	function testControllerSet() {
+		$Controller =& new Controller();
+		$Controller->set('variable_with_underscores', null);
+		$this->assertTrue(array_key_exists('variable_with_underscores', $Controller->viewVars));
+
+		$Controller->viewVars = array();
+		$viewVars = array('ModelName' => array('id' => 1, 'name' => 'value'));
+		$Controller->set($viewVars);
+		$this->assertTrue(array_key_exists('ModelName', $Controller->viewVars));
+
+		$Controller->viewVars = array();
+		$Controller->set('variable_with_underscores', 'value');
+		$this->assertTrue(array_key_exists('variable_with_underscores', $Controller->viewVars));
+
+		$Controller->viewVars = array();
+		$viewVars = array('ModelName' => 'name');
+		$Controller->set($viewVars);
+		$this->assertTrue(array_key_exists('ModelName', $Controller->viewVars));
+
+		$Controller->set('title', 'someTitle');
+		$this->assertIdentical($Controller->viewVars['title'], 'someTitle');
+		$this->assertTrue(empty($Controller->pageTitle));
+
+		$Controller->viewVars = array();
+		$expected = array('ModelName' => 'name', 'ModelName2' => 'name2');
+		$Controller->set(array('ModelName', 'ModelName2'), array('name', 'name2'));
+		$this->assertIdentical($Controller->viewVars, $expected);
+
+		$Controller->viewVars = array();
+		$Controller->set(array(3 => 'three', 4 => 'four'));
+		$Controller->set(array(1 => 'one', 2 => 'two'));
+		$expected = array(3 => 'three', 4 => 'four', 1 => 'one', 2 => 'two');
+		$this->assertEqual($Controller->viewVars, $expected);
+		
+	}
+
+/**
+ * testRender method
+ *
+ * @access public
+ * @return void
+ */
+	function testRender() {
+		App::build(array(
+			'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS)
+		), true);
+
+		$Controller =& new Controller();
+		$Controller->viewPath = 'posts';
+
+		$result = $Controller->render('index');
+		$this->assertPattern('/posts index/', $result);
+
+		$result = $Controller->render('/elements/test_element');
+		$this->assertPattern('/this is the test element/', $result);
+
+		$Controller = new TestController();
+		$Controller->constructClasses();
+		$Controller->ControllerComment->validationErrors = array('title' => 'tooShort');
+		$expected = $Controller->ControllerComment->validationErrors;
+
+		ClassRegistry::flush();
+		$Controller->viewPath = 'posts';
+		$result = $Controller->render('index');
+		$View = ClassRegistry::getObject('view');
+		$this->assertTrue(isset($View->validationErrors['ControllerComment']));
+		$this->assertEqual($expected, $View->validationErrors['ControllerComment']);
+
+		$Controller->ControllerComment->validationErrors = array();
+		ClassRegistry::flush();
+		App::build();
+	}
+
+/**
+ * test that a component beforeRender can change the controller view class.
+ *
+ * @return void
+ */
+	function testComponentBeforeRenderChangingViewClass() {
+		$core = App::core('views');
+		App::build(array(
+			'views' => array(
+				TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS,
+				$core[0]
+			)
+		), true);
+		$Controller =& new Controller();
+		$Controller->uses = array();
+		$Controller->components = array('Test');
+		$Controller->constructClasses();
+		$Controller->Test->viewclass = 'Theme';
+		$Controller->viewPath = 'posts';
+		$Controller->theme = 'test_theme';
+		$result = $Controller->render('index');
+		$this->assertPattern('/default test_theme layout/', $result);
+		App::build();
+	}
+
+/**
+ * testToBeInheritedGuardmethods method
+ *
+ * @access public
+ * @return void
+ */
+	function testToBeInheritedGuardmethods() {
+		$Controller =& new Controller();
+		$this->assertTrue($Controller->_beforeScaffold(''));
+		$this->assertTrue($Controller->_afterScaffoldSave(''));
+		$this->assertTrue($Controller->_afterScaffoldSaveError(''));
+		$this->assertFalse($Controller->_scaffoldError(''));
+	}
+
+/**
+ * testRedirect method
+ *
+ * @access public
+ * @return void
+ */
+	function testRedirect() {
+		$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"
+		);
+
+		Mock::generatePartial('Controller', 'MockController', array('header'));
+		Mock::generate('TestComponent', 'MockTestComponent');
+		Mock::generate('TestComponent', 'MockTestBComponent');
+
+		App::import('Helper', 'Cache');
+
+		foreach ($codes as $code => $msg) {
+			$MockController =& new MockController();
+			$MockController->Component =& new Component();
+			$MockController->Component->init($MockController);
+			$MockController->expectAt(0, 'header', array("HTTP/1.1 {$code} {$msg}"));
+			$MockController->expectAt(1, 'header', array('Location: http://cakephp.org'));
+			$MockController->expectCallCount('header', 2);
+			$MockController->redirect('http://cakephp.org', (int)$code, false);
+			$this->assertFalse($MockController->autoRender);
+		}
+		foreach ($codes as $code => $msg) {
+			$MockController =& new MockController();
+			$MockController->Component =& new Component();
+			$MockController->Component->init($MockController);
+			$MockController->expectAt(0, 'header', array("HTTP/1.1 {$code} {$msg}"));
+			$MockController->expectAt(1, 'header', array('Location: http://cakephp.org'));
+			$MockController->expectCallCount('header', 2);
+			$MockController->redirect('http://cakephp.org', $msg, false);
+			$this->assertFalse($MockController->autoRender);
+		}
+
+		$MockController =& new MockController();
+		$MockController->Component =& new Component();
+		$MockController->Component->init($MockController);
+		$MockController->expectAt(0, 'header', array('Location: http://www.example.org/users/login'));
+		$MockController->expectCallCount('header', 1);
+		$MockController->redirect('http://www.example.org/users/login', null, false);
+
+		$MockController =& new MockController();
+		$MockController->Component =& new Component();
+		$MockController->Component->init($MockController);
+		$MockController->expectAt(0, 'header', array('HTTP/1.1 301 Moved Permanently'));
+		$MockController->expectAt(1, 'header', array('Location: http://www.example.org/users/login'));
+		$MockController->expectCallCount('header', 2);
+		$MockController->redirect('http://www.example.org/users/login', 301, false);
+
+		$MockController =& new MockController();
+		$MockController->components = array('MockTest');
+		$MockController->Component =& new Component();
+		$MockController->Component->init($MockController);
+		$MockController->MockTest->setReturnValue('beforeRedirect', null);
+		$MockController->expectAt(0, 'header', array('HTTP/1.1 301 Moved Permanently'));
+		$MockController->expectAt(1, 'header', array('Location: http://cakephp.org'));
+		$MockController->expectCallCount('header', 2);
+		$MockController->redirect('http://cakephp.org', 301, false);
+
+		$MockController =& new MockController();
+		$MockController->components = array('MockTest');
+		$MockController->Component =& new Component();
+		$MockController->Component->init($MockController);
+		$MockController->MockTest->setReturnValue('beforeRedirect', 'http://book.cakephp.org');
+		$MockController->expectAt(0, 'header', array('HTTP/1.1 301 Moved Permanently'));
+		$MockController->expectAt(1, 'header', array('Location: http://book.cakephp.org'));
+		$MockController->expectCallCount('header', 2);
+		$MockController->redirect('http://cakephp.org', 301, false);
+
+		$MockController =& new MockController();
+		$MockController->components = array('MockTest');
+		$MockController->Component =& new Component();
+		$MockController->Component->init($MockController);
+		$MockController->MockTest->setReturnValue('beforeRedirect', false);
+		$MockController->expectNever('header');
+		$MockController->redirect('http://cakephp.org', 301, false);
+
+		$MockController =& new MockController();
+		$MockController->components = array('MockTest', 'MockTestB');
+		$MockController->Component =& new Component();
+		$MockController->Component->init($MockController);
+		$MockController->MockTest->setReturnValue('beforeRedirect', 'http://book.cakephp.org');
+		$MockController->MockTestB->setReturnValue('beforeRedirect', 'http://bakery.cakephp.org');
+		$MockController->expectAt(0, 'header', array('HTTP/1.1 301 Moved Permanently'));
+		$MockController->expectAt(1, 'header', array('Location: http://bakery.cakephp.org'));
+		$MockController->expectCallCount('header', 2);
+		$MockController->redirect('http://cakephp.org', 301, false);
+	}
+
+/**
+ * testMergeVars method
+ *
+ * @access public
+ * @return void
+ */
+	function testMergeVars() {
+		if ($this->skipIf(defined('APP_CONTROLLER_EXISTS'), '%s Need a non-existent AppController')) {
+			return;
+		}
+
+		$TestController =& new TestController();
+		$TestController->constructClasses();
+
+		$testVars = get_class_vars('TestController');
+		$appVars = get_class_vars('AppController');
+
+		$components = is_array($appVars['components'])
+						? array_merge($appVars['components'], $testVars['components'])
+						: $testVars['components'];
+		if (!in_array('Session', $components)) {
+			$components[] = 'Session';
+		}
+		$helpers = is_array($appVars['helpers'])
+					? array_merge($appVars['helpers'], $testVars['helpers'])
+					: $testVars['helpers'];
+		$uses = is_array($appVars['uses'])
+					? array_merge($appVars['uses'], $testVars['uses'])
+					: $testVars['uses'];
+
+		$this->assertEqual(count(array_diff_assoc(Set::normalize($TestController->helpers), Set::normalize($helpers))), 0);
+		$this->assertEqual(count(array_diff($TestController->uses, $uses)), 0);
+		$this->assertEqual(count(array_diff_assoc(Set::normalize($TestController->components), Set::normalize($components))), 0);
+
+		$expected = array('ControllerComment', 'ControllerAlias', 'ControllerPost');
+		$this->assertEqual($expected, $TestController->uses, '$uses was merged incorrectly, AppController models should be last.');
+		
+
+		$TestController =& new AnotherTestController();
+		$TestController->constructClasses();
+
+		$appVars = get_class_vars('AppController');
+		$testVars = get_class_vars('AnotherTestController');
+
+
+		$this->assertTrue(in_array('ControllerPost', $appVars['uses']));
+		$this->assertNull($testVars['uses']);
+
+		$this->assertFalse(isset($TestController->ControllerPost));
+
+
+		$TestController =& new ControllerCommentsController();
+		$TestController->constructClasses();
+
+		$appVars = get_class_vars('AppController');
+		$testVars = get_class_vars('ControllerCommentsController');
+
+
+		$this->assertTrue(in_array('ControllerPost', $appVars['uses']));
+		$this->assertEqual(array('ControllerPost'), $testVars['uses']);
+
+		$this->assertTrue(isset($TestController->ControllerPost));
+		$this->assertTrue(isset($TestController->ControllerComment));
+	}
+
+/**
+ * test that options from child classes replace those in the parent classes.
+ *
+ * @access public
+ * @return void
+ */
+	function testChildComponentOptionsSupercedeParents() {
+		if ($this->skipIf(defined('APP_CONTROLLER_EXISTS'), '%s Need a non-existent AppController')) {
+			return;
+		}
+		$TestController =& new TestController();
+		$expected = array('foo');
+		$TestController->components = array('Cookie' => $expected);
+		$TestController->constructClasses();
+		$this->assertEqual($TestController->components['Cookie'], $expected);
+	}
+
+/**
+ * Ensure that __mergeVars is not being greedy and merging with
+ * AppController when you make an instance of Controller
+ *
+ * @return void
+ */
+	function testMergeVarsNotGreedy() {
+		$Controller =& new Controller();
+		$Controller->components = array();
+		$Controller->uses = array();
+		$Controller->constructClasses();
+
+		$this->assertFalse(isset($Controller->Session));
+	}
+
+/**
+ * testReferer method
+ *
+ * @access public
+ * @return void
+ */
+	function testReferer() {
+		$Controller =& new Controller();
+		$_SERVER['HTTP_REFERER'] = 'http://cakephp.org';
+		$result = $Controller->referer(null, false);
+		$expected = 'http://cakephp.org';
+		$this->assertIdentical($result, $expected);
+
+		$_SERVER['HTTP_REFERER'] = '';
+		$result = $Controller->referer('http://cakephp.org', false);
+		$expected = 'http://cakephp.org';
+		$this->assertIdentical($result, $expected);
+
+		$_SERVER['HTTP_REFERER'] = '';
+		$referer = array(
+			'controller' => 'pages',
+			'action' => 'display',
+			'home'
+		);
+		$result = $Controller->referer($referer, false);
+		$expected = 'http://' . env('HTTP_HOST') . '/pages/display/home';
+		$this->assertIdentical($result, $expected);
+
+		$_SERVER['HTTP_REFERER'] = '';
+		$result = $Controller->referer(null, false);
+		$expected = '/';
+		$this->assertIdentical($result, $expected);
+
+		$_SERVER['HTTP_REFERER'] = FULL_BASE_URL.$Controller->webroot.'/some/path';
+		$result = $Controller->referer(null, false);
+		$expected = '/some/path';
+		$this->assertIdentical($result, $expected);
+
+		$Controller->webroot .= '/';
+		$_SERVER['HTTP_REFERER'] = FULL_BASE_URL.$Controller->webroot.'/some/path';
+		$result = $Controller->referer(null, false);
+		$expected = '/some/path';
+		$this->assertIdentical($result, $expected);
+
+		$_SERVER['HTTP_REFERER'] = FULL_BASE_URL.$Controller->webroot.'some/path';
+		$result = $Controller->referer(null, false);
+		$expected = '/some/path';
+		$this->assertIdentical($result, $expected);
+
+		$Controller->webroot = '/recipe/';
+
+		$_SERVER['HTTP_REFERER'] = FULL_BASE_URL.$Controller->webroot.'recipes/add';
+		$result = $Controller->referer();
+		$expected = '/recipes/add';
+		$this->assertIdentical($result, $expected);
+	}
+
+/**
+ * testSetAction method
+ *
+ * @access public
+ * @return void
+ */
+	function testSetAction() {
+		$TestController =& new TestController();
+		$TestController->setAction('index', 1, 2);
+		$expected = array('testId' => 1, 'test2Id' => 2);
+		$this->assertidentical($TestController->data, $expected);
+	}
+
+/**
+ * testUnimplementedIsAuthorized method
+ *
+ * @access public
+ * @return void
+ */
+	function testUnimplementedIsAuthorized() {
+		$TestController =& new TestController();
+		$TestController->isAuthorized();
+		$this->assertError();
+	}
+
+/**
+ * testValidateErrors method
+ *
+ * @access public
+ * @return void
+ */
+	function testValidateErrors() {
+		$TestController =& new TestController();
+		$TestController->constructClasses();
+		$this->assertFalse($TestController->validateErrors());
+		$this->assertEqual($TestController->validate(), 0);
+
+		$TestController->ControllerComment->invalidate('some_field', 'error_message');
+		$TestController->ControllerComment->invalidate('some_field2', 'error_message2');
+		$comment =& new ControllerComment();
+		$comment->set('someVar', 'data');
+		$result = $TestController->validateErrors($comment);
+		$expected = array('some_field' => 'error_message', 'some_field2' => 'error_message2');
+		$this->assertIdentical($result, $expected);
+		$this->assertEqual($TestController->validate($comment), 2);
+	}
+
+/**
+ * test that validateErrors works with any old model.
+ *
+ * @return void
+ */
+	function testValidateErrorsOnArbitraryModels() {
+		$TestController =& new TestController();
+
+		$Post = new ControllerPost();
+		$Post->validate = array('title' => 'notEmpty');
+		$Post->set('title', '');
+		$result = $TestController->validateErrors($Post);
+
+		$expected = array('title' => 'This field cannot be left blank');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testPostConditions method
+ *
+ * @access public
+ * @return void
+ */
+	function testPostConditions() {
+		$Controller =& new Controller();
+
+
+		$data = array(
+			'Model1' => array('field1' => '23'),
+			'Model2' => array('field2' => 'string'),
+			'Model3' => array('field3' => '23'),
+		);
+		$expected = array(
+			'Model1.field1' => '23',
+			'Model2.field2' => 'string',
+			'Model3.field3' => '23',
+		);
+		$result = $Controller->postConditions($data);
+		$this->assertIdentical($result, $expected);
+
+
+		$data = array();
+		$Controller->data = array(
+			'Model1' => array('field1' => '23'),
+			'Model2' => array('field2' => 'string'),
+			'Model3' => array('field3' => '23'),
+		);
+		$expected = array(
+			'Model1.field1' => '23',
+			'Model2.field2' => 'string',
+			'Model3.field3' => '23',
+		);
+		$result = $Controller->postConditions($data);
+		$this->assertIdentical($result, $expected);
+
+
+		$data = array();
+		$Controller->data = array();
+		$result = $Controller->postConditions($data);
+		$this->assertNull($result);
+
+
+		$data = array();
+		$Controller->data = array(
+			'Model1' => array('field1' => '23'),
+			'Model2' => array('field2' => 'string'),
+			'Model3' => array('field3' => '23'),
+		);
+		$ops = array(
+			'Model1.field1' => '>',
+			'Model2.field2' => 'LIKE',
+			'Model3.field3' => '<=',
+		);
+		$expected = array(
+			'Model1.field1 >' => '23',
+			'Model2.field2 LIKE' => "%string%",
+			'Model3.field3 <=' => '23',
+		);
+		$result = $Controller->postConditions($data, $ops);
+		$this->assertIdentical($result, $expected);
+	}
+
+/**
+ * testRequestHandlerPrefers method
+ *
+ * @access public
+ * @return void
+ */
+	function testRequestHandlerPrefers(){
+		Configure::write('debug', 2);
+		$Controller =& new Controller();
+		$Controller->components = array("RequestHandler");
+		$Controller->modelClass='ControllerPost';
+		$Controller->params['url']['ext'] = 'rss';
+		$Controller->constructClasses();
+		$Controller->Component->initialize($Controller);
+		$Controller->beforeFilter();
+		$Controller->Component->startup($Controller);
+
+		$this->assertEqual($Controller->RequestHandler->prefers(), 'rss');
+		unset($Controller);
+	}
+
+/**
+ * testControllerHttpCodes method
+ *
+ * @access public
+ * @return void
+ */
+	function testControllerHttpCodes() {
+		$Controller =& new Controller();
+		$result = $Controller->httpCodes();
+		$this->assertEqual(count($result), 39);
+
+		$result = $Controller->httpCodes(100);
+		$expected = array(100 => 'Continue');
+		$this->assertEqual($result, $expected);
+
+		$codes = array(
+			1337 => 'Undefined Unicorn',
+			1729 => 'Hardy-Ramanujan Located'
+		);
+
+		$result = $Controller->httpCodes($codes);
+		$this->assertTrue($result);
+		$this->assertEqual(count($Controller->httpCodes()), 41);
+
+		$result = $Controller->httpCodes(1337);
+		$expected = array(1337 => 'Undefined Unicorn');
+		$this->assertEqual($result, $expected);
+
+		$codes = array(404 => 'Sorry Bro');
+		$result = $Controller->httpCodes($codes);
+		$this->assertTrue($result);
+		$this->assertEqual(count($Controller->httpCodes()), 41);
+
+		$result = $Controller->httpCodes(404);
+		$expected = array(404 => 'Sorry Bro');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * Tests that the startup process calls the correct functions
+ *
+ * @access public
+ * @return void
+ */
+	function testStartupProcess() {
+		Mock::generatePartial('AnotherTestController','MockedController', array('beforeFilter', 'afterFilter'));
+		Mock::generate('TestComponent', 'MockTestComponent', array('startup', 'initialize'));
+		$MockedController =& new MockedController();
+		$MockedController->components = array('MockTest');
+		$MockedController->Component =& new Component();
+		$MockedController->Component->init($MockedController);
+		$MockedController->expectCallCount('beforeFilter', 1);
+		$MockedController->MockTest->expectCallCount('initialize', 1);
+		$MockedController->MockTest->expectCallCount('startup', 1);
+		$MockedController->startupProcess();
+	}
+/**
+ * Tests that the shutdown process calls the correct functions
+ *
+ * @access public
+ * @return void
+ */
+	function testShutdownProcess() {
+		Mock::generate('TestComponent', 'MockTestComponent', array('shutdown'));
+		$MockedController =& new MockedController();
+		$MockedController->components = array('MockTest');
+		$MockedController->Component =& new Component();
+		$MockedController->Component->init($MockedController);
+		$MockedController->expectCallCount('afterFilter', 1);
+		$MockedController->MockTest->expectCallCount('shutdown', 1);
+		$MockedController->shutdownProcess();
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/controller/controller_merge_vars.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/controller/controller_merge_vars.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/controller/controller_merge_vars.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,257 @@
+<?php
+/**
+ * Controller Merge vars Test file
+ *
+ * Isolated from the Controller and Component test as to not pollute their AppController class
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ * @since         CakePHP(tm) v 1.2.3
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+if (!class_exists('AppController')) {
+
+/**
+ * Test case AppController
+ *
+ * @package cake
+ * @subpackage cake.tests.cases.libs.controller
+ */
+	class AppController extends Controller {
+
+/**
+ * components
+ *
+ * @var array
+ */
+		var $components = array('MergeVar' => array('flag', 'otherFlag', 'redirect' => false));
+/**
+ * helpers
+ *
+ * @var array
+ */
+		var $helpers = array('MergeVar' => array('format' => 'html', 'terse'));
+	}
+} elseif (!defined('APP_CONTROLLER_EXISTS')) {
+	define('APP_CONTROLLER_EXISTS', true);
+}
+
+/**
+ * MergeVar Component
+ *
+ * @package cake.tests.cases.libs.controller
+ */
+class MergeVarComponent extends Object {
+
+}
+
+/**
+ * Additional controller for testing
+ *
+ * @package cake.tests.cases.libs.controller
+ */
+class MergeVariablesController extends AppController {
+
+/**
+ * name
+ *
+ * @var string
+ */
+	var $name = 'MergeVariables';
+
+/**
+ * uses
+ *
+ * @var arrays
+ */
+	var $uses = array();
+}
+
+/**
+ * MergeVarPlugin App Controller
+ *
+ * @package cake.tests.cases.libs.controller
+ */
+class MergeVarPluginAppController extends AppController {
+
+/**
+ * components
+ *
+ * @var array
+ */
+	var $components = array('Auth' => array('setting' => 'val', 'otherVal'));
+
+/**
+ * helpers
+ *
+ * @var array
+ */
+	var $helpers = array('Javascript');
+}
+
+/**
+ * MergePostsController
+ *
+ * @package cake.tests.cases.libs.controller
+ */
+class MergePostsController extends MergeVarPluginAppController {
+
+/**
+ * name
+ *
+ * @var string
+ */
+	var $name = 'MergePosts';
+
+/**
+ * uses
+ *
+ * @var array
+ */
+	var $uses = array();
+}
+
+
+/**
+ * Test Case for Controller Merging of Vars.
+ *
+ * @package cake.tests.cases.libs.controller
+ */
+class ControllerMergeVarsTestCase extends CakeTestCase {
+/**
+ * Skips the case if APP_CONTROLLER_EXISTS is defined
+ *
+ * @return void
+ */
+	function skip() {
+		$this->skipIf(defined('APP_CONTROLLER_EXISTS'), 'APP_CONTROLLER_EXISTS cannot run. %s');
+	}
+/**
+ * end test
+ *
+ * @return void
+ */
+	function endTest() {
+		ClassRegistry::flush();
+	}
+
+/**
+ * test that component settings are not duplicated when merging component settings
+ *
+ * @return void
+ */
+	function testComponentParamMergingNoDuplication() {
+		$Controller =& new MergeVariablesController();
+		$Controller->constructClasses();
+
+		$expected = array('MergeVar' => array('flag', 'otherFlag', 'redirect' => false));
+		$this->assertEqual($Controller->components, $expected, 'Duplication of settings occured. %s');
+	}
+
+/**
+ * test component merges with redeclared components
+ *
+ * @return void
+ */
+	function testComponentMergingWithRedeclarations() {
+		$Controller =& new MergeVariablesController();
+		$Controller->components['MergeVar'] = array('remote', 'redirect' => true);
+		$Controller->constructClasses();
+
+		$expected = array('MergeVar' => array('flag', 'otherFlag', 'redirect' => true, 'remote'));
+		$this->assertEqual($Controller->components, $expected, 'Merging of settings is wrong. %s');
+	}
+
+/**
+ * test merging of helpers array, ensure no duplication occurs
+ *
+ * @return void
+ */
+	function testHelperSettingMergingNoDuplication() {
+		$Controller =& new MergeVariablesController();
+		$Controller->constructClasses();
+
+		$expected = array('MergeVar' => array('format' => 'html', 'terse'));
+		$this->assertEqual($Controller->helpers, $expected, 'Duplication of settings occured. %s');
+	}
+
+/**
+ * Test that helpers declared in appcontroller come before those in the subclass
+ * orderwise
+ *
+ * @return void
+ */
+	function testHelperOrderPrecedence() {
+		$Controller =& new MergeVariablesController();
+		$Controller->helpers = array('Custom', 'Foo' => array('something'));
+		$Controller->constructClasses();
+
+		$expected = array(
+			'MergeVar' => array('format' => 'html', 'terse'),
+			'Custom' => null,
+			'Foo' => array('something')
+		);
+		$this->assertIdentical($Controller->helpers, $expected, 'Order is incorrect. %s');
+	}
+
+/**
+ * test merging of vars with plugin
+ *
+ * @return void
+ */
+	function testMergeVarsWithPlugin() {
+		$Controller =& new MergePostsController();
+		$Controller->components = array('Email' => array('ports' => 'open'));
+		$Controller->plugin = 'MergeVarPlugin';
+		$Controller->constructClasses();
+
+		$expected = array(
+			'MergeVar' => array('flag', 'otherFlag', 'redirect' => false),
+			'Auth' => array('setting' => 'val', 'otherVal'),
+			'Email' => array('ports' => 'open')
+		);
+		$this->assertEqual($Controller->components, $expected, 'Components are unexpected %s');
+
+		$expected = array(
+			'MergeVar' => array('format' => 'html', 'terse'),
+			'Javascript' => null
+		);
+		$this->assertEqual($Controller->helpers, $expected, 'Helpers are unexpected %s');
+
+		$Controller =& new MergePostsController();
+		$Controller->components = array();
+		$Controller->plugin = 'MergeVarPlugin';
+		$Controller->constructClasses();
+
+		$expected = array(
+			'MergeVar' => array('flag', 'otherFlag', 'redirect' => false),
+			'Auth' => array('setting' => 'val', 'otherVal'),
+		);
+		$this->assertEqual($Controller->components, $expected, 'Components are unexpected %s');
+	}
+
+/**
+ * Ensure that __mergeVars is not being greedy and merging with
+ * AppController when you make an instance of Controller
+ *
+ * @return void
+ */
+	function testMergeVarsNotGreedy() {
+		$Controller =& new Controller();
+		$Controller->components = array();
+		$Controller->uses = array();
+		$Controller->constructClasses();
+
+		$this->assertFalse(isset($Controller->Session));
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/controller/pages_controller.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/controller/pages_controller.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/controller/pages_controller.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,72 @@
+<?php
+/**
+ * PagesControllerTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ * @since         CakePHP(tm) v 1.2.0.5436
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+if (!class_exists('AppController')) {
+	require_once LIBS . 'controller' . DS . 'app_controller.php';
+} elseif (!defined('APP_CONTROLLER_EXISTS')) {
+	define('APP_CONTROLLER_EXISTS', true);
+}
+App::import('Controller', 'Pages');
+
+/**
+ * PagesControllerTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class PagesControllerTest extends CakeTestCase {
+
+/**
+ * endTest method
+ *
+ * @access public
+ * @return void
+ */
+	function endTest() {
+		App::build();
+	}
+
+/**
+ * testDisplay method
+ *
+ * @access public
+ * @return void
+ */
+	function testDisplay() {
+		if ($this->skipIf(defined('APP_CONTROLLER_EXISTS'), '%s Need a non-existent AppController')) {
+			return;
+		}
+
+		App::build(array(
+			'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS, TEST_CAKE_CORE_INCLUDE_PATH . 'libs' . DS . 'view' . DS)
+		));
+		$Pages =& new PagesController();
+
+		$Pages->viewPath = 'posts';
+		$Pages->display('index');
+		$this->assertPattern('/posts index/', $Pages->output);
+		$this->assertEqual($Pages->viewVars['page'], 'index');
+
+		$Pages->viewPath = 'themed';
+		$Pages->display('test_theme', 'posts', 'index');
+		$this->assertPattern('/posts index themed view/', $Pages->output);
+		$this->assertEqual($Pages->viewVars['page'], 'test_theme');
+		$this->assertEqual($Pages->viewVars['subpage'], 'posts');
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/controller/scaffold.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/controller/scaffold.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/controller/scaffold.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,894 @@
+<?php
+/**
+ * ScaffoldTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ * @since         CakePHP(tm) v 1.2.0.5436
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', 'Scaffold');
+
+/**
+ * ScaffoldMockController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class ScaffoldMockController extends Controller {
+
+/**
+ * name property
+ *
+ * @var string 'ScaffoldMock'
+ * @access public
+ */
+	var $name = 'ScaffoldMock';
+
+/**
+ * scaffold property
+ *
+ * @var mixed
+ * @access public
+ */
+	var $scaffold;
+}
+
+/**
+ * ScaffoldMockControllerWithFields class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class ScaffoldMockControllerWithFields extends Controller {
+
+/**
+ * name property
+ *
+ * @var string 'ScaffoldMock'
+ * @access public
+ */
+	var $name = 'ScaffoldMock';
+
+/**
+ * scaffold property
+ *
+ * @var mixed
+ * @access public
+ */
+	var $scaffold;
+
+/**
+ * function _beforeScaffold
+ *
+ * @param string method
+ */
+	function _beforeScaffold($method) {
+		$this->set('scaffoldFields', array('title'));
+		return true;
+	}
+}
+
+/**
+ * TestScaffoldMock class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class TestScaffoldMock extends Scaffold {
+
+/**
+ * Overload __scaffold
+ *
+ * @param unknown_type $params
+ */
+    function __scaffold($params) {
+        $this->_params = $params;
+    }
+
+/**
+ * Get Params from the Controller.
+ *
+ * @return unknown
+ */
+    function getParams() {
+        return $this->_params;
+    }
+}
+
+/**
+ * ScaffoldMock class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class ScaffoldMock extends CakeTestModel {
+
+/**
+ * useTable property
+ *
+ * @var string 'posts'
+ * @access public
+ */
+	var $useTable = 'articles';
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array(
+		'User' => array(
+			'className' => 'ScaffoldUser',
+			'foreignKey' => 'user_id',
+		)
+	);
+
+/**
+ * hasMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasMany = array(
+		'Comment' => array(
+			'className' => 'ScaffoldComment',
+			'foreignKey' => 'article_id',
+		)
+	);
+/**
+ * hasAndBelongsToMany property
+ *
+ * @var string
+ */
+	var $hasAndBelongsToMany = array(
+		'ScaffoldTag' => array(
+			'className' => 'ScaffoldTag',
+			'foreignKey' => 'something_id',
+			'associationForeignKey' => 'something_else_id',
+			'joinTable' => 'join_things'
+		)
+	);
+}
+
+/**
+ * ScaffoldUser class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class ScaffoldUser extends CakeTestModel {
+
+/**
+ * useTable property
+ *
+ * @var string 'posts'
+ * @access public
+ */
+	var $useTable = 'users';
+
+/**
+ * hasMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasMany = array(
+		'Article' => array(
+			'className' => 'ScaffoldMock',
+			'foreignKey' => 'article_id',
+		)
+	);
+}
+
+/**
+ * ScaffoldComment class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class ScaffoldComment extends CakeTestModel {
+
+/**
+ * useTable property
+ *
+ * @var string 'posts'
+ * @access public
+ */
+	var $useTable = 'comments';
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array(
+		'Article' => array(
+			'className' => 'ScaffoldMock',
+			'foreignKey' => 'article_id',
+		)
+	);
+}
+
+/**
+ * ScaffoldTag class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class ScaffoldTag extends CakeTestModel {
+/**
+ * useTable property
+ *
+ * @var string 'posts'
+ * @access public
+ */
+	var $useTable = 'tags';
+}
+/**
+ * TestScaffoldView class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class TestScaffoldView extends ScaffoldView {
+
+/**
+ * testGetFilename method
+ *
+ * @param mixed $action
+ * @access public
+ * @return void
+ */
+	function testGetFilename($action) {
+		return $this->_getViewFileName($action);
+	}
+}
+
+/**
+ * ScaffoldViewTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class ScaffoldViewTest extends CakeTestCase {
+
+/**
+ * fixtures property
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array('core.article', 'core.user', 'core.comment', 'core.join_thing', 'core.tag');
+
+/**
+ * startTest method
+ *
+ * @access public
+ * @return void
+ */
+	function startTest() {
+		$this->Controller =& new ScaffoldMockController();
+
+		App::build(array(
+			'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS),
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)
+		));
+	}
+
+/**
+ * endTest method
+ *
+ * @access public
+ * @return void
+ */
+	function endTest() {
+		unset($this->Controller);
+
+		App::build();
+	}
+
+/**
+ * testGetViewFilename method
+ *
+ * @access public
+ * @return void
+ */
+	function testGetViewFilename() {
+		$_admin = Configure::read('Routing.prefixes');
+		Configure::write('Routing.prefixes', array('admin'));
+
+		$this->Controller->action = 'index';
+		$ScaffoldView =& new TestScaffoldView($this->Controller);
+		$result = $ScaffoldView->testGetFilename('index');
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'libs' . DS . 'view' . DS . 'scaffolds' . DS . 'index.ctp';
+		$this->assertEqual($result, $expected);
+
+		$result = $ScaffoldView->testGetFilename('edit');
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'libs' . DS . 'view' . DS . 'scaffolds' . DS . 'edit.ctp';
+		$this->assertEqual($result, $expected);
+
+		$result = $ScaffoldView->testGetFilename('add');
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'libs' . DS . 'view' . DS . 'scaffolds' . DS . 'edit.ctp';
+		$this->assertEqual($result, $expected);
+
+		$result = $ScaffoldView->testGetFilename('view');
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'libs' . DS . 'view' . DS . 'scaffolds' . DS . 'view.ctp';
+		$this->assertEqual($result, $expected);
+
+		$result = $ScaffoldView->testGetFilename('admin_index');
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'libs' . DS . 'view' . DS . 'scaffolds' . DS . 'index.ctp';
+		$this->assertEqual($result, $expected);
+
+		$result = $ScaffoldView->testGetFilename('admin_view');
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'libs' . DS . 'view' . DS . 'scaffolds' . DS . 'view.ctp';
+		$this->assertEqual($result, $expected);
+
+		$result = $ScaffoldView->testGetFilename('admin_edit');
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'libs' . DS . 'view' . DS . 'scaffolds' . DS . 'edit.ctp';
+		$this->assertEqual($result, $expected);
+
+		$result = $ScaffoldView->testGetFilename('admin_add');
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'libs' . DS . 'view' . DS . 'scaffolds' . DS . 'edit.ctp';
+		$this->assertEqual($result, $expected);
+
+		$result = $ScaffoldView->testGetFilename('error');
+		$expected = 'cake' . DS . 'libs' . DS . 'view' . DS . 'errors' . DS . 'scaffold_error.ctp';
+		$this->assertEqual($result, $expected);
+
+		$Controller =& new ScaffoldMockController();
+		$Controller->scaffold = 'admin';
+		$Controller->viewPath = 'posts';
+		$Controller->action = 'admin_edit';
+		$ScaffoldView =& new TestScaffoldView($Controller);
+		$result = $ScaffoldView->testGetFilename('admin_edit');
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' .DS . 'views' . DS . 'posts' . DS . 'scaffold.edit.ctp';
+		$this->assertEqual($result, $expected);
+
+		$result = $ScaffoldView->testGetFilename('edit');
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' .DS . 'views' . DS . 'posts' . DS . 'scaffold.edit.ctp';
+		$this->assertEqual($result, $expected);
+
+		$Controller =& new ScaffoldMockController();
+		$Controller->scaffold = 'admin';
+		$Controller->viewPath = 'tests';
+		$Controller->plugin = 'test_plugin';
+		$Controller->action = 'admin_add';
+		$ScaffoldView =& new TestScaffoldView($Controller);
+		$result = $ScaffoldView->testGetFilename('admin_add');
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins'
+			. DS .'test_plugin' . DS . 'views' . DS . 'tests' . DS . 'scaffold.edit.ctp';
+		$this->assertEqual($result, $expected);
+
+		$result = $ScaffoldView->testGetFilename('add');
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins'
+			. DS .'test_plugin' . DS . 'views' . DS . 'tests' . DS . 'scaffold.edit.ctp';
+		$this->assertEqual($result, $expected);
+
+		Configure::write('Routing.prefixes', $_admin);
+	}
+
+/**
+ * test getting the view file name for themed scaffolds.
+ *
+ * @return void
+ */
+	function testGetViewFileNameWithTheme() {
+		$this->Controller->action = 'index';
+		$this->Controller->viewPath = 'posts';
+		$this->Controller->theme = 'test_theme';
+		$ScaffoldView =& new TestScaffoldView($this->Controller);
+
+		$result = $ScaffoldView->testGetFilename('index');
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS 
+			. 'themed' . DS . 'test_theme' . DS . 'posts' . DS . 'scaffold.index.ctp';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test default index scaffold generation
+ *
+ * @access public
+ * @return void
+ */
+	function testIndexScaffold() {
+		$this->Controller->action = 'index';
+		$this->Controller->here = '/scaffold_mock';
+		$this->Controller->webroot = '/';
+		$params = array(
+			'plugin' => null,
+			'pass' => array(),
+			'form' => array(),
+			'named' => array(),
+			'url' => array('url' =>'scaffold_mock'),
+			'controller' => 'scaffold_mock',
+			'action' => 'index',
+		);
+		//set router.
+		Router::reload();
+		Router::setRequestInfo(array($params, array('base' => '/', 'here' => '/scaffold_mock', 'webroot' => '/')));
+		$this->Controller->params = $params;
+		$this->Controller->controller = 'scaffold_mock';
+		$this->Controller->base = '/';
+		$this->Controller->constructClasses();
+		ob_start();
+		new Scaffold($this->Controller, $params);
+		$result = ob_get_clean();
+
+		$this->assertPattern('#<h2>Scaffold Mock</h2>#', $result);
+		$this->assertPattern('#<table cellpadding="0" cellspacing="0">#', $result);
+
+		$this->assertPattern('#<a href="/scaffold_users/view/1">1</a>#', $result); //belongsTo links
+		$this->assertPattern('#<li><a href="/scaffold_mock/add">New Scaffold Mock</a></li>#', $result);
+		$this->assertPattern('#<li><a href="/scaffold_users">List Scaffold Users</a></li>#', $result);
+		$this->assertPattern('#<li><a href="/scaffold_comments/add">New Comment</a></li>#', $result);
+	}
+
+/**
+ * test default view scaffold generation
+ *
+ * @access public
+ * @return void
+ */
+	function testViewScaffold() {
+		$this->Controller->action = 'view';
+		$this->Controller->here = '/scaffold_mock';
+		$this->Controller->webroot = '/';
+		$params = array(
+			'plugin' => null,
+			'pass' => array(1),
+			'form' => array(),
+			'named' => array(),
+			'url' => array('url' =>'scaffold_mock'),
+			'controller' => 'scaffold_mock',
+			'action' => 'view',
+		);
+		//set router.
+		Router::reload();
+		Router::setRequestInfo(array($params, array('base' => '/', 'here' => '/scaffold_mock', 'webroot' => '/')));
+		$this->Controller->params = $params;
+		$this->Controller->controller = 'scaffold_mock';
+		$this->Controller->base = '/';
+		$this->Controller->constructClasses();
+
+		ob_start();
+		new Scaffold($this->Controller, $params);
+		$result = ob_get_clean();
+
+		$this->assertPattern('/<h2>View Scaffold Mock<\/h2>/', $result);
+		$this->assertPattern('/<dl>/', $result);
+		//TODO: add specific tests for fields.
+		$this->assertPattern('/<a href="\/scaffold_users\/view\/1">1<\/a>/', $result); //belongsTo links
+		$this->assertPattern('/<li><a href="\/scaffold_mock\/edit\/1">Edit Scaffold Mock<\/a>\s<\/li>/', $result);
+		$this->assertPattern('/<li><a href="\/scaffold_mock\/delete\/1"[^>]*>Delete Scaffold Mock<\/a>\s*<\/li>/', $result);
+		//check related table
+		$this->assertPattern('/<div class="related">\s*<h3>Related Scaffold Comments<\/h3>\s*<table cellpadding="0" cellspacing="0">/', $result);
+		$this->assertPattern('/<li><a href="\/scaffold_comments\/add">New Comment<\/a><\/li>/', $result);
+		$this->assertNoPattern('/<th>JoinThing<\/th>/', $result);
+	}
+
+/**
+ * test default view scaffold generation
+ *
+ * @access public
+ * @return void
+ */
+	function testEditScaffold() {
+		$this->Controller->action = 'edit';
+		$this->Controller->here = '/scaffold_mock';
+		$this->Controller->webroot = '/';
+		$params = array(
+			'plugin' => null,
+			'pass' => array(1),
+			'form' => array(),
+			'named' => array(),
+			'url' => array('url' =>'scaffold_mock'),
+			'controller' => 'scaffold_mock',
+			'action' => 'edit',
+		);
+		//set router.
+		Router::reload();
+		Router::setRequestInfo(array($params, array('base' => '/', 'here' => '/scaffold_mock', 'webroot' => '/')));
+		$this->Controller->params = $params;
+		$this->Controller->controller = 'scaffold_mock';
+		$this->Controller->base = '/';
+		$this->Controller->constructClasses();
+		ob_start();
+		new Scaffold($this->Controller, $params);
+		$result = ob_get_clean();
+
+		$this->assertPattern('/<form action="\/scaffold_mock\/edit\/1" id="ScaffoldMockEditForm" method="post"/', $result);
+		$this->assertPattern('/<legend>Edit Scaffold Mock<\/legend>/', $result);
+
+		$this->assertPattern('/input type="hidden" name="data\[ScaffoldMock\]\[id\]" value="1" id="ScaffoldMockId"/', $result);
+		$this->assertPattern('/select name="data\[ScaffoldMock\]\[user_id\]" id="ScaffoldMockUserId"/', $result);
+		$this->assertPattern('/input name="data\[ScaffoldMock\]\[title\]" type="text" maxlength="255" value="First Article" id="ScaffoldMockTitle"/', $result);
+		$this->assertPattern('/input name="data\[ScaffoldMock\]\[published\]" type="text" maxlength="1" value="Y" id="ScaffoldMockPublished"/', $result);
+		$this->assertPattern('/textarea name="data\[ScaffoldMock\]\[body\]" cols="30" rows="6" id="ScaffoldMockBody"/', $result);
+		$this->assertPattern('/<li><a href="\/scaffold_mock\/delete\/1"[^>]*>Delete<\/a>\s*<\/li>/', $result);
+	}
+
+/**
+ * Test Admin Index Scaffolding.
+ *
+ * @access public
+ * @return void
+ */
+	function testAdminIndexScaffold() {
+		$_backAdmin = Configure::read('Routing.prefixes');
+
+		Configure::write('Routing.prefixes', array('admin'));
+		$params = array(
+			'plugin' => null,
+			'pass' => array(),
+			'form' => array(),
+			'named' => array(),
+			'prefix' => 'admin',
+			'url' => array('url' =>'admin/scaffold_mock'),
+			'controller' => 'scaffold_mock',
+			'action' => 'admin_index',
+			'admin' => 1,
+		);
+		//reset, and set router.
+		Router::reload();
+		Router::setRequestInfo(array($params, array('base' => '/', 'here' => '/admin/scaffold_mock', 'webroot' => '/')));
+		$this->Controller->params = $params;
+		$this->Controller->controller = 'scaffold_mock';
+		$this->Controller->base = '/';
+		$this->Controller->action = 'admin_index';
+		$this->Controller->here = '/tests/admin/scaffold_mock';
+		$this->Controller->webroot = '/';
+		$this->Controller->scaffold = 'admin';
+		$this->Controller->constructClasses();
+
+		ob_start();
+		$Scaffold = new Scaffold($this->Controller, $params);
+		$result = ob_get_clean();
+
+		$this->assertPattern('/<h2>Scaffold Mock<\/h2>/', $result);
+		$this->assertPattern('/<table cellpadding="0" cellspacing="0">/', $result);
+		//TODO: add testing for table generation
+		$this->assertPattern('/<li><a href="\/admin\/scaffold_mock\/add">New Scaffold Mock<\/a><\/li>/', $result);
+
+		Configure::write('Routing.prefixes', $_backAdmin);
+	}
+
+/**
+ * Test Admin Index Scaffolding.
+ *
+ * @access public
+ * @return void
+ */
+	function testAdminEditScaffold() {
+		$_backAdmin = Configure::read('Routing.prefixes');
+
+		Configure::write('Routing.prefixes', array('admin'));
+		$params = array(
+			'plugin' => null,
+			'pass' => array(),
+			'form' => array(),
+			'named' => array(),
+			'prefix' => 'admin',
+			'url' => array('url' =>'admin/scaffold_mock/edit'),
+			'controller' => 'scaffold_mock',
+			'action' => 'admin_edit',
+			'admin' => 1,
+		);
+		//reset, and set router.
+		Router::reload();
+		Router::setRequestInfo(array($params, array('base' => '/', 'here' => '/admin/scaffold_mock/edit', 'webroot' => '/')));
+		$this->Controller->params = $params;
+		$this->Controller->controller = 'scaffold_mock';
+		$this->Controller->base = '/';
+		$this->Controller->action = 'admin_index';
+		$this->Controller->here = '/tests/admin/scaffold_mock';
+		$this->Controller->webroot = '/';
+		$this->Controller->scaffold = 'admin';
+		$this->Controller->constructClasses();
+
+		ob_start();
+		$Scaffold = new Scaffold($this->Controller, $params);
+		$result = ob_get_clean();
+
+		$this->assertPattern('#admin/scaffold_mock/edit/1#', $result);
+		$this->assertPattern('#Scaffold Mock#', $result);
+
+		Configure::write('Routing.prefixes', $_backAdmin);
+	}
+
+/**
+ * Test Admin Index Scaffolding.
+ *
+ * @access public
+ * @return void
+ */
+	function testMultiplePrefixScaffold() {
+		$_backAdmin = Configure::read('Routing.prefixes');
+
+		Configure::write('Routing.prefixes', array('admin', 'member'));
+		$params = array(
+			'plugin' => null,
+			'pass' => array(),
+			'form' => array(),
+			'named' => array(),
+			'prefix' => 'member',
+			'url' => array('url' =>'member/scaffold_mock'),
+			'controller' => 'scaffold_mock',
+			'action' => 'member_index',
+			'member' => 1,
+		);
+		//reset, and set router.
+		Router::reload();
+		Router::setRequestInfo(array($params, array('base' => '/', 'here' => '/member/scaffold_mock', 'webroot' => '/')));
+		$this->Controller->params = $params;
+		$this->Controller->controller = 'scaffold_mock';
+		$this->Controller->base = '/';
+		$this->Controller->action = 'member_index';
+		$this->Controller->here = '/tests/member/scaffold_mock';
+		$this->Controller->webroot = '/';
+		$this->Controller->scaffold = 'member';
+		$this->Controller->constructClasses();
+
+		ob_start();
+		$Scaffold = new Scaffold($this->Controller, $params);
+		$result = ob_get_clean();
+
+		$this->assertPattern('/<h2>Scaffold Mock<\/h2>/', $result);
+		$this->assertPattern('/<table cellpadding="0" cellspacing="0">/', $result);
+		//TODO: add testing for table generation
+		$this->assertPattern('/<li><a href="\/member\/scaffold_mock\/add">New Scaffold Mock<\/a><\/li>/', $result);
+
+		Configure::write('Routing.prefixes', $_backAdmin);
+	}
+
+}
+
+/**
+ * Scaffold Test class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller
+ */
+class ScaffoldTest extends CakeTestCase {
+
+/**
+ * Controller property
+ *
+ * @var SecurityTestController
+ * @access public
+ */
+	var $Controller;
+
+/**
+ * fixtures property
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array('core.article', 'core.user', 'core.comment', 'core.join_thing', 'core.tag');
+/**
+ * startTest method
+ *
+ * @access public
+ * @return void
+ */
+	function startTest() {
+		$this->Controller =& new ScaffoldMockController();
+	}
+
+/**
+ * endTest method
+ *
+ * @access public
+ * @return void
+ */
+	function endTest() {
+		unset($this->Controller);
+	}
+
+/**
+ * Test the correct Generation of Scaffold Params.
+ * This ensures that the correct action and view will be generated
+ *
+ * @access public
+ * @return void
+ */
+	function testScaffoldParams() {
+		$this->Controller->action = 'admin_edit';
+		$this->Controller->here = '/admin/scaffold_mock/edit';
+		$this->Controller->webroot = '/';
+		$params = array(
+			'plugin' => null,
+			'pass' => array(),
+			'form' => array(),
+			'named' => array(),
+			'url' => array('url' =>'admin/scaffold_mock/edit'),
+			'controller' => 'scaffold_mock',
+			'action' => 'admin_edit',
+			'admin' => true,
+		);
+		//set router.
+		Router::setRequestInfo(array($params, array('base' => '/', 'here' => 'admin/scaffold_mock', 'webroot' => '/')));
+
+		$this->Controller->params = $params;
+		$this->Controller->controller = 'scaffold_mock';
+		$this->Controller->base = '/';
+		$this->Controller->constructClasses();
+		$Scaffold =& new TestScaffoldMock($this->Controller, $params);
+		$result = $Scaffold->getParams();
+		$this->assertEqual($result['action'], 'admin_edit');
+	}
+
+/**
+ * test that the proper names and variable values are set by Scaffold
+ *
+ * @return void
+ */
+	function testScaffoldVariableSetting() {
+		$this->Controller->action = 'admin_edit';
+		$this->Controller->here = '/admin/scaffold_mock/edit';
+		$this->Controller->webroot = '/';
+		$params = array(
+			'plugin' => null,
+			'pass' => array(),
+			'form' => array(),
+			'named' => array(),
+			'url' => array('url' =>'admin/scaffold_mock/edit'),
+			'controller' => 'scaffold_mock',
+			'action' => 'admin_edit',
+			'admin' => true,
+		);
+		//set router.
+		Router::setRequestInfo(array($params, array('base' => '/', 'here' => 'admin/scaffold_mock', 'webroot' => '/')));
+
+		$this->Controller->params = $params;
+		$this->Controller->controller = 'scaffold_mock';
+		$this->Controller->base = '/';
+		$this->Controller->constructClasses();
+		$Scaffold =& new TestScaffoldMock($this->Controller, $params);
+		$result = $Scaffold->controller->viewVars;
+
+		$this->assertEqual($result['title_for_layout'], 'Scaffold :: Admin Edit :: Scaffold Mock');
+		$this->assertEqual($result['singularHumanName'], 'Scaffold Mock');
+		$this->assertEqual($result['pluralHumanName'], 'Scaffold Mock');
+		$this->assertEqual($result['modelClass'], 'ScaffoldMock');
+		$this->assertEqual($result['primaryKey'], 'id');
+		$this->assertEqual($result['displayField'], 'title');
+		$this->assertEqual($result['singularVar'], 'scaffoldMock');
+		$this->assertEqual($result['pluralVar'], 'scaffoldMock');
+		$this->assertEqual($result['scaffoldFields'], array('id', 'user_id', 'title', 'body', 'published', 'created', 'updated'));
+	}
+
+/**
+ * test that Scaffold overrides the view property even if its set to 'Theme'
+ *
+ * @return void
+ */
+	function testScaffoldChangingViewProperty() {
+		$this->Controller->action = 'edit';
+		$this->Controller->theme = 'test_theme';
+		$this->Controller->view = 'Theme';
+		$this->Controller->constructClasses();
+		$Scaffold =& new TestScaffoldMock($this->Controller, array());
+
+		$this->assertEqual($this->Controller->view, 'Scaffold');
+	}
+
+/**
+ * test that scaffold outputs flash messages when sessions are unset.
+ *
+ * @return void
+ */
+	function testScaffoldFlashMessages() {
+		$this->Controller->action = 'edit';
+		$this->Controller->here = '/scaffold_mock';
+		$this->Controller->webroot = '/';
+		$params = array(
+			'plugin' => null,
+			'pass' => array(1),
+			'form' => array(),
+			'named' => array(),
+			'url' => array('url' =>'scaffold_mock'),
+			'controller' => 'scaffold_mock',
+			'action' => 'edit',
+		);
+		//set router.
+		Router::reload();
+		Router::setRequestInfo(array($params, array('base' => '/', 'here' => '/scaffold_mock', 'webroot' => '/')));
+		$this->Controller->params = $params;
+		$this->Controller->controller = 'scaffold_mock';
+		$this->Controller->base = '/';
+		$this->Controller->data = array(
+			'ScaffoldMock' => array(
+				'id' => 1,
+				'title' => 'New title',
+				'body' => 'new body'
+			)
+		);
+		$this->Controller->constructClasses();
+		unset($this->Controller->Session);
+
+		ob_start();
+		new Scaffold($this->Controller, $params);
+		$result = ob_get_clean();
+		$this->assertPattern('/Scaffold Mock has been updated/', $result);
+	}
+/**
+ * test that habtm relationship keys get added to scaffoldFields.
+ *
+ * @see http://code.cakephp.org/tickets/view/48
+ * @return void
+ */
+	function testHabtmFieldAdditionWithScaffoldForm() {
+		$this->Controller->action = 'edit';
+		$this->Controller->here = '/scaffold_mock';
+		$this->Controller->webroot = '/';
+		$params = array(
+			'plugin' => null,
+			'pass' => array(1),
+			'form' => array(),
+			'named' => array(),
+			'url' => array('url' =>'scaffold_mock'),
+			'controller' => 'scaffold_mock',
+			'action' => 'edit',
+		);
+		//set router.
+		Router::reload();
+		Router::setRequestInfo(array($params, array('base' => '/', 'here' => '/scaffold_mock', 'webroot' => '/')));
+		$this->Controller->params = $params;
+		$this->Controller->controller = 'scaffold_mock';
+		$this->Controller->base = '/';
+		$this->Controller->constructClasses();
+		ob_start();
+		$Scaffold = new Scaffold($this->Controller, $params);
+		$result = ob_get_clean();
+		$this->assertPattern('/name="data\[ScaffoldTag\]\[ScaffoldTag\]"/', $result);
+
+		$result = $Scaffold->controller->viewVars;
+		$this->assertEqual($result['scaffoldFields'], array('id', 'user_id', 'title', 'body', 'published', 'created', 'updated', 'ScaffoldTag'));
+	}
+/**
+ * test that the proper names and variable values are set by Scaffold
+ *
+ * @return void
+ */
+	function testEditScaffoldWithScaffoldFields() {
+		$this->Controller = new ScaffoldMockControllerWithFields();
+		$this->Controller->action = 'edit';
+		$this->Controller->here = '/scaffold_mock';
+		$this->Controller->webroot = '/';
+		$params = array(
+			'plugin' => null,
+			'pass' => array(1),
+			'form' => array(),
+			'named' => array(),
+			'url' => array('url' =>'scaffold_mock'),
+			'controller' => 'scaffold_mock',
+			'action' => 'edit',
+		);
+		//set router.
+		Router::reload();
+		Router::setRequestInfo(array($params, array('base' => '/', 'here' => '/scaffold_mock', 'webroot' => '/')));
+		$this->Controller->params = $params;
+		$this->Controller->controller = 'scaffold_mock';
+		$this->Controller->base = '/';
+		$this->Controller->constructClasses();
+		ob_start();
+		new Scaffold($this->Controller, $params);
+		$result = ob_get_clean();
+
+		$this->assertNoPattern('/textarea name="data\[ScaffoldMock\]\[body\]" cols="30" rows="6" id="ScaffoldMockBody"/', $result);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/debugger.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/debugger.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/debugger.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,372 @@
+<?php
+/**
+ * DebuggerTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP Project
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.5432
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Core', 'Debugger');
+
+/**
+ * DebugggerTestCaseDebuggger class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class DebuggerTestCaseDebugger extends Debugger {
+}
+
+/**
+ * DebuggerTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class DebuggerTest extends CakeTestCase {
+// !!!
+// !!! Be careful with changing code below as it may
+// !!! change line numbers which are used in the tests
+// !!!
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		Configure::write('log', false);
+		if (!defined('SIMPLETESTVENDORPATH')) {
+			if (file_exists(APP . DS . 'vendors' . DS . 'simpletest' . DS . 'reporter.php')) {
+				define('SIMPLETESTVENDORPATH', 'APP' . DS . 'vendors');
+			} else {
+				define('SIMPLETESTVENDORPATH', 'CORE' . DS . 'vendors');
+			}
+		}
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		Configure::write('log', true);
+	}
+
+/**
+ * testDocRef method
+ *
+ * @access public
+ * @return void
+ */
+	function testDocRef() {
+		ini_set('docref_root', '');
+		$this->assertEqual(ini_get('docref_root'), '');
+		$debugger = new Debugger();
+		$this->assertEqual(ini_get('docref_root'), 'http://php.net/');
+	}
+
+/**
+ * test Excerpt writing
+ *
+ * @access public
+ * @return void
+ */
+	function testExcerpt() {
+		$result = Debugger::excerpt(__FILE__, __LINE__, 2);
+		$this->assertTrue(is_array($result));
+		$this->assertEqual(count($result), 5);
+		$this->assertPattern('/function(.+)testExcerpt/', $result[1]);
+
+		$result = Debugger::excerpt(__FILE__, 2, 2);
+		$this->assertTrue(is_array($result));
+		$this->assertEqual(count($result), 4);
+
+		$expected = '<code><span style="color: #000000">&lt;?php';
+		$expected .= '</span></code>';
+		$this->assertEqual($result[0], $expected);
+
+		$return = Debugger::excerpt('[internal]', 2, 2);
+		$this->assertTrue(empty($return));
+	}
+
+/**
+ * testOutput method
+ *
+ * @access public
+ * @return void
+ */
+	function testOutput() {
+		Debugger::invoke(Debugger::getInstance());
+		$result = Debugger::output(false);
+		$this->assertEqual($result, '');
+		$out .= '';
+		$result = Debugger::output(true);
+
+		$this->assertEqual($result[0]['error'], 'Notice');
+		$this->assertPattern('/Undefined variable\:\s+out/', $result[0]['description']);
+		$this->assertPattern('/DebuggerTest::testOutput/i', $result[0]['trace']);
+		$this->assertPattern('/SimpleInvoker::invoke/i', $result[0]['trace']);
+
+		ob_start();
+		Debugger::output('txt');
+		$other .= '';
+		$result = ob_get_clean();
+
+		$this->assertPattern('/Undefined variable:\s+other/', $result);
+		$this->assertPattern('/Context:/', $result);
+		$this->assertPattern('/DebuggerTest::testOutput/i', $result);
+		$this->assertPattern('/SimpleInvoker::invoke/i', $result);
+
+		ob_start();
+		Debugger::output('html');
+		$wrong .= '';
+		$result = ob_get_clean();
+		$this->assertPattern('/<pre class="cake-debug">.+<\/pre>/', $result);
+		$this->assertPattern('/<b>Notice<\/b>/', $result);
+		$this->assertPattern('/variable:\s+wrong/', $result);
+
+		ob_start();
+		Debugger::output('js');
+		$buzz .= '';
+		$result = explode('</a>', ob_get_clean());
+		$this->assertTags($result[0], array(
+			'pre' => array('class' => 'cake-debug'),
+			'a' => array(
+				'href' => "javascript:void(0);",
+				'onclick' => "document.getElementById('cakeErr4-trace').style.display = " .
+				             "(document.getElementById('cakeErr4-trace').style.display == 'none'" .
+				             " ? '' : 'none');"
+			),
+			'b' => array(), 'Notice', '/b', ' (8)',
+		));
+
+		$this->assertPattern('/Undefined variable:\s+buzz/', $result[1]);
+		$this->assertPattern('/<a[^>]+>Code/', $result[1]);
+		$this->assertPattern('/<a[^>]+>Context/', $result[2]);
+		set_error_handler('simpleTestErrorHandler');
+	}
+
+/**
+ * Tests that changes in output formats using Debugger::output() change the templates used.
+ *
+ * @return void
+ */
+	function testChangeOutputFormats() {
+		Debugger::invoke(Debugger::getInstance());
+		Debugger::output('js', array(
+			'traceLine' => '{:reference} - <a href="txmt://open?url=file://{:file}' .
+			               '&line={:line}">{:path}</a>, line {:line}'
+		));
+		$result = Debugger::trace();
+		$this->assertPattern('/' . preg_quote('txmt://open?url=file:///', '/') . '/', $result);
+
+		Debugger::output('xml', array(
+			'error' => '<error><code>{:code}</code><file>{:file}</file><line>{:line}</line>' .
+			           '{:description}</error>',
+			'context' => "<context>{:context}</context>",
+			'trace' => "<stack>{:trace}</stack>",
+		));
+		Debugger::output('xml');
+
+		ob_start();
+		$foo .= '';
+		$result = ob_get_clean();
+		set_error_handler('SimpleTestErrorHandler');
+
+		$data = array(
+			'error' => array(),
+			'code' => array(), '8', '/code',
+			'file' => array(), 'preg:/[^<]+/', '/file',
+			'line' => array(), '' . (intval(__LINE__) + -8), '/line',
+			'preg:/Undefined variable:\s+foo/',
+			'/error'
+		);
+		$this->assertTags($result, $data, true);
+	}
+
+/**
+ * testTrimPath method
+ *
+ * @access public
+ * @return void
+ */
+	function testTrimPath() {
+		$this->assertEqual(Debugger::trimPath(APP), 'APP' . DS);
+		$this->assertEqual(Debugger::trimPath(CAKE_CORE_INCLUDE_PATH), 'CORE');
+	}
+
+/**
+ * testExportVar method
+ *
+ * @access public
+ * @return void
+ */
+	function testExportVar() {
+		App::import('Controller');
+		$Controller = new Controller();
+		$Controller->helpers = array('Html', 'Form');
+		$View = new View($Controller);
+		$result = Debugger::exportVar($View);
+		$expected = 'ViewView::$base = NULL
+		View::$here = NULL
+		View::$plugin = NULL
+		View::$name = ""
+		View::$action = NULL
+		View::$params = array
+		View::$passedArgs = array
+		View::$data = array
+		View::$helpers = array
+		View::$viewPath = ""
+		View::$viewVars = array
+		View::$layout = "default"
+		View::$layoutPath = NULL
+		View::$autoRender = true
+		View::$autoLayout = true
+		View::$ext = ".ctp"
+		View::$subDir = NULL
+		View::$theme = NULL
+		View::$cacheAction = false
+		View::$validationErrors = array
+		View::$hasRendered = false
+		View::$loaded = array
+		View::$modelScope = false
+		View::$model = NULL
+		View::$association = NULL
+		View::$field = NULL
+		View::$fieldSuffix = NULL
+		View::$modelId = NULL
+		View::$uuids = array
+		View::$output = false
+		View::$__passedVars = array
+		View::$__scripts = array
+		View::$__paths = array
+		View::$webroot = NULL';
+		$result = str_replace(array("\t", "\r\n", "\n"), "", strtolower($result));
+		$expected =  str_replace(array("\t", "\r\n", "\n"), "", strtolower($expected));
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testLog method
+ *
+ * @access public
+ * @return void
+ */
+	function testLog() {
+		if (file_exists(LOGS . 'debug.log')) {
+			unlink(LOGS . 'debug.log');
+		}
+
+		Debugger::log('cool');
+		$result = file_get_contents(LOGS . 'debug.log');
+		$this->assertPattern('/DebuggerTest\:\:testLog/i', $result);
+		$this->assertPattern('/"cool"/', $result);
+
+		unlink(TMP . 'logs' . DS . 'debug.log');
+
+		Debugger::log(array('whatever', 'here'));
+		$result = file_get_contents(TMP . 'logs' . DS . 'debug.log');
+		$this->assertPattern('/DebuggerTest\:\:testLog/i', $result);
+		$this->assertPattern('/\[main\]/', $result);
+		$this->assertPattern('/array/', $result);
+		$this->assertPattern('/"whatever",/', $result);
+		$this->assertPattern('/"here"/', $result);
+	}
+
+/**
+ * testDump method
+ *
+ * @access public
+ * @return void
+ */
+	function testDump() {
+		$var = array('People' => array(
+					array(
+					'name' => 'joeseph',
+					'coat' => 'technicolor',
+					'hair_color' => 'brown'
+					),
+					array(
+					'name' => 'Shaft',
+					'coat' => 'black',
+					'hair' => 'black'
+					)
+				)
+			);
+		ob_start();
+		Debugger::dump($var);
+		$result = ob_get_clean();
+		$expected = "<pre>array(\n\t\"People\" => array()\n)</pre>";
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * test getInstance.
+ *
+ * @access public
+ * @return void
+ */
+	function testGetInstance() {
+		$result =& Debugger::getInstance();
+		$this->assertIsA($result, 'Debugger');
+
+		$result =& Debugger::getInstance('DebuggerTestCaseDebugger');
+		$this->assertIsA($result, 'DebuggerTestCaseDebugger');
+
+		$result =& Debugger::getInstance();
+		$this->assertIsA($result, 'DebuggerTestCaseDebugger');
+
+		$result =& Debugger::getInstance('Debugger');
+		$this->assertIsA($result, 'Debugger');
+	}
+
+/**
+ * testNoDbCredentials
+ *
+ * If a connection error occurs, the config variable is passed through exportVar
+ * *** our database login credentials such that they are never visible
+ *
+ * @access public
+ * @return void
+ */
+	function testNoDbCredentials() {
+		$config = array(
+			'driver' => 'mysql',
+			'persistent' => false,
+			'host' => 'void.cakephp.org',
+			'login' => 'cakephp-user',
+			'password' => 'cakephp-password',
+			'database' => 'cakephp-database',
+			'prefix' => ''
+		);
+
+		$output = Debugger::exportVar($config);
+
+		$expectedArray = array(
+			'driver' => 'mysql',
+			'persistent' => false,
+			'host' => '*****',
+			'login' => '*****',
+			'password' => '*****',
+			'database' => '*****',
+			'prefix' => ''
+		);
+		$expected = Debugger::exportVar($expectedArray);
+
+		$this->assertEqual($expected, $output);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/error.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/error.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/error.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,630 @@
+<?php
+/**
+ * ErrorHandlerTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.5432
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+if (class_exists('TestErrorHandler')) {
+	return;
+}
+if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) {
+	define('CAKEPHP_UNIT_TEST_EXECUTION', 1);
+}
+
+/**
+ * BlueberryComponent class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class BlueberryComponent extends Object {
+
+/**
+ * testName property
+ *
+ * @access public
+ * @return void
+ */
+	var $testName = null;
+
+/**
+ * initialize method
+ *
+ * @access public
+ * @return void
+ */
+	function initialize(&$controller) {
+		$this->testName = 'BlueberryComponent';
+	}
+}
+
+/**
+ * BlueberryDispatcher class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class BlueberryDispatcher extends Dispatcher {
+
+/**
+ * cakeError method
+ *
+ * @access public
+ * @return void
+ */
+	function cakeError($method, $messages = array()) {
+		$error = new TestErrorHandler($method, $messages);
+		return $error;
+	}
+}
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class AuthBlueberryUser extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'AuthBlueberryUser'
+ * @access public
+ */
+	var $name = 'AuthBlueberryUser';
+
+/**
+ * useTable property
+ *
+ * @var string
+ * @access public
+ */
+	var $useTable = false;
+}
+if (!class_exists('AppController')) {
+	/**
+	 * AppController class
+	 *
+	 * @package       cake
+	 * @subpackage    cake.tests.cases.libs
+	 */
+	class AppController extends Controller {
+	/**
+	 * components property
+	 *
+	 * @access public
+	 * @return void
+	 */
+		var $components = array('Blueberry');
+	/**
+	 * beforeRender method
+	 *
+	 * @access public
+	 * @return void
+	 */
+		function beforeRender() {
+			echo $this->Blueberry->testName;
+		}
+	/**
+	 * header method
+	 *
+	 * @access public
+	 * @return void
+	 */
+		function header($header) {
+			echo $header;
+		}
+	/**
+	 * _stop method
+	 *
+	 * @access public
+	 * @return void
+	 */
+		function _stop($status = 0) {
+			echo 'Stopped with status: ' . $status;
+		}
+	}
+} elseif (!defined('APP_CONTROLLER_EXISTS')){
+	define('APP_CONTROLLER_EXISTS', true);
+}
+App::import('Core', array('Error', 'Controller'));
+
+/**
+ * TestErrorController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class TestErrorController extends AppController {
+
+/**
+ * uses property
+ *
+ * @var array
+ * @access public
+ */
+	var $uses = array();
+
+/**
+ * index method
+ *
+ * @access public
+ * @return void
+ */
+	function index() {
+		$this->autoRender = false;
+		return 'what up';
+	}
+}
+
+/**
+ * BlueberryController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class BlueberryController extends AppController {
+
+/**
+ * name property
+ *
+ * @access public
+ * @return void
+ */
+	var $name = 'BlueberryController';
+
+/**
+ * uses property
+ *
+ * @access public
+ * @return void
+ */
+	var $uses = array('AuthBlueberryUser');
+
+/**
+ * components property
+ *
+ * @access public
+ * @return void
+ */
+	var $components = array('Auth');
+}
+
+/**
+ * MyCustomErrorHandler class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class MyCustomErrorHandler extends ErrorHandler {
+
+/**
+ * custom error message type.
+ *
+ * @return void
+ */
+	function missingWidgetThing() {
+		echo 'widget thing is missing';
+	}
+
+/**
+ * stop method
+ *
+ * @access public
+ * @return void
+ */
+	function _stop() {
+		return;
+	}
+}
+
+/**
+ * TestErrorHandler class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class TestErrorHandler extends ErrorHandler {
+
+/**
+ * stop method
+ *
+ * @access public
+ * @return void
+ */
+	function _stop() {
+		return;
+	}
+}
+
+/**
+ * ErrorHandlerTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class ErrorHandlerTest extends CakeTestCase {
+
+/**
+ * skip method
+ *
+ * @access public
+ * @return void
+ */
+	function skip() {
+		$this->skipIf(PHP_SAPI === 'cli', '%s Cannot be run from console');
+	}
+
+/**
+ * test that methods declared in an ErrorHandler subclass are not converted
+ * into error404 when debug == 0
+ *
+ * @return void
+ */
+	function testSubclassMethodsNotBeingConvertedToError() {
+		$back = Configure::read('debug');
+		Configure::write('debug', 2);
+		ob_start();
+		$ErrorHandler =& new MyCustomErrorHandler('missingWidgetThing', array('message' => 'doh!'));
+		$result = ob_get_clean();
+		$this->assertEqual($result, 'widget thing is missing');
+
+		Configure::write('debug', 0);
+		ob_start();
+		$ErrorHandler =& new MyCustomErrorHandler('missingWidgetThing', array('message' => 'doh!'));
+		$result = ob_get_clean();
+		$this->assertEqual($result, 'widget thing is missing', 'Method declared in subclass converted to error404. %s');
+
+		Configure::write('debug', 0);
+		ob_start();
+		$ErrorHandler =& new MyCustomErrorHandler('missingController', array(
+			'className' => 'Missing', 'message' => 'Page not found'
+		));
+		$result = ob_get_clean();
+		$this->assertPattern('/Not Found/', $result, 'Method declared in error handler not converted to error404. %s');
+
+		Configure::write('debug', $back);
+	}
+
+/**
+ * testError method
+ *
+ * @access public
+ * @return void
+ */
+	function testError() {
+		ob_start();
+		$TestErrorHandler = new TestErrorHandler('error404', array('message' => 'Page not found'));
+		ob_clean();
+		ob_start();
+		$TestErrorHandler->error(array(
+			'code' => 404,
+			'message' => 'Page not Found',
+			'name' => "Couldn't find what you were looking for"
+		));
+		$result = ob_get_clean();
+		$this->assertPattern("/<h2>Couldn't find what you were looking for<\/h2>/", $result);
+		$this->assertPattern('/Page not Found/', $result);
+	}
+
+/**
+ * testError404 method
+ *
+ * @access public
+ * @return void
+ */
+	function testError404() {
+		App::build(array(
+			'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'libs' . DS . 'view' . DS)
+		), true);
+
+		ob_start();
+		$TestErrorHandler = new TestErrorHandler('error404', array('message' => 'Page not found', 'url' => '/test_error'));
+		$result = ob_get_clean();
+		$this->assertPattern('/<h2>Not Found<\/h2>/', $result);
+	 	$this->assertPattern("/<strong>'\/test_error'<\/strong>/", $result);
+
+		ob_start();
+		$TestErrorHandler =& new TestErrorHandler('error404', array('message' => 'Page not found'));
+		ob_get_clean();
+		ob_start();
+		$TestErrorHandler->error404(array(
+			'url' => 'pages/<span id=333>pink</span></id><script>document.body.style.background = t=document.getElementById(333).innerHTML;window.alert(t);</script>',
+			'message' => 'Page not found'
+		));
+		$result = ob_get_clean();
+		$this->assertNoPattern('#<script>#', $result);
+		$this->assertNoPattern('#</script>#', $result);
+
+		App::build();
+	}
+
+/**
+ * testError500 method
+ *
+ * @access public
+ * @return void
+ */
+	function testError500() {
+		ob_start();
+		$TestErrorHandler = new TestErrorHandler('error500', array(
+			'message' => 'An Internal Error Has Occurred'
+		));
+		$result = ob_get_clean();
+		$this->assertPattern('/<h2>An Internal Error Has Occurred<\/h2>/', $result);
+
+		ob_start();
+		$TestErrorHandler = new TestErrorHandler('error500', array(
+			'message' => 'An Internal Error Has Occurred',
+			'code' => '500'
+		));
+		$result = ob_get_clean();
+		$this->assertPattern('/<h2>An Internal Error Has Occurred<\/h2>/', $result);
+	}
+
+/**
+ * testMissingController method
+ *
+ * @access public
+ * @return void
+ */
+	function testMissingController() {
+		$this->skipIf(defined('APP_CONTROLLER_EXISTS'), '%s Need a non-existent AppController');
+
+		ob_start();
+		$TestErrorHandler = new TestErrorHandler('missingController', array('className' => 'PostsController'));
+		$result = ob_get_clean();
+		$this->assertPattern('/<h2>Missing Controller<\/h2>/', $result);
+		$this->assertPattern('/<em>PostsController<\/em>/', $result);
+		$this->assertPattern('/BlueberryComponent/', $result);
+	}
+
+/**
+ * testMissingAction method
+ *
+ * @access public
+ * @return void
+ */
+	function testMissingAction() {
+		ob_start();
+		$TestErrorHandler = new TestErrorHandler('missingAction', array('className' => 'PostsController', 'action' => 'index'));
+		$result = ob_get_clean();
+		$this->assertPattern('/<h2>Missing Method in PostsController<\/h2>/', $result);
+		$this->assertPattern('/<em>PostsController::<\/em><em>index\(\)<\/em>/', $result);
+
+		ob_start();
+		$dispatcher = new BlueberryDispatcher('/blueberry/inexistent');
+		$result = ob_get_clean();
+		$this->assertPattern('/<h2>Missing Method in BlueberryController<\/h2>/', $result);
+		$this->assertPattern('/<em>BlueberryController::<\/em><em>inexistent\(\)<\/em>/', $result);
+		$this->assertNoPattern('/Location: (.*)\/users\/login/', $result);
+		$this->assertNoPattern('/Stopped with status: 0/', $result);
+	}
+
+/**
+ * testPrivateAction method
+ *
+ * @access public
+ * @return void
+ */
+	function testPrivateAction() {
+		ob_start();
+		$TestErrorHandler = new TestErrorHandler('privateAction', array('className' => 'PostsController', 'action' => '_secretSauce'));
+		$result = ob_get_clean();
+		$this->assertPattern('/<h2>Private Method in PostsController<\/h2>/', $result);
+		$this->assertPattern('/<em>PostsController::<\/em><em>_secretSauce\(\)<\/em>/', $result);
+	}
+
+/**
+ * testMissingTable method
+ *
+ * @access public
+ * @return void
+ */
+	function testMissingTable() {
+		ob_start();
+		$TestErrorHandler = new TestErrorHandler('missingTable', array('className' => 'Article', 'table' => 'articles'));
+		$result = ob_get_clean();
+		$this->assertPattern('/HTTP\/1\.0 500 Internal Server Error/', $result);
+		$this->assertPattern('/<h2>Missing Database Table<\/h2>/', $result);
+		$this->assertPattern('/table <em>articles<\/em> for model <em>Article<\/em>/', $result);
+	}
+
+/**
+ * testMissingDatabase method
+ *
+ * @access public
+ * @return void
+ */
+	function testMissingDatabase() {
+		ob_start();
+		$TestErrorHandler = new TestErrorHandler('missingDatabase', array());
+		$result = ob_get_clean();
+		$this->assertPattern('/HTTP\/1\.0 500 Internal Server Error/', $result);
+		$this->assertPattern('/<h2>Missing Database Connection<\/h2>/', $result);
+		$this->assertPattern('/Confirm you have created the file/', $result);
+	}
+
+/**
+ * testMissingView method
+ *
+ * @access public
+ * @return void
+ */
+	function testMissingView() {
+		restore_error_handler();
+		ob_start();
+		$TestErrorHandler = new TestErrorHandler('missingView', array('className' => 'Pages', 'action' => 'display', 'file' => 'pages/about.ctp', 'base' => ''));
+		$expected = ob_get_clean();
+		set_error_handler('simpleTestErrorHandler');
+		$this->assertPattern("/PagesController::/", $expected);
+		$this->assertPattern("/pages\/about.ctp/", $expected);
+	}
+
+/**
+ * testMissingLayout method
+ *
+ * @access public
+ * @return void
+ */
+	function testMissingLayout() {
+		restore_error_handler();
+		ob_start();
+		$TestErrorHandler = new TestErrorHandler('missingLayout', array( 'layout' => 'my_layout', 'file' => 'layouts/my_layout.ctp', 'base' => ''));
+		$expected = ob_get_clean();
+		set_error_handler('simpleTestErrorHandler');
+		$this->assertPattern("/Missing Layout/", $expected);
+		$this->assertPattern("/layouts\/my_layout.ctp/", $expected);
+	}
+
+/**
+ * testMissingConnection method
+ *
+ * @access public
+ * @return void
+ */
+	function testMissingConnection() {
+		ob_start();
+		$TestErrorHandler = new TestErrorHandler('missingConnection', array('className' => 'Article'));
+		$result = ob_get_clean();
+		$this->assertPattern('/<h2>Missing Database Connection<\/h2>/', $result);
+		$this->assertPattern('/Article requires a database connection/', $result);
+	}
+
+/**
+ * testMissingHelperFile method
+ *
+ * @access public
+ * @return void
+ */
+	function testMissingHelperFile() {
+		ob_start();
+		$TestErrorHandler = new TestErrorHandler('missingHelperFile', array('helper' => 'MyCustom', 'file' => 'my_custom.php'));
+		$result = ob_get_clean();
+		$this->assertPattern('/<h2>Missing Helper File<\/h2>/', $result);
+		$this->assertPattern('/Create the class below in file:/', $result);
+		$this->assertPattern('/(\/|\\\)my_custom.php/', $result);
+	}
+
+/**
+ * testMissingHelperClass method
+ *
+ * @access public
+ * @return void
+ */
+	function testMissingHelperClass() {
+		ob_start();
+		$TestErrorHandler = new TestErrorHandler('missingHelperClass', array('helper' => 'MyCustom', 'file' => 'my_custom.php'));
+		$result = ob_get_clean();
+		$this->assertPattern('/<h2>Missing Helper Class<\/h2>/', $result);
+		$this->assertPattern('/The helper class <em>MyCustomHelper<\/em> can not be found or does not exist./', $result);
+		$this->assertPattern('/(\/|\\\)my_custom.php/', $result);
+	}
+
+/**
+ * test missingBehaviorFile method
+ *
+ * @access public
+ * @return void
+ */
+	function testMissingBehaviorFile() {
+		ob_start();
+		$TestErrorHandler = new TestErrorHandler('missingBehaviorFile', array('behavior' => 'MyCustom', 'file' => 'my_custom.php'));
+		$result = ob_get_clean();
+		$this->assertPattern('/<h2>Missing Behavior File<\/h2>/', $result);
+		$this->assertPattern('/Create the class below in file:/', $result);
+		$this->assertPattern('/(\/|\\\)my_custom.php/', $result);
+	}
+
+/**
+ * test MissingBehaviorClass method
+ *
+ * @access public
+ * @return void
+ */
+	function testMissingBehaviorClass() {
+		ob_start();
+		$TestErrorHandler = new TestErrorHandler('missingBehaviorClass', array('behavior' => 'MyCustom', 'file' => 'my_custom.php'));
+		$result = ob_get_clean();
+		$this->assertPattern('/<h2>Missing Behavior Class<\/h2>/', $result);
+		$this->assertPattern('/The behavior class <em>MyCustomBehavior<\/em> can not be found or does not exist./', $result);
+		$this->assertPattern('/(\/|\\\)my_custom.php/', $result);
+	}
+
+/**
+ * testMissingComponentFile method
+ *
+ * @access public
+ * @return void
+ */
+	function testMissingComponentFile() {
+		ob_start();
+		$TestErrorHandler = new TestErrorHandler('missingComponentFile', array('className' => 'PostsController', 'component' => 'Sidebox', 'file' => 'sidebox.php'));
+		$result = ob_get_clean();
+		$this->assertPattern('/<h2>Missing Component File<\/h2>/', $result);
+		$this->assertPattern('/Create the class <em>SideboxComponent<\/em> in file:/', $result);
+		$this->assertPattern('/(\/|\\\)sidebox.php/', $result);
+	}
+
+/**
+ * testMissingComponentClass method
+ *
+ * @access public
+ * @return void
+ */
+	function testMissingComponentClass() {
+		ob_start();
+		$TestErrorHandler = new TestErrorHandler('missingComponentClass', array('className' => 'PostsController', 'component' => 'Sidebox', 'file' => 'sidebox.php'));
+		$result = ob_get_clean();
+		$this->assertPattern('/<h2>Missing Component Class<\/h2>/', $result);
+		$this->assertPattern('/Create the class <em>SideboxComponent<\/em> in file:/', $result);
+		$this->assertPattern('/(\/|\\\)sidebox.php/', $result);
+	}
+
+/**
+ * testMissingModel method
+ *
+ * @access public
+ * @return void
+ */
+	function testMissingModel() {
+		ob_start();
+		$TestErrorHandler = new TestErrorHandler('missingModel', array('className' => 'Article', 'file' => 'article.php'));
+		$result = ob_get_clean();
+		$this->assertPattern('/<h2>Missing Model<\/h2>/', $result);
+		$this->assertPattern('/<em>Article<\/em> could not be found./', $result);
+		$this->assertPattern('/(\/|\\\)article.php/', $result);
+	}
+
+/**
+ * testing that having a code => 500 in the cakeError call makes an 
+ * internal server error.
+ *
+ * @return void
+ */
+	function testThatCode500Works() {
+		Configure::write('debug', 0);
+		ob_start();
+		$TestErrorHandler = new TestErrorHandler('missingTable', array(
+			'className' => 'Article',
+			'table' => 'articles',
+			'code' => 500
+		));
+		$result = ob_get_clean();
+		$this->assertPattern('/<h2>An Internal Error Has Occurred<\/h2>/', $result);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/file.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/file.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/file.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,490 @@
+<?php
+/**
+ * FileTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', 'File');
+
+/**
+ * FileTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class FileTest extends CakeTestCase {
+
+/**
+ * File property
+ *
+ * @var mixed null
+ * @access public
+ */
+	var $File = null;
+
+/**
+ * testBasic method
+ *
+ * @access public
+ * @return void
+ */
+	function testBasic() {
+		$file = __FILE__;
+		$this->File =& new File($file);
+
+		$result = $this->File->pwd();
+		$expecting = $file;
+		$this->assertEqual($result, $expecting);
+
+		$result = $this->File->name;
+		$expecting = basename(__FILE__);
+		$this->assertEqual($result, $expecting);
+
+		$result = $this->File->info();
+		$expecting = array(
+			'dirname' => dirname(__FILE__), 'basename' => basename(__FILE__),
+			'extension' => 'php', 'filename' =>'file.test'
+		);
+		$this->assertEqual($result, $expecting);
+
+		$result = $this->File->ext();
+		$expecting = 'php';
+		$this->assertEqual($result, $expecting);
+
+		$result = $this->File->name();
+		$expecting = 'file.test';
+		$this->assertEqual($result, $expecting);
+
+		$result = $this->File->md5();
+		$expecting = md5_file($file);
+		$this->assertEqual($result, $expecting);
+
+		$result = $this->File->md5(true);
+		$expecting = md5_file($file);
+		$this->assertEqual($result, $expecting);
+
+		$result = $this->File->size();
+		$expecting = filesize($file);
+		$this->assertEqual($result, $expecting);
+
+		$result = $this->File->owner();
+		$expecting = fileowner($file);
+		$this->assertEqual($result, $expecting);
+
+		$result = $this->File->group();
+		$expecting = filegroup($file);
+		$this->assertEqual($result, $expecting);
+
+		$result = $this->File->Folder();
+		$this->assertIsA($result, 'Folder');
+
+		$this->skipIf(DIRECTORY_SEPARATOR === '\\', '%s File permissions tests not supported on Windows');
+		$result = $this->File->perms();
+		$expecting = '0644';
+		$this->assertEqual($result, $expecting);
+	}
+
+/**
+ * testRead method
+ *
+ * @access public
+ * @return void
+ */
+	function testRead() {
+		$file = __FILE__;
+		$this->File =& new File($file);
+
+		$result = $this->File->read();
+		$expecting = file_get_contents(__FILE__);
+		$this->assertEqual($result, $expecting);
+		$this->assertTrue(!is_resource($this->File->handle));
+
+		$this->File->lock = true;
+		$result = $this->File->read();
+		$expecting = file_get_contents(__FILE__);
+		$this->assertEqual($result, trim($expecting));
+		$this->File->lock = null;
+
+		$data = $expecting;
+		$expecting = substr($data, 0, 3);
+		$result = $this->File->read(3);
+		$this->assertEqual($result, $expecting);
+		$this->assertTrue(is_resource($this->File->handle));
+
+		$expecting = substr($data, 3, 3);
+		$result = $this->File->read(3);
+		$this->assertEqual($result, $expecting);
+	}
+
+/**
+ * testOffset method
+ *
+ * @access public
+ * @return void
+ */
+	function testOffset() {
+		$this->File->close();
+
+		$result = $this->File->offset();
+		$this->assertFalse($result);
+
+		$this->assertFalse(is_resource($this->File->handle));
+		$success = $this->File->offset(0);
+		$this->assertTrue($success);
+		$this->assertTrue(is_resource($this->File->handle));
+
+		$result = $this->File->offset();
+		$expecting = 0;
+		$this->assertIdentical($result, $expecting);
+
+		$data = file_get_contents(__FILE__);
+		$success = $this->File->offset(5);
+		$expecting = substr($data, 5, 3);
+		$result = $this->File->read(3);
+		$this->assertTrue($success);
+		$this->assertEqual($result, $expecting);
+
+		$result = $this->File->offset();
+		$expecting = 5+3;
+		$this->assertIdentical($result, $expecting);
+	}
+
+/**
+ * testOpen method
+ *
+ * @access public
+ * @return void
+ */
+	function testOpen() {
+		$this->File->handle = null;
+
+		$r = $this->File->open();
+		$this->assertTrue(is_resource($this->File->handle));
+		$this->assertTrue($r);
+
+		$handle = $this->File->handle;
+		$r = $this->File->open();
+		$this->assertTrue($r);
+		$this->assertTrue($handle === $this->File->handle);
+		$this->assertTrue(is_resource($this->File->handle));
+
+		$r = $this->File->open('r', true);
+		$this->assertTrue($r);
+		$this->assertFalse($handle === $this->File->handle);
+		$this->assertTrue(is_resource($this->File->handle));
+	}
+
+/**
+ * testClose method
+ *
+ * @access public
+ * @return void
+ */
+	function testClose() {
+		$this->File->handle = null;
+		$this->assertFalse(is_resource($this->File->handle));
+		$this->assertTrue($this->File->close());
+		$this->assertFalse(is_resource($this->File->handle));
+
+		$this->File->handle = fopen(__FILE__, 'r');
+		$this->assertTrue(is_resource($this->File->handle));
+		$this->assertTrue($this->File->close());
+		$this->assertFalse(is_resource($this->File->handle));
+	}
+
+/**
+ * testCreate method
+ *
+ * @access public
+ * @return void
+ */
+	function testCreate() {
+		$tmpFile = TMP.'tests'.DS.'cakephp.file.test.tmp';
+		$File =& new File($tmpFile, true, 0777);
+		$this->assertTrue($File->exists());
+	}
+
+/**
+ * testOpeningNonExistantFileCreatesIt method
+ *
+ * @access public
+ * @return void
+ */
+	function testOpeningNonExistantFileCreatesIt() {
+		$someFile =& new File(TMP . 'some_file.txt', false);
+		$this->assertTrue($someFile->open());
+		$this->assertEqual($someFile->read(), '');
+		$someFile->close();
+		$someFile->delete();
+	}
+
+/**
+ * testPrepare method
+ *
+ * @access public
+ * @return void
+ */
+	function testPrepare() {
+		$string = "some\nvery\ncool\r\nteststring here\n\n\nfor\r\r\n\n\r\n\nhere";
+		if (DS == '\\') {
+			$expected = "some\r\nvery\r\ncool\r\nteststring here\r\n\r\n\r\n";
+			$expected .= "for\r\n\r\n\r\n\r\n\r\nhere";
+		} else {
+			$expected = "some\nvery\ncool\nteststring here\n\n\nfor\n\n\n\n\nhere";
+		}
+		$this->assertIdentical(File::prepare($string), $expected);
+
+		$expected = "some\r\nvery\r\ncool\r\nteststring here\r\n\r\n\r\n";
+		$expected .= "for\r\n\r\n\r\n\r\n\r\nhere";
+		$this->assertIdentical(File::prepare($string, true), $expected);
+	}
+
+/**
+ * testReadable method
+ *
+ * @access public
+ * @return void
+ */
+	function testReadable() {
+		$someFile =& new File(TMP . 'some_file.txt', false);
+		$this->assertTrue($someFile->open());
+		$this->assertTrue($someFile->readable());
+		$someFile->close();
+		$someFile->delete();
+	}
+
+/**
+ * testWritable method
+ *
+ * @access public
+ * @return void
+ */
+	function testWritable() {
+		$someFile =& new File(TMP . 'some_file.txt', false);
+		$this->assertTrue($someFile->open());
+		$this->assertTrue($someFile->writable());
+		$someFile->close();
+		$someFile->delete();
+	}
+
+/**
+ * testExecutable method
+ *
+ * @access public
+ * @return void
+ */
+	function testExecutable() {
+		$someFile =& new File(TMP . 'some_file.txt', false);
+		$this->assertTrue($someFile->open());
+		$this->assertFalse($someFile->executable());
+		$someFile->close();
+		$someFile->delete();
+	}
+
+/**
+ * testLastAccess method
+ *
+ * @access public
+ * @return void
+ */
+	function testLastAccess() {
+		$someFile =& new File(TMP . 'some_file.txt', false);
+		$this->assertFalse($someFile->lastAccess());
+		$this->assertTrue($someFile->open());
+		$this->assertEqual($someFile->lastAccess(), time());
+		$someFile->close();
+		$someFile->delete();
+	}
+
+/**
+ * testLastChange method
+ *
+ * @access public
+ * @return void
+ */
+	function testLastChange() {
+		$someFile =& new File(TMP . 'some_file.txt', false);
+		$this->assertFalse($someFile->lastChange());
+		$this->assertTrue($someFile->open('r+'));
+		$this->assertEqual($someFile->lastChange(), time());
+		$someFile->write('something');
+		$this->assertEqual($someFile->lastChange(), time());
+		$someFile->close();
+		$someFile->delete();
+	}
+
+/**
+ * testWrite method
+ *
+ * @access public
+ * @return void
+ */
+	function testWrite() {
+		if (!$tmpFile = $this->_getTmpFile()) {
+			return false;
+		};
+		if (file_exists($tmpFile)) {
+			unlink($tmpFile);
+		}
+
+		$TmpFile =& new File($tmpFile);
+		$this->assertFalse(file_exists($tmpFile));
+		$this->assertFalse(is_resource($TmpFile->handle));
+
+		$testData = array('CakePHP\'s', ' test suite', ' was here ...', '');
+		foreach ($testData as $data) {
+			$r = $TmpFile->write($data);
+			$this->assertTrue($r);
+			$this->assertTrue(file_exists($tmpFile));
+			$this->assertEqual($data, file_get_contents($tmpFile));
+			$this->assertTrue(is_resource($TmpFile->handle));
+			$TmpFile->close();
+
+		}
+		unlink($tmpFile);
+	}
+
+/**
+ * testAppend method
+ *
+ * @access public
+ * @return void
+ */
+	function testAppend() {
+		if (!$tmpFile = $this->_getTmpFile()) {
+			return false;
+		};
+		if (file_exists($tmpFile)) {
+			unlink($tmpFile);
+		}
+
+		$TmpFile =& new File($tmpFile);
+		$this->assertFalse(file_exists($tmpFile));
+
+		$fragments = array('CakePHP\'s', ' test suite', ' was here ...', '');
+		$data = null;
+		foreach ($fragments as $fragment) {
+			$r = $TmpFile->append($fragment);
+			$this->assertTrue($r);
+			$this->assertTrue(file_exists($tmpFile));
+			$data = $data.$fragment;
+			$this->assertEqual($data, file_get_contents($tmpFile));
+			$TmpFile->close();
+		}
+	}
+
+/**
+ * testDelete method
+ *
+ * @access public
+ * @return void
+ */
+	function testDelete() {
+		if (!$tmpFile = $this->_getTmpFile()) {
+			return false;
+		}
+
+		if (!file_exists($tmpFile)) {
+			touch($tmpFile);
+		}
+		$TmpFile =& new File($tmpFile);
+		$this->assertTrue(file_exists($tmpFile));
+		$result = $TmpFile->delete();
+		$this->assertTrue($result);
+		$this->assertFalse(file_exists($tmpFile));
+
+		$TmpFile =& new File('/this/does/not/exist');
+		$result = $TmpFile->delete();
+		$this->assertFalse($result);
+	}
+
+/**
+ * Windows has issues unlinking files if there are
+ * active filehandles open.
+ *
+ * @return void
+ */
+	function testDeleteAfterRead() {
+		if (!$tmpFile = $this->_getTmpFile()) {
+			return false;
+		}
+		if (!file_exists($tmpFile)) {
+			touch($tmpFile);
+		}
+		$file =& new File($tmpFile);
+		$file->read();
+		$this->assertTrue($file->delete());
+	}
+
+/**
+ * testCopy method
+ *
+ * @access public
+ * @return void
+ */
+	function testCopy() {
+		$dest = TMP . 'tests' . DS . 'cakephp.file.test.tmp';
+		$file = __FILE__;
+		$this->File =& new File($file);
+		$result = $this->File->copy($dest);
+		$this->assertTrue($result);
+
+		$result = $this->File->copy($dest, true);
+		$this->assertTrue($result);
+
+		$result = $this->File->copy($dest, false);
+		$this->assertFalse($result);
+
+		$this->File->close();
+		unlink($dest);
+
+		$TmpFile =& new File('/this/does/not/exist');
+		$result = $TmpFile->copy($dest);
+		$this->assertFalse($result);
+
+		$TmpFile->close();
+	}
+
+/**
+ * getTmpFile method
+ *
+ * @param bool $paintSkip
+ * @access protected
+ * @return void
+ */
+	function _getTmpFile($paintSkip = true) {
+		$tmpFile = TMP . 'tests' . DS . 'cakephp.file.test.tmp';
+		if (is_writable(dirname($tmpFile)) && (!file_exists($tmpFile) || is_writable($tmpFile))) {
+			return $tmpFile;
+		};
+
+		if ($paintSkip) {
+			$caller = 'test';
+			if (function_exists('debug_backtrace')) {
+				$trace = debug_backtrace();
+				$caller = $trace[1]['function'] . '()';
+			}
+			$assertLine = new SimpleStackTrace(array(__FUNCTION__));
+			$assertLine = $assertLine->traceMethod();
+			$shortPath = substr($tmpFile, strlen(ROOT));
+
+			$message = '[FileTest] Skipping %s because "%s" not writeable!';
+			$message = sprintf(__($message, true), $caller, $shortPath) . $assertLine;
+			$this->_reporter->paintSkip($message);
+		}
+		return false;
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/folder.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/folder.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/folder.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,794 @@
+<?php
+/**
+ * FolderTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', 'File');
+
+/**
+ * FolderTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class FolderTest extends CakeTestCase {
+
+/**
+ * testBasic method
+ *
+ * @access public
+ * @return void
+ */
+	function testBasic() {
+		$path = dirname(__FILE__);
+		$Folder =& new Folder($path);
+
+		$result = $Folder->pwd();
+		$this->assertEqual($result, $path);
+
+		$result = Folder::addPathElement($path, 'test');
+		$expected = $path . DS . 'test';
+		$this->assertEqual($result, $expected);
+
+		$result = $Folder->cd(ROOT);
+		$expected = ROOT;
+		$this->assertEqual($result, $expected);
+
+		$result = $Folder->cd(ROOT . DS . 'non-existent');
+		$this->assertFalse($result);
+	}
+
+/**
+ * testInPath method
+ *
+ * @access public
+ * @return void
+ */
+	function testInPath() {
+		$path = dirname(dirname(__FILE__));
+		$inside = dirname($path) . DS;
+
+		$Folder =& new Folder($path);
+
+		$result = $Folder->pwd();
+		$this->assertEqual($result, $path);
+
+		$result = Folder::isSlashTerm($inside);
+		$this->assertTrue($result);
+
+		$result = $Folder->realpath('tests/');
+		$this->assertEqual($result, $path . DS .'tests' . DS);
+
+		$result = $Folder->inPath('tests' . DS);
+		$this->assertTrue($result);
+
+		$result = $Folder->inPath(DS . 'non-existing' . $inside);
+		$this->assertFalse($result);
+	}
+
+/**
+ * test creation of single and mulitple paths.
+ *
+ * @return void
+ */
+	function testCreation() {
+		$folder =& new Folder(TMP . 'tests');
+		$result = $folder->create(TMP . 'tests' . DS . 'first' . DS . 'second' . DS . 'third');
+		$this->assertTrue($result);
+
+		rmdir(TMP . 'tests' . DS . 'first' . DS . 'second' . DS . 'third');
+		rmdir(TMP . 'tests' . DS . 'first' . DS . 'second');
+		rmdir(TMP . 'tests' . DS . 'first');
+
+		$folder =& new Folder(TMP . 'tests');
+		$result = $folder->create(TMP . 'tests' . DS . 'first');
+		$this->assertTrue($result);
+		rmdir(TMP . 'tests' . DS . 'first');
+	}
+
+/**
+ * test that creation of folders with trailing ds works
+ *
+ * @return void
+ */
+	function testCreateWithTrailingDs() {
+		$folder =& new Folder(TMP);
+		$path = TMP . 'tests' . DS . 'trailing' . DS . 'dir' . DS;
+		$result = $folder->create($path);
+		$this->assertTrue($result);
+
+		$this->assertTrue(is_dir($path), 'Folder was not made');
+
+		$folder =& new Folder(TMP . 'tests' . DS . 'trailing');
+		$this->assertTrue($folder->delete());
+	}
+
+/**
+ * test recurisve directory create failure.
+ *
+ * @return void
+ */
+	function testRecursiveCreateFailure() {
+		if ($this->skipIf(DS == '\\', 'Cant perform operations using permissions on windows. %s')) {
+			return;
+		}
+		$path = TMP . 'tests' . DS . 'one';
+		mkdir($path);
+		chmod($path, '0444');
+
+		$this->expectError();
+
+		$folder =& new Folder($path);
+		$result = $folder->create($path . DS . 'two' . DS . 'three');
+		$this->assertFalse($result);
+
+		chmod($path, '0777');
+		rmdir($path);
+	}
+/**
+ * testOperations method
+ *
+ * @access public
+ * @return void
+ */
+	function testOperations() {
+		$path = TEST_CAKE_CORE_INCLUDE_PATH . 'console' . DS . 'templates' . DS . 'skel';
+		$Folder =& new Folder($path);
+
+		$result = is_dir($Folder->pwd());
+		$this->assertTrue($result);
+
+		$new = TMP . 'test_folder_new';
+		$result = $Folder->create($new);
+		$this->assertTrue($result);
+
+		$copy = TMP . 'test_folder_copy';
+		$result = $Folder->copy($copy);
+		$this->assertTrue($result);
+
+		$copy = TMP . 'test_folder_copy';
+		$result = $Folder->copy($copy);
+		$this->assertTrue($result);
+
+		$copy = TMP . 'test_folder_copy';
+		$result = $Folder->chmod($copy, 0755, false);
+		$this->assertTrue($result);
+
+		$result = $Folder->cd($copy);
+		$this->assertTrue($result);
+
+		$mv = TMP . 'test_folder_mv';
+		$result = $Folder->move($mv);
+		$this->assertTrue($result);
+
+		$mv = TMP . 'test_folder_mv_2';
+		$result = $Folder->move($mv);
+		$this->assertTrue($result);
+
+		$result = $Folder->delete($new);
+		$this->assertTrue($result);
+
+		$result = $Folder->delete($mv);
+		$this->assertTrue($result);
+
+		$result = $Folder->delete($mv);
+		$this->assertTrue($result);
+
+		$new = APP . 'index.php';
+		$result = $Folder->create($new);
+		$this->assertFalse($result);
+
+		$expected = $new . ' is a file';
+		$result = array_pop($Folder->errors());
+		$this->assertEqual($result, $expected);
+
+		$new = TMP . 'test_folder_new';
+		$result = $Folder->create($new);
+		$this->assertTrue($result);
+
+		$result = $Folder->cd($new);
+		$this->assertTrue($result);
+
+		$result = $Folder->delete();
+		$this->assertTrue($result);
+
+		$Folder =& new Folder('non-existent');
+		$result = $Folder->pwd();
+		$this->assertNull($result);
+	}
+
+/**
+ * testChmod method
+ *
+ * @return void
+ * @access public
+ */
+	function testChmod() {
+		$this->skipIf(DIRECTORY_SEPARATOR === '\\', '%s Folder permissions tests not supported on Windows');
+
+		$path = TEST_CAKE_CORE_INCLUDE_PATH . 'console' . DS . 'templates' . DS . 'skel';
+		$Folder =& new Folder($path);
+
+		$subdir = 'test_folder_new';
+		$new = TMP . $subdir;
+
+		$this->assertTrue($Folder->create($new));
+		$this->assertTrue($Folder->create($new . DS . 'test1'));
+		$this->assertTrue($Folder->create($new . DS . 'test2'));
+
+		$filePath = $new . DS . 'test1.php';
+		$File =& new File($filePath);
+		$this->assertTrue($File->create());
+		$copy = TMP . 'test_folder_copy';
+
+		$this->assertTrue($Folder->chmod($new, 0777, true));
+		$this->assertEqual($File->perms(), '0777');
+
+		$Folder->delete($new);
+	}
+
+/**
+ * testRealPathForWebroot method
+ *
+ * @access public
+ * @return void
+ */
+	function testRealPathForWebroot() {
+		$Folder = new Folder('files/');
+		$this->assertEqual(realpath('files/'), $Folder->path);
+	}
+
+/**
+ * testZeroAsDirectory method
+ *
+ * @access public
+ * @return void
+ */
+	function testZeroAsDirectory() {
+		$Folder =& new Folder(TMP);
+		$new = TMP . '0';
+		$this->assertTrue($Folder->create($new));
+
+		$result = $Folder->read(true, true);
+		$expected = array('0', 'cache', 'logs', 'sessions', 'tests');
+		$this->assertEqual($expected, $result[0]);
+
+		$result = $Folder->read(true, array('.', '..', 'logs', '.svn'));
+		$expected = array('0', 'cache', 'sessions', 'tests');
+		$this->assertEqual($expected, $result[0]);
+
+		$result = $Folder->delete($new);
+		$this->assertTrue($result);
+	}
+
+/**
+ * test Adding path elements to a path
+ *
+ * @return void
+ */
+	function testAddPathElement() {
+		$result = Folder::addPathElement(DS . 'some' . DS . 'dir', 'another_path');
+		$this->assertEqual($result, DS . 'some' . DS . 'dir' . DS . 'another_path');
+
+		$result = Folder::addPathElement(DS . 'some' . DS . 'dir' . DS, 'another_path');
+		$this->assertEqual($result, DS . 'some' . DS . 'dir' . DS . 'another_path');
+	}
+/**
+ * testFolderRead method
+ *
+ * @access public
+ * @return void
+ */
+	function testFolderRead() {
+		$Folder =& new Folder(TMP);
+
+		$expected = array('cache', 'logs', 'sessions', 'tests');
+		$result = $Folder->read(true, true);
+		$this->assertEqual($result[0], $expected);
+
+		$Folder->path = TMP . DS . 'non-existent';
+		$expected = array(array(), array());
+		$result = $Folder->read(true, true);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testFolderTree method
+ *
+ * @access public
+ * @return void
+ */
+	function testFolderTree() {
+		$Folder =& new Folder();
+		$expected = array(
+			array(
+				TEST_CAKE_CORE_INCLUDE_PATH . 'config',
+				TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode',
+				TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' .  DS . 'casefolding'
+			),
+			array(
+				TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'config.php',
+				TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'paths.php',
+				TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' .  DS . 'casefolding' . DS . '0080_00ff.php',
+				TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' .  DS . 'casefolding' . DS . '0100_017f.php',
+				TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' .  DS . 'casefolding' . DS . '0180_024F.php',
+				TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' .  DS . 'casefolding' . DS . '0250_02af.php',
+				TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' .  DS . 'casefolding' . DS . '0370_03ff.php',
+				TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' .  DS . 'casefolding' . DS . '0400_04ff.php',
+				TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' .  DS . 'casefolding' . DS . '0500_052f.php',
+				TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' .  DS . 'casefolding' . DS . '0530_058f.php',
+				TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' .  DS . 'casefolding' . DS . '1e00_1eff.php',
+				TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' .  DS . 'casefolding' . DS . '1f00_1fff.php',
+				TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' .  DS . 'casefolding' . DS . '2100_214f.php',
+				TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' .  DS . 'casefolding' . DS . '2150_218f.php',
+				TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' .  DS . 'casefolding' . DS . '2460_24ff.php',
+				TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' .  DS . 'casefolding' . DS . '2c00_2c5f.php',
+				TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' .  DS . 'casefolding' . DS . '2c60_2c7f.php',
+				TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' .  DS . 'casefolding' . DS . '2c80_2cff.php',
+				TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'unicode' .  DS . 'casefolding' . DS . 'ff00_ffef.php'
+			)
+		);
+
+		$result = $Folder->tree(TEST_CAKE_CORE_INCLUDE_PATH . 'config', false);
+		$this->assertIdentical(array_diff($expected[0], $result[0]), array());
+		$this->assertIdentical(array_diff($result[0], $expected[0]), array());
+
+		$result = $Folder->tree(TEST_CAKE_CORE_INCLUDE_PATH . 'config', false, 'dir');
+		$this->assertIdentical(array_diff($expected[0], $result), array());
+		$this->assertIdentical(array_diff($result, $expected[0]), array());
+
+		$result = $Folder->tree(TEST_CAKE_CORE_INCLUDE_PATH . 'config', false, 'files');
+		$this->assertIdentical(array_diff($expected[1], $result), array());
+		$this->assertIdentical(array_diff($result, $expected[1]), array());
+	}
+
+/**
+ * testWindowsPath method
+ *
+ * @access public
+ * @return void
+ */
+	function testWindowsPath() {
+		$this->assertFalse(Folder::isWindowsPath('0:\\cake\\is\\awesome'));
+		$this->assertTrue(Folder::isWindowsPath('C:\\cake\\is\\awesome'));
+		$this->assertTrue(Folder::isWindowsPath('d:\\cake\\is\\awesome'));
+		$this->assertTrue(Folder::isWindowsPath('\\\\vmware-host\\Shared Folders\\file'));
+	}
+
+/**
+ * testIsAbsolute method
+ *
+ * @access public
+ * @return void
+ */
+	function testIsAbsolute() {
+		$this->assertFalse(Folder::isAbsolute('path/to/file'));
+		$this->assertFalse(Folder::isAbsolute('cake/'));
+		$this->assertFalse(Folder::isAbsolute('path\\to\\file'));
+		$this->assertFalse(Folder::isAbsolute('0:\\path\\to\\file'));
+		$this->assertFalse(Folder::isAbsolute('\\path/to/file'));
+		$this->assertFalse(Folder::isAbsolute('\\path\\to\\file'));
+
+		$this->assertTrue(Folder::isAbsolute('/usr/local'));
+		$this->assertTrue(Folder::isAbsolute('//path/to/file'));
+		$this->assertTrue(Folder::isAbsolute('C:\\cake'));
+		$this->assertTrue(Folder::isAbsolute('C:\\path\\to\\file'));
+		$this->assertTrue(Folder::isAbsolute('d:\\path\\to\\file'));
+		$this->assertTrue(Folder::isAbsolute('\\\\vmware-host\\Shared Folders\\file'));
+	}
+
+/**
+ * testIsSlashTerm method
+ *
+ * @access public
+ * @return void
+ */
+	function testIsSlashTerm() {
+		$this->assertFalse(Folder::isSlashTerm('cake'));
+
+		$this->assertTrue(Folder::isSlashTerm('C:\\cake\\'));
+		$this->assertTrue(Folder::isSlashTerm('/usr/local/'));
+	}
+
+/**
+ * testStatic method
+ *
+ * @access public
+ * @return void
+ */
+	function testSlashTerm() {
+		$result = Folder::slashTerm('/path/to/file');
+		$this->assertEqual($result, '/path/to/file/');
+	}
+
+/**
+ * testNormalizePath method
+ *
+ * @access public
+ * @return void
+ */
+	function testNormalizePath() {
+		$path = '/path/to/file';
+		$result = Folder::normalizePath($path);
+		$this->assertEqual($result, '/');
+
+		$path = '\\path\\\to\\\file';
+		$result = Folder::normalizePath($path);
+		$this->assertEqual($result, '/');
+
+		$path = 'C:\\path\\to\\file';
+		$result = Folder::normalizePath($path);
+		$this->assertEqual($result, '\\');
+	}
+
+/**
+ * correctSlashFor method
+ *
+ * @access public
+ * @return void
+ */
+	function testCorrectSlashFor() {
+		$path = '/path/to/file';
+		$result = Folder::correctSlashFor($path);
+		$this->assertEqual($result, '/');
+
+		$path = '\\path\\to\\file';
+		$result = Folder::correctSlashFor($path);
+		$this->assertEqual($result, '/');
+
+		$path = 'C:\\path\to\\file';
+		$result = Folder::correctSlashFor($path);
+		$this->assertEqual($result, '\\');
+	}
+
+/**
+ * testInCakePath method
+ *
+ * @access public
+ * @return void
+ */
+	function testInCakePath() {
+		$Folder =& new Folder();
+		$Folder->cd(ROOT);
+		$path = 'C:\\path\\to\\file';
+		$result = $Folder->inCakePath($path);
+		$this->assertFalse($result);
+
+		$path = ROOT;
+		$Folder->cd(ROOT);
+		$result = $Folder->inCakePath($path);
+		$this->assertFalse($result);
+
+		// WHY DOES THIS FAIL ??
+		$path = DS . 'cake' . DS . 'config';
+		$Folder->cd(ROOT . DS . 'cake' . DS . 'config');
+		$result = $Folder->inCakePath($path);
+		$this->assertTrue($result);
+	}
+
+/**
+ * testFind method
+ *
+ * @access public
+ * @return void
+ */
+	function testFind() {
+		$Folder =& new Folder();
+		$Folder->cd(TEST_CAKE_CORE_INCLUDE_PATH . 'config');
+		$result = $Folder->find();
+		$expected = array('config.php', 'paths.php');
+		$this->assertIdentical(array_diff($expected, $result), array());
+		$this->assertIdentical(array_diff($result, $expected), array());
+
+		$result = $Folder->find('.*', true);
+		$expected = array('config.php', 'paths.php');
+		$this->assertIdentical($result, $expected);
+
+		$result = $Folder->find('.*\.php');
+		$expected = array('config.php', 'paths.php');
+		$this->assertIdentical(array_diff($expected, $result), array());
+		$this->assertIdentical(array_diff($result, $expected), array());
+
+		$result = $Folder->find('.*\.php', true);
+		$expected = array('config.php', 'paths.php');
+		$this->assertIdentical($result, $expected);
+
+		$result = $Folder->find('.*ig\.php');
+		$expected = array('config.php');
+		$this->assertIdentical($result, $expected);
+
+		$result = $Folder->find('paths\.php');
+		$expected = array('paths.php');
+		$this->assertIdentical($result, $expected);
+
+		$Folder->cd(TMP);
+		$file = new File($Folder->pwd() . DS . 'paths.php', true);
+		$Folder->create($Folder->pwd() . DS . 'testme');
+		$Folder->cd('testme');
+		$result = $Folder->find('paths\.php');
+		$expected = array();
+		$this->assertIdentical($result, $expected);
+
+		$Folder->cd($Folder->pwd() . '/..');
+		$result = $Folder->find('paths\.php');
+		$expected = array('paths.php');
+		$this->assertIdentical($result, $expected);
+
+		$Folder->cd(TMP);
+		$Folder->delete($Folder->pwd() . DS . 'testme');
+		$file->delete();
+	}
+
+/**
+ * testFindRecursive method
+ *
+ * @access public
+ * @return void
+ */
+	function testFindRecursive() {
+		$Folder =& new Folder();
+		$Folder->cd(TEST_CAKE_CORE_INCLUDE_PATH);
+		$result = $Folder->findRecursive('(config|paths)\.php');
+		$expected = array(
+			TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'config.php',
+			TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'paths.php'
+		);
+		$this->assertIdentical(array_diff($expected, $result), array());
+		$this->assertIdentical(array_diff($result, $expected), array());
+
+		$result = $Folder->findRecursive('(config|paths)\.php', true);
+		$expected = array(
+			TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'config.php',
+			TEST_CAKE_CORE_INCLUDE_PATH . 'config' . DS . 'paths.php'
+		);
+		$this->assertIdentical($result, $expected);
+
+		$Folder->cd(TMP);
+		$Folder->create($Folder->pwd() . DS . 'testme');
+		$Folder->cd('testme');
+		$File =& new File($Folder->pwd() . DS . 'paths.php');
+		$File->create();
+		$Folder->cd(TMP . 'sessions');
+		$result = $Folder->findRecursive('paths\.php');
+		$expected = array();
+		$this->assertIdentical($result, $expected);
+
+		$Folder->cd(TMP . 'testme');
+		$File =& new File($Folder->pwd() . DS . 'my.php');
+		$File->create();
+		$Folder->cd($Folder->pwd() . '/../..');
+
+		$result = $Folder->findRecursive('(paths|my)\.php');
+		$expected = array(
+			TMP . 'testme' . DS . 'my.php',
+			TMP . 'testme' . DS . 'paths.php'
+		);
+		$this->assertIdentical(array_diff($expected, $result), array());
+		$this->assertIdentical(array_diff($result, $expected), array());
+
+		$result = $Folder->findRecursive('(paths|my)\.php', true);
+		$expected = array(
+			TMP . 'testme' . DS . 'my.php',
+			TMP . 'testme' . DS . 'paths.php'
+		);
+		$this->assertIdentical($result, $expected);
+
+		$Folder->cd(TEST_CAKE_CORE_INCLUDE_PATH . 'config');
+		$Folder->cd(TMP);
+		$Folder->delete($Folder->pwd() . DS . 'testme');
+		$File->delete();
+	}
+
+/**
+ * testConstructWithNonExistantPath method
+ *
+ * @access public
+ * @return void
+ */
+	function testConstructWithNonExistantPath() {
+		$Folder =& new Folder(TMP . 'config_non_existant', true);
+		$this->assertTrue(is_dir(TMP . 'config_non_existant'));
+		$Folder->cd(TMP);
+		$Folder->delete($Folder->pwd() . 'config_non_existant');
+	}
+
+/**
+ * testDirSize method
+ *
+ * @access public
+ * @return void
+ */
+	function testDirSize() {
+		$Folder =& new Folder(TMP . 'config_non_existant', true);
+		$this->assertEqual($Folder->dirSize(), 0);
+
+		$File =& new File($Folder->pwd() . DS . 'my.php', true, 0777);
+		$File->create();
+		$File->write('something here');
+		$File->close();
+		$this->assertEqual($Folder->dirSize(), 14);
+
+		$Folder->cd(TMP);
+		$Folder->delete($Folder->pwd() . 'config_non_existant');
+	}
+
+/**
+ * testDelete method
+ *
+ * @access public
+ * @return void
+ */
+	function testDelete() {
+		$path = TMP . 'folder_delete_test';
+		$Folder =& new Folder($path, true);
+		touch(TMP . 'folder_delete_test' . DS . 'file1');
+		touch(TMP . 'folder_delete_test' . DS . 'file2');
+
+		$return = $Folder->delete();
+		$this->assertTrue($return);
+
+		$messages = $Folder->messages();
+		$errors = $Folder->errors();
+		$this->assertEqual($errors, array());
+
+		$expected = array(
+			$path . ' created',
+			$path . DS . 'file1 removed',
+			$path . DS . 'file2 removed',
+			$path . ' removed'
+		);
+		$this->assertEqual($expected, $messages);
+	}
+
+/**
+ * testCopy method
+ *
+ * Verify that directories and files are copied recursively
+ * even if the destination directory already exists.
+ * Subdirectories existing in both destination and source directory
+ * are skipped and not merged or overwritten.
+ *
+ * @return void
+ * @access public
+ * @link   https://trac.cakephp.org/ticket/6259
+ */
+	function testCopy() {
+		$path = TMP . 'folder_test';
+		$folder1 = $path . DS . 'folder1';
+		$folder2 = $folder1 . DS . 'folder2';
+		$folder3 = $path . DS . 'folder3';
+		$file1 = $folder1 . DS . 'file1.php';
+		$file2 = $folder2 . DS . 'file2.php';
+
+		new Folder($path, true);
+		new Folder($folder1, true);
+		new Folder($folder2, true);
+		new Folder($folder3, true);
+		touch($file1);
+		touch($file2);
+
+		$Folder =& new Folder($folder1);
+		$result = $Folder->copy($folder3);
+		$this->assertTrue($result);
+		$this->assertTrue(file_exists($folder3 . DS . 'file1.php'));
+		$this->assertTrue(file_exists($folder3 . DS . 'folder2' . DS . 'file2.php'));
+
+		$Folder =& new Folder($folder3);
+		$Folder->delete();
+
+		$Folder =& new Folder($folder1);
+		$result = $Folder->copy($folder3);
+		$this->assertTrue($result);
+		$this->assertTrue(file_exists($folder3 . DS . 'file1.php'));
+		$this->assertTrue(file_exists($folder3 . DS . 'folder2' . DS . 'file2.php'));
+
+		$Folder =& new Folder($folder3);
+		$Folder->delete();
+
+		new Folder($folder3, true);
+		new Folder($folder3 . DS . 'folder2', true);
+		file_put_contents($folder3 . DS . 'folder2' . DS . 'file2.php', 'untouched');
+
+		$Folder =& new Folder($folder1);
+		$result = $Folder->copy($folder3);
+		$this->assertTrue($result);
+		$this->assertTrue(file_exists($folder3 . DS . 'file1.php'));
+		$this->assertEqual(file_get_contents($folder3 . DS . 'folder2' . DS . 'file2.php'), 'untouched');
+
+		$Folder =& new Folder($path);
+		$Folder->delete();
+	}
+
+/**
+ * testMove method
+ *
+ * Verify that directories and files are moved recursively
+ * even if the destination directory already exists.
+ * Subdirectories existing in both destination and source directory
+ * are skipped and not merged or overwritten.
+ *
+ * @return void
+ * @access public
+ * @link   https://trac.cakephp.org/ticket/6259
+ */
+	function testMove() {
+		$path = TMP . 'folder_test';
+		$folder1 = $path . DS . 'folder1';
+		$folder2 = $folder1 . DS . 'folder2';
+		$folder3 = $path . DS . 'folder3';
+		$file1 = $folder1 . DS . 'file1.php';
+		$file2 = $folder2 . DS . 'file2.php';
+
+		new Folder($path, true);
+		new Folder($folder1, true);
+		new Folder($folder2, true);
+		new Folder($folder3, true);
+		touch($file1);
+		touch($file2);
+
+		$Folder =& new Folder($folder1);
+		$result = $Folder->move($folder3);
+		$this->assertTrue($result);
+		$this->assertTrue(file_exists($folder3 . DS . 'file1.php'));
+		$this->assertTrue(is_dir($folder3 . DS . 'folder2'));
+		$this->assertTrue(file_exists($folder3 . DS . 'folder2' . DS . 'file2.php'));
+		$this->assertFalse(file_exists($file1));
+		$this->assertFalse(file_exists($folder2));
+		$this->assertFalse(file_exists($file2));
+
+		$Folder =& new Folder($folder3);
+		$Folder->delete();
+
+		new Folder($folder1, true);
+		new Folder($folder2, true);
+		touch($file1);
+		touch($file2);
+
+		$Folder =& new Folder($folder1);
+		$result = $Folder->move($folder3);
+		$this->assertTrue($result);
+		$this->assertTrue(file_exists($folder3 . DS . 'file1.php'));
+		$this->assertTrue(is_dir($folder3 . DS . 'folder2'));
+		$this->assertTrue(file_exists($folder3 . DS . 'folder2' . DS . 'file2.php'));
+		$this->assertFalse(file_exists($file1));
+		$this->assertFalse(file_exists($folder2));
+		$this->assertFalse(file_exists($file2));
+
+		$Folder =& new Folder($folder3);
+		$Folder->delete();
+
+		new Folder($folder1, true);
+		new Folder($folder2, true);
+		new Folder($folder3, true);
+		new Folder($folder3 . DS . 'folder2', true);
+		touch($file1);
+		touch($file2);
+		file_put_contents($folder3 . DS . 'folder2' . DS . 'file2.php', 'untouched');
+
+		$Folder =& new Folder($folder1);
+		$result = $Folder->move($folder3);
+		$this->assertTrue($result);
+		$this->assertTrue(file_exists($folder3 . DS . 'file1.php'));
+		$this->assertEqual(file_get_contents($folder3 . DS . 'folder2' . DS . 'file2.php'), 'untouched');
+		$this->assertFalse(file_exists($file1));
+		$this->assertFalse(file_exists($folder2));
+		$this->assertFalse(file_exists($file2));
+
+		$Folder =& new Folder($path);
+		$Folder->delete();
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/http_socket.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/http_socket.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/http_socket.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,1558 @@
+<?php
+/**
+ * HttpSocketTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', 'HttpSocket');
+
+class TestHttpSocket extends HttpSocket {
+
+/**
+ * Convenience method for testing protected method
+ *
+ * @param mixed $uri URI (see {@link _parseUri()})
+ * @return array Current configuration settings
+ */
+	function configUri($uri = null) {
+		return parent::_configUri($uri);
+	}
+
+/**
+ * Convenience method for testing protected method
+ *
+ * @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
+ */
+	function parseUri($uri = null, $base = array()) {
+		return parent::_parseUri($uri, $base);
+	}
+
+/**
+ * Convenience method for testing protected method
+ *
+ * @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
+ */
+	function buildUri($uri = array(), $uriTemplate = '%scheme://%user:%pass@%host:%port/%path?%query#%fragment') {
+		return parent::_buildUri($uri, $uriTemplate);
+	}
+
+/**
+ * Convenience method for testing protected method
+ *
+ * @param array $header Header to build
+ * @return string Header built from array
+ */
+	function buildHeader($header, $mode = 'standard') {
+		return parent::_buildHeader($header, $mode);
+	}
+
+/**
+ * Convenience method for testing protected method
+ *
+ * @param string $message Message to parse
+ * @return array Parsed message (with indexed elements such as raw, status, header, body)
+ */
+	function parseResponse($message) {
+		return parent::_parseResponse($message);
+	}
+
+/**
+ * Convenience method for testing protected method
+ *
+ * @param array $header Header as an indexed array (field => value)
+ * @return array Parsed header
+ */
+	function parseHeader($header) {
+		return parent::_parseHeader($header);
+	}
+
+/**
+ * Convenience method for testing protected method
+ *
+ * @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.
+ */
+	function parseQuery($query) {
+		return parent::_parseQuery($query);
+	}
+
+/**
+ * Convenience method for testing protected method
+ *
+ * @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
+ */
+	function decodeBody($body, $encoding = 'chunked') {
+		return parent::_decodeBody($body, $encoding);
+	}
+
+/**
+ * Convenience method for testing protected method
+ *
+ * @param string $body A string continaing the chunked body to decode
+ * @return mixed Array or false
+ */
+	function decodeChunkedBody($body) {
+		return parent::_decodeChunkedBody($body);
+	}
+
+/**
+ * Convenience method for testing protected method
+ *
+ * @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
+ */
+	function buildRequestLine($request = array(), $versionToken = 'HTTP/1.1') {
+		return parent::_buildRequestLine($request, $versionToken);
+	}
+
+/**
+ * Convenience method for testing protected method
+ *
+ * @param boolean $hex true to get them as HEX values, false otherwise
+ * @return array Escape chars
+ */
+	function tokenEscapeChars($hex = true, $chars = null) {
+		return parent::_tokenEscapeChars($hex, $chars);
+	}
+
+/**
+ * Convenience method for testing protected method
+ *
+ * @param string $token Token to escape
+ * @return string Escaped token
+ */
+	function EscapeToken($token, $chars = null) {
+		return parent::_escapeToken($token, $chars);
+	}
+
+/**
+ * Convenience method for testing protected method
+ *
+ * @param string $token Token to unescape
+ * @return string Unescaped token
+ */
+	function unescapeToken($token, $chars = null) {
+		return parent::_unescapeToken($token, $chars);
+	}
+}
+
+/**
+ * HttpSocketTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class HttpSocketTest extends CakeTestCase {
+
+/**
+ * Socket property
+ *
+ * @var mixed null
+ * @access public
+ */
+	var $Socket = null;
+
+/**
+ * RequestSocket property
+ *
+ * @var mixed null
+ * @access public
+ */
+	var $RequestSocket = null;
+
+/**
+ * This function sets up a TestHttpSocket instance we are going to use for testing
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		if (!class_exists('MockHttpSocket')) {
+			Mock::generatePartial('TestHttpSocket', 'MockHttpSocket', array('read', 'write', 'connect'));
+			Mock::generatePartial('TestHttpSocket', 'MockHttpSocketRequests', array('read', 'write', 'connect', 'request'));
+		}
+
+		$this->Socket =& new MockHttpSocket();
+		$this->RequestSocket =& new MockHttpSocketRequests();
+	}
+
+/**
+ * We use this function to clean up after the test case was executed
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		unset($this->Socket, $this->RequestSocket);
+	}
+
+/**
+ * Test that HttpSocket::__construct does what one would expect it to do
+ *
+ * @access public
+ * @return void
+ */
+	function testConstruct() {
+		$this->Socket->reset();
+		$baseConfig = $this->Socket->config;
+		$this->Socket->expectNever('connect');
+		$this->Socket->__construct(array('host' => 'foo-bar'));
+		$baseConfig['host']	 = 'foo-bar';
+		$baseConfig['protocol'] = getprotobyname($baseConfig['protocol']);
+		$this->assertIdentical($this->Socket->config, $baseConfig);
+		$this->Socket->reset();
+		$baseConfig = $this->Socket->config;
+		$this->Socket->__construct('http://www.cakephp.org:23/');
+		$baseConfig['host']	 = 'www.cakephp.org';
+		$baseConfig['request']['uri']['host'] = 'www.cakephp.org';
+		$baseConfig['port']	 = 23;
+		$baseConfig['request']['uri']['port'] = 23;
+		$baseConfig['protocol'] = getprotobyname($baseConfig['protocol']);
+		$this->assertIdentical($this->Socket->config, $baseConfig);
+
+		$this->Socket->reset();
+		$this->Socket->__construct(array('request' => array('uri' => 'http://www.cakephp.org:23/')));
+		$this->assertIdentical($this->Socket->config, $baseConfig);
+	}
+
+/**
+ * Test that HttpSocket::configUri works properly with different types of arguments
+ *
+ * @access public
+ * @return void
+ */
+	function testConfigUri() {
+		$this->Socket->reset();
+		$r = $this->Socket->configUri('https://bob:secre****@www*****:23/?query=foo');
+		$expected = array(
+			'persistent' => false,
+			'host' 		 => 'www.cakephp.org',
+			'protocol'   => 'tcp',
+			'port' 		 => 23,
+			'timeout' 	 =>	30,
+			'request' => array(
+				'uri' => array(
+					'scheme' => 'https'
+					, 'host' => 'www.cakephp.org'
+					, 'port' => 23
+				),
+				'auth' => array(
+					'method' => 'Basic'
+					, 'user' => 'bob'
+					, 'pass' => 'secret'
+				),
+				'cookies' => array(),
+			)
+		);
+		$this->assertIdentical($this->Socket->config, $expected);
+		$this->assertIdentical($r, $expected);
+		$r = $this->Socket->configUri(array('host' => 'www.foo-bar.org'));
+		$expected['host'] = 'www.foo-bar.org';
+		$expected['request']['uri']['host'] = 'www.foo-bar.org';
+		$this->assertIdentical($this->Socket->config, $expected);
+		$this->assertIdentical($r, $expected);
+
+		$r = $this->Socket->configUri('http://www.foo.com');
+		$expected = array(
+			'persistent' => false,
+			'host' 		 => 'www.foo.com',
+			'protocol'   => 'tcp',
+			'port' 		 => 80,
+			'timeout' 	 =>	30,
+			'request' => array(
+				'uri' => array(
+					'scheme' => 'http'
+					, 'host' => 'www.foo.com'
+					, 'port' => 80
+				),
+				'auth' => array(
+					'method' => 'Basic'
+					, 'user' => null
+					, 'pass' => null
+				),
+				'cookies' => array()
+			)
+		);
+		$this->assertIdentical($this->Socket->config, $expected);
+		$this->assertIdentical($r, $expected);
+		$r = $this->Socket->configUri('/this-is-broken');
+		$this->assertIdentical($this->Socket->config, $expected);
+		$this->assertIdentical($r, false);
+		$r = $this->Socket->configUri(false);
+		$this->assertIdentical($this->Socket->config, $expected);
+		$this->assertIdentical($r, false);
+	}
+
+/**
+ * Tests that HttpSocket::request (the heart of the HttpSocket) is working properly.
+ *
+ * @access public
+ * @return void
+ */
+	function testRequest() {
+		$this->Socket->reset();
+
+		$this->Socket->reset();
+		$response = $this->Socket->request(true);
+		$this->assertFalse($response);
+
+		$tests = array(
+			0 => array(
+				'request' => 'http://www.cakephp.org/?foo=bar'
+				, 'expectation' => array(
+					'config' => array(
+						'persistent' => false
+						, 'host' => 'www.cakephp.org'
+						, 'protocol' => 'tcp'
+						, 'port' => 80
+						, 'timeout' => 30
+						, 'request' => array(
+							'uri' => array (
+								'scheme' => 'http'
+								, 'host' => 'www.cakephp.org'
+								, 'port' => 80,
+							)
+							, 'auth' => array(
+								'method' => 'Basic'
+								,'user' => null
+								,'pass' => null
+							),
+							'cookies' => array(),
+						),
+					)
+					, 'request' => array(
+						'method' => 'GET'
+						, 'uri' => array(
+							'scheme' => 'http'
+							, 'host' => 'www.cakephp.org'
+							, 'port' => 80
+							, 'user' => null
+							, 'pass' => null
+							, 'path' => '/'
+							, 'query' => array('foo' => 'bar')
+							, 'fragment' => null
+						)
+						, 'auth' => array(
+							'method' => 'Basic'
+							, 'user' => null
+							, 'pass' => null
+						)
+						, 'version' => '1.1'
+						, 'body' => ''
+						, 'line' => "GET /?foo=bar HTTP/1.1\r\n"
+						, 'header' => "Host: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\n"
+						, 'raw' => ""
+						, 'cookies' => array(),
+					)
+				)
+			)
+			, 1 => array(
+				'request' => array(
+					'uri' => array(
+						'host' => 'www.cakephp.org'
+						, 'query' => '?foo=bar'
+					)
+				)
+			)
+			, 2 => array(
+				'request' => 'www.cakephp.org/?foo=bar'
+			)
+			, 3 => array(
+				'request' => array('host' => '192.168.0.1', 'uri' => 'http://www.cakephp.org/?foo=bar')
+				, 'expectation' => array(
+					'request' => array(
+						'uri' => array('host' => 'www.cakephp.org')
+					)
+					, 'config' => array(
+						'request' => array(
+							'uri' => array('host' => 'www.cakephp.org')
+						)
+						, 'host' => '192.168.0.1'
+					)
+				)
+			)
+			, 'reset4' => array(
+				'request.uri.query' => array()
+			)
+			, 4 => array(
+				'request' => array('header' => array('Foo @ woo' => 'bar-value'))
+				, 'expectation' => array(
+					'request' => array(
+						'header' => "Host: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\nFoo\"@\"woo: bar-value\r\n"
+						, 'line' => "GET / HTTP/1.1\r\n"
+					)
+				)
+			)
+			, 5 => array(
+				'request' => array('header' => array('Foo @ woo' => 'bar-value', 'host' => 'foo.com'), 'uri' => 'http://www.cakephp.org/')
+				, 'expectation' => array(
+					'request' => array(
+						'header' => "Host: foo.com\r\nConnection: close\r\nUser-Agent: CakePHP\r\nFoo\"@\"woo: bar-value\r\n"
+					)
+					, 'config' => array(
+						'host' => 'www.cakephp.org'
+					)
+				)
+			)
+			, 6 => array(
+				'request' => array('header' => "Foo: bar\r\n")
+				, 'expectation' => array(
+					'request' => array(
+						'header' => "Foo: bar\r\n"
+					)
+				)
+			)
+			, 7 => array(
+				'request' => array('header' => "Foo: bar\r\n", 'uri' => 'http://www.cakephp.org/search?q=http_socket#ignore-me')
+				, 'expectation' => array(
+					'request' => array(
+						'uri' => array(
+							'path' => '/search'
+							, 'query' => array('q' => 'http_socket')
+							, 'fragment' => 'ignore-me'
+						)
+						, 'line' => "GET /search?q=http_socket HTTP/1.1\r\n"
+					)
+				)
+			)
+			, 'reset8' => array(
+				'request.uri.query' => array()
+			)
+			, 8 => array(
+				'request' => array('method' => 'POST', 'uri' => 'http://www.cakephp.org/posts/add', 'body' => array('name' => 'HttpSocket-is-released', 'date' => 'today'))
+				, 'expectation' => array(
+					'request' => array(
+						'method' => 'POST'
+						, 'uri' => array(
+							'path' => '/posts/add'
+							, 'fragment' => null
+						)
+						, 'body' => "name=HttpSocket-is-released&date=today"
+						, 'line' => "POST /posts/add HTTP/1.1\r\n"
+						, 'header' => "Host: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 38\r\n"
+						, 'raw' => "name=HttpSocket-is-released&date=today"
+					)
+				)
+			)
+			, 9 => array(
+				'request' => array('method' => 'POST', 'uri' => 'http://www.cakephp.org:8080/posts/add', 'body' => array('name' => 'HttpSocket-is-released', 'date' => 'today'))
+				, 'expectation' => array(
+					'config' => array(
+						'port' => 8080
+						, 'request' => array(
+							'uri' => array(
+								'port' => 8080
+							)
+						)
+					)
+					, 'request' => array(
+						'uri' => array(
+							'port' => 8080
+						)
+						, 'header' => "Host: www.cakephp.org:8080\r\nConnection: close\r\nUser-Agent: CakePHP\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 38\r\n"
+					)
+				)
+			)
+			, 10 => array(
+				'request' => array('method' => 'POST', 'uri' => 'https://www.cakephp.org/posts/add', 'body' => array('name' => 'HttpSocket-is-released', 'date' => 'today'))
+				, 'expectation' => array(
+					'config' => array(
+						'port' => 443
+						, 'request' => array(
+							'uri' => array(
+								'scheme' => 'https'
+								, 'port' => 443
+							)
+						)
+					)
+					, 'request' => array(
+						'uri' => array(
+							'scheme' => 'https'
+							, 'port' => 443
+						)
+						, 'header' => "Host: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 38\r\n"
+					)
+				)
+			)
+			, 11 => array(
+				'request' => array(
+						'method' => 'POST',
+						'uri' => 'https://www.cakephp.org/posts/add',
+						'body' => array('name' => 'HttpSocket-is-released', 'date' => 'today'),
+						'cookies' => array('foo' => array('value' => 'bar'))
+				)
+				, 'expectation' => array(
+					'request' => array(
+						'header' => "Host: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 38\r\nCookie: foo=bar\r\n",
+						'cookies' => array(
+							'foo' => array('value' => 'bar'),
+						)
+					)
+				)
+			)
+		);
+
+		$expectation = array();
+		foreach ($tests as $i => $test) {
+			if (strpos($i, 'reset') === 0) {
+				foreach ($test as $path => $val) {
+					$expectation = Set::insert($expectation, $path, $val);
+				}
+				continue;
+			}
+
+			if (isset($test['expectation'])) {
+				$expectation = Set::merge($expectation, $test['expectation']);
+			}
+			$this->Socket->request($test['request']);
+
+			$raw = $expectation['request']['raw'];
+			$expectation['request']['raw'] = $expectation['request']['line'].$expectation['request']['header']."\r\n".$raw;
+
+			$r = array('config' => $this->Socket->config, 'request' => $this->Socket->request);
+			$v = $this->assertIdentical($r, $expectation, '%s in test #'.$i.' ');
+			$expectation['request']['raw'] = $raw;
+		}
+
+		$this->Socket->reset();
+		$request = array('method' => 'POST', 'uri' => 'http://www.cakephp.org/posts/add', 'body' => array('name' => 'HttpSocket-is-released', 'date' => 'today'));
+		$response = $this->Socket->request($request);
+		$this->assertIdentical($this->Socket->request['body'], "name=HttpSocket-is-released&date=today");
+
+		$request = array('uri' => '*', 'method' => 'GET');
+		$this->expectError(new PatternExpectation('/activate quirks mode/i'));
+		$response = $this->Socket->request($request);
+		$this->assertFalse($response);
+		$this->assertFalse($this->Socket->response);
+
+		$this->Socket->reset();
+		$request = array('uri' => 'htpp://www.cakephp.org/');
+		$this->Socket->setReturnValue('connect', true);
+		$this->Socket->setReturnValue('read', false);
+		$this->Socket->_mock->_call_counts['read'] = 0;
+		$number = mt_rand(0, 9999999);
+		$serverResponse = "HTTP/1.x 200 OK\r\nDate: Mon, 16 Apr 2007 04:14:16 GMT\r\nServer: CakeHttp Server\r\nContent-Type: text/html\r\n\r\n<h1>Hello, your lucky number is " . $number . "</h1>";
+		$this->Socket->setReturnValueAt(0, 'read', $serverResponse);
+		$this->Socket->expect('write', array("GET / HTTP/1.1\r\nHost: www.cakephp.org\r\nConnection: close\r\nUser-Agent: CakePHP\r\n\r\n"));
+		$this->Socket->expectCallCount('read', 2);
+		$response = $this->Socket->request($request);
+		$this->assertIdentical($response, "<h1>Hello, your lucky number is " . $number . "</h1>");
+
+		$this->Socket->reset();
+		$serverResponse = "HTTP/1.x 200 OK\r\nSet-Cookie: foo=bar\r\nDate: Mon, 16 Apr 2007 04:14:16 GMT\r\nServer: CakeHttp Server\r\nContent-Type: text/html\r\n\r\n<h1>This is a cookie test!</h1>";
+		unset($this->Socket->_mock->_actions->_at['read']);
+		unset($this->Socket->_mock->_return_sequence['read']);
+		$this->Socket->_mock->_call_counts['read'] = 0;
+		$this->Socket->setReturnValueAt(0, 'read', $serverResponse);
+
+		$this->Socket->connected = true;
+		$this->Socket->request($request);
+		$result = $this->Socket->response['cookies'];
+		$expect = array(
+			'foo' => array(
+				'value' => 'bar'
+			)
+		);
+		$this->assertEqual($result, $expect);
+		$this->assertEqual($this->Socket->config['request']['cookies'], $expect);
+		$this->assertFalse($this->Socket->connected);
+	}
+
+/**
+ * testUrl method
+ *
+ * @access public
+ * @return void
+ */
+	function testUrl() {
+		$this->Socket->reset(true);
+
+		$this->assertIdentical($this->Socket->url(true), false);
+
+		$url = $this->Socket->url('www.cakephp.org');
+		$this->assertIdentical($url, 'http://www.cakephp.org/');
+
+		$url = $this->Socket->url('https://www.cakephp.org/posts/add');
+		$this->assertIdentical($url, 'https://www.cakephp.org/posts/add');
+		$url = $this->Socket->url('http://www.cakephp/search?q=socket', '/%path?%query');
+		$this->assertIdentical($url, '/search?q=socket');
+
+		$this->Socket->config['request']['uri']['host'] = 'bakery.cakephp.org';
+		$url = $this->Socket->url();
+		$this->assertIdentical($url, 'http://bakery.cakephp.org/');
+
+		$this->Socket->configUri('http://www.cakephp.org');
+		$url = $this->Socket->url('/search?q=bar');
+		$this->assertIdentical($url, 'http://www.cakephp.org/search?q=bar');
+
+		$url = $this->Socket->url(array('host' => 'www.foobar.org', 'query' => array('q' => 'bar')));
+		$this->assertIdentical($url, 'http://www.foobar.org/?q=bar');
+
+		$url = $this->Socket->url(array('path' => '/supersearch', 'query' => array('q' => 'bar')));
+		$this->assertIdentical($url, 'http://www.cakephp.org/supersearch?q=bar');
+
+		$this->Socket->configUri('http://www.google.com');
+		$url = $this->Socket->url('/search?q=socket');
+		$this->assertIdentical($url, 'http://www.google.com/search?q=socket');
+
+		$url = $this->Socket->url();
+		$this->assertIdentical($url, 'http://www.google.com/');
+
+		$this->Socket->configUri('https://www.google.com');
+		$url = $this->Socket->url('/search?q=socket');
+		$this->assertIdentical($url, 'https://www.google.com/search?q=socket');
+
+		$this->Socket->reset();
+		$this->Socket->configUri('www.google.com:443');
+		$url = $this->Socket->url('/search?q=socket');
+		$this->assertIdentical($url, 'https://www.google.com/search?q=socket');
+
+		$this->Socket->reset();
+		$this->Socket->configUri('www.google.com:8080');
+		$url = $this->Socket->url('/search?q=socket');
+		$this->assertIdentical($url, 'http://www.google.com:8080/search?q=socket');
+	}
+
+/**
+ * testGet method
+ *
+ * @access public
+ * @return void
+ */
+	function testGet() {
+		$this->RequestSocket->reset();
+
+		$this->RequestSocket->expect('request', a(array('method' => 'GET', 'uri' => 'http://www.google.com/')));
+		$this->RequestSocket->get('http://www.google.com/');
+
+		$this->RequestSocket->expect('request', a(array('method' => 'GET', 'uri' => 'http://www.google.com/?foo=bar')));
+		$this->RequestSocket->get('http://www.google.com/', array('foo' => 'bar'));
+
+		$this->RequestSocket->expect('request', a(array('method' => 'GET', 'uri' => 'http://www.google.com/?foo=bar')));
+		$this->RequestSocket->get('http://www.google.com/', 'foo=bar');
+
+		$this->RequestSocket->expect('request', a(array('method' => 'GET', 'uri' => 'http://www.google.com/?foo=23&foobar=42')));
+		$this->RequestSocket->get('http://www.google.com/?foo=bar', array('foobar' => '42', 'foo' => '23'));
+
+		$this->RequestSocket->expect('request', a(array('method' => 'GET', 'uri' => 'http://www.google.com/', 'auth' => array('user' => 'foo', 'pass' => 'bar'))));
+		$this->RequestSocket->get('http://www.google.com/', null, array('auth' => array('user' => 'foo', 'pass' => 'bar')));
+	}
+
+/**
+ * test that two consecutive get() calls reset the authentication credentials.
+ *
+ * @return void
+ */
+	function testConsecutiveGetResetsAuthCredentials() {
+		$socket = new MockHttpSocket();
+		$socket->config['request']['auth'] = array(
+			'method' => 'Basic',
+			'user' => 'mark',
+			'pass' => 'secret'
+		);
+		$socket->get('http://mark:secre****@examp*****/test');
+		$this->assertEqual($socket->request['uri']['user'], 'mark');
+		$this->assertEqual($socket->request['uri']['pass'], 'secret');
+
+		$socket->get('/test2');
+		$this->assertEqual($socket->request['auth']['user'], 'mark');
+		$this->assertEqual($socket->request['auth']['pass'], 'secret');
+
+		$socket->get('/test3');
+		$this->assertEqual($socket->request['auth']['user'], 'mark');
+		$this->assertEqual($socket->request['auth']['pass'], 'secret');
+	}
+
+/**
+ * testPostPutDelete method
+ *
+ * @access public
+ * @return void
+ */
+	function testPostPutDelete() {
+		$this->RequestSocket->reset();
+
+		foreach (array('POST', 'PUT', 'DELETE') as $method) {
+			$this->RequestSocket->expect('request', a(array('method' => $method, 'uri' => 'http://www.google.com/', 'body' => array())));
+			$this->RequestSocket->{low($method)}('http://www.google.com/');
+
+			$this->RequestSocket->expect('request', a(array('method' => $method, 'uri' => 'http://www.google.com/', 'body' => array('Foo' => 'bar'))));
+			$this->RequestSocket->{low($method)}('http://www.google.com/', array('Foo' => 'bar'));
+
+			$this->RequestSocket->expect('request', a(array('method' => $method, 'uri' => 'http://www.google.com/', 'body' => null, 'line' => 'Hey Server')));
+			$this->RequestSocket->{low($method)}('http://www.google.com/', null, array('line' => 'Hey Server'));
+		}
+	}
+
+/**
+ * testParseResponse method
+ *
+ * @access public
+ * @return void
+ */
+	function testParseResponse() {
+		$this->Socket->reset();
+
+		$r = $this->Socket->parseResponse(array('foo' => 'bar'));
+		$this->assertIdentical($r, array('foo' => 'bar'));
+
+		$r = $this->Socket->parseResponse(true);
+		$this->assertIdentical($r, false);
+
+		$r = $this->Socket->parseResponse("HTTP Foo\r\nBar: La");
+		$this->assertIdentical($r, false);
+
+		$tests = array(
+			'simple-request' => array(
+				'response' => array(
+					'status-line' => "HTTP/1.x 200 OK\r\n",
+					'header' => "Date: Mon, 16 Apr 2007 04:14:16 GMT\r\nServer: CakeHttp Server\r\n",
+					'body' => "<h1>Hello World</h1>\r\n<p>It's good to be html</p>"
+				)
+				, 'expectations' => array(
+					'status.http-version' => 'HTTP/1.x',
+					'status.code' => 200,
+					'status.reason-phrase' => 'OK',
+					'header' => $this->Socket->parseHeader("Date: Mon, 16 Apr 2007 04:14:16 GMT\r\nServer: CakeHttp Server\r\n"),
+					'body' => "<h1>Hello World</h1>\r\n<p>It's good to be html</p>"
+				)
+			),
+			'no-header' => array(
+				'response' => array(
+					'status-line' => "HTTP/1.x 404 OK\r\n",
+					'header' => null,
+				)
+				, 'expectations' => array(
+					'status.code' => 404,
+					'header' => array()
+				)
+			),
+			'chunked' => array(
+				'response' => array(
+					'header' => "Transfer-Encoding: chunked\r\n",
+					'body' => "19\r\nThis is a chunked message\r\n0\r\n"
+				),
+				'expectations' => array(
+					'body' => "This is a chunked message",
+					'header' => $this->Socket->parseHeader("Transfer-Encoding: chunked\r\n")
+				)
+			),
+			'enitity-header' => array(
+				'response' => array(
+					'body' => "19\r\nThis is a chunked message\r\n0\r\nFoo: Bar\r\n"
+				),
+				'expectations' => array(
+					'header' => $this->Socket->parseHeader("Transfer-Encoding: chunked\r\nFoo: Bar\r\n")
+				)
+			),
+			'enitity-header-combine' => array(
+				'response' => array(
+					'header' => "Transfer-Encoding: chunked\r\nFoo: Foobar\r\n"
+				),
+				'expectations' => array(
+					'header' => $this->Socket->parseHeader("Transfer-Encoding: chunked\r\nFoo: Foobar\r\nFoo: Bar\r\n")
+				)
+			)
+		);
+
+		$testResponse = array();
+		$expectations = array();
+
+		foreach ($tests as $name => $test) {
+
+			$testResponse = array_merge($testResponse, $test['response']);
+			$testResponse['response'] = $testResponse['status-line'].$testResponse['header']."\r\n".$testResponse['body'];
+			$r = $this->Socket->parseResponse($testResponse['response']);
+			$expectations = array_merge($expectations, $test['expectations']);
+
+			foreach ($expectations as $property => $expectedVal) {
+				$val = Set::extract($r, $property);
+				$this->assertIdentical($val, $expectedVal, 'Test "'.$name.'": response.'.$property.' - %s');
+			}
+
+			foreach (array('status-line', 'header', 'body', 'response') as $field) {
+				$this->assertIdentical($r['raw'][$field], $testResponse[$field], 'Test response.raw.'.$field.': %s');
+			}
+		}
+	}
+
+/**
+ * testDecodeBody method
+ *
+ * @access public
+ * @return void
+ */
+	function testDecodeBody() {
+		$this->Socket->reset();
+
+		$r = $this->Socket->decodeBody(true);
+		$this->assertIdentical($r, false);
+
+		$r = $this->Socket->decodeBody('Foobar', false);
+		$this->assertIdentical($r, array('body' => 'Foobar', 'header' => false));
+
+		$encodings = array(
+			'chunked' => array(
+				'encoded' => "19\r\nThis is a chunked message\r\n0\r\n",
+				'decoded' => array('body' => "This is a chunked message", 'header' => false)
+			),
+			'foo-coded' => array(
+				'encoded' => '!Foobar!',
+				'decoded' => array('body' => '!Foobar!', 'header' => false),
+				'error' => new PatternExpectation('/unknown encoding: foo-coded/i')
+			)
+		);
+
+		foreach ($encodings as $encoding => $sample) {
+			if (isset($sample['error'])) {
+				$this->expectError($sample['error']);
+			}
+
+			$r = $this->Socket->decodeBody($sample['encoded'], $encoding);
+			$this->assertIdentical($r, $sample['decoded']);
+
+			if (isset($sample['error'])) {
+				$this->Socket->quirksMode = true;
+				$r = $this->Socket->decodeBody($sample['encoded'], $encoding);
+				$this->assertIdentical($r, $sample['decoded']);
+				$this->Socket->quirksMode = false;
+			}
+		}
+	}
+
+/**
+ * testDecodeChunkedBody method
+ *
+ * @access public
+ * @return void
+ */
+	function testDecodeChunkedBody() {
+		$this->Socket->reset();
+
+		$r = $this->Socket->decodeChunkedBody(true);
+		$this->assertIdentical($r, false);
+
+		$encoded = "19\r\nThis is a chunked message\r\n0\r\n";
+		$decoded = "This is a chunked message";
+		$r = $this->Socket->decodeChunkedBody($encoded);
+		$this->assertIdentical($r['body'], $decoded);
+		$this->assertIdentical($r['header'], false);
+
+		$encoded = "19 \r\nThis is a chunked message\r\n0\r\n";
+		$r = $this->Socket->decodeChunkedBody($encoded);
+		$this->assertIdentical($r['body'], $decoded);
+
+		$encoded = "19\r\nThis is a chunked message\r\nE\r\n\nThat is cool\n\r\n0\r\n";
+		$decoded = "This is a chunked message\nThat is cool\n";
+		$r = $this->Socket->decodeChunkedBody($encoded);
+		$this->assertIdentical($r['body'], $decoded);
+		$this->assertIdentical($r['header'], false);
+
+		$encoded = "19\r\nThis is a chunked message\r\nE;foo-chunk=5\r\n\nThat is cool\n\r\n0\r\n";
+		$r = $this->Socket->decodeChunkedBody($encoded);
+		$this->assertIdentical($r['body'], $decoded);
+		$this->assertIdentical($r['header'], false);
+
+		$encoded = "19\r\nThis is a chunked message\r\nE\r\n\nThat is cool\n\r\n0\r\nfoo-header: bar\r\ncake: PHP\r\n\r\n";
+		$r = $this->Socket->decodeChunkedBody($encoded);
+		$this->assertIdentical($r['body'], $decoded);
+		$this->assertIdentical($r['header'], array('Foo-Header' => 'bar', 'Cake' => 'PHP'));
+
+		$encoded = "19\r\nThis is a chunked message\r\nE\r\n\nThat is cool\n\r\n";
+		$this->expectError(new PatternExpectation('/activate quirks mode/i'));
+		$r = $this->Socket->decodeChunkedBody($encoded);
+		$this->assertIdentical($r, false);
+
+		$this->Socket->quirksMode = true;
+		$r = $this->Socket->decodeChunkedBody($encoded);
+		$this->assertIdentical($r['body'], $decoded);
+		$this->assertIdentical($r['header'], false);
+
+		$encoded = "19\r\nThis is a chunked message\r\nE\r\n\nThat is cool\n\r\nfoo-header: bar\r\ncake: PHP\r\n\r\n";
+		$r = $this->Socket->decodeChunkedBody($encoded);
+		$this->assertIdentical($r['body'], $decoded);
+		$this->assertIdentical($r['header'], array('Foo-Header' => 'bar', 'Cake' => 'PHP'));
+	}
+
+/**
+ * testBuildRequestLine method
+ *
+ * @access public
+ * @return void
+ */
+	function testBuildRequestLine() {
+		$this->Socket->reset();
+
+		$this->expectError(new PatternExpectation('/activate quirks mode/i'));
+		$r = $this->Socket->buildRequestLine('Foo');
+		$this->assertIdentical($r, false);
+
+		$this->Socket->quirksMode = true;
+		$r = $this->Socket->buildRequestLine('Foo');
+		$this->assertIdentical($r, 'Foo');
+		$this->Socket->quirksMode = false;
+
+		$r = $this->Socket->buildRequestLine(true);
+		$this->assertIdentical($r, false);
+
+		$r = $this->Socket->buildRequestLine(array('foo' => 'bar', 'method' => 'foo'));
+		$this->assertIdentical($r, false);
+
+		$r = $this->Socket->buildRequestLine(array('method' => 'GET', 'uri' => 'http://www.cakephp.org/search?q=socket'));
+		$this->assertIdentical($r, "GET /search?q=socket HTTP/1.1\r\n");
+
+		$request = array(
+			'method' => 'GET',
+			'uri' => array(
+				'path' => '/search',
+				'query' => array('q' => 'socket')
+			)
+		);
+		$r = $this->Socket->buildRequestLine($request);
+		$this->assertIdentical($r, "GET /search?q=socket HTTP/1.1\r\n");
+
+		unset($request['method']);
+		$r = $this->Socket->buildRequestLine($request);
+		$this->assertIdentical($r, "GET /search?q=socket HTTP/1.1\r\n");
+
+		$r = $this->Socket->buildRequestLine($request, 'CAKE-HTTP/0.1');
+		$this->assertIdentical($r, "GET /search?q=socket CAKE-HTTP/0.1\r\n");
+
+		$request = array('method' => 'OPTIONS', 'uri' => '*');
+		$r = $this->Socket->buildRequestLine($request);
+		$this->assertIdentical($r, "OPTIONS * HTTP/1.1\r\n");
+
+		$request['method'] = 'GET';
+		$this->expectError(new PatternExpectation('/activate quirks mode/i'));
+		$r = $this->Socket->buildRequestLine($request);
+		$this->assertIdentical($r, false);
+
+		$this->expectError(new PatternExpectation('/activate quirks mode/i'));
+		$r = $this->Socket->buildRequestLine("GET * HTTP/1.1\r\n");
+		$this->assertIdentical($r, false);
+
+		$this->Socket->quirksMode = true;
+		$r = $this->Socket->buildRequestLine($request);
+		$this->assertIdentical($r,  "GET * HTTP/1.1\r\n");
+
+		$r = $this->Socket->buildRequestLine("GET * HTTP/1.1\r\n");
+		$this->assertIdentical($r, "GET * HTTP/1.1\r\n");
+	}
+
+/**
+ * Asserts that HttpSocket::parseUri is working properly
+ *
+ * @access public
+ * @return void
+ */
+	function testParseUri() {
+		$this->Socket->reset();
+
+		$uri = $this->Socket->parseUri(array('invalid' => 'uri-string'));
+		$this->assertIdentical($uri, false);
+
+		$uri = $this->Socket->parseUri(array('invalid' => 'uri-string'), array('host' => 'somehost'));
+		$this->assertIdentical($uri, array('host' => 'somehost', 'invalid' => 'uri-string'));
+
+		$uri = $this->Socket->parseUri(false);
+		$this->assertIdentical($uri, false);
+
+		$uri = $this->Socket->parseUri('/my-cool-path');
+		$this->assertIdentical($uri, array('path' => '/my-cool-path'));
+
+		$uri = $this->Socket->parseUri('http://bob:foo12****@www*****:40/search?q=dessert#results');
+		$this->assertIdentical($uri, array(
+			'scheme' => 'http',
+			'host' => 'www.cakephp.org',
+			'port' => 40,
+			'user' => 'bob',
+			'pass' => 'foo123',
+			'path' => '/search',
+			'query' => array('q' => 'dessert'),
+			'fragment' => 'results'
+		));
+
+		$uri = $this->Socket->parseUri('http://www.cakephp.org/');
+		$this->assertIdentical($uri, array(
+			'scheme' => 'http',
+			'host' => 'www.cakephp.org',
+			'path' => '/',
+		));
+
+		$uri = $this->Socket->parseUri('http://www.cakephp.org', true);
+		$this->assertIdentical($uri, array(
+			'scheme' => 'http',
+			'host' => 'www.cakephp.org',
+			'port' => 80,
+			'user' => null,
+			'pass' => null,
+			'path' => '/',
+			'query' => array(),
+			'fragment' => null
+		));
+
+		$uri = $this->Socket->parseUri('https://www.cakephp.org', true);
+		$this->assertIdentical($uri, array(
+			'scheme' => 'https',
+			'host' => 'www.cakephp.org',
+			'port' => 443,
+			'user' => null,
+			'pass' => null,
+			'path' => '/',
+			'query' => array(),
+			'fragment' => null
+		));
+
+		$uri = $this->Socket->parseUri('www.cakephp.org:443/query?foo', true);
+		$this->assertIdentical($uri, array(
+			'scheme' => 'https',
+			'host' => 'www.cakephp.org',
+			'port' => 443,
+			'user' => null,
+			'pass' => null,
+			'path' => '/query',
+			'query' => array('foo' => ""),
+			'fragment' => null
+		));
+
+		$uri = $this->Socket->parseUri('http://www.cakephp.org', array('host' => 'piephp.org', 'user' => 'bob', 'fragment' => 'results'));
+		$this->assertIdentical($uri, array(
+			'host' => 'www.cakephp.org',
+			'user' => 'bob',
+			'fragment' => 'results',
+			'scheme' => 'http'
+		));
+
+		$uri = $this->Socket->parseUri('https://www.cakephp.org', array('scheme' => 'http', 'port' => 23));
+		$this->assertIdentical($uri, array(
+			'scheme' => 'https',
+			'port' => 23,
+			'host' => 'www.cakephp.org'
+		));
+
+		$uri = $this->Socket->parseUri('www.cakephp.org:59', array('scheme' => array('http', 'https'), 'port' => 80));
+		$this->assertIdentical($uri, array(
+			'scheme' => 'http',
+			'port' => 59,
+			'host' => 'www.cakephp.org'
+		));
+
+		$uri = $this->Socket->parseUri(array('scheme' => 'http', 'host' => 'www.google.com', 'port' => 8080), array('scheme' => array('http', 'https'), 'host' => 'www.google.com', 'port' => array(80, 443)));
+		$this->assertIdentical($uri, array(
+			'scheme' => 'http',
+			'host' => 'www.google.com',
+			'port' => 8080,
+		));
+
+		$uri = $this->Socket->parseUri('http://www.cakephp.org/?param1=value1&param2=value2%3Dvalue3');
+		$this->assertIdentical($uri, array(
+			'scheme' => 'http',
+			'host' => 'www.cakephp.org',
+			'path' => '/',
+			'query' => array(
+				'param1' => 'value1',
+				'param2' => 'value2=value3'
+			)
+		));
+
+		$uri = $this->Socket->parseUri('http://www.cakephp.org/?param1=value1&param2=value2=value3');
+		$this->assertIdentical($uri, array(
+			'scheme' => 'http',
+			'host' => 'www.cakephp.org',
+			'path' => '/',
+			'query' => array(
+				'param1' => 'value1',
+				'param2' => 'value2=value3'
+			)
+		));
+	}
+
+/**
+ * Tests that HttpSocket::buildUri can turn all kinds of uri arrays (and strings) into fully or partially qualified URI's
+ *
+ * @access public
+ * @return void
+ */
+	function testBuildUri() {
+		$this->Socket->reset();
+
+		$r = $this->Socket->buildUri(true);
+		$this->assertIdentical($r, false);
+
+		$r = $this->Socket->buildUri('foo.com');
+		$this->assertIdentical($r, 'http://foo.com/');
+
+		$r = $this->Socket->buildUri(array('host' => 'www.cakephp.org'));
+		$this->assertIdentical($r, 'http://www.cakephp.org/');
+
+		$r = $this->Socket->buildUri(array('host' => 'www.cakephp.org', 'scheme' => 'https'));
+		$this->assertIdentical($r, 'https://www.cakephp.org/');
+
+		$r = $this->Socket->buildUri(array('host' => 'www.cakephp.org', 'port' => 23));
+		$this->assertIdentical($r, 'http://www.cakephp.org:23/');
+
+		$r = $this->Socket->buildUri(array('path' => 'www.google.com/search', 'query' => 'q=cakephp'));
+		$this->assertIdentical($r, 'http://www.google.com/search?q=cakephp');
+
+		$r = $this->Socket->buildUri(array('host' => 'www.cakephp.org', 'scheme' => 'https', 'port' => 79));
+		$this->assertIdentical($r, 'https://www.cakephp.org:79/');
+
+		$r = $this->Socket->buildUri(array('host' => 'www.cakephp.org', 'path' => 'foo'));
+		$this->assertIdentical($r, 'http://www.cakephp.org/foo');
+
+		$r = $this->Socket->buildUri(array('host' => 'www.cakephp.org', 'path' => '/foo'));
+		$this->assertIdentical($r, 'http://www.cakephp.org/foo');
+
+		$r = $this->Socket->buildUri(array('host' => 'www.cakephp.org', 'path' => '/search', 'query' => array('q' => 'HttpSocket')));
+		$this->assertIdentical($r, 'http://www.cakephp.org/search?q=HttpSocket');
+
+		$r = $this->Socket->buildUri(array('host' => 'www.cakephp.org', 'fragment' => 'bar'));
+		$this->assertIdentical($r, 'http://www.cakephp.org/#bar');
+
+		$r = $this->Socket->buildUri(array(
+			'scheme' => 'https',
+			'host' => 'www.cakephp.org',
+			'port' => 25,
+			'user' => 'bob',
+			'pass' => 'secret',
+			'path' => '/cool',
+			'query' => array('foo' => 'bar'),
+			'fragment' => 'comment'
+		));
+		$this->assertIdentical($r, 'https://bob:secre****@www*****:25/cool?foo=bar#comment');
+
+		$r = $this->Socket->buildUri(array('host' => 'www.cakephp.org', 'fragment' => 'bar'), '%fragment?%host');
+		$this->assertIdentical($r, 'bar?www.cakephp.org');
+
+		$r = $this->Socket->buildUri(array('host' => 'www.cakephp.org'), '%fragment???%host');
+		$this->assertIdentical($r, '???www.cakephp.org');
+
+		$r = $this->Socket->buildUri(array('path' => '*'), '/%path?%query');
+		$this->assertIdentical($r, '*');
+
+		$r = $this->Socket->buildUri(array('scheme' => 'foo', 'host' => 'www.cakephp.org'));
+		$this->assertIdentical($r, 'foo://www.cakephp.org:80/');
+	}
+
+/**
+ * Asserts that HttpSocket::parseQuery is working properly
+ *
+ * @access public
+ * @return void
+ */
+	function testParseQuery() {
+		$this->Socket->reset();
+
+		$query = $this->Socket->parseQuery(array('framework' => 'cakephp'));
+		$this->assertIdentical($query, array('framework' => 'cakephp'));
+
+		$query = $this->Socket->parseQuery('');
+		$this->assertIdentical($query, array());
+
+		$query = $this->Socket->parseQuery('framework=cakephp');
+		$this->assertIdentical($query, array('framework' => 'cakephp'));
+
+		$query = $this->Socket->parseQuery('?framework=cakephp');
+		$this->assertIdentical($query, array('framework' => 'cakephp'));
+
+		$query = $this->Socket->parseQuery('a&b&c');
+		$this->assertIdentical($query, array('a' => '', 'b' => '', 'c' => ''));
+
+		$query = $this->Socket->parseQuery('value=12345');
+		$this->assertIdentical($query, array('value' => '12345'));
+
+		$query = $this->Socket->parseQuery('a[0]=foo&a[1]=bar&a[2]=cake');
+		$this->assertIdentical($query, array('a' => array(0 => 'foo', 1 => 'bar', 2 => 'cake')));
+
+		$query = $this->Socket->parseQuery('a[]=foo&a[]=bar&a[]=cake');
+		$this->assertIdentical($query, array('a' => array(0 => 'foo', 1 => 'bar', 2 => 'cake')));
+
+		$query = $this->Socket->parseQuery('a]][[=foo&[]=bar&]]][]=cake');
+		$this->assertIdentical($query, array('a]][[' => 'foo', 0 => 'bar', ']]]' => array('cake')));
+
+		$query = $this->Socket->parseQuery('a[][]=foo&a[][]=bar&a[][]=cake');
+		$expectedQuery = array(
+			'a' => array(
+				0 => array(
+					0 => 'foo'
+				),
+				1 => array(
+					0 => 'bar'
+				),
+				array(
+					0 => 'cake'
+				)
+			)
+		);
+		$this->assertIdentical($query, $expectedQuery);
+
+		$query = $this->Socket->parseQuery('a[][]=foo&a[bar]=php&a[][]=bar&a[][]=cake');
+		$expectedQuery = array(
+			'a' => array(
+				0 => array(
+					0 => 'foo'
+				),
+				'bar' => 'php',
+				1 => array(
+					0 => 'bar'
+				),
+				array(
+					0 => 'cake'
+				)
+			)
+		);
+		$this->assertIdentical($query, $expectedQuery);
+
+		$query = $this->Socket->parseQuery('user[]=jim&user[3]=tom&user[]=bob');
+		$expectedQuery = array(
+			'user' => array(
+				0 => 'jim',
+				3 => 'tom',
+				4 => 'bob'
+			)
+		);
+		$this->assertIdentical($query, $expectedQuery);
+
+		$queryStr = 'user[0]=foo&user[0][items][]=foo&user[0][items][]=bar&user[][name]=jim&user[1][items][personal][]=book&user[1][items][personal][]=pen&user[1][items][]=ball&user[count]=2&empty';
+		$query = $this->Socket->parseQuery($queryStr);
+		$expectedQuery = array(
+			'user' => array(
+				0 => array(
+					'items' => array(
+						'foo',
+						'bar'
+					)
+				),
+				1 => array(
+					'name' => 'jim',
+					'items' => array(
+						'personal' => array(
+							'book'
+							, 'pen'
+						),
+						'ball'
+					)
+				),
+				'count' => '2'
+			),
+			'empty' => ''
+		);
+		$this->assertIdentical($query, $expectedQuery);
+	}
+
+/**
+ * Tests that HttpSocket::buildHeader can turn a given $header array into a proper header string according to
+ * HTTP 1.1 specs.
+ *
+ * @access public
+ * @return void
+ */
+	function testBuildHeader() {
+		$this->Socket->reset();
+
+		$r = $this->Socket->buildHeader(true);
+		$this->assertIdentical($r, false);
+
+		$r = $this->Socket->buildHeader('My raw header');
+		$this->assertIdentical($r, 'My raw header');
+
+		$r = $this->Socket->buildHeader(array('Host' => 'www.cakephp.org'));
+		$this->assertIdentical($r, "Host: www.cakephp.org\r\n");
+
+		$r = $this->Socket->buildHeader(array('Host' => 'www.cakephp.org', 'Connection' => 'Close'));
+		$this->assertIdentical($r, "Host: www.cakephp.org\r\nConnection: Close\r\n");
+
+		$r = $this->Socket->buildHeader(array('People' => array('Bob', 'Jim', 'John')));
+		$this->assertIdentical($r, "People: Bob,Jim,John\r\n");
+
+		$r = $this->Socket->buildHeader(array('Multi-Line-Field' => "This is my\r\nMulti Line field"));
+		$this->assertIdentical($r, "Multi-Line-Field: This is my\r\n Multi Line field\r\n");
+
+		$r = $this->Socket->buildHeader(array('Multi-Line-Field' => "This is my\r\n Multi Line field"));
+		$this->assertIdentical($r, "Multi-Line-Field: This is my\r\n Multi Line field\r\n");
+
+		$r = $this->Socket->buildHeader(array('Multi-Line-Field' => "This is my\r\n\tMulti Line field"));
+		$this->assertIdentical($r, "Multi-Line-Field: This is my\r\n\tMulti Line field\r\n");
+
+		$r = $this->Socket->buildHeader(array('Test @ Field' => "My value"));
+		$this->assertIdentical($r, "Test\"@\"Field: My value\r\n");
+
+	}
+
+/**
+ * Test that HttpSocket::parseHeader can take apart a given (and valid) $header string and turn it into an array.
+ *
+ * @access public
+ * @return void
+ */
+	function testParseHeader() {
+		$this->Socket->reset();
+
+		$r = $this->Socket->parseHeader(array('foo' => 'Bar', 'fOO-bAr' => 'quux'));
+		$this->assertIdentical($r, array('Foo' => 'Bar', 'Foo-Bar' => 'quux'));
+
+		$r = $this->Socket->parseHeader(true);
+		$this->assertIdentical($r, false);
+
+		$header = "Host: cakephp.org\t\r\n";
+		$r = $this->Socket->parseHeader($header);
+		$expected = array(
+			'Host' => 'cakephp.org'
+		);
+		$this->assertIdentical($r, $expected);
+
+		$header = "Date:Sat, 07 Apr 2007 10:10:25 GMT\r\nX-Powered-By: PHP/5.1.2\r\n";
+		$r = $this->Socket->parseHeader($header);
+		$expected = array(
+			'Date' => 'Sat, 07 Apr 2007 10:10:25 GMT'
+			, 'X-Powered-By' =>  'PHP/5.1.2'
+		);
+		$this->assertIdentical($r, $expected);
+
+		$header = "people: Jim,John\r\nfoo-LAND: Bar\r\ncAKe-PHP: rocks\r\n";
+		$r = $this->Socket->parseHeader($header);
+		$expected = array(
+			'People' => 'Jim,John'
+			, 'Foo-Land' => 'Bar'
+			, 'Cake-Php' =>  'rocks'
+		);
+		$this->assertIdentical($r, $expected);
+
+		$header = "People: Jim,John,Tim\r\nPeople: Lisa,Tina,Chelsea\r\n";
+		$r = $this->Socket->parseHeader($header);
+		$expected = array(
+			'People' =>  array('Jim,John,Tim', 'Lisa,Tina,Chelsea')
+		);
+		$this->assertIdentical($r, $expected);
+
+		$header = "Multi-Line: I am a \r\nmulti line\t\r\nfield value.\r\nSingle-Line: I am not\r\n";
+		$r = $this->Socket->parseHeader($header);
+		$expected = array(
+			'Multi-Line' => "I am a\r\nmulti line\r\nfield value."
+			, 'Single-Line' => 'I am not'
+		);
+		$this->assertIdentical($r, $expected);
+
+		$header = "Esc\"@\"ped: value\r\n";
+		$r = $this->Socket->parseHeader($header);
+		$expected = array(
+			'Esc @ ped' => 'value'
+		);
+		$this->assertIdentical($r, $expected);
+	}
+
+/**
+ * testParseCookies method
+ *
+ * @access public
+ * @return void
+ */
+	function testParseCookies() {
+		$header = array(
+			'Set-Cookie' => array(
+				'foo=bar',
+				'people=jim,jack,johnny";";Path=/accounts',
+				'google=not=nice'
+			),
+			'Transfer-Encoding' => 'chunked',
+			'Date' => 'Sun, 18 Nov 2007 18:57:42 GMT',
+		);
+		$cookies = $this->Socket->parseCookies($header);
+		$expected = array(
+			'foo' => array(
+				'value' => 'bar'
+			),
+			'people' => array(
+				'value' => 'jim,jack,johnny";"',
+				'path' => '/accounts',
+			),
+			'google' => array(
+				'value' => 'not=nice',
+			)
+		);
+		$this->assertEqual($cookies, $expected);
+
+		$header['Set-Cookie'][] = 'cakephp=great; Secure';
+		$expected['cakephp'] = array('value' => 'great', 'secure' => true);
+		$cookies = $this->Socket->parseCookies($header);
+		$this->assertEqual($cookies, $expected);
+
+		$header['Set-Cookie'] = 'foo=bar';
+		unset($expected['people'], $expected['cakephp'], $expected['google']);
+		$cookies = $this->Socket->parseCookies($header);
+		$this->assertEqual($cookies, $expected);
+	}
+
+/**
+ * testBuildCookies method
+ *
+ * @return void
+ * @access public
+ * @todo Test more scenarios
+ */
+	function testBuildCookies() {
+		$cookies = array(
+			'foo' => array(
+				'value' => 'bar'
+			),
+			'people' => array(
+				'value' => 'jim,jack,johnny;',
+				'path' => '/accounts'
+			)
+		);
+		$expect = "Cookie: foo=bar; people=jim,jack,johnny\";\"\r\n";
+		$result = $this->Socket->buildCookies($cookies);
+		$this->assertEqual($result, $expect);
+	}
+
+/**
+ * Tests that HttpSocket::_tokenEscapeChars() returns the right characters.
+ *
+ * @access public
+ * @return void
+ */
+	function testTokenEscapeChars() {
+		$this->Socket->reset();
+
+		$expected = array(
+			'\x22','\x28','\x29','\x3c','\x3e','\x40','\x2c','\x3b','\x3a','\x5c','\x2f','\x5b','\x5d','\x3f','\x3d','\x7b',
+			'\x7d','\x20','\x00','\x01','\x02','\x03','\x04','\x05','\x06','\x07','\x08','\x09','\x0a','\x0b','\x0c','\x0d',
+			'\x0e','\x0f','\x10','\x11','\x12','\x13','\x14','\x15','\x16','\x17','\x18','\x19','\x1a','\x1b','\x1c','\x1d',
+			'\x1e','\x1f','\x7f'
+		);
+		$r = $this->Socket->tokenEscapeChars();
+		$this->assertEqual($r, $expected);
+
+		foreach ($expected as $key => $char) {
+			$expected[$key] = chr(hexdec(substr($char, 2)));
+		}
+
+		$r = $this->Socket->tokenEscapeChars(false);
+		$this->assertEqual($r, $expected);
+	}
+
+/**
+ * Test that HttpSocket::escapeToken is escaping all characters as descriped in RFC 2616 (HTTP 1.1 specs)
+ *
+ * @access public
+ * @return void
+ */
+	function testEscapeToken() {
+		$this->Socket->reset();
+
+		$this->assertIdentical($this->Socket->escapeToken('Foo'), 'Foo');
+
+		$escape = $this->Socket->tokenEscapeChars(false);
+		foreach ($escape as $char) {
+			$token = 'My-special-'.$char.'-Token';
+			$escapedToken = $this->Socket->escapeToken($token);
+			$expectedToken = 'My-special-"'.$char.'"-Token';
+
+			$this->assertIdentical($escapedToken, $expectedToken, 'Test token escaping for ASCII '.ord($char));
+		}
+
+		$token = 'Extreme-:Token-	-"@-test';
+		$escapedToken = $this->Socket->escapeToken($token);
+		$expectedToken = 'Extreme-":"Token-"	"-""""@"-test';
+		$this->assertIdentical($expectedToken, $escapedToken);
+	}
+
+/**
+ * Test that escaped token strings are properly unescaped by HttpSocket::unescapeToken
+ *
+ * @access public
+ * @return void
+ */
+	function testUnescapeToken() {
+		$this->Socket->reset();
+
+		$this->assertIdentical($this->Socket->unescapeToken('Foo'), 'Foo');
+
+		$escape = $this->Socket->tokenEscapeChars(false);
+		foreach ($escape as $char) {
+			$token = 'My-special-"'.$char.'"-Token';
+			$unescapedToken = $this->Socket->unescapeToken($token);
+			$expectedToken = 'My-special-'.$char.'-Token';
+
+			$this->assertIdentical($unescapedToken, $expectedToken, 'Test token unescaping for ASCII '.ord($char));
+		}
+
+		$token = 'Extreme-":"Token-"	"-""""@"-test';
+		$escapedToken = $this->Socket->unescapeToken($token);
+		$expectedToken = 'Extreme-:Token-	-"@-test';
+		$this->assertIdentical($expectedToken, $escapedToken);
+	}
+
+/**
+ * This tests asserts HttpSocket::reset() resets a HttpSocket instance to it's initial state (before Object::__construct
+ * got executed)
+ *
+ * @access public
+ * @return void
+ */
+	function testReset() {
+		$this->Socket->reset();
+
+		$initialState = get_class_vars('HttpSocket');
+		foreach ($initialState as $property => $value) {
+			$this->Socket->{$property} = 'Overwritten';
+		}
+
+		$return = $this->Socket->reset();
+
+		foreach ($initialState as $property => $value) {
+			$this->assertIdentical($this->Socket->{$property}, $value);
+		}
+
+		$this->assertIdentical($return, true);
+	}
+
+/**
+ * This tests asserts HttpSocket::reset(false) resets certain HttpSocket properties to their initial state (before
+ * Object::__construct got executed).
+ *
+ * @access public
+ * @return void
+ */
+	function testPartialReset() {
+		$this->Socket->reset();
+
+		$partialResetProperties = array('request', 'response');
+		$initialState = get_class_vars('HttpSocket');
+
+		foreach ($initialState as $property => $value) {
+			$this->Socket->{$property} = 'Overwritten';
+		}
+
+		$return = $this->Socket->reset(false);
+
+		foreach ($initialState as $property => $originalValue) {
+			if (in_array($property, $partialResetProperties)) {
+				$this->assertIdentical($this->Socket->{$property}, $originalValue);
+			} else {
+				$this->assertIdentical($this->Socket->{$property}, 'Overwritten');
+			}
+		}
+		$this->assertIdentical($return, true);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/i18n.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/i18n.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/i18n.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,2777 @@
+<?php
+/**
+ * I18nTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.5432
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', 'i18n');
+
+/**
+ * I18nTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class I18nTest extends CakeTestCase {
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		Cache::delete('object_map', '_cake_core_');
+		App::build(array(
+			'locales' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'locale' . DS),
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)
+		), true);
+		App::objects('plugin', null, false);
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		Cache::delete('object_map', '_cake_core_');
+		App::build();
+		App::objects('plugin', null, false);
+	}
+
+
+	function testTranslationCaching() {
+		Configure::write('Config.language', 'cache_test_po');
+		$i18n =& i18n::getInstance();
+
+		// reset internally stored entries
+		I18n::clear();
+
+		Cache::clear(false, '_cake_core_');
+		$lang = Configure::read('Config.language');#$i18n->l10n->locale;
+
+		Cache::config('_cake_core_', Cache::config('default'));
+
+		// make some calls to translate using different domains
+		$this->assertEqual(I18n::translate('dom1.foo', false, 'dom1'), 'Dom 1 Foo');
+		$this->assertEqual(I18n::translate('dom1.bar', false, 'dom1'), 'Dom 1 Bar');
+		$this->assertEqual($i18n->__domains['dom1']['cache_test_po']['LC_MESSAGES']['dom1.foo'], 'Dom 1 Foo');
+
+		// reset internally stored entries
+		I18n::clear();
+
+		// now only dom1 should be in cache
+		$cachedDom1 = Cache::read('dom1_' . $lang, '_cake_core_');
+		$this->assertEqual($cachedDom1['LC_MESSAGES']['dom1.foo'], 'Dom 1 Foo');
+		$this->assertEqual($cachedDom1['LC_MESSAGES']['dom1.bar'], 'Dom 1 Bar');
+		// dom2 not in cache
+		$this->assertFalse(Cache::read('dom2_' . $lang, '_cake_core_'));
+
+		// translate a item of dom2 (adds dom2 to cache)
+		$this->assertEqual(I18n::translate('dom2.foo', false, 'dom2'), 'Dom 2 Foo');
+
+		// verify dom2 was cached through manual read from cache
+		$cachedDom2 = Cache::read('dom2_' . $lang, '_cake_core_');
+		$this->assertEqual($cachedDom2['LC_MESSAGES']['dom2.foo'], 'Dom 2 Foo');
+		$this->assertEqual($cachedDom2['LC_MESSAGES']['dom2.bar'], 'Dom 2 Bar');
+
+		// modify cache entry manually to verify that dom1 entries now will be read from cache 
+		$cachedDom1['LC_MESSAGES']['dom1.foo'] = 'FOO';
+		Cache::write('dom1_' . $lang, $cachedDom1, '_cake_core_');
+		$this->assertEqual(I18n::translate('dom1.foo', false, 'dom1'), 'FOO');
+	}
+
+
+/**
+ * testDefaultStrings method
+ *
+ * @access public
+ * @return void
+ */
+	function testDefaultStrings() {
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 1', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 = 0 or > 1', $plurals));
+		$this->assertTrue(in_array('1 = 1', $plurals));
+		$this->assertTrue(in_array('2 = 0 or > 1', $plurals));
+		$this->assertTrue(in_array('3 = 0 or > 1', $plurals));
+		$this->assertTrue(in_array('4 = 0 or > 1', $plurals));
+		$this->assertTrue(in_array('5 = 0 or > 1', $plurals));
+		$this->assertTrue(in_array('6 = 0 or > 1', $plurals));
+		$this->assertTrue(in_array('7 = 0 or > 1', $plurals));
+		$this->assertTrue(in_array('8 = 0 or > 1', $plurals));
+		$this->assertTrue(in_array('9 = 0 or > 1', $plurals));
+		$this->assertTrue(in_array('10 = 0 or > 1', $plurals));
+		$this->assertTrue(in_array('11 = 0 or > 1', $plurals));
+		$this->assertTrue(in_array('12 = 0 or > 1', $plurals));
+		$this->assertTrue(in_array('13 = 0 or > 1', $plurals));
+		$this->assertTrue(in_array('14 = 0 or > 1', $plurals));
+		$this->assertTrue(in_array('15 = 0 or > 1', $plurals));
+		$this->assertTrue(in_array('16 = 0 or > 1', $plurals));
+		$this->assertTrue(in_array('17 = 0 or > 1', $plurals));
+		$this->assertTrue(in_array('18 = 0 or > 1', $plurals));
+		$this->assertTrue(in_array('19 = 0 or > 1', $plurals));
+		$this->assertTrue(in_array('20 = 0 or > 1', $plurals));
+		$this->assertTrue(in_array('21 = 0 or > 1', $plurals));
+		$this->assertTrue(in_array('22 = 0 or > 1', $plurals));
+		$this->assertTrue(in_array('23 = 0 or > 1', $plurals));
+		$this->assertTrue(in_array('24 = 0 or > 1', $plurals));
+		$this->assertTrue(in_array('25 = 0 or > 1', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 1 (from core)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 = 0 or > 1 (from core)', $corePlurals));
+		$this->assertTrue(in_array('1 = 1 (from core)', $corePlurals));
+		$this->assertTrue(in_array('2 = 0 or > 1 (from core)', $corePlurals));
+		$this->assertTrue(in_array('3 = 0 or > 1 (from core)', $corePlurals));
+		$this->assertTrue(in_array('4 = 0 or > 1 (from core)', $corePlurals));
+		$this->assertTrue(in_array('5 = 0 or > 1 (from core)', $corePlurals));
+		$this->assertTrue(in_array('6 = 0 or > 1 (from core)', $corePlurals));
+		$this->assertTrue(in_array('7 = 0 or > 1 (from core)', $corePlurals));
+		$this->assertTrue(in_array('8 = 0 or > 1 (from core)', $corePlurals));
+		$this->assertTrue(in_array('9 = 0 or > 1 (from core)', $corePlurals));
+		$this->assertTrue(in_array('10 = 0 or > 1 (from core)', $corePlurals));
+		$this->assertTrue(in_array('11 = 0 or > 1 (from core)', $corePlurals));
+		$this->assertTrue(in_array('12 = 0 or > 1 (from core)', $corePlurals));
+		$this->assertTrue(in_array('13 = 0 or > 1 (from core)', $corePlurals));
+		$this->assertTrue(in_array('14 = 0 or > 1 (from core)', $corePlurals));
+		$this->assertTrue(in_array('15 = 0 or > 1 (from core)', $corePlurals));
+		$this->assertTrue(in_array('16 = 0 or > 1 (from core)', $corePlurals));
+		$this->assertTrue(in_array('17 = 0 or > 1 (from core)', $corePlurals));
+		$this->assertTrue(in_array('18 = 0 or > 1 (from core)', $corePlurals));
+		$this->assertTrue(in_array('19 = 0 or > 1 (from core)', $corePlurals));
+		$this->assertTrue(in_array('20 = 0 or > 1 (from core)', $corePlurals));
+		$this->assertTrue(in_array('21 = 0 or > 1 (from core)', $corePlurals));
+		$this->assertTrue(in_array('22 = 0 or > 1 (from core)', $corePlurals));
+		$this->assertTrue(in_array('23 = 0 or > 1 (from core)', $corePlurals));
+		$this->assertTrue(in_array('24 = 0 or > 1 (from core)', $corePlurals));
+		$this->assertTrue(in_array('25 = 0 or > 1 (from core)', $corePlurals));
+	}
+
+/**
+ * testPoRulesZero method
+ *
+ * @access public
+ * @return void
+ */
+	function testPoRulesZero() {
+		Configure::write('Config.language', 'rule_0_po');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 0 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('1 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('2 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('3 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('4 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('5 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('6 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('7 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('8 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('9 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('10 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('11 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('12 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('13 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('14 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('15 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('16 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('17 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('18 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('19 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('20 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('21 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('22 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('23 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('24 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('25 ends with any # (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 0 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 ends with any # (from core translated)', $corePlurals));
+	}
+
+/**
+ * testMoRulesZero method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoRulesZero() {
+		Configure::write('Config.language', 'rule_0_mo');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 0 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('1 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('2 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('3 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('4 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('5 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('6 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('7 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('8 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('9 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('10 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('11 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('12 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('13 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('14 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('15 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('16 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('17 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('18 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('19 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('20 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('21 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('22 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('23 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('24 ends with any # (translated)', $plurals));
+		$this->assertTrue(in_array('25 ends with any # (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 0 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 ends with any # (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 ends with any # (from core translated)', $corePlurals));
+	}
+
+/**
+ * testPoRulesOne method
+ *
+ * @access public
+ * @return void
+ */
+	function testPoRulesOne() {
+		Configure::write('Config.language', 'rule_1_po');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 1 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('1 = 1 (translated)', $plurals));
+		$this->assertTrue(in_array('2 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('3 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('4 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('5 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('6 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('7 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('8 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('9 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('10 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('11 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('12 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('13 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('14 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('15 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('16 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('17 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('18 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('19 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('20 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('21 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('22 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('23 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('24 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('25 = 0 or > 1 (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 1 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 = 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 = 0 or > 1 (from core translated)', $corePlurals));
+	}
+
+/**
+ * testMoRulesOne method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoRulesOne() {
+		Configure::write('Config.language', 'rule_1_mo');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 1 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('1 = 1 (translated)', $plurals));
+		$this->assertTrue(in_array('2 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('3 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('4 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('5 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('6 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('7 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('8 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('9 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('10 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('11 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('12 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('13 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('14 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('15 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('16 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('17 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('18 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('19 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('20 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('21 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('22 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('23 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('24 = 0 or > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('25 = 0 or > 1 (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 1 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 = 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 = 0 or > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 = 0 or > 1 (from core translated)', $corePlurals));
+	}
+
+/**
+ * testPoRulesTwo method
+ *
+ * @access public
+ * @return void
+ */
+	function testPoRulesTwo() {
+		Configure::write('Config.language', 'rule_2_po');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 2 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 = 0 or 1 (translated)', $plurals));
+		$this->assertTrue(in_array('1 = 0 or 1 (translated)', $plurals));
+		$this->assertTrue(in_array('2 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('3 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('4 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('5 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('6 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('7 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('8 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('9 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('10 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('11 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('12 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('13 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('14 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('15 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('16 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('17 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('18 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('19 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('20 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('21 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('22 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('23 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('24 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('25 > 1 (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 2 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 = 0 or 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 = 0 or 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 > 1 (from core translated)', $corePlurals));
+	}
+
+/**
+ * testMoRulesTwo method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoRulesTwo() {
+		Configure::write('Config.language', 'rule_2_mo');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 2 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 = 0 or 1 (translated)', $plurals));
+		$this->assertTrue(in_array('1 = 0 or 1 (translated)', $plurals));
+		$this->assertTrue(in_array('2 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('3 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('4 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('5 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('6 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('7 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('8 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('9 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('10 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('11 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('12 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('13 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('14 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('15 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('16 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('17 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('18 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('19 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('20 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('21 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('22 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('23 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('24 > 1 (translated)', $plurals));
+		$this->assertTrue(in_array('25 > 1 (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 2 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 = 0 or 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 = 0 or 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 > 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 > 1 (from core translated)', $corePlurals));
+	}
+
+/**
+ * testPoRulesThree method
+ *
+ * @access public
+ * @return void
+ */
+	function testPoRulesThree() {
+		Configure::write('Config.language', 'rule_3_po');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 3 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 = 0 (translated)', $plurals));
+		$this->assertTrue(in_array('1 ends 1 but not 11 (translated)', $plurals));
+		$this->assertTrue(in_array('2 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('3 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('4 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('5 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('6 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('7 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('8 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('9 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('10 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('11 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('12 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('13 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('14 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('15 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('16 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('17 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('18 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('19 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('20 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('21 ends 1 but not 11 (translated)', $plurals));
+		$this->assertTrue(in_array('22 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('23 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('24 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('25 everything else (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 3 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 = 0 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 ends 1 but not 11 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 ends 1 but not 11 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals));
+	}
+
+/**
+ * testMoRulesThree method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoRulesThree() {
+		Configure::write('Config.language', 'rule_3_mo');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 3 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 = 0 (translated)', $plurals));
+		$this->assertTrue(in_array('1 ends 1 but not 11 (translated)', $plurals));
+		$this->assertTrue(in_array('2 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('3 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('4 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('5 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('6 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('7 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('8 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('9 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('10 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('11 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('12 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('13 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('14 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('15 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('16 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('17 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('18 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('19 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('20 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('21 ends 1 but not 11 (translated)', $plurals));
+		$this->assertTrue(in_array('22 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('23 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('24 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('25 everything else (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 3 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 = 0 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 ends 1 but not 11 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 ends 1 but not 11 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals));
+	}
+
+/**
+ * testPoRulesFour method
+ *
+ * @access public
+ * @return void
+ */
+	function testPoRulesFour() {
+		Configure::write('Config.language', 'rule_4_po');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 4 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('1 = 1 (translated)', $plurals));
+		$this->assertTrue(in_array('2 = 2 (translated)', $plurals));
+		$this->assertTrue(in_array('3 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('4 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('5 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('6 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('7 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('8 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('9 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('10 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('11 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('12 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('13 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('14 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('15 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('16 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('17 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('18 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('19 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('20 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('21 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('22 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('23 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('24 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('25 everything else (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 4 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 = 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 = 2 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals));
+	}
+
+/**
+ * testMoRulesFour method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoRulesFour() {
+		Configure::write('Config.language', 'rule_4_mo');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 4 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('1 = 1 (translated)', $plurals));
+		$this->assertTrue(in_array('2 = 2 (translated)', $plurals));
+		$this->assertTrue(in_array('3 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('4 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('5 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('6 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('7 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('8 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('9 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('10 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('11 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('12 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('13 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('14 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('15 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('16 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('17 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('18 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('19 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('20 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('21 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('22 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('23 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('24 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('25 everything else (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 4 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 = 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 = 2 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals));
+	}
+
+/**
+ * testPoRulesFive method
+ *
+ * @access public
+ * @return void
+ */
+	function testPoRulesFive() {
+		Configure::write('Config.language', 'rule_5_po');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 5 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('0 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('1 = 1 (translated)', $plurals));
+		$this->assertTrue(in_array('2 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('3 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('4 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('5 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('6 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('7 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('8 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('9 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('10 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('11 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('12 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('13 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('14 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('15 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('16 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('17 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('18 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('19 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('20 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('21 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('22 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('23 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('24 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('25 everything else (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 5 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('0 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 = 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals));
+	}
+
+/**
+ * testMoRulesFive method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoRulesFive() {
+		Configure::write('Config.language', 'rule_5_mo');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 5 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('0 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('1 = 1 (translated)', $plurals));
+		$this->assertTrue(in_array('2 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('3 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('4 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('5 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('6 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('7 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('8 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('9 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('10 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('11 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('12 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('13 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('14 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('15 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('16 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('17 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('18 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('19 = 0 or ends in 01-19 (translated)', $plurals));
+		$this->assertTrue(in_array('20 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('21 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('22 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('23 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('24 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('25 everything else (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 5 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('0 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 = 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 = 0 or ends in 01-19 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals));
+	}
+
+/**
+ * testPoRulesSix method
+ *
+ * @access public
+ * @return void
+ */
+	function testPoRulesSix() {
+		Configure::write('Config.language', 'rule_6_po');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 6 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 ends in 0 or ends in 10-20 (translated)', $plurals));
+		$this->assertTrue(in_array('1 ends in 1, not 11 (translated)', $plurals));
+		$this->assertTrue(in_array('2 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('3 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('4 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('5 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('6 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('7 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('8 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('9 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('10 ends in 0 or ends in 10-20 (translated)', $plurals));
+		$this->assertTrue(in_array('11 ends in 0 or ends in 10-20 (translated)', $plurals));
+		$this->assertTrue(in_array('12 ends in 0 or ends in 10-20 (translated)', $plurals));
+		$this->assertTrue(in_array('13 ends in 0 or ends in 10-20 (translated)', $plurals));
+		$this->assertTrue(in_array('14 ends in 0 or ends in 10-20 (translated)', $plurals));
+		$this->assertTrue(in_array('15 ends in 0 or ends in 10-20 (translated)', $plurals));
+		$this->assertTrue(in_array('16 ends in 0 or ends in 10-20 (translated)', $plurals));
+		$this->assertTrue(in_array('17 ends in 0 or ends in 10-20 (translated)', $plurals));
+		$this->assertTrue(in_array('18 ends in 0 or ends in 10-20 (translated)', $plurals));
+		$this->assertTrue(in_array('19 ends in 0 or ends in 10-20 (translated)', $plurals));
+		$this->assertTrue(in_array('20 ends in 0 or ends in 10-20 (translated)', $plurals));
+		$this->assertTrue(in_array('21 ends in 1, not 11 (translated)', $plurals));
+		$this->assertTrue(in_array('22 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('23 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('24 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('25 everything else (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 6 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 ends in 0 or ends in 10-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 ends in 1, not 11 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 ends in 0 or ends in 10-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 ends in 0 or ends in 10-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 ends in 0 or ends in 10-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 ends in 0 or ends in 10-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 ends in 0 or ends in 10-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 ends in 0 or ends in 10-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 ends in 0 or ends in 10-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 ends in 0 or ends in 10-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 ends in 0 or ends in 10-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 ends in 0 or ends in 10-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 ends in 0 or ends in 10-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 ends in 1, not 11 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals));
+	}
+
+/**
+ * testMoRulesSix method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoRulesSix() {
+		Configure::write('Config.language', 'rule_6_mo');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 6 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 ends in 0 or ends in 10-20 (translated)', $plurals));
+		$this->assertTrue(in_array('1 ends in 1, not 11 (translated)', $plurals));
+		$this->assertTrue(in_array('2 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('3 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('4 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('5 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('6 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('7 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('8 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('9 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('10 ends in 0 or ends in 10-20 (translated)', $plurals));
+		$this->assertTrue(in_array('11 ends in 0 or ends in 10-20 (translated)', $plurals));
+		$this->assertTrue(in_array('12 ends in 0 or ends in 10-20 (translated)', $plurals));
+		$this->assertTrue(in_array('13 ends in 0 or ends in 10-20 (translated)', $plurals));
+		$this->assertTrue(in_array('14 ends in 0 or ends in 10-20 (translated)', $plurals));
+		$this->assertTrue(in_array('15 ends in 0 or ends in 10-20 (translated)', $plurals));
+		$this->assertTrue(in_array('16 ends in 0 or ends in 10-20 (translated)', $plurals));
+		$this->assertTrue(in_array('17 ends in 0 or ends in 10-20 (translated)', $plurals));
+		$this->assertTrue(in_array('18 ends in 0 or ends in 10-20 (translated)', $plurals));
+		$this->assertTrue(in_array('19 ends in 0 or ends in 10-20 (translated)', $plurals));
+		$this->assertTrue(in_array('20 ends in 0 or ends in 10-20 (translated)', $plurals));
+		$this->assertTrue(in_array('21 ends in 1, not 11 (translated)', $plurals));
+		$this->assertTrue(in_array('22 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('23 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('24 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('25 everything else (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 6 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 ends in 0 or ends in 10-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 ends in 1, not 11 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 ends in 0 or ends in 10-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 ends in 0 or ends in 10-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 ends in 0 or ends in 10-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 ends in 0 or ends in 10-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 ends in 0 or ends in 10-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 ends in 0 or ends in 10-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 ends in 0 or ends in 10-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 ends in 0 or ends in 10-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 ends in 0 or ends in 10-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 ends in 0 or ends in 10-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 ends in 0 or ends in 10-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 ends in 1, not 11 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals));
+	}
+
+/**
+ * testPoRulesSeven method
+ *
+ * @access public
+ * @return void
+ */
+	function testPoRulesSeven() {
+		Configure::write('Config.language', 'rule_7_po');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 7 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('1 ends in 1, not 11 (translated)', $plurals));
+		$this->assertTrue(in_array('2 ends in 2-4, not 12-14 (translated)', $plurals));
+		$this->assertTrue(in_array('3 ends in 2-4, not 12-14 (translated)', $plurals));
+		$this->assertTrue(in_array('4 ends in 2-4, not 12-14 (translated)', $plurals));
+		$this->assertTrue(in_array('5 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('6 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('7 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('8 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('9 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('10 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('11 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('12 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('13 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('14 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('15 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('16 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('17 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('18 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('19 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('20 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('21 ends in 1, not 11 (translated)', $plurals));
+		$this->assertTrue(in_array('22 ends in 2-4, not 12-14 (translated)', $plurals));
+		$this->assertTrue(in_array('23 ends in 2-4, not 12-14 (translated)', $plurals));
+		$this->assertTrue(in_array('24 ends in 2-4, not 12-14 (translated)', $plurals));
+		$this->assertTrue(in_array('25 everything else (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 7 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 ends in 1, not 11 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 ends in 2-4, not 12-14 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 ends in 2-4, not 12-14 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 ends in 2-4, not 12-14 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 ends in 1, not 11 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 ends in 2-4, not 12-14 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 ends in 2-4, not 12-14 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 ends in 2-4, not 12-14 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals));
+	}
+
+/**
+ * testMoRulesSeven method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoRulesSeven() {
+		Configure::write('Config.language', 'rule_7_mo');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 7 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('1 ends in 1, not 11 (translated)', $plurals));
+		$this->assertTrue(in_array('2 ends in 2-4, not 12-14 (translated)', $plurals));
+		$this->assertTrue(in_array('3 ends in 2-4, not 12-14 (translated)', $plurals));
+		$this->assertTrue(in_array('4 ends in 2-4, not 12-14 (translated)', $plurals));
+		$this->assertTrue(in_array('5 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('6 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('7 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('8 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('9 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('10 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('11 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('12 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('13 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('14 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('15 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('16 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('17 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('18 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('19 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('20 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('21 ends in 1, not 11 (translated)', $plurals));
+		$this->assertTrue(in_array('22 ends in 2-4, not 12-14 (translated)', $plurals));
+		$this->assertTrue(in_array('23 ends in 2-4, not 12-14 (translated)', $plurals));
+		$this->assertTrue(in_array('24 ends in 2-4, not 12-14 (translated)', $plurals));
+		$this->assertTrue(in_array('25 everything else (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 7 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 ends in 1, not 11 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 ends in 2-4, not 12-14 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 ends in 2-4, not 12-14 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 ends in 2-4, not 12-14 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 ends in 1, not 11 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 ends in 2-4, not 12-14 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 ends in 2-4, not 12-14 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 ends in 2-4, not 12-14 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals));
+	}
+
+/**
+ * testPoRulesEight method
+ *
+ * @access public
+ * @return void
+ */
+	function testPoRulesEight() {
+		Configure::write('Config.language', 'rule_8_po');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 8 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('1 is 1 (translated)', $plurals));
+		$this->assertTrue(in_array('2 is 2-4 (translated)', $plurals));
+		$this->assertTrue(in_array('3 is 2-4 (translated)', $plurals));
+		$this->assertTrue(in_array('4 is 2-4 (translated)', $plurals));
+		$this->assertTrue(in_array('5 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('6 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('7 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('8 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('9 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('10 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('11 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('12 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('13 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('14 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('15 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('16 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('17 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('18 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('19 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('20 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('21 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('22 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('23 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('24 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('25 everything else (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 8 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 is 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 is 2-4 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 is 2-4 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 is 2-4 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals));
+	}
+
+/**
+ * testMoRulesEight method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoRulesEight() {
+		Configure::write('Config.language', 'rule_8_mo');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 8 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('1 is 1 (translated)', $plurals));
+		$this->assertTrue(in_array('2 is 2-4 (translated)', $plurals));
+		$this->assertTrue(in_array('3 is 2-4 (translated)', $plurals));
+		$this->assertTrue(in_array('4 is 2-4 (translated)', $plurals));
+		$this->assertTrue(in_array('5 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('6 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('7 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('8 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('9 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('10 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('11 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('12 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('13 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('14 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('15 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('16 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('17 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('18 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('19 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('20 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('21 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('22 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('23 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('24 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('25 everything else (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 8 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 is 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 is 2-4 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 is 2-4 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 is 2-4 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals));
+	}
+
+/**
+ * testPoRulesNine method
+ *
+ * @access public
+ * @return void
+ */
+	function testPoRulesNine() {
+		Configure::write('Config.language', 'rule_9_po');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 9 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('0 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('1 is 1 (translated)', $plurals));
+		$this->assertTrue(in_array('2 ends in 2-4, not 12-14 (translated)', $plurals));
+		$this->assertTrue(in_array('3 ends in 2-4, not 12-14 (translated)', $plurals));
+		$this->assertTrue(in_array('4 ends in 2-4, not 12-14 (translated)', $plurals));
+		$this->assertTrue(in_array('5 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('6 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('7 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('8 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('9 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('10 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('11 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('12 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('13 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('14 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('15 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('16 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('17 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('18 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('19 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('20 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('21 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('22 ends in 2-4, not 12-14 (translated)', $plurals));
+		$this->assertTrue(in_array('23 ends in 2-4, not 12-14 (translated)', $plurals));
+		$this->assertTrue(in_array('24 ends in 2-4, not 12-14 (translated)', $plurals));
+		$this->assertTrue(in_array('25 everything else (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 9 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 is 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 ends in 2-4, not 12-14 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 ends in 2-4, not 12-14 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 ends in 2-4, not 12-14 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 ends in 2-4, not 12-14 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 ends in 2-4, not 12-14 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 ends in 2-4, not 12-14 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals));
+	}
+
+/**
+ * testMoRulesNine method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoRulesNine() {
+		Configure::write('Config.language', 'rule_9_po');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 9 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('0 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('1 is 1 (translated)', $plurals));
+		$this->assertTrue(in_array('2 ends in 2-4, not 12-14 (translated)', $plurals));
+		$this->assertTrue(in_array('3 ends in 2-4, not 12-14 (translated)', $plurals));
+		$this->assertTrue(in_array('4 ends in 2-4, not 12-14 (translated)', $plurals));
+		$this->assertTrue(in_array('5 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('6 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('7 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('8 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('9 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('10 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('11 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('12 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('13 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('14 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('15 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('16 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('17 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('18 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('19 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('20 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('21 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('22 ends in 2-4, not 12-14 (translated)', $plurals));
+		$this->assertTrue(in_array('23 ends in 2-4, not 12-14 (translated)', $plurals));
+		$this->assertTrue(in_array('24 ends in 2-4, not 12-14 (translated)', $plurals));
+		$this->assertTrue(in_array('25 everything else (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 9 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 is 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 ends in 2-4, not 12-14 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 ends in 2-4, not 12-14 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 ends in 2-4, not 12-14 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 ends in 2-4, not 12-14 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 ends in 2-4, not 12-14 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 ends in 2-4, not 12-14 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals));
+	}
+
+/**
+ * testPoRulesTen method
+ *
+ * @access public
+ * @return void
+ */
+	function testPoRulesTen() {
+		Configure::write('Config.language', 'rule_10_po');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 10 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('0 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('1 ends in 1 (translated)', $plurals));
+		$this->assertTrue(in_array('2 ends in 2 (translated)', $plurals));
+		$this->assertTrue(in_array('3 ends in 03-04 (translated)', $plurals));
+		$this->assertTrue(in_array('4 ends in 03-04 (translated)', $plurals));
+		$this->assertTrue(in_array('5 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('6 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('7 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('8 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('9 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('10 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('11 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('12 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('13 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('14 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('15 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('16 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('17 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('18 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('19 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('20 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('21 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('22 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('23 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('24 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('25 everything else (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 10 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 ends in 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 ends in 2 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 ends in 03-04 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 ends in 03-04 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals));
+	}
+
+/**
+ * testMoRulesTen method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoRulesTen() {
+		Configure::write('Config.language', 'rule_10_mo');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 10 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('0 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('1 ends in 1 (translated)', $plurals));
+		$this->assertTrue(in_array('2 ends in 2 (translated)', $plurals));
+		$this->assertTrue(in_array('3 ends in 03-04 (translated)', $plurals));
+		$this->assertTrue(in_array('4 ends in 03-04 (translated)', $plurals));
+		$this->assertTrue(in_array('5 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('6 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('7 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('8 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('9 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('10 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('11 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('12 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('13 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('14 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('15 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('16 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('17 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('18 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('19 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('20 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('21 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('22 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('23 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('24 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('25 everything else (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 10 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 ends in 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 ends in 2 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 ends in 03-04 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 ends in 03-04 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals));
+	}
+
+/**
+ * testPoRulesEleven method
+ *
+ * @access public
+ * @return void
+ */
+	function testPoRulesEleven() {
+		Configure::write('Config.language', 'rule_11_po');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 11 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('1 is 1 (translated)', $plurals));
+		$this->assertTrue(in_array('2 is 2 (translated)', $plurals));
+		$this->assertTrue(in_array('3 is 3-6 (translated)', $plurals));
+		$this->assertTrue(in_array('4 is 3-6 (translated)', $plurals));
+		$this->assertTrue(in_array('5 is 3-6 (translated)', $plurals));
+		$this->assertTrue(in_array('6 is 3-6 (translated)', $plurals));
+		$this->assertTrue(in_array('7 is 7-10 (translated)', $plurals));
+		$this->assertTrue(in_array('8 is 7-10 (translated)', $plurals));
+		$this->assertTrue(in_array('9 is 7-10 (translated)', $plurals));
+		$this->assertTrue(in_array('10 is 7-10 (translated)', $plurals));
+		$this->assertTrue(in_array('11 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('12 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('13 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('14 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('15 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('16 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('17 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('18 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('19 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('20 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('21 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('22 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('23 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('24 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('25 everything else (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 11 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 is 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 is 2 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 is 3-6 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 is 3-6 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 is 3-6 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 is 3-6 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 is 7-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 is 7-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 is 7-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 is 7-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals));
+	}
+
+/**
+ * testMoRulesEleven method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoRulesEleven() {
+		Configure::write('Config.language', 'rule_11_mo');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 11 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('1 is 1 (translated)', $plurals));
+		$this->assertTrue(in_array('2 is 2 (translated)', $plurals));
+		$this->assertTrue(in_array('3 is 3-6 (translated)', $plurals));
+		$this->assertTrue(in_array('4 is 3-6 (translated)', $plurals));
+		$this->assertTrue(in_array('5 is 3-6 (translated)', $plurals));
+		$this->assertTrue(in_array('6 is 3-6 (translated)', $plurals));
+		$this->assertTrue(in_array('7 is 7-10 (translated)', $plurals));
+		$this->assertTrue(in_array('8 is 7-10 (translated)', $plurals));
+		$this->assertTrue(in_array('9 is 7-10 (translated)', $plurals));
+		$this->assertTrue(in_array('10 is 7-10 (translated)', $plurals));
+		$this->assertTrue(in_array('11 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('12 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('13 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('14 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('15 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('16 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('17 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('18 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('19 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('20 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('21 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('22 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('23 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('24 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('25 everything else (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 11 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 is 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 is 2 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 is 3-6 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 is 3-6 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 is 3-6 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 is 3-6 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 is 7-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 is 7-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 is 7-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 is 7-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals));
+	}
+
+/**
+ * testPoRulesTwelve method
+ *
+ * @access public
+ * @return void
+ */
+	function testPoRulesTwelve() {
+		Configure::write('Config.language', 'rule_12_po');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 12 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 is 0 or 3-10 (translated)', $plurals));
+		$this->assertTrue(in_array('1 is 1 (translated)', $plurals));
+		$this->assertTrue(in_array('2 is 2 (translated)', $plurals));
+		$this->assertTrue(in_array('3 is 0 or 3-10 (translated)', $plurals));
+		$this->assertTrue(in_array('4 is 0 or 3-10 (translated)', $plurals));
+		$this->assertTrue(in_array('5 is 0 or 3-10 (translated)', $plurals));
+		$this->assertTrue(in_array('6 is 0 or 3-10 (translated)', $plurals));
+		$this->assertTrue(in_array('7 is 0 or 3-10 (translated)', $plurals));
+		$this->assertTrue(in_array('8 is 0 or 3-10 (translated)', $plurals));
+		$this->assertTrue(in_array('9 is 0 or 3-10 (translated)', $plurals));
+		$this->assertTrue(in_array('10 is 0 or 3-10 (translated)', $plurals));
+		$this->assertTrue(in_array('11 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('12 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('13 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('14 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('15 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('16 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('17 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('18 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('19 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('20 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('21 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('22 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('23 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('24 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('25 everything else (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 12 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 is 0 or 3-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 is 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 is 2 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 is 0 or 3-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 is 0 or 3-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 is 0 or 3-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 is 0 or 3-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 is 0 or 3-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 is 0 or 3-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 is 0 or 3-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 is 0 or 3-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals));
+	}
+
+/**
+ * testMoRulesTwelve method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoRulesTwelve() {
+		Configure::write('Config.language', 'rule_12_mo');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 12 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 is 0 or 3-10 (translated)', $plurals));
+		$this->assertTrue(in_array('1 is 1 (translated)', $plurals));
+		$this->assertTrue(in_array('2 is 2 (translated)', $plurals));
+		$this->assertTrue(in_array('3 is 0 or 3-10 (translated)', $plurals));
+		$this->assertTrue(in_array('4 is 0 or 3-10 (translated)', $plurals));
+		$this->assertTrue(in_array('5 is 0 or 3-10 (translated)', $plurals));
+		$this->assertTrue(in_array('6 is 0 or 3-10 (translated)', $plurals));
+		$this->assertTrue(in_array('7 is 0 or 3-10 (translated)', $plurals));
+		$this->assertTrue(in_array('8 is 0 or 3-10 (translated)', $plurals));
+		$this->assertTrue(in_array('9 is 0 or 3-10 (translated)', $plurals));
+		$this->assertTrue(in_array('10 is 0 or 3-10 (translated)', $plurals));
+		$this->assertTrue(in_array('11 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('12 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('13 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('14 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('15 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('16 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('17 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('18 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('19 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('20 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('21 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('22 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('23 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('24 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('25 everything else (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 12 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 is 0 or 3-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 is 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 is 2 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 is 0 or 3-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 is 0 or 3-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 is 0 or 3-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 is 0 or 3-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 is 0 or 3-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 is 0 or 3-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 is 0 or 3-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 is 0 or 3-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals));
+	}
+
+/**
+ * testPoRulesThirteen method
+ *
+ * @access public
+ * @return void
+ */
+	function testPoRulesThirteen() {
+		Configure::write('Config.language', 'rule_13_po');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 13 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 is 0 or ends in 01-10 (translated)', $plurals));
+		$this->assertTrue(in_array('1 is 1 (translated)', $plurals));
+		$this->assertTrue(in_array('2 is 0 or ends in 01-10 (translated)', $plurals));
+		$this->assertTrue(in_array('3 is 0 or ends in 01-10 (translated)', $plurals));
+		$this->assertTrue(in_array('4 is 0 or ends in 01-10 (translated)', $plurals));
+		$this->assertTrue(in_array('5 is 0 or ends in 01-10 (translated)', $plurals));
+		$this->assertTrue(in_array('6 is 0 or ends in 01-10 (translated)', $plurals));
+		$this->assertTrue(in_array('7 is 0 or ends in 01-10 (translated)', $plurals));
+		$this->assertTrue(in_array('8 is 0 or ends in 01-10 (translated)', $plurals));
+		$this->assertTrue(in_array('9 is 0 or ends in 01-10 (translated)', $plurals));
+		$this->assertTrue(in_array('10 is 0 or ends in 01-10 (translated)', $plurals));
+		$this->assertTrue(in_array('11 ends in 11-20 (translated)', $plurals));
+		$this->assertTrue(in_array('12 ends in 11-20 (translated)', $plurals));
+		$this->assertTrue(in_array('13 ends in 11-20 (translated)', $plurals));
+		$this->assertTrue(in_array('14 ends in 11-20 (translated)', $plurals));
+		$this->assertTrue(in_array('15 ends in 11-20 (translated)', $plurals));
+		$this->assertTrue(in_array('16 ends in 11-20 (translated)', $plurals));
+		$this->assertTrue(in_array('17 ends in 11-20 (translated)', $plurals));
+		$this->assertTrue(in_array('18 ends in 11-20 (translated)', $plurals));
+		$this->assertTrue(in_array('19 ends in 11-20 (translated)', $plurals));
+		$this->assertTrue(in_array('20 ends in 11-20 (translated)', $plurals));
+		$this->assertTrue(in_array('21 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('22 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('23 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('24 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('25 everything else (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 13 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 is 0 or ends in 01-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 is 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 is 0 or ends in 01-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 is 0 or ends in 01-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 is 0 or ends in 01-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 is 0 or ends in 01-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 is 0 or ends in 01-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 is 0 or ends in 01-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 is 0 or ends in 01-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 is 0 or ends in 01-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 is 0 or ends in 01-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 ends in 11-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 ends in 11-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 ends in 11-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 ends in 11-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 ends in 11-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 ends in 11-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 ends in 11-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 ends in 11-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 ends in 11-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 ends in 11-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals));
+	}
+
+/**
+ * testMoRulesThirteen method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoRulesThirteen() {
+		Configure::write('Config.language', 'rule_13_mo');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 13 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 is 0 or ends in 01-10 (translated)', $plurals));
+		$this->assertTrue(in_array('1 is 1 (translated)', $plurals));
+		$this->assertTrue(in_array('2 is 0 or ends in 01-10 (translated)', $plurals));
+		$this->assertTrue(in_array('3 is 0 or ends in 01-10 (translated)', $plurals));
+		$this->assertTrue(in_array('4 is 0 or ends in 01-10 (translated)', $plurals));
+		$this->assertTrue(in_array('5 is 0 or ends in 01-10 (translated)', $plurals));
+		$this->assertTrue(in_array('6 is 0 or ends in 01-10 (translated)', $plurals));
+		$this->assertTrue(in_array('7 is 0 or ends in 01-10 (translated)', $plurals));
+		$this->assertTrue(in_array('8 is 0 or ends in 01-10 (translated)', $plurals));
+		$this->assertTrue(in_array('9 is 0 or ends in 01-10 (translated)', $plurals));
+		$this->assertTrue(in_array('10 is 0 or ends in 01-10 (translated)', $plurals));
+		$this->assertTrue(in_array('11 ends in 11-20 (translated)', $plurals));
+		$this->assertTrue(in_array('12 ends in 11-20 (translated)', $plurals));
+		$this->assertTrue(in_array('13 ends in 11-20 (translated)', $plurals));
+		$this->assertTrue(in_array('14 ends in 11-20 (translated)', $plurals));
+		$this->assertTrue(in_array('15 ends in 11-20 (translated)', $plurals));
+		$this->assertTrue(in_array('16 ends in 11-20 (translated)', $plurals));
+		$this->assertTrue(in_array('17 ends in 11-20 (translated)', $plurals));
+		$this->assertTrue(in_array('18 ends in 11-20 (translated)', $plurals));
+		$this->assertTrue(in_array('19 ends in 11-20 (translated)', $plurals));
+		$this->assertTrue(in_array('20 ends in 11-20 (translated)', $plurals));
+		$this->assertTrue(in_array('21 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('22 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('23 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('24 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('25 everything else (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 13 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 is 0 or ends in 01-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 is 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 is 0 or ends in 01-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 is 0 or ends in 01-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 is 0 or ends in 01-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 is 0 or ends in 01-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 is 0 or ends in 01-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 is 0 or ends in 01-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 is 0 or ends in 01-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 is 0 or ends in 01-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 is 0 or ends in 01-10 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 ends in 11-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 ends in 11-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 ends in 11-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 ends in 11-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 ends in 11-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 ends in 11-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 ends in 11-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 ends in 11-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 ends in 11-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 ends in 11-20 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals));
+	}
+
+/**
+ * testPoRulesFourteen method
+ *
+ * @access public
+ * @return void
+ */
+	function testPoRulesFourteen() {
+		Configure::write('Config.language', 'rule_14_po');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 14 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('1 ends in 1 (translated)', $plurals));
+		$this->assertTrue(in_array('2 ends in 2 (translated)', $plurals));
+		$this->assertTrue(in_array('3 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('4 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('5 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('6 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('7 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('8 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('9 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('10 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('11 ends in 1 (translated)', $plurals));
+		$this->assertTrue(in_array('12 ends in 2 (translated)', $plurals));
+		$this->assertTrue(in_array('13 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('14 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('15 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('16 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('17 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('18 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('19 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('20 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('21 ends in 1 (translated)', $plurals));
+		$this->assertTrue(in_array('22 ends in 2 (translated)', $plurals));
+		$this->assertTrue(in_array('23 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('24 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('25 everything else (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 14 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 ends in 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 ends in 2 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 ends in 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 ends in 2 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 ends in 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 ends in 2 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals));
+	}
+
+/**
+ * testMoRulesFourteen method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoRulesFourteen() {
+		Configure::write('Config.language', 'rule_14_mo');
+
+		$singular = $this->__singular();
+		$this->assertEqual('Plural Rule 14 (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('1 ends in 1 (translated)', $plurals));
+		$this->assertTrue(in_array('2 ends in 2 (translated)', $plurals));
+		$this->assertTrue(in_array('3 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('4 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('5 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('6 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('7 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('8 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('9 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('10 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('11 ends in 1 (translated)', $plurals));
+		$this->assertTrue(in_array('12 ends in 2 (translated)', $plurals));
+		$this->assertTrue(in_array('13 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('14 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('15 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('16 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('17 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('18 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('19 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('20 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('21 ends in 1 (translated)', $plurals));
+		$this->assertTrue(in_array('22 ends in 2 (translated)', $plurals));
+		$this->assertTrue(in_array('23 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('24 everything else (translated)', $plurals));
+		$this->assertTrue(in_array('25 everything else (translated)', $plurals));
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertEqual('Plural Rule 14 (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertTrue(in_array('0 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('1 ends in 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('2 ends in 2 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('3 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('4 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('5 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('6 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('7 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('8 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('9 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('10 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('11 ends in 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('12 ends in 2 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('13 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('14 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('15 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('16 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('17 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('18 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('19 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('20 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('21 ends in 1 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('22 ends in 2 (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('23 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('24 everything else (from core translated)', $corePlurals));
+		$this->assertTrue(in_array('25 everything else (from core translated)', $corePlurals));
+	}
+
+/**
+ * testSetLanguageWithSession method
+ *
+ * @access public
+ * @return void
+ */
+	function testSetLanguageWithSession () {
+		$_SESSION['Config']['language'] = 'po';
+		$singular = $this->__singular();
+		$this->assertEqual('Po (translated)', $singular);
+
+		$plurals = $this->__plural();
+		$this->assertTrue(in_array('0 everything else (po translated)', $plurals));
+		$this->assertTrue(in_array('1 is 1 (po translated)', $plurals));
+		$this->assertTrue(in_array('2 is 2-4 (po translated)', $plurals));
+		$this->assertTrue(in_array('3 is 2-4 (po translated)', $plurals));
+		$this->assertTrue(in_array('4 is 2-4 (po translated)', $plurals));
+		$this->assertTrue(in_array('5 everything else (po translated)', $plurals));
+		$this->assertTrue(in_array('6 everything else (po translated)', $plurals));
+		$this->assertTrue(in_array('7 everything else (po translated)', $plurals));
+		$this->assertTrue(in_array('8 everything else (po translated)', $plurals));
+		$this->assertTrue(in_array('9 everything else (po translated)', $plurals));
+		$this->assertTrue(in_array('10 everything else (po translated)', $plurals));
+		$this->assertTrue(in_array('11 everything else (po translated)', $plurals));
+		$this->assertTrue(in_array('12 everything else (po translated)', $plurals));
+		$this->assertTrue(in_array('13 everything else (po translated)', $plurals));
+		$this->assertTrue(in_array('14 everything else (po translated)', $plurals));
+		$this->assertTrue(in_array('15 everything else (po translated)', $plurals));
+		$this->assertTrue(in_array('16 everything else (po translated)', $plurals));
+		$this->assertTrue(in_array('17 everything else (po translated)', $plurals));
+		$this->assertTrue(in_array('18 everything else (po translated)', $plurals));
+		$this->assertTrue(in_array('19 everything else (po translated)', $plurals));
+		$this->assertTrue(in_array('20 everything else (po translated)', $plurals));
+		$this->assertTrue(in_array('21 everything else (po translated)', $plurals));
+		$this->assertTrue(in_array('22 everything else (po translated)', $plurals));
+		$this->assertTrue(in_array('23 everything else (po translated)', $plurals));
+		$this->assertTrue(in_array('24 everything else (po translated)', $plurals));
+		$this->assertTrue(in_array('25 everything else (po translated)', $plurals));
+		unset($_SESSION['Config']['language']);
+	}
+
+/**
+ * testNoCoreTranslation method
+ *
+ * @access public
+ * @return void
+ */
+	function testNoCoreTranslation () {
+		Configure::write('Config.language', 'po');
+		$singular = $this->__singular();
+		$this->assertEqual('Po (translated)', $singular);
+
+		$coreSingular = $this->__singularFromCore();
+		$this->assertNotEqual('Po (from core translated)', $coreSingular);
+
+		$corePlurals = $this->__pluralFromCore();
+		$this->assertFalse(in_array('0 everything else (from core translated)', $corePlurals));
+		$this->assertFalse(in_array('1 is 1 (from core translated)', $corePlurals));
+		$this->assertFalse(in_array('2 is 2-4 (from core translated)', $corePlurals));
+		$this->assertFalse(in_array('3 is 2-4 (from core translated)', $corePlurals));
+		$this->assertFalse(in_array('4 is 2-4 (from core translated)', $corePlurals));
+		$this->assertFalse(in_array('5 everything else (from core translated)', $corePlurals));
+		$this->assertFalse(in_array('6 everything else (from core translated)', $corePlurals));
+		$this->assertFalse(in_array('7 everything else (from core translated)', $corePlurals));
+		$this->assertFalse(in_array('8 everything else (from core translated)', $corePlurals));
+		$this->assertFalse(in_array('9 everything else (from core translated)', $corePlurals));
+		$this->assertFalse(in_array('10 everything else (from core translated)', $corePlurals));
+		$this->assertFalse(in_array('11 everything else (from core translated)', $corePlurals));
+		$this->assertFalse(in_array('12 everything else (from core translated)', $corePlurals));
+		$this->assertFalse(in_array('13 everything else (from core translated)', $corePlurals));
+		$this->assertFalse(in_array('14 everything else (from core translated)', $corePlurals));
+		$this->assertFalse(in_array('15 everything else (from core translated)', $corePlurals));
+		$this->assertFalse(in_array('16 everything else (from core translated)', $corePlurals));
+		$this->assertFalse(in_array('17 everything else (from core translated)', $corePlurals));
+		$this->assertFalse(in_array('18 everything else (from core translated)', $corePlurals));
+		$this->assertFalse(in_array('19 everything else (from core translated)', $corePlurals));
+		$this->assertFalse(in_array('20 everything else (from core translated)', $corePlurals));
+		$this->assertFalse(in_array('21 everything else (from core translated)', $corePlurals));
+		$this->assertFalse(in_array('22 everything else (from core translated)', $corePlurals));
+		$this->assertFalse(in_array('23 everything else (from core translated)', $corePlurals));
+		$this->assertFalse(in_array('24 everything else (from core translated)', $corePlurals));
+		$this->assertFalse(in_array('25 everything else (from core translated)', $corePlurals));
+	}
+
+/**
+ * testPluginTranslation method
+ *
+ * @access public
+ * @return void
+ */
+	function testPluginTranslation() {
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)
+		));
+
+		Configure::write('Config.language', 'po');
+		$singular = $this->__domainSingular();
+		$this->assertEqual('Plural Rule 1 (from plugin)', $singular);
+
+		$plurals = $this->__domainPlural();
+		$this->assertTrue(in_array('0 = 0 or > 1 (from plugin)', $plurals));
+		$this->assertTrue(in_array('1 = 1 (from plugin)', $plurals));
+		$this->assertTrue(in_array('2 = 0 or > 1 (from plugin)', $plurals));
+		$this->assertTrue(in_array('3 = 0 or > 1 (from plugin)', $plurals));
+		$this->assertTrue(in_array('4 = 0 or > 1 (from plugin)', $plurals));
+		$this->assertTrue(in_array('5 = 0 or > 1 (from plugin)', $plurals));
+		$this->assertTrue(in_array('6 = 0 or > 1 (from plugin)', $plurals));
+		$this->assertTrue(in_array('7 = 0 or > 1 (from plugin)', $plurals));
+		$this->assertTrue(in_array('8 = 0 or > 1 (from plugin)', $plurals));
+		$this->assertTrue(in_array('9 = 0 or > 1 (from plugin)', $plurals));
+		$this->assertTrue(in_array('10 = 0 or > 1 (from plugin)', $plurals));
+		$this->assertTrue(in_array('11 = 0 or > 1 (from plugin)', $plurals));
+		$this->assertTrue(in_array('12 = 0 or > 1 (from plugin)', $plurals));
+		$this->assertTrue(in_array('13 = 0 or > 1 (from plugin)', $plurals));
+		$this->assertTrue(in_array('14 = 0 or > 1 (from plugin)', $plurals));
+		$this->assertTrue(in_array('15 = 0 or > 1 (from plugin)', $plurals));
+		$this->assertTrue(in_array('16 = 0 or > 1 (from plugin)', $plurals));
+		$this->assertTrue(in_array('17 = 0 or > 1 (from plugin)', $plurals));
+		$this->assertTrue(in_array('18 = 0 or > 1 (from plugin)', $plurals));
+		$this->assertTrue(in_array('19 = 0 or > 1 (from plugin)', $plurals));
+		$this->assertTrue(in_array('20 = 0 or > 1 (from plugin)', $plurals));
+		$this->assertTrue(in_array('21 = 0 or > 1 (from plugin)', $plurals));
+		$this->assertTrue(in_array('22 = 0 or > 1 (from plugin)', $plurals));
+		$this->assertTrue(in_array('23 = 0 or > 1 (from plugin)', $plurals));
+		$this->assertTrue(in_array('24 = 0 or > 1 (from plugin)', $plurals));
+		$this->assertTrue(in_array('25 = 0 or > 1 (from plugin)', $plurals));
+	}
+
+/**
+ * testPoMultipleLineTranslation method
+ *
+ * @access public
+ * @return void
+ */
+	function testPoMultipleLineTranslation () {
+		Configure::write('Config.language', 'po');
+
+		$string = "This is a multiline translation\n";
+		$string .= "broken up over multiple lines.\n";
+		$string .= "This is the third line.\n";
+		$string .= "This is the forth line.";
+		$result = __($string, true);
+
+		$expected = "This is a multiline translation\n";
+		$expected .= "broken up over multiple lines.\n";
+		$expected .= "This is the third line.\n";
+		$expected .= "This is the forth line. (translated)";
+		$this->assertEqual($result, $expected);
+
+		// Windows Newline is \r\n
+		$string = "This is a multiline translation\r\n";
+		$string .= "broken up over multiple lines.\r\n";
+		$string .= "This is the third line.\r\n";
+		$string .= "This is the forth line.";
+		$result = __($string, true);
+		$this->assertEqual($result, $expected);
+
+		$singular = "valid\nsecond line";
+		$plural = "valids\nsecond line";
+
+		$result = __n($singular, $plural, 1, true);
+		$expected = "v\nsecond line";
+		$this->assertEqual($result, $expected);
+
+		$result = __n($singular, $plural, 2, true);
+		$expected = "vs\nsecond line";
+		$this->assertEqual($result, $expected);
+
+		$string = "This is a multiline translation\n";
+		$string .= "broken up over multiple lines.\n";
+		$string .= "This is the third line.\n";
+		$string .= "This is the forth line.";
+
+		$singular = "%d = 1\n" . $string;
+		$plural = "%d = 0 or > 1\n" . $string;
+
+		$result = __n($singular, $plural, 1, true);
+		$expected = "%d is 1\n" . $string;
+		$this->assertEqual($result, $expected);
+
+		$result = __n($singular, $plural, 2, true);
+		$expected = "%d is 2-4\n" . $string;
+		$this->assertEqual($result, $expected);
+
+		// Windows Newline is \r\n
+		$string = "This is a multiline translation\r\n";
+		$string .= "broken up over multiple lines.\r\n";
+		$string .= "This is the third line.\r\n";
+		$string .= "This is the forth line.";
+
+		$singular = "%d = 1\r\n" . $string;
+		$plural = "%d = 0 or > 1\r\n" . $string;
+
+		$result = __n($singular, $plural, 1, true);
+		$expected = "%d is 1\n" . str_replace("\r\n", "\n", $string);
+		$this->assertEqual($result, $expected);
+
+		$result = __n($singular, $plural, 2, true);
+		$expected = "%d is 2-4\n" . str_replace("\r\n", "\n", $string);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testPoNoTranslationNeeded method
+ *
+ * @access public
+ * @return void
+ */
+	function testPoNoTranslationNeeded () {
+		Configure::write('Config.language', 'po');
+		$result = __('No Translation needed', true);
+		$this->assertEqual($result, 'No Translation needed');
+	}
+
+/**
+ * testPoQuotedString method
+ *
+ * @access public
+ * @return void
+ */
+	function testPoQuotedString () {
+		$expected = 'this is a "quoted string" (translated)';
+		$this->assertEqual(__('this is a "quoted string"', true), $expected);
+	}
+
+/**
+ * testFloatValue method
+ *
+ * @access public
+ * @return void
+ */
+	function testFloatValue() {
+		Configure::write('Config.language', 'rule_9_po');
+
+		$result = __n('%d = 1', '%d = 0 or > 1', (float)1, true);
+		$expected = '%d is 1 (translated)';
+		$this->assertEqual($result, $expected);
+
+		$result = __n('%d = 1', '%d = 0 or > 1', (float)2, true);
+		$expected = "%d ends in 2-4, not 12-14 (translated)";
+		$this->assertEqual($result, $expected);
+
+		$result = __n('%d = 1', '%d = 0 or > 1', (float)5, true);
+		$expected = "%d everything else (translated)";
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testCategory method
+ *
+ * @access public
+ * @return void
+ */
+	function testCategory() {
+		Configure::write('Config.language', 'po');
+		$category = $this->__category();
+		$this->assertEqual('Monetary Po (translated)', $category);
+	}
+
+/**
+ * testPluginCategory method
+ *
+ * @access public
+ * @return void
+ */
+	function testPluginCategory() {
+		Configure::write('Config.language', 'po');
+
+		$singular = $this->__domainCategorySingular();
+		$this->assertEqual('Monetary Plural Rule 1 (from plugin)', $singular);
+
+		$plurals = $this->__domainCategoryPlural();
+		$this->assertTrue(in_array('Monetary 0 = 0 or > 1 (from plugin)', $plurals));
+		$this->assertTrue(in_array('Monetary 1 = 1 (from plugin)', $plurals));
+	}
+
+/**
+ * testCategoryThenSingular method
+ *
+ * @access public
+ * @return void
+ */
+	function testCategoryThenSingular() {
+		Configure::write('Config.language', 'po');
+		$category = $this->__category();
+		$this->assertEqual('Monetary Po (translated)', $category);
+
+		$singular = $this->__singular();
+		$this->assertEqual('Po (translated)', $singular);
+	}
+
+	function testTimeDefinition() {
+		Configure::write('Config.language', 'po');
+		$result = __c('d_fmt', 5, true);
+		$expected = '%m/%d/%Y';
+		$this->assertEqual($result, $expected);
+
+		$result = __c('am_pm', 5, true);
+		$expected = array('AM', 'PM');
+		$this->assertEqual($result, $expected);
+
+		$result = __c('abmon', 5, true);
+		$expected = array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
+		$this->assertEqual($result, $expected);
+	}
+
+	function testTimeDefinitionJapanese(){
+		Configure::write('Config.language', 'ja_jp');
+		$result = __c('d_fmt', 5, true);
+		
+		$expected = "%Y年%m月%d日";
+		
+		$this->assertEqual($result, $expected);
+
+		$result = __c('am_pm', 5, true);
+		$expected = array("午前", "午後");
+		$this->assertEqual($result, $expected);
+
+		$result = __c('abmon', 5, true);
+		$expected = array(" 1月", " 2月", " 3月", " 4月", " 5月", " 6月", " 7月", " 8月", " 9月", "10月", "11月", "12月");
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * Singular method
+ *
+ * @access private
+ * @return void
+ */
+	function __domainCategorySingular($domain = 'test_plugin', $category = 3) {
+		$singular = __dc($domain, 'Plural Rule 1', $category, true);
+		return $singular;
+	}
+
+/**
+ * Plural method
+ *
+ * @access private
+ * @return void
+ */
+	function __domainCategoryPlural($domain = 'test_plugin', $category = 3) {
+		$plurals = array();
+		for ($number = 0; $number <= 25; $number++) {
+			$plurals[] =  sprintf(__dcn($domain, '%d = 1', '%d = 0 or > 1', (float)$number, $category, true), (float)$number);
+		}
+		return $plurals;
+	}
+
+/**
+ * Singular method
+ *
+ * @access private
+ * @return void
+ */
+	function __domainSingular($domain = 'test_plugin') {
+		$singular = __d($domain, 'Plural Rule 1', true);
+		return $singular;
+	}
+
+/**
+ * Plural method
+ *
+ * @access private
+ * @return void
+ */
+	function __domainPlural($domain = 'test_plugin') {
+		$plurals = array();
+		for ($number = 0; $number <= 25; $number++) {
+			$plurals[] =  sprintf(__dn($domain, '%d = 1', '%d = 0 or > 1', (float)$number, true), (float)$number );
+		}
+		return $plurals;
+	}
+
+/**
+ * category method
+ *
+ * @access private
+ * @return void
+ */
+	function __category($category = 3) {
+		$singular = __c('Plural Rule 1', $category, true);
+		return $singular;
+	}
+
+/**
+ * Singular method
+ *
+ * @access private
+ * @return void
+ */
+	function __singular() {
+		$singular = __('Plural Rule 1', true);
+		return $singular;
+	}
+
+/**
+ * Plural method
+ *
+ * @access private
+ * @return void
+ */
+	function __plural() {
+		$plurals = array();
+		for ($number = 0; $number <= 25; $number++) {
+			$plurals[] =  sprintf(__n('%d = 1', '%d = 0 or > 1', (float)$number, true), (float)$number );
+		}
+		return $plurals;
+	}
+
+/**
+ * singularFromCore method
+ *
+ * @access private
+ * @return void
+ */
+	function __singularFromCore() {
+		$singular = __('Plural Rule 1 (from core)', true);
+		return $singular;
+	}
+
+/**
+ * pluralFromCore method
+ *
+ * @access private
+ * @return void
+ */
+	function __pluralFromCore() {
+		$plurals = array();
+		for ($number = 0; $number <= 25; $number++) {
+			$plurals[] =  sprintf(__n('%d = 1 (from core)', '%d = 0 or > 1 (from core)', (float)$number, true), (float)$number );
+		}
+		return $plurals;
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/inflector.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/inflector.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/inflector.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,476 @@
+<?php
+/**
+ * InflectorTest
+ *
+ * InflectorTest is used to test cases on the Inflector class
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The Open Group Test Suite License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing
+ * @package       cake.tests
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       Open Group Test Suite License (http://www.opensource.org/licenses/opengroup.php)
+ */
+
+/**
+ * Included libraries.
+ *
+ */
+App::import('Core', 'Inflector');
+
+/**
+ * Short description for class.
+ *
+ * @package		  cake.tests
+ * @subpackage	  cake.tests.cases.libs
+ */
+class InflectorTest extends CakeTestCase {
+
+/**
+ * Inflector property
+ *
+ * @var mixed null
+ * @access public
+ */
+	var $Inflector = null;
+
+/**
+ * testInstantiation method
+ *
+ * @access public
+ * @return void
+ */
+	function testInstantiation() {
+		$Inflector =& Inflector::getInstance();
+		$this->assertEqual(Inflector::getInstance(), $Inflector);
+	}
+
+/**
+ * testInflectingSingulars method
+ *
+ * @access public
+ * @return void
+ */
+	function testInflectingSingulars() {
+		$this->assertEqual(Inflector::singularize('categorias'), 'categoria');
+		$this->assertEqual(Inflector::singularize('menus'), 'menu');
+		$this->assertEqual(Inflector::singularize('news'), 'news');
+		$this->assertEqual(Inflector::singularize('food_menus'), 'food_menu');
+		$this->assertEqual(Inflector::singularize('Menus'), 'Menu');
+		$this->assertEqual(Inflector::singularize('FoodMenus'), 'FoodMenu');
+		$this->assertEqual(Inflector::singularize('houses'), 'house');
+		$this->assertEqual(Inflector::singularize('powerhouses'), 'powerhouse');
+		$this->assertEqual(Inflector::singularize('quizzes'), 'quiz');
+		$this->assertEqual(Inflector::singularize('Buses'), 'Bus');
+		$this->assertEqual(Inflector::singularize('buses'), 'bus');
+		$this->assertEqual(Inflector::singularize('matrix_rows'), 'matrix_row');
+		$this->assertEqual(Inflector::singularize('matrices'), 'matrix');
+		$this->assertEqual(Inflector::singularize('vertices'), 'vertex');
+		$this->assertEqual(Inflector::singularize('indices'), 'index');
+		$this->assertEqual(Inflector::singularize('Aliases'), 'Alias');
+		$this->assertEqual(Inflector::singularize('Alias'), 'Alias');
+		$this->assertEqual(Inflector::singularize('Media'), 'Media');
+		$this->assertEqual(Inflector::singularize('NodeMedia'), 'NodeMedia');
+		$this->assertEqual(Inflector::singularize('alumni'), 'alumnus');
+		$this->assertEqual(Inflector::singularize('bacilli'), 'bacillus');
+		$this->assertEqual(Inflector::singularize('cacti'), 'cactus');
+		$this->assertEqual(Inflector::singularize('foci'), 'focus');
+		$this->assertEqual(Inflector::singularize('fungi'), 'fungus');
+		$this->assertEqual(Inflector::singularize('nuclei'), 'nucleus');
+		$this->assertEqual(Inflector::singularize('octopuses'), 'octopus');
+		$this->assertEqual(Inflector::singularize('radii'), 'radius');
+		$this->assertEqual(Inflector::singularize('stimuli'), 'stimulus');
+		$this->assertEqual(Inflector::singularize('syllabi'), 'syllabus');
+		$this->assertEqual(Inflector::singularize('termini'), 'terminus');
+		$this->assertEqual(Inflector::singularize('viri'), 'virus');
+		$this->assertEqual(Inflector::singularize('people'), 'person');
+		$this->assertEqual(Inflector::singularize('gloves'), 'glove');
+		$this->assertEqual(Inflector::singularize('doves'), 'dove');
+		$this->assertEqual(Inflector::singularize('lives'), 'life');
+		$this->assertEqual(Inflector::singularize('knives'), 'knife');
+		$this->assertEqual(Inflector::singularize('wolves'), 'wolf');
+		$this->assertEqual(Inflector::singularize('slaves'), 'slave');
+		$this->assertEqual(Inflector::singularize('shelves'), 'shelf');
+		$this->assertEqual(Inflector::singularize('taxis'), 'taxi');
+		$this->assertEqual(Inflector::singularize('taxes'), 'tax');
+		$this->assertEqual(Inflector::singularize('Taxes'), 'Tax');
+		$this->assertEqual(Inflector::singularize('AwesomeTaxes'), 'AwesomeTax');
+		$this->assertEqual(Inflector::singularize('faxes'), 'fax');
+		$this->assertEqual(Inflector::singularize('waxes'), 'wax');
+		$this->assertEqual(Inflector::singularize('niches'), 'niche');
+		$this->assertEqual(Inflector::singularize('waves'), 'wave');
+		$this->assertEqual(Inflector::singularize('bureaus'), 'bureau');
+		$this->assertEqual(Inflector::singularize('genetic_analyses'), 'genetic_analysis');
+		$this->assertEqual(Inflector::singularize('doctor_diagnoses'), 'doctor_diagnosis');
+		$this->assertEqual(Inflector::singularize('parantheses'), 'paranthesis');
+		$this->assertEqual(Inflector::singularize('Causes'), 'Cause');
+		$this->assertEqual(Inflector::singularize('colossuses'), 'colossus');
+		$this->assertEqual(Inflector::singularize('diagnoses'), 'diagnosis');
+		$this->assertEqual(Inflector::singularize('bases'), 'basis');
+		$this->assertEqual(Inflector::singularize('analyses'), 'analysis');
+		$this->assertEqual(Inflector::singularize('curves'), 'curve');
+		$this->assertEqual(Inflector::singularize('cafes'), 'cafe');
+		$this->assertEqual(Inflector::singularize('roofs'), 'roof');
+		$this->assertEqual(Inflector::singularize('foes'), 'foe');
+
+		$this->assertEqual(Inflector::singularize(''), '');
+	}
+
+/**
+ * testInflectingPlurals method
+ *
+ * @access public
+ * @return void
+ */
+	function testInflectingPlurals() {
+		$this->assertEqual(Inflector::pluralize('categoria'), 'categorias');
+		$this->assertEqual(Inflector::pluralize('house'), 'houses');
+		$this->assertEqual(Inflector::pluralize('powerhouse'), 'powerhouses');
+		$this->assertEqual(Inflector::pluralize('Bus'), 'Buses');
+		$this->assertEqual(Inflector::pluralize('bus'), 'buses');
+		$this->assertEqual(Inflector::pluralize('menu'), 'menus');
+		$this->assertEqual(Inflector::pluralize('news'), 'news');
+		$this->assertEqual(Inflector::pluralize('food_menu'), 'food_menus');
+		$this->assertEqual(Inflector::pluralize('Menu'), 'Menus');
+		$this->assertEqual(Inflector::pluralize('FoodMenu'), 'FoodMenus');
+		$this->assertEqual(Inflector::pluralize('quiz'), 'quizzes');
+		$this->assertEqual(Inflector::pluralize('matrix_row'), 'matrix_rows');
+		$this->assertEqual(Inflector::pluralize('matrix'), 'matrices');
+		$this->assertEqual(Inflector::pluralize('vertex'), 'vertices');
+		$this->assertEqual(Inflector::pluralize('index'), 'indices');
+		$this->assertEqual(Inflector::pluralize('Alias'), 'Aliases');
+		$this->assertEqual(Inflector::pluralize('Aliases'), 'Aliases');
+		$this->assertEqual(Inflector::pluralize('Media'), 'Media');
+		$this->assertEqual(Inflector::pluralize('NodeMedia'), 'NodeMedia');
+		$this->assertEqual(Inflector::pluralize('alumnus'), 'alumni');
+		$this->assertEqual(Inflector::pluralize('bacillus'), 'bacilli');
+		$this->assertEqual(Inflector::pluralize('cactus'), 'cacti');
+		$this->assertEqual(Inflector::pluralize('focus'), 'foci');
+		$this->assertEqual(Inflector::pluralize('fungus'), 'fungi');
+		$this->assertEqual(Inflector::pluralize('nucleus'), 'nuclei');
+		$this->assertEqual(Inflector::pluralize('octopus'), 'octopuses');
+		$this->assertEqual(Inflector::pluralize('radius'), 'radii');
+		$this->assertEqual(Inflector::pluralize('stimulus'), 'stimuli');
+		$this->assertEqual(Inflector::pluralize('syllabus'), 'syllabi');
+		$this->assertEqual(Inflector::pluralize('terminus'), 'termini');
+		$this->assertEqual(Inflector::pluralize('virus'), 'viri');
+		$this->assertEqual(Inflector::pluralize('person'), 'people');
+		$this->assertEqual(Inflector::pluralize('people'), 'people');
+		$this->assertEqual(Inflector::pluralize('glove'), 'gloves');
+		$this->assertEqual(Inflector::pluralize('crisis'), 'crises');
+		$this->assertEqual(Inflector::pluralize('tax'), 'taxes');
+		$this->assertEqual(Inflector::pluralize('wave'), 'waves');
+		$this->assertEqual(Inflector::pluralize('bureau'), 'bureaus');
+		$this->assertEqual(Inflector::pluralize('cafe'), 'cafes');
+		$this->assertEqual(Inflector::pluralize('roof'), 'roofs');
+		$this->assertEqual(Inflector::pluralize('foe'), 'foes');
+		$this->assertEqual(Inflector::pluralize(''), '');
+	}
+
+/**
+ * testInflectorSlug method
+ *
+ * @access public
+ * @return void
+ */
+	function testInflectorSlug() {
+		$result = Inflector::slug('Foo Bar: Not just for breakfast any-more');
+		$expected = 'Foo_Bar_Not_just_for_breakfast_any_more';
+		$this->assertEqual($result, $expected);
+
+		$result = Inflector::slug('this/is/a/path');
+		$expected = 'this_is_a_path';
+		$this->assertEqual($result, $expected);
+
+		$result = Inflector::slug('Foo Bar: Not just for breakfast any-more', "-");
+		$expected = 'Foo-Bar-Not-just-for-breakfast-any-more';
+		$this->assertEqual($result, $expected);
+
+		$result = Inflector::slug('Foo Bar: Not just for breakfast any-more', "+");
+		$expected = 'Foo+Bar+Not+just+for+breakfast+any+more';
+		$this->assertEqual($result, $expected);
+
+		$result = Inflector::slug('Äpfel Über Öl grün ärgert groß öko', '-');
+		$expected = 'Aepfel-Ueber-Oel-gruen-aergert-gross-oeko';
+		$this->assertEqual($result, $expected);
+
+		$result = Inflector::slug('The truth - and- more- news', '-');
+		$expected = 'The-truth-and-more-news';
+		$this->assertEqual($result, $expected);
+
+		$result = Inflector::slug('The truth: and more news', '-');
+		$expected = 'The-truth-and-more-news';
+		$this->assertEqual($result, $expected);
+
+		$result = Inflector::slug('La langue française est un attribut de souveraineté en France', '-');
+		$expected = 'La-langue-francaise-est-un-attribut-de-souverainete-en-France';
+		$this->assertEqual($result, $expected);
+
+		$result = Inflector::slug('!@$#exciting stuff! - what !@-# was that?', '-');
+		$expected = 'exciting-stuff-what-was-that';
+		$this->assertEqual($result, $expected);
+
+		$result = Inflector::slug('20% of profits went to me!', '-');
+		$expected = '20-of-profits-went-to-me';
+		$this->assertEqual($result, $expected);
+
+		$result = Inflector::slug('#this melts your face1#2#3', '-');
+		$expected = 'this-melts-your-face1-2-3';
+		$this->assertEqual($result, $expected);
+
+		$result = Inflector::slug('controller/action/りんご/1');
+		$expected = 'controller_action_りんご_1';
+		$this->assertEqual($result, $expected);
+
+		$result = Inflector::slug('の話が出たので大丈夫かなあと');
+		$expected = 'の話が出たので大丈夫かなあと';
+		$this->assertEqual($result, $expected);
+
+		$result = Inflector::slug('posts/view/한국어/page:1/sort:asc');
+		$expected = 'posts_view_한국어_page_1_sort_asc';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testInflectorSlugWithMap method
+ *
+ * @access public
+ * @return void
+ */
+	function testInflectorSlugWithMap() {
+		$result = Inflector::slug('replace every r', array('/r/' => '1'));
+		$expected = '1eplace_eve1y_1';
+		$this->assertEqual($result, $expected);
+
+		$result = Inflector::slug('replace every r', '_', array('/r/' => '1'));
+		$expected = '1eplace_eve1y_1';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testInflectorSlugWithMapOverridingDefault method
+ *
+ * @access public
+ * @return void
+ */
+	function testInflectorSlugWithMapOverridingDefault() {
+		$result = Inflector::slug('Testing æ ø å', '-', array('/å/' => 'aa', '/ø/' => 'oe'));
+		$expected = 'Testing-ae-oe-aa';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testInflectorUnderscore method
+ *
+ * @return void
+ * @access public
+ */
+	function testInflectorUnderscore() {
+		$this->assertIdentical(Inflector::underscore('TestThing'), 'test_thing');
+		$this->assertIdentical(Inflector::underscore('testThing'), 'test_thing');
+		$this->assertIdentical(Inflector::underscore('TestThingExtra'), 'test_thing_extra');
+		$this->assertIdentical(Inflector::underscore('testThingExtra'), 'test_thing_extra');
+
+		// Identical checks test the cache code path.
+		$this->assertIdentical(Inflector::underscore('TestThing'), 'test_thing');
+		$this->assertIdentical(Inflector::underscore('testThing'), 'test_thing');
+		$this->assertIdentical(Inflector::underscore('TestThingExtra'), 'test_thing_extra');
+		$this->assertIdentical(Inflector::underscore('testThingExtra'), 'test_thing_extra');
+
+		// Test stupid values
+		$this->assertIdentical(Inflector::underscore(''), '');
+		$this->assertIdentical(Inflector::underscore(0), '0');
+		$this->assertIdentical(Inflector::underscore(false), '');
+	}
+
+/**
+ * testVariableNaming method
+ *
+ * @access public
+ * @return void
+ */
+	function testVariableNaming() {
+		$this->assertEqual(Inflector::variable('test_field'), 'testField');
+		$this->assertEqual(Inflector::variable('test_fieLd'), 'testFieLd');
+		$this->assertEqual(Inflector::variable('test field'), 'testField');
+		$this->assertEqual(Inflector::variable('Test_field'), 'testField');
+	}
+
+/**
+ * testClassNaming method
+ *
+ * @access public
+ * @return void
+ */
+	function testClassNaming() {
+		$this->assertEqual(Inflector::classify('artists_genres'), 'ArtistsGenre');
+		$this->assertEqual(Inflector::classify('file_systems'), 'FileSystem');
+		$this->assertEqual(Inflector::classify('news'), 'News');
+		$this->assertEqual(Inflector::classify('bureaus'), 'Bureau');
+	}
+
+/**
+ * testTableNaming method
+ *
+ * @access public
+ * @return void
+ */
+	function testTableNaming() {
+		$this->assertEqual(Inflector::tableize('ArtistsGenre'), 'artists_genres');
+		$this->assertEqual(Inflector::tableize('FileSystem'), 'file_systems');
+		$this->assertEqual(Inflector::tableize('News'), 'news');
+		$this->assertEqual(Inflector::tableize('Bureau'), 'bureaus');
+	}
+
+/**
+ * testHumanization method
+ *
+ * @access public
+ * @return void
+ */
+	function testHumanization() {
+		$this->assertEqual(Inflector::humanize('posts'), 'Posts');
+		$this->assertEqual(Inflector::humanize('posts_tags'), 'Posts Tags');
+		$this->assertEqual(Inflector::humanize('file_systems'), 'File Systems');
+	}
+
+/**
+ * This test if run in isolation should not cause errors in PHP4.
+ *
+ * @return void
+ */
+	function testRulesNoErrorPHP4() {
+		Inflector::rules('plural', array(
+			'rules' => array(),
+			'irregular' => array(),
+			'uninflected' => array('pays')
+		));
+	}
+
+/**
+ * testCustomPluralRule method
+ *
+ * @access public
+ * @return void
+ */
+	function testCustomPluralRule() {
+		Inflector::rules('plural', array('/^(custom)$/i' => '\1izables'));
+		$this->assertEqual(Inflector::pluralize('custom'), 'customizables');
+
+		Inflector::rules('plural', array('uninflected' => array('uninflectable')));
+		$this->assertEqual(Inflector::pluralize('uninflectable'), 'uninflectable');
+
+		Inflector::rules('plural', array(
+			'rules' => array('/^(alert)$/i' => '\1ables'),
+			'uninflected' => array('noflect', 'abtuse'),
+			'irregular' => array('amaze' => 'amazable', 'phone' => 'phonezes')
+		));
+		$this->assertEqual(Inflector::pluralize('noflect'), 'noflect');
+		$this->assertEqual(Inflector::pluralize('abtuse'), 'abtuse');
+		$this->assertEqual(Inflector::pluralize('alert'), 'alertables');
+		$this->assertEqual(Inflector::pluralize('amaze'), 'amazable');
+		$this->assertEqual(Inflector::pluralize('phone'), 'phonezes');
+	}
+
+/**
+ * testCustomSingularRule method
+ *
+ * @access public
+ * @return void
+ */
+	function testCustomSingularRule() {
+		Inflector::rules('singular', array('/(eple)r$/i' => '\1', '/(jente)r$/i' => '\1'));
+
+		$this->assertEqual(Inflector::singularize('epler'), 'eple');
+		$this->assertEqual(Inflector::singularize('jenter'), 'jente');
+
+		Inflector::rules('singular', array(
+			'rules' => array('/^(bil)er$/i' => '\1', '/^(inflec|contribu)tors$/i' => '\1ta'),
+			'uninflected' => array('singulars'),
+			'irregular' => array('spins' => 'spinor')
+		));
+
+		$this->assertEqual(Inflector::singularize('inflectors'), 'inflecta');
+		$this->assertEqual(Inflector::singularize('contributors'), 'contributa');
+		$this->assertEqual(Inflector::singularize('spins'), 'spinor');
+		$this->assertEqual(Inflector::singularize('singulars'), 'singulars');
+	}
+
+/**
+ * testCustomTransliterationRule method
+ *
+ * @access public
+ * @return void
+ */
+	function testCustomTransliterationRule() {
+		$this->assertEqual(Inflector::slug('Testing æ ø å'), 'Testing_ae_o_a');
+
+		Inflector::rules('transliteration', array('/å/' => 'aa', '/ø/' => 'oe'));
+		$this->assertEqual(Inflector::slug('Testing æ ø å'), 'Testing_ae_oe_aa');
+
+		Inflector::rules('transliteration', array('/ä|æ/' => 'ae', '/å/' => 'aa'), true);
+		$this->assertEqual(Inflector::slug('Testing æ ø å'), 'Testing_ae_ø_aa');
+
+		$this->assertEqual(Inflector::slug('Testing æ ø å', '-', array('/ø/' => 'oe')), 'Testing-ae-oe-aa');
+	}
+
+/**
+ * test that setting new rules clears the inflector caches.
+ *
+ * @return void
+ */
+	function testRulesClearsCaches() {
+		$this->assertEqual(Inflector::singularize('Bananas'), 'Banana');
+		$this->assertEqual(Inflector::tableize('Banana'), 'bananas');
+		$this->assertEqual(Inflector::pluralize('Banana'), 'Bananas');
+
+		Inflector::rules('singular', array(
+			'rules' => array('/(.*)nas$/i' => '\1zzz')
+		));
+		$this->assertEqual(Inflector::singularize('Bananas'), 'Banazzz', 'Was inflected with old rules. %s');
+
+		Inflector::rules('plural', array(
+			'rules' => array('/(.*)na$/i' => '\1zzz'),
+			'irregular' => array('corpus' => 'corpora')
+		));
+		$this->assertEqual(Inflector::pluralize('Banana'), 'Banazzz', 'Was inflected with old rules: %s');
+		$this->assertEqual(Inflector::pluralize('corpus'), 'corpora', 'Was inflected with old irregular form: %s');
+	}
+
+/**
+ * Test resetting inflection rules.
+ *
+ * @return void
+ */
+	function testCustomRuleWithReset() {
+		$uninflected = array('atlas', 'lapis', 'onibus', 'pires', 'virus', '.*x');
+		$pluralIrregular = array('as' => 'ases');
+
+		Inflector::rules('singular', array(
+			'rules' => array('/^(.*)(a|e|o|u)is$/i' => '\1\2l'),
+			'uninflected' => $uninflected,
+		), true);
+
+		Inflector::rules('plural', array(
+			'rules' => array(
+				'/^(.*)(a|e|o|u)l$/i' => '\1\2is',
+			),
+			'uninflected' => $uninflected,
+			'irregular' => $pluralIrregular
+		), true);
+
+		$this->assertEqual(Inflector::pluralize('Alcool'), 'Alcoois');
+		$this->assertEqual(Inflector::pluralize('Atlas'), 'Atlas');
+		$this->assertEqual(Inflector::singularize('Alcoois'), 'Alcool');
+		$this->assertEqual(Inflector::singularize('Atlas'), 'Atlas');
+	}
+
+}

Added: trunk/src/Web/cake/tests/cases/libs/l10n.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/l10n.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/l10n.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,958 @@
+<?php
+/**
+ * L10nTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.5432
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', 'l10n');
+
+/**
+ * L10nTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class L10nTest extends CakeTestCase {
+
+/**
+ * testGet method
+ *
+ * @access public
+ * @return void
+ */
+	function testGet() {
+		$l10n =& new L10n();
+
+		// Catalog Entry
+		$l10n->get('en');
+
+		$this->assertEqual($l10n->language, 'English');
+		$this->assertEqual($l10n->languagePath, array('eng', 'eng'));
+		$this->assertEqual($l10n->locale, 'eng');
+
+		// Map Entry
+		$l10n->get('eng');
+
+		$this->assertEqual($l10n->language, 'English');
+		$this->assertEqual($l10n->languagePath, array('eng', 'eng'));
+		$this->assertEqual($l10n->locale, 'eng');
+
+		// Catalog Entry
+		$l10n->get('en-ca');
+
+		$this->assertEqual($l10n->language, 'English (Canadian)');
+		$this->assertEqual($l10n->languagePath, array('en_ca', 'eng'));
+		$this->assertEqual($l10n->locale, 'en_ca');
+
+		// Default Entry
+		define('DEFAULT_LANGUAGE', 'en-us');
+
+		$l10n->get('use_default');
+
+		$this->assertEqual($l10n->language, 'English (United States)');
+		$this->assertEqual($l10n->languagePath, array('en_us', 'eng'));
+		$this->assertEqual($l10n->locale, 'en_us');
+
+		$l10n->get('es');
+		$l10n->get('');
+		$this->assertEqual($l10n->lang, 'en-us');
+
+
+		// Using $this->default
+		$l10n = new L10n();
+
+		$l10n->get('use_default');
+		$this->assertEqual($l10n->language, 'English (United States)');
+		$this->assertEqual($l10n->languagePath, array('en_us', 'eng', 'eng'));
+		$this->assertEqual($l10n->locale, 'en_us');
+	}
+
+/**
+ * testGetAutoLanguage method
+ *
+ * @access public
+ * @return void
+ */
+	function testGetAutoLanguage() {
+		$__SERVER = $_SERVER;
+		$_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'inexistent,en-ca';
+
+		$l10n =& new L10n();
+		$l10n->get();
+
+		$this->assertEqual($l10n->language, 'English (Canadian)');
+		$this->assertEqual($l10n->languagePath, array('en_ca', 'eng', 'eng'));
+		$this->assertEqual($l10n->locale, 'en_ca');
+
+		$_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'es_mx';
+		$l10n->get();
+
+		$this->assertEqual($l10n->language, 'Spanish (Mexican)');
+		$this->assertEqual($l10n->languagePath, array('es_mx', 'spa', 'eng'));
+		$this->assertEqual($l10n->locale, 'es_mx');
+
+		$_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'en_xy,en_ca';
+		$l10n->get();
+
+		$this->assertEqual($l10n->language, 'English');
+		$this->assertEqual($l10n->languagePath, array('eng', 'eng', 'eng'));
+		$this->assertEqual($l10n->locale, 'eng');
+
+		$_SERVER = $__SERVER;
+	}
+
+/**
+ * testMap method
+ *
+ * @access public
+ * @return void
+ */
+	function testMap() {
+		$l10n =& new L10n();
+
+		$result = $l10n->map(array('afr', 'af'));
+		$expected = array('afr' => 'af', 'af' => 'afr');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('alb', 'sq'));
+		$expected = array('alb' => 'sq', 'sq' => 'alb');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('ara', 'ar'));
+		$expected = array('ara' => 'ar', 'ar' => 'ara');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('hye', 'hy'));
+		$expected = array('hye' => 'hy', 'hy' => 'hye');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('baq', 'eu'));
+		$expected = array('baq' => 'eu', 'eu' => 'baq');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('baq', 'eu'));
+		$expected = array('baq' => 'eu', 'eu' => 'baq');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('bos', 'bs'));
+		$expected = array('bos' => 'bs', 'bs' => 'bos');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('bul', 'bg'));
+		$expected = array('bul' => 'bg', 'bg' => 'bul');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('bel', 'be'));
+		$expected = array('bel' => 'be', 'be' => 'bel');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('cat', 'ca'));
+		$expected = array('cat' => 'ca', 'ca' => 'cat');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('chi', 'zh'));
+		$expected = array('chi' => 'zh', 'zh' => 'chi');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('zho', 'zh'));
+		$expected = array('zho' => 'zh', 'zh' => 'chi');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('hrv', 'hr'));
+		$expected = array('hrv' => 'hr', 'hr' => 'hrv');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('ces', 'cs'));
+		$expected = array('ces' => 'cs', 'cs' => 'cze');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('cze', 'cs'));
+		$expected = array('cze' => 'cs', 'cs' => 'cze');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('dan', 'da'));
+		$expected = array('dan' => 'da', 'da' => 'dan');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('dut', 'nl'));
+		$expected = array('dut' => 'nl', 'nl' => 'dut');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('nld', 'nl'));
+		$expected = array('nld' => 'nl', 'nl' => 'dut');
+		$this->assertEqual($result, $expected);
+		
+		$result = $l10n->map(array('nld'));
+		$expected = array('nld' => 'nl');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('eng', 'en'));
+		$expected = array('eng' => 'en', 'en' => 'eng');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('est', 'et'));
+		$expected = array('est' => 'et', 'et' => 'est');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('fao', 'fo'));
+		$expected = array('fao' => 'fo', 'fo' => 'fao');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('fas', 'fa'));
+		$expected = array('fas' => 'fa', 'fa' => 'fas');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('per', 'fa'));
+		$expected = array('per' => 'fa', 'fa' => 'fas');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('fin', 'fi'));
+		$expected = array('fin' => 'fi', 'fi' => 'fin');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('fra', 'fr'));
+		$expected = array('fra' => 'fr', 'fr' => 'fre');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('fre', 'fr'));
+		$expected = array('fre' => 'fr', 'fr' => 'fre');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('gla', 'gd'));
+		$expected = array('gla' => 'gd', 'gd' => 'gla');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('glg', 'gl'));
+		$expected = array('glg' => 'gl', 'gl' => 'glg');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('deu', 'de'));
+		$expected = array('deu' => 'de', 'de' => 'deu');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('ger', 'de'));
+		$expected = array('ger' => 'de', 'de' => 'deu');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('ell', 'el'));
+		$expected = array('ell' => 'el', 'el' => 'gre');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('gre', 'el'));
+		$expected = array('gre' => 'el', 'el' => 'gre');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('heb', 'he'));
+		$expected = array('heb' => 'he', 'he' => 'heb');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('hin', 'hi'));
+		$expected = array('hin' => 'hi', 'hi' => 'hin');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('hun', 'hu'));
+		$expected = array('hun' => 'hu', 'hu' => 'hun');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('ice', 'is'));
+		$expected = array('ice' => 'is', 'is' => 'ice');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('isl', 'is'));
+		$expected = array('isl' => 'is', 'is' => 'ice');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('ind', 'id'));
+		$expected = array('ind' => 'id', 'id' => 'ind');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('gle', 'ga'));
+		$expected = array('gle' => 'ga', 'ga' => 'gle');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('ita', 'it'));
+		$expected = array('ita' => 'it', 'it' => 'ita');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('jpn', 'ja'));
+		$expected = array('jpn' => 'ja', 'ja' => 'jpn');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('kor', 'ko'));
+		$expected = array('kor' => 'ko', 'ko' => 'kor');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('lav', 'lv'));
+		$expected = array('lav' => 'lv', 'lv' => 'lav');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('lit', 'lt'));
+		$expected = array('lit' => 'lt', 'lt' => 'lit');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('mac', 'mk'));
+		$expected = array('mac' => 'mk', 'mk' => 'mac');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('mkd', 'mk'));
+		$expected = array('mkd' => 'mk', 'mk' => 'mac');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('may', 'ms'));
+		$expected = array('may' => 'ms', 'ms' => 'may');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('msa', 'ms'));
+		$expected = array('msa' => 'ms', 'ms' => 'may');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('mlt', 'mt'));
+		$expected = array('mlt' => 'mt', 'mt' => 'mlt');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('nor', 'no'));
+		$expected = array('nor' => 'no', 'no' => 'nor');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('nob', 'nb'));
+		$expected = array('nob' => 'nb', 'nb' => 'nob');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('nno', 'nn'));
+		$expected = array('nno' => 'nn', 'nn' => 'nno');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('pol', 'pl'));
+		$expected = array('pol' => 'pl', 'pl' => 'pol');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('por', 'pt'));
+		$expected = array('por' => 'pt', 'pt' => 'por');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('roh', 'rm'));
+		$expected = array('roh' => 'rm', 'rm' => 'roh');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('ron', 'ro'));
+		$expected = array('ron' => 'ro', 'ro' => 'rum');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('rum', 'ro'));
+		$expected = array('rum' => 'ro', 'ro' => 'rum');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('rus', 'ru'));
+		$expected = array('rus' => 'ru', 'ru' => 'rus');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('smi', 'sz'));
+		$expected = array('smi' => 'sz', 'sz' => 'smi');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('scc', 'sr'));
+		$expected = array('scc' => 'sr', 'sr' => 'scc');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('srp', 'sr'));
+		$expected = array('srp' => 'sr', 'sr' => 'scc');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('slk', 'sk'));
+		$expected = array('slk' => 'sk', 'sk' => 'slo');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('slo', 'sk'));
+		$expected = array('slo' => 'sk', 'sk' => 'slo');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('slv', 'sl'));
+		$expected = array('slv' => 'sl', 'sl' => 'slv');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('wen', 'sb'));
+		$expected = array('wen' => 'sb', 'sb' => 'wen');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('spa', 'es'));
+		$expected = array('spa' => 'es', 'es' => 'spa');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('swe', 'sv'));
+		$expected = array('swe' => 'sv', 'sv' => 'swe');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('tha', 'th'));
+		$expected = array('tha' => 'th', 'th' => 'tha');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('tso', 'ts'));
+		$expected = array('tso' => 'ts', 'ts' => 'tso');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('tsn', 'tn'));
+		$expected = array('tsn' => 'tn', 'tn' => 'tsn');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('tur', 'tr'));
+		$expected = array('tur' => 'tr', 'tr' => 'tur');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('ukr', 'uk'));
+		$expected = array('ukr' => 'uk', 'uk' => 'ukr');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('urd', 'ur'));
+		$expected = array('urd' => 'ur', 'ur' => 'urd');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('ven', 've'));
+		$expected = array('ven' => 've', 've' => 'ven');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('vie', 'vi'));
+		$expected = array('vie' => 'vi', 'vi' => 'vie');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('xho', 'xh'));
+		$expected = array('xho' => 'xh', 'xh' => 'xho');
+		$this->assertEqual($result, $expected);
+	
+		$result = $l10n->map(array('cy', 'cym'));
+		$expected = array('cym' => 'cy', 'cy' => 'cym');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('yid', 'yi'));
+		$expected = array('yid' => 'yi', 'yi' => 'yid');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->map(array('zul', 'zu'));
+		$expected = array('zul' => 'zu', 'zu' => 'zul');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testCatalog method
+ *
+ * @access public
+ * @return void
+ */
+	function testCatalog() {
+		$l10n =& new L10n();
+
+		$result = $l10n->catalog(array('af'));
+		$expected = array(
+			'af' => array('language' => 'Afrikaans', 'locale' => 'afr', 'localeFallback' => 'afr', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('ar', 'ar-ae', 'ar-bh', 'ar-dz', 'ar-eg', 'ar-iq', 'ar-jo', 'ar-kw', 'ar-lb', 'ar-ly', 'ar-ma',
+			'ar-om', 'ar-qa', 'ar-sa', 'ar-sy', 'ar-tn', 'ar-ye'));
+		$expected = array(
+			'ar' => array('language' => 'Arabic', 'locale' => 'ara', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+			'ar-ae' => array('language' => 'Arabic (U.A.E.)', 'locale' => 'ar_ae', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+			'ar-bh' => array('language' => 'Arabic (Bahrain)', 'locale' => 'ar_bh', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+			'ar-dz' => array('language' => 'Arabic (Algeria)', 'locale' => 'ar_dz', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+			'ar-eg' => array('language' => 'Arabic (Egypt)', 'locale' => 'ar_eg', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+			'ar-iq' => array('language' => 'Arabic (Iraq)', 'locale' => 'ar_iq', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+			'ar-jo' => array('language' => 'Arabic (Jordan)', 'locale' => 'ar_jo', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+			'ar-kw' => array('language' => 'Arabic (Kuwait)', 'locale' => 'ar_kw', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+			'ar-lb' => array('language' => 'Arabic (Lebanon)', 'locale' => 'ar_lb', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+			'ar-ly' => array('language' => 'Arabic (Libya)', 'locale' => 'ar_ly', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+			'ar-ma' => array('language' => 'Arabic (Morocco)', 'locale' => 'ar_ma', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+			'ar-om' => array('language' => 'Arabic (Oman)', 'locale' => 'ar_om', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+			'ar-qa' => array('language' => 'Arabic (Qatar)', 'locale' => 'ar_qa', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+			'ar-sa' => array('language' => 'Arabic (Saudi Arabia)', 'locale' => 'ar_sa', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+			'ar-sy' => array('language' => 'Arabic (Syria)', 'locale' => 'ar_sy', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+			'ar-tn' => array('language' => 'Arabic (Tunisia)', 'locale' => 'ar_tn', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+			'ar-ye' => array('language' => 'Arabic (Yemen)', 'locale' => 'ar_ye', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('be'));
+		$expected = array(
+			'be' => array('language' => 'Byelorussian', 'locale' => 'bel', 'localeFallback' => 'bel', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('bg'));
+		$expected = array(
+			'bg' => array('language' => 'Bulgarian', 'locale' => 'bul', 'localeFallback' => 'bul', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('bs'));
+		$expected = array(
+			'bs' => array('language' => 'Bosnian', 'locale' => 'bos', 'localeFallback' => 'bos', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('ca'));
+		$expected = array(
+			'ca' => array('language' => 'Catalan', 'locale' => 'cat', 'localeFallback' => 'cat', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('cs'));
+		$expected = array(
+			'cs' => array('language' => 'Czech', 'locale' => 'cze', 'localeFallback' => 'cze', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('da'));
+		$expected = array(
+			'da' => array('language' => 'Danish', 'locale' => 'dan', 'localeFallback' => 'dan', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('de', 'de-at', 'de-ch', 'de-de', 'de-li', 'de-lu'));
+		$expected = array(
+			'de' => array('language' => 'German (Standard)', 'locale' => 'deu', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'de-at' => array('language' => 'German (Austria)', 'locale' => 'de_at', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'de-ch' => array('language' => 'German (Swiss)', 'locale' => 'de_ch', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'de-de' => array('language' => 'German (Germany)', 'locale' => 'de_de', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'de-li' => array('language' => 'German (Liechtenstein)', 'locale' => 'de_li', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'de-lu' => array('language' => 'German (Luxembourg)', 'locale' => 'de_lu', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('e', 'el'));
+		$expected = array(
+			'e' => array('language' => 'Greek', 'locale' => 'gre', 'localeFallback' => 'gre', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'el' => array('language' => 'Greek', 'locale' => 'gre', 'localeFallback' => 'gre', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('en', 'en-au', 'en-bz', 'en-ca', 'en-gb', 'en-ie', 'en-jm', 'en-nz', 'en-tt', 'en-us', 'en-za'));
+		$expected = array(
+			'en' => array('language' => 'English', 'locale' => 'eng', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'en-au' => array('language' => 'English (Australian)', 'locale' => 'en_au', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'en-bz' => array('language' => 'English (Belize)', 'locale' => 'en_bz', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'en-ca' => array('language' => 'English (Canadian)', 'locale' => 'en_ca', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'en-gb' => array('language' => 'English (British)', 'locale' => 'en_gb', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'en-ie' => array('language' => 'English (Ireland)', 'locale' => 'en_ie', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'en-jm' => array('language' => 'English (Jamaica)', 'locale' => 'en_jm', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'en-nz' => array('language' => 'English (New Zealand)', 'locale' => 'en_nz', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'en-tt' => array('language' => 'English (Trinidad)', 'locale' => 'en_tt', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'en-us' => array('language' => 'English (United States)', 'locale' => 'en_us', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'en-za' => array('language' => 'English (South Africa)', 'locale' => 'en_za', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('es', 'es-ar', 'es-bo', 'es-cl', 'es-co', 'es-cr', 'es-do', 'es-ec', 'es-es', 'es-gt', 'es-hn',
+			'es-mx', 'es-ni', 'es-pa', 'es-pe', 'es-pr', 'es-py', 'es-sv', 'es-uy', 'es-ve'));
+		$expected = array(
+			'es' => array('language' => 'Spanish (Spain - Traditional)', 'locale' => 'spa', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'es-ar' => array('language' => 'Spanish (Argentina)', 'locale' => 'es_ar', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'es-bo' => array('language' => 'Spanish (Bolivia)', 'locale' => 'es_bo', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'es-cl' => array('language' => 'Spanish (Chile)', 'locale' => 'es_cl', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'es-co' => array('language' => 'Spanish (Colombia)', 'locale' => 'es_co', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'es-cr' => array('language' => 'Spanish (Costa Rica)', 'locale' => 'es_cr', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'es-do' => array('language' => 'Spanish (Dominican Republic)', 'locale' => 'es_do', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'es-ec' => array('language' => 'Spanish (Ecuador)', 'locale' => 'es_ec', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'es-es' => array('language' => 'Spanish (Spain)', 'locale' => 'es_es', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'es-gt' => array('language' => 'Spanish (Guatemala)', 'locale' => 'es_gt', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'es-hn' => array('language' => 'Spanish (Honduras)', 'locale' => 'es_hn', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'es-mx' => array('language' => 'Spanish (Mexican)', 'locale' => 'es_mx', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'es-ni' => array('language' => 'Spanish (Nicaragua)', 'locale' => 'es_ni', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'es-pa' => array('language' => 'Spanish (Panama)', 'locale' => 'es_pa', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'es-pe' => array('language' => 'Spanish (Peru)', 'locale' => 'es_pe', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'es-pr' => array('language' => 'Spanish (Puerto Rico)', 'locale' => 'es_pr', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'es-py' => array('language' => 'Spanish (Paraguay)', 'locale' => 'es_py', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'es-sv' => array('language' => 'Spanish (El Salvador)', 'locale' => 'es_sv', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'es-uy' => array('language' => 'Spanish (Uruguay)', 'locale' => 'es_uy', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'es-ve' => array('language' => 'Spanish (Venezuela)', 'locale' => 'es_ve', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('et'));
+		$expected = array(
+			'et' => array('language' => 'Estonian', 'locale' => 'est', 'localeFallback' => 'est', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('eu'));
+		$expected = array(
+			'eu' => array('language' => 'Basque', 'locale' => 'baq', 'localeFallback' => 'baq', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('fa'));
+		$expected = array(
+			'fa' => array('language' => 'Farsi', 'locale' => 'per', 'localeFallback' => 'per', 'charset' => 'utf-8', 'direction' => 'rtl')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('fi'));
+		$expected = array(
+			'fi' => array('language' => 'Finnish', 'locale' => 'fin', 'localeFallback' => 'fin', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('fo'));
+		$expected = array(
+			'fo' => array('language' => 'Faeroese', 'locale' => 'fao', 'localeFallback' => 'fao', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('fr', 'fr-be', 'fr-ca', 'fr-ch', 'fr-fr', 'fr-lu'));
+		$expected = array(
+			'fr' => array('language' => 'French (Standard)', 'locale' => 'fre', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'fr-be' => array('language' => 'French (Belgium)', 'locale' => 'fr_be', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'fr-ca' => array('language' => 'French (Canadian)', 'locale' => 'fr_ca', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'fr-ch' => array('language' => 'French (Swiss)', 'locale' => 'fr_ch', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'fr-fr' => array('language' => 'French (France)', 'locale' => 'fr_fr', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'fr-lu' => array('language' => 'French (Luxembourg)', 'locale' => 'fr_lu', 'localeFallback' => 'fre', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('ga'));
+		$expected = array(
+			'ga' => array('language' => 'Irish', 'locale' => 'gle', 'localeFallback' => 'gle', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('gd', 'gd-ie'));
+		$expected = array(
+			'gd' => array('language' => 'Gaelic (Scots)', 'locale' => 'gla', 'localeFallback' => 'gla', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'gd-ie' => array('language' => 'Gaelic (Irish)', 'locale' => 'gd_ie', 'localeFallback' => 'gla', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('gl'));
+		$expected = array(
+			'gl' => array('language' => 'Galician', 'locale' => 'glg', 'localeFallback' => 'glg', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('he'));
+		$expected = array(
+			'he' => array('language' => 'Hebrew', 'locale' => 'heb', 'localeFallback' => 'heb', 'charset' => 'utf-8', 'direction' => 'rtl')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('hi'));
+		$expected = array(
+			'hi' => array('language' => 'Hindi', 'locale' => 'hin', 'localeFallback' => 'hin', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('hr'));
+		$expected = array(
+			'hr' => array('language' => 'Croatian', 'locale' => 'hrv', 'localeFallback' => 'hrv', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('hu'));
+		$expected = array(
+			'hu' => array('language' => 'Hungarian', 'locale' => 'hun', 'localeFallback' => 'hun', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('hy'));
+		$expected = array(
+			'hy' => array('language' => 'Armenian - Armenia', 'locale' => 'hye', 'localeFallback' => 'hye', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('id', 'in'));
+		$expected = array(
+			'id' => array('language' => 'Indonesian', 'locale' => 'ind', 'localeFallback' => 'ind', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'in' => array('language' => 'Indonesian', 'locale' => 'ind', 'localeFallback' => 'ind', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('is'));
+		$expected = array(
+			'is' => array('language' => 'Icelandic', 'locale' => 'ice', 'localeFallback' => 'ice', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('it', 'it-ch'));
+		$expected = array(
+			'it' => array('language' => 'Italian', 'locale' => 'ita', 'localeFallback' => 'ita', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'it-ch' => array('language' => 'Italian (Swiss) ', 'locale' => 'it_ch', 'localeFallback' => 'ita', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('ja'));
+		$expected = array(
+			'ja' => array('language' => 'Japanese', 'locale' => 'jpn', 'localeFallback' => 'jpn', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('ko', 'ko-kp', 'ko-kr'));
+		$expected = array(
+			'ko' => array('language' => 'Korean', 'locale' => 'kor', 'localeFallback' => 'kor', 'charset' => 'kr', 'direction' => 'ltr'),
+			'ko-kp' => array('language' => 'Korea (North)', 'locale' => 'ko_kp', 'localeFallback' => 'kor', 'charset' => 'kr', 'direction' => 'ltr'),
+			'ko-kr' => array('language' => 'Korea (South)', 'locale' => 'ko_kr', 'localeFallback' => 'kor', 'charset' => 'kr', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('koi8-r', 'ru', 'ru-mo'));
+		$expected = array(
+			'koi8-r' => array('language' => 'Russian', 'locale' => 'koi8_r', 'localeFallback' => 'rus', 'charset' => 'koi8-r', 'direction' => 'ltr'),
+			'ru' => array('language' => 'Russian', 'locale' => 'rus', 'localeFallback' => 'rus', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'ru-mo' => array('language' => 'Russian (Moldavia)', 'locale' => 'ru_mo', 'localeFallback' => 'rus', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('lt'));
+		$expected = array(
+			'lt' => array('language' => 'Lithuanian', 'locale' => 'lit', 'localeFallback' => 'lit', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('lv'));
+		$expected = array(
+			'lv' => array('language' => 'Latvian', 'locale' => 'lav', 'localeFallback' => 'lav', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('mk', 'mk-mk'));
+		$expected = array(
+			'mk' => array('language' => 'FYRO Macedonian', 'locale' => 'mk', 'localeFallback' => 'mac', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'mk-mk' => array('language' => 'Macedonian', 'locale' => 'mk_mk', 'localeFallback' => 'mac', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('ms'));
+		$expected = array(
+			'ms' => array('language' => 'Malaysian', 'locale' => 'may', 'localeFallback' => 'may', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('mt'));
+		$expected = array(
+			'mt' => array('language' => 'Maltese', 'locale' => 'mlt', 'localeFallback' => 'mlt', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('n', 'nl', 'nl-be'));
+		$expected = array(
+			'n' => array('language' => 'Dutch (Standard)', 'locale' => 'dut', 'localeFallback' => 'dut', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'nl' => array('language' => 'Dutch (Standard)', 'locale' => 'dut', 'localeFallback' => 'dut', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'nl-be' => array('language' => 'Dutch (Belgium)', 'locale' => 'nl_be', 'localeFallback' => 'dut', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog('nl');
+		$expected = array('language' => 'Dutch (Standard)', 'locale' => 'dut', 'localeFallback' => 'dut', 'charset' => 'utf-8', 'direction' => 'ltr');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog('nld');
+		$expected = array('language' => 'Dutch (Standard)', 'locale' => 'dut', 'localeFallback' => 'dut', 'charset' => 'utf-8', 'direction' => 'ltr');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog('dut');
+		$expected = array('language' => 'Dutch (Standard)', 'locale' => 'dut', 'localeFallback' => 'dut', 'charset' => 'utf-8', 'direction' => 'ltr');
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('nb'));
+		$expected = array(
+			'nb' => array('language' => 'Norwegian Bokmal', 'locale' => 'nob', 'localeFallback' => 'nor', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('nn', 'no'));
+		$expected = array(
+			'nn' => array('language' => 'Norwegian Nynorsk', 'locale' => 'nno', 'localeFallback' => 'nor', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'no' => array('language' => 'Norwegian', 'locale' => 'nor', 'localeFallback' => 'nor', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('p', 'pl'));
+		$expected = array(
+			'p' => array('language' => 'Polish', 'locale' => 'pol', 'localeFallback' => 'pol', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'pl' => array('language' => 'Polish', 'locale' => 'pol', 'localeFallback' => 'pol', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('pt', 'pt-br'));
+		$expected = array(
+			'pt' => array('language' => 'Portuguese (Portugal)', 'locale' => 'por', 'localeFallback' => 'por', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'pt-br' => array('language' => 'Portuguese (Brazil)', 'locale' => 'pt_br', 'localeFallback' => 'por', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('rm'));
+		$expected = array(
+			'rm' => array('language' => 'Rhaeto-Romanic', 'locale' => 'roh', 'localeFallback' => 'roh', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('ro', 'ro-mo'));
+		$expected = array(
+			'ro' => array('language' => 'Romanian', 'locale' => 'rum', 'localeFallback' => 'rum', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'ro-mo' => array('language' => 'Romanian (Moldavia)', 'locale' => 'ro_mo', 'localeFallback' => 'rum', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('sb'));
+		$expected = array(
+			'sb' => array('language' => 'Sorbian', 'locale' => 'wen', 'localeFallback' => 'wen', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('sk'));
+		$expected = array(
+			'sk' => array('language' => 'Slovak', 'locale' => 'slo', 'localeFallback' => 'slo', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('sl'));
+		$expected = array(
+			'sl' => array('language' => 'Slovenian', 'locale' => 'slv', 'localeFallback' => 'slv', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('sq'));
+		$expected = array(
+			'sq' => array('language' => 'Albanian', 'locale' => 'alb', 'localeFallback' => 'alb', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('sr'));
+		$expected = array(
+			'sr' => array('language' => 'Serbian', 'locale' => 'scc', 'localeFallback' => 'scc', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('sv', 'sv-fi'));
+		$expected = array(
+			'sv' => array('language' => 'Swedish', 'locale' => 'swe', 'localeFallback' => 'swe', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'sv-fi' => array('language' => 'Swedish (Finland)', 'locale' => 'sv_fi', 'localeFallback' => 'swe', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('sx'));
+		$expected = array(
+			'sx' => array('language' => 'Sutu', 'locale' => 'sx', 'localeFallback' => 'sx', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('sz'));
+		$expected = array(
+			'sz' => array('language' => 'Sami (Lappish)', 'locale' => 'smi', 'localeFallback' => 'smi', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('th'));
+		$expected = array(
+			'th' => array('language' => 'Thai', 'locale' => 'tha', 'localeFallback' => 'tha', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('tn'));
+		$expected = array(
+			'tn' => array('language' => 'Tswana', 'locale' => 'tsn', 'localeFallback' => 'tsn', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('tr'));
+		$expected = array(
+			'tr' => array('language' => 'Turkish', 'locale' => 'tur', 'localeFallback' => 'tur', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('ts'));
+		$expected = array(
+			'ts' => array('language' => 'Tsonga', 'locale' => 'tso', 'localeFallback' => 'tso', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('uk'));
+		$expected = array(
+			'uk' => array('language' => 'Ukrainian', 'locale' => 'ukr', 'localeFallback' => 'ukr', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('ur'));
+		$expected = array(
+			'ur' => array('language' => 'Urdu', 'locale' => 'urd', 'localeFallback' => 'urd', 'charset' => 'utf-8', 'direction' => 'rtl')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('ve'));
+		$expected = array(
+			've' => array('language' => 'Venda', 'locale' => 'ven', 'localeFallback' => 'ven', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('vi'));
+		$expected = array(
+			'vi' => array('language' => 'Vietnamese', 'locale' => 'vie', 'localeFallback' => 'vie', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('cy'));
+		$expected = array(
+			'cy' => array('language' => 'Welsh', 'locale' => 'cym', 'localeFallback' => 'cym', 'charset' => 'utf-8',
+'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('xh'));
+		$expected = array(
+			'xh' => array('language' => 'Xhosa', 'locale' => 'xho', 'localeFallback' => 'xho', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('yi'));
+		$expected = array(
+			'yi' => array('language' => 'Yiddish', 'locale' => 'yid', 'localeFallback' => 'yid', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('zh', 'zh-cn', 'zh-hk', 'zh-sg', 'zh-tw'));
+		$expected = array(
+			'zh' => array('language' => 'Chinese', 'locale' => 'chi', 'localeFallback' => 'chi', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'zh-cn' => array('language' => 'Chinese (PRC)', 'locale' => 'zh_cn', 'localeFallback' => 'chi', 'charset' => 'GB2312', 'direction' => 'ltr'),
+			'zh-hk' => array('language' => 'Chinese (Hong Kong)', 'locale' => 'zh_hk', 'localeFallback' => 'chi', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'zh-sg' => array('language' => 'Chinese (Singapore)', 'locale' => 'zh_sg', 'localeFallback' => 'chi', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'zh-tw' => array('language' => 'Chinese (Taiwan)', 'locale' => 'zh_tw', 'localeFallback' => 'chi', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('zu'));
+		$expected = array(
+			'zu' => array('language' => 'Zulu', 'locale' => 'zul', 'localeFallback' => 'zul', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $l10n->catalog(array('en-nz', 'es-do', 'sz', 'ar-lb', 'zh-hk', 'pt-br'));
+		$expected = array(
+			'en-nz' => array('language' => 'English (New Zealand)', 'locale' => 'en_nz', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'es-do' => array('language' => 'Spanish (Dominican Republic)', 'locale' => 'es_do', 'localeFallback' => 'spa', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'sz' => array('language' => 'Sami (Lappish)', 'locale' => 'smi', 'localeFallback' => 'smi', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'ar-lb' => array('language' => 'Arabic (Lebanon)', 'locale' => 'ar_lb', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'),
+			'zh-hk' => array('language' => 'Chinese (Hong Kong)', 'locale' => 'zh_hk', 'localeFallback' => 'chi', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'pt-br' => array('language' => 'Portuguese (Brazil)', 'locale' => 'pt_br', 'localeFallback' => 'por', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+		
+		$result = $l10n->catalog(array('eng', 'deu', 'zho', 'rum', 'zul', 'yid'));
+		$expected = array(
+			'eng' => array('language' => 'English', 'locale' => 'eng', 'localeFallback' => 'eng', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'deu' => array('language' => 'German (Standard)', 'locale' => 'deu', 'localeFallback' => 'deu', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'zho' => array('language' => 'Chinese', 'locale' => 'chi', 'localeFallback' => 'chi', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'rum' => array('language' => 'Romanian', 'locale' => 'rum', 'localeFallback' => 'rum', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'zul' => array('language' => 'Zulu', 'locale' => 'zul', 'localeFallback' => 'zul', 'charset' => 'utf-8', 'direction' => 'ltr'),
+			'yid' => array('language' => 'Yiddish', 'locale' => 'yid', 'localeFallback' => 'yid', 'charset' => 'utf-8', 'direction' => 'ltr')
+		);
+		$this->assertEqual($result, $expected);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/log/file_log.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/log/file_log.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/log/file_log.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,79 @@
+<?php
+/**
+ * FileLogTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.log
+ * @since         CakePHP(tm) v 1.3
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+require_once LIBS . 'log' . DS . 'file_log.php';
+
+/**
+ * CakeLogTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class FileLogTest extends CakeTestCase {
+
+/**
+ * testLogFileWriting method
+ *
+ * @access public
+ * @return void
+ */
+	function testLogFileWriting() {
+		@unlink(LOGS . 'error.log');
+		$log =& new FileLog();
+		$log->write('warning', 'Test warning');
+		$this->assertTrue(file_exists(LOGS . 'error.log'));
+
+		$result = file_get_contents(LOGS . 'error.log');
+		$this->assertPattern('/^2[0-9]{3}-[0-9]+-[0-9]+ [0-9]+:[0-9]+:[0-9]+ Warning: Test warning/', $result);
+		unlink(LOGS . 'error.log');
+
+		@unlink(LOGS . 'debug.log');
+		$log->write('debug', 'Test warning');
+		$this->assertTrue(file_exists(LOGS . 'debug.log'));
+
+		$result = file_get_contents(LOGS . 'debug.log');
+		$this->assertPattern('/^2[0-9]{3}-[0-9]+-[0-9]+ [0-9]+:[0-9]+:[0-9]+ Debug: Test warning/', $result);
+		unlink(LOGS . 'debug.log');
+
+		@unlink(LOGS . 'random.log');
+		$log->write('random', 'Test warning');
+		$this->assertTrue(file_exists(LOGS . 'random.log'));
+
+		$result = file_get_contents(LOGS . 'random.log');
+		$this->assertPattern('/^2[0-9]{3}-[0-9]+-[0-9]+ [0-9]+:[0-9]+:[0-9]+ Random: Test warning/', $result);
+		unlink(LOGS . 'random.log');
+	}
+
+/**
+ * test using the path setting to write logs in other places.
+ *
+ * @return void
+ */
+	function testPathSetting() {
+		$path = TMP . 'tests' . DS;
+		@unlink($path . 'error.log');
+
+		$log =& new FileLog(compact('path'));
+		$log->write('warning', 'Test warning');
+		$this->assertTrue(file_exists($path . 'error.log'));
+		unlink($path . 'error.log');
+	}
+
+}

Added: trunk/src/Web/cake/tests/cases/libs/magic_db.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/magic_db.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/magic_db.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,203 @@
+<?php
+/**
+ * MagicDbTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+if (!class_exists('MagicDb')) {
+	require LIBS . 'magic_db.php';
+}
+/**
+ * The test class for the MagicDb class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class MagicDbTest extends UnitTestCase {
+/**
+ * The MagicDb instance to be tested
+ *
+ * @var MagicDb
+ * @access public
+ */
+	var $Db = null;
+/**
+ * Sets up a MagicDb class instance for testing
+ *
+ * @access public
+ */
+	function setUp() {
+		$this->Db =& new MagicDb();
+	}
+/**
+ * MagicDb::analyze should properly detect the file type and output additional info as requested.
+ *
+ * @access public
+ */
+	function testAnalyze() {
+		$r = $this->Db->read(MagicDbTestData::get('magic.db'));
+		$this->assertTrue($r === true);
+
+		$r = $this->Db->analyze(array());
+		$this->assertTrue($r === false);
+
+		$r = $this->Db->analyze(WWW_ROOT.'img'.DS.'cake.icon.gif');
+		// TODO: Check several serialized file samples for accurate detection
+	}
+/**
+ * MagicDb::read should properly read MagicDb databases from .php-/.db-files and plain data arguments passed in and return false if the file wasn't found or
+ * if the readed data did not validate.
+ *
+ * @access public
+ */
+	function testRead() {
+		$this->Db->db = array();
+
+		$r = $this->Db->read(true);
+		$this->assertTrue($r === false);
+		$r = $this->Db->read(5);
+		$this->assertTrue($r === false);
+
+		$this->Db->db = array('a');
+		$r = $this->Db->read(array('foo' => 'bar'));
+		$this->assertTrue($r === false);
+		$this->assertTrue($this->Db->db === array('a'));
+
+		$magicDb = array('header' => array(), 'database' => array());
+		$r = $this->Db->read($magicDb);
+		$this->assertTrue($r === true);
+		$this->assertTrue($this->Db->db === $magicDb);
+
+		// @TODO: Test parsing an actual magic.db file
+
+		$r = $this->Db->read('does-not-exist.db');
+		$this->assertTrue($r === false);
+		$this->assertTrue($this->Db->db === $magicDb);
+
+		if (file_exists(VENDORS.'magic.php')) {
+			$r = $this->Db->read(VENDORS.'magic.php');
+			$this->assertTrue($r === true);
+			$this->assertTrue($this->Db->db === array('header' => array(), 'database' => array()));
+		}
+
+		$r = $this->Db->read(MagicDbTestData::get('magic.snippet.db'));
+		$this->assertTrue($r === true);
+	}
+/**
+ * MagicDb::toArray should either return the MagicDb::db property, or the parsed array data if a magic.db dump is passed in as the first argument
+ *
+ * @access public
+ */
+	function testToArray() {
+		$this->Db->db = array();
+
+		$r = $this->Db->toArray();
+		$this->assertTrue($r === array());
+		$this->Db->db = array('foo' => 'bar');
+		$r = $this->Db->toArray();
+		$this->assertTrue($r === array('foo' => 'bar'));
+
+		$r = $this->Db->toArray(array('yeah'));
+		$this->assertTrue($r === array('yeah'));
+
+		$r = $this->Db->toArray("# FILE_ID DB\r\n# Date:2009-10-10\r\n# Source:xxx.php");
+		$this->assertTrue($r === array());
+
+		$r = $this->Db->toArray('foo');
+		$this->assertTrue($r === array());
+
+		$r = $this->Db->toArray(MagicDbTestData::get('magic.snippet.db'));
+		$this->assertTrue($r === MagicDbTestData::get('magic.snippet.db.result'));
+	}
+/**
+ * The MagicDb::validates function should return if the array passed to it or the local db property contains a valid MagicDb record set
+ *
+ * @access public
+ */
+	function testValidates() {
+		$r = $this->Db->validates(array());
+		$this->assertTrue($r === false);
+
+		$r = $this->Db->validates(array('header' => true, 'database' => true));
+		$this->assertTrue($r === false);
+		$magicDb = array('header' => array(), 'database' => array());
+		$r = $this->Db->validates($magicDb);
+		$this->assertTrue($r === true);
+
+		$this->Db->db = array();
+		$r = $this->Db->validates();
+		$this->assertTrue($r === false);
+
+		$this->Db->db = $magicDb;
+		$r = $this->Db->validates();
+		$this->assertTrue($r === true);
+	}
+}
+/**
+ * Test data holding object for MagicDb tests
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+/**
+ * MagicDbTestData class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class MagicDbTestData extends Object {
+/**
+ * Base64 encoded data
+ *
+ * @var array
+ * @access public
+ */
+	var $data = array(
+		'magic.snippet.db' => 'IyBGSUxFX0lEIERCDQojIERhdGU6MjAwNS0wMy0yOQ0KIyBTb3VyY2U6aHR0cDovL3d3dy5tYWdpY2RiLm9yZw0KDQojIE1hZ2ljIElEIGZvciBXb3JkcGVyZmVjdCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTQgYnkgQ2FybA0KMAlzdHJpbmcJXFx4RkZXUEMJW2ZpZD0wMDAwMDEwMDgtMDAtMDAwMDAwMTtleHQ9O21pbWU9O11Xb3JkcGVyZmVjdCBoZWxwIGZpbGUNCiY5CWJ5dGUJMHgwMgkNCj4xMCBieXRlCXgJLCB2ZXJzaW9uICVkDQo+MTEJYnl0ZQl4CS4lZA0KDQojIE1hZ2ljIElEIGZvciBXb3JkcGVyZmVjdCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTQgYnkgQ2FybA0KMAlzdHJpbmcJXFx4RkZXUEMJW2ZpZD0wMDAwMDEwMDgtMDAtMDAwMDAwMTtleHQ9O21pbWU9O11Xb3JkcGVyZmVjdCBhcHBsaWNhdGlvbiByZXNvdXJjZSBsaWJyYXJ5DQomOQlieXRlCTUxCQ0KPjEwCWJ5dGUJeAksIHZlcnNpb24gJWQNCj4xMQlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlcXHhGRldQQwlbZmlkPTAwMDAwMTAwOC0wMC0wMDAwMDAxO2V4dD07bWltZT07XVdvcmRwZXJmZWN0IGJsb2NrIGZpbGUNCiY5CWJ5dGUJMTMJDQo+MTAJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjExCWJ5dGUJeAkuJWQ=',
+		'magic.snippet.db.result' => 'YToyOntzOjY6ImhlYWRlciI7YToyOntzOjQ6IkRhdGUiO3M6MTA6IjIwMDUtMDMtMjkiO3M6NjoiU291cmNlIjtzOjIyOiJodHRwOi8vd3d3Lm1hZ2ljZGIub3JnIjt9czo4OiJkYXRhYmFzZSI7YToyOntpOjA7YTo0OntpOjA7YTo0OntpOjA7czoxOiIwIjtpOjE7czo2OiJzdHJpbmciO2k6MjtzOjg6IlxceEZGV1BDIjtpOjM7czo1OToiW2ZpZD0wMDAwMDEwMDgtMDAtMDAwMDAwMTtleHQ9O21pbWU9O11Xb3JkcGVyZmVjdCBoZWxwIGZpbGUiO31pOjE7YTo0OntpOjA7czoyOiImOSI7aToxO3M6NDoiYnl0ZSI7aToyO3M6NDoiMHgwMiI7aTozO3M6MDoiIjt9aToyO2E6Mzp7aTowO3M6ODoiPjEwIGJ5dGUiO2k6MTtzOjE6IngiO2k6MjtzOjEyOiIsIHZlcnNpb24gJWQiO31pOjM7YTo0OntpOjA7czozOiI+MTEiO2k6MTtzOjQ6ImJ5dGUiO2k6MjtzOjE6IngiO2k6MztzOjM6Ii4lZCI7fX1pOjE7YTo0OntpOjA7YTo0OntpOjA7czoxOiIwIjtpOjE7czo2OiJzdHJpbmciO2k6MjtzOjg6IlxceEZGV1BDIjtpOjM7czo3ODoiW2ZpZD0wMDAwMDEwMDgtMDAtMDAwMDAwMTtleHQ9O21pbWU9O11Xb3JkcGVyZmVjdCBhcHBsaWNhdGlvbiByZXNvdXJjZSBsaWJyYXJ5Ijt9aToxO2E6NDp7aTowO3M6MjoiJjkiO2k6MTtzOjQ6ImJ5dGUiO2k6MjtzOjI6IjUxIjtpOjM7czowOiIiO31pOjI7YTo0OntpOjA7czozOiI+MTAiO2k6MTtzOjQ6ImJ5dGUiO2k6MjtzOjE6IngiO
 2k6MztzOjEyOiIsIHZlcnNpb24gJWQiO31pOjM7YTo0OntpOjA7czozOiI+MTEiO2k6MTtzOjQ6ImJ5dGUiO2k6MjtzOjE6IngiO2k6MztzOjM6Ii4lZCI7fX19fQ==',
+		'magic.db' => 'IyBGSUxFX0lEIERCDQojIERhdGU6MjAwNS0wMy0yOQ0KIyBTb3VyY2U6aHR0cDovL3d3dy5tYWdpY2RiLm9yZw0KDQojIE1hZ2ljIElEIGZvciBXb3JkcGVyZmVjdCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTQgYnkgQ2FybA0KMAlzdHJpbmcJXFx4RkZXUEMJW2ZpZD0wMDAwMDEwMDgtMDAtMDAwMDAwMTtleHQ9O21pbWU9O11Xb3JkcGVyZmVjdCBoZWxwIGZpbGUNCiY5CWJ5dGUJMHgwMgkNCj4xMCBieXRlCXgJLCB2ZXJzaW9uICVkDQo+MTEJYnl0ZQl4CS4lZA0KDQojIE1hZ2ljIElEIGZvciBXb3JkcGVyZmVjdCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTQgYnkgQ2FybA0KMAlzdHJpbmcJXFx4RkZXUEMJW2ZpZD0wMDAwMDEwMDgtMDAtMDAwMDAwMTtleHQ9O21pbWU9O11Xb3JkcGVyZmVjdCBhcHBsaWNhdGlvbiByZXNvdXJjZSBsaWJyYXJ5DQomOQlieXRlCTUxCQ0KPjEwCWJ5dGUJeAksIHZlcnNpb24gJWQNCj4xMQlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlcXHhGRldQQwlbZmlkPTAwMDAwMTAwOC0wMC0wMDAwMDAxO2V4dD07bWltZT07XVdvcmRwZXJmZWN0IGJsb2NrIGZpbGUNCiY5CWJ5dGUJMTMJDQo+MTAJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjExCWJ5dGUJeAkuJWQNCg0KIyBNYWdpYyBJRCBmb3IgV29yZHBlcmZl
 Y3QgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE0IGJ5IENhcmwNCjAJc3RyaW5nCVxceEZGV1BDCVtmaWQ9MDAwMDAxMDA4LTAwLTAwMDAwMDE7ZXh0PTttaW1lPTtdV29yZHBlcmZlY3QgY29sdW1uIGJsb2NrDQomOQlieXRlCTE1CQ0KPjEwCWJ5dGUJeAksIHZlcnNpb24gJWQNCj4xMQlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlcXHhGRldQQwlbZmlkPTAwMDAwMTAwOC0wMC0wMDAwMDAxO2V4dD07bWltZT07XVdvcmRwZXJmZWN0IGRpY3Rpb25hcnkgZmlsZQ0KJjkJYnl0ZQkweDBCCQ0KPjEwCWJ5dGUJeAksIHZlcnNpb24gJWQNCj4xMQlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlcXHhGRldQQwlbZmlkPTAwMDAwMTAwOC0wMC0wMDAwMDAxO2V4dD07bWltZT07XVdvcmRwZXJmZWN0IGRpY3Rpb25hcnkgcnVsZXMgZmlsZQ0KJjkJYnl0ZQkzNAkNCj4xMAlieXRlCXgJLCB2ZXJzaW9uICVkDQo+MTEJYnl0ZQl4CS4lZA0KDQojIE1hZ2ljIElEIGZvciBXb3JkcGVyZmVjdCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTQgYnkgQ2FybA0KMAlzdHJpbmcJXFx4RkZXUEMJW2ZpZD0wMDAwMDEwMDgtMDAtMDAwMDAwMTtleHQ9O21pbWU9O11Xb
 3JkcGVyZmVjdCBleHRlcm5hbCBzcGVsbCBjb2RlIG1vZHVsZSBmaWxlDQomOQlieXRlCTQ2CQ0KPjEwCWJ5dGUJeAksIHZlcnNpb24gJWQNCj4xMQlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlcXHhGRldQQwlbZmlkPTAwMDAwMTAwOC0wMC0wMDAwMDAxO2V4dD07bWltZT07XVdvcmRwZXJmZWN0IGV4dGVybmFsIHNwZWxsIGRpY3Rpb25hcnkgZmlsZQ0KJjkJYnl0ZQk0NwkNCj4xMAlieXRlCXgJLCB2ZXJzaW9uICVkDQo+MTEJYnl0ZQl4CS4lZA0KDQojIE1hZ2ljIElEIGZvciBXb3JkcGVyZmVjdCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTQgYnkgQ2FybA0KMAlzdHJpbmcJXFx4RkZXUEMJW2ZpZD0wMDAwMDEwMDgtMDAtMDAwMDAwMTtleHQ9O21pbWU9O11Xb3JkcGVyZmVjdCBHcmFwaGljcyBzY3JlZW4gZHJpdmVyIGZpbGUNCiY5CWJ5dGUJMjYJDQo+MTAJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjExCWJ5dGUJeAkuJWQNCg0KIyBNYWdpYyBJRCBmb3IgV29yZHBlcmZlY3QgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE0IGJ5IENhcmwNCjAJc3RyaW5nCVxceEZGV1BDCVtmaWQ9MDAwMDAxMDA4LTAwLTAwMDAwMDE7ZXh0PTttaW1lPTtdV29yZHBlcmZlY3QgaHlwaGVuYXRpb24gY29kZSBtb2R1bGUgZmlsZQ0KJjkJYnl0ZQkyMwkNCj4xMAlieXRlCXgJLC
 B2ZXJzaW9uICVkDQo+MTEJYnl0ZQl4CS4lZA0KDQojIE1hZ2ljIElEIGZvciBXb3JkcGVyZmVjdCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTQgYnkgQ2FybA0KMAlzdHJpbmcJXFx4RkZXUEMJW2ZpZD0wMDAwMDEwMDgtMDAtMDAwMDAwMTtleHQ9O21pbWU9O11Xb3JkcGVyZmVjdCBoeXBoZW5hdGlvbiBkYXRhIG1vZHVsZSBmaWxlDQomOQlieXRlCTI0CQ0KPjEwCWJ5dGUJeAksIHZlcnNpb24gJWQNCj4xMQlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlcXHhGRldQQwlbZmlkPTAwMDAwMTAwOC0wMC0wMDAwMDAxO2V4dD07bWltZT07XVdvcmRwZXJmZWN0IGh5cGhlbmF0aW9uIGxleCBtb2R1bGUNCiY5CWJ5dGUJMjcJDQo+MTAJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjExCWJ5dGUJeAkuJWQNCg0KIyBNYWdpYyBJRCBmb3IgV29yZHBlcmZlY3QgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE0IGJ5IENhcmwNCjAJc3RyaW5nCVxceEZGV1BDCVtmaWQ9MDAwMDAxMDA4LTAwLTAwMDAwMDE7ZXh0PTttaW1lPTtdV29yZHBlcmZlY3QgaW5zdGFsbGF0aW9uIGluZm9ybWF0aW9uIGZpbGUNCiY5CWJ5dGUJNDEJDQo+MTAJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjExCWJ5dGUJeAkuJWQNCg0KIyBNYWdpYyBJRCBmb3IgV29yZHBlcmZlY3QgZmlsZXMuDQojIFN1Ym1pdHR
 lZCBvbiAyMDA0LTAyLTE0IGJ5IENhcmwNCjAJc3RyaW5nCVxceEZGV1BDCVtmaWQ9MDAwMDAxMDA4LTAwLTAwMDAwMDE7ZXh0PTttaW1lPTtdV29yZHBlcmZlY3Qga2V5Ym9hcmQgZGVmaW5pdGlvbiBmaWxlDQomOQlieXRlCTB4MDMJDQo+MTAJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjExCWJ5dGUJeAkuJWQNCg0KIyBNYWdpYyBJRCBmb3IgV29yZHBlcmZlY3QgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE0IGJ5IENhcmwNCjAJc3RyaW5nCVxceEZGV1BDCVtmaWQ9MDAwMDAxMDA4LTAwLTAwMDAwMDE7ZXh0PTttaW1lPTtdV29yZHBlcmZlY3QgbWFjcm8gZGF0YSBmaWxlDQomOQlieXRlCTB4MDEJDQo+MTAJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjExCWJ5dGUJeAkuJWQNCg0KIyBNYWdpYyBJRCBmb3IgV29yZHBlcmZlY3QgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE0IGJ5IENhcmwNCjAJc3RyaW5nCVxceEZGV1BDCVtmaWQ9MDAwMDAxMDA4LTAwLTAwMDAwMDE7ZXh0PTttaW1lPTtdV29yZHBlcmZlY3QgbWFjcm8gcmVzb3VyY2UgZmlsZQ0KJjkJYnl0ZQkyNQkNCj4xMAlieXRlCXgJLCB2ZXJzaW9uICVkDQo+MTEJYnl0ZQl4CS4lZA0KDQojIE1hZ2ljIElEIGZvciBXb3JkcGVyZmVjdCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTQgYnkgQ2FybA0KMAlzdHJpbmcJXFx4RkZXUEMJW2ZpZD0wMDAwMDEwMDgtMDAtMDAwMDAwMTtleHQ9O21pbWU9O11Xb3JkcGVyZmVj
 dCBwcmludGVyIFEgY29kZXMgKHVzZWQgYnkgVkFYL0RHKQ0KJjkJYnl0ZQkyOAkNCj4xMAlieXRlCXgJLCB2ZXJzaW9uICVkDQo+MTEJYnl0ZQl4CS4lZA0KDQojIE1hZ2ljIElEIGZvciBXb3JkcGVyZmVjdCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTQgYnkgQ2FybA0KMAlzdHJpbmcJXFx4RkZXUEMJW2ZpZD0wMDAwMDEwMDgtMDAtMDAwMDAwMTtleHQ9O21pbWU9O11Xb3JkcGVyZmVjdCByZWN0YW5ndWxhciBibG9jayBmaWxlDQomOQlieXRlCTE0CQ0KPjEwCWJ5dGUJeAksIHZlcnNpb24gJWQNCj4xMQlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlcXHhGRldQQwlbZmlkPTAwMDAwMTAwOC0wMC0wMDAwMDAxO2V4dD07bWltZT07XVdvcmRwZXJmZWN0IHNwZWxsIGNvZGUgbW9kdWxlIHJ1bGVzIGZpbGUNCiY5CWJ5dGUJMzMJDQo+MTAJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjExCWJ5dGUJeAkuJWQNCg0KIyBNYWdpYyBJRCBmb3IgV29yZHBlcmZlY3QgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE0IGJ5IENhcmwNCjAJc3RyaW5nCVxceEZGV1BDCVtmaWQ9MDAwMDAxMDA4LTAwLTAwMDAwMDE7ZXh0PTttaW1lPTtdV29yZHBlcmZlY3Qgc3BlbGwgY29kZSBtb2R1bGUgd29yZCBsaXN0DQomOQlieXRlCTI5CQ0KPjEwCWJ5dGUJeAksIHZlcnNpb24gJWQNCj4xM
 QlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlcXHhGRldQQwlbZmlkPTAwMDAwMTAwOC0wMC0wMDAwMDAxO2V4dD07bWltZT07XVdvcmRwZXJmZWN0IHRoZXNhcnVzIGZpbGUNCiY5CWJ5dGUJMTIJDQo+MTAJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjExCWJ5dGUJeAkuJWQNCg0KIyBNYWdpYyBJRCBmb3IgV29yZHBlcmZlY3QgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE0IGJ5IENhcmwNCjAJc3RyaW5nCVxceEZGV1BDCVtmaWQ9MDAwMDAxMDA4LTAwLTAwMDAwMDE7ZXh0PTttaW1lPTtdV29yZHBlcmZlY3QgVkFYIGtleWJvYXJkIGRlZmluaXRpb24gZmlsZQ0KJjkJYnl0ZQkweDA0CQ0KPjEwCWJ5dGUJeAksIHZlcnNpb24gJWQNCj4xMQlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlcXHhGRldQQwlbZmlkPTAwMDAwMTAwOC0wMC0wMDAwQUxMO2V4dD1hbGw7bWltZT07XVdvcmRwZXJmZWN0IHByaW50ZXIgcmVzb3VyY2UgZmlsZQ0KJjkJYnl0ZQkxOQkNCj4xMAlieXRlCXgJLCB2ZXJzaW9uICVkDQo+MTEJYnl0ZQl4CS4lZA0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTExIGJ5IENhcmwNCjAJc3RyaW5nCVJJRkYJW2ZpZD0wMDAwMDEwMDEtMDAtMDAwME
 NPTjtleHQ9Y29uO21pbWU9O11NaWNyb3NvZnQgQW5pbWF0ZWQgY3Vyc29yLCBsaXR0bGUtZW5kaWFuDQomOAlzdHJpbmcJQUNPTgkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQowCXN0cmluZwlSSUZYCVtmaWQ9MDAwMDAxMDAxLTAwLTAwMDBDT047ZXh0PWNvbjttaW1lPTtdTWljcm9zb2Z0IEFuaW1hdGVkIGN1cnNvciwgYmlnLWVuZGlhbg0KJjgJc3RyaW5nCUFDT04JDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlcXHhGRldQQwlbZmlkPTAwMDAwMTAwOC0wMC0wMDAwRE9DO2V4dD1kb2M7bWltZT07XU1hY2ludG9zaCBXb3JkcGVyZmVjdCBkb2N1bWVudCBmaWxlDQomOQlieXRlCTQ0CQ0KPjEwCWJ5dGUJeAksIHZlcnNpb24gJWQNCj4xMQlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlcXHhGRldQQwlbZmlkPTAwMDAwMTAwOC0wMC0wMDAwRE9DO2V4dD1kb2M7bWltZT07XVZBWCBXb3JkcGVyZmVjdCBkb2N1bWVudCBmaWxlDQomOQlieXRlCTQ1CQ0KPjEwCWJ5dGUJeAksIHZlcnNpb24gJWQNCj4xMQlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cml
 uZwlcXHhGRldQQwlbZmlkPTAwMDAwMTAwOC0wMC0wMDAwRFJTO2V4dD1kcnM7bWltZT07XVdvcmRwZXJmZWN0IGRpc3BsYXkgcmVzb3VyY2UgZmlsZQ0KJjkJYnl0ZQkyMAkNCj4xMAlieXRlCXgJLCB2ZXJzaW9uICVkDQo+MTEJYnl0ZQl4CS4lZA0KDQojIE1hZ2ljIElEIGZvciBXb3JkcGVyZmVjdCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTQgYnkgQ2FybA0KMAlzdHJpbmcJXFx4RkZXUEMJW2ZpZD0wMDAwMDEwMDgtMDAtMDAwMEZJTDtleHQ9ZmlsO21pbWU9O11Xb3JkcGVyZmVjdCBvdmVybGF5IGZpbGUNCiY5CWJ5dGUJMjEJDQo+MTAJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjExCWJ5dGUJeAkuJWQNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlQTUNDCVtmaWQ9MDAwMDAxMDAxLTAwLTAwMDBHUlA7ZXh0PWdycDttaW1lPTtdTWljcm9zb2Z0IHdpbmRvd3MgZ3JvdXAgZmlsZQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE0IGJ5IENhcmwNCjAJYmVzaG9ydAkweEUzMTAJW2ZpZD0wMDAwMDEwMDctMDAtMDAwSU5GTztleHQ9aW5mbzttaW1lPTtdQW1pZ2Egc2hvcnRjdXQgLyBpY29uIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgV29yZHBlcmZlY3QgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE0IGJ5IENhcmwNCjAJc3RyaW5nCVxceEZGV1BDCVtmaWQ9MDAwMDAxMDA4LTAwLTAwMDBJTlM7ZXh0PWluczttaW1lPTtdV29yZHBlcmZlY3Qg
 aW5zdGFsbGF0aW9uIGluZm9ybWF0aW9uIGZpbGUNCiY5CWJ5dGUJNDMJDQo+MTAJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjExCWJ5dGUJeAkuJWQNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCWxlbG9uZwkweDAwMDAwMDRDCVtmaWQ9MDAwMDAxMDAxLTAwLTAwMDBMTks7ZXh0PWxuazttaW1lPTtdTWljcm9zb2Z0IFdpbmRvd3Mgc2hvcnRjdXQgZmlsZQ0KJjQJc3RyaW5nCVxceDAxXFx4MTRcXHgwMgkNCg0KIyBNYWdpYyBJRCBmb3IgV29yZHBlcmZlY3QgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE0IGJ5IENhcmwNCjAJc3RyaW5nCVxceEZGV1BDCVtmaWQ9MDAwMDAxMDA4LTAwLTAwMDBQUlM7ZXh0PXByczttaW1lPTtdV29yZHBlcmZlY3QgcHJpbnRlciByZXNvdXJjZSBmaWxlDQomOQlieXRlCTE2CQ0KPjEwCWJ5dGUJeAksIHZlcnNpb24gJWQNCj4xMQlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlcXHhGRldQQwlbZmlkPTAwMDAwMTAwOC0wMC0wMDAwUVJTO2V4dD1xcnM7bWltZT07XVdvcmRwZXJmZWN0IDUuMSBlcXVhdGlvbiByZXNvdXJjZSBmaWxlDQomOQlieXRlCTMwCQ0KPjEwCWJ5dGUJeAksIHZlcnNpb24gJWQNCj4xMQlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gM
 jAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlcXHhGRldQQwlbZmlkPTAwMDAwMTAwOC0wMC0wMDAwU0VUO2V4dD1zZXQ7bWltZT07XVdvcmRwZXJmZWN0IHNldHVwIGRhdGENCiY5CWJ5dGUJMTcJDQo+MTAJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjExCWJ5dGUJeAkuJWQNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0wOCBieSBDYXJsDQowCXN0cmluZwlSSUZGCVtmaWQ9MDAwMDAxMDAxLTBFLTAwMDBQQUw7ZXh0PXBhbCxyaWZmO21pbWU9O11NaWNyb3NvZnQgUGFsZXR0ZSwgbGl0dGxlLWVuZGlhbg0KJjgJc3RyaW5nCVBBTAkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0wOCBieSBDYXJsDQowCXN0cmluZwlSSUZYCVtmaWQ9MDAwMDAxMDAxLTBFLTAwMDBQQUw7ZXh0PXBhbCxyaWZ4O21pbWU9O11NaWNyb3NvZnQgUGFsZXR0ZSwgYmlnLWVuZGlhbg0KJjgJc3RyaW5nCVBBTAkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wNCBieSBDYXJsDQowCXN0cmluZwlET1MJW2ZpZD0wMDAwMDEwMDctMEYtMDAwMEFERjtleHQ9YWRmO21pbWU9O11BbWlnYU9TIEZpbGUgc3lzdGVtDQomMwlieXRlJjB4ZjgJMAkNCj4zCWJ5dGUmMQkwCSwgT0ZTDQo+MwlieXRlJjEJMQksIEZGUw0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA1LTExIGJ5IENhcmwNCjAJYmVsb25nCTB4MDNGMwlbZmlkPTAwMDAwMTAwNy0xMC1MSUJSQVJZO2V4dD0sbGlicmFyeTttaW1lPTtdQW1pZ2EgQ2xhc3NpYyBleGVjdXRhYmxlIGZpbG
 UgKDY4MHgwIGZhbWlseSkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQowCXN0cmluZwlcXHg3ZkVMRglbZmlkPTAwMDAwMDAwMy0xMC0wMDAwMDBPO2V4dD0sbyxzbyxvdXQ7bWltZT07XUV4ZWN1dGFibGUgbGlua2FibGUgZmlsZSAoRUxGKQ0KJjQJYnl0ZQk9MQksIDMyLWJpdA0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTExIGJ5IENhcmwNCjAJc3RyaW5nCVxceDdmRUxGCVtmaWQ9MDAwMDAwMDAzLTEwLTAwMDAwME87ZXh0PSxvLHNvLG91dDttaW1lPTtdRXhlY3V0YWJsZSBsaW5rYWJsZSBmaWxlIChFTEYpDQomNAlieXRlCT0yCSwgNjQtYml0DQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMTQgYnkgQ2FybA0KMAlzdHJpbmcJTVoJW2ZpZD0wMDAwMDEwMDEtMTAtMDAwMEVYRTtleHQ9ZXhlLGRsbDttaW1lPTtdTmV3IGV4ZWN1dGFibGUgZmlsZQ0KJjB4MTgJbGVzaG9ydAk+MHgzRgkNCiYoNjAubCkJc3RyaW5nCU5FCQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTE0IGJ5IENhcmwNCjAJc3RyaW5nCVpNCVtmaWQ9MDAwMDAxMDAxLTEwLTAwMDBFWEU7ZXh0PWV4ZSxkbGw7bWltZT07XU5ldyBleGVjdXRhYmxlIGZpbGUNCiYweDE4CWxlc2hvcnQJPjB4M0YJDQomKDYwLmwpCXN0cmluZwlORQkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0xNCBieSBDYXJsDQowCXN0cmluZwlNWglbZmlkPTAwMDAwMTAwMS0xMC0wMDAwRVhFO2V4dD1leGUsZGxsO21pbWU9O11NaWNyb3N
 vZnQgV2luZG93cyAzLnggTmV3IEV4ZWN1dGFibGUgZmlsZQ0KJjB4MTgJbGVzaG9ydAk+MHgzRgkNCiYoNjAubCkJc3RyaW5nCU5FCQ0KJig2MC5sKzU0KQlieXRlCTB4MDIJDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMTQgYnkgQ2FybA0KMAlzdHJpbmcJWk0JW2ZpZD0wMDAwMDEwMDEtMTAtMDAwMEVYRTtleHQ9ZXhlLGRsbDttaW1lPTtdTWljcm9zb2Z0IFdpbmRvd3MgMy54IE5ldyBFeGVjdXRhYmxlIGZpbGUNCiYweDE4CWxlc2hvcnQJPjB4M0YJDQomKDYwLmwpCXN0cmluZwlORQkNCiYoNjAubCs1NCkJYnl0ZQkweDAyCQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTE0IGJ5IENhcmwNCjAJc3RyaW5nCU1aCVtmaWQ9MDAwMDAxMDA5LTEwLTAwMDBFWEU7ZXh0PWV4ZSxkbGw7bWltZT07XUlCTSBPUy8yIE5ldyBFeGVjdXRhYmxlIGZpbGUNCiYweDE4CWxlc2hvcnQJPjB4M0YJDQomKDYwLmwpCXN0cmluZwlORQkNCiYoNjAubCs1NCkJYnl0ZQkweDAxCQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTE0IGJ5IENhcmwNCjAJc3RyaW5nCVpNCVtmaWQ9MDAwMDAxMDA5LTEwLTAwMDBFWEU7ZXh0PWV4ZSxkbGw7bWltZT07XUlCTSBPUy8yIE5ldyBFeGVjdXRhYmxlIGZpbGUNCiYweDE4CWxlc2hvcnQJPjB4M0YJDQomKDYwLmwpCXN0cmluZwlORQkNCiYoNjAubCs1NCkJYnl0ZQkweDAxCQ0KDQojIE1hZ2ljIElEIGZvciBNaWNyb3NvZnQgV2luZG93cyBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIw
 MDQtMDQtMTMgYnkgQ2FybA0KMAlzdHJpbmcJTVoJW2ZpZD0wMDAwMDEwMDEtMTAtMDAwMEVYRTtleHQ9ZXhlLGRsbDttaW1lPTtdTWljcm9zb2Z0IFdpbmRvd3MgTlQgUG9ydGFibGUgRXhlY3V0YWJsZSBmaWxlDQomMHgxOAlsZXNob3J0CTB4NDAJDQomKDYwLmwpCXN0cmluZwlQRVxceDAwXFx4MDAJDQoNCiMgTWFnaWMgSUQgZm9yIE1pY3Jvc29mdCBXaW5kb3dzIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0xMyBieSBDYXJsDQowCXN0cmluZwlaTQlbZmlkPTAwMDAwMTAwMS0xMC0wMDAwRVhFO2V4dD1leGUsZGxsO21pbWU9O11NaWNyb3NvZnQgV2luZG93cyBOVCBQb3J0YWJsZSBFeGVjdXRhYmxlIGZpbGUNCiYweDE4CWxlc2hvcnQJMHg0MAkNCiYoNjAubCkJc3RyaW5nCVBFXFx4MDBcXHgwMAkNCg0KIyBNYWdpYyBJRCBmb3IgTWljcm9zb2Z0IFdpbmRvd3MsRE9TNEdXIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0xNCBieSBDYXJsDQowCXN0cmluZwlNWglbZmlkPTAwMDAwMTAwMS0xMC0wMDAwRVhFO2V4dD1leGUsZGxsLGRydjttaW1lPTtdTWljcm9zb2Z0IFdpbmRvd3MgTGluZWFyIGV4ZWN1dGFibGUNCiYweDE4CWxlc2hvcnQJPjB4M0YJDQomKDYwLmwpCXN0cmluZwlMRQkNCg0KIyBNYWdpYyBJRCBmb3IgTWljcm9zb2Z0IFdpbmRvd3MsRE9TNEdXIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0xNCBieSBDYXJsDQowCXN0cmluZwlaTQlbZmlkPTAwMDAwMTAwM
 S0xMC0wMDAwRVhFO2V4dD1leGUsZGxsLGRydjttaW1lPTtdTWljcm9zb2Z0IFdpbmRvd3MgTGluZWFyIGV4ZWN1dGFibGUNCiYweDE4CWxlc2hvcnQJPjB4M0YJDQomKDYwLmwpCXN0cmluZwlMRQkNCg0KIyBNYWdpYyBJRCBmb3IgT1MvMixET1M0R1cgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTE0IGJ5IENhcmwNCjAJc3RyaW5nCU1aCVtmaWQ9MDAwMDAxMDA5LTEwLTAwMDBFWEU7ZXh0PWV4ZSxkbGwsZHJ2O21pbWU9O11PUy8yIExpbmVhciBleGVjdXRhYmxlDQomMHgxOAlsZXNob3J0CT4weDNGCQ0KJig2MC5sKQlzdHJpbmcJTFgJDQoNCiMgTWFnaWMgSUQgZm9yIE9TLzIsRE9TNEdXIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0xNCBieSBDYXJsDQowCXN0cmluZwlaTQlbZmlkPTAwMDAwMTAwOS0xMC0wMDAwRVhFO2V4dD1leGUsZGxsLGRydjttaW1lPTtdT1MvMiBMaW5lYXIgZXhlY3V0YWJsZQ0KJjB4MTgJbGVzaG9ydAk+MHgzRgkNCiYoNjAubCkJc3RyaW5nCUxYCQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA3LTMwIGJ5IENhcmwNCjAJc3RyaW5nCU1TRlQJW2ZpZD0wMDAwMDEwMDEtMTAtMDAwMFRMQjtleHQ9dGxiO21pbWU9O11NaWNyb3NvZnQgY29tcG9uZW50IHR5cGUgbGlicmFyeQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA1LTExIGJ5IENhcmwNCjAJYmVzaG9ydAkweDYwMUEJW2ZpZD0wMDAwMDEwMDYtMTAtMDAwMFRUUDtleHQ9dHRwLGdlbSxwcmc7bWltZT07XU
 F0YXJpIE1pTlQgZXhlY3V0YWJsZS9vYmplY3QgZmlsZQ0KJjB4MTIJc3RyaW5nCU1pTlQJDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDUtMTEgYnkgQ2FybA0KMAliZXNob3J0CTB4NjAxQQlbZmlkPTAwMDAwMTAwNi0xMC0wMDAwVFRQO2V4dD10dHAsZ2VtLHByZzttaW1lPTtdQXRhcmkgVE9TIGV4ZWN1dGFibGUvb2JqZWN0IGZpbGUNCiYweDEyCWJlbG9uZwkweDAwMDAJDQoNCiMgTWFnaWMgSUQgZm9yIFZpcnR1YWwgUGFzY2FsIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNy0zMCBieSBDYXJsDQowCXN0cmluZwlWUEkJW2ZpZD0wMDAwMDAwMDAtMTAtMDAwMFZQSTtleHQ9dnBpO21pbWU9O11WaXJ0dWFsIHBhc2NhbCB1bml0IGZpbGUNCiYweDAzCWJ5dGUJPjQ3CQ0KJjB4MDMJYnl0ZQk8NTgJDQoNCiMgTWFnaWMgSUQgZm9yIEphdmEgY29tcGlsZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA1LTExIGJ5IENhcmwNCjAJYmVsb25nCTB4Q0FGRUJBQkUJW2ZpZD0wMDAwMDEwMTEtMTEtMDBDTEFTUztleHQ9Y2xhc3M7bWltZT07XUphdmFsIHZpcnR1YWwgbWFjaGluZSBjbGFzcyBmaWxlDQo+NgliZXNob3J0CXgJLCB2ZXJzaW9uICVkDQo+NAliZXNob3J0CXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIEJvcmxhbmQgRGVscGhpIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNy0zMCBieSBDYXJsDQowCXN0cmluZwlQS0cJW2ZpZD0wMDAwMDEwMDUtMTEtMDAwMERDUDtleHQ9ZGNwO21
 pbWU9O11Cb3JsYW5kIERlbHBoaSBjb21waWxlZCBwYWNrYWdlIGNvZGUgZmlsZQ0KJjB4MDMJYnl0ZQk+NDcJDQomMHgwMwlieXRlCTw1OAkNCg0KIyBNYWdpYyBJRCBmb3IgQm9ybGFuZCBEZWxwaGkgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA3LTMwIGJ5IENhcmwNCjAJc3RyaW5nCVxceERGXFx4MDBcXHgwMFxceDBGCVtmaWQ9MDAwMDAxMDA1LTExLTAwMDBEQ1U7ZXh0PWRjdTttaW1lPTtdQm9ybGFuZCBEZWxwaGkgY29kZSB1bml0IGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgVHVyYm8gUGFzY2FsIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNy0yOSBieSBDYXJsDQowCXN0cmluZwlUUFU5CVtmaWQ9MDAwMDAxMDA1LTExLTAwMDBUUFU7ZXh0PXRwdTttaW1lPTtdVHVyYm8gUGFzY2FsIDYuMCBjb2RlIHVuaXQgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBUdXJibyBQYXNjYWwsIEJvcmxhbmQgUGFzY2FsIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNy0yOSBieSBDYXJsDQowCXN0cmluZwlUUFVRCVtmaWQ9MDAwMDAxMDA1LTExLTAwMDBUUFU7ZXh0PXRwdSx0cHAsdHB3O21pbWU9O11Cb3JsYW5kIFBhc2NhbCA3LjAgY29kZSB1bml0IGZpbGUNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQoyCXN0cmluZwlBRExJQi0JW2ZpZD0wMDAwMDEwMTYtMjAtMDAwMEJOSztleHQ9Ym5rO21pbWU9O11BZGxpYiBGTSBpbnN0cnVtZW50IGJhbmsgZmlsZQ0KPjAJYnl0ZQl4
 CSwgdmVyc2lvbiAlZA0KPjEJYnl0ZQl4CS4lZA0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE2IGJ5IENhcmwNCjAJc3RyaW5nCUlCS1xceDFBCVtmaWQ9MDAwMDAxMDEzLTIwLTAwMDBJQks7ZXh0PWliazttaW1lPTtdQ3JlYXRpdmUgTGFicyBGTSBpbnN0cnVtZW50IGJhbmsgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBNaWNyb3NvZnQgSW5zdHJ1bWVudCBEZWZpbml0aW9uIGZpbGUgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAzLTA0IGJ5IENhcmwNCjAJc3RyaW5nCVJJRkYJW2ZpZD0wMDAwMDEwMDEtMjAtMDAwMElERjtleHQ9aWRmO21pbWU9O11NaWNyb3NvZnQgaW5zdHJ1bWVudCBkZWZpbml0aW9uIGZpbGUsIGxpdHRsZS1lbmRpYW4NCiY4CXN0cmluZwlJREZcXCAJDQoNCiMgTWFnaWMgSUQgZm9yIE1pY3Jvc29mdCBJbnN0cnVtZW50IERlZmluaXRpb24gZmlsZSBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDQgYnkgQ2FybA0KMAlzdHJpbmcJUklGWAlbZmlkPTAwMDAwMTAwMS0yMC0wMDAwSURGO2V4dD1pZGY7bWltZT07XU1pY3Jvc29mdCBpbnN0cnVtZW50IGRlZmluaXRpb24gZmlsZSwgYmlnLWVuZGlhbg0KJjgJc3RyaW5nCUlERlxcIAkNCg0KIyBNYWdpYyBJRCBmb3IgRGlnaXRyYWtrZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAzLTA0IGJ5IENhcmwNCjAJc3RyaW5nCURJU1QJW2ZpZD0wMDAxMDAwODgtMjAtMDAwMElTVDtleHQ9aXN0O21pbWU9O11EaWdpd
 HJha2tlciBJbnN0cnVtZW50IGZpbGUNCiY0CWJ5dGUJPDIJDQoNCiMgTWFnaWMgSUQgZm9yIEltcHVsc2UgdHJhY2tlciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDQgYnkgQ2FybA0KMAlzdHJpbmcJSU1QSQlbZmlkPTAwMDEwMDAzMi0yMC0wMDAwSVRJO2V4dD1pdGk7bWltZT07XUltcHVsc2UgdHJhY2tlciBpbnN0cnVtZW50IGZpbGUNCj4weDIwCXN0cmluZwl4CVt0aXRsZT0lLjI2c10NCg0KIyBNYWdpYyBJRCBmb3IgTWFkdHJhY2tlciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDUgYnkgQ2FybA0KMAlzdHJpbmcJTUkyMQlbZmlkPTAwMDEwMDA5MS0yMC0wMDAwTVRJO2V4dD1tdGk7bWltZT07XU1hZHRyYWNrZXIgaW5zdHJ1bWVudCBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIEdyYXZpcyBVbHRyYXNvdW5kIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQowCXN0cmluZwlHRjFQQVRDSDEwMFxcMElEIzAwMDAwMlxcMAlbZmlkPTAwMDAwMTAxOC0yMC0wMDAwUEFUO2V4dD1wYXQ7bWltZT07XUdyYXZpcyBVbHRyYXNvdW5kIFBhdGNoIChvbGQgaW5zdHJ1bWVudCBkYXRhKQ0KDQojIE1hZ2ljIElEIGZvciBHcmF2aXMgVWx0cmFzb3VuZCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTUgYnkgQ2FybA0KMAlzdHJpbmcJR0YxUEFUQ0gxMTBcXDBJRCMwMDAwMDJcXDAJW2ZpZD0wMDAwMDEwMTgtMjAtMDAwMFBBVDtleHQ9cGF0O21pbWU9O1
 1HcmF2aXMgVWx0cmFzb3VuZCBQYXRjaCAoaW5zdHJ1bWVudCBkYXRhKQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE1IGJ5IENhcmwNCjAJc3RyaW5nCVNCSVxceDFBCVtmaWQ9MDAwMDAxMDEzLTIwLTAwMDBTQkk7ZXh0PXNiaTttaW1lPTtdQ3JlYXRpdmUgTGFicyBGTSBpbnN0cnVtZW50IGRhdGEgZmlsZQ0KPjQJc3RyaW5nCT4wCVt0aXRsZT0lLjMyc10NCg0KIyBNYWdpYyBJRCBmb3IgU2lkcGxheSBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDUgYnkgQ2FybA0KMAlzdHJpbmcJU0lEUExBWVxcIElORk9GSUxFCVtmaWQ9MDAwMDAwMDAwLTIwLTAwMDBTSUQ7ZXh0PXNpZDttaW1lPTtdU0lEUGxheWVyIG11c2ljIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgRmFzdHRyYWNrZXIgMi4wIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQowCXN0cmluZwlFeHRlbmRlZFxcIEluc3RydW1lbnQ6XFwgCVtmaWQ9MDAwMTAwMDI2LTIwLTAwMDAwWEk7ZXh0PXhpO21pbWU9O11GYXN0VHJhY2tlciBJSSBpbnN0cnVtZW50IGZpbGUNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQowCXN0cmluZwlSSUZGCVtmaWQ9MDAwMDAxMDAxLTIxLTAwMDAwMDA7ZXh0PTttaW1lPTtdTUlESSBtdXNpYyBmaWxlLCBsaXR0bGUtZW5kaWFuDQomOAlzdHJpbmcJUk1JRAkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQowCXN0cmluZwlSSUZYCVtmaWQ
 9MDAwMDAxMDAxLTIxLTAwMDAwMDA7ZXh0PTttaW1lPTtdTUlESSBtdXNpYyBmaWxlLCBiaWctZW5kaWFuDQomOAlzdHJpbmcJUk1JRAkNCg0KIyBNYWdpYyBJRCBmb3IgQWJ5c3NcJ3MgaGlnaGVzdCBleHBlcmllbmNlIChBSFgpIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQowCXN0cmluZwlUSFgJW2ZpZD0wMDAxMDAwMjktMjEtMDAwMEFIWDtleHQ9YWh4O21pbWU9O11BSFggbW9kdWxlIG11c2ljIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgQW11c2ljIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQoxMDYyCXN0cmluZwlcXHgzY1xceDZmXFx4ZWZcXHg1MVxceDU1XFx4RUVcXHg1MlxceDZGXFx4NTIJW2ZpZD0wMDAxMDAwMzQtMjEtMDAwMEFNRDtleHQ9YW1kO21pbWU9O11BbXVzaWMgQWRsaWIgdHJhY2tlciBtdXNpYyBmaWxlDQo+MAlzdHJpbmcJPlxceDAwCVt0aXRsZT0lLjIzc10NCj4yNAlzdHJpbmcJPlxcMAlbY3JlYXRvcj0lLjI0c10NCg0KIyBNYWdpYyBJRCBmb3IgVmVsdmV0IFN0dWRpbyBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTUgYnkgQ2FybA0KMAlzdHJpbmcJQU1TaGRyXFx4MWEJW2ZpZD0wMDAwMDEyNzYtMjEtMDAwMEFNUztleHQ9YW1zO21pbWU9O11WZWx2ZXQgU3R1ZGlvIG1vZHVsZSBtdXNpYyBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIEF1ZGlvIHZpc3VhbCByZXNlYXJjaCBmaWxlcy4NCiMgU3VibWl0dGVk
 IG9uIDIwMDQtMDMtMDQgYnkgQ2FybA0KMAlzdHJpbmcJMkJJVAlbZmlkPTAwMDAwMTMwMS0yMS0wMDAwQVZSO2V4dD1hdnI7bWltZT07XUF1ZGlvIHZpc3VhbCByZXNlYXJjaCBhdWRpbyBmaWxlDQo+NAlzdHJpbmcJeAlbdGl0bGU9JS44c10NCg0KIyBNYWdpYyBJRCBmb3IgU291bmRtb24gZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE1IGJ5IENhcmwNCjI2CXN0cmluZwlWLjIJW2ZpZD0wMDAxMDAwMjgtMjEtMDAwMDBCUDtleHQ9YnA7bWltZT07XVNvdW5kbW9uIG1vZHVsZSBtdXNpYyBmaWxlLCB2ZXJzaW9uIDIueA0KPjAJc3RyaW5nCT5cXDAJW3RpdGxlPSUuMjZzXQ0KDQojIE1hZ2ljIElEIGZvciBTb3VuZG1vbiBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTUgYnkgQ2FybA0KMjYJc3RyaW5nCVYuMwlbZmlkPTAwMDEwMDAyOC0yMS0wMDAwQlAzO2V4dD1icDM7bWltZT07XVNvdW5kbW9uIG1vZHVsZSBtdXNpYyBmaWxlLCB2ZXJzaW9uIDMueA0KPjAJc3RyaW5nCT5cXDAJW3RpdGxlPSUuMjZzXQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE1IGJ5IENhcmwNCjAJc3RyaW5nCUNUTUYJW2ZpZD0wMDAwMDEwMTMtMjEtMDAwMENNRjtleHQ9Y21mO21pbWU9O11DcmVhdGl2ZSBMYWJzIG11c2ljIGZpbGUNCj40CWJ5dGUJeAksIHZlcnNpb24gJWQNCj41CWJ5dGUJeAkuJWQNCg0KIyBNYWdpYyBJRCBmb3IgRGlnaWJvb3N0ZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0L
 TAzLTA0IGJ5IENhcmwNCjAJc3RyaW5nCURJR0kgQm9vc3RlciBtb2R1bGVcXDAJW2ZpZD0wMDAwMDEzMDItMjEtMDAwRElHSTtleHQ9ZGlnaTttaW1lPTtdRGlnaWJvb3N0ZXIgbXVzaWMgZmlsZQ0KPjYxMAlzdHJpbmcJeAlbdGl0bGU9JS4zMnNdDQoNCiMgTWFnaWMgSUQgZm9yIERlbHVzaW9uIFh0cmFja2VyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQowCXN0cmluZwlERE1GCVtmaWQ9MDAwMDAwMDAwLTIxLTAwMDBETUY7ZXh0PWRtZjttaW1lPTtdRGVsdXNpb24gdHJhY2tlciBtb2R1bGUgbXVzaWMgZmlsZQ0KPjQJYnl0ZQl4CSwgdmVyc2lvbiAlZC4wDQo+MTMJc3RyaW5nCT5cXDAJW3RpdGxlPSUuMzBzXQ0KPjQzCXN0cmluZwk+XFwwCVtjcmVhdG9yPSUuMjBzXQ0KDQojIE1hZ2ljIElEIGZvciBET1MgU291bmQgaW50ZXJmYWNlIGtpdCAoRFNJSykgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE1IGJ5IENhcmwNCjAJc3RyaW5nCVJJRkYJW2ZpZD0wMDAwMDAwMDAtMjEtMDAwMERTTTtleHQ9ZHNtO21pbWU9O11ET1MgU291bmQgaW50ZXJmYWNlIGtpdCBtb2R1bGUgbXVzaWMgZmlsZQ0KJjgJc3RyaW5nCURTTUYJDQoNCiMgTWFnaWMgSUQgZm9yIEVkbGliIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wNyBieSBDYXJsDQowCXN0cmluZwlcXHgwMFxceDA2XFx4RkVcXHhGRAlbZmlkPTAwMDEwMDAyNy0yMS0wMDAwRURMO2V4dD1lZGw7bWltZT
 07XUVkbGliIEZNIHRyYWNrZXIgbXVzaWMgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBRdWFkcmEgQ29tcG9zZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAzLTA0IGJ5IENhcmwNCjAJc3RyaW5nCUZPUk0JW2ZpZD0wMDAxMDAwODUtMjEtMDAwMEVNRDtleHQ9ZW1kO21pbWU9O11FbmhhbmNlZCBtb2R1bGUgbXVzaWMgZmlsZSAoSUZGKQ0KJjgJc3RyaW5nCUVNT0QJDQoNCiMgTWFnaWMgSUQgZm9yIEZhcmFuZG9sZSBjb21wb3NlciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTUgYnkgQ2FybA0KMAlzdHJpbmcJRkFSXFx4RkUJW2ZpZD0wMDAxMDAwODctMjEtMDAwMEZBUjtleHQ9ZmFyO21pbWU9O11GYXJhbmRvbGUgY29tcG9zZXIgbW9kdWxlIG11c2ljIGZpbGUNCj40CXN0cmluZwk+XFwwCVt0aXRsZT0lLjQwc10NCg0KIyBNYWdpYyBJRCBmb3IgRnVua3RyYWNrZXIgR29sZCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDQgYnkgQ2FybA0KMAlzdHJpbmcJRnVuawlbZmlkPTAwMDEwMDA4Ni0yMS0wMDAwRk5LO2V4dD1mbms7bWltZT07XUZ1bmt0cmFja2VyIEdvbGQgbXVzaWMgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBCZWxscywgV2hpc3RsZXMsIGFuZCBTb3VuZCBCb2FyZHMgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE2IGJ5IENhcmwNCjAJc3RyaW5nCUdETVxceEZFCVtmaWQ9MDAwMDAxMjgwLTIxLTAwMDBHRE07ZXh0PWdkbTttaW1lPTtdR2VuZXJhbCB
 EaWdpTXVzaWMgbW9kdWxlIG11c2ljIGZpbGUNCj40CXN0cmluZwk+XFx4MDAJW3RpdGxlPSUuMzJzXQ0KDQojIE1hZ2ljIElEIGZvciBHcmFvdW1mIFRyYWNrZXIgMiBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTUgYnkgQ2FybA0KMAlzdHJpbmcJR1QyCVtmaWQ9MDAwMTAwMDMxLTIxLTAwMDBHVDI7ZXh0PWd0MjttaW1lPTtdR3Jhb3VtZiBUcmFja2VyIG1vZHVsZSBtdXNpYyBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIEltYWdvIE1vcnBoZXVzIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wNCBieSBDYXJsDQoweDNDCXN0cmluZwlJTTEwCVtmaWQ9MDAwMDAxMjc5LTIxLTAwMDBJTUY7ZXh0PWltZjttaW1lPTtdSW1hZ28gbW9ycGhldXMgbXVzaWMgZmlsZSwgMzIgY2hhbm5lbHMNCj4wCXN0cmluZwl4CVt0aXRsZT0lLjMxc10NCg0KIyBNYWdpYyBJRCBmb3IgSW1wdWxzZSB0cmFja2VyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQowCXN0cmluZwlJTVBNCVtmaWQ9MDAwMTAwMDMyLTIxLTAwMDAwSVQ7ZXh0PWl0O21pbWU9O11JbXB1bHNlIFRyYWNrZXIgbW9kdWxlIG11c2ljIGZpbGUNCj40CXN0cmluZwk+XFx4MDAJW3RpdGxlPSUuMjZzXQ0KDQojIE1hZ2ljIElEIGZvciBKYW1jcmFja2VyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wNCBieSBDYXJsDQowCXN0cmluZwlCZUVwCVtmaWQ9MDAwMDAwMDAwLTIxLTAwMDBKQU07ZXh0PWph
 bTttaW1lPTtdSmFtY3JhY2tlciB0cmFja2VyIG1vZHVsZSBtdXNpYyBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIExpcXVpZCB0cmFja2VyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wNCBieSBDYXJsDQowCXN0cmluZwlMaXF1aWQgTW9kdWxlOglbZmlkPTAwMDEwMDA5MC0yMS0wMDAwTElRO2V4dD1saXE7bWltZT07XUxpcXVpZCB0cmFja2VyIG1vZHVsZSBtdXNpYyBmaWxlDQomMHg0MAlieXRlCTB4MUEJDQo+MHgwRQlzdHJpbmcJPlxcMAlbdGl0bGU9JS4zMHNdDQo+MHgyYwlzdHJpbmcJPlxcMAlbY3JlYXRvcj0lLjIwc10NCg0KIyBNYWdpYyBJRCBmb3IgRGlnaXRyYWtrZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAzLTA0IGJ5IENhcmwNCjAJc3RyaW5nCURNREwJW2ZpZD0wMDAxMDAwODgtMjEtMDAwME1ETDtleHQ9bWRsO21pbWU9O11EaWdpdHJha2tlciBtb2R1bGUgbXVzaWMgZmlsZQ0KJjQJYnl0ZQk8MHgxMgkNCg0KIyBNYWdpYyBJRCBmb3IgTUVEIFNvdW5kc3R1ZGlvIC8gT2N0YU1FRCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTUgYnkgQ2FybA0KMAlzdHJpbmcJTU1EMAlbZmlkPTAwMDAwMTI3OC0yMS0wMDAwTUVEO2V4dD1tZWQ7bWltZT07XU9jdGFtZWQgdHJhY2tlciBtb2R1bGUgbXVzaWMgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBNRUQgU291bmRzdHVkaW8gLyBPY3RhTUVEIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsD
 QowCXN0cmluZwlNTUQxCVtmaWQ9MDAwMDAxMjc4LTIxLTAwMDBNRUQ7ZXh0PW1lZDttaW1lPTtdT2N0YW1lZCBQcm8gVHJhY2tlciBtb2R1bGUgbXVzaWMgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBNRUQgU291bmRzdHVkaW8gLyBPY3RhTUVEIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQowCXN0cmluZwlNTUQyCVtmaWQ9MDAwMDAxMjc4LTIxLTAwMDBNRUQ7ZXh0PW1lZDttaW1lPTtdT2N0YW1lZCBQcm8gVHJhY2tlciBtb2R1bGUgbXVzaWMgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBNRUQgU291bmRzdHVkaW8gLyBPY3RhTUVEIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQowCXN0cmluZwlNTUQzCVtmaWQ9MDAwMDAxMjc4LTIxLTAwMDBNRUQ7ZXh0PW1lZDttaW1lPTtdT2N0YW1lZCBTb3VuZCBTdHVkaW8gbW9kdWxlIG11c2ljIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgTXVzaWNsaW5lIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wNSBieSBDYXJsDQowCXN0cmluZwlNTEVETU9ETAlbZmlkPTAwMDAwMTMwNC0yMS0wMDAwME1MO2V4dD1tbDttaW1lPTtdTXVzaWNsaW5lIG1vZHVsZSBtdXNpYyBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIFByb3RyYWNrZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE1IGJ5IENhcmwNCjEwODAJc3RyaW5nCU0hSyEJW2ZpZD0wMDAxMDAwMjItMjEtMDAwME1PRDtleHQ9bW9kO21pbWU9O1
 1Qcm90cmFja2VyIDIuMyBtb2R1bGUgbXVzaWMgZmlsZQ0KPjAJc3RyaW5nCT5cXDAJW3RpdGxlPSUuMjBzXQ0KDQojIE1hZ2ljIElEIGZvciBQcm90cmFja2VyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQoxMDgwCXN0cmluZwlNLksuCVtmaWQ9MDAwMTAwMDIyLTIxLTAwMDBNT0Q7ZXh0PW1vZDttaW1lPTtdUHJvdHJhY2tlciBtb2R1bGUgbXVzaWMgZmlsZQ0KPjAJc3RyaW5nCT5cXDAJW3RpdGxlPSUuMjBzXQ0KDQojIE1hZ2ljIElEIGZvciBQcm90cmFja2VyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wNSBieSBDYXJsDQowCXN0cmluZwlGT1JNCVtmaWQ9MDAwMTAwMDIyLTIxLTAwMDBNT0Q7ZXh0PW1vZDttaW1lPTtdUHJvdHJhY2tlciBtb2R1bGUgbXVzaWMgZmlsZSwgdmVyc2lvbiAzLngNCiY4CXN0cmluZwlNT0RMCQ0KDQojIE1hZ2ljIElEIGZvciBTdGFydHJla2tlciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTUgYnkgQ2FybA0KMTA4MAlzdHJpbmcJRkxUNAlbZmlkPTAwMDEwMDAyNC0yMS0wMDAwTU9EO2V4dD1tb2Q7bWltZT07XVN0YXJ0cmVra2VyIG1vZHVsZSBtdXNpYyBmaWxlLCA0IGNoYW5uZWxzDQo+MAlzdHJpbmcJPlxcMAlbdGl0bGU9JS4yMHNdDQoNCiMgTWFnaWMgSUQgZm9yIFN0YXJ0cmVra2VyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQoxMDgwCXN0cmluZwlGTFQ4CVtmaWQ9MDAwMTA
 wMDI0LTIxLTAwMDBNT0Q7ZXh0PW1vZDttaW1lPTtdU3RhcnRyZWtrZXIgbW9kdWxlIG11c2ljIGZpbGUsIDggY2hhbm5lbHMNCj4wCXN0cmluZwk+XFwwCVt0aXRsZT0lLjIwc10NCg0KIyBNYWdpYyBJRCBmb3IgRmFzdHRyYWNrZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE1IGJ5IENhcmwNCjEwODAJc3RyaW5nCTZDSE4JW2ZpZD0wMDAwMDEyNzUtMjEtMDAwME1PRDtleHQ9bW9kO21pbWU9O11GYXN0dHJhY2tlciBtb2R1bGUgbXVzaWMgZmlsZSwgNiBjaGFubmVscw0KPjAJc3RyaW5nCT5cXDAJW3RpdGxlPSUuMjBzXQ0KDQojIE1hZ2ljIElEIGZvciBGYXN0dHJhY2tlciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTUgYnkgQ2FybA0KMTA4MAlzdHJpbmcJOENITglbZmlkPTAwMDAwMTI3NS0yMS0wMDAwTU9EO2V4dD1tb2Q7bWltZT07XUZhc3R0cmFja2VyIG1vZHVsZSBtdXNpYyBmaWxlLCA2IGNoYW5uZWxzDQo+MAlzdHJpbmcJPlxcMAlbdGl0bGU9JS4yMHNdDQoNCiMgTWFnaWMgSUQgZm9yIE1hZHRyYWNrZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAzLTA1IGJ5IENhcmwNCjAJc3RyaW5nCU1UMjAJW2ZpZD0wMDAxMDAwOTEtMjEtMDAwME1UMjtleHQ9bXQyO21pbWU9O11NYWR0cmFja2VyIG1vZHVsZSBtdXNpYyBmaWxlDQo+NDIJc3RyaW5nCXgJW3RpdGxlPSUuNjRzXQ0KPjExMglsZXNob3J0CXgJW2Nobj0lZF0NCg0KIyBNYWdpYyBJRCBmb3IgTXVsdGl0
 cmFja2VyIE1vZHVsZSBlZGl0b3IgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE1IGJ5IENhcmwNCjAJc3RyaW5nCU1UTQlbZmlkPTAwMDEwMDA4OS0yMS0wMDAwTVRNO2V4dD1tdG07bWltZT07XU11bHRpVHJhY2tlciBtb2R1bGUgbXVzaWMgZmlsZQ0KPjQJc3RyaW5nCT5cXHgwMAlbdGl0bGU9JS4yMHNdDQoNCiMgTWFnaWMgSUQgZm9yIE1hZHRyYWNrZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAzLTA1IGJ5IENhcmwNCjAJc3RyaW5nCU1UUDIJW2ZpZD0wMDAxMDAwOTEtMjEtMDAwME1UUDtleHQ9bXRwO21pbWU9O11NYWR0cmFja2VyIHBhdHRlcm4gZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBBL05FUyBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDcgYnkgQ2FybA0KMAlzdHJpbmcJTkVTQQlbZmlkPTAwMDEwMDA5NC0yMS0wMDAwTlNBO2V4dD1uc2E7bWltZT07XUEvTkVTIHJpcHBlZCBhdWRpbyBmaWxlDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDcgYnkgQ2FybA0KMAlzdHJpbmcJTkVTTVxceDFBCVtmaWQ9MDAwMTAwMDkzLTIxLTAwMDBOU0Y7ZXh0PW5zZjttaW1lPTtdTkVTIHJpcHBlZCBhdWRpbyBmaWxlDQo+NQlieXRlCXgJLCB2ZXJzaW9uICVkLjANCj4weDBFCXN0cmluZwl4CVt0aXRsZT0lLjMyc10NCj4weDJFCXN0cmluZwl4CVtjcmVhdG9yPSUuMzJzXQ0KDQojIE1hZ2ljIElEIGZvciBOb2lzZXRyYWNrZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyM
 DA0LTAyLTE1IGJ5IENhcmwNCjEwODAJc3RyaW5nCU0mSyEJW2ZpZD0wMDAxMDAwMjMtMjEtMDAwME5TVDtleHQ9bnN0O21pbWU9O11Ob2lzZXRyYWNrZXIgbW9kdWxlIG11c2ljIGZpbGUNCj4wCXN0cmluZwk+XFwwCVt0aXRsZT0lLjIwc10NCg0KIyBNYWdpYyBJRCBmb3IgT2t0YWx5emVyIHRyYWNrZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE1IGJ5IENhcmwNCjAJc3RyaW5nCU9LVEFTT05HCVtmaWQ9MDAwMTAwMDMwLTIxLTAwMDBPS1Q7ZXh0PW9rdDttaW1lPTtdT2t0YWx5emVyIG1vZHVsZSBtdXNpYyBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIFNCU3R1ZGlvIHNvdW5kIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wNSBieSBDYXJsDQowCXN0cmluZwlQQUNHCVtmaWQ9MDAwMTAwMDIwLTIxLTAwMDBQQUM7ZXh0PXBhYzttaW1lPTtdU0JTdHVkaW8gbW9kdWxlIG11c2ljIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgUG9seXRyYWNrZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE1IGJ5IENhcmwNCjQ0CXN0cmluZwlQVE1GCVtmaWQ9MDAwMDAwMDAwLTIxLTAwMDBQVE07ZXh0PXB0bTttaW1lPTtdUG9seSBUcmFja2VyIG1vZHVsZSBtdXNpYyBmaWxlDQo+MzgJbGVzaG9ydAk+MAlbY2huPSVkXQ0KPjAJc3RyaW5nCT5cXDAJW3RpdGxlPSUuMjhzXQ0KDQojIE1hZ2ljIElEIGZvciBSZWFsaXR5IEFkbGliIHRyYWNrZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMD
 A0LTAyLTE1IGJ5IENhcmwNCjAJc3RyaW5nCVJBRFxcIGJ5CVtmaWQ9MDAwMDAwMDAwLTIxLTAwMDBSQUQ7ZXh0PXJhZDttaW1lPTtdUmVhbGl0eSBBZGxpYiB0cmFja2VyIG11c2ljIGZpbGUNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQowCXN0cmluZwlSSUZGCVtmaWQ9MDAwMDAwMDAwLTIxLTAwMDBSTUk7ZXh0PXJtaTttaW1lPWFwcGxpY2F0aW9uL3ZuZC5tdXNpYy1uaWZmO11Tb25nIG5vdGF0aW9uIGRhdGEgZmlsZSwgbGl0dGxlLWVuZGlhbg0KJjgJc3RyaW5nCU5JRkYJDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTUgYnkgQ2FybA0KMAlzdHJpbmcJUklGWAlbZmlkPTAwMDAwMDAwMC0yMS0wMDAwUk1JO2V4dD1ybWk7bWltZT1hcHBsaWNhdGlvbi92bmQubXVzaWMtbmlmZjtdU29uZyBub3RhdGlvbiBkYXRhIGZpbGUsIGJpZy1lbmRpYW4NCiY4CXN0cmluZwlOSUZGCQ0KDQojIE1hZ2ljIElEIGZvciBBZGxpYiBWaXN1YWwgQ29tcG9zZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE1IGJ5IENhcmwNCjAJbGVzaG9ydAkweDAwMDAJW2ZpZD0wMDAwMDEwMTYtMjEtMDAwMFJPTDtleHQ9cm9sO21pbWU9O11BZGxpYiBtdXNpYyBmaWxlDQomMglsZXNob3J0CTB4MDAwNAkNCg0KIyBNYWdpYyBJRCBmb3IgU2NyZWFtdHJhY2tlciAzIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQoweDJDCXN0cmluZwlTQ1JNCVtmaWQ9MDAwMTAwMDI
 1LTIxLTAwMDBTM007ZXh0PXMzbTttaW1lPTtdU2NyZWFtIHRyYWNrZXIgbW9kdWxlIG11c2ljIGZpbGUNCj4weDJBCWxlc2hvcnQJPjAJLCB2ZXJzaW9uICVkLjANCj4wCXN0cmluZwk+XFwwCVt0aXRsZT0lLjI4c10NCg0KIyBNYWdpYyBJRCBmb3IgU3VycHJpc2UhIEFkbGliIFRyYWNrZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE1IGJ5IENhcmwNCjAJc3RyaW5nCVNBZFQJW2ZpZD0wMDAxMDAwMjEtMjEtMDAwMFNBMjtleHQ9c2EyO21pbWU9O11TdXJwcmlzZSBQcm9kdWN0aW9ucyBBZGxpYiB0cmFja2VyIG11c2ljIGZpbGUNCj40CWJ5dGUJeAksIHZlcnNpb24gMC4lZA0KDQojIE1hZ2ljIElEIGZvciBTb3VuZEZYIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQo2MAlzdHJpbmcJU09ORwlbZmlkPTAwMDAwMTI3Ny0yMS0wMDAwU0ZYO2V4dD1zZng7bWltZT07XVNvdW5kRlggVHJhY2tlciBtb2R1bGUgbXVzaWMgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBQbGF5U0lELCBTaWRwbGF5IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wNSBieSBDYXJsDQowCXN0cmluZwlQU0lECVtmaWQ9MDAwMDAwMDAwLTIxLTAwMDBTSUQ7ZXh0PXNpZDttaW1lPTtdUGxheVNJRCBtdXNpYyBmaWxlDQo+MTYJc3RyaW5nCXgJW3RpdGxlPSUuMjBzXQ0KPjM2CXN0cmluZwl4CVtjcmVhdG9yPSUuMjBzXQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE1IGJ5IENhcmwN
 CjAJc3RyaW5nCU1UaGQJW2ZpZD0wMDAwMDEwMTctMjEtMDAwMFNNRjtleHQ9c21mLG1pZGk7bWltZT07XVN0YW5kYXJkIE1JREkgbXVzaWMgZmlsZQ0KPjEwCWJlc2hvcnQJPjAJW2Nobj0lZF0NCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wNSBieSBDYXJsDQowCXN0cmluZwlGT1JNCVtmaWQ9MDAwMDAxMDEwLTIxLTAwMFNNVVM7ZXh0PXNtdXMsbXVzO21pbWU9O11JRkYgU2ltcGxlIE11c2ljYWwgU2NvcmUgZmlsZQ0KJjgJc3RyaW5nCVNNVVMJDQoNCiMgTWFnaWMgSUQgZm9yIFNuZHRvb2wyLG5lenBsYXkgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAzLTA1IGJ5IENhcmwNCjAJc3RyaW5nCVNORFxceDFBCVtmaWQ9MDAwMDAwMDAwLTIxLTAwMDBTTkQ7ZXh0PXNuZDttaW1lPTtdTmludGVuZG8gRW50ZXJ0YWlubWVudCBTeXN0ZW0gYXVkaW8gZmlsZSAoTkVTKQ0KJjQJYnl0ZQkzCSwgdmVyc2lvbiAlMy4wDQo+NQlieXRlCXgJW2Nobj0lZF0NCg0KIyBNYWdpYyBJRCBmb3IgaU5FUyBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDUgYnkgQ2FybA0KMAlzdHJpbmcJU05EXFx4MUEJW2ZpZD0wMDAxMDAwOTItMjEtMDAwMFNORDtleHQ9c25kO21pbWU9O11pTkVTIGVtdWxhdG9yIGF1ZGlvIGZpbGUNCiY0CWJ5dGUJMQksIHZlcnNpb24gJTEuMA0KPjUJYnl0ZQl4CVtjaG49JWRdDQoNCiMgTWFnaWMgSUQgZm9yIFNUTUlLIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBie
 SBDYXJsDQoweDE1CXN0cmluZwlTY3JlYW0hCVtmaWQ9MDAwMTAwMDI1LTIxLTAwMDBTVFg7ZXh0PXN0eDttaW1lPTtdU1RNSUsgbW9kdWxlIG11c2ljIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgVGhlIEZpbmFsIE11c2ljc3lzdGVtIGVYdGVuZGVkIChURk1YKSBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDcgYnkgQ2FybA0KMAlzdHJpbmcJVEZNWC1TT05HXFwgCVtmaWQ9MDAwMTAwMDk2LTIxLTAwMDBURlg7ZXh0PXRmeCx0Zm14O21pbWU9O11URk1YIHRyYWNrZXIgbW9kdWxlIG11c2ljIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgVWx0cmEgVHJhY2tlciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDcgYnkgQ2FybA0KMAlzdHJpbmcJTUFTX1VUcmFja19WMDAJW2ZpZD0wMDAxMDAwOTctMjEtMDAwMFVMVDtleHQ9dWx0O21pbWU9O11VbHRyYSBUcmFja2VyIG1vZHVsZSBtdXNpYyBmaWxlDQo+MTUJc3RyaW5nCXgJW3RpdGxlPSUuMzJzXQ0KDQojIE1hZ2ljIElEIGZvciBBUGxheWVyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQowCXN0cmluZwlBUFVOXFx4MDEJW2ZpZD0wMDAwMDAwMDAtMjEtMDAwMFVOSTtleHQ9dW5pO21pbWU9O11BUGxheWVyIG1vZHVsZSBtdXNpYyBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIE1pa21vZCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTUgYnkgQ2FybA0KMAlzdHJpbmcJVU4wCVtmaWQ9MDAwMDAwMDAwLT
 IxLTAwMDBVTkk7ZXh0PXVuaTttaW1lPTtdTWlrbW9kIG1vZHVsZSBtdXNpYyBmaWxlDQo+NAlieXRlCXgJW2Nobj0lZF0NCg0KIyBNYWdpYyBJRCBmb3IgRmFzdHRyYWNrZXIgMi4wIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNSBieSBDYXJsDQowCXN0cmluZwlFeHRlbmRlZFxcIE1vZHVsZTpcXCAJW2ZpZD0wMDAxMDAwMjYtMjEtMDAwMDBYTTtleHQ9eG07bWltZT07XUZhc3RUcmFja2VyIElJIG1vZHVsZSBtdXNpYyBmaWxlDQo+NTkJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjU4CWJ5dGUJeAkuMCVkDQo+MTcJc3RyaW5nCXgJW3RpdGxlPSUuMjBzXQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTExIGJ5IENhcmwNCjAJc3RyaW5nCUZPUk0JW2ZpZD0wMDAwMDEwMTAtMjItMDAwOFNWWDtleHQ9OHN2eDttaW1lPTtdQW1pZ2EgU2FtcGxlZCBhdWRpbyBmaWxlDQomOAlzdHJpbmcJOFNWWAkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQowCXN0cmluZwlGT1JNCVtmaWQ9MDAwMDAxMDAyLTIyLTAwMEFJRkM7ZXh0PWFpZmMsYWlmO21pbWU9O11BdWRpbyBDb21wcmVzc2VkIEludGVyY2hhbmdlIEZpbGUgRm9ybWF0DQomOAlzdHJpbmcJQUlGQwkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQowCXN0cmluZwlGT1JNCVtmaWQ9MDAwMDAxMDAyLTIyLTAwMEFJRkY7ZXh0PWFpZmYsYWlmO21pbWU9O11BdWRpbyBJbnRlcmNoYW5nZSBGaWxlIEZvcm1hdA0
 KJjgJc3RyaW5nCUFJRkYJDQoNCiMgTWFnaWMgSUQgZm9yIE1vbmtleUF1ZGlvIHNvZnR3YXJlIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNS0yMCBieSBDYXJsDQowCXN0cmluZwlNQUNcXCAJW2ZpZD0wMDAxMDAxMjAtMjItMDAwMEFQRTtleHQ9YXBlO21pbWU9O11Nb25rZXlBdWRpbyBjb21wcmVzc2VkIGF1ZGlvIGZpbGUNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQowCXN0cmluZwkuc25kCVtmaWQ9MDAwMDAxMDExLTIyLTAwMDAwQVU7ZXh0PWF1LHNuZDttaW1lPTtdU3VuIC8gTmVYdCBzYW1wbGVkIGF1ZGlvIGZpbGUNCj4xNgliZWxvbmcJPjAJW2ZyZXE9JWRdDQoNCiMgTWFnaWMgSUQgZm9yIFNwcGFjayBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDQgYnkgQ2FybA0KMjUyCWJlc2hvcnQJMHg0MEMzCVtmaWQ9MDAwMDAxMDE5LTIyLTAwMDAwMEQ7ZXh0PWQ7bWltZT07XVNwcGFjayBhdWRpbyBzYW1wbGUgZmlsZQ0KJjI1NAliZXNob3J0CTB4RkMwRQkNCg0KIyBNYWdpYyBJRCBmb3IgRmxhYyBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDggYnkgQ2FybA0KMAlzdHJpbmcJZkxhQwlbZmlkPTAwMDEwMDA5OC0yMi0wMDBGTEFDO2V4dD1mbGFjO21pbWU9O11GcmVlIExvc3NsZXNzIEF1ZGlvIENvZGVjIHJhdyBhdWRpbyBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIEZhcmFuZG9sZSBDb21wb3NlciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQt
 MDMtMDQgYnkgQ2FybA0KMAlzdHJpbmcJRlNNXFx4RkUJW2ZpZD0wMDAxMDAwODctMjItMDAwMEZTTTtleHQ9ZnNtO21pbWU9O11GYXJhbmRvbGUgY29tcG9zZXIgYXVkaW8gc2FtcGxlIGZpbGUNCj40CXN0cmluZwl4CVt0aXRsZT0lLjMyc10NCg0KIyBNYWdpYyBJRCBmb3IgTUFVRCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDQgYnkgQ2FybA0KMAlzdHJpbmcJRk9STQlbZmlkPTAwMDAwMDAwMC0yMi0wMDBNQVVEO2V4dD1tYXVkO21pbWU9O11NQVVEIGF1ZGlvIHNhbXBsZSBmaWxlDQomNAlzdHJpbmcJTUFVRAkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNS0wNCBieSBDYXJsDQowCWJlc2hvcnQmMHhGRkUwCTB4ZmZlMAlbZmlkPTAwMDAwMDAwMS0yMi0wMDExMTcyO2V4dD1tcDEsbXAyLG1wMzttaW1lPWF1ZGlvL21wZWc7XU1QMyBBdWRpbyBzdHJlYW0gZmlsZQ0KJloxMjgJc3RyaW5nCVRBRwkNCj5aMTI1CXN0cmluZwl4CVt0aXRsZT0lLjMwc10NCj5aOTUJc3RyaW5nCXgJW2NyZWF0b3I9JS4zMHNdDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDUtMDQgYnkgQ2FybA0KMAlzdHJpbmcJSUQzCVtmaWQ9MDAwMDAwMDAxLTIyLTAwMTExNzI7ZXh0PW1wMSxtcDIsbXAzO21pbWU9YXVkaW8vbXBlZztdTVAzIEF1ZGlvIHN0cmVhbSBmaWxlDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTEgYnkgQ2FybA0KMAlzdHJpbmcJXFwwXFwwMDFcXDI0M1xcMTQ0CVtmaWQ9MDAwMDAxMDE0LTIyLTAwM
 DAwU0Y7ZXh0PXNmO21pbWU9O11JUkNBTSBhdWRpbyBzYW1wbGUgZmlsZQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTExIGJ5IENhcmwNCjAJc3RyaW5nCVxcMFxcMDAyXFwyNDNcXDE0NAlbZmlkPTAwMDAwMTAxNC0yMi0wMDAwMFNGO2V4dD1zZjttaW1lPTtdSVJDQU0gYXVkaW8gc2FtcGxlIGZpbGUsIGxpdHRsZS1lbmRpYW4NCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQowCXN0cmluZwlcXDBcXDAwM1xcMjQzXFwxNDQJW2ZpZD0wMDAwMDEwMTQtMjItMDAwMDBTRjtleHQ9c2Y7bWltZT07XUlSQ0FNIGF1ZGlvIHNhbXBsZSBmaWxlLCBiaWctZW5kaWFuDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTEgYnkgQ2FybA0KMAlzdHJpbmcJXFwxNDRcXDI0M1xcMDAxXFwwCVtmaWQ9MDAwMDAxMDE0LTIyLTAwMDAwU0Y7ZXh0PXNmO21pbWU9O11JUkNBTSBhdWRpbyBzYW1wbGUgZmlsZQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTExIGJ5IENhcmwNCjAJc3RyaW5nCVxcMTQ0XFwyNDNcXDAwMlxcMAlbZmlkPTAwMDAwMTAxNC0yMi0wMDAwMFNGO2V4dD1zZjttaW1lPTtdSVJDQU0gYXVkaW8gc2FtcGxlIGZpbGUsIGJpZy1lbmRpYW4NCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQowCXN0cmluZwlcXDE0NFxcMjQzXFwwMDNcXDAJW2ZpZD0wMDAwMDEwMTQtMjItMDAwMDBTRjtleHQ9c2Y7bWltZT07XUlSQ0FNIGF1ZGlvIHNhbXBsZSBmaWxlLCBsaXR0bGUtZW
 5kaWFuDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTEgYnkgQ2FybA0KMAlzdHJpbmcJXFwxNDRcXDI0M1xcMDA0XFwwCVtmaWQ9MDAwMDAxMDE0LTIyLTAwMDAwU0Y7ZXh0PXNmO21pbWU9O11JUkNBTSBhdWRpbyBzYW1wbGUgZmlsZSwgYmlnLWVuZGlhbg0KDQojIE1hZ2ljIElEIGZvciBTY3JlYW10cmFja2VyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wNSBieSBDYXJsDQoweDRDCXN0cmluZwlTQ1JTCVtmaWQ9MDAwMTAwMDI1LTIyLTAwMDBTTVA7ZXh0PXNtcDttaW1lPTtdU2NyZWFtdHJhY2tlciBhdWRpbyBzYW1wbGUNCj4weDMwCXN0cmluZwl4CVt0aXRsZT0lLjMwc10NCg0KIyBNYWdpYyBJRCBmb3IgU291bmRUb29sIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wNyBieSBDYXJsDQowCXN0cmluZwlTT1VORFxceDFBCVtmaWQ9MDAwMTAwMDk1LTIyLTAwMDBTTkQ7ZXh0PXNuZDttaW1lPTtdU291bmQgdG9vbCBhdWRpbyBkYXRhIGZpbGUNCj4xNAlsZXNob3J0CXgJW2ZyZXE9JWRdDQoNCiMgTWFnaWMgSUQgZm9yIFNCU3R1ZGlvIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQowCXN0cmluZwlTTkRcXCAJW2ZpZD0wMDAxMDAwMjAtMjItMDAwMFNPVTtleHQ9c291O21pbWU9O11TQlN0dWRpbyBzYW1wbGVkIGF1ZGlvIGZpbGUNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNS0xOCBieSBDYXJsDQowCXN0cmluZwlTcGVleAlbZmlkPTAwMDAwMDA
 wMC0yMi0wMFNQRUVYO2V4dD1zcGVleDttaW1lPTtdU3BlZXggTG9zc3kgQXVkaW8gQ29kZWMgcmF3IGF1ZGlvIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgU291bmQgQmxhc3RlciBTREsgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTExIGJ5IENhcmwNCjAJc3RyaW5nCUNyZWF0aXZlXFwgVm9pY2VcXCBGaWxlXFx4MUEJW2ZpZD0wMDAwMDEwMTMtMjItMDAwMFZPQztleHQ9dm9jO21pbWU9O11DcmVhdGl2ZSBWb2ljZSBhdWRpbyBmaWxlDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDUtMTggYnkgQ2FybA0KMAlzdHJpbmcJdm9yYmlzCVtmaWQ9MDAwMDAwMDAwLTIyLTBWT1JCSVM7ZXh0PXZvcmJpczttaW1lPTtdVm9yYmlzIExvc3N5IEF1ZGlvIENvZGVjIHJhdyBhdWRpbyBmaWxlDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTEgYnkgQ2FybA0KMAlzdHJpbmcJUklGRglbZmlkPTAwMDAwMTAwMS0yMi0wMDAwV0FWO2V4dD13YXY7bWltZT07XU1pY3Jvc29mdCBXYXZlZm9ybSBBdWRpbyBmaWxlLCBsaXR0bGUtZW5kaWFuDQomOAlzdHJpbmcJV0FWRQkNCj4yNAlsZWxvbmcJPjAJW2ZyZXE9JWRdDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTEgYnkgQ2FybA0KMAlzdHJpbmcJUklGWAlbZmlkPTAwMDAwMTAwMS0yMi0wMDAwV0FWO2V4dD13YXY7bWltZT07XU1pY3Jvc29mdCBXYXZlZm9ybSBBdWRpbyBmaWxlLCBiaWctZW5kaWFuDQomOAlzdHJpbmcJV0FWRQkNCj4yNAliZWxvbmcJPjAJW2Zy
 ZXE9JWRdDQoNCiMgTWFnaWMgSUQgZm9yIE1heWEgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTEyIGJ5IENhcmwNCjAJc3RyaW5nCUZPUjQJW2ZpZD0wMDAwMDEzMTItMzEtMDAwMDAwMDtleHQ9O21pbWU9O11NYXlhIGltYWdlIGZpbGUNCiY4CXN0cmluZwlDSU1HCQ0KDQojIE1hZ2ljIElEIGZvciBNYXlhIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0xMiBieSBDYXJsDQowCXN0cmluZwlGT1I4CVtmaWQ9MDAwMDAxMzEyLTMxLTAwMDAwMDA7ZXh0PTttaW1lPTtdTWF5YSBpbWFnZSBmaWxlDQomOAlzdHJpbmcJQ0lNRwkNCg0KIyBNYWdpYyBJRCBmb3IgQU9MIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0wMiBieSBDYXJsDQowCXN0cmluZwlKR1xceDA0XFx4MEUJW2ZpZD0wMDAwMDEwMjMtMzEtMDAwMEFSVDtleHQ9YXJ0O21pbWU9O11BT0wvSm9obnNvbi1HcmFjZSBpbWFnZSBmaWxlLCB2ZXJzaW9uIDIuMA0KPjB4MEQJbGVzaG9ydAl4CVtyZXM9JWR4DQo+MHgwRglsZXNob3J0CXgJJWRdDQoNCiMgTWFnaWMgSUQgZm9yIEJNRiBpbWFnZSBjb21wcmVzc29yIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0yNyBieSBDYXJsDQowCXN0cmluZwlcXHg4MVxceDhBMAlbZmlkPTAwMDEwMDA0OC0zMS0wMDAwQk1GO2V4dD1ibWY7bWltZT07XUJNRiBpbWFnZSBmaWxlDQo+MglzdHJpbmcJeAksIHZlcnNpb24gJS4xcw0KPjMJc3RyaW5nCXgJLiUuMXMNCg0KIyBNYWdpY
 yBJRCBmb3IgQk1GIGltYWdlIGNvbXByZXNzb3IgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTI3IGJ5IENhcmwNCjAJc3RyaW5nCVxceDgxXFx4OEEyCVtmaWQ9MDAwMTAwMDQ4LTMxLTAwMDBCTUY7ZXh0PWJtZjttaW1lPTtdQk1GIGltYWdlIGZpbGUNCj4yCXN0cmluZwl4CSwgdmVyc2lvbiAlLjFzDQo+MwlzdHJpbmcJeAkuJS4xcw0KDQojIE1hZ2ljIElEIGZvciBCTUYgaW1hZ2UgY29tcHJlc3NvciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMjcgYnkgQ2FybA0KMAlzdHJpbmcJXFx4ODFcXHg4QTEJW2ZpZD0wMDAxMDAwNDgtMzEtMDAwMEJNRjtleHQ9Ym1mO21pbWU9O11CTUYgaW1hZ2UgZmlsZQ0KPjIJc3RyaW5nCXgJLCB2ZXJzaW9uICUuMXMNCj4zCXN0cmluZwl4CS4lLjFzDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTQgYnkgQ2FybA0KMAlzdHJpbmcJQk0JW2ZpZD0wMDAwMDEwMDEtMzEtMDAwMEJNUDtleHQ9Ym1wO21pbWU9O11XaW5kb3dzIG9yIE9TLzIgQml0bWFwIGltYWdlIGZpbGUNCiY2CWxlbG9uZwkwCQ0KDQojIE1hZ2ljIElEIGZvciBBdXRvZGVzayBBbmltYXRvciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMDggYnkgQ2FybA0KMAlsZXNob3J0CTB4OTExOQlbZmlkPTAwMDAwMTI1NC0zMS0wMDAwQ0VMO2V4dD1jZWwscGljO21pbWU9O11BdXRvZGVzayBhbmltYXRvciBpbWFnZSBmaWxlDQomMTAJYnl0ZQk4CQ0KJjExCWJ5dGUJMAkNCj4yCW
 xlc2hvcnQJeAlbcmVzPSVkDQo+NAlsZXNob3J0CXgJeCVkeDhicHBdDQoNCiMgTWFnaWMgSUQgZm9yIEFuZHJldyBVc2VyIEludGVyZmFjZSBTeXN0ZW0gZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTAyIGJ5IENhcmwNCjEJc3RyaW5nCWJlZ2luZGF0YXtyYXN0ZXIJW2ZpZD0wMDAwMDEzMTUtMzEtMDAwMENNVTtleHQ9Y211O21pbWU9O11BbmRyZXcgdG9vbGtpdCByYXN0ZXIgaW1hZ2UgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBBbml2Z2EgdG9vbGtpdCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMDIgYnkgQ2FybA0KMzgJc3RyaW5nCUtSXFx4MDFcXHgwMAlbZmlkPTAwMDEwMDEwNC0zMS0wMDAwQ09EO2V4dD1jb2Q7bWltZT07XUFuaXZnYSBzcHJpdGUgaW1hZ2UgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBXaW5kb3dzIEN1cnNvciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMDEgYnkgQ2FybA0KMAlzdHJpbmcJXFx4MDBcXHgwMFxceDAyXFx4MDAJW2ZpZD0wMDAwMDEwMDEtMzEtMDAwMENVUjtleHQ9Y3VyO21pbWU9O11NaWNyb3NvZnQgd2luZG93cyBjdXJzb3IgaW1hZ2UgZmlsZQ0KJjB4MDgJYnl0ZQkwCQ0KPjQJbGVzaG9ydAl4CSwgJWQgY3Vyc29yKHMpDQo+NglieXRlCXgJW3Jlcz0lZHgNCj43CWJ5dGUJeAklZHg4YnBwXQ0KDQojIE1hZ2ljIElEIGZvciBXaW5kb3dzIEN1cnNvciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMDEgYnkgQ2FybA0KMAl
 zdHJpbmcJXFx4MDBcXHgwMFxceDAyXFx4MDAJW2ZpZD0wMDAwMDEwMDEtMzEtMDAwMENVUjtleHQ9Y3VyO21pbWU9O11NaWNyb3NvZnQgd2luZG93cyBjdXJzb3IgaW1hZ2UgZmlsZQ0KJjB4MDgJYnl0ZQkxNgkNCj40CWxlc2hvcnQJeAksICVkIGN1cnNvcihzKQ0KPjYJYnl0ZQl4CVtyZXM9JWR4DQo+NwlieXRlCXgJJWR4NGJwcF0NCg0KIyBNYWdpYyBJRCBmb3IgV2luZG93cyBDdXJzb3IgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTAxIGJ5IENhcmwNCjAJc3RyaW5nCVxceDAwXFx4MDBcXHgwMlxceDAwCVtmaWQ9MDAwMDAxMDAxLTMxLTAwMDBDVVI7ZXh0PWN1cjttaW1lPTtdTWljcm9zb2Z0IHdpbmRvd3MgY3Vyc29yIGltYWdlIGZpbGUNCiYweDA4CWJ5dGUJMgkNCj40CWxlc2hvcnQJeAksICVkIGN1cnNvcihzKQ0KPjYJYnl0ZQl4CVtyZXM9JWR4DQo+NwlieXRlCXgJJWR4MWJwcF0NCg0KIyBNYWdpYyBJRCBmb3IgV2luZG93cyBDdXJzb3IgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTAxIGJ5IENhcmwNCjAJc3RyaW5nCVxceDAwXFx4MDBcXHgwMlxceDAwCVtmaWQ9MDAwMDAxMDAxLTMxLTAwMDBDVVI7ZXh0PWN1cjttaW1lPTtdTWljcm9zb2Z0IHdpbmRvd3MgY3Vyc29yIGltYWdlIGZpbGUNCiYweDA4CWJ5dGUJMzIJDQo+NAlsZXNob3J0CXgJLCAlZCBjdXJzb3IocykNCj42CWJ5dGUJeAlbcmVzPSVkeA0KPjcJYnl0ZQl4CSVkeDVicHBdDQoNCiMgTWFnaWMgSUQgZm9y
 IFdpbmRvd3MgQ3Vyc29yIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0wMSBieSBDYXJsDQowCXN0cmluZwlcXHgwMFxceDAwXFx4MDJcXHgwMAlbZmlkPTAwMDAwMTAwMS0zMS0wMDAwQ1VSO2V4dD1jdXI7bWltZT07XU1pY3Jvc29mdCB3aW5kb3dzIGN1cnNvciBpbWFnZSBmaWxlDQomMHgwOAlieXRlCTY0CQ0KPjQJbGVzaG9ydAl4CSwgJWQgY3Vyc29yKHMpDQo+NglieXRlCXgJW3Jlcz0lZHgNCj43CWJ5dGUJeAklZHg2YnBwXQ0KDQojIE1hZ2ljIElEIGZvciBXaW5kb3dzIEN1cnNvciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMDEgYnkgQ2FybA0KMAlzdHJpbmcJXFx4MDBcXHgwMFxceDAyXFx4MDAJW2ZpZD0wMDAwMDEwMDEtMzEtMDAwMENVUjtleHQ9Y3VyO21pbWU9O11NaWNyb3NvZnQgd2luZG93cyBjdXJzb3IgaW1hZ2UgZmlsZQ0KJjB4MDgJYnl0ZQk4CQ0KPjQJbGVzaG9ydAl4CSwgJWQgY3Vyc29yKHMpDQo+NglieXRlCXgJW3Jlcz0lZHgNCj43CWJ5dGUJeAklZHgzYnBwXQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTEzIGJ5IENhcmwNCjEyOAlzdHJpbmcJRElDTQlbZmlkPTAwMDAwMDAwNC0zMS0wMDBESUNNO2V4dD1kaWNtLGRjbTttaW1lPTtdRGlnaXRhbCBpbWFnaW5nIGFuZCBjb21tdW5pY2F0aW9uIGluIG1lZGVjaW5lIGltZy4NCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0wMSBieSBDYXJsDQowCXN0cmluZwlTRFBYCVtmaWQ9MDAwMDAxMzA5LTMxLTAwM
 DBEUFg7ZXh0PWRweDttaW1lPTtdRGlnaXRhbCBNb3ZpbmctUGljdHVyZSBFeGNoYW5nZSBpbWFnZSBmaWxlDQo+MTYwCXN0cmluZwk+XFx4MDAJW2NyZWF0b3I9JS4xMDBzXQ0KPjI2MAlzdHJpbmcJPlxceDAwCVt0aXRsZT0lLjIwMHNdDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMDEgYnkgQ2FybA0KMAlzdHJpbmcJWFBEUwlbZmlkPTAwMDAwMTMwOS0zMS0wMDAwRFBYO2V4dD1kcHg7bWltZT07XURpZ2l0YWwgTW92aW5nLVBpY3R1cmUgRXhjaGFuZ2UgaW1hZ2UgZmlsZQ0KPjE2MAlzdHJpbmcJPlxceDAwCVtjcmVhdG9yPSUuMTAwc10NCj4yNjAJc3RyaW5nCT5cXHgwMAlbdGl0bGU9JS4yMDBzXQ0KDQojIE1hZ2ljIElEIGZvciBMaWdodHdhdmUgM0QgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTEyIGJ5IENhcmwNCjAJc3RyaW5nCUZPUk0JW2ZpZD0wMDAwMDEyNTEtMzEtMDAwRlBCTTtleHQ9ZnBibTttaW1lPTtdRmxleGlibGUgUHJlY2lzaW9uIEJ1ZmZlciBNYXAgaW1hZ2UgZmlsZQ0KJjgJc3RyaW5nCUZQQk0JDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTQgYnkgQ2FybA0KMAlzdHJpbmcJR0lGOAlbZmlkPTAwMDAwMTI3NC0zMS0wMDAwR0lGO2V4dD1naWY7bWltZT1pbWFnZS9naWY7XUdJRiBpbWFnZSBmaWxlDQomMTAJYnl0ZSYweDcwCSEweDcwCQ0KPjQJc3RyaW5nCXgJLCB2ZXJzaW9uIDglLjJzDQo+NglsZXNob3J0CT4wCVtyZXM9JWR4DQo+OAlsZXNob3J0CT4wCSVkXQ
 0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE0IGJ5IENhcmwNCjAJc3RyaW5nCUdJRjgJW2ZpZD0wMDAwMDEyNzQtMzEtMDAwMEdJRjtleHQ9Z2lmO21pbWU9aW1hZ2UvZ2lmO11HSUYgaW1hZ2UgZmlsZQ0KJjEwCWJ5dGUmMHg3MAkweDcwCQ0KPjQJc3RyaW5nCXgJLCB2ZXJzaW9uIDglLjJzDQo+NglsZXNob3J0CT4wCVtyZXM9JWR4DQo+OAlsZXNob3J0CT4wCSVkeDhicHBdDQoNCiMgTWFnaWMgSUQgZm9yIFdpbmRvd3MgSWNvbiBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMDEgYnkgQ2FybA0KMAlzdHJpbmcJXFx4MDBcXHgwMFxceDAxXFx4MDAJW2ZpZD0wMDAwMDEwMDEtMzEtMDAwMElDTztleHQ9aWNvO21pbWU9aW1hZ2Uvdm5kLm1pY3Jvc29mdC5pY29uO11NaWNyb3NvZnQgd2luZG93cyBJY29uIGltYWdlIGZpbGUNCiYweDA4CWJ5dGUJMAkNCj40CWxlc2hvcnQJeAksICVkIGljb24ocykNCj42CWJ5dGUJeAlbcmVzPSVkeA0KPjcJYnl0ZQl4CSVkeDhicHBdDQoNCiMgTWFnaWMgSUQgZm9yIFdpbmRvd3MgSWNvbiBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMDEgYnkgQ2FybA0KMAlzdHJpbmcJXFx4MDBcXHgwMFxceDAxXFx4MDAJW2ZpZD0wMDAwMDEwMDEtMzEtMDAwMElDTztleHQ9aWNvO21pbWU9aW1hZ2Uvdm5kLm1pY3Jvc29mdC5pY29uO11NaWNyb3NvZnQgd2luZG93cyBJY29uIGltYWdlIGZpbGUNCiYweDA4CWJ5dGUJMTYJDQo+NAlsZXNob3J0CXgJLCAlZCBpY29
 uKHMpDQo+NglieXRlCXgJW3Jlcz0lZHgNCj43CWJ5dGUJeAklZHg0YnBwXQ0KDQojIE1hZ2ljIElEIGZvciBXaW5kb3dzIEljb24gZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTAxIGJ5IENhcmwNCjAJc3RyaW5nCVxceDAwXFx4MDBcXHgwMVxceDAwCVtmaWQ9MDAwMDAxMDAxLTMxLTAwMDBJQ087ZXh0PWljbzttaW1lPWltYWdlL3ZuZC5taWNyb3NvZnQuaWNvbjtdTWljcm9zb2Z0IHdpbmRvd3MgSWNvbiBpbWFnZSBmaWxlDQomMHgwOAlieXRlCTIJDQo+NAlsZXNob3J0CXgJLCAlZCBpY29uKHMpDQo+NglieXRlCXgJW3Jlcz0lZHgNCj43CWJ5dGUJeAklZHgxYnBwXQ0KDQojIE1hZ2ljIElEIGZvciBXaW5kb3dzIEljb24gZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTAxIGJ5IENhcmwNCjAJc3RyaW5nCVxceDAwXFx4MDBcXHgwMVxceDAwCVtmaWQ9MDAwMDAxMDAxLTMxLTAwMDBJQ087ZXh0PWljbzttaW1lPWltYWdlL3ZuZC5taWNyb3NvZnQuaWNvbjtdTWljcm9zb2Z0IHdpbmRvd3MgSWNvbiBpbWFnZSBmaWxlDQomMHgwOAlieXRlCTMyCQ0KPjQJbGVzaG9ydAl4CSwgJWQgaWNvbihzKQ0KPjYJYnl0ZQl4CVtyZXM9JWR4DQo+NwlieXRlCXgJJWR4NWJwcF0NCg0KIyBNYWdpYyBJRCBmb3IgV2luZG93cyBJY29uIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0wMSBieSBDYXJsDQowCXN0cmluZwlcXHgwMFxceDAwXFx4MDFcXHgwMAlbZmlkPTAwMDAwMTAwMS0zMS0wMDAw
 SUNPO2V4dD1pY287bWltZT1pbWFnZS92bmQubWljcm9zb2Z0Lmljb247XU1pY3Jvc29mdCB3aW5kb3dzIEljb24gaW1hZ2UgZmlsZQ0KJjB4MDgJYnl0ZQk2NAkNCj40CWxlc2hvcnQJeAksICVkIGljb24ocykNCj42CWJ5dGUJeAlbcmVzPSVkeA0KPjcJYnl0ZQl4CSVkeDZicHBdDQoNCiMgTWFnaWMgSUQgZm9yIFdpbmRvd3MgSWNvbiBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMDEgYnkgQ2FybA0KMAlzdHJpbmcJXFx4MDBcXHgwMFxceDAxXFx4MDAJW2ZpZD0wMDAwMDEwMDEtMzEtMDAwMElDTztleHQ9aWNvO21pbWU9aW1hZ2Uvdm5kLm1pY3Jvc29mdC5pY29uO11NaWNyb3NvZnQgd2luZG93cyBJY29uIGltYWdlIGZpbGUNCiYweDA4CWJ5dGUJOAkNCj40CWxlc2hvcnQJeAksICVkIGljb24ocykNCj42CWJ5dGUJeAlbcmVzPSVkeA0KPjcJYnl0ZQl4CSVkeDNicHBdDQoNCiMgTWFnaWMgSUQgZm9yIFN1bk9TIEljb24gZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTAyIGJ5IENhcmwNCjAJc3RyaW5nCS8qXFwgRm9ybWF0X3ZlcnNpb249MSxcXCAJW2ZpZD0wMDAwMDEwMTEtMzEtMDAwSUNPTjtleHQ9aWNvbjttaW1lPTtdU3VuT1MgaWNvbiBpbWFnZSBmaWxlDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMzEgYnkgQ2FybA0KMAliZXNob3J0CTB4MDEJW2ZpZD0wMDAwMDEyNzMtMzEtMDAwMElNRztleHQ9aW1nO21pbWU9O11HRU0gQml0IEltYWdlDQomMgliZXNob3J0CTB4MDgJD
 Qo+MTIJYmVzaG9ydAk+MAlbcmVzPSVkeA0KPjE0CWJlc2hvcnQJPjAJJWQNCj40CWJlc2hvcnQJPjAJeCVkYnBwXQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTAyIGJ5IENhcmwNCjAJc3RyaW5nCVxceDhiSk5HXFx4MGRcXHgwYVxceDFhXFx4MGEJW2ZpZD0wMDAwMDAwMDAtMzEtMDAwMEpORztleHQ9am5nO21pbWU9O11KUEVHIE5ldHdvcmsgZ3JhcGhpY3MgaW1hZ2UgZmlsZQ0KJjEyCXN0cmluZwlKSERSCQ0KPjE2CWJlbG9uZwl4CVtyZXM9JWQNCj4yMAliZWxvbmcJeAl4JWRdDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMTMgYnkgQ2FybA0KMAlzdHJpbmcJXFx4MDBcXHgwMFxceDAwXFx4MGNqUFxceDIwXFx4MjAJW2ZpZD0wMDAwMDAwMDEtMzEtMDAxNTQ0NDtleHQ9anAyO21pbWU9aW1hZ2UvanAyO11KUEVHIDIwMDAgaW1hZ2UgZmlsZQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTEzIGJ5IENhcmwNCjAJc3RyaW5nCVxceGZmXFx4NGZcXHhmZlxceDUxCVtmaWQ9MDAwMDAwMDAxLTMxLTAwMTU0NDQ7ZXh0PWpwYzttaW1lPTtdSlBFRyAyMDAwIGNvZGUgc3RyZWFtIGltYWdlIGZpbGUNCiZaMgliZXNob3J0CTB4RkZEOQkNCj44CWJlbG9uZwl4CVtyZXM9JWR4DQo+MTIJYmVsb25nCXgJJWRdDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMzEgYnkgQ2FybA0KMAliZWxvbmcJMHhmZmQ4ZmZlMAlbZmlkPTAwMDAwMTMwNS0zMS0wMDBKUEVHO2V4dD1qcGVnLGpwZzttaW1lPWltYWdlL2pwZW
 c7XUpvaW50IFBob3RvZ3JhcGhpYyBFeHBlcnRzIEdyb3VwIEpGSUYgaW1hZ2UgZmlsZQ0KJjYJc3RyaW5nCUpGSUZcXHgwMAkNCj4xMQlieXRlCXgJLCB2ZXJzaW9uICVkDQo+MTIJYnl0ZQl4CS4wJWQNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0xMyBieSBDYXJsDQowCXN0cmluZwlcXHhmZlxceGQ4XFx4ZmZcXHhlMQlbZmlkPTAwMDAwMDAwNS0zMS0wMDBKUEVHO2V4dD1qcGcsanBlZzttaW1lPWltYWdlL2pwZWc7XURpZ2l0YWwgc3RpbGwgY2FtZXJhIGltYWdlIGZpbGUNCiY2CXN0cmluZwlFeGlmCQ0KDQojIE1hZ2ljIElEIGZvciBEZWx1eGUgUGFpbnQgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAzLTMxIGJ5IENhcmwNCjAJc3RyaW5nCUZPUk0JW2ZpZD0wMDAwMDEwMTAtMzEtMDAwMExCTTtleHQ9bGJtO21pbWU9O11JbnRlcmxlYXZlZCBiaXRtYXAgaW1hZ2UgZmlsZQ0KJjgJc3RyaW5nCUlMQk0JDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMTMgYnkgQ2FybA0KMAlzdHJpbmcJVGhpc1xcIGlzXFwgYVxcIEJpdE1hcFxcIGZpbGUJW2ZpZD0wMDAwMDAwMDAtMzEtMDAwTElTUDtleHQ9bGlzcDttaW1lPTtdTGlzcCBtYWNoaW5lIGZvcm1hdCBpbWFnZSBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIE1pY3JvZGVzaWduMiwgTWljcm9kZXNpZ24zIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0wMiBieSBDYXJsDQowCXN0cmluZwkuTURBCVtmaWQ9MDAwMDAxMzE2LTMxLTAwMDBNREE
 7ZXh0PW1kYTttaW1lPTtdTWljcm9kZXNpZ24gQXJlYSBpbWFnZSBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIE1pY3JvZGVzaWduMiwgTWljcm9kZXNpZ24zIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0wMiBieSBDYXJsDQowCXN0cmluZwkuTURQCVtmaWQ9MDAwMDAxMzE2LTMxLTAwMDBNRFA7ZXh0PW1kcDttaW1lPTtdTWljcm9kZXNpZ24gcGFnZSBpbWFnZSBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIEltYWdlbWFnaWNrIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0wMSBieSBDYXJsDQowCXN0cmluZwlpZD1JbWFnZU1hZ2ljawlbZmlkPTAwMDEwMDEwMS0zMS0wMDBNSUZGO2V4dD1taWZmLG1pZjttaW1lPTtdSW1hZ2VtYWdpY2sgaW1hZ2UgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBOZXRwYm0gZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTAyIGJ5IENhcmwNCjAJc3RyaW5nCU1SRjEJW2ZpZD0wMDAxMDAxMDUtMzEtMDAwME1SRjtleHQ9bXJmO21pbWU9O11Nb25vY2hyb21lIHJlY3Vyc2l2ZSBmb3JtYXQgaW1hZ2UgZmlsZQ0KPjQJYmVsb25nCXgJW3Jlcz0lZHgNCj44CWJlbG9uZwl4CSVkeDFicHBdDQoNCiMgTWFnaWMgSUQgZm9yIE1pY3Jvc29mdCBQYWludCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMDIgYnkgQ2FybA0KMAlsZXNob3J0CTB4NjE0NAlbZmlkPTAwMDAwMTAwMS0zMS0wMDAwTVNQO2V4dD1tc3A7bWltZT07XU1pY3Jvc29mdCBwYWludCBpbWFn
 ZSBmaWxlLCB2ZXJzaW9uIDEuMA0KJjIJbGVzaG9ydAkweDRkNmUJDQo+NAlsZXNob3J0CXgJW3Jlcz0lZHgNCj42CWxlc2hvcnQJeAklZHgxYnBwXQ0KDQojIE1hZ2ljIElEIGZvciBNaWNyb3NvZnQgUGFpbnQgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTAyIGJ5IENhcmwNCjAJbGVzaG9ydAkweDY5NGMJW2ZpZD0wMDAwMDEwMDEtMzEtMDAwME1TUDtleHQ9bXNwO21pbWU9O11NaWNyb3NvZnQgcGFpbnQgaW1hZ2UgZmlsZSwgdmVyc2lvbiAyLjANCiYyCWxlc2hvcnQJMHg1MzZlCQ0KPjQJbGVzaG9ydAl4CVtyZXM9JWR4DQo+NglsZXNob3J0CXgJJWR4MWJwcF0NCg0KIyBNYWdpYyBJRCBmb3IgTmV0cGJtIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0zMSBieSBDYXJsDQowCXN0cmluZwlQN1xceDBBCVtmaWQ9MDAwMTAwMTAwLTMxLTAwMDBQQU07ZXh0PXBhbTttaW1lPTtdUG9ydGFibGUgYXJiaXRyYXJ5IG1hcCBpbWFnZSBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIE5ldHBibSBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMzEgYnkgQ2FybA0KMAlzdHJpbmcJUDEJW2ZpZD0wMDAxMDAxMDAtMzEtMDAwMFBCTTtleHQ9cGJtO21pbWU9O11Qb3J0YWJsZSBiaXRtYXAgaW1hZ2UgZmlsZSwgYXNjaWkNCg0KIyBNYWdpYyBJRCBmb3IgTmV0cGJtIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0zMSBieSBDYXJsDQowCXN0cmluZwlQNAlbZmlkPTAwMDEwMDEwMC0zMS0wM
 DAwUEJNO2V4dD1wYm07bWltZT07XVBvcnRhYmxlIGJpdG1hcCBpbWFnZSBmaWxlLCBiaW5hcnkNCg0KIyBNYWdpYyBJRCBmb3IgUEMtUGFpbnRicnVzaCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMzEgYnkgQ2FybA0KMAliZXNob3J0CTB4MEEwMAlbZmlkPTAwMDAwMTI1Ny0zMS0wMDAwUENYO2V4dD1wY3g7bWltZT07XVBDLVBhaW50YnJ1c2ggaW1hZ2UgZmlsZSwgdmVyc2lvbiAyLjUNCiYyCWJ5dGUJMQkNCg0KIyBNYWdpYyBJRCBmb3IgUEMtUGFpbnRicnVzaCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMzEgYnkgQ2FybA0KMAliZXNob3J0CTB4MEEwMglbZmlkPTAwMDAwMTI1Ny0zMS0wMDAwUENYO2V4dD1wY3g7bWltZT07XVBDLVBhaW50YnJ1c2ggaW1hZ2UgZmlsZSwgdmVyc2lvbiAyLjgNCiYyCWJ5dGUJMQkNCg0KIyBNYWdpYyBJRCBmb3IgUEMtUGFpbnRicnVzaCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMzEgYnkgQ2FybA0KMAliZXNob3J0CTB4MEEwMwlbZmlkPTAwMDAwMTI1Ny0zMS0wMDAwUENYO2V4dD1wY3g7bWltZT07XVBDLVBhaW50YnJ1c2ggaW1hZ2UgZmlsZSwgdmVyc2lvbiAyLjgNCiYyCWJ5dGUJMQkNCg0KIyBNYWdpYyBJRCBmb3IgUEMtUGFpbnRicnVzaCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMzEgYnkgQ2FybA0KMAliZXNob3J0CTB4MEEwNAlbZmlkPTAwMDAwMTI1Ny0zMS0wMDAwUENYO2V4dD1wY3g7bWltZT07XVBDLV
 BhaW50YnJ1c2ggZm9yIHdpbmRvd3MgaW1hZ2UgZmlsZQ0KJjIJYnl0ZQkxCQ0KDQojIE1hZ2ljIElEIGZvciBQQy1QYWludGJydXNoIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0zMSBieSBDYXJsDQowCWJlc2hvcnQJMHgwQTA1CVtmaWQ9MDAwMDAxMjU3LTMxLTAwMDBQQ1g7ZXh0PXBjeDttaW1lPTtdUEMtUGFpbnRicnVzaCBpbWFnZSBmaWxlLCB2ZXJzaW9uIDMuMA0KJjIJYnl0ZQkxCQ0KDQojIE1hZ2ljIElEIGZvciBOZXRwYm0gZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAzLTMxIGJ5IENhcmwNCjAJc3RyaW5nCVAyCVtmaWQ9MDAwMTAwMTAwLTMxLTAwMDBQR007ZXh0PXBnbTttaW1lPTtdUG9ydGFibGUgZ3JheSBtYXAgaW1hZ2UgZmlsZSwgYXNjaWkNCg0KIyBNYWdpYyBJRCBmb3IgTmV0cGJtIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0zMSBieSBDYXJsDQowCXN0cmluZwlQNQlbZmlkPTAwMDEwMDEwMC0zMS0wMDAwUEdNO2V4dD1wZ207bWltZT07XVBvcnRhYmxlIGdyYXkgbWFwIGltYWdlIGZpbGUsIGJpbmFyeQ0KDQojIE1hZ2ljIElEIGZvciBQQyBQYWludCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMDggYnkgQ2FybA0KMAlsZXNob3J0CTB4MTIzNAlbZmlkPTAwMDAwMTMxOC0zMS0wMDAwUElDO2V4dD1waWM7bWltZT07XVBpY3RvciBQQyBQYWludCBpbWFnZSBmaWxlDQomMTEJYnl0ZQkweEZGCQ0KJjEwCWJ5dGUJMHgwMgkNCj4yCWxlc2hvcnQ
 JeAlbcmVzPSVkDQo+NAlsZXNob3J0CXgJeCVkXQ0KDQojIE1hZ2ljIElEIGZvciBQQyBQYWludCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMDggYnkgQ2FybA0KMAlsZXNob3J0CTB4MTIzNAlbZmlkPTAwMDAwMTMxOC0zMS0wMDAwUElDO2V4dD1waWM7bWltZT07XVBpY3RvciBQQyBQYWludCBpbWFnZSBmaWxlDQomMTEJYnl0ZQkweEZGCQ0KJjEwCWJ5dGUJMHgwOAkNCj4yCWxlc2hvcnQJeAlbcmVzPSVkDQo+NAlsZXNob3J0CXgJeCVkXQ0KDQojIE1hZ2ljIElEIGZvciBQQyBQYWludCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMDggYnkgQ2FybA0KMAlsZXNob3J0CTB4MTIzNAlbZmlkPTAwMDAwMTMxOC0zMS0wMDAwUElDO2V4dD1waWM7bWltZT07XVBpY3RvciBQQyBQYWludCBpbWFnZSBmaWxlDQomMTEJYnl0ZQkweEZGCQ0KJjEwCWJ5dGUJMHgzMQkNCj4yCWxlc2hvcnQJeAlbcmVzPSVkDQo+NAlsZXNob3J0CXgJeCVkXQ0KDQojIE1hZ2ljIElEIGZvciBTb2Z0aW1hZ2UgM0QgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTA4IGJ5IENhcmwNCjAJYmVsb25nCTB4NTM4MGY2MzQJW2ZpZD0wMDAwMDEzMjEtMzEtMDAwMFBJQztleHQ9cGljO21pbWU9O11Tb2Z0aW1hZ2UgM0QgaW1hZ2UgZmlsZQ0KJjg4CXN0cmluZwlQSUNUCQ0KPjgJc3RyaW5nCT5cXHgwMAlbdGl0bGU9JS44MHNdDQo+OTIJYmVzaG9ydAl4CVtyZXM9JWQNCj45NAliZXNob3J0CXgJeCVkXQ0KDQoj
 IE1hZ2ljIElEIGZvciBCaW8tcmFkIG1pY3Jvc2NvcGUgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTA4IGJ5IENhcmwNCjU0CWxlc2hvcnQJMTIzNDUJW2ZpZD0wMDAwMDEzMjItMzEtMDAwMFBJQztleHQ9cGljO21pbWU9O11CaW8tcmFkIGNvbmZvY2FsIG1pY3Jvc2NvcGUgaW1hZ2UgZmlsZQ0KJjE2CWxlc2hvcnQJMAkNCj4wCWxlc2hvcnQJPjAJW3Jlcz0lZA0KPjIJbGVzaG9ydAk+MAl4JWRdDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMDYgYnkgQ2FybA0KMAlzdHJpbmcJXFx4ODlQTkdcXHgwZFxceDBhXFx4MWFcXHgwYQlbZmlkPTAwMDAwMDAwMS0zMS0wMDE1OTQ4O2V4dD1wbmc7bWltZT1pbWFnZS9wbmc7XVBvcnRhYmxlIE5ldHdvcmsgR3JhcGhpYyBmaWxlDQomMTIJc3RyaW5nCUlIRFIJDQo+MTYJYmVsb25nCT4wCVtyZXM9JWQNCj4yMAliZWxvbmcJPjAJeCVkDQo+MjQJYnl0ZQl4CXglZGJwcF0NCg0KIyBNYWdpYyBJRCBmb3IgTmV0cGJtIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0zMSBieSBDYXJsDQowCXN0cmluZwlQMwlbZmlkPTAwMDEwMDEwMC0zMS0wMDAwUFBNO2V4dD1wcG07bWltZT07XVBvcnRhYmxlIHBpeGVsIG1hcCBpbWFnZSBmaWxlLCBhc2NpaQ0KDQojIE1hZ2ljIElEIGZvciBOZXRwYm0gZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAzLTMxIGJ5IENhcmwNCjAJc3RyaW5nCVA2CVtmaWQ9MDAwMTAwMTAwLTMxLTAwMDBQUE07ZXh0PXBwbTtta
 W1lPTtdUG9ydGFibGUgcGl4ZWwgbWFwIGltYWdlIGZpbGUsIGJpbmFyeQ0KDQojIE1hZ2ljIElEIGZvciBQaG90b3Nob3AgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTAxIGJ5IENhcmwNCjAJc3RyaW5nCThCUFNcXHgwMFxceDAxCVtmaWQ9MDAwMDAxMDAzLTMxLTAwMDBQU0Q7ZXh0PXBzZDttaW1lPTtdQWRvYmUgUGhvdG9zaG9wIGltYWdlIGZpbGUNCj4xOAliZWxvbmcJPjAJW3Jlcz0lZHgNCj4xNAliZWxvbmcJPjAJJWRdDQoNCiMgTWFnaWMgSUQgZm9yIFBhaW50IHNob3AgcHJvIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0wMSBieSBDYXJsDQowCXN0cmluZwlQYWludCBTaG9wIFBybyBJbWFnZSBGaWxlXFx4MGFcXHgxYQlbZmlkPTAwMDAwMTMxMC0zMS0wMDAwUFNQO2V4dD1wc3A7bWltZT07XVBhaW50c2hvcCBwcm8gaW1hZ2UgZmlsZQ0KPjUwCWxlbG9uZwk+MAlbcmVzPSVkeA0KPjU0CWxlbG9uZwk+MAklZA0KPjY5CWxlc2hvcnQJPjAJeCVkYnBwXQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTA4IGJ5IENhcmwNCjAJYmVsb25nCTB4NTlhNjZhOTUJW2ZpZD0wMDAwMDEwMTEtMzEtMDAwMFJBUztleHQ9cmFzO21pbWU9O11TdW4gcmFzdGVyIGltYWdlDQo+NAliZWxvbmcJPjAJW3Jlcz0lZHgNCj44CWJlbG9uZwk+MAklZA0KPjEyCWJlbG9uZwk+MAl4JWRicHBdDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMzEgYnkgQ2FybA0KMAlzdHJpbmcJXFwweDAxXFx4REEJW2
 ZpZD0wMDAwMDEwMDQtMzEtMDAwMFJHQjtleHQ9cmdiO21pbWU9O11TR0kgSW1hZ2UgZmlsZQ0KPjI0CXN0cmluZwk+XFx4MDAJW3RpdGxlPSUuODBzXQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAzLTMxIGJ5IENhcmwNCjAJc3RyaW5nCVxceDAxXFx4REFcXHgwMFxceDAxCVtmaWQ9MDAwMDAxMDA0LTMxLTAwMDBSR0I7ZXh0PXJnYjttaW1lPTtdU0dJIEltYWdlIGZpbGUNCiYxMAliZXNob3J0CTEJDQo+NgliZXNob3J0CXgJW3Jlcz0lZHgNCj44CWJlc2hvcnQJeAklZHg4YnBwXQ0KPjI0CXN0cmluZwk+XFx4MAlbdGl0bGU9JS44MHNdDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMzEgYnkgQ2FybA0KMAlzdHJpbmcJXFx4MDFcXHhEQVxceDAwXFx4MDEJW2ZpZD0wMDAwMDEwMDQtMzEtMDAwMFJHQjtleHQ9cmdiO21pbWU9O11TR0kgSW1hZ2UgZmlsZQ0KJjEwCWJlc2hvcnQJMwkNCj42CWJlc2hvcnQJeAlbcmVzPSVkeA0KPjgJYmVzaG9ydAl4CSVkeDI0YnBwXQ0KPjI0CXN0cmluZwk+XFx4MAlbdGl0bGU9JS44MHNdDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMzEgYnkgQ2FybA0KMAlzdHJpbmcJXFx4MDFcXHhEQVxceDAwXFx4MDEJW2ZpZD0wMDAwMDEwMDQtMzEtMDAwMFJHQjtleHQ9cmdiO21pbWU9O11TR0kgSW1hZ2UgZmlsZQ0KJjEwCWJlc2hvcnQJNAkNCj42CWJlc2hvcnQJeAlbcmVzPSVkeA0KPjgJYmVzaG9ydAl4CSVkeDI0YnBwXQ0KPjI0CXN0cmluZwk+XFx4MAlbdGl0bGU9JS44MHN
 dDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMzEgYnkgQ2FybA0KMAlzdHJpbmcJXFx4MDFcXHhEQVxceDAxXFx4MDEJW2ZpZD0wMDAwMDEwMDQtMzEtMDAwMFJHQjtleHQ9cmdiO21pbWU9O11TR0kgSW1hZ2UgZmlsZSwgY29tcHJlc3NlZA0KJjEwCWJlc2hvcnQJMQkNCj42CWJlc2hvcnQJeAlbcmVzPSVkeA0KPjgJYmVzaG9ydAl4CSVkeDhicHBdDQo+MjQJc3RyaW5nCT5cXHgwCVt0aXRsZT0lLjgwc10NCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0zMSBieSBDYXJsDQowCXN0cmluZwlcXHgwMVxceERBXFx4MDFcXHgwMQlbZmlkPTAwMDAwMTAwNC0zMS0wMDAwUkdCO2V4dD1yZ2I7bWltZT07XVNHSSBJbWFnZSBmaWxlLCBjb21wcmVzc2VkDQomMTAJYmVzaG9ydAkzCQ0KPjYJYmVzaG9ydAl4CVtyZXM9JWR4DQo+OAliZXNob3J0CXgJJWR4MjRicHBdDQo+MjQJc3RyaW5nCT5cXHgwCVt0aXRsZT0lLjgwc10NCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0zMSBieSBDYXJsDQowCXN0cmluZwlcXHgwMVxceERBXFx4MDFcXHgwMQlbZmlkPTAwMDAwMTAwNC0zMS0wMDAwUkdCO2V4dD1yZ2I7bWltZT07XVNHSSBJbWFnZSBmaWxlLCBjb21wcmVzc2VkDQomMTAJYmVzaG9ydAk0CQ0KPjYJYmVzaG9ydAl4CVtyZXM9JWR4DQo+OAliZXNob3J0CXgJJWR4MjRicHBdDQo+MjQJc3RyaW5nCT5cXHgwCVt0aXRsZT0lLjgwc10NCg0KIyBNYWdpYyBJRCBmb3IgVHVyYm8gU2lsdmVyIGZpbGVzLg0KIyBTdWJtaXR0
 ZWQgb24gMjAwNC0wNC0xMyBieSBDYXJsDQowCXN0cmluZwlGT1JNCVtmaWQ9MDAwMDAxMjUyLTMxLTAwMFJHQjg7ZXh0PXJnYjgscmdiO21pbWU9O11UdXJibyBTaWx2ZXIgMjQtYml0IFJHQiBpbWFnZSBmaWxlDQomOAlzdHJpbmcJUkdCOAkNCg0KIyBNYWdpYyBJRCBmb3IgVHVyYm8gU2lsdmVyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0xMyBieSBDYXJsDQowCXN0cmluZwlGT1JNCVtmaWQ9MDAwMDAxMjUyLTMxLTAwMFJHQk47ZXh0PXJnYm4scmdiO21pbWU9O11UdXJibyBTaWx2ZXIgMTItYml0IFJHQiBpbWFnZSBmaWxlDQomOAlzdHJpbmcJUkdCTgkNCg0KIyBNYWdpYyBJRCBmb3IgQ29sb1JJWCBWR0EgUGFpbnQgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTA4IGJ5IENhcmwNCjAJc3RyaW5nCVJJWDMJW2ZpZD0wMDAwMDEzMjAtMzEtMDAwMFNDWjtleHQ9c2N6O21pbWU9O11Db2xvclJJWCBWR0EgUGFpbnQgaW1hZ2UgZmlsZQ0KPjQJbGVzaG9ydAl4CVtyZXM9JWQNCj42CWxlc2hvcnQJeAl4JWRdDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMTMgYnkgQnJ5YW4gSGVuZGVyc29uDQowCXN0cmluZwlcXDExN1xcMDcyCVtmaWQ9MDAwMDAwMDAwLTMxLTAwMDBTSVI7ZXh0PXNpcjttaW1lPTtdU29saXRhaXJlIGltYWdlIHJlY29yZGVyIGltYWdlIGZpbGUsIE1HSSB0eXBlIDExDQomNAlzdHJpbmcJXFwwMTMJDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMTMgYnkgQnJ5Y
 W4gSGVuZGVyc29uDQowCXN0cmluZwlcXDExN1xcMDcyCVtmaWQ9MDAwMDAwMDAwLTMxLTAwMDBTSVI7ZXh0PXNpcjttaW1lPTtdU29saXRhaXJlIGltYWdlIHJlY29yZGVyIGltYWdlIGZpbGUsIE1HSSB0eXBlIDE3DQomNAlzdHJpbmcJXFwwMjEJDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMDEgYnkgQ2FybA0KWjE4CXN0cmluZwlUUlVFVklTSU9OLVhGSUxFCVtmaWQ9MDAwMDAxMzA2LTMxLTAwMDBUR0E7ZXh0PXRnYTttaW1lPTtdVHJ1ZXZpc2lvbiBUYXJnYSBpbWFnZSBmaWxlDQo+MTIJbGVzaG9ydAl4CVtyZXM9JWR4DQo+MTQJbGVzaG9ydAl4CSVkDQo+MTYJYnl0ZQl4CXglZGJwcF0NCg0KIyBNYWdpYyBJRCBmb3IgR3JhcGhpY3MgV29ya3Nob3AgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTAxIGJ5IENhcmwNCjAJc3RyaW5nCVRITkwJW2ZpZD0wMDAwMDEyNTgtMzEtMDAwMFRITjtleHQ9dGhuO21pbWU9O11HcmFwaGljcyB3b3Jrc2hvcCB0aHVtYm5haWwgaW1hZ2UgZmlsZQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAzLTMxIGJ5IENhcmwNCjAJc3RyaW5nCUlJCVtmaWQ9MDAwMDAxMDAzLTMxLTAwMDBUSUY7ZXh0PXRpZix0aWZmLGRuZzttaW1lPWltYWdlL3RpZmY7XVRhZ2dlZCBpbWFnZSBmaWxlIGZvcm1hdCBpbWFnZSBmaWxlLCBsaXR0bGUtZW5kaWFuDQomMglsZXNob3J0CTQyCQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAzLTMxIGJ5IENhcmwNCjAJc3RyaW5nCU1NCVtmaW
 Q9MDAwMDAxMDAzLTMxLTAwMDBUSUY7ZXh0PXRpZix0aWZmLGRuZzttaW1lPWltYWdlL3RpZmY7XVRhZ2dlZCBpbWFnZSBmaWxlIGZvcm1hdCBpbWFnZSBmaWxlLCBiaWctZW5kaWFuDQomMglsZXNob3J0CQkNCg0KIyBNYWdpYyBJRCBmb3IgVklDQVIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTAxIGJ5IENhcmwNCjAJc3RyaW5nCUxCTFNJWkU9CVtmaWQ9MDAwMDAxMDIyLTMxLTAwMDBWSUM7ZXh0PXZpYyx2aWNhcjttaW1lPTtdVmljYXIgaW1hZ2UgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBLaG9yb3MgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTAxIGJ5IENhcmwNCjAJc3RyaW5nCVxceEFCXFx4MDFcXHgwMVxceDAzCVtmaWQ9MDAwMDAxMzA4LTMxLTAwMDBWSUY7ZXh0PXZpZix2aWZmO21pbWU9O11LaG9yb3MgVmlzdWFsaXphdGlvbi9JbWFnZSBGaWxlIEZvcm1hdCwgdmVyc2lvbiAxLjMNCiY0CWJ5dGUJMgkNCj41MjAJYmVsb25nCT4wCVtyZXM9JWR4DQo+NTI0CWJlbG9uZwk+MAklZF0NCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0wMiBieSBDYXJsDQowCXN0cmluZwlGSUFTQ08JW2ZpZD0wMDAxMDAxMDctMzEtMDAwMFdGQTtleHQ9d2ZhO21pbWU9O11GcmFjdGFsIEltYWdlIEFuZCBTZXF1ZW5jZSBDb2RlYyBpbWFnZSBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIFgtV2luZG93cyBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMDEgYnkgQ2FybA0KMAlzdHJpbmcJLyo
 gWFBNICovCVtmaWQ9MDAwMDAxMDIwLTMxLTAwMDBYUE07ZXh0PXhwbTttaW1lPTtdWC1XaW5kb3dzIHBpeGVsIG1hcCBpbWFnZSBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIFhWIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0wMSBieSBDYXJsDQowCXN0cmluZwlQN1xcIDMzMglbZmlkPTAwMDEwMDEwMy0zMS0wMDAwMFhWO2V4dD14djttaW1lPTtdWFYgVGh1bWJuYWlsIGltYWdlIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgWFdpbmRvd3MgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTAyIGJ5IENhcmwNCjAJYmVsb25nCTB4NDAJW2ZpZD0wMDAwMDEwMjAtMzEtMDAwMFhXRDtleHQ9eHdkO21pbWU9O11YMTAgWFdpbmRvd3MgZHVtcCBpbWFnZSBmaWxlDQomNAliZWxvbmcJMHgwNgkNCj4yNAliZWxvbmcJeAlbcmVzPSVkDQo+MjgJYmVsb25nCXgJeCVkXQ0KDQojIE1hZ2ljIElEIGZvciBYV2luZG93cyBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMDIgYnkgQ2FybA0KMAlsZWxvbmcJMHg0MAlbZmlkPTAwMDAwMTAyMC0zMS0wMDAwWFdEO2V4dD14d2Q7bWltZT07XVgxMCBYV2luZG93cyBkdW1wIGltYWdlIGZpbGUNCiY0CWxlbG9uZwkweDA2CQ0KPjI0CWxlbG9uZwl4CVtyZXM9JWQNCj4yOAlsZWxvbmcJeAl4JWRdDQoNCiMgTWFnaWMgSUQgZm9yIFByb3ZlY3RvciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMDggYnkgQ2FybA0KMAlzdHJpbmcJRk9STQlbZmlkPTAw
 MDAwMTMxOS0zMi0wMDBEUjJEO2V4dD1kcjJkO21pbWU9O11Qcm92ZWN0b3IgMkQgaW1hZ2UgZmlsZQ0KJjgJc3RyaW5nCURSMkQJDQoNCiMgTWFnaWMgSUQgZm9yIFhGaWcsIFdpbkZpZywgakZpZyBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMDggYnkgQ2FybA0KMAlzdHJpbmcJI0ZJRwlbZmlkPTAwMDEwMDAxMS0zMi0wMDAwRklHO2V4dD1maWc7bWltZT07XUZhY2lsaXR5IGZvciBJbnRlcmFjdGl2ZSBHZW5lcmF0aW9uIGZpbGUNCj41CXN0cmluZwl4CSwgdmVyc2lvbiAlLjFzLg0KPjcJc3RyaW5nCXgJJS4xcw0KDQojIE1hZ2ljIElEIGZvciBMb3R1cyAxLTItMyBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMDIgYnkgQ2FybA0KMAlzdHJpbmcJXFx4MDFcXHgwMFxceDAwXFx4MDBcXHgwMVxceDAwXFx4MDhcXHgwMFxceDQ0CVtmaWQ9MDAwMDAxMDA5LTMyLTAwMDBQSUM7ZXh0PXBpYzttaW1lPTtdTG90dXMgMS0yLTMgaW1hZ2UgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBBdXRvY2FkIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0xMiBieSBDYXJsDQowCXN0cmluZwlBdXRvQ0FEIFNsaWRlCVtmaWQ9MDAwMDAxMjU0LTMyLTAwMDBTTEQ7ZXh0PXNsZDttaW1lPTtdQXV0b2NhZCBzbGlkZSBpbWFnZSBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIFdvcmRwZXJmZWN0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0wOCBieSBDYXJsDQowCXN0cmluZwlcXHhmZldQQwlbZ
 mlkPTAwMDAwMTAwOC0zMi0wMDAwV1BHO2V4dD13cGc7bWltZT07XVdvcmRwZXJmZWN0IEdyYXBoaWNzIHZlY3RvcnMNCiY4CWJ5dGUJMQkNCiY5CWJ5dGUJMHgxNgkNCj4xMAlieXRlCXgJLCB2ZXJzaW9uICVkLg0KPjExCWJ5dGUJeAklZA0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTExIGJ5IENhcmwNCjAJc3RyaW5nCUZPUk0JW2ZpZD0wMDAxMDAwMTktMzMtMDAwQU1GRjtleHQ9YW1mZjttaW1lPTtdQW1pZ2EgbWV0YWZpbGUNCiY4CXN0cmluZwlBTUZGCQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTExIGJ5IENhcmwNCjAJbGVsb25nCTB4OUFDNkNERDcJW2ZpZD0wMDAwMDEwMDMtMzMtMDAwMEFQTTtleHQ9YXBtO21pbWU9O11BbGR1cyBwbGFjZWFibGUgV2luZG93cyBtZXRhZmlsZQ0KJjQJbGVzaG9ydAkwCQ0KDQojIE1hZ2ljIElEIGZvciBDb3JlbERSQVcgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTExIGJ5IENhcmwNCjAJc3RyaW5nCVJJRkYJW2ZpZD0wMDAwMDEwMDgtMzMtMDAwMENEUjtleHQ9Y2RyO21pbWU9O11Db3JlbGRyYXcgIGxpdHRsZS1lbmRpYW4gbWV0YWZpbGUNCiY4CXN0cmluZwlDRFIJDQoNCiMgTWFnaWMgSUQgZm9yIENvcmVsRFJBVyBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTEgYnkgQ2FybA0KMAlzdHJpbmcJUklGWAlbZmlkPTAwMDAwMTAwOC0zMy0wMDAwQ0RSO2V4dD1jZHI7bWltZT07XUNvcmVsZHJhdyAgYmlnLWVuZGlhbiBtZXRhZmlsZQ
 0KJjgJc3RyaW5nCUNEUgkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0wOCBieSBDYXJsDQowCWJlc2hvcnQmMHhGRjIwCTB4MDAyMAlbZmlkPTAwMDAwMDAwMS0zMy0wMDA4NjMyO2V4dD1jZ207bWltZT1pbWFnZS9jZ207XUNvbXB1dGVyIGdyYXBoaWNzIG1ldGFmaWxlLCBiaW5hcnkgZW5jb2RlZA0KJloweDAyCWJlc2hvcnQmMHhGRjQwCTB4MDA0MAkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0wOCBieSBDYXJsDQowCXN0cmluZwlCRUdNRglbZmlkPTAwMDAwMDAwMS0zMy0wMDA4NjMyO2V4dD1jZ207bWltZT1pbWFnZS9jZ207XUNvbXB1dGVyIGdyYXBoaWNzIG1ldGFmaWxlLCBhc2NpaSBlbmNvZGVkDQoNCiMgTWFnaWMgSUQgZm9yIENvcmVsRFJBVyBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTEgYnkgQ2FybA0KMAlzdHJpbmcJUklGRglbZmlkPTAwMDAwMTAwOC0zMy0wMDAwQ01YO2V4dD1jbXg7bWltZT07XUNvcmVsIGxpdHRsZS1lbmRpYW4gbWV0YWZpbGUNCiY4CXN0cmluZwlDTVgxCQ0KDQojIE1hZ2ljIElEIGZvciBDb3JlbERSQVcgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTExIGJ5IENhcmwNCjAJc3RyaW5nCVJJRlgJW2ZpZD0wMDAwMDEwMDgtMzMtMDAwMENNWDtleHQ9Y214O21pbWU9O11Db3JlbCBiaWctZW5kaWFuIG1ldGFmaWxlDQomOAlzdHJpbmcJQ01YMQkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQowCWxlbG9uZwkweDAwMDA
 wMDAxCVtmaWQ9MDAwMDAxMDAxLTMzLTAwMDBFTUY7ZXh0PWVtZjttaW1lPTtdTWljcm9zb2Z0IFdpbmRvd3MgRW5oYW5jZWQgbWV0YWZpbGUNCiY0MAlsZWxvbmcJMHg0NjRENDUyMAkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQowCXN0cmluZwklIVBTLUFkb2JlLTIuMFxcIEVQU0YtMS4yCVtmaWQ9MDAwMDAxMDAzLTMzLTAwMEVQU0Y7ZXh0PWVwc2Y7bWltZT1hcHBsaWNhdGlvbi9wb3N0c2NyaXB0O11BZG9iZSBFbmNhcHN1bGF0ZWQgUG9zdHNjcmlwdCBMZXZlbCAyLCB2ZXJzaW9uIDEuMg0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTExIGJ5IENhcmwNCjAJc3RyaW5nCSUhUFMtQWRvYmUtMi4wXFwgRVBTRi0yLjAJW2ZpZD0wMDAwMDEwMDMtMzMtMDAwRVBTRjtleHQ9ZXBzZjttaW1lPWFwcGxpY2F0aW9uL3Bvc3RzY3JpcHQ7XUFkb2JlIEVuY2Fwc3VsYXRlZCBQb3N0c2NyaXB0IExldmVsIDIsIHZlcnNpb24gMi4wDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTEgYnkgQ2FybA0KMAlzdHJpbmcJJSFQUy1BZG9iZS0zLjBcXCBFUFNGLTMuMAlbZmlkPTAwMDAwMTAwMy0zMy0wMDBFUFNGO2V4dD1lcHNmO21pbWU9YXBwbGljYXRpb24vcG9zdHNjcmlwdDtdQWRvYmUgRW5jYXBzdWxhdGVkIFBvc3RzY3JpcHQgTGV2ZWwgMywgdmVyc2lvbiAzLjANCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQowCXN0cmluZwlcXHhDNVxceEQwXFx4RDNcXHhDNglbZmlk
 PTAwMDAwMTAwMy0zMy0wMDBFUFNGO2V4dD1lcHNmLGFpO21pbWU9YXBwbGljYXRpb24vcG9zdHNjcmlwdDtdQWRvYmUgRW5jYXBzdWxhdGVkIFBvc3RzY3JpcHQsIHZlcnNpb24gMy4wLCBiaW5hcnkNCg0KIyBNYWdpYyBJRCBmb3IgR0VNIFBhaW50IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0zMSBieSBDYXJsDQowCWJlc2hvcnQJMHhGRkZGCVtmaWQ9MDAwMDAxMjczLTMzLTAwMDBHRU07ZXh0PWdlbTttaW1lPTtdR2VtRE9TIE1vdG9yb2xhIE1ldGFmaWxlLCB2ZXJzaW9uIDEuMDENCiY0CWJlc2hvcnQJMTAxCQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTExIGJ5IGh0dHA6Ly93d3cuc2Vhc2lwLmluZm8vR2VtL2ZmX2dlbS5odG1sDQowCWxlc2hvcnQJMHhGRkZGCVtmaWQ9MDAwMDAxMjczLTMzLTAwMDBHRU07ZXh0PWdlbTttaW1lPTtdR2VtRE9TIE1ldGFmaWxlDQomNAlsZXNob3J0CTAJDQoNCiMgTWFnaWMgSUQgZm9yIEFydGxpbmUgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTExIGJ5IGh0dHA6Ly93d3cuc2Vhc2lwLmluZm8vR2VtL2ZmX2dlbS5odG1sDQowCWxlc2hvcnQJMHhGRkZGCVtmaWQ9MDAwMDAxMjczLTMzLTAwMDBHRU07ZXh0PWdlbTttaW1lPTtdR2VtRE9TIEludGVsIE1ldGFmaWxlLCB2ZXJzaW9uIDQuMDANCiY0CWxlc2hvcnQJNDAwCQ0KDQojIE1hZ2ljIElEIGZvciBEZXNrcHJlc3MgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTExIGJ5IGh0d
 HA6Ly93d3cuc2Vhc2lwLmluZm8vR2VtL2ZmX2dlbS5odG1sDQowCWxlc2hvcnQJMHhGRkZGCVtmaWQ9MDAwMDAxMjczLTMzLTAwMDBHRU07ZXh0PWdlbTttaW1lPTtdR2VtRE9TIEludGVsIE1ldGFmaWxlLCB2ZXJzaW9uIDMuMTANCiY0CWxlc2hvcnQJMzEwCQ0KDQojIE1hZ2ljIElEIGZvciBHRU0gUGFpbnQgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTExIGJ5IGh0dHA6Ly93d3cuc2Vhc2lwLmluZm8vR2VtL2ZmX2dlbS5odG1sDQowCWxlc2hvcnQJMHhGRkZGCVtmaWQ9MDAwMDAxMjczLTMzLTAwMDBHRU07ZXh0PWdlbTttaW1lPTtdR2VtRE9TIEludGVsIE1ldGFmaWxlLCB2ZXJzaW9uIDEuMDENCiY0CWxlc2hvcnQJMTAxCQ0KDQojIE1hZ2ljIElEIGZvciBRdWlja2RyYXcgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTAxIGJ5IENhcmwNCjB4MjBBCWJlbG9uZwkweDAwMTEwMkZGCVtmaWQ9MDAwMDAxMDAyLTMzLTAwMDBQQ1Q7ZXh0PXBjdDttaW1lPTtdTWFjaW50b3NoIFF1aWNrZHJhdyBtZXRhZmlsZSBcJ1BJQ1RcJywgdmVyc2lvbiAyLjANCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQowCWxlc2hvcnQJMHgwMDAxCVtmaWQ9MDAwMDAxMDAxLTMzLTAwMDBXTUY7ZXh0PXdtZjttaW1lPTtdTWljcm9zb2Z0IFdpbmRvd3MgbWV0YWZpbGUNCiYyCWxlc2hvcnQJOQkNCg0KIyBNYWdpYyBJRCBmb3IgQ2luZW1hIDREIFZlcnNpb24gNS54IGZpbGVzLg0KIyBTdW
 JtaXR0ZWQgb24gMjAwNC0wMi0wNiBieSBDYXJsDQowCXN0cmluZwlNQzUwCVtmaWQ9MDAwMDAxMjU1LTQwLTAwMDAwMDA7ZXh0PTttaW1lPTtdTWF4b24gQ2luZW1hIDREIHZlcnNpb24gNSAzRCBkYXRhDQoNCiMgTWFnaWMgSUQgZm9yIFF1aWNrZHJhdyAzRCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMDYgYnkgQ2FybA0KMAlzdHJpbmcJM0RNRglbZmlkPTAwMDAwMTAwMi00MC0wMDAzRE1GO2V4dD0zZG1mO21pbWU9O11BcHBsZSBRdWlja2RyYXcgM0QgbWV0YWZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgUmhpbm8gM2QgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTEyIGJ5IENhcmwNCjAJc3RyaW5nCTNEIEdlb21ldHJ5IEZpbGUgRm9ybWF0CVtmaWQ9MDAwMDAxMzMwLTQwLTAwMDNETUY7ZXh0PTNkbWY7bWltZT07XVJoaW5vM2QgLyBPcGVuTnVyYnMgM2QgbW9kZWwNCg0KIyBNYWdpYyBJRCBmb3IgUXVpY2tkcmF3IDNEIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0xMSBieSBDYXJsDQowCXN0cmluZwkzRE1ldGFmaWxlCVtmaWQ9MDAwMDAxMDAyLTQwLTAwMDNETUY7ZXh0PTNkbWYsYTNkO21pbWU9O11BcHBsZSBRdWlja2RyYXcgM0QgbWV0YWZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgQUMzZCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMTIgYnkgQ2FybA0KMAlzdHJpbmcJQUMzRAlbZmlkPTAwMDAwMDAwMC00MC0wMDAwMEFDO2V4dD1hYzttaW1lPTtdQWMzZCA
 zZCBtb2RlbA0KDQojIE1hZ2ljIElEIGZvciAzZFN0dWRpbyBNYXggZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTEyIGJ5IENhcmwNCjAJc3RyaW5nCSozRFNNQVhfQVNDSUlFWFBPUlQJW2ZpZD0wMDAwMDEzMjYtNDAtMDAwMEFTRTtleHQ9YXNlO21pbWU9O10zZCBzdHVkaW8gbWF4IGFzY2lpIGV4cG9ydCAzRCBtb2RlbA0KDQojIE1hZ2ljIElEIGZvciBDYWxpZ2FyaSBUcnVlc3BhY2UgTW9kZWxlciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMDYgYnkgQ2FybA0KMAlzdHJpbmcJQ2FsaWdhcmlcXCBWCVtmaWQ9MDAwMDAxMjU2LTQwLTAwMDBDT0I7ZXh0PWNvYixzY247bWltZT07XUNhbGlnYXJpIFRydWVzcGFjZTIgM0QgbW9kZWwNCg0KIyBNYWdpYyBJRCBmb3IgVGFjaHlvbiBwYXJhbGxlbCByYXl0cmFjZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTEyIGJ5IENhcmwNCjAJc3RyaW5nCUJFR0lOX1NDRU5FCVtmaWQ9MDAwMTAwMTE2LTQwLTAwMDBEQVQ7ZXh0PWRhdDttaW1lPTtdVGFjaHlvbiByYXktdHJhY2VyIDNkIG1vZGVsDQoNCiMgTWFnaWMgSUQgZm9yIEF1dG9jYWQgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTEyIGJ5IENhcmwNCjAJc3RyaW5nCUFDMTAJW2ZpZD0wMDAwMDEyNTQtNDAtMDAwMERXRztleHQ9ZHdnO21pbWU9aW1hZ2Uvdm5kLmR3ZztdQXV0b2NhZCBkcmF3aW5nIGZvcm1hdCAzZCBtb2RlbA0KDQojIE1hZ2ljIElEIGZvciBBdXRv
 Y2FkIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0xMiBieSBDYXJsDQowCXN0cmluZwlBdXRvQ0FEIEJpbmFyeSBEWEYJW2ZpZD0wMDAwMDEyNTQtNDAtMDAwMERYRjtleHQ9ZHhmO21pbWU9aW1hZ2Uvdm5kLmR4ZjtdQXV0b2NhZCBkcmF3aW5nIGludGVyY2hhbmdlIDNkIG1vZGVsLCBiaW5hcnkNCg0KIyBNYWdpYyBJRCBmb3IgTXVsdGlnZW4gY3JlYXRvciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMTIgYnkgQ2FybA0KMAliZXNob3J0CTEJW2ZpZD0wMDAwMDEzMzItNDAtMDAwMEZMVDtleHQ9Zmx0O21pbWU9O11PcGVuZmxpZ2h0IHNjZW5lIGRlc2NyaXB0aW9uIDNkIG1vZGVsDQomNAlzdHJpbmcJZGIJDQoNCiMgTWFnaWMgSUQgZm9yIFZpZGVvc2NhcGUgM0QgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTEyIGJ5IENhcmwNCjAJc3RyaW5nCUdPVVIJW2ZpZD0wMDAwMDEzMjgtNDAtMDAwMEdFTztleHQ9Z2VvO21pbWU9O11WaWRlb3NjYXBlIDNkIG1vZGVsIHdpdGggY29sb3JlZCB2ZXJ0aWNlcw0KDQojIE1hZ2ljIElEIGZvciBWaWRlb3NjYXBlIDNELCBCbGVuZGVyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0xMiBieSBDYXJsDQowCXN0cmluZwkzREcxCVtmaWQ9MDAwMDAxMzI4LTQwLTAwMDBHRU87ZXh0PWdlbzttaW1lPTtdVmlkZW9zY2FwZSAzZCBtb2RlbCB3aXRoIGNvbG9yZWQgZmFjZXMNCg0KIyBNYWdpYyBJRCBmb3IgVmlkZW9zY2FwZSAzRCwgQ
 mxlbmRlciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMTIgYnkgQ2FybA0KMAlzdHJpbmcJM0RHMglbZmlkPTAwMDAwMTMyOC00MC0wMDAwR0VPO2V4dD1nZW87bWltZT07XVZpZGVvc2NhcGUgM2QgbW9kZWwgbGlnaHQgc291cmNlDQoNCiMgTWFnaWMgSUQgZm9yIFZpZGVvc2NhcGUgM0QsIEJsZW5kZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTEyIGJ5IENhcmwNCjAJc3RyaW5nCTNERzMJW2ZpZD0wMDAwMDEzMjgtNDAtMDAwMEdFTztleHQ9Z2VvO21pbWU9O11WaWRlb3NjYXBlIDNkIG1vZGVsIHdpdGggZ291cmF1ZCBjdXJ2ZXMNCg0KIyBNYWdpYyBJRCBmb3IgU29mdGltYWdlIDREIENyZWF0aXZlIGVudmlyb25tZW50IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0xMiBieSBDYXJsDQowCXN0cmluZwlIUkNIOglbZmlkPTAwMDAwMTMyMS00MC0wMDAwSFJDO2V4dD1ocmM7bWltZT07XVNvZnRpbWFnZSA0ZCBtb2RlbCwgYXNjaWkgZW5jb2RlZA0KDQojIE1hZ2ljIElEIGZvciBPcGVuIEludmVudG9yIFRvb2xraXQgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTA2IGJ5IENhcmwNCjAJc3RyaW5nCSNJbnZlbnRvcglbZmlkPTAwMDAwMTAwNC00MC0wMDAwMElWO2V4dD1pdjttaW1lPTtdT3BlbiBJbnZlbnRvciAzZCBtb2RlbA0KPjExCXN0cmluZwl4CSwgdmVyc2lvbiAlLjFzLg0KPjEzCXN0cmluZwl4CSUuMXMNCj4xNQlzdHJpbmcJYmluYXJ5CSwgYmluYX
 J5IGVuY29kZWQNCj4xNQlzdHJpbmcJYXNjaWkJLCBhc2NpaSBlbmNvZGVkDQoNCiMgTWFnaWMgSUQgZm9yIEdlb212aWV3IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0xMiBieSBDYXJsDQowCXN0cmluZwlMSVNUCVtmaWQ9MDAwMTAwMTE4LTQwLTAwMExJU1Q7ZXh0PWxpc3Q7bWltZT07XUdlb212aWV3IGxpc3Qgb2YgM0QgbW9kZWxzIGFuZCBvYmplY3RzDQoNCiMgTWFnaWMgSUQgZm9yIExpZ2h0d2F2ZSAzRCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMTIgYnkgQ2FybA0KMAlzdHJpbmcJRk9STQlbZmlkPTAwMDAwMTI1MS00MC0wMDAwTFdPO2V4dD1sd28sbHdvYjttaW1lPTtdTGlnaHR3YXZlIDNEIG9iamVjdA0KJjgJc3RyaW5nCUxXTzIJDQoNCiMgTWFnaWMgSUQgZm9yIExpZ2h0d2F2ZSAzRCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMDYgYnkgQ2FybA0KMAlzdHJpbmcJRk9STQlbZmlkPTAwMDAwMTI1MS00MC0wMDAwTFdPO2V4dD1sd29iLGx3bzttaW1lPTtdTGlnaHR3YXZlIDNEIG9iamVjdA0KJjgJc3RyaW5nCUxXT0IJDQoNCiMgTWFnaWMgSUQgZm9yIExpZ2h0d2F2ZSAzRCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMDYgYnkgQ2FybA0KMAlzdHJpbmcJTFdTQwlbZmlkPTAwMDAwMTI1MS00MC0wMDAwTFdTO2V4dD1sd3NjLGx3czttaW1lPTtdTGlnaHR3YXZlIDNEIHNjZW5lDQoNCiMgTWFnaWMgSUQgZm9yIE1heWEgZmlsZXMuDQojIFN1Ym1
 pdHRlZCBvbiAyMDA0LTA0LTEyIGJ5IENhcmwNCjAJc3RyaW5nCS8vTWF5YQlbZmlkPTAwMDAwMTMxMi00MC0wMDAwME1BO2V4dD1tYTttaW1lPTtdTWF5YSAzZCBtb2RlbCwgYXNjaWkgZW5jb2RlZA0KDQojIE1hZ2ljIElEIGZvciBDaW5lbWEgNEQgVmVyc2lvbiA0LnggYW5kIGVhcmxpZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTA2IGJ5IENhcmwNCjAJc3RyaW5nCUZPUk0JW2ZpZD0wMDAwMDEyNTUtNDAtMDAwTUM0RDtleHQ9bWM0ZDttaW1lPTtdTWF4b24gQ2luZW1hIDREIHY0LnggM0QgZGF0YQ0KJjgJc3RyaW5nCU1DNEQJDQoNCiMgTWFnaWMgSUQgZm9yIEdlb212aWV3IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0xMiBieSBDYXJsDQowCXN0cmluZwlDTUVTSAlbZmlkPTAwMDEwMDExOC00MC0wMDBNRVNIO2V4dD1tZXNoO21pbWU9O11HZW9tdmlldyBwb2x5Z29uDQoNCiMgTWFnaWMgSUQgZm9yIEdlb212aWV3IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0xMiBieSBDYXJsDQowCXN0cmluZwlNRVNICVtmaWQ9MDAwMTAwMTE4LTQwLTAwME1FU0g7ZXh0PW1lc2g7bWltZT07XUdlb212aWV3IHBvbHlnb24NCg0KIyBNYWdpYyBJRCBmb3IgTWlsc2hhcGUgM2QgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTEyIGJ5IENhcmwNCjAJc3RyaW5nCU1TM0QwMDAwMDAJW2ZpZD0wMDAxMDAxMTctNDAtMDAwTVMzRDtleHQ9bXMzZDttaW1lPTtdTWlsa3NoYXBl
 IDNkIG1vZGVsLCBiaW5hcnkgZW5jb2RlZA0KDQojIE1hZ2ljIElEIGZvciBXb3JsZHRvb2xraXQgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTA2IGJ5IENhcmwNCjAJc3RyaW5nCW5mZglbZmlkPTAwMDAwMTI1My00MC0wMDAwTkZGO2V4dD1uZmY7bWltZT07XVNlbnNlOCBXb3JsZHRvb2xraXQgM0Qgb2JqZWN0DQoNCiMgTWFnaWMgSUQgZm9yIEF1dG9kZXNrIEFuaW1hdG9yIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0xMiBieSBDYXJsDQoyCWxlbG9uZwkweDAwMDAJW2ZpZD0wMDAwMDEyNTQtNDAtMDAwMFBMWTtleHQ9cGx5O21pbWU9O11BdXRvZGVzayBhbmltYXRvciBwb2x5Z29uIGZpbGUNCiY2CWJ5dGUJMAkNCiY3CWJ5dGUJMHg5OQkNCg0KIyBNYWdpYyBJRCBmb3IgQXV0b2Rlc2sgQW5pbWF0b3IgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTEyIGJ5IENhcmwNCjIJbGVsb25nCTB4MDAwMAlbZmlkPTAwMDAwMTI1NC00MC0wMDAwUExZO2V4dD1wbHk7bWltZT07XUF1dG9kZXNrIGFuaW1hdG9yIHBvbHlnb24gZmlsZQ0KJjYJYnl0ZQkxCQ0KJjcJYnl0ZQkweDk5CQ0KDQojIE1hZ2ljIElEIGZvciBRdWljazNkIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0xMiBieSBDYXJsDQowCXN0cmluZwlxdWljazNEbwlbZmlkPTAwMDAwMTMzNC00MC0wMDAwUTNPO2V4dD1xM287bWltZT07XVF1aWNrM2QgM0Qgb2JqZWN0DQoNCiMgTWFnaWMgSUQgZm9yIFF1aWNrM
 2QgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTEyIGJ5IENhcmwNCjAJc3RyaW5nCXF1aWNrM0RzCVtmaWQ9MDAwMDAxMzM0LTQwLTAwMDBRM1M7ZXh0PXEzczttaW1lPTtdUXVpY2szZCAzRCBzY2VuZQ0KDQojIE1hZ2ljIElEIGZvciBSZW5kZXJtYW4gZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTEyIGJ5IENhcmwNCjAJc3RyaW5nCSMjUmVuZGVyTWFuXFwgUklCLVN0cnVjdHVyZQlbZmlkPTAwMDAwMTMyMy00MC0wMDAwUklCO2V4dD1yaWI7bWltZT07XVJlbmRlcm1hbiBieXRlc3RyZWFtIDNEIG1vZGVsDQoNCiMgTWFnaWMgSUQgZm9yIFNjdWxwdCAzZCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMTIgYnkgQ2FybA0KMAlzdHJpbmcJRk9STQlbZmlkPTAwMDAwMDAwMC00MC0wMFNDRU5FO2V4dD1zY2VuZTttaW1lPTtdU2N1bHB0IDNkIHNjZW5lIG1vZGVsDQomOAlzdHJpbmcJU0MzRAkNCg0KIyBNYWdpYyBJRCBmb3IgSW1hZ2luZSAzRCBTdHVkaW8sIFR1cmJvIFNpbHZlciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMDYgYnkgQ2FybA0KMAlzdHJpbmcJRk9STQlbZmlkPTAwMDAwMTI1Mi00MC0wMDBUREREO2V4dD10ZGRkLG9iajttaW1lPTtdSW1hZ2luZSAzRCBvYmplY3QNCiY4CXN0cmluZwlURERECQ0KDQojIE1hZ2ljIElEIGZvciBNYWNyb21lZGlhIERpcmVjdG9yIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0xMiBieSBDYXJsDQowCXN0cm
 luZwlJRlgJW2ZpZD0wMDAwMDEyNTktNDAtMDAwMFczRDtleHQ9dzNkO21pbWU9O11TaG9ja3dhdmUgM0QgbW9kZWwNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0wNiBieSBDYXJsDQowCXN0cmluZwkjVlJNTAlbZmlkPTAwMDAwMDAwMS00MC0wMDE0NzcyO2V4dD13cmw7bWltZT1tb2RlbC92cm1sO11WaXJ0dWFsIFJlYWxpdHkgbW9kZWxpbmcgbGFuZ3VhZ2UNCj43CXN0cmluZwl4CSwgdmVyc2lvbiAlLjFzLg0KPjkJc3RyaW5nCXgJJS4xcw0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTEyIGJ5IENhcmwNCjAJc3RyaW5nCTxXT1JMRD4JW2ZpZD0wMDAwMDAwMDAtNDAtMDAwMFhHTDtleHQ9eGdsO21pbWU9O11YR0wgM2QgbW9kZWwNCg0KIyBNYWdpYyBJRCBmb3IgRGlyZWN0M0QgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA0LTExIGJ5IENhcmwNCjAJc3RyaW5nCXhvZlxcIAlbZmlkPTAwMDAwMTAwMS00MC0wMDAwWElFO2V4dD14aWU7bWltZT07XU1pY3Jvc29mdCBkaXJlY3QzZCAzRCBtb2RlbA0KDQojIE1hZ2ljIElEIGZvciBTb2Z0aW1hZ2UgWFNJIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0xMiBieSBDYXJsDQowCXN0cmluZwl4c2lcXCAJW2ZpZD0wMDAwMDEzMjEtNDAtMDAwMFhTSTtleHQ9eHNpO21pbWU9O11Tb2Z0aW1hZ2UgM2QgbW9kZWwNCg0KIyBNYWdpYyBJRCBmb3IgS2Fib29tIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0wOCBieSBDYXJsDQowCXN0cml
 uZwlcXHhBOE1QXFx4QTgJW2ZpZD0wMDAwMDAwMDAtNTAtMDAwMDAwMDtleHQ9O21pbWU9O11LYm9vbSBhcmNoaXZlIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgQ1RXIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wNCBieSBDYXJsDQowCWxlc2hvcnQJMTIJW2ZpZD0wMDAxMDAwNzctNTAtMDAwMDAwMDtleHQ9O21pbWU9O11Db250ZXh0IHRyZWUgd2VpZ2hpbmcgKENUVykgYXJjaGl2ZSBmaWxlDQomMglsZXNob3J0CTAJDQoNCiMgTWFnaWMgSUQgZm9yIE1pY3Jvc29mdCBDb21wcmVzcyBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDQgYnkgQ2FybA0KMAlzdHJpbmcJU1pERFxceDg4XFx4RjBcXHgyN1xceDMzCVtmaWQ9MDAwMDAxMDAxLTUwLTAwMDAwMDA7ZXh0PTttaW1lPTtdTWljcm9zb2Z0IExaU1MgY29tcHJlc3NlZCBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIEFBWCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTggYnkgQ2FybA0KMAlzdHJpbmcJXFx4NDBcXHhGRVxceDAwXFx4MDAJW2ZpZD0wMDAwMDEyODEtNTAtMDAwMEFBWDtleHQ9YWF4O21pbWU9O11BQVggYXJjaGl2ZSBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIEFCQ29tcCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTggYnkgQ2FybA0KMAlzdHJpbmcJXFx4MDNBQjIJW2ZpZD0wMDAwMDEyODItNTAtMDAwMEFCUDtleHQ9YWJwO21pbWU9O11BQkNvbXAgYXJjaGl2ZSBmaWxlDQoNCiMgTWFnaWMgSUQg
 Zm9yIEFjZSAvIFdpbkFjZSBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMDggYnkgQ2FybA0KNwlzdHJpbmcJKipBQ0UqKglbZmlkPTAwMDAwMTI2NS01MC0wMDAwQUNFO2V4dD1hY2U7bWltZT07XUFjZSAvIFdpbkFDRSBhcmNoaXZlIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgQWkgQXJjaGl2ZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE4IGJ5IENhcmwNCjAJc3RyaW5nCUFpCVtmaWQ9MDAwMDAwMDAwLTUwLTAwMDAwQUk7ZXh0PWFpO21pbWU9O11BaSBhcmNoaXZlIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgQWt0IGFyY2hpdmVyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xOCBieSBDYXJsDQowCXN0cmluZwlBS1RcXHgwQQlbZmlkPTAwMDAwMDAwMC01MC0wMDAwQUtUO2V4dD1ha3Q7bWltZT07XUFLVCBhcmNoaXZlIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgQU1HIEFyY2hpdmVyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xOCBieSBDYXJsDQowCXN0cmluZwlcXHhBRFxceDM2CVtmaWQ9MDAwMDAxMjg0LTUwLTAwMDBBTUc7ZXh0PWFtZzttaW1lPTtdQU1HIGFyY2hpdmUgZmlsZQ0KPjIJYnl0ZQl4CSwgdmVyc2lvbiAlYmguDQo+MglieXRlCXgJJWJsDQomMwlieXRlCTAJDQoNCiMgTWFnaWMgSUQgZm9yIGFyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wNCBieSBDYXJsDQowCXN0cmluZwlcXHgyMTxhcmNoPlxceDBBCVtmaWQ9MDAwMDAwM
 DAzLTUwLTAwMDAwQVI7ZXh0PWFyO21pbWU9O11VTklYIGFyY2hpdmUgZmlsZSAoYXIpDQoNCiMgTWFnaWMgSUQgZm9yIEFSNyBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTggYnkgQ2FybA0KMAlzdHJpbmcJLEFSNyBlLW1haWxhYmxlIGFyY2hpdmU6CVtmaWQ9MDAwMTAwMDM2LTUwLTAwMDBBUjc7ZXh0PWFyNzttaW1lPTtdQVI3IGFyY2hpdmUgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBTcXVhc2ggZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE4IGJ5IENhcmwNCjMJc3RyaW5nCU9jdFNxdQlbZmlkPTAwMDEwMDAzOC01MC0wMDAwQVJIO2V4dD1hcmg7bWltZT07XVNxdWFzaCBhcmNoaXZlIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgQXJqIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xOCBieSBDYXJsDQowCXN0cmluZwlcXHg2MFxceEVBCVtmaWQ9MDAwMDAxMjg1LTUwLTAwMDBBUko7ZXh0PWFyajttaW1lPTtdQVJKIGFyY2hpdmUgZmlsZQ0KJjEwCWJ5dGUJMgkNCg0KIyBNYWdpYyBJRCBmb3IgQVNEIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xOCBieSBDYXJsDQowCXN0cmluZwlBU0QwMVxceDFBCVtmaWQ9MDAwMDAxMjg3LTUwLTAwMDBBU0Q7ZXh0PWFzZDttaW1lPTtdQVNEIGFyY2hpdmUgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBCb2EgY29uc3RyaWN0b3IgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE4IGJ5IENhcmwNCjAJc3RyaW5nCUJPQV
 xceDAwCVtmaWQ9MDAwMTAwMDQwLTUwLTAwMDBCNTg7ZXh0PWI1ODttaW1lPTtdQk9BIGNvbnN0cmljdG9yIGFyY2hpdmUgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBCV0MgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE4IGJ5IENhcmwNCjAJc3RyaW5nCUJXQwlbZmlkPTAwMDEwMDA0Mi01MC0wMDAwMEJDO2V4dD1iYzttaW1lPTtdQldDIGFyY2hpdmUgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBCaXggZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE4IGJ5IENhcmwNCjAJc3RyaW5nCUJJWDAJW2ZpZD0wMDAxMDAwMzctNTAtMDAwMEJJWDtleHQ9Yml4O21pbWU9O11CSVggYXJjaGl2ZSBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIEJ0b2EgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE4IGJ5IENhcmwNCjAJc3RyaW5nCXhidG9hNQlbZmlkPTAwMDEwMDA0My01MC0wMDAwQk9PO2V4dD1ib287bWltZT07XUJ0b2EgZW5jb2RlZCBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIEJzYSBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTggYnkgQ2FybA0KMAlzdHJpbmcJXFx4RkZCU0dcXHgwMFxceDAwXFx4RkZCU0EJW2ZpZD0wMDAwMDEyODktNTAtMDAwMEJTTjtleHQ9YnNuO21pbWU9O11Cc2EgYXJjaGl2ZSBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIEJUUEMgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAzLTAzIGJ5IENhcmwNCjAJc3RyaW5nCWJ0cGNcXCAJW2ZpZD0wMDAxMDA
 wNzQtNTAtMDAwQlRQQztleHQ9YnRwYzttaW1lPTtdQlRQQyBjb21wcmVzc2VkIGltYWdlIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgQlRTIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xOCBieSBDYXJsDQowCXN0cmluZwlcXHgxQVxceDAzRGVzY3JpcHQJW2ZpZD0wMDAxMDAwNDQtNTAtMDAwMEJUUztleHQ9YnRzO21pbWU9O11CVFNwayBhcmNoaXZlIGZpbGUNCiYweDUyMQlzdHJpbmcJQlRTUEshCQ0KDQojIE1hZ2ljIElEIGZvciBCemlwIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xOCBieSBDYXJsDQowCXN0cmluZwlCWjAJW2ZpZD0wMDAxMDAwMDgtNTAtMDAwMDBCWjtleHQ9Yno7bWltZT07XUJ6aXAgYXJjaGl2ZSBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIEJ6aXAyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0wOCBieSBDYXJsDQowCXN0cmluZwlCWmgJW2ZpZD0wMDAxMDAwMDgtNTAtMDAwMEJaMjtleHQ9YnoyO21pbWU9O11CemlwMiBhcmNoaXZlIGZpbGUNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0wOCBieSBDYXJsDQowCXN0cmluZwlNU0NGCVtmaWQ9MDAwMDAxMDAxLTUwLTAwMDBDQUI7ZXh0PWNhYjttaW1lPTtdTWljcm9zb2Z0IENhYmluZXQgZmlsZQ0KPjI1CWJ5dGUJeAksIHZlcnNpb24gJWQuDQo+MjQJYnl0ZQl4CSVkDQoNCiMgTWFnaWMgSUQgZm9yIENydXNoIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0yMyBieSBDYXJsDQowCXN0cmlu
 ZwlDUlVTSFxcIHYxLjgJW2ZpZD0wMDAwMDEyOTAtNTAtMDAwMENSVTtleHQ9Y3J1O21pbWU9O11DcnVzaCBhcmNoaXZlIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgQ3R4ZiBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMjMgYnkgQ2FybA0KMAlzdHJpbmcJQ1hGXFx4MUEJW2ZpZD0wMDAxMDAwNDUtNTAtMDAwMENYRjtleHQ9Y3hmO21pbWU9O11DdHggYXJjaGl2ZSBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIERBWFdhdiBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDQgYnkgQ2FybA0KMAlzdHJpbmcJRk9STQlbZmlkPTAwMDAwMDAwMC01MC0wMDAwREFYO2V4dD1kYXg7bWltZT07XURBWCBhdWRpbyBhcmNoaXZlIGZpbGUNCiY4CXN0cmluZwlkYXhBCQ0KDQojIE1hZ2ljIElEIGZvciBEaXNrbWFzaGVyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0yMyBieSBDYXJsDQowCXN0cmluZwlETVMhCVtmaWQ9MDAwMDAwMDAwLTUwLTAwMDBETVM7ZXh0PWRtczttaW1lPTtdRGlza21hc2hlciBhcmNoaXZlIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgRHBhZSBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMjMgYnkgQ2FybA0KMAlzdHJpbmcJRGlya1xcIFBhZWhsKGMpCVtmaWQ9MDAwMTAwMDQ2LTUwLTAwMDBEUEE7ZXh0PWRwYTttaW1lPTtdRHBhZSBhcmNoaXZlIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgRGlzaW50ZWdyYXRvciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtM
 DItMjMgYnkgQ2FybA0KMAlzdHJpbmcJRFNUYglbZmlkPTAwMDEwMDA0Ny01MC0wMDAwRFNUO2V4dD1kc3Q7bWltZT07XURpc2ludGVncmF0b3IgYXJjaGl2ZSBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIEVuaGFuY2VkIGNvbXByZXNzb3IgKEVOQykgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTIzIGJ5IENhcmwNCjAJc3RyaW5nCUVuY2gJW2ZpZD0wMDAxMDAwNTEtNTAtMDAwMEVOQztleHQ9ZW5jO21pbWU9O11FbmhhbmNlZCBjb21wcmVzc29yIGFyY2hpdmUgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBFU1AgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTA4IGJ5IENhcmwNCjAJc3RyaW5nCUVTUD4JW2ZpZD0wMDAxMDAwMDItNTAtMDAwMEVTUDtleHQ9ZXNwO21pbWU9O11FU1AgYXJjaGl2ZSBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIEVTUCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMDggYnkgQ2FybA0KMHg1M2YJc3RyaW5nCUVTUAlbZmlkPTAwMDEwMDAwMi01MC0wMDAwRVhFO2V4dD1leGU7bWltZT07XUVTUCBTZWxmLWV4dHJhY3RpbmcgYXJjaGl2ZXcgKE1TLURPUykNCg0KIyBNYWdpYyBJRCBmb3IgRnJlZXplIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQowCXN0cmluZwlcXHgxRlxceDlFCVtmaWQ9MDAwMDAwMDAwLTUwLTAwMDAwMEY7ZXh0PWY7bWltZT07XUZyZWV6ZSBhcmNoaXZlIGZpbGUsIHZlcnNpb24gMS4wDQoNCiMgTWFnaWMgSU
 QgZm9yIEZyZWV6ZSBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTEgYnkgQ2FybA0KMAlzdHJpbmcJXFx4MUZcXHg5RglbZmlkPTAwMDAwMDAwMC01MC0wMDAwMDBGO2V4dD1mO21pbWU9O11GcmVlemUgYXJjaGl2ZSBmaWxlLCB2ZXJzaW9uIDIuMA0KDQojIE1hZ2ljIElEIGZvciBRbGZjIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0yMyBieSBDYXJsDQowCXN0cmluZwlcXHg0N1xceDY4XFx4NjlcXHg2NFxceDZmCVtmaWQ9MDAwMTAwMDU1LTUwLTAwMDAwR1E7ZXh0PWdxO21pbWU9O11RbGZjIGFyY2hpdmUgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBHemlwIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0wOCBieSBDYXJsDQowCXN0cmluZwlcXHgxRlxceDhCCVtmaWQ9MDAwMDAwMDAyLTUwLTAwMDE5NTI7ZXh0PWd6O21pbWU9O11HemlwIGFyY2hpdmUgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBIYSBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTAgYnkgQ2FybA0KMAlzdHJpbmcJSEEJW2ZpZD0wMDAxMDAwMTMtNTAtMDAwMDBIQTtleHQ9aGE7bWltZT07XUhBIGFyY2hpdmUgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBIQVAgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTIzIGJ5IENhcmwNCjAJc3RyaW5nCVxceDkxM0hGCVtmaWQ9MDAwMDAxMjkxLTUwLTAwMDBIQVA7ZXh0PWhhcDttaW1lPTtdSEFQIGFyY2hpdmUgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciB
 IcGFjayBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTEgYnkgQ2FybA0KMAlzdHJpbmcJSFBBSwlbZmlkPTAwMDEwMDAxNi01MC0wMDAwSFBLO2V4dD1ocGs7bWltZT07XUhwYWNrIGFyY2hpdmUgZmlsZQ0KJlo0CXN0cmluZwlIUEFLCQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTEwIGJ5IENhcmwNCjExCXN0cmluZwltdXN0XFwgYmVcXCBjb252ZXJ0ZWRcXCB3aXRoXFwgQmluSGV4CVtmaWQ9MDAwMDAxMDAyLTUwLTAwMDBIUVg7ZXh0PWhxeDttaW1lPTtdQmluSGV4IGFyY2hpdmUNCg0KIyBNYWdpYyBJRCBmb3IgSFlQIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0yMyBieSBDYXJsDQowCXN0cmluZwlcXHgxQUhQXFx4MjUJW2ZpZD0wMDAxMDAwNTYtNTAtMDAwMEhZUDtleHQ9aHlwO21pbWU9O11IWVAgYXJjaGl2ZSBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIEhZUCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMjMgYnkgQ2FybA0KMAlzdHJpbmcJXFx4MUFTVFxceDI1CVtmaWQ9MDAwMTAwMDU2LTUwLTAwMDBIWVA7ZXh0PWh5cDttaW1lPTtdSFlQIGFyY2hpdmUgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBXaW5pbXAgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTA4IGJ5IENhcmwNCjAJbGVsb25nCTB4QTUwNEQ0OQlbZmlkPTAwMDAwMTI3MC01MC0wMDAwSU1QO2V4dD1pbXA7bWltZT07XVdpbmltcCBhcmNoaXZlIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgSlJj
 aGl2ZSBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMDggYnkgQ2FybA0KMAlzdHJpbmcJSlJjaGl2ZQlbZmlkPTAwMDAwMTI2My01MC0wMDAwSlJDO2V4dD1qcmM7bWltZT07XUpSY2hpdmUgYXJjaGl2ZSBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIEFyY2hpdmUgSGFuZGxlciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMDggYnkgQ2FybA0KMAlzdHJpbmcJTEcJW2ZpZD0wMDAxMDAwMDEtNTAtMDAwMDBMRztleHQ9bGc7bWltZT07XUFyaGFuZ2VsIGFyY2hpdmUgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBMaW1pdCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMDggYnkgQ2FybA0KMAlzdHJpbmcJTE1cXHgxQQlbZmlkPTAwMDEwMDAwMy01MC0wMDAwTElNO2V4dD1saW07bWltZT07XUxpbWl0IGFyY2hpdmUgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBMYXJjIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQoyCXN0cmluZwktbHo0LQlbZmlkPTAwMDEwMDAxNy01MC0wMDAwTFpIO2V4dD1semg7bWltZT07XUxhcmMgYXJjaGl2ZQ0KDQojIE1hZ2ljIElEIGZvciBMYXJjIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQoyCXN0cmluZwktbHo1LQlbZmlkPTAwMDEwMDAxNy01MC0wMDAwTFpIO2V4dD1semg7bWltZT07XUxhcmMgYXJjaGl2ZSBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIExhcmMgZmlsZXMuDQojIFN1Ym1pdHRlZCBvb
 iAyMDA0LTAyLTExIGJ5IENhcmwNCjIJc3RyaW5nCS1senMtCVtmaWQ9MDAwMTAwMDE3LTUwLTAwMDBMWkg7ZXh0PWx6aDttaW1lPTtdTGFyYyBhcmNoaXZlIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgbGhhIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQoyCXN0cmluZwktbGhcXCAtCVtmaWQ9MDAwMTAwMDE3LTUwLTAwMDBMWkg7ZXh0PWx6aCxsaGE7bWltZT07XUxIYXJjIGFyY2hpdmUgZmlsZSwgdmVyc2lvbiAyLngNCg0KIyBNYWdpYyBJRCBmb3IgbGhhIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQoyCXN0cmluZwktbGgwLQlbZmlkPTAwMDEwMDAxNy01MC0wMDAwTFpIO2V4dD1semgsbGhhO21pbWU9O11MSGFyYyBhcmNoaXZlIGZpbGUsIHZlcnNpb24gMS54DQoNCiMgTWFnaWMgSUQgZm9yIGxoYSBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTEgYnkgQ2FybA0KMglzdHJpbmcJLWxoMS0JW2ZpZD0wMDAxMDAwMTctNTAtMDAwMExaSDtleHQ9bHpoLGxoYTttaW1lPTtdTEhhcmMgYXJjaGl2ZSBmaWxlLCB2ZXJzaW9uIDEueA0KDQojIE1hZ2ljIElEIGZvciBsaGEgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTExIGJ5IENhcmwNCjIJc3RyaW5nCS1saDItCVtmaWQ9MDAwMTAwMDE3LTUwLTAwMDBMWkg7ZXh0PWx6aCxsaGE7bWltZT07XUxIYXJjIGFyY2hpdmUgZmlsZSwgdmVyc2lvbiAyLngNCg0KIyBNYWdpYyBJRCBmb3
 IgbGhhIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQoyCXN0cmluZwktbGgzLQlbZmlkPTAwMDEwMDAxNy01MC0wMDAwTFpIO2V4dD1semgsbGhhO21pbWU9O11MSGFyYyBhcmNoaXZlIGZpbGUsIHZlcnNpb24gMi54DQoNCiMgTWFnaWMgSUQgZm9yIGxoYSBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTEgYnkgQ2FybA0KMglzdHJpbmcJLWxoNC0JW2ZpZD0wMDAxMDAwMTctNTAtMDAwMExaSDtleHQ9bHpoLGxoYTttaW1lPTtdTEhhcmMgYXJjaGl2ZSBmaWxlLCB2ZXJzaW9uIDIueA0KDQojIE1hZ2ljIElEIGZvciBsaGEgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTExIGJ5IENhcmwNCjIJc3RyaW5nCS1saDUtCVtmaWQ9MDAwMTAwMDE3LTUwLTAwMDBMWkg7ZXh0PWx6aCxsaGE7bWltZT07XUxIYXJjIGFyY2hpdmUgZmlsZSwgdmVyc2lvbiAyLngNCg0KIyBNYWdpYyBJRCBmb3IgbGhhIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQoyCXN0cmluZwktbGg2LQlbZmlkPTAwMDEwMDAxNy01MC0wMDAwTFpIO2V4dD1semgsbGhhO21pbWU9O11MSGFyYyBhcmNoaXZlIGZpbGUsIHZlcnNpb24gMi54DQoNCiMgTWFnaWMgSUQgZm9yIGxoYSBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTEgYnkgQ2FybA0KMglzdHJpbmcJLWxoNy0JW2ZpZD0wMDAxMDAwMTctNTAtMDAwMExaSDtleHQ9bHpoLGxoYTttaW1lPTtdTEhhcmM
 gYXJjaGl2ZSBmaWxlLCB2ZXJzaW9uIDIueA0KDQojIE1hZ2ljIElEIGZvciBsaGEgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTExIGJ5IENhcmwNCjIJc3RyaW5nCS1saGQtCVtmaWQ9MDAwMTAwMDE3LTUwLTAwMDBMWkg7ZXh0PWx6aCxsaGE7bWltZT07XUxIYXJjIGFyY2hpdmUgZmlsZSwgdmVyc2lvbiAyLngNCg0KIyBNYWdpYyBJRCBmb3IgbHpvIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQowCXN0cmluZwlcXHg4OVxceDRjXFx4NWFcXHg0ZlxceDAwXFx4MGRcXHgwYVxceDFhXFx4MGEJW2ZpZD0wMDAxMDAwMTgtNTAtMDAwMExaTztleHQ9bHpvO21pbWU9O11MWk9QIGFyY2hpdmUgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBMenggZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTIzIGJ5IENhcmwNCjAJc3RyaW5nCUxaWAlbZmlkPTAwMDEwMDA1OC01MC0wMDAwTFpYO2V4dD1seng7bWltZT07XUxaWCBhcmNoaXZlIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgTWRjZCAoTWlrZSBEYXZlbnBvcnQgY29tcHJlc3NvcikgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTA4IGJ5IENhcmwNCjAJc3RyaW5nCU1EbWQJW2ZpZD0wMDAxMDAwMDQtNTAtMDAwMDBNRDtleHQ9bWQ7bWltZT07XU1pa2UgRGF2ZW5wb3J0IGFyY2hpdmUgZmlsZQ0KPjQJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KJjUJYnl0ZQkxCQ0KDQojIE1hZ2ljIElEIGZvciBOYXNocmluayBmaWxl
 cy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMjMgYnkgQ2FybA0KMAlzdHJpbmcJTlNLCVtmaWQ9MDAwMDAxMjkyLTUwLTAwMDBOU0s7ZXh0PW5zazttaW1lPTtdTmFTaHJpbmsgYXJjaGl2ZSBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIFNlbW9uZSBhcmNoaXZlciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMjcgYnkgQ2FybA0KMAlzdHJpbmcJU0VNaAlbZmlkPTAwMDEwMDA2OC01MC0wMDAwT05FO2V4dD1vbmU7bWltZT07XVNlbW9uZSBhcmNoaXZlIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgTHBhYyBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDQgYnkgQ2FybA0KMAlzdHJpbmcJTFBBQwlbZmlkPTAwMDEwMDA4NC01MC0wMDAwUEFDO2V4dD1wYWM7bWltZT07XUxQQUMgYXVkaW8gYXJjaGl2ZSBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIENyb3NzZVBBQyBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMDggYnkgQ2FybA0KMAlzdHJpbmcJRFNJR0RDQwlbZmlkPTAwMDAwMTI2Mi01MC0wMDAwUEFDO2V4dD1wYWM7bWltZT07XUNyb3NzZVBBQyBhcmNoaXZlIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgSGlnaCBDb21wcmVzc2lvbiBNYXJrb3YgUHJlZGljdGl2ZSBDb2RlciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMjMgYnkgQ2FybA0KMAlzdHJpbmcJUFBaMglbZmlkPTAwMDEwMDA2MS01MC0wMDAwUE1aO2V4dD1wbXo7bWltZT07XVBQTVoyIGFyY2hpdmUgZmlsZQ0KD
 QojIE1hZ2ljIElEIGZvciBQb3dlcnBhY2tlciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMjcgYnkgQ2FybA0KMAlzdHJpbmcJUFAyMAlbZmlkPTAwMDEwMDA2Mi01MC0wMDAwMFBQO2V4dD1wcDttaW1lPTtdUG93ZXJwYWNrZXIgYXJjaGl2ZSBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIFBBUTEgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTI3IGJ5IENhcmwNCjAJc3RyaW5nCVBBUTFcXHgwRFxceDBBCVtmaWQ9MDAwMTAwMDY0LTUwLTAwMDBQUTE7ZXh0PXBxMTttaW1lPTtdUEFRMSBhcmNoaXZlIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgUEFRMyBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMjcgYnkgQ2FybA0KMAlzdHJpbmcJUEFRM1xceDBEXFx4MEEJW2ZpZD0wMDAxMDAwNjQtNTAtMDAwMFBRMztleHQ9cHEzO21pbWU9O11QQVEzIGFyY2hpdmUgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBQQVE2IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0yNyBieSBDYXJsDQowCXN0cmluZwlQQVE2CVtmaWQ9MDAwMTAwMDY0LTUwLTAwMDBQUTY7ZXh0PXBxNjttaW1lPTtdUEFRNiBhcmNoaXZlIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgUHJldHR5IHNpbXBsZSBhcmNoaXZlciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMjcgYnkgQ2FybA0KMAlzdHJpbmcJUFNBXFx4MDFcXHgwMwlbZmlkPTAwMDEwMDA2NS01MC0wMDAwUFNBO2V4dD1wc2E7bWltZT07XVByZXR0eS
 BzaW1wbGUgYXJjaGl2ZXIgYXJjaGl2ZSBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIFF1YW50dW0gY29tcHJlc3NvciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMjcgYnkgQ2FybA0KMAlzdHJpbmcJRFMJW2ZpZD0wMDAwMDEyOTQtNTAtMDAwMDAwUTtleHQ9cTttaW1lPTtdUXVhbnR1bSBhcmNoaXZlIGZpbGUNCiYyCWJ5dGUJPDIJDQo+MglieXRlCXgJLCB2ZXJzaW9uICVkDQo+MwlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIFJhciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMDggYnkgQ2FybA0KMAlzdHJpbmcJXFx4NTJcXHg2MVxceDcyXFx4MjFcXHgxYVxceDA3XFx4MDAJW2ZpZD0wMDAwMDEyNjctNTAtMDAwMFJBUjtleHQ9cmFyO21pbWU9O11SQVIgYXJjaGl2ZSBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIFJvbWFuaWFuIGFyY2hpdmVyIGVYcGVydCAoUkFYKSBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMjcgYnkgQ2FybA0KMAlzdHJpbmcJVUxFQglbZmlkPTAwMDAwMTI5NS01MC0wMDAwUkFYO2V4dD1yYXg7bWltZT07XVJBWCBhcmNoaXZlIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgUmVkdXEgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTI3IGJ5IENhcmwNCjAJc3RyaW5nCXJkcXgJW2ZpZD0wMDAxMDAwNjYtNTAtMDAwMFJEUTtleHQ9cmRxO21pbWU9O11SZWR1cSBhcmNoaXZlIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgUlBNIGZpbGVzLg0KIyBTdWJ
 taXR0ZWQgb24gMjAwNC0wMy0wNCBieSBDYXJsDQowCXN0cmluZwlcXHhlZFxceGFiXFx4ZWVcXHhkYglbZmlkPTAwMDAwMDAwMC01MC0wMDAwUlBNO2V4dD1ycG07bWltZT07XVJQTSBhcmNoaXZlDQo+NAlieXRlCXgJLCB2ZXJzaW9uICVkDQo+NQlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIHJ6aXAgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAzLTA0IGJ5IENhcmwNCjAJc3RyaW5nCVJaSVAJW2ZpZD0wMDAxMDAwNzYtNTAtMDAwMDBSWjtleHQ9cno7bWltZT07XVJ6aXAgYXJjaGl2ZSBmaWxlDQo+NAlieXRlCXgJLCB2ZXJzaW9uICVkDQo+NQlieXRlCXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIFN0cmVhbWxpbmUgQXJjaGl2YWwgVXRpbGl0eSBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMDggYnkgQ2FybA0KMwlzdHJpbmcJTEgwCVtmaWQ9MDAwMDAxMjY0LTUwLTAwMDBTQVI7ZXh0PXNhcjttaW1lPTtdU3RyZWFtaW5nIEFyY2hpdmVyIFV0aWxpdHkgYXJjaGl2ZSBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIFN0cmVhbWxpbmUgQXJjaGl2YWwgVXRpbGl0eSBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMDggYnkgQ2FybA0KMwlzdHJpbmcJTEg0CVtmaWQ9MDAwMDAxMjY0LTUwLTAwMDBTQVI7ZXh0PXNhcjttaW1lPTtdU3RyZWFtaW5nIEFyY2hpdmVyIFV0aWxpdHkgYXJjaGl2ZSBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIFN0cmVhbWxpbmUgQXJjaGl2YWwgVXRpbGl0eSBm
 aWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMDggYnkgQ2FybA0KMwlzdHJpbmcJTEg1CVtmaWQ9MDAwMDAxMjY0LTUwLTAwMDBTQVI7ZXh0PXNhcjttaW1lPTtdU3RyZWFtaW5nIEFyY2hpdmVyIFV0aWxpdHkgYXJjaGl2ZSBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIFNCWCBBcmNoaXZlciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMjcgYnkgQ2FybA0KMAlzdHJpbmcJU0IxXFx4MDAJW2ZpZD0wMDAwMDEyOTctNTAtMDAwMDBTQjtleHQ9c2I7bWltZT07XVNCWCBhcmNoaXZlIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgU0JDIEFyY2hpdmVyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0yNyBieSBDYXJsDQowCXN0cmluZwlTQkNcXHgxRQlbZmlkPTAwMDEwMDA2Ny01MC0wMDAwU0JDO2V4dD1zYmM7bWltZT07XVNCQyBhcmNoaXZlIGZpbGUNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMCBieSBDYXJsDQoxMAlzdHJpbmcJI1xcIFRoaXNcXCBpc1xcIGFcXCBzaGVsbFxcIGFyY2hpdmUJW2ZpZD0wMDAwMDAwMDAtNTAtMDAwU0hBUjtleHQ9c2hhcjttaW1lPTtdVU5JWCBzaGVsbCBhcmNoaXZlDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTAgYnkgQ2FybA0KMTEJc3RyaW5nCSNcXCBUaGlzXFwgaXNcXCBhXFwgc2hlbGxcXCBhcmNoaXZlCVtmaWQ9MDAwMDAwMDAwLTUwLTAwMFNIQVI7ZXh0PXNoYXI7bWltZT07XVVOSVggc2hlbGwgYXJjaGl2ZQ0KDQojIE1hZ2ljIElEIGZvc
 iBOdWxpYiBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMDggYnkgQ2FybA0KMAlzdHJpbmcJXFx4NGVcXHhmNVxceDQ2XFx4ZDgJW2ZpZD0wMDAxMDAwMDUtNTAtMDAwMFNISztleHQ9c2hrO21pbWU9O11TaHJpbmtpdC9OdWxpYiBhcmNoaXZlIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgTnVsaWIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTA4IGJ5IENhcmwNCjAJc3RyaW5nCVxceDRlXFx4ZjVcXHg0NlxceGU5XFx4NmNcXHhlNQlbZmlkPTAwMDEwMDAwNS01MC0wMDAwU0hLO2V4dD1zaGs7bWltZT07XVNocmlua2l0L051bGliIGFyY2hpdmUgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBTdHVmZml0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wNCBieSBDYXJsDQowCXN0cmluZwlTdHVmZkl0XFwgCVtmaWQ9MDAwMDAxMjcyLTUwLTAwMDBTSVQ7ZXh0PXNpdDttaW1lPTtdU3R1ZmZpdCBhcmNoaXZlIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgU3R1ZmZpdCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTEgYnkgQ2FybA0KMAlzdHJpbmcJU0lUIQlbZmlkPTAwMDAwMTI3Mi01MC0wMDAwU0lUO2V4dD1zaXQ7bWltZT07XVN0dWZmaXQgYXJjaGl2ZSBmaWxlDQomMTAJc3RyaW5nCXJMYXUJDQo+MTQJYnl0ZQl4CSwgdmVyc2lvbiAlZC4wDQoNCiMgTWFnaWMgSUQgZm9yIFN0dWZmaXQgRXh0ZW5kZWQgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAzLTA0IGJ5IENhcm
 wNCjAJc3RyaW5nCVN0dWZmSXQhCVtmaWQ9MDAwMDAxMjcyLTUwLTAwMFNJVFg7ZXh0PXNpdHg7bWltZT07XVN0dWZmaXQgZXh0ZW5kZWQgYXJjaGl2ZSBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIFNvZiBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDMtMDQgYnkgQ2FybA0KMAlzdHJpbmcJUEtcXHgwM1xceDA2CVtmaWQ9MDAwMDAwMDAwLTUwLTAwMDBTT0Y7ZXh0PXNvZjttaW1lPTtdU09GIGFyY2hpdmUgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBTcGxpbnQgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTA4IGJ5IENhcmwNCjAJc3RyaW5nCVxceDkzXFx4QjlcXHgwNglbZmlkPTAwMDEwMDAwNi01MC0wMDAwU1BMO2V4dD1zcGw7bWltZT07XVNwbGludCBhcmNoaXZlIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgU3F3ZWV6IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMCBieSBDYXJsDQowCXN0cmluZwlTUVdFWlxcIAlbZmlkPTAwMDEwMDAxNC01MC0wMDAwU1FaO2V4dD1zcXo7bWltZT07XVNxd2V6IGFyY2hpdmUgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBTcXogZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTEwIGJ5IENhcmwNCjAJc3RyaW5nCUhMU1FaCVtmaWQ9MDAwMTAwMDE1LTUwLTAwMDBTUVo7ZXh0PXNxejttaW1lPTtdU3F1ZWV6ZSBhcmNoaXZlIGZpbGUNCj41CXN0cmluZwl4CSwgdmVyc2lvbiAlLjFzLjANCg0KIyBNYWdpYyBJRCBmb3IgU3RvbmVjcmFja2VyIGZ
 pbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0yNyBieSBDYXJsDQowCXN0cmluZwlTNDAxCVtmaWQ9MDAwMTAwMDYzLTUwLTAwMDBTVEM7ZXh0PXN0YzttaW1lPTtdU3RvbmVjcmFja2VyIGFyY2hpdmUgZmlsZQ0KPjAJc3RyaW5nCVM0MDEJLCB2ZXJzaW9uIDQuMDENCg0KIyBNYWdpYyBJRCBmb3IgU3RvbmVjcmFja2VyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0yNyBieSBDYXJsDQowCXN0cmluZwlTNDAzCVtmaWQ9MDAwMTAwMDYzLTUwLTAwMDBTVEM7ZXh0PXN0YzttaW1lPTtdU3RvbmVjcmFja2VyIGFyY2hpdmUgZmlsZQ0KPjAJc3RyaW5nCVM0MDMJLCB2ZXJzaW9uIDQuMDMNCg0KIyBNYWdpYyBJRCBmb3IgU3RvbmVjcmFja2VyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0yNyBieSBDYXJsDQowCXN0cmluZwlTNDA0CVtmaWQ9MDAwMTAwMDYzLTUwLTAwMDBTVEM7ZXh0PXN0YzttaW1lPTtdU3RvbmVjcmFja2VyIGFyY2hpdmUgZmlsZQ0KPjAJc3RyaW5nCVM0MDQJLCB2ZXJzaW9uIDQuMDQNCg0KIyBNYWdpYyBJRCBmb3IgU3ppcCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMDggYnkgQ2FybA0KMAlzdHJpbmcJU1pcXHgwQVxceDA0CVtmaWQ9MDAwMTAwMDA5LTUwLTAwMDAwU1o7ZXh0PXN6O21pbWU9O11TWklQIGFyY2hpdmUgZmlsZQ0KPjQJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjUJYnl0ZQl4CS4lZA0KDQojIE1hZ2ljIElEIGZvciBUYXIscGF4IGZpbGVz
 Lg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMCBieSBDYXJsDQoyNTcJc3RyaW5nCXVzdGFyXFwwNDBcXDA0MFxcMAlbZmlkPTAwMDAwMDAwMC01MC0wMDAwVEFSO2V4dD10YXI7bWltZT07XUdOVSB0YXIgYXJjaGl2ZQ0KDQojIE1hZ2ljIElEIGZvciBUYXIscGF4IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMCBieSBDYXJsDQoyNTcJc3RyaW5nCXVzdGFyXFwwXFx4MDYJW2ZpZD0wMDAwMDAwMDMtNTAtMDAwMTAwMztleHQ9dGFyO21pbWU9O11PcGVuZ3JvdXAvUE9TSVggdGFyIGFyY2hpdmUNCg0KIyBNYWdpYyBJRCBmb3IgdWMyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMCBieSBDYXJsDQowCXN0cmluZwlVQzJcXHgxYQlbZmlkPTAwMDAwMTI3MS01MC0wMDAwVUMyO2V4dD11YzI7bWltZT07XVVsdHJhIENvbXByZXNzb3IgYXJjaGl2ZSBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIFVoYXJjIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wNCBieSBDYXJsDQowCXN0cmluZwlVSEEJW2ZpZD0wMDAxMDAwODMtNTAtMDAwMFVIQTtleHQ9dWhhO21pbWU9O11VSEFyYyBhcmNoaXZlIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgWUJTIGFyY2hpdmVyIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0yNyBieSBDYXJsDQowCXN0cmluZwlZQlMzCVtmaWQ9MDAwMTAwMDcxLTUwLTAwMDBZQlM7ZXh0PXliczttaW1lPTtdWUJTIGFyY2hpdmUgZmlsZQ0KDQojIFN1Ym1pdHRlZ
 CBvbiAyMDA0LTAyLTExIGJ5IENhcmwNCjAJc3RyaW5nCVxcMDM3XFwyMzUJW2ZpZD0wMDAwMDAwMDAtNTAtMDAwMDAwWjtleHQ9ejttaW1lPTtdVU5JWCBhcmNoaXZlIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgUGt1bnppcCwgSW5mby16aXAgVW56aXAgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTA4IGJ5IENhcmwNCjAJbGVsb25nCTB4MDQwMzRiNTAJW2ZpZD0wMDAwMDEyNjYtNTAtMDAwMFpJUDtleHQ9emlwO21pbWU9YXBwbGljYXRpb24vemlwO11Qa3ppcCBhcmNoaXZlIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3Igem9vIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMCBieSBDYXJsDQowCXN0cmluZwlaT08JW2ZpZD0wMDAxMDAwMTItNTAtMDAwMFpPTztleHQ9em9vO21pbWU9O11ab28gYXJjaGl2ZSBmaWxlDQomMHgxNAlsZWxvbmcJMHgwRkRDNEE3REMJDQo+MzIJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjMzCWJ5dGUJeAkuJWQNCg0KIyBNYWdpYyBJRCBmb3IgWnppcCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMDggYnkgQ2FybA0KMAlzdHJpbmcJWloJW2ZpZD0wMDAxMDAwMDctNTAtMDAwMDBaWjtleHQ9eno7bWltZT07XVpaaXAgYXJjaGl2ZSBmaWxlDQo+MglieXRlCXgJLCB2ZXJzaW9uICVkLjANCg0KIyBNYWdpYyBJRCBmb3IgNjI0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wMyBieSBDYXJsDQowCXN0cmluZwlbRVNQXVxceEI1XFx4NzgJW2ZpZD
 0wMDAxMDAwMDItNTEtMDAwMENPTTtleHQ9Y29tO21pbWU9O102MjQgZXhlY3V0YWJsZSBjb21wcmVzc2VkIGZpbGUgKE1TLURPUykNCg0KIyBNYWdpYyBJRCBmb3IgNjI0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMy0wMyBieSBDYXJsDQowCXN0cmluZwlQVUxQXFx4ODMJW2ZpZD0wMDAxMDAwMDItNTEtMDAwMENPTTtleHQ9Y29tO21pbWU9O102MjQgZXhlY3V0YWJsZSBjb21wcmVzc2VkIGZpbGUgKE1TLURPUykNCg0KIyBNYWdpYyBJRCBmb3IgTHpleGUgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTIzIGJ5IENhcmwNCjB4MUMJc3RyaW5nCUxaOTEJW2ZpZD0wMDAxMDAwNTMtNTEtMDAwMEVYRTtleHQ9ZXhlO21pbWU9O11MemV4ZSBjb21wcmVzc2VkIGV4ZWN1dGFibGUgZmlsZSAoTVMtRE9TKQ0KDQojIE1hZ2ljIElEIGZvciBIVE1MIEhlbHAgV29ya3Nob3AgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTA4IGJ5IENhcmwNCjAJc3RyaW5nCUlUU0YJW2ZpZD0wMDAwMDEwMDEtNjAtMDAwMENITTtleHQ9Y2htO21pbWU9O11NaWNyb3NvZnQgY29tcGlsZWQgaHlwZXJ0ZXh0IGRvY3VtZW50DQoNCiMgTWFnaWMgSUQgZm9yIEJvcmxhbmQgRGVscGhpIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNy0zMCBieSBDYXJsDQoyCXN0cmluZwlERUxQSEkuRElBR1JBTS5QT1JURk9MSU8JW2ZpZD0wMDAwMDEwMDUtNjAtMDAwMEREUDtleHQ9ZGRwO21pbWU9O11EZWxwaGkgRGl
 hZ3JhbSBQb3J0Zm9saW8gZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBQYWdlc3RyZWFtIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0wOCBieSBDYXJsDQowCXN0cmluZwlGT1JNCVtmaWQ9MDAwMDAxMjY4LTYwLTAwMDBET0M7ZXh0PWRvYzttaW1lPTtdUGFnZXN0cmVhbSBkb2N1bWVudA0KJjgJc3RyaW5nCURPQ1xcIAkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0wOCBieSBDYXJsDQowCXN0cmluZwlDQVRcXCAJW2ZpZD0wMDAwMDEwMTAtNjAtMDAwRlRYVDtleHQ9ZnR4dDttaW1lPTtdRm9ybWF0dGVkIHRleHQgaW50ZXJjaGFuZ2UgZmlsZQ0KJjgJc3RyaW5nCUZUWFQJDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMDggYnkgQ2FybA0KMAlzdHJpbmcJRk9STQlbZmlkPTAwMDAwMTAxMC02MC0wMDBGVFhUO2V4dD1mdHh0O21pbWU9O11Gb3JtYXR0ZWQgdGV4dCBpbnRlcmNoYW5nZSBmaWxlDQomOAlzdHJpbmcJRlRYVAkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0wOCBieSBDYXJsDQowCXN0cmluZwlMSVNUCVtmaWQ9MDAwMDAxMDEwLTYwLTAwMEZUWFQ7ZXh0PWZ0eHQ7bWltZT07XUZvcm1hdHRlZCB0ZXh0IGludGVyY2hhbmdlIGZpbGUNCiY4CXN0cmluZwlGVFhUCQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTA4IGJ5IENhcmwNCjAJc3RyaW5nCUBkYXRhYmFzZQlbZmlkPTAwMDAwMTAwNy02MC0wMEdVSURFO2V4dD1ndWlkZTttaW1lPTtdQW1pZ2FHdWlkZSBoeXBlcnRleHQgZG9j
 dW1lbnQNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCWxlbG9uZwkweDAwMDM1RjNGCVtmaWQ9MDAwMDAxMDAxLTYwLTAwMDBITFA7ZXh0PWhscDttaW1lPTtdTWljcm9zb2Z0IFdpbmRvd3MgSGVscCBmaWxlDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMTAtMDggYnkgQ2FybA0KMAlzdHJpbmcJPCFET0NUWVBFXFwgCVtmaWQ9MDAwMDAwMDAxLTYwLTAwMTU0NDU7ZXh0PWh0bWwsaHRtO21pbWU9dGV4dC9odG1sO11IeXBlclRleHQgTWFya3VwIExhbmd1YWdlIGRvY3VtZW50IChIVE1MKQ0KJjEwCXN0cmluZy9jCWh0bWwJDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMDggYnkgQ2FybA0KMAlzdHJpbmcJSERPQwlbZmlkPTAwMDAwMTAwNi02MC0wMDAwSFlQO2V4dD1oeXA7bWltZT07XUF0YXJpIFNULUd1aWRlIGh5cGVydGV4dCBkb2N1bWVudA0KDQojIE1hZ2ljIElEIGZvciBPUy8yIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0wOCBieSBDYXJsDQowCXN0cmluZwlIU1AJW2ZpZD0wMDAwMDEwMDktNjAtMDAwMElORjtleHQ9aW5mO21pbWU9O11PUy8yIEd1aWRlIGh5cGVydGV4dCBkb2N1bWVudA0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTA4IGJ5IENhcmwNCjAJc3RyaW5nCVRoaXNcXCBpc1xcIEluZm9cXCBmaWxlCVtmaWQ9MDAwMTAwMDEwLTYwLTAwMElORk87ZXh0PWluZm87bWltZT07XUdOVSBJbmZvIGh5cGVydGV4dCBkb2N1bWVudA0KDQojIE1hZ2ljIElEI
 GZvciBBZG9iZSBBY3JvYmF0IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0xMC0wOCBieSBDYXJsDQowCXN0cmluZwklUERGLQlbZmlkPTAwMDAwMDAwMS02MC0wMDE1OTMwO2V4dD1wZGY7bWltZT1hcHBsaWNhdGlvbi9wZGY7XVBvcnRhYmxlIGRvY3VtZW50IGZvcm1hdCBkb2N1bWVudCBmaWxlIChQREYpDQo+NQlzdHJpbmcJeAksIHZlcnNpb24gJS4xcw0KPjcJc3RyaW5nCXgJLiUuMXMNCg0KIyBNYWdpYyBJRCBmb3IgTWljcm9zb2Z0IFdvcmRwYWQgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTA4IGJ5IENhcmwNCjAJc3RyaW5nCXtcXFxccnRmCVtmaWQ9MDAwMDAxMDAxLTYwLTAwMDBSVEY7ZXh0PXJ0ZjttaW1lPXRleHQvcnRmO11SaWNoIFRleHQgRm9ybWF0IGRvY3VtZW50DQo+NQlzdHJpbmcJeAksIHZlcnNpb24gJS4xcy54DQoNCiMgTWFnaWMgSUQgZm9yIERvY0Jvb2sgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTEwLTA4IGJ5IENhcmwNCjAJc3RyaW5nCTwhRE9DVFlQRVxcIAlbZmlkPTAwMDAwMDAwMS02MC0wMDA4ODc5O2V4dD1zZ21sLHNnbTttaW1lPXRleHQvc2dtbDtdU3RhbmRhcmQgZ2VuZXJhbCBtYXJrdXAgbGFuZ3VhZ2UgZG9jdW1lbnQgKFNHTUwpDQoNCiMgTWFnaWMgSUQgZm9yIFR1cmJvIEMgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE0IGJ5IENhcmwNCjAJc3RyaW5nCVRVUkJPXFwgQ1xcIEhFTFBcXCBGSUxFXFx4MDAJW2ZpZD0wMDAwMDEwMD
 UtNjAtMDAwMFRDSDtleHQ9dGNoO21pbWU9O11UdXJibyBDIGhlbHAgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBUdXJibyBQYXNjYWwgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTA4IGJ5IENhcmwNCjAJc3RyaW5nCVRVUkJPXFwgUEFTQ0FMXFwgSEVMUFxcIEZJTEVcXHgwMAlbZmlkPTAwMDAwMTAwNS02MC0wMDAwVFBIO2V4dD10cGg7bWltZT07XUJvcmxhbmQgUGFzY2FsIGhlbHAgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBUdXJibyBWaXNpb24gZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTA4IGJ5IENhcmwNCjAJbGVsb25nCTB4NDY0ODQyNDYJW2ZpZD0wMDAwMDEwMDUtNjAtMDAwMFRWSDtleHQ9dHZoO21pbWU9O11Cb3JsYW5kIFR1cmJvIFZpc2lvbiBoZWxwIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgV29yZHBlcmZlY3QgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTA4IGJ5IENhcmwNCjAJc3RyaW5nCVxceEZGV1BDCVtmaWQ9MDAwMDAxMDA4LTYwLTAwMDBXUEQ7ZXh0PXdwZCxkb2M7bWltZT1hcHBsaWNhdGlvbi92bmQud29yZHBlcmZlY3Q7XVdvcmRwZXJmZWN0IGRvY3VtZW50DQomOQlieXRlCTB4MEEJDQo+MTAJYnl0ZQl4CSwgdmVyc2lvbiAlZA0KPjExCWJ5dGUJeAkuJWQNCg0KIyBNYWdpYyBJRCBmb3IgTWljcm9zb2Z0IFdyaXRlIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0wOCBieSBDYXJsDQowCWxlc2hvcnQJMDEzNzA2MQlbZmlkPTAwMDAwMTA
 wMS02MC0wMDAwV1JJO2V4dD13cmk7bWltZT07XU1pY3Jvc29mdCBXcml0ZSBkb2N1bWVudA0KJjIJbGVzaG9ydAkweDAwMDAJDQoNCiMgTWFnaWMgSUQgZm9yIE1pY3Jvc29mdCBXcml0ZSBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMDggYnkgQ2FybA0KMAlsZXNob3J0CTAxMzcwNjIJW2ZpZD0wMDAwMDEwMDEtNjAtMDAwMFdSSTtleHQ9d3JpO21pbWU9O11NaWNyb3NvZnQgV3JpdGUgZG9jdW1lbnQNCiYyCWxlc2hvcnQJMHgwMDAwCQ0KDQojIE1hZ2ljIElEIGZvciBYLVdpbmRvd3MgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA5LTAxIGJ5IENhcmwNCjAJc3RyaW5nCVNUQVJURk9OVFxcIAlbZmlkPTAwMDAwMTAwMy03MC0wMDAwQkRGO2V4dD1iZGY7bWltZT07XUFkb2JlIGdseXBoIGJpdG1hcCBkaXN0cmlidXRpb24gZm9ybWF0IGZvbnQgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBEZWx1eGUgUGFpbnQgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA5LTAxIGJ5IENhcmwNCjAJc3RyaW5nCUNGXFx4MDFcXHgwMAlbZmlkPTAwMDAwMTAxMC03MC0wMDAwMDBDO2V4dD1jO21pbWU9O11EZWx1eGUgcGFpbnQgbXVsdGktY29sb3VyIGZvbnQgZmlsZQ0KDQojIE1hZ2ljIElEIGZvciBGaWdsZXQgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA5LTAxIGJ5IENhcmwNCjAJc3RyaW5nCWZsZjIJW2ZpZD0wMDAxMDAxMjItNzAtMDAwMEZMRjtleHQ9ZmxmO21pbWU9O11GaWdsZXQgZm9u
 dCBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIFBDTCA1IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wOS0wMSBieSBDYXJsDQowCXN0cmluZwlcXHgwMFxceDQ0XFx4MDBcXHgwMVxceDAwXFx4MDBcXHgwMFxceDFBCVtmaWQ9MDAwMDAxMzM2LTcwLTAwMDBMSUI7ZXh0PWxpYix0eXBlO21pbWU9O11JbnRlbGxpZm9udCBTY2FsYWJsZSBUeXBlZmFjZSBGb3JtYXQgIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgRGVsdXhlIFBhaW50IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wOS0wMSBieSBDYXJsDQowCXN0cmluZwlcXHgxQlxceDI5XFx4NzNcXHgzNlxceDM0XFx4NTdcXHgwMFxceDQwCVtmaWQ9MDAwMDAxMDEwLTcwLTAwMDAwME07ZXh0PW07bWltZT07XURlbHV4ZSBwYWludCBtb25vIGNvbG91ciBmb250IGZpbGUNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNS0yMCBieSBDYXJsDQowCWJlbG9uZwkweDAwMDEwMDAwCVtmaWQ9MDAwMDAxMDAxLTcwLTAwMDBUVEY7ZXh0PW90Zix0dGY7bWltZT07XU9wZW50eXBlIGZvbnQgZmlsZQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA1LTIwIGJ5IENhcmwNCjAJc3RyaW5nCU9UVE8JW2ZpZD0wMDAwMDEwMDEtNzAtMDAwMFRURjtleHQ9b3RmLHR0ZjttaW1lPTtdT3BlbnR5cGUgZm9udCBmaWxlLCBDRkYgdHlwZQ0KDQojIE1hZ2ljIElEIGZvciBYLVdpbmRvd3MgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA5LTAxIGJ5IENhcmwNCjAJYmVsb25nCTB4M
 DE2NjYzNzAJW2ZpZD0wMDAwMDAwMDAtNzAtMDAwMFBDRjtleHQ9cGNmO21pbWU9O11Qb3J0YWJsZSBjb21waWxlZCBmb3JtYXQgKFBDRikgZm9udCBmaWxlDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDUtMjAgYnkgQ2FybA0KMAlzdHJpbmcJJSFGb250VHlwZTEJW2ZpZD0wMDAwMDAwMDEtNzAtMDAwOTU0MTtleHQ9cGZhO21pbWU9O11UeXBlIDEgZm9udCBmaWxlDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDUtMjAgYnkgQ2FybA0KMAlzdHJpbmcJJSFQUy1BZG9iZUZvbnQtMS4wCVtmaWQ9MDAwMDAwMDAxLTcwLTAwMDk1NDE7ZXh0PXBmYTttaW1lPTtdVHlwZSAxIGZvbnQgZmlsZQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA1LTIwIGJ5IENhcmwNCjYJc3RyaW5nCSUhRm9udFR5cGUxCVtmaWQ9MDAwMDAxMDAzLTcwLTAwMDBQRkI7ZXh0PXBmYjttaW1lPTtdVHlwZSAxIHByaW50ZXIgZm9udCBiaW5hcnkgZmlsZQ0KJjAJYnl0ZQkweDgwCQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA1LTIwIGJ5IENhcmwNCjYJc3RyaW5nCSUhUFMtQWRvYmVGb250LTEuMAlbZmlkPTAwMDAwMTAwMy03MC0wMDAwUEZCO2V4dD1wZmI7bWltZT07XVR5cGUgMSBwcmludGVyIGZvbnQgYmluYXJ5IGZpbGUNCiYwCWJ5dGUJMHg4MAkNCg0KIyBNYWdpYyBJRCBmb3IgWC1XaW5kb3dzLCBHZW1ET1MgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA5LTAxIGJ5IENhcmwNCjIJc3RyaW5nCVxceDJFXFx4MzBcXHgwRFxceDBBXF
 x4MDBcXHgwMAlbZmlkPTAwMDAwMTMzNy03MC0wMDAwU1BEO2V4dD1zcGQ7bWltZT07XUJpdHN0cmVhbSBTcGVlZG8gZm9udCBmaWxlDQomMAlzdHJpbmcJRAkNCj4yNAlzdHJpbmcJeAlbdGl0bGU9JS43MHNdDQo+MQlzdHJpbmcJeAksdmVyc2lvbiAlLjFzLjANCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNS0yMCBieSBDYXJsDQowCXN0cmluZwl0cnVlCVtmaWQ9MDAwMDAxMDAyLTcwLTAwMDBUVEY7ZXh0PXR0ZjttaW1lPTtdVHJ1ZXR5cGUgZm9udCBmaWxlDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDUtMjAgYnkgQ2FybA0KMAlzdHJpbmcJdHlwMQlbZmlkPTAwMDAwMTAwMi03MC0wMDAwVFRGO2V4dD10dGY7bWltZT07XVRydWV0eXBlIGZvbnQgZmlsZQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA1LTIwIGJ5IENhcmwNCjAJc3RyaW5nCVN0YXJ0Q29tcEZvbnRNZXRyaWNzCVtmaWQ9MDAwMDAxMDAzLTcwLTAwMDBBRk07ZXh0PWFmbTttaW1lPTtdQWRvYmUgY29tcG9zaXRlIGZvbnQgbWV0cmljcyBmaWxlDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDUtMjAgYnkgQ2FybA0KMAlzdHJpbmcJU3RhcnRGb250TWV0cmljcwlbZmlkPTAwMDAwMTAwMy03MC0wMDAwQUZNO2V4dD1hZm07bWltZT07XUFkb2JlIGZvbnQgbWV0cmljcyBmaWxlDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDUtMjAgYnkgQ2FybA0KMAlzdHJpbmcJU3RhcnRNYXN0ZXJGb250TWV0cmljcwlbZmlkPTAwMDAwMTAwMy03MC0wMDAwQUZNO2V
 4dD1hZm07bWltZT07XUFkb2JlIG1hc3RlciBmb250IG1ldHJpY3MgZmlsZQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA1LTIwIGJ5IENhcmwNCjAJbGVzaG9ydAkweDAxMDAJW2ZpZD0wMDAwMDEwMDEtNzAtMDAwMFBGTTtleHQ9cGZtO21pbWU9O11QcmludGVyIEZvbnQgTWV0cmljcyBmaWxlIGZvciBUeXBlIDEgZm9udHMNCiY2NglsZXNob3J0CTB4MDA4MQkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0wOCBieSBDYXJsDQowCWxlbG9uZwkweDc1QjIyNjMwCVtmaWQ9MDAwMDAxMDAxLTgxLTAwMDBBU0Y7ZXh0PWFzZix3bWEsd212O21pbWU9O11NaWNyb3NvZnQgQWR2YW5jZWQgU3RyZWFtaW5nIGZvcm1hdCBtdWx0aW1lZGlhIGZpbGUNCiY2CWxlc2hvcnQJMHgxMUNGCQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTA4IGJ5IENhcmwNCjAJc3RyaW5nCVJJRkYJW2ZpZD0wMDAwMDEwMDEtODEtMDAwMEFWSTtleHQ9YXZpO21pbWU9O11BdWRpby12aWRlbyBpbnRlcmxlYXZlZCBtb3ZpZSwgbGl0dGxlLWVuZGlhbg0KJjgJc3RyaW5nCUFWSVxcIAkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMyBieSBDYXJsDQowCXN0cmluZwlSSUZYCVtmaWQ9MDAwMDAxMDAxLTgxLTAwMDBBVkk7ZXh0PWF2aTttaW1lPTtdQXVkaW8tdmlkZW8gaW50ZXJsZWF2ZWQgbW92aWUsIGJpZy1lbmRpYW4NCiY4CXN0cmluZwlBVklcXCAJDQoNCiMgTWFnaWMgSUQgZm9yIFF1aWNrdGltZSBmaWxlcy4NCiMgU3VibWl0dGVk
 IG9uIDIwMDQtMDItMDggYnkgQ2FybA0KNAlzdHJpbmcJbWRhdAlbZmlkPTAwMDAwMTAwMi04MS0wMDAwTU9WO2V4dD1tb3YscXQ7bWltZT12aWRlby9xdWlja3RpbWU7XUFwcGxlIFF1aWNrVGltZSBtb3ZpZSBkYXRhIChtZGF0KQ0KDQojIE1hZ2ljIElEIGZvciBRdWlja3RpbWUgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTA4IGJ5IENhcmwNCjQJc3RyaW5nCW1vb3YJW2ZpZD0wMDAwMDEwMDItODEtMDAwME1PVjtleHQ9bW92LHF0O21pbWU9dmlkZW8vcXVpY2t0aW1lO11BcHBsZSBRdWlja1RpbWUgbW92aWUgcmVzb3VyY2UgKG1vb3YpDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDUtMjAgYnkgQ2FybA0KMAliZWxvbmcJMHgxQjMJW2ZpZD0wMDAwMDAwMDEtODEtMDAwTVBFRztleHQ9bXBlLG1wZWcsbXBnO21pbWU9dmlkZW8vbXBlZztdTVBFRyBtdWx0aW1lZGlhIChhdWRpby92aWRlbykgc3RyZWFtIGZpbGUNCiZaNAliZWxvbmcJMHgwMUI5CQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA1LTIwIGJ5IENhcmwNCjAJYmVsb25nCTB4MUJBCVtmaWQ9MDAwMDAwMDAxLTgxLTAwME1QRUc7ZXh0PW1wZSxtcGVnLG1wZzttaW1lPXZpZGVvL21wZWc7XU1QRUcgbXVsdGltZWRpYSAoYXVkaW8vdmlkZW8pIHN0cmVhbSBmaWxlDQomWjQJYmVsb25nCTB4MDFCOQkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNS0yMCBieSBDYXJsDQowCWJlbG9uZwkweDFFMAlbZmlkPTAwMDAwMDAwMS04MS0wMDBNUEVHO
 2V4dD1tcGUsbXBlZyxtcGc7bWltZT12aWRlby9tcGVnO11NUEVHIG11bHRpbWVkaWEgKGF1ZGlvL3ZpZGVvKSBzdHJlYW0gZmlsZQ0KJlo0CWJlbG9uZwkweDAxQjkJDQoNCiMgTWFnaWMgSUQgZm9yIEFsaWFzL1dhdmVmcm9udCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMDggYnkgQ2FybA0KMAlzdHJpbmcJTU9WSQlbZmlkPTAwMDAwMTAwNC04MS0wMDAwME1WO2V4dD1tdjttaW1lPTtdQWxpYXMvV2F2ZWZyb250IG1vdmllIGZpbGUNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNS0xOCBieSBDYXJsDQowCXN0cmluZwlPZ2dTCVtmaWQ9MDAwMDAwMDAwLTgxLTAwMDBPR0c7ZXh0PW9nZzttaW1lPWFwcGxpY2F0aW9uL29nZztdT0dHIE11bHRpbWVkaWEgY29udGFpbmVyIHN0cmVhbSBmaWxlDQoNCiMgTWFnaWMgSUQgZm9yIFJlYWxwbGF5ZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA1LTE5IGJ5IENhcmwNCjAJc3RyaW5nCS5STUYJW2ZpZD0wMDAwMDEwMjctODEtMDAwMDBSTTtleHQ9cm0scmEscnQscnAscnBhO21pbWU9O11SZWFsTWVkaWEgbXVsdGltZWRpYSBjb250YWluZXIgc3RyZWFtIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgTWFjcm9tZWRpYSBGbGFzaCBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMDggYnkgQ2FybA0KMAlzdHJpbmcJRldTCVtmaWQ9MDAwMDAxMjU5LTgxLTAwMDBTV0Y7ZXh0PXN3ZjttaW1lPTtdU2hvY2t3YXZlIE1hY3JvbWVkaWEgYW5pbWF0aW
 9uDQo+MwlieXRlCXgJLCB2ZXJzaW9uICVkLjANCg0KIyBNYWdpYyBJRCBmb3IgVml2byBwbGF5ZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA1LTIwIGJ5IENhcmwNCjUJc3RyaW5nCVZlcnNpb246Vml2bwlbZmlkPTAwMDAwMDAwMC04MS0wMDBWSVZPO2V4dD12aXYsdml2bzttaW1lPXZpZGVvL3ZuZC52aXZvO11WaXZvIG11bHRpbWVkaWEgc3RyZWFtIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgQWVnaXMgQW5pbWF0b3IgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTA4IGJ5IENhcmwNCjAJc3RyaW5nCUZPUk0JW2ZpZD0wMDAwMDEyNjEtODItMDAwQU5JTTtleHQ9YW5pbTttaW1lPTtdQW1pZ2EgLyBTcGFydGEgc29mdHdhcmUgYW5pbWF0aW9uDQomOAlzdHJpbmcJQU5JTQkNCg0KIyBNYWdpYyBJRCBmb3IgRmFudGF2aXNpb24gbW92aWUgbWFrZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTA4IGJ5IENhcmwNCjAJc3RyaW5nCUZPUk0JW2ZpZD0wMDAwMDEyNjAtODItMDAwRkFOVDtleHQ9ZmFudDttaW1lPTtdRmFudGF2aXNpb24gbW92aWUNCiY4CXN0cmluZwlGQU5UCQ0KDQojIE1hZ2ljIElEIGZvciBBdXRvZGVzayBBbmltYXRvciBQcm8gZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTA4IGJ5IENhcmwNCjQJbGVzaG9ydAkweEFGMTIJW2ZpZD0wMDAwMDEyNTQtODItMDAwMEZMQztleHQ9ZmxjO21pbWU9O11BdXRvZGVzayBBbmltYXRvciBQcm8gYW5pbWF0aW9
 uDQomMTIJbGVzaG9ydAkweDA4CQ0KPjgJbGVzaG9ydAl4CVtyZXM9JWR4DQo+MTAJbGVzaG9ydAl4CSVkDQo+MTIJbGVzaG9ydAl4CXg4YnBwXQ0KDQojIE1hZ2ljIElEIGZvciBBdXRvZGVzayBBbmltYXRvciBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMDggYnkgQ2FybA0KNAlsZXNob3J0CTB4QUYxMQlbZmlkPTAwMDAwMTI1NC04Mi0wMDAwRkxJO2V4dD1mbGk7bWltZT07XUF1dG9kZXNrIEFuaW1hdG9yIGFuaW1hdGlvbg0KJjEyCWxlc2hvcnQJMHgwOAkNCj44CWxlc2hvcnQJeAlbcmVzPSVkeA0KPjEwCWxlc2hvcnQJeAklZA0KPjEyCWxlc2hvcnQJeAl4OGJwcF0NCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0xMyBieSBDYXJsDQowCXN0cmluZwlcXHg4QU1OR1xceDBkXFx4MEFcXHgxQVxceDBBCVtmaWQ9MDAwMDAwMDAwLTgyLTAwMDBNTkc7ZXh0PW1uZzttaW1lPTtdTXVsdGktaW1hZ2UgbmV0d29yayBncmFwaGljcyBhbmltYXRpb24gZmlsZQ0KPjB4MTAJYmVsb25nCXgJW3Jlcz0lZHgNCj4weDE0CWJlbG9uZwl4CSVkXQ0KDQojIE1hZ2ljIElEIGZvciBDeWJlcnBhaW50IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0wOCBieSBDYXJsDQowCWJlc2hvcnQJMHhGRURCCVtmaWQ9MDAwMDAwMDAwLTgyLTAwMDBTRVE7ZXh0PXNlcTttaW1lPTtdQ3liZXJwYWludCBBbmltYXRpb24gU2VxdWVuY2UNCiYyCWJlc2hvcnQJMHgwMDAwCQ0KDQojIE1hZ2ljIElEIGZvciBRdWlja2Vu
 LE1vbmV5IGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wOC0wMSBieSBDYXJsDQowCXN0cmluZwlPRlhIRUFERVI6CVtmaWQ9MDAwMDAwMDA4LTkxLTAwMDBRRlg7ZXh0PXFmeDttaW1lPTtdT3BlbiBmaW5hbmNpYWwgZXhjaGFuZ2UgZmlsZSAoU0dNTCkNCg0KIyBNYWdpYyBJRCBmb3IgUXVpY2tlbiBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDgtMDEgYnkgQ2FybA0KMQlzdHJpbmcJQWNjb3VudAlbZmlkPTAwMDAwMTMzNS05MS0wMDAwUUlGO2V4dD1xaWY7bWltZT07XVF1aWNrZW4gaW50ZXJjaGFuZ2UgZmlsZQ0KJjAJYnl0ZQkzMwkNCg0KIyBNYWdpYyBJRCBmb3IgUXVpY2tlbiBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDgtMDEgYnkgQ2FybA0KMQlzdHJpbmcJVHlwZTpCYW5rCVtmaWQ9MDAwMDAxMzM1LTkxLTAwMDBRSUY7ZXh0PXFpZjttaW1lPTtdUXVpY2tlbiBpbnRlcmNoYW5nZSBmaWxlDQomMAlieXRlCTMzCQ0KDQojIE1hZ2ljIElEIGZvciBRdWlja2VuIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wOC0wMSBieSBDYXJsDQoxCXN0cmluZwlUeXBlOkNhc2gJW2ZpZD0wMDAwMDEzMzUtOTEtMDAwMFFJRjtleHQ9cWlmO21pbWU9O11RdWlja2VuIGludGVyY2hhbmdlIGZpbGUNCiYwCWJ5dGUJMzMJDQoNCiMgTWFnaWMgSUQgZm9yIFF1aWNrZW4gZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA4LTAxIGJ5IENhcmwNCjEJc3RyaW5nCVR5cGU6Q2F0CVtmaWQ9MDAwM
 DAxMzM1LTkxLTAwMDBRSUY7ZXh0PXFpZjttaW1lPTtdUXVpY2tlbiBpbnRlcmNoYW5nZSBmaWxlDQomMAlieXRlCTMzCQ0KDQojIE1hZ2ljIElEIGZvciBRdWlja2VuIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wOC0wMSBieSBDYXJsDQoxCXN0cmluZwlUeXBlOkNDYXJkCVtmaWQ9MDAwMDAxMzM1LTkxLTAwMDBRSUY7ZXh0PXFpZjttaW1lPTtdUXVpY2tlbiBpbnRlcmNoYW5nZSBmaWxlDQomMAlieXRlCTMzCQ0KDQojIE1hZ2ljIElEIGZvciBRdWlja2VuIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wOC0wMSBieSBDYXJsDQoxCXN0cmluZwlUeXBlOkNsYXNzCVtmaWQ9MDAwMDAxMzM1LTkxLTAwMDBRSUY7ZXh0PXFpZjttaW1lPTtdUXVpY2tlbiBpbnRlcmNoYW5nZSBmaWxlDQomMAlieXRlCTMzCQ0KDQojIE1hZ2ljIElEIGZvciBRdWlja2VuIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wOC0wMSBieSBDYXJsDQoxCXN0cmluZwlUeXBlOkludnN0CVtmaWQ9MDAwMDAxMzM1LTkxLTAwMDBRSUY7ZXh0PXFpZjttaW1lPTtdUXVpY2tlbiBpbnRlcmNoYW5nZSBmaWxlDQomMAlieXRlCTMzCQ0KDQojIE1hZ2ljIElEIGZvciBRdWlja2VuIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wOC0wMSBieSBDYXJsDQoxCXN0cmluZwlUeXBlOk1lbW9yaXplZAlbZmlkPTAwMDAwMTMzNS05MS0wMDAwUUlGO2V4dD1xaWY7bWltZT07XVF1aWNrZW4gaW50ZXJjaGFuZ2UgZmlsZQ0KJjAJYnl0ZQ
 kzMwkNCg0KIyBNYWdpYyBJRCBmb3IgUXVpY2tlbiBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDgtMDEgYnkgQ2FybA0KMQlzdHJpbmcJVHlwZTpPdGggQQlbZmlkPTAwMDAwMTMzNS05MS0wMDAwUUlGO2V4dD1xaWY7bWltZT07XVF1aWNrZW4gaW50ZXJjaGFuZ2UgZmlsZQ0KJjAJYnl0ZQkzMwkNCg0KIyBNYWdpYyBJRCBmb3IgUXVpY2tlbiBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMDgtMDEgYnkgQ2FybA0KMQlzdHJpbmcJVHlwZTpPdGggTAlbZmlkPTAwMDAwMTMzNS05MS0wMDAwUUlGO2V4dD1xaWY7bWltZT07XVF1aWNrZW4gaW50ZXJjaGFuZ2UgZmlsZQ0KJjAJYnl0ZQkzMwkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xMSBieSBDYXJsDQowCXN0cmluZwlSUkcJW2ZpZD0wMDAwMDEwMDEtQjAtMDAwMENSRDtleHQ9Y3JkO21pbWU9O11XaW5kb3dzIDMueCBjYXJkZmlsZQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTExIGJ5IENhcmwNCjAJc3RyaW5nCU1HQwlbZmlkPTAwMDAwMTAwMS1CMC0wMDAwQ1JEO2V4dD1jcmQ7bWltZT07XVdpbmRvd3MgTlQgMy41MSBjYXJkIGZpbGUNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wMi0xNCBieSBDYXJsDQowCXN0cmluZwlCRUdJTjpWQ0FSRAlbZmlkPTAwMDAwMTAxNS1CMC0wMDAwVkNGO2V4dD12Y2Y7bWltZT07XXZDYXJkIEJ1c2luZXNzIENhcmQgZmlsZQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTAyLTE0IGJ5IENhcmwNCjAJc3RyaW5
 nCVxceGI1XFx4YTJcXHhiMFxceGIzXFx4YjNcXHhiMFxceGEyXFx4YjUJW2ZpZD0wMDAwMDEwMDEtQjEtMDAwMENBTDtleHQ9Y2FsO21pbWU9O11NaWNyb3NvZnQgd2luZG93cyBjYWxlbmRhciBmaWxlDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDItMTQgYnkgQ2FybA0KMAlzdHJpbmcJQkVHSU46VkNBTEVOREFSCVtmaWQ9MDAwMDAxMDE2LUIxLTAwMDBWQ1M7ZXh0PXZjczttaW1lPTtddkNhbGVuZGFyL2lDYWxlbmRhciBBZ2VuZGEgZmlsZQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA1LTE3IGJ5IENhcmwNCjAJc3RyaW5nCVxceDAwXFx4MDBcXHgwMFxceDNDXFx4MDBcXHgwMFxceDAwXFx4M0YJW2ZpZD0wMDAwMDAwMDctRjMtMDAwMFhNTDtleHQ9eG1sO21pbWU9dGV4dC94bWw7XUV4dGVuc2libGUgTWFya3VwIGxhbmd1YWdlIChYTUwpIGZpbGUsIFVURi0zMkJFIGVuY29kZWQNCiY4CWJlbG9uZwkweDAwMDAwMDc4CQ0KJjEyCWJlbG9uZwkweDAwMDAwMDZkCQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA1LTE3IGJ5IENhcmwNCjAJc3RyaW5nCVxceDAwXFx4MDBcXHhmZVxceGZmXFx4MDBcXHgwMFxceDAwXFx4M0MJW2ZpZD0wMDAwMDAwMDctRjMtMDAwMFhNTDtleHQ9eG1sO21pbWU9dGV4dC94bWw7XUV4dGVuc2libGUgTWFya3VwIGxhbmd1YWdlIChYTUwpIGZpbGUsIFVURi0zMkJFIGVuY29kZWQNCiY4CWJlbG9uZwkweDAwMDAwMDNGCQ0KJjEyCWJlbG9uZwkweDAwMDAwMDc4CQ0KDQojIFN1Ym1p
 dHRlZCBvbiAyMDA0LTA1LTE3IGJ5IENhcmwNCjAJc3RyaW5nCVxceDAwXFx4M0NcXHgwMFxceDNGXFx4MDBcXHg3OFxceDAwXFx4NkRcXHgwMFxceDZjCVtmaWQ9MDAwMDAwMDA3LUYzLTAwMDBYTUw7ZXh0PXhtbDttaW1lPXRleHQveG1sO11FeHRlbnNpYmxlIE1hcmt1cCBsYW5ndWFnZSAoWE1MKSBmaWxlLCBVVEYtMTZCRSBlbmNvZGVkDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDUtMTcgYnkgQ2FybA0KMAlzdHJpbmcJXFx4M0NcXHgwMFxceDAwXFx4MDBcXHgzRlxceDAwXFx4MDBcXHgwMAlbZmlkPTAwMDAwMDAwNy1GMy0wMDAwWE1MO2V4dD14bWw7bWltZT10ZXh0L3htbDtdRXh0ZW5zaWJsZSBNYXJrdXAgbGFuZ3VhZ2UgKFhNTCkgZmlsZSwgVVRGLTMyTEUgZW5jb2RlZA0KJjgJYmVzaG9ydAkweDAwMDAwMDc4CQ0KJjEyCWJlc2hvcnQJMHgwMDAwMDA2ZAkNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNS0xNyBieSBDYXJsDQowCXN0cmluZwlcXHgzQ1xceDAwXFx4M0ZcXHgwMFxceDc4XFx4MDBcXHg2RFxceDAwXFx4NmNcXHgwMAlbZmlkPTAwMDAwMDAwNy1GMy0wMDAwWE1MO2V4dD14bWw7bWltZT10ZXh0L3htbDtdRXh0ZW5zaWJsZSBNYXJrdXAgbGFuZ3VhZ2UgKFhNTCkgZmlsZSwgVVRGLTE2TEUgZW5jb2RlZA0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA1LTE3IGJ5IENhcmwNCjAJc3RyaW5nCVxceGVmXFx4YmJcXHhiZjw/eG1sCVtmaWQ9MDAwMDAwMDA3LUYzLTAwMDBYTUw7ZXh0PXhtbDtta
 W1lPXRleHQveG1sO11FeHRlbnNpYmxlIE1hcmt1cCBsYW5ndWFnZSAoWE1MKSBmaWxlLCBVVEYtOCBlbmNvZGVkDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDUtMTcgYnkgQ2FybA0KMAlzdHJpbmcJXFx4ZmVcXHhmZlxceDAwXFx4M0NcXHgwMFxceDNGXFx4MDBcXHg3OFxceDAwXFx4NkQJW2ZpZD0wMDAwMDAwMDctRjMtMDAwMFhNTDtleHQ9eG1sO21pbWU9dGV4dC94bWw7XUV4dGVuc2libGUgTWFya3VwIGxhbmd1YWdlIChYTUwpIGZpbGUsIFVURi0xNkJFIGVuY29kZWQNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNS0xNyBieSBDYXJsDQowCXN0cmluZwlcXHhmZlxceGZlXFx4MDBcXHgwMFxceDNDXFx4MDBcXHgwMFxceDAwCVtmaWQ9MDAwMDAwMDA3LUYzLTAwMDBYTUw7ZXh0PXhtbDttaW1lPXRleHQveG1sO11FeHRlbnNpYmxlIE1hcmt1cCBsYW5ndWFnZSAoWE1MKSBmaWxlLCBVVEYtMzJMRSBlbmNvZGVkDQomOAliZXNob3J0CTB4M0YwMDAwMDAJDQomMTIJYmVzaG9ydAkweDc4MDAwMDAwCQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTA1LTE3IGJ5IENhcmwNCjAJc3RyaW5nCVxceGZmXFx4ZmVcXHgzQ1xceDAwXFx4M0ZcXHgwMFxceDc4XFx4MDBcXHg2RFxceDAwCVtmaWQ9MDAwMDAwMDA3LUYzLTAwMDBYTUw7ZXh0PXhtbDttaW1lPXRleHQveG1sO11FeHRlbnNpYmxlIE1hcmt1cCBsYW5ndWFnZSAoWE1MKSBmaWxlLCBVVEYtMTZMRSBlbmNvZGVkDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDUtMT
 cgYnkgQ2FybA0KMAlzdHJpbmcJPD94bWwJW2ZpZD0wMDAwMDAwMDctRjMtMDAwMFhNTDtleHQ9eG1sO21pbWU9dGV4dC94bWw7XUV4dGVuc2libGUgTWFya3VwIGxhbmd1YWdlIChYTUwpIGZpbGUNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0wNC0yMCBieSBDYXJsDQowCXN0cmluZwlcXHhkMFxceGNmXFx4MTFcXHhlMFxceGExXFx4YjFcXHgxYVxceGUxCVtmaWQ9MDAwMDAxMDAxLUY0LTAwMDBPTEU7ZXh0PW9sZTttaW1lPTtdTWljcm9zb2Z0IE9MRSBDb21wb3VuZCBmaWxlLCBiaWctZW5kaWFuDQomMHgxQwlsZXNob3J0CSEweEZGRkUJDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMDQtMjAgYnkgQ2FybA0KMAlzdHJpbmcJXFx4ZDBcXHhjZlxceDExXFx4ZTBcXHhhMVxceGIxXFx4MWFcXHhlMQlbZmlkPTAwMDAwMTAwMS1GNC0wMDAwT0xFO2V4dD1vbGU7bWltZT07XU1pY3Jvc29mdCBPTEUgQ29tcG91bmQgZmlsZSwgbGl0dGxlLWVuZGlhbg0KJjB4MUMJbGVzaG9ydAkweEZGRkUJDQo+MHgxQQlsZXNob3J0CXgJLCB2ZXJzaW9uICVkDQo+MHgxOAlsZXNob3J0CXgJLiVkDQoNCiMgTWFnaWMgSUQgZm9yIEZyZWVwYXNjYWwgY29tcGlsZXIgZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTEwLTI0IGJ5IENhcmwNCjAJc3RyaW5nCVBQVTAJW2ZpZD0wMDAxMDAxMjAtMTEtMDAwMFBQVTtleHQ9cHB1LHBwbCxwcG8scHB3LHBwYSxwcHQ7bWltZT07XUZyZWVwYXNjYWwgdW5pdCBmaWxlDQo+MHgwNAlzdHJpbmc
 JeAksIHZlcnNpb24gJS4ycw0KDQojIE1hZ2ljIElEIGZvciBTbWFydGNhcmRzIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0xMC0yOSBieSBDYXJsDQowCWJlbG9uZwkweDAwRkFDQURFCVtmaWQ9MDAwMDAxMDExLTEwLTAwMDBFWFA7ZXh0PWV4cDttaW1lPTtdSmF2YWNhcmQgQVBJIGV4cG9ydCBmaWxlDQo+MHgwNQlieXRlCXgJLCB2ZXJzaW9uICVkDQo+NAlieXRlCXgJLiVkDQoNCiMgU3VibWl0dGVkIG9uIDIwMDQtMTAtMjkgYnkgQ2FybA0KMAlzdHJpbmcJLXJvbTFmcy0JW2ZpZD0wMDAxMDAxMjEtMEYtMDAwMElNRztleHQ9aW1nO21pbWU9O11TaW1wbGUgUk9NIGZpbGVzeXN0ZW0gKFJPTUZTKQ0KPjE2CXN0cmluZwl4CVt0aXRsZT0lc10NCg0KIyBNYWdpYyBJRCBmb3IgVmlydHVhbCBQQyBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMTAtMjkgYnkgQ2FybA0KMAlzdHJpbmcJY29uZWN0aXgJW2ZpZD0wMDAwMDEwMDEtMEYtMDAwMFZIRDtleHQ9dmhkO21pbWU9O11WaXJ0dWFsIFBDIEhhcmQgZHJpdmUNCg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0xMS0xMiBieSBDYXJsIEVyaWMgQ29kZXJlDQowCXN0cmluZwklIVBTLUFkb2JlLQlbZmlkPTAwMDAwMTAwMy0zMy0wMDAwMFBTO2V4dD1wcyxlcHM7bWltZT1hcHBsaWNhdGlvbi9wb3N0c2NyaXB0O11Qb3N0c2NyaXB0IGZpbGUNCj4xMQlzdHJpbmcJeAksIGxldmVsICUuMXMNCj4xMwlzdHJpbmcJeAkuJS4xcw0KDQojIFN1Ym1pdHRlZCBvbiAy
 MDA0LTExLTEyIGJ5IENhcmwgRXJpYyBDb2RlcmUNCjAJbGVsb25nCTB4NTI2ZjZkMmUJW2ZpZD0wMDAwMDEzMzgtMEYtMDAwMFJPTTtleHQ9cm9tLGZzO21pbWU9O11lQ29zIFJPTSBGaWxlc3lzdGVtIGltYWdlIChST01GUykNCj4xNglzdHJpbmcJeAlbdGl0bGU9JS4xNnNdDQoNCiMgTWFnaWMgSUQgZm9yIFBBREdlbiBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDQtMTEtMjYgYnkgQ2FybCBFcmljIENvZGVyZQ0KMHgyOQlzdHJpbmcJPFhNTF9ESVpfSU5GTz4JW2ZpZD0wMDAwMDEzMzktQTAtMDAwMFhNTDtleHQ9eG1sO21pbWU9dGV4dC94bWw7XVBvcnRhYmxlIGFwcGxpY2F0aW9uIGRlc2NyaXB0aW9uIGZpbGUgKFhNTCkNCiYwCXN0cmluZwk8P3htbAkNCg0KIyBNYWdpYyBJRCBmb3IgUEFER2VuIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0xMS0yNiBieSBDYXJsIEVyaWMgQ29kZXJlDQoweDI4CXN0cmluZwk8WE1MX0RJWl9JTkZPPglbZmlkPTAwMDAwMTMzOS1BMC0wMDAwWE1MO2V4dD14bWw7bWltZT10ZXh0L3htbDtdUG9ydGFibGUgYXBwbGljYXRpb24gZGVzY3JpcHRpb24gZmlsZSAoWE1MKQ0KJjAJc3RyaW5nCTw/eG1sCQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTExLTI2IGJ5IENhcmwgRXJpYyBDb2RlcmUNCjB4MjkJc3RyaW5nCTx4OnhtcG1ldGFcXCAJW2ZpZD0wMDAwMDEwMDMtQTAtMDAwMFhNUDtleHQ9eG1sLHhtcDttaW1lPXRleHQveG1sO11FeHRlbnNpYmxlIG1ldGFkYXRhI
 HBsYXRmb3JtIHNpZGVjYXIgZmlsZSAoWE1MKQ0KJjAJc3RyaW5nCTw/eG1sCQ0KDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTExLTI2IGJ5IENhcmwgRXJpYyBDb2RlcmUNCjB4MjgJc3RyaW5nCTx4OnhtcG1ldGFcXCAJW2ZpZD0wMDAwMDEwMDMtQTAtMDAwMFhNUDtleHQ9eG1sLHhtcDttaW1lPXRleHQveG1sO11FeHRlbnNpYmxlIG1ldGFkYXRhIHBsYXRmb3JtIHNpZGVjYXIgZmlsZSAoWE1MKQ0KJjAJc3RyaW5nCTw/eG1sCQ0KDQojIE1hZ2ljIElEIGZvciBUZXhpbmZvIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0xMS0yNiBieSBDYXJsIEVyaWMgQ29kZXJlDQoxCXN0cmluZwlpbnB1dCB0ZXhpbmZvCVtmaWQ9MDAwMDAxMzQwLTAwLTAwMFRFWEk7ZXh0PXRleGk7bWltZT07XVRleGluZm8gc291cmNlIGZpbGUNCg0KIyBNYWdpYyBJRCBmb3IgTVMtRE9TIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNC0xMS0yNiBieSBDYXJsIEVyaWMgQ29kZXJlDQowCXN0cmluZwlcXHhGRkZPTlQJW2ZpZD0wMDAwMDEwMDEtNzAtMDAwMENQSTtleHQ9Y3BpO21pbWU9O11NUy1ET1MgY29kZSBwYWdlIHJhc3RlciBmb250DQoNCiMgTWFnaWMgSUQgZm9yIFVOSVggZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA0LTExLTI2IGJ5IENhcmwgRXJpYyBDb2RlcmUNCjAJc3RyaW5nCVRaaWYJW2ZpZD0wMDAwMDAwMDAtOTAtMDAwMDAwMDtleHQ9O21pbWU9O11UaW1lem9uZSBpbmZvcm1hdGlvbiBkYXRhYmFzZSBmaWxlIC
 hjb21waWxlZCkNCg0KIyBNYWdpYyBJRCBmb3IgVGVsaXggZmlsZXMuDQojIFN1Ym1pdHRlZCBvbiAyMDA1LTAzLTA0IGJ5IENhcmwgRXJpYyBDb2RlcmUNCjAJbGVsb25nCTB4MmUyYjI5MWEJW2ZpZD0wMDAxMDAxMjYtQjAtMDAwMEZPTjtleHQ9Zm9uO21pbWU9O11UZWxpeCBwaG9uZSBib29rDQomNAlsZXNob3J0CTEJDQoNCiMgTWFnaWMgSUQgZm9yIE1pY3Jvc29mdCBWaXN1YWwgQysrIGZpbGVzLg0KIyBTdWJtaXR0ZWQgb24gMjAwNS0wMy0wNCBieSBDYXJsIEVyaWMgQ29kZXJlDQowCXN0cmluZwlWQ1BDSDBcXHgwMFxceDAwCVtmaWQ9MDAwMDAxMDAxLTkwLTAwMDBQQ0g7ZXh0PXBjaDttaW1lPTtdTWljcm9zb2Z0IFZpc3VhbCBDKysgcHJlLWNvbXBpbGVkIGhlYWRlcihzKQ0KDQojIE1hZ2ljIElEIGZvciBNaWNyb3NvZnQgVmlzdWFsIEMrKyBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDUtMDMtMDQgYnkgQ2FybCBFcmljIENvZGVyZQ0KMAlzdHJpbmcJTWljcm9zb2Z0XFwgQy9DKytcXCBwcm9ncmFtXFwgZGF0YWJhc2UJW2ZpZD0wMDAwMDEwMDEtOTAtMDAwMFBEQjtleHQ9cGRiO21pbWU9O11NaWNyb3NvZnQgVmlzdWFsIEMrKyBkZWJ1ZyBpbmZvcm1hdGlvbiBkYXRhYmFzZQ0KJjB4MjUJc3RyaW5nCVxceDBEXFx4MEFcXHgxQQkNCiYweDI4CXN0cmluZwlKR1xceDAwCQ0KDQojIE1hZ2ljIElEIGZvciBNaWNyb3NvZnQgVmlzdWFsIEMrKyBmaWxlcy4NCiMgU3VibWl0dGVkIG9uIDIwMDUtMDM
 tMDQgYnkgQ2FybCBFcmljIENvZGVyZQ0KMAlzdHJpbmcJTWljcm9zb2Z0XFwgTGlua2VyXFwgRGF0YWJhc2VcXHgwQVxceDA3XFx4MUEJW2ZpZD0wMDAwMDEwMDEtOTAtMDAwMElMSztleHQ9aWxrO21pbWU9O11NaWNyb3NvZnQgVmlzdWFsIEMrKyBsaW5rZXIgZGF0YWJhc2UNCg0KDQojIENSQzMyOjhCQzNCQTQ2DQo='
+	);
+/**
+ * Returns the test data for a given key
+ *
+ * @param string $key
+ * @access public
+ */
+	function get($key) {
+/**
+ * data property
+ *
+ * @var array
+ * @access public
+ */
+		static $data = array();
+
+		if (empty($data)) {
+			$vars = get_class_vars(__CLASS__);
+			foreach ($vars['data'] as $k => $v) {
+				$data[$k] = base64_decode($v);
+				if (preg_match('/^[ais]:[0-9]+/', $data[$k])) {
+					$data[$k] = unserialize($data[$k]);
+				}
+			}
+		}
+
+		if (!isset($data[$key])) {
+			return false;
+		}
+		return $data[$key];
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/model/behaviors/acl.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/model/behaviors/acl.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/model/behaviors/acl.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,462 @@
+<?php
+/**
+ * AclBehaviorTest file
+ *
+ * Test the Acl Behavior
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://cakephp.org CakePHP Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.tests.model.behaviors.acl
+ * @since         CakePHP v 1.2.0.4487
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Behavior', 'Acl');
+App::import('Core', 'db_acl');
+
+/**
+* Test Person class - self joined model
+*
+* @package       cake
+* @subpackage    cake.tests.cases.libs.model.behaviors
+*/
+class AclPerson extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string
+ * @access public
+ */
+	var $name = 'AclPerson';
+
+/**
+ * useTable property
+ *
+ * @var string
+ * @access public
+ */
+	var $useTable = 'people';
+
+/**
+ * actsAs property
+ *
+ * @var array
+ * @access public
+ */
+	var $actsAs = array('Acl' => 'requester');
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array(
+		'Mother' => array(
+			'className' => 'AclPerson',
+			'foreignKey' => 'mother_id',
+		)
+	);
+
+/**
+ * hasMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasMany = array(
+		'Child' => array(
+			'className' => 'AclPerson',
+			'foreignKey' => 'mother_id'
+		)
+	);
+
+/**
+ * parentNode method
+ *
+ * @return void
+ * @access public
+ */
+	function parentNode() {
+		if (!$this->id && empty($this->data)) {
+			return null;
+		}
+		if (isset($this->data['AclPerson']['mother_id'])) {
+			$motherId = $this->data['AclPerson']['mother_id'];
+		} else {
+			$motherId = $this->field('mother_id');
+		}
+		if (!$motherId) {
+			return null;
+		} else {
+			return array('AclPerson' => array('id' => $motherId));
+		}
+	}
+}
+
+/**
+* AclUser class
+*
+* @package       cake
+* @subpackage    cake.tests.cases.libs.model.behaviors
+*/
+class AclUser extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string
+ * @access public
+ */
+	var $name = 'User';
+
+/**
+ * useTable property
+ *
+ * @var string
+ * @access public
+ */
+	var $useTable = 'users';
+
+/**
+ * actsAs property
+ *
+ * @var array
+ * @access public
+ */
+	var $actsAs = array('Acl');
+
+/**
+ * parentNode
+ *
+ * @access public
+ */
+	function parentNode() {
+		return null;
+	}
+}
+
+/**
+* AclPost class
+*
+* @package       cake
+* @subpackage    cake.tests.cases.libs.model.behaviors
+*/
+class AclPost extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string
+ * @access public
+ */
+	var $name = 'Post';
+
+/**
+ * useTable property
+ *
+ * @var string
+ * @access public
+ */
+	var $useTable = 'posts';
+
+/**
+ * actsAs property
+ *
+ * @var array
+ * @access public
+ */
+	var $actsAs = array('Acl' => 'Controlled');
+
+/**
+ * parentNode
+ *
+ * @access public
+ */
+	function parentNode() {
+		return null;
+	}
+}
+
+/**
+* AclBehaviorTest class
+*
+* @package       cake
+* @subpackage    cake.tests.cases.libs.controller.components
+*/
+class AclBehaviorTestCase extends CakeTestCase {
+
+/**
+ * Aco property
+ *
+ * @var Aco
+ * @access public
+ */
+	var $Aco;
+
+/**
+ * Aro property
+ *
+ * @var Aro
+ * @access public
+ */
+	var $Aro;
+
+/**
+ * fixtures property
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array('core.person', 'core.user', 'core.post', 'core.aco', 'core.aro', 'core.aros_aco');
+
+/**
+ * Set up the test
+ *
+ * @return void
+ * @access public
+ */
+	function startTest() {
+		Configure::write('Acl.database', 'test_suite');
+
+		$this->Aco =& new Aco();
+		$this->Aro =& new Aro();
+	}
+
+/**
+ * tearDown method
+ *
+ * @return void
+ * @access public
+ */
+	function tearDown() {
+		ClassRegistry::flush();
+		unset($this->Aro, $this->Aco);
+	}
+
+/**
+ * Test Setup of AclBehavior
+ *
+ * @return void
+ * @access public
+ */
+	function testSetup() {
+		$User =& new AclUser();
+		$this->assertTrue(isset($User->Behaviors->Acl->settings['User']));
+		$this->assertEqual($User->Behaviors->Acl->settings['User']['type'], 'requester');
+		$this->assertTrue(is_object($User->Aro));
+
+		$Post =& new AclPost();
+		$this->assertTrue(isset($Post->Behaviors->Acl->settings['Post']));
+		$this->assertEqual($Post->Behaviors->Acl->settings['Post']['type'], 'controlled');
+		$this->assertTrue(is_object($Post->Aco));
+	}
+
+/**
+ * test After Save
+ *
+ * @return void
+ * @access public
+ */
+	function testAfterSave() {
+		$Post =& new AclPost();
+		$data = array(
+			'Post' => array(
+				'author_id' => 1,
+				'title' => 'Acl Post',
+				'body' => 'post body',
+				'published' => 1
+			),
+		);
+		$Post->save($data);
+		$result = $this->Aco->find('first', array(
+			'conditions' => array('Aco.model' => 'Post', 'Aco.foreign_key' => $Post->id)
+		));
+		$this->assertTrue(is_array($result));
+		$this->assertEqual($result['Aco']['model'], 'Post');
+		$this->assertEqual($result['Aco']['foreign_key'], $Post->id);
+
+		$aroData = array(
+			'Aro' => array(
+				'model' => 'AclPerson',
+				'foreign_key' => 2,
+				'parent_id' => null
+			)
+		);
+		$this->Aro->save($aroData);
+
+		$Person =& new AclPerson();
+		$data = array(
+			'AclPerson' => array(
+				'name' => 'Trent',
+				'mother_id' => 2,
+				'father_id' => 3,
+			),
+		);
+		$Person->save($data);
+		$result = $this->Aro->find('first', array(
+			'conditions' => array('Aro.model' => 'AclPerson', 'Aro.foreign_key' => $Person->id)
+		));
+		$this->assertTrue(is_array($result));
+		$this->assertEqual($result['Aro']['parent_id'], 5);
+
+		$node = $Person->node(array('model' => 'AclPerson', 'foreign_key' => 8));
+		$this->assertEqual(count($node), 2);
+		$this->assertEqual($node[0]['Aro']['parent_id'], 5);
+		$this->assertEqual($node[1]['Aro']['parent_id'], null);
+
+		$aroData = array(
+			'Aro' => array(
+			'model' => 'AclPerson',
+				'foreign_key' => 1,
+				'parent_id' => null
+			)
+		);
+		$this->Aro->create();
+		$this->Aro->save($aroData);
+ 
+		$Person->read(null, 8);
+		$Person->set('mother_id', 1);
+		$Person->save();
+		$result = $this->Aro->find('first', array(
+			'conditions' => array('Aro.model' => 'AclPerson', 'Aro.foreign_key' => $Person->id)
+		));
+		 $this->assertTrue(is_array($result));
+		 $this->assertEqual($result['Aro']['parent_id'], 7);
+ 
+		$node = $Person->node(array('model' => 'AclPerson', 'foreign_key' => 8));
+		$this->assertEqual(sizeof($node), 2);
+		$this->assertEqual($node[0]['Aro']['parent_id'], 7);
+		$this->assertEqual($node[1]['Aro']['parent_id'], null);
+	}
+
+/**
+ * test that an afterSave on an update does not cause parent_id to become null.
+ *
+ * @return void
+ */
+	function testAfterSaveUpdateParentIdNotNull() {
+		$aroData = array(
+			'Aro' => array(
+				'model' => 'AclPerson',
+				'foreign_key' => 2,
+				'parent_id' => null
+			)
+		);
+		$this->Aro->save($aroData);
+
+		$Person =& new AclPerson();
+		$data = array(
+			'AclPerson' => array(
+				'name' => 'Trent',
+				'mother_id' => 2,
+				'father_id' => 3,
+			),
+		);
+		$Person->save($data);
+		$result = $this->Aro->find('first', array(
+			'conditions' => array('Aro.model' => 'AclPerson', 'Aro.foreign_key' => $Person->id)
+		));
+		$this->assertTrue(is_array($result));
+		$this->assertEqual($result['Aro']['parent_id'], 5);
+
+		$Person->save(array('id' => $Person->id, 'name' => 'Bruce'));
+		$result = $this->Aro->find('first', array(
+			'conditions' => array('Aro.model' => 'AclPerson', 'Aro.foreign_key' => $Person->id)
+		));
+		$this->assertEqual($result['Aro']['parent_id'], 5);
+	}
+
+/**
+ * Test After Delete
+ *
+ * @return void
+ * @access public
+ */
+	function testAfterDelete() {
+		$aroData = array(
+			'Aro' => array(
+				'model' => 'AclPerson',
+				'foreign_key' => 2,
+				'parent_id' => null
+			)
+		);
+		$this->Aro->save($aroData);
+		$Person =& new AclPerson();
+		$data = array(
+			'AclPerson' => array(
+				'name' => 'Trent',
+				'mother_id' => 2,
+				'father_id' => 3,
+			),
+		);
+		$Person->save($data);
+		$id = $Person->id;
+		$node = $Person->node();
+		$this->assertEqual(count($node), 2);
+		$this->assertEqual($node[0]['Aro']['parent_id'], 5);
+		$this->assertEqual($node[1]['Aro']['parent_id'], null);
+
+		$Person->delete($id);
+		$result = $this->Aro->find('first', array(
+			'conditions' => array('Aro.model' => 'AclPerson', 'Aro.foreign_key' => $id)
+		));
+		$this->assertTrue(empty($result));
+		$result = $this->Aro->find('first', array(
+			'conditions' => array('Aro.model' => 'AclPerson', 'Aro.foreign_key' => 2)
+		));
+		$this->assertFalse(empty($result));
+
+		$data = array(
+			'AclPerson' => array(
+				'name' => 'Trent',
+				'mother_id' => 2,
+				'father_id' => 3,
+			),
+		);
+		$Person->save($data);
+		$id = $Person->id;
+		$Person->delete(2);
+		$result = $this->Aro->find('first', array(
+			'conditions' => array('Aro.model' => 'AclPerson', 'Aro.foreign_key' => $id)
+		));
+		$this->assertTrue(empty($result));
+
+		$result = $this->Aro->find('first', array(
+			'conditions' => array('Aro.model' => 'AclPerson', 'Aro.foreign_key' => 2)
+		));
+		$this->assertTrue(empty($result));
+	}
+
+/**
+ * Test Node()
+ *
+ * @return void
+ * @access public
+ */
+	function testNode() {
+		$Person =& new AclPerson();
+		$aroData = array(
+			'Aro' => array(
+				'model' => 'AclPerson',
+				'foreign_key' => 2,
+				'parent_id' => null
+			)
+		);
+		$this->Aro->save($aroData);
+
+		$Person->id = 2;
+		$result = $Person->node();
+		$this->assertTrue(is_array($result));
+		$this->assertEqual(count($result), 1);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/model/behaviors/containable.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/model/behaviors/containable.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/model/behaviors/containable.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,3781 @@
+<?php
+/**
+ * ContainableBehaviorTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.behaviors
+ * @since         CakePHP(tm) v 1.2.0.5669
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', array('AppModel', 'Model'));
+require_once(dirname(dirname(__FILE__)) . DS . 'models.php');
+
+/**
+ * ContainableTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.behaviors
+ */
+class ContainableBehaviorTest extends CakeTestCase {
+
+/**
+ * Fixtures associated with this test case
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array(
+		'core.article', 'core.article_featured', 'core.article_featureds_tags', 'core.articles_tag', 'core.attachment', 'core.category',
+		'core.comment', 'core.featured', 'core.tag', 'core.user', 'core.join_a', 'core.join_b', 'core.join_c', 'core.join_a_c', 'core.join_a_b'
+	);
+
+/**
+ * Method executed before each test
+ *
+ * @access public
+ */
+	function startTest() {
+		$this->User =& ClassRegistry::init('User');
+		$this->Article =& ClassRegistry::init('Article');
+		$this->Tag =& ClassRegistry::init('Tag');
+
+		$this->User->bindModel(array(
+			'hasMany' => array('Article', 'ArticleFeatured', 'Comment')
+		), false);
+		$this->User->ArticleFeatured->unbindModel(array('belongsTo' => array('Category')), false);
+		$this->User->ArticleFeatured->hasMany['Comment']['foreignKey'] = 'article_id';
+
+		$this->Tag->bindModel(array(
+			'hasAndBelongsToMany' => array('Article')
+		), false);
+
+		$this->User->Behaviors->attach('Containable');
+		$this->Article->Behaviors->attach('Containable');
+		$this->Tag->Behaviors->attach('Containable');
+	}
+
+/**
+ * Method executed after each test
+ *
+ * @access public
+ */
+	function endTest() {
+		unset($this->Article);
+		unset($this->User);
+		unset($this->Tag);
+
+		ClassRegistry::flush();
+	}
+
+/**
+ * testContainments method
+ *
+ * @access public
+ * @return void
+ */
+	function testContainments() {
+		$r = $this->__containments($this->Article, array('Comment' => array('conditions' => array('Comment.user_id' => 2))));
+		$this->assertTrue(Set::matches('/Article/keep/Comment/conditions[Comment.user_id=2]', $r));
+
+		$r = $this->__containments($this->User, array(
+			'ArticleFeatured' => array(
+				'Featured' => array(
+					'id',
+					'Category' => 'name'
+				)
+		)));
+		$this->assertEqual(Set::extract('/ArticleFeatured/keep/Featured/fields', $r), array('id'));
+
+		$r = $this->__containments($this->Article, array(
+			'Comment' => array(
+				'User',
+				'conditions' => array('Comment' => array('user_id' => 2)),
+			),
+		));
+		$this->assertTrue(Set::matches('/User', $r));
+		$this->assertTrue(Set::matches('/Comment', $r));
+		$this->assertTrue(Set::matches('/Article/keep/Comment/conditions/Comment[user_id=2]', $r));
+
+		$r = $this->__containments($this->Article, array('Comment(comment, published)' => 'Attachment(attachment)', 'User(user)'));
+		$this->assertTrue(Set::matches('/Comment', $r));
+		$this->assertTrue(Set::matches('/User', $r));
+		$this->assertTrue(Set::matches('/Article/keep/Comment', $r));
+		$this->assertTrue(Set::matches('/Article/keep/User', $r));
+		$this->assertEqual(Set::extract('/Article/keep/Comment/fields', $r), array('comment', 'published'));
+		$this->assertEqual(Set::extract('/Article/keep/User/fields', $r), array('user'));
+		$this->assertTrue(Set::matches('/Comment/keep/Attachment', $r));
+		$this->assertEqual(Set::extract('/Comment/keep/Attachment/fields', $r), array('attachment'));
+
+		$r = $this->__containments($this->Article, array('Comment' => array('limit' => 1)));
+		$this->assertEqual(array_keys($r), array('Comment', 'Article'));
+		$this->assertEqual(array_shift(Set::extract('/Comment/keep', $r)), array('keep' => array()));
+		$this->assertTrue(Set::matches('/Article/keep/Comment', $r));
+		$this->assertEqual(array_shift(Set::extract('/Article/keep/Comment/.', $r)), array('limit' => 1));
+
+		$r = $this->__containments($this->Article, array('Comment.User'));
+		$this->assertEqual(array_keys($r), array('User', 'Comment', 'Article'));
+		$this->assertEqual(array_shift(Set::extract('/User/keep', $r)), array('keep' => array()));
+		$this->assertEqual(array_shift(Set::extract('/Comment/keep', $r)), array('keep' => array('User' => array())));
+		$this->assertEqual(array_shift(Set::extract('/Article/keep', $r)), array('keep' => array('Comment' => array())));
+
+		$r = $this->__containments($this->Tag, array('Article' => array('User' => array('Comment' => array(
+			'Attachment' => array('conditions' => array('Attachment.id >' => 1))
+		)))));
+		$this->assertTrue(Set::matches('/Attachment', $r));
+		$this->assertTrue(Set::matches('/Comment/keep/Attachment/conditions', $r));
+		$this->assertEqual($r['Comment']['keep']['Attachment']['conditions'], array('Attachment.id >' => 1));
+		$this->assertTrue(Set::matches('/User/keep/Comment', $r));
+		$this->assertTrue(Set::matches('/Article/keep/User', $r));
+		$this->assertTrue(Set::matches('/Tag/keep/Article', $r));
+	}
+
+/**
+ * testInvalidContainments method
+ *
+ * @access public
+ * @return void
+ */
+	function testInvalidContainments() {
+		$this->expectError();
+		$r = $this->__containments($this->Article, array('Comment', 'InvalidBinding'));
+
+		$this->Article->Behaviors->attach('Containable', array('notices' => false));
+		$r = $this->__containments($this->Article, array('Comment', 'InvalidBinding'));
+	}
+
+/**
+ * testBeforeFind method
+ *
+ * @access public
+ * @return void
+ */
+	function testBeforeFind() {
+		$r = $this->Article->find('all', array('contain' => array('Comment')));
+		$this->assertFalse(Set::matches('/User', $r));
+		$this->assertTrue(Set::matches('/Comment', $r));
+		$this->assertFalse(Set::matches('/Comment/User', $r));
+
+		$r = $this->Article->find('all', array('contain' => 'Comment.User'));
+		$this->assertTrue(Set::matches('/Comment/User', $r));
+		$this->assertFalse(Set::matches('/Comment/Article', $r));
+
+		$r = $this->Article->find('all', array('contain' => array('Comment' => array('User', 'Article'))));
+		$this->assertTrue(Set::matches('/Comment/User', $r));
+		$this->assertTrue(Set::matches('/Comment/Article', $r));
+
+		$r = $this->Article->find('all', array('contain' => array('Comment' => array('conditions' => array('Comment.user_id' => 2)))));
+		$this->assertFalse(Set::matches('/Comment[user_id!=2]', $r));
+		$this->assertTrue(Set::matches('/Comment[user_id=2]', $r));
+
+		$r = $this->Article->find('all', array('contain' => array('Comment.user_id = 2')));
+		$this->assertFalse(Set::matches('/Comment[user_id!=2]', $r));
+
+		$r = $this->Article->find('all', array('contain' => 'Comment.id DESC'));
+		$ids = $descIds = Set::extract('/Comment[1]/id', $r);
+		rsort($descIds);
+		$this->assertEqual($ids, $descIds);
+
+		$r = $this->Article->find('all', array('contain' => 'Comment'));
+		$this->assertTrue(Set::matches('/Comment[user_id!=2]', $r));
+
+		$r = $this->Article->find('all', array('contain' => array('Comment' => array('fields' => 'comment'))));
+		$this->assertFalse(Set::matches('/Comment/created', $r));
+		$this->assertTrue(Set::matches('/Comment/comment', $r));
+		$this->assertFalse(Set::matches('/Comment/updated', $r));
+
+		$r = $this->Article->find('all', array('contain' => array('Comment' => array('fields' => array('comment', 'updated')))));
+		$this->assertFalse(Set::matches('/Comment/created', $r));
+		$this->assertTrue(Set::matches('/Comment/comment', $r));
+		$this->assertTrue(Set::matches('/Comment/updated', $r));
+
+		$r = $this->Article->find('all', array('contain' => array('Comment' => array('comment', 'updated'))));
+		$this->assertFalse(Set::matches('/Comment/created', $r));
+		$this->assertTrue(Set::matches('/Comment/comment', $r));
+		$this->assertTrue(Set::matches('/Comment/updated', $r));
+
+		$r = $this->Article->find('all', array('contain' => array('Comment(comment,updated)')));
+		$this->assertFalse(Set::matches('/Comment/created', $r));
+		$this->assertTrue(Set::matches('/Comment/comment', $r));
+		$this->assertTrue(Set::matches('/Comment/updated', $r));
+
+		$r = $this->Article->find('all', array('contain' => 'Comment.created'));
+		$this->assertTrue(Set::matches('/Comment/created', $r));
+		$this->assertFalse(Set::matches('/Comment/comment', $r));
+
+		$r = $this->Article->find('all', array('contain' => array('User.Article(title)', 'Comment(comment)')));
+		$this->assertFalse(Set::matches('/Comment/Article', $r));
+		$this->assertFalse(Set::matches('/Comment/User', $r));
+		$this->assertTrue(Set::matches('/Comment/comment', $r));
+		$this->assertFalse(Set::matches('/Comment/created', $r));
+		$this->assertTrue(Set::matches('/User/Article/title', $r));
+		$this->assertFalse(Set::matches('/User/Article/created', $r));
+
+		$r = $this->Article->find('all', array('contain' => array()));
+		$this->assertFalse(Set::matches('/User', $r));
+		$this->assertFalse(Set::matches('/Comment', $r));
+
+		$this->expectError();
+		$r = $this->Article->find('all', array('contain' => array('Comment' => 'NonExistingBinding')));
+	}
+
+/**
+ * testContain method
+ *
+ * @access public
+ * @return void
+ */
+	function testContain() {
+		$this->Article->contain('Comment.User');
+		$r = $this->Article->find('all');
+		$this->assertTrue(Set::matches('/Comment/User', $r));
+		$this->assertFalse(Set::matches('/Comment/Article', $r));
+
+		$r = $this->Article->find('all');
+		$this->assertFalse(Set::matches('/Comment/User', $r));
+	}
+
+/**
+ * testFindEmbeddedNoBindings method
+ *
+ * @access public
+ * @return void
+ */
+	function testFindEmbeddedNoBindings() {
+		$result = $this->Article->find('all', array('contain' => false));
+		$expected = array(
+			array('Article' => array(
+				'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+				'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+			)),
+			array('Article' => array(
+				'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+				'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+			)),
+			array('Article' => array(
+				'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+				'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+			))
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testFindFirstLevel method
+ *
+ * @access public
+ * @return void
+ */
+	function testFindFirstLevel() {
+		$this->Article->contain('User');
+		$result = $this->Article->find('all', array('recursive' => 1));
+		$expected = array(
+			array(
+				'Article' => array(
+					'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+				),
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				)
+			),
+			array(
+				'Article' => array(
+					'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+				),
+				'User' => array(
+					'id' => 3, 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31'
+				)
+			),
+			array(
+				'Article' => array(
+					'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+				),
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				)
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$this->Article->contain('User', 'Comment');
+		$result = $this->Article->find('all', array('recursive' => 1));
+		$expected = array(
+			array(
+				'Article' => array(
+					'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+				),
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				),
+				'Comment' => array(
+					array(
+						'id' => 1, 'article_id' => 1, 'user_id' => 2, 'comment' => 'First Comment for First Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:45:23', 'updated' => '2007-03-18 10:47:31'
+					),
+					array(
+						'id' => 2, 'article_id' => 1, 'user_id' => 4, 'comment' => 'Second Comment for First Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:47:23', 'updated' => '2007-03-18 10:49:31'
+					),
+					array(
+						'id' => 3, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Third Comment for First Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:49:23', 'updated' => '2007-03-18 10:51:31'
+					),
+					array(
+						'id' => 4, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Fourth Comment for First Article',
+						'published' => 'N', 'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31'
+					)
+				)
+			),
+			array(
+				'Article' => array(
+					'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+				),
+				'User' => array(
+					'id' => 3, 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31'
+				),
+				'Comment' => array(
+					array(
+						'id' => 5, 'article_id' => 2, 'user_id' => 1, 'comment' => 'First Comment for Second Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:53:23', 'updated' => '2007-03-18 10:55:31'
+					),
+					array(
+						'id' => 6, 'article_id' => 2, 'user_id' => 2, 'comment' => 'Second Comment for Second Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:55:23', 'updated' => '2007-03-18 10:57:31'
+					)
+				)
+			),
+			array(
+				'Article' => array(
+					'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+				),
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				),
+				'Comment' => array()
+			)
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testFindEmbeddedFirstLevel method
+ *
+ * @access public
+ * @return void
+ */
+	function testFindEmbeddedFirstLevel() {
+		$result = $this->Article->find('all', array('contain' => array('User')));
+		$expected = array(
+			array(
+				'Article' => array(
+					'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+				),
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				)
+			),
+			array(
+				'Article' => array(
+					'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+				),
+				'User' => array(
+					'id' => 3, 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31'
+				)
+			),
+			array(
+				'Article' => array(
+					'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+				),
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				)
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Article->find('all', array('contain' => array('User', 'Comment')));
+		$expected = array(
+			array(
+				'Article' => array(
+					'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+				),
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				),
+				'Comment' => array(
+					array(
+						'id' => 1, 'article_id' => 1, 'user_id' => 2, 'comment' => 'First Comment for First Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:45:23', 'updated' => '2007-03-18 10:47:31'
+					),
+					array(
+						'id' => 2, 'article_id' => 1, 'user_id' => 4, 'comment' => 'Second Comment for First Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:47:23', 'updated' => '2007-03-18 10:49:31'
+					),
+					array(
+						'id' => 3, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Third Comment for First Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:49:23', 'updated' => '2007-03-18 10:51:31'
+					),
+					array(
+						'id' => 4, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Fourth Comment for First Article',
+						'published' => 'N', 'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31'
+					)
+				)
+			),
+			array(
+				'Article' => array(
+					'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+				),
+				'User' => array(
+					'id' => 3, 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31'
+				),
+				'Comment' => array(
+					array(
+						'id' => 5, 'article_id' => 2, 'user_id' => 1, 'comment' => 'First Comment for Second Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:53:23', 'updated' => '2007-03-18 10:55:31'
+					),
+					array(
+						'id' => 6, 'article_id' => 2, 'user_id' => 2, 'comment' => 'Second Comment for Second Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:55:23', 'updated' => '2007-03-18 10:57:31'
+					)
+				)
+			),
+			array(
+				'Article' => array(
+					'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+				),
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				),
+				'Comment' => array()
+			)
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testFindSecondLevel method
+ *
+ * @access public
+ * @return void
+ */
+	function testFindSecondLevel() {
+		$this->Article->contain(array('Comment' => 'User'));
+		$result = $this->Article->find('all', array('recursive' => 2));
+		$expected = array(
+			array(
+				'Article' => array(
+					'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+				),
+				'Comment' => array(
+					array(
+						'id' => 1, 'article_id' => 1, 'user_id' => 2, 'comment' => 'First Comment for First Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:45:23', 'updated' => '2007-03-18 10:47:31',
+						'User' => array(
+							'id' => 2, 'user' => 'nate', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+							'created' => '2007-03-17 01:18:23', 'updated' => '2007-03-17 01:20:31'
+						)
+					),
+					array(
+						'id' => 2, 'article_id' => 1, 'user_id' => 4, 'comment' => 'Second Comment for First Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:47:23', 'updated' => '2007-03-18 10:49:31',
+						'User' => array(
+							'id' => 4, 'user' => 'garrett', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+							'created' => '2007-03-17 01:22:23', 'updated' => '2007-03-17 01:24:31'
+						)
+					),
+					array(
+						'id' => 3, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Third Comment for First Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:49:23', 'updated' => '2007-03-18 10:51:31',
+						'User' => array(
+							'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+							'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+						)
+					),
+					array(
+						'id' => 4, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Fourth Comment for First Article',
+						'published' => 'N', 'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31',
+						'User' => array(
+							'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+							'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+						)
+					)
+				)
+			),
+			array(
+				'Article' => array(
+					'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+				),
+				'Comment' => array(
+					array(
+						'id' => 5, 'article_id' => 2, 'user_id' => 1, 'comment' => 'First Comment for Second Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:53:23', 'updated' => '2007-03-18 10:55:31',
+						'User' => array(
+							'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+							'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+						)
+					),
+					array(
+						'id' => 6, 'article_id' => 2, 'user_id' => 2, 'comment' => 'Second Comment for Second Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:55:23', 'updated' => '2007-03-18 10:57:31',
+						'User' => array(
+							'id' => 2, 'user' => 'nate', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+							'created' => '2007-03-17 01:18:23', 'updated' => '2007-03-17 01:20:31'
+						)
+					)
+				)
+			),
+			array(
+				'Article' => array(
+					'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+				),
+				'Comment' => array()
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$this->Article->contain(array('User' => 'ArticleFeatured'));
+		$result = $this->Article->find('all', array('recursive' => 2));
+		$expected = array(
+			array(
+				'Article' => array(
+					'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+				),
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31',
+					'ArticleFeatured' => array(
+						array(
+							'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+							'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+						),
+						array(
+							'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+							'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+						)
+					)
+				)
+			),
+			array(
+				'Article' => array(
+					'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+				),
+				'User' => array(
+					'id' => 3, 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31',
+					'ArticleFeatured' => array(
+						array(
+						'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+						)
+					)
+				)
+			),
+			array(
+				'Article' => array(
+					'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+				),
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31',
+					'ArticleFeatured' => array(
+						array(
+							'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+							'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+						),
+						array(
+							'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+							'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+						)
+					)
+				)
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$this->Article->contain(array('User' => array('ArticleFeatured', 'Comment')));
+		$result = $this->Article->find('all', array('recursive' => 2));
+		$expected = array(
+			array(
+				'Article' => array(
+					'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+				),
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31',
+					'ArticleFeatured' => array(
+						array(
+							'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+							'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+						),
+						array(
+							'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+							'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+						)
+					),
+					'Comment' => array(
+						array(
+							'id' => 3, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Third Comment for First Article',
+							'published' => 'Y', 'created' => '2007-03-18 10:49:23', 'updated' => '2007-03-18 10:51:31'
+						),
+						array(
+							'id' => 4, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Fourth Comment for First Article',
+							'published' => 'N', 'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31'
+						),
+						array(
+							'id' => 5, 'article_id' => 2, 'user_id' => 1, 'comment' => 'First Comment for Second Article',
+							'published' => 'Y', 'created' => '2007-03-18 10:53:23', 'updated' => '2007-03-18 10:55:31'
+						)
+					)
+				)
+			),
+			array(
+				'Article' => array(
+					'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+				),
+				'User' => array(
+					'id' => 3, 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31',
+					'ArticleFeatured' => array(
+						array(
+						'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+						)
+					),
+					'Comment' => array()
+				)
+			),
+			array(
+				'Article' => array(
+					'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+				),
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31',
+					'ArticleFeatured' => array(
+						array(
+							'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+							'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+						),
+						array(
+							'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+							'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+						)
+					),
+					'Comment' => array(
+						array(
+							'id' => 3, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Third Comment for First Article',
+							'published' => 'Y', 'created' => '2007-03-18 10:49:23', 'updated' => '2007-03-18 10:51:31'
+						),
+						array(
+							'id' => 4, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Fourth Comment for First Article',
+							'published' => 'N', 'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31'
+						),
+						array(
+							'id' => 5, 'article_id' => 2, 'user_id' => 1, 'comment' => 'First Comment for Second Article',
+							'published' => 'Y', 'created' => '2007-03-18 10:53:23', 'updated' => '2007-03-18 10:55:31'
+						)
+					)
+				)
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$this->Article->contain(array('User' => array('ArticleFeatured')), 'Tag', array('Comment' => 'Attachment'));
+		$result = $this->Article->find('all', array('recursive' => 2));
+		$expected = array(
+			array(
+				'Article' => array(
+					'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+				),
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31',
+					'ArticleFeatured' => array(
+						array(
+							'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+							'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+						),
+						array(
+							'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+							'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+						)
+					)
+				),
+				'Comment' => array(
+					array(
+						'id' => 1, 'article_id' => 1, 'user_id' => 2, 'comment' => 'First Comment for First Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:45:23', 'updated' => '2007-03-18 10:47:31',
+						'Attachment' => array()
+					),
+					array(
+						'id' => 2, 'article_id' => 1, 'user_id' => 4, 'comment' => 'Second Comment for First Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:47:23', 'updated' => '2007-03-18 10:49:31',
+						'Attachment' => array()
+					),
+					array(
+						'id' => 3, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Third Comment for First Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:49:23', 'updated' => '2007-03-18 10:51:31',
+						'Attachment' => array()
+					),
+					array(
+						'id' => 4, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Fourth Comment for First Article',
+						'published' => 'N', 'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31',
+						'Attachment' => array()
+					)
+				),
+				'Tag' => array(
+					array('id' => 1, 'tag' => 'tag1', 'created' => '2007-03-18 12:22:23', 'updated' => '2007-03-18 12:24:31'),
+					array('id' => 2, 'tag' => 'tag2', 'created' => '2007-03-18 12:24:23', 'updated' => '2007-03-18 12:26:31')
+				)
+			),
+			array(
+				'Article' => array(
+					'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+				),
+				'User' => array(
+					'id' => 3, 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31',
+					'ArticleFeatured' => array(
+						array(
+						'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+						)
+					)
+				),
+				'Comment' => array(
+					array(
+						'id' => 5, 'article_id' => 2, 'user_id' => 1, 'comment' => 'First Comment for Second Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:53:23', 'updated' => '2007-03-18 10:55:31',
+						'Attachment' => array(
+							'id' => 1, 'comment_id' => 5, 'attachment' => 'attachment.zip',
+							'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31'
+						)
+					),
+					array(
+						'id' => 6, 'article_id' => 2, 'user_id' => 2, 'comment' => 'Second Comment for Second Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:55:23', 'updated' => '2007-03-18 10:57:31',
+						'Attachment' => array()
+					)
+				),
+				'Tag' => array(
+					array('id' => 1, 'tag' => 'tag1', 'created' => '2007-03-18 12:22:23', 'updated' => '2007-03-18 12:24:31'),
+					array('id' => 3, 'tag' => 'tag3', 'created' => '2007-03-18 12:26:23', 'updated' => '2007-03-18 12:28:31')
+				)
+			),
+			array(
+				'Article' => array(
+					'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+				),
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31',
+					'ArticleFeatured' => array(
+						array(
+							'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+							'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+						),
+						array(
+							'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+							'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+						)
+					)
+				),
+				'Comment' => array(),
+				'Tag' => array()
+			)
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testFindEmbeddedSecondLevel method
+ *
+ * @access public
+ * @return void
+ */
+	function testFindEmbeddedSecondLevel() {
+		$result = $this->Article->find('all', array('contain' => array('Comment' => 'User')));
+		$expected = array(
+			array(
+				'Article' => array(
+					'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+				),
+				'Comment' => array(
+					array(
+						'id' => 1, 'article_id' => 1, 'user_id' => 2, 'comment' => 'First Comment for First Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:45:23', 'updated' => '2007-03-18 10:47:31',
+						'User' => array(
+							'id' => 2, 'user' => 'nate', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+							'created' => '2007-03-17 01:18:23', 'updated' => '2007-03-17 01:20:31'
+						)
+					),
+					array(
+						'id' => 2, 'article_id' => 1, 'user_id' => 4, 'comment' => 'Second Comment for First Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:47:23', 'updated' => '2007-03-18 10:49:31',
+						'User' => array(
+							'id' => 4, 'user' => 'garrett', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+							'created' => '2007-03-17 01:22:23', 'updated' => '2007-03-17 01:24:31'
+						)
+					),
+					array(
+						'id' => 3, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Third Comment for First Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:49:23', 'updated' => '2007-03-18 10:51:31',
+						'User' => array(
+							'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+							'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+						)
+					),
+					array(
+						'id' => 4, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Fourth Comment for First Article',
+						'published' => 'N', 'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31',
+						'User' => array(
+							'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+							'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+						)
+					)
+				)
+			),
+			array(
+				'Article' => array(
+					'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+				),
+				'Comment' => array(
+					array(
+						'id' => 5, 'article_id' => 2, 'user_id' => 1, 'comment' => 'First Comment for Second Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:53:23', 'updated' => '2007-03-18 10:55:31',
+						'User' => array(
+							'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+							'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+						)
+					),
+					array(
+						'id' => 6, 'article_id' => 2, 'user_id' => 2, 'comment' => 'Second Comment for Second Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:55:23', 'updated' => '2007-03-18 10:57:31',
+						'User' => array(
+							'id' => 2, 'user' => 'nate', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+							'created' => '2007-03-17 01:18:23', 'updated' => '2007-03-17 01:20:31'
+						)
+					)
+				)
+			),
+			array(
+				'Article' => array(
+					'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+				),
+				'Comment' => array()
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Article->find('all', array('contain' => array('User' => 'ArticleFeatured')));
+		$expected = array(
+			array(
+				'Article' => array(
+					'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+				),
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31',
+					'ArticleFeatured' => array(
+						array(
+							'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+							'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+						),
+						array(
+							'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+							'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+						)
+					)
+				)
+			),
+			array(
+				'Article' => array(
+					'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+				),
+				'User' => array(
+					'id' => 3, 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31',
+					'ArticleFeatured' => array(
+						array(
+						'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+						)
+					)
+				)
+			),
+			array(
+				'Article' => array(
+					'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+				),
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31',
+					'ArticleFeatured' => array(
+						array(
+							'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+							'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+						),
+						array(
+							'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+							'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+						)
+					)
+				)
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Article->find('all', array('contain' => array('User' => array('ArticleFeatured', 'Comment'))));
+		$expected = array(
+			array(
+				'Article' => array(
+					'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+				),
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31',
+					'ArticleFeatured' => array(
+						array(
+							'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+							'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+						),
+						array(
+							'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+							'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+						)
+					),
+					'Comment' => array(
+						array(
+							'id' => 3, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Third Comment for First Article',
+							'published' => 'Y', 'created' => '2007-03-18 10:49:23', 'updated' => '2007-03-18 10:51:31'
+						),
+						array(
+							'id' => 4, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Fourth Comment for First Article',
+							'published' => 'N', 'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31'
+						),
+						array(
+							'id' => 5, 'article_id' => 2, 'user_id' => 1, 'comment' => 'First Comment for Second Article',
+							'published' => 'Y', 'created' => '2007-03-18 10:53:23', 'updated' => '2007-03-18 10:55:31'
+						)
+					)
+				)
+			),
+			array(
+				'Article' => array(
+					'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+				),
+				'User' => array(
+					'id' => 3, 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31',
+					'ArticleFeatured' => array(
+						array(
+						'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+						)
+					),
+					'Comment' => array()
+				)
+			),
+			array(
+				'Article' => array(
+					'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+				),
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31',
+					'ArticleFeatured' => array(
+						array(
+							'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+							'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+						),
+						array(
+							'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+							'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+						)
+					),
+					'Comment' => array(
+						array(
+							'id' => 3, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Third Comment for First Article',
+							'published' => 'Y', 'created' => '2007-03-18 10:49:23', 'updated' => '2007-03-18 10:51:31'
+						),
+						array(
+							'id' => 4, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Fourth Comment for First Article',
+							'published' => 'N', 'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31'
+						),
+						array(
+							'id' => 5, 'article_id' => 2, 'user_id' => 1, 'comment' => 'First Comment for Second Article',
+							'published' => 'Y', 'created' => '2007-03-18 10:53:23', 'updated' => '2007-03-18 10:55:31'
+						)
+					)
+				)
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Article->find('all', array('contain' => array('User' => 'ArticleFeatured', 'Tag', 'Comment' => 'Attachment')));
+		$expected = array(
+			array(
+				'Article' => array(
+					'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+				),
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31',
+					'ArticleFeatured' => array(
+						array(
+							'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+							'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+						),
+						array(
+							'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+							'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+						)
+					)
+				),
+				'Comment' => array(
+					array(
+						'id' => 1, 'article_id' => 1, 'user_id' => 2, 'comment' => 'First Comment for First Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:45:23', 'updated' => '2007-03-18 10:47:31',
+						'Attachment' => array()
+					),
+					array(
+						'id' => 2, 'article_id' => 1, 'user_id' => 4, 'comment' => 'Second Comment for First Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:47:23', 'updated' => '2007-03-18 10:49:31',
+						'Attachment' => array()
+					),
+					array(
+						'id' => 3, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Third Comment for First Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:49:23', 'updated' => '2007-03-18 10:51:31',
+						'Attachment' => array()
+					),
+					array(
+						'id' => 4, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Fourth Comment for First Article',
+						'published' => 'N', 'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31',
+						'Attachment' => array()
+					)
+				),
+				'Tag' => array(
+					array('id' => 1, 'tag' => 'tag1', 'created' => '2007-03-18 12:22:23', 'updated' => '2007-03-18 12:24:31'),
+					array('id' => 2, 'tag' => 'tag2', 'created' => '2007-03-18 12:24:23', 'updated' => '2007-03-18 12:26:31')
+				)
+			),
+			array(
+				'Article' => array(
+					'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+				),
+				'User' => array(
+					'id' => 3, 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31',
+					'ArticleFeatured' => array(
+						array(
+						'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+						)
+					)
+				),
+				'Comment' => array(
+					array(
+						'id' => 5, 'article_id' => 2, 'user_id' => 1, 'comment' => 'First Comment for Second Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:53:23', 'updated' => '2007-03-18 10:55:31',
+						'Attachment' => array(
+							'id' => 1, 'comment_id' => 5, 'attachment' => 'attachment.zip',
+							'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31'
+						)
+					),
+					array(
+						'id' => 6, 'article_id' => 2, 'user_id' => 2, 'comment' => 'Second Comment for Second Article',
+						'published' => 'Y', 'created' => '2007-03-18 10:55:23', 'updated' => '2007-03-18 10:57:31',
+						'Attachment' => array()
+					)
+				),
+				'Tag' => array(
+					array('id' => 1, 'tag' => 'tag1', 'created' => '2007-03-18 12:22:23', 'updated' => '2007-03-18 12:24:31'),
+					array('id' => 3, 'tag' => 'tag3', 'created' => '2007-03-18 12:26:23', 'updated' => '2007-03-18 12:28:31')
+				)
+			),
+			array(
+				'Article' => array(
+					'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+					'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+				),
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31',
+					'ArticleFeatured' => array(
+						array(
+							'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+							'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+						),
+						array(
+							'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+							'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+						)
+					)
+				),
+				'Comment' => array(),
+				'Tag' => array()
+			)
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testFindThirdLevel method
+ *
+ * @access public
+ * @return void
+ */
+	function testFindThirdLevel() {
+		$this->User->contain(array('ArticleFeatured' => array('Featured' => 'Category')));
+		$result = $this->User->find('all', array('recursive' => 3));
+		$expected = array(
+			array(
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				),
+				'ArticleFeatured' => array(
+					array(
+						'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+						'Featured' => array(
+							'id' => 1, 'article_featured_id' => 1, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23',
+							'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+							'Category' => array(
+								'id' => 1, 'parent_id' => 0, 'name' => 'Category 1',
+								'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'
+							)
+						)
+					),
+					array(
+						'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31',
+						'Featured' => array()
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 2, 'user' => 'nate', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:18:23', 'updated' => '2007-03-17 01:20:31'
+				),
+				'ArticleFeatured' => array()
+			),
+			array(
+				'User' => array(
+					'id' => 3, 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31'
+				),
+				'ArticleFeatured' => array(
+					array(
+						'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31',
+						'Featured' => array(
+							'id' => 2, 'article_featured_id' => 2, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23',
+							'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+							'Category' => array(
+								'id' => 1, 'parent_id' => 0, 'name' => 'Category 1',
+								'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'
+							)
+						)
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 4, 'user' => 'garrett', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:22:23', 'updated' => '2007-03-17 01:24:31'
+				),
+				'ArticleFeatured' => array()
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$this->User->contain(array('ArticleFeatured' => array('Featured' => 'Category', 'Comment' => array('Article', 'Attachment'))));
+		$result = $this->User->find('all', array('recursive' => 3));
+		$expected = array(
+			array(
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				),
+				'ArticleFeatured' => array(
+					array(
+						'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+						'Featured' => array(
+							'id' => 1, 'article_featured_id' => 1, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23',
+							'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+							'Category' => array(
+								'id' => 1, 'parent_id' => 0, 'name' => 'Category 1',
+								'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'
+							)
+						),
+						'Comment' => array(
+							array(
+								'id' => 1, 'article_id' => 1, 'user_id' => 2, 'comment' => 'First Comment for First Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:45:23', 'updated' => '2007-03-18 10:47:31',
+								'Article' => array(
+									'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+								),
+								'Attachment' => array()
+							),
+							array(
+								'id' => 2, 'article_id' => 1, 'user_id' => 4, 'comment' => 'Second Comment for First Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:47:23', 'updated' => '2007-03-18 10:49:31',
+								'Article' => array(
+									'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+								),
+								'Attachment' => array()
+							),
+							array(
+								'id' => 3, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Third Comment for First Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:49:23', 'updated' => '2007-03-18 10:51:31',
+								'Article' => array(
+									'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+								),
+								'Attachment' => array()
+							),
+							array(
+								'id' => 4, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Fourth Comment for First Article',
+								'published' => 'N', 'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31',
+								'Article' => array(
+									'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+								),
+								'Attachment' => array()
+							)
+						)
+					),
+					array(
+						'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31',
+						'Featured' => array(),
+						'Comment' => array()
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 2, 'user' => 'nate', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:18:23', 'updated' => '2007-03-17 01:20:31'
+				),
+				'ArticleFeatured' => array()
+			),
+			array(
+				'User' => array(
+					'id' => 3, 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31'
+				),
+				'ArticleFeatured' => array(
+					array(
+						'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31',
+						'Featured' => array(
+							'id' => 2, 'article_featured_id' => 2, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23',
+							'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+							'Category' => array(
+								'id' => 1, 'parent_id' => 0, 'name' => 'Category 1',
+								'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'
+							)
+						),
+						'Comment' => array(
+							array(
+								'id' => 5, 'article_id' => 2, 'user_id' => 1, 'comment' => 'First Comment for Second Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:53:23', 'updated' => '2007-03-18 10:55:31',
+								'Article' => array(
+									'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+								),
+								'Attachment' => array(
+									'id' => 1, 'comment_id' => 5, 'attachment' => 'attachment.zip',
+									'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31'
+								)
+							),
+							array(
+								'id' => 6, 'article_id' => 2, 'user_id' => 2, 'comment' => 'Second Comment for Second Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:55:23', 'updated' => '2007-03-18 10:57:31',
+								'Article' => array(
+									'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+								),
+								'Attachment' => array()
+							)
+						)
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 4, 'user' => 'garrett', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:22:23', 'updated' => '2007-03-17 01:24:31'
+				),
+				'ArticleFeatured' => array()
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$this->User->contain(array('ArticleFeatured' => array('Featured' => 'Category', 'Comment' => 'Attachment'), 'Article'));
+		$result = $this->User->find('all', array('recursive' => 3));
+		$expected = array(
+			array(
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				),
+				'Article' => array(
+					array(
+						'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+					),
+					array(
+						'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+					)
+				),
+				'ArticleFeatured' => array(
+					array(
+						'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+						'Featured' => array(
+							'id' => 1, 'article_featured_id' => 1, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23',
+							'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+							'Category' => array(
+								'id' => 1, 'parent_id' => 0, 'name' => 'Category 1',
+								'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'
+							)
+						),
+						'Comment' => array(
+							array(
+								'id' => 1, 'article_id' => 1, 'user_id' => 2, 'comment' => 'First Comment for First Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:45:23', 'updated' => '2007-03-18 10:47:31',
+								'Attachment' => array()
+							),
+							array(
+								'id' => 2, 'article_id' => 1, 'user_id' => 4, 'comment' => 'Second Comment for First Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:47:23', 'updated' => '2007-03-18 10:49:31',
+								'Attachment' => array()
+							),
+							array(
+								'id' => 3, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Third Comment for First Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:49:23', 'updated' => '2007-03-18 10:51:31',
+								'Attachment' => array()
+							),
+							array(
+								'id' => 4, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Fourth Comment for First Article',
+								'published' => 'N', 'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31',
+								'Attachment' => array()
+							)
+						)
+					),
+					array(
+						'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31',
+						'Featured' => array(),
+						'Comment' => array()
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 2, 'user' => 'nate', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:18:23', 'updated' => '2007-03-17 01:20:31'
+				),
+				'Article' => array(),
+				'ArticleFeatured' => array()
+			),
+			array(
+				'User' => array(
+					'id' => 3, 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31'
+				),
+				'Article' => array(
+					array(
+						'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+					)
+				),
+				'ArticleFeatured' => array(
+					array(
+						'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31',
+						'Featured' => array(
+							'id' => 2, 'article_featured_id' => 2, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23',
+							'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+							'Category' => array(
+								'id' => 1, 'parent_id' => 0, 'name' => 'Category 1',
+								'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'
+							)
+						),
+						'Comment' => array(
+							array(
+								'id' => 5, 'article_id' => 2, 'user_id' => 1, 'comment' => 'First Comment for Second Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:53:23', 'updated' => '2007-03-18 10:55:31',
+								'Attachment' => array(
+									'id' => 1, 'comment_id' => 5, 'attachment' => 'attachment.zip',
+									'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31'
+								)
+							),
+							array(
+								'id' => 6, 'article_id' => 2, 'user_id' => 2, 'comment' => 'Second Comment for Second Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:55:23', 'updated' => '2007-03-18 10:57:31',
+								'Attachment' => array()
+							)
+						)
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 4, 'user' => 'garrett', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:22:23', 'updated' => '2007-03-17 01:24:31'
+				),
+				'Article' => array(),
+				'ArticleFeatured' => array()
+			)
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testFindEmbeddedThirdLevel method
+ *
+ * @access public
+ * @return void
+ */
+	function testFindEmbeddedThirdLevel() {
+		$result = $this->User->find('all', array('contain' => array('ArticleFeatured' => array('Featured' => 'Category'))));
+		$expected = array(
+			array(
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				),
+				'ArticleFeatured' => array(
+					array(
+						'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+						'Featured' => array(
+							'id' => 1, 'article_featured_id' => 1, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23',
+							'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+							'Category' => array(
+								'id' => 1, 'parent_id' => 0, 'name' => 'Category 1',
+								'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'
+							)
+						)
+					),
+					array(
+						'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31',
+						'Featured' => array()
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 2, 'user' => 'nate', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:18:23', 'updated' => '2007-03-17 01:20:31'
+				),
+				'ArticleFeatured' => array()
+			),
+			array(
+				'User' => array(
+					'id' => 3, 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31'
+				),
+				'ArticleFeatured' => array(
+					array(
+						'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31',
+						'Featured' => array(
+							'id' => 2, 'article_featured_id' => 2, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23',
+							'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+							'Category' => array(
+								'id' => 1, 'parent_id' => 0, 'name' => 'Category 1',
+								'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'
+							)
+						)
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 4, 'user' => 'garrett', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:22:23', 'updated' => '2007-03-17 01:24:31'
+				),
+				'ArticleFeatured' => array()
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $this->User->find('all', array('contain' => array('ArticleFeatured' => array('Featured' => 'Category', 'Comment' => array('Article', 'Attachment')))));
+		$expected = array(
+			array(
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				),
+				'ArticleFeatured' => array(
+					array(
+						'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+						'Featured' => array(
+							'id' => 1, 'article_featured_id' => 1, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23',
+							'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+							'Category' => array(
+								'id' => 1, 'parent_id' => 0, 'name' => 'Category 1',
+								'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'
+							)
+						),
+						'Comment' => array(
+							array(
+								'id' => 1, 'article_id' => 1, 'user_id' => 2, 'comment' => 'First Comment for First Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:45:23', 'updated' => '2007-03-18 10:47:31',
+								'Article' => array(
+									'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+								),
+								'Attachment' => array()
+							),
+							array(
+								'id' => 2, 'article_id' => 1, 'user_id' => 4, 'comment' => 'Second Comment for First Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:47:23', 'updated' => '2007-03-18 10:49:31',
+								'Article' => array(
+									'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+								),
+								'Attachment' => array()
+							),
+							array(
+								'id' => 3, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Third Comment for First Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:49:23', 'updated' => '2007-03-18 10:51:31',
+								'Article' => array(
+									'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+								),
+								'Attachment' => array()
+							),
+							array(
+								'id' => 4, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Fourth Comment for First Article',
+								'published' => 'N', 'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31',
+								'Article' => array(
+									'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+								),
+								'Attachment' => array()
+							)
+						)
+					),
+					array(
+						'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31',
+						'Featured' => array(),
+						'Comment' => array()
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 2, 'user' => 'nate', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:18:23', 'updated' => '2007-03-17 01:20:31'
+				),
+				'ArticleFeatured' => array()
+			),
+			array(
+				'User' => array(
+					'id' => 3, 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31'
+				),
+				'ArticleFeatured' => array(
+					array(
+						'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31',
+						'Featured' => array(
+							'id' => 2, 'article_featured_id' => 2, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23',
+							'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+							'Category' => array(
+								'id' => 1, 'parent_id' => 0, 'name' => 'Category 1',
+								'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'
+							)
+						),
+						'Comment' => array(
+							array(
+								'id' => 5, 'article_id' => 2, 'user_id' => 1, 'comment' => 'First Comment for Second Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:53:23', 'updated' => '2007-03-18 10:55:31',
+								'Article' => array(
+									'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+								),
+								'Attachment' => array(
+									'id' => 1, 'comment_id' => 5, 'attachment' => 'attachment.zip',
+									'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31'
+								)
+							),
+							array(
+								'id' => 6, 'article_id' => 2, 'user_id' => 2, 'comment' => 'Second Comment for Second Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:55:23', 'updated' => '2007-03-18 10:57:31',
+								'Article' => array(
+									'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+								),
+								'Attachment' => array()
+							)
+						)
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 4, 'user' => 'garrett', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:22:23', 'updated' => '2007-03-17 01:24:31'
+				),
+				'ArticleFeatured' => array()
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $this->User->find('all', array('contain' => array('ArticleFeatured' => array('Featured' => 'Category', 'Comment' => 'Attachment'), 'Article')));
+		$expected = array(
+			array(
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				),
+				'Article' => array(
+					array(
+						'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+					),
+					array(
+						'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+					)
+				),
+				'ArticleFeatured' => array(
+					array(
+						'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+						'Featured' => array(
+							'id' => 1, 'article_featured_id' => 1, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23',
+							'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+							'Category' => array(
+								'id' => 1, 'parent_id' => 0, 'name' => 'Category 1',
+								'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'
+							)
+						),
+						'Comment' => array(
+							array(
+								'id' => 1, 'article_id' => 1, 'user_id' => 2, 'comment' => 'First Comment for First Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:45:23', 'updated' => '2007-03-18 10:47:31',
+								'Attachment' => array()
+							),
+							array(
+								'id' => 2, 'article_id' => 1, 'user_id' => 4, 'comment' => 'Second Comment for First Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:47:23', 'updated' => '2007-03-18 10:49:31',
+								'Attachment' => array()
+							),
+							array(
+								'id' => 3, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Third Comment for First Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:49:23', 'updated' => '2007-03-18 10:51:31',
+								'Attachment' => array()
+							),
+							array(
+								'id' => 4, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Fourth Comment for First Article',
+								'published' => 'N', 'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31',
+								'Attachment' => array()
+							)
+						)
+					),
+					array(
+						'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31',
+						'Featured' => array(),
+						'Comment' => array()
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 2, 'user' => 'nate', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:18:23', 'updated' => '2007-03-17 01:20:31'
+				),
+				'Article' => array(),
+				'ArticleFeatured' => array()
+			),
+			array(
+				'User' => array(
+					'id' => 3, 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31'
+				),
+				'Article' => array(
+					array(
+						'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+					)
+				),
+				'ArticleFeatured' => array(
+					array(
+						'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31',
+						'Featured' => array(
+							'id' => 2, 'article_featured_id' => 2, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23',
+							'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+							'Category' => array(
+								'id' => 1, 'parent_id' => 0, 'name' => 'Category 1',
+								'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'
+							)
+						),
+						'Comment' => array(
+							array(
+								'id' => 5, 'article_id' => 2, 'user_id' => 1, 'comment' => 'First Comment for Second Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:53:23', 'updated' => '2007-03-18 10:55:31',
+								'Attachment' => array(
+									'id' => 1, 'comment_id' => 5, 'attachment' => 'attachment.zip',
+									'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31'
+								)
+							),
+							array(
+								'id' => 6, 'article_id' => 2, 'user_id' => 2, 'comment' => 'Second Comment for Second Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:55:23', 'updated' => '2007-03-18 10:57:31',
+								'Attachment' => array()
+							)
+						)
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 4, 'user' => 'garrett', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:22:23', 'updated' => '2007-03-17 01:24:31'
+				),
+				'Article' => array(),
+				'ArticleFeatured' => array()
+			)
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testSettingsThirdLevel method
+ *
+ * @access public
+ * @return void
+ */
+	function testSettingsThirdLevel() {
+		$result = $this->User->find('all', array('contain' => array('ArticleFeatured' => array('Featured' => array('Category' => array('id', 'name'))))));
+		$expected = array(
+			array(
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				),
+				'ArticleFeatured' => array(
+					array(
+						'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+						'Featured' => array(
+							'id' => 1, 'article_featured_id' => 1, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23',
+							'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+							'Category' => array(
+								'id' => 1, 'name' => 'Category 1'
+							)
+						)
+					),
+					array(
+						'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31',
+						'Featured' => array()
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 2, 'user' => 'nate', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:18:23', 'updated' => '2007-03-17 01:20:31'
+				),
+				'ArticleFeatured' => array()
+			),
+			array(
+				'User' => array(
+					'id' => 3, 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31'
+				),
+				'ArticleFeatured' => array(
+					array(
+						'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31',
+						'Featured' => array(
+							'id' => 2, 'article_featured_id' => 2, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23',
+							'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+							'Category' => array(
+								'id' => 1, 'name' => 'Category 1'
+							)
+						)
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 4, 'user' => 'garrett', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:22:23', 'updated' => '2007-03-17 01:24:31'
+				),
+				'ArticleFeatured' => array()
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$r = $this->User->find('all', array('contain' => array(
+			'ArticleFeatured' => array(
+				'id', 'title',
+				'Featured' => array(
+					'id', 'category_id',
+					'Category' => array('id', 'name')
+				)
+			)
+		)));
+
+		$this->assertTrue(Set::matches('/User[id=1]', $r));
+		$this->assertFalse(Set::matches('/Article', $r) || Set::matches('/Comment', $r));
+		$this->assertTrue(Set::matches('/ArticleFeatured', $r));
+		$this->assertFalse(Set::matches('/ArticleFeatured/User', $r) || Set::matches('/ArticleFeatured/Comment', $r) || Set::matches('/ArticleFeatured/Tag', $r));
+		$this->assertTrue(Set::matches('/ArticleFeatured/Featured', $r));
+		$this->assertFalse(Set::matches('/ArticleFeatured/Featured/ArticleFeatured', $r));
+		$this->assertTrue(Set::matches('/ArticleFeatured/Featured/Category', $r));
+		$this->assertTrue(Set::matches('/ArticleFeatured/Featured[id=1]', $r));
+		$this->assertTrue(Set::matches('/ArticleFeatured/Featured[id=1]/Category[id=1]', $r));
+		$this->assertTrue(Set::matches('/ArticleFeatured/Featured[id=1]/Category[name=Category 1]', $r));
+
+		$r = $this->User->find('all', array('contain' => array(
+			'ArticleFeatured' => array(
+				'title',
+				'Featured' => array(
+					'id',
+					'Category' => 'name'
+				)
+			)
+		)));
+
+		$this->assertTrue(Set::matches('/User[id=1]', $r));
+		$this->assertFalse(Set::matches('/Article', $r) || Set::matches('/Comment', $r));
+		$this->assertTrue(Set::matches('/ArticleFeatured', $r));
+		$this->assertFalse(Set::matches('/ArticleFeatured/User', $r) || Set::matches('/ArticleFeatured/Comment', $r) || Set::matches('/ArticleFeatured/Tag', $r));
+		$this->assertTrue(Set::matches('/ArticleFeatured/Featured', $r));
+		$this->assertFalse(Set::matches('/ArticleFeatured/Featured/ArticleFeatured', $r));
+		$this->assertTrue(Set::matches('/ArticleFeatured/Featured/Category', $r));
+		$this->assertTrue(Set::matches('/ArticleFeatured/Featured[id=1]', $r));
+		$this->assertTrue(Set::matches('/ArticleFeatured/Featured[id=1]/Category[name=Category 1]', $r));
+
+		$result = $this->User->find('all', array('contain' => array(
+			'ArticleFeatured' => array(
+				'title',
+				'Featured' => array(
+					'category_id',
+					'Category' => 'name'
+				)
+			)
+		)));
+		$expected = array(
+			array(
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				),
+				'ArticleFeatured' => array(
+					array(
+						'title' => 'First Article', 'id' => 1, 'user_id' => 1,
+						'Featured' => array(
+							'category_id' => 1, 'id' => 1,
+							'Category' => array(
+								'name' => 'Category 1'
+							)
+						)
+					),
+					array(
+						'title' => 'Third Article', 'id' => 3, 'user_id' => 1,
+						'Featured' => array()
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 2, 'user' => 'nate', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:18:23', 'updated' => '2007-03-17 01:20:31'
+				),
+				'ArticleFeatured' => array()
+			),
+			array(
+				'User' => array(
+					'id' => 3, 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31'
+				),
+				'ArticleFeatured' => array(
+					array(
+						'title' => 'Second Article', 'id' => 2, 'user_id' => 3,
+						'Featured' => array(
+							'category_id' => 1, 'id' => 2,
+							'Category' => array(
+								'name' => 'Category 1'
+							)
+						)
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 4, 'user' => 'garrett', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:22:23', 'updated' => '2007-03-17 01:24:31'
+				),
+				'ArticleFeatured' => array()
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$orders = array(
+			'title DESC', 'title DESC, published DESC',
+			array('title' => 'DESC'), array('title' => 'DESC', 'published' => 'DESC'),
+		);
+		foreach ($orders as $order) {
+			$result = $this->User->find('all', array('contain' => array(
+				'ArticleFeatured' => array(
+					'title', 'order' => $order,
+					'Featured' => array(
+						'category_id',
+						'Category' => 'name'
+					)
+				)
+			)));
+			$expected = array(
+				array(
+					'User' => array(
+						'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+						'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+					),
+					'ArticleFeatured' => array(
+						array(
+							'title' => 'Third Article', 'id' => 3, 'user_id' => 1,
+							'Featured' => array()
+						),
+						array(
+							'title' => 'First Article', 'id' => 1, 'user_id' => 1,
+							'Featured' => array(
+								'category_id' => 1, 'id' => 1,
+								'Category' => array(
+									'name' => 'Category 1'
+								)
+							)
+						)
+					)
+				),
+				array(
+					'User' => array(
+						'id' => 2, 'user' => 'nate', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+						'created' => '2007-03-17 01:18:23', 'updated' => '2007-03-17 01:20:31'
+					),
+					'ArticleFeatured' => array()
+				),
+				array(
+					'User' => array(
+						'id' => 3, 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+						'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31'
+					),
+					'ArticleFeatured' => array(
+						array(
+							'title' => 'Second Article', 'id' => 2, 'user_id' => 3,
+							'Featured' => array(
+								'category_id' => 1, 'id' => 2,
+								'Category' => array(
+									'name' => 'Category 1'
+								)
+							)
+						)
+					)
+				),
+				array(
+					'User' => array(
+						'id' => 4, 'user' => 'garrett', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+						'created' => '2007-03-17 01:22:23', 'updated' => '2007-03-17 01:24:31'
+					),
+					'ArticleFeatured' => array()
+				)
+			);
+			$this->assertEqual($result, $expected);
+		}
+	}
+
+/**
+ * testFindThirdLevelNonReset method
+ *
+ * @access public
+ * @return void
+ */
+	function testFindThirdLevelNonReset() {
+		$this->User->contain(false, array('ArticleFeatured' => array('Featured' => 'Category')));
+		$result = $this->User->find('all', array('recursive' => 3));
+		$expected = array(
+			array(
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				),
+				'ArticleFeatured' => array(
+					array(
+						'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+						'Featured' => array(
+							'id' => 1, 'article_featured_id' => 1, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23',
+							'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+							'Category' => array(
+								'id' => 1, 'parent_id' => 0, 'name' => 'Category 1',
+								'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'
+							)
+						)
+					),
+					array(
+						'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31',
+						'Featured' => array()
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 2, 'user' => 'nate', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:18:23', 'updated' => '2007-03-17 01:20:31'
+				),
+				'ArticleFeatured' => array()
+			),
+			array(
+				'User' => array(
+					'id' => 3, 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31'
+				),
+				'ArticleFeatured' => array(
+					array(
+						'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31',
+						'Featured' => array(
+							'id' => 2, 'article_featured_id' => 2, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23',
+							'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+							'Category' => array(
+								'id' => 1, 'parent_id' => 0, 'name' => 'Category 1',
+								'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'
+							)
+						)
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 4, 'user' => 'garrett', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:22:23', 'updated' => '2007-03-17 01:24:31'
+				),
+				'ArticleFeatured' => array()
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$this->User->resetBindings();
+
+		$this->User->contain(false, array('ArticleFeatured' => array('Featured' => 'Category', 'Comment' => array('Article', 'Attachment'))));
+		$result = $this->User->find('all', array('recursive' => 3));
+		$expected = array(
+			array(
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				),
+				'ArticleFeatured' => array(
+					array(
+						'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+						'Featured' => array(
+							'id' => 1, 'article_featured_id' => 1, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23',
+							'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+							'Category' => array(
+								'id' => 1, 'parent_id' => 0, 'name' => 'Category 1',
+								'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'
+							)
+						),
+						'Comment' => array(
+							array(
+								'id' => 1, 'article_id' => 1, 'user_id' => 2, 'comment' => 'First Comment for First Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:45:23', 'updated' => '2007-03-18 10:47:31',
+								'Article' => array(
+									'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+								),
+								'Attachment' => array()
+							),
+							array(
+								'id' => 2, 'article_id' => 1, 'user_id' => 4, 'comment' => 'Second Comment for First Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:47:23', 'updated' => '2007-03-18 10:49:31',
+								'Article' => array(
+									'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+								),
+								'Attachment' => array()
+							),
+							array(
+								'id' => 3, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Third Comment for First Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:49:23', 'updated' => '2007-03-18 10:51:31',
+								'Article' => array(
+									'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+								),
+								'Attachment' => array()
+							),
+							array(
+								'id' => 4, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Fourth Comment for First Article',
+								'published' => 'N', 'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31',
+								'Article' => array(
+									'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+								),
+								'Attachment' => array()
+							)
+						)
+					),
+					array(
+						'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31',
+						'Featured' => array(),
+						'Comment' => array()
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 2, 'user' => 'nate', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:18:23', 'updated' => '2007-03-17 01:20:31'
+				),
+				'ArticleFeatured' => array()
+			),
+			array(
+				'User' => array(
+					'id' => 3, 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31'
+				),
+				'ArticleFeatured' => array(
+					array(
+						'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31',
+						'Featured' => array(
+							'id' => 2, 'article_featured_id' => 2, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23',
+							'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+							'Category' => array(
+								'id' => 1, 'parent_id' => 0, 'name' => 'Category 1',
+								'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'
+							)
+						),
+						'Comment' => array(
+							array(
+								'id' => 5, 'article_id' => 2, 'user_id' => 1, 'comment' => 'First Comment for Second Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:53:23', 'updated' => '2007-03-18 10:55:31',
+								'Article' => array(
+									'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+								),
+								'Attachment' => array(
+									'id' => 1, 'comment_id' => 5, 'attachment' => 'attachment.zip',
+									'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31'
+								)
+							),
+							array(
+								'id' => 6, 'article_id' => 2, 'user_id' => 2, 'comment' => 'Second Comment for Second Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:55:23', 'updated' => '2007-03-18 10:57:31',
+								'Article' => array(
+									'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+								),
+								'Attachment' => array()
+							)
+						)
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 4, 'user' => 'garrett', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:22:23', 'updated' => '2007-03-17 01:24:31'
+				),
+				'ArticleFeatured' => array()
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$this->User->resetBindings();
+
+		$this->User->contain(false, array('ArticleFeatured' => array('Featured' => 'Category', 'Comment' => 'Attachment'), 'Article'));
+		$result = $this->User->find('all', array('recursive' => 3));
+		$expected = array(
+			array(
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				),
+				'Article' => array(
+					array(
+						'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+					),
+					array(
+						'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+					)
+				),
+				'ArticleFeatured' => array(
+					array(
+						'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+						'Featured' => array(
+							'id' => 1, 'article_featured_id' => 1, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23',
+							'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+							'Category' => array(
+								'id' => 1, 'parent_id' => 0, 'name' => 'Category 1',
+								'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'
+							)
+						),
+						'Comment' => array(
+							array(
+								'id' => 1, 'article_id' => 1, 'user_id' => 2, 'comment' => 'First Comment for First Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:45:23', 'updated' => '2007-03-18 10:47:31',
+								'Attachment' => array()
+							),
+							array(
+								'id' => 2, 'article_id' => 1, 'user_id' => 4, 'comment' => 'Second Comment for First Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:47:23', 'updated' => '2007-03-18 10:49:31',
+								'Attachment' => array()
+							),
+							array(
+								'id' => 3, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Third Comment for First Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:49:23', 'updated' => '2007-03-18 10:51:31',
+								'Attachment' => array()
+							),
+							array(
+								'id' => 4, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Fourth Comment for First Article',
+								'published' => 'N', 'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31',
+								'Attachment' => array()
+							)
+						)
+					),
+					array(
+						'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31',
+						'Featured' => array(),
+						'Comment' => array()
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 2, 'user' => 'nate', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:18:23', 'updated' => '2007-03-17 01:20:31'
+				),
+				'Article' => array(),
+				'ArticleFeatured' => array()
+			),
+			array(
+				'User' => array(
+					'id' => 3, 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31'
+				),
+				'Article' => array(
+					array(
+						'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+					)
+				),
+				'ArticleFeatured' => array(
+					array(
+						'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31',
+						'Featured' => array(
+							'id' => 2, 'article_featured_id' => 2, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23',
+							'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+							'Category' => array(
+								'id' => 1, 'parent_id' => 0, 'name' => 'Category 1',
+								'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'
+							)
+						),
+						'Comment' => array(
+							array(
+								'id' => 5, 'article_id' => 2, 'user_id' => 1, 'comment' => 'First Comment for Second Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:53:23', 'updated' => '2007-03-18 10:55:31',
+								'Attachment' => array(
+									'id' => 1, 'comment_id' => 5, 'attachment' => 'attachment.zip',
+									'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31'
+								)
+							),
+							array(
+								'id' => 6, 'article_id' => 2, 'user_id' => 2, 'comment' => 'Second Comment for Second Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:55:23', 'updated' => '2007-03-18 10:57:31',
+								'Attachment' => array()
+							)
+						)
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 4, 'user' => 'garrett', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:22:23', 'updated' => '2007-03-17 01:24:31'
+				),
+				'Article' => array(),
+				'ArticleFeatured' => array()
+			)
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testFindEmbeddedThirdLevelNonReset method
+ *
+ * @access public
+ * @return void
+ */
+	function testFindEmbeddedThirdLevelNonReset() {
+		$result = $this->User->find('all', array('reset' => false, 'contain' => array('ArticleFeatured' => array('Featured' => 'Category'))));
+		$expected = array(
+			array(
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				),
+				'ArticleFeatured' => array(
+					array(
+						'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+						'Featured' => array(
+							'id' => 1, 'article_featured_id' => 1, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23',
+							'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+							'Category' => array(
+								'id' => 1, 'parent_id' => 0, 'name' => 'Category 1',
+								'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'
+							)
+						)
+					),
+					array(
+						'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31',
+						'Featured' => array()
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 2, 'user' => 'nate', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:18:23', 'updated' => '2007-03-17 01:20:31'
+				),
+				'ArticleFeatured' => array()
+			),
+			array(
+				'User' => array(
+					'id' => 3, 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31'
+				),
+				'ArticleFeatured' => array(
+					array(
+						'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31',
+						'Featured' => array(
+							'id' => 2, 'article_featured_id' => 2, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23',
+							'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+							'Category' => array(
+								'id' => 1, 'parent_id' => 0, 'name' => 'Category 1',
+								'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'
+							)
+						)
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 4, 'user' => 'garrett', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:22:23', 'updated' => '2007-03-17 01:24:31'
+				),
+				'ArticleFeatured' => array()
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$this->__assertBindings($this->User, array('hasMany' => array('ArticleFeatured')));
+		$this->__assertBindings($this->User->ArticleFeatured, array('hasOne' => array('Featured')));
+		$this->__assertBindings($this->User->ArticleFeatured->Featured, array('belongsTo' => array('Category')));
+
+		$this->User->resetBindings();
+
+		$this->__assertBindings($this->User, array('hasMany' => array('Article', 'ArticleFeatured', 'Comment')));
+		$this->__assertBindings($this->User->ArticleFeatured, array('belongsTo' => array('User'), 'hasOne' => array('Featured'), 'hasMany' => array('Comment'), 'hasAndBelongsToMany' => array('Tag')));
+		$this->__assertBindings($this->User->ArticleFeatured->Featured, array('belongsTo' => array('ArticleFeatured', 'Category')));
+		$this->__assertBindings($this->User->ArticleFeatured->Comment, array('belongsTo' => array('Article', 'User'), 'hasOne' => array('Attachment')));
+
+		$result = $this->User->find('all', array('reset' => false, 'contain' => array('ArticleFeatured' => array('Featured' => 'Category', 'Comment' => array('Article', 'Attachment')))));
+		$expected = array(
+			array(
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				),
+				'ArticleFeatured' => array(
+					array(
+						'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+						'Featured' => array(
+							'id' => 1, 'article_featured_id' => 1, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23',
+							'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+							'Category' => array(
+								'id' => 1, 'parent_id' => 0, 'name' => 'Category 1',
+								'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'
+							)
+						),
+						'Comment' => array(
+							array(
+								'id' => 1, 'article_id' => 1, 'user_id' => 2, 'comment' => 'First Comment for First Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:45:23', 'updated' => '2007-03-18 10:47:31',
+								'Article' => array(
+									'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+								),
+								'Attachment' => array()
+							),
+							array(
+								'id' => 2, 'article_id' => 1, 'user_id' => 4, 'comment' => 'Second Comment for First Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:47:23', 'updated' => '2007-03-18 10:49:31',
+								'Article' => array(
+									'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+								),
+								'Attachment' => array()
+							),
+							array(
+								'id' => 3, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Third Comment for First Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:49:23', 'updated' => '2007-03-18 10:51:31',
+								'Article' => array(
+									'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+								),
+								'Attachment' => array()
+							),
+							array(
+								'id' => 4, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Fourth Comment for First Article',
+								'published' => 'N', 'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31',
+								'Article' => array(
+									'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+								),
+								'Attachment' => array()
+							)
+						)
+					),
+					array(
+						'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31',
+						'Featured' => array(),
+						'Comment' => array()
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 2, 'user' => 'nate', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:18:23', 'updated' => '2007-03-17 01:20:31'
+				),
+				'ArticleFeatured' => array()
+			),
+			array(
+				'User' => array(
+					'id' => 3, 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31'
+				),
+				'ArticleFeatured' => array(
+					array(
+						'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31',
+						'Featured' => array(
+							'id' => 2, 'article_featured_id' => 2, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23',
+							'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+							'Category' => array(
+								'id' => 1, 'parent_id' => 0, 'name' => 'Category 1',
+								'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'
+							)
+						),
+						'Comment' => array(
+							array(
+								'id' => 5, 'article_id' => 2, 'user_id' => 1, 'comment' => 'First Comment for Second Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:53:23', 'updated' => '2007-03-18 10:55:31',
+								'Article' => array(
+									'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+								),
+								'Attachment' => array(
+									'id' => 1, 'comment_id' => 5, 'attachment' => 'attachment.zip',
+									'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31'
+								)
+							),
+							array(
+								'id' => 6, 'article_id' => 2, 'user_id' => 2, 'comment' => 'Second Comment for Second Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:55:23', 'updated' => '2007-03-18 10:57:31',
+								'Article' => array(
+									'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+								),
+								'Attachment' => array()
+							)
+						)
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 4, 'user' => 'garrett', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:22:23', 'updated' => '2007-03-17 01:24:31'
+				),
+				'ArticleFeatured' => array()
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$this->__assertBindings($this->User, array('hasMany' => array('ArticleFeatured')));
+		$this->__assertBindings($this->User->ArticleFeatured, array('hasOne' => array('Featured'), 'hasMany' => array('Comment')));
+		$this->__assertBindings($this->User->ArticleFeatured->Featured, array('belongsTo' => array('Category')));
+		$this->__assertBindings($this->User->ArticleFeatured->Comment, array('belongsTo' => array('Article'), 'hasOne' => array('Attachment')));
+
+		$this->User->resetBindings();
+		$this->__assertBindings($this->User, array('hasMany' => array('Article', 'ArticleFeatured', 'Comment')));
+		$this->__assertBindings($this->User->ArticleFeatured, array('belongsTo' => array('User'), 'hasOne' => array('Featured'), 'hasMany' => array('Comment'), 'hasAndBelongsToMany' => array('Tag')));
+		$this->__assertBindings($this->User->ArticleFeatured->Featured, array('belongsTo' => array('ArticleFeatured', 'Category')));
+		$this->__assertBindings($this->User->ArticleFeatured->Comment, array('belongsTo' => array('Article', 'User'), 'hasOne' => array('Attachment')));
+
+		$result = $this->User->find('all', array('contain' => array('ArticleFeatured' => array('Featured' => 'Category', 'Comment' => array('Article', 'Attachment')), false)));
+		$expected = array(
+			array(
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				),
+				'ArticleFeatured' => array(
+					array(
+						'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+						'Featured' => array(
+							'id' => 1, 'article_featured_id' => 1, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23',
+							'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+							'Category' => array(
+								'id' => 1, 'parent_id' => 0, 'name' => 'Category 1',
+								'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'
+							)
+						),
+						'Comment' => array(
+							array(
+								'id' => 1, 'article_id' => 1, 'user_id' => 2, 'comment' => 'First Comment for First Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:45:23', 'updated' => '2007-03-18 10:47:31',
+								'Article' => array(
+									'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+								),
+								'Attachment' => array()
+							),
+							array(
+								'id' => 2, 'article_id' => 1, 'user_id' => 4, 'comment' => 'Second Comment for First Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:47:23', 'updated' => '2007-03-18 10:49:31',
+								'Article' => array(
+									'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+								),
+								'Attachment' => array()
+							),
+							array(
+								'id' => 3, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Third Comment for First Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:49:23', 'updated' => '2007-03-18 10:51:31',
+								'Article' => array(
+									'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+								),
+								'Attachment' => array()
+							),
+							array(
+								'id' => 4, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Fourth Comment for First Article',
+								'published' => 'N', 'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31',
+								'Article' => array(
+									'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+								),
+								'Attachment' => array()
+							)
+						)
+					),
+					array(
+						'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31',
+						'Featured' => array(),
+						'Comment' => array()
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 2, 'user' => 'nate', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:18:23', 'updated' => '2007-03-17 01:20:31'
+				),
+				'ArticleFeatured' => array()
+			),
+			array(
+				'User' => array(
+					'id' => 3, 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31'
+				),
+				'ArticleFeatured' => array(
+					array(
+						'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31',
+						'Featured' => array(
+							'id' => 2, 'article_featured_id' => 2, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23',
+							'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+							'Category' => array(
+								'id' => 1, 'parent_id' => 0, 'name' => 'Category 1',
+								'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'
+							)
+						),
+						'Comment' => array(
+							array(
+								'id' => 5, 'article_id' => 2, 'user_id' => 1, 'comment' => 'First Comment for Second Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:53:23', 'updated' => '2007-03-18 10:55:31',
+								'Article' => array(
+									'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+								),
+								'Attachment' => array(
+									'id' => 1, 'comment_id' => 5, 'attachment' => 'attachment.zip',
+									'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31'
+								)
+							),
+							array(
+								'id' => 6, 'article_id' => 2, 'user_id' => 2, 'comment' => 'Second Comment for Second Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:55:23', 'updated' => '2007-03-18 10:57:31',
+								'Article' => array(
+									'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+									'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+								),
+								'Attachment' => array()
+							)
+						)
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 4, 'user' => 'garrett', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:22:23', 'updated' => '2007-03-17 01:24:31'
+				),
+				'ArticleFeatured' => array()
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$this->__assertBindings($this->User, array('hasMany' => array('ArticleFeatured')));
+		$this->__assertBindings($this->User->ArticleFeatured, array('hasOne' => array('Featured'), 'hasMany' => array('Comment')));
+		$this->__assertBindings($this->User->ArticleFeatured->Featured, array('belongsTo' => array('Category')));
+		$this->__assertBindings($this->User->ArticleFeatured->Comment, array('belongsTo' => array('Article'), 'hasOne' => array('Attachment')));
+
+		$this->User->resetBindings();
+		$this->__assertBindings($this->User, array('hasMany' => array('Article', 'ArticleFeatured', 'Comment')));
+		$this->__assertBindings($this->User->ArticleFeatured, array('belongsTo' => array('User'), 'hasOne' => array('Featured'), 'hasMany' => array('Comment'), 'hasAndBelongsToMany' => array('Tag')));
+		$this->__assertBindings($this->User->ArticleFeatured->Featured, array('belongsTo' => array('ArticleFeatured', 'Category')));
+		$this->__assertBindings($this->User->ArticleFeatured->Comment, array('belongsTo' => array('Article', 'User'), 'hasOne' => array('Attachment')));
+
+		$result = $this->User->find('all', array('reset' => false, 'contain' => array('ArticleFeatured' => array('Featured' => 'Category', 'Comment' => 'Attachment'), 'Article')));
+		$expected = array(
+			array(
+				'User' => array(
+					'id' => 1, 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				),
+				'Article' => array(
+					array(
+						'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+					),
+					array(
+						'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'
+					)
+				),
+				'ArticleFeatured' => array(
+					array(
+						'id' => 1, 'user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+						'Featured' => array(
+							'id' => 1, 'article_featured_id' => 1, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23',
+							'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+							'Category' => array(
+								'id' => 1, 'parent_id' => 0, 'name' => 'Category 1',
+								'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'
+							)
+						),
+						'Comment' => array(
+							array(
+								'id' => 1, 'article_id' => 1, 'user_id' => 2, 'comment' => 'First Comment for First Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:45:23', 'updated' => '2007-03-18 10:47:31',
+								'Attachment' => array()
+							),
+							array(
+								'id' => 2, 'article_id' => 1, 'user_id' => 4, 'comment' => 'Second Comment for First Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:47:23', 'updated' => '2007-03-18 10:49:31',
+								'Attachment' => array()
+							),
+							array(
+								'id' => 3, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Third Comment for First Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:49:23', 'updated' => '2007-03-18 10:51:31',
+								'Attachment' => array()
+							),
+							array(
+								'id' => 4, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Fourth Comment for First Article',
+								'published' => 'N', 'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31',
+								'Attachment' => array()
+							)
+						)
+					),
+					array(
+						'id' => 3, 'user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31',
+						'Featured' => array(),
+						'Comment' => array()
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 2, 'user' => 'nate', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:18:23', 'updated' => '2007-03-17 01:20:31'
+				),
+				'Article' => array(),
+				'ArticleFeatured' => array()
+			),
+			array(
+				'User' => array(
+					'id' => 3, 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31'
+				),
+				'Article' => array(
+					array(
+						'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'
+					)
+				),
+				'ArticleFeatured' => array(
+					array(
+						'id' => 2, 'user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body',
+						'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31',
+						'Featured' => array(
+							'id' => 2, 'article_featured_id' => 2, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23',
+							'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31',
+							'Category' => array(
+								'id' => 1, 'parent_id' => 0, 'name' => 'Category 1',
+								'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'
+							)
+						),
+						'Comment' => array(
+							array(
+								'id' => 5, 'article_id' => 2, 'user_id' => 1, 'comment' => 'First Comment for Second Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:53:23', 'updated' => '2007-03-18 10:55:31',
+								'Attachment' => array(
+									'id' => 1, 'comment_id' => 5, 'attachment' => 'attachment.zip',
+									'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31'
+								)
+							),
+							array(
+								'id' => 6, 'article_id' => 2, 'user_id' => 2, 'comment' => 'Second Comment for Second Article',
+								'published' => 'Y', 'created' => '2007-03-18 10:55:23', 'updated' => '2007-03-18 10:57:31',
+								'Attachment' => array()
+							)
+						)
+					)
+				)
+			),
+			array(
+				'User' => array(
+					'id' => 4, 'user' => 'garrett', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:22:23', 'updated' => '2007-03-17 01:24:31'
+				),
+				'Article' => array(),
+				'ArticleFeatured' => array()
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$this->__assertBindings($this->User, array('hasMany' => array('Article', 'ArticleFeatured')));
+		$this->__assertBindings($this->User->Article);
+		$this->__assertBindings($this->User->ArticleFeatured, array('hasOne' => array('Featured'), 'hasMany' => array('Comment')));
+		$this->__assertBindings($this->User->ArticleFeatured->Featured, array('belongsTo' => array('Category')));
+		$this->__assertBindings($this->User->ArticleFeatured->Comment, array('hasOne' => array('Attachment')));
+
+		$this->User->resetBindings();
+		$this->__assertBindings($this->User, array('hasMany' => array('Article', 'ArticleFeatured', 'Comment')));
+		$this->__assertBindings($this->User->Article, array('belongsTo' => array('User'), 'hasMany' => array('Comment'), 'hasAndBelongsToMany' => array('Tag')));
+		$this->__assertBindings($this->User->ArticleFeatured, array('belongsTo' => array('User'), 'hasOne' => array('Featured'), 'hasMany' => array('Comment'), 'hasAndBelongsToMany' => array('Tag')));
+		$this->__assertBindings($this->User->ArticleFeatured->Featured, array('belongsTo' => array('ArticleFeatured', 'Category')));
+		$this->__assertBindings($this->User->ArticleFeatured->Comment, array('belongsTo' => array('Article', 'User'), 'hasOne' => array('Attachment')));
+	}
+
+/**
+ * testEmbeddedFindFields method
+ *
+ * @access public
+ * @return void
+ */
+	function testEmbeddedFindFields() {
+		$result = $this->Article->find('all', array(
+			'contain' => array('User(user)'),
+			'fields' => array('title')
+		));
+		$expected = array(
+			array('Article' => array('title' => 'First Article'), 'User' => array('user' => 'mariano', 'id' => 1)),
+			array('Article' => array('title' => 'Second Article'), 'User' => array('user' => 'larry', 'id' => 3)),
+			array('Article' => array('title' => 'Third Article'), 'User' => array('user' => 'mariano', 'id' => 1)),
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Article->find('all', array(
+			'contain' => array('User(id, user)'),
+			'fields' => array('title')
+		));
+		$expected = array(
+			array('Article' => array('title' => 'First Article'), 'User' => array('user' => 'mariano', 'id' => 1)),
+			array('Article' => array('title' => 'Second Article'), 'User' => array('user' => 'larry', 'id' => 3)),
+			array('Article' => array('title' => 'Third Article'), 'User' => array('user' => 'mariano', 'id' => 1)),
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Article->find('all', array(
+			'contain' => array(
+				'Comment(comment, published)' => 'Attachment(attachment)', 'User(user)'
+			),
+			'fields' => array('title')
+		));
+		if (!empty($result)) {
+			foreach($result as $i=>$article) {
+				foreach($article['Comment'] as $j=>$comment) {
+					$result[$i]['Comment'][$j] = array_diff_key($comment, array('id'=>true));
+				}
+			}
+		}
+		$expected = array(
+			array(
+				'Article' => array('title' => 'First Article', 'id' => 1),
+				'User' => array('user' => 'mariano', 'id' => 1),
+				'Comment' => array(
+					array('comment' => 'First Comment for First Article', 'published' => 'Y', 'article_id' => 1, 'Attachment' => array()),
+					array('comment' => 'Second Comment for First Article', 'published' => 'Y', 'article_id' => 1, 'Attachment' => array()),
+					array('comment' => 'Third Comment for First Article', 'published' => 'Y', 'article_id' => 1, 'Attachment' => array()),
+					array('comment' => 'Fourth Comment for First Article', 'published' => 'N', 'article_id' => 1, 'Attachment' => array()),
+				)
+			),
+			array(
+				'Article' => array('title' => 'Second Article', 'id' => 2),
+				'User' => array('user' => 'larry', 'id' => 3),
+				'Comment' => array(
+					array('comment' => 'First Comment for Second Article', 'published' => 'Y', 'article_id' => 2, 'Attachment' => array(
+						'attachment' => 'attachment.zip', 'id' => 1
+					)),
+					array('comment' => 'Second Comment for Second Article', 'published' => 'Y', 'article_id' => 2, 'Attachment' => array())
+				)
+			),
+			array(
+				'Article' => array('title' => 'Third Article', 'id' => 3),
+				'User' => array('user' => 'mariano', 'id' => 1),
+				'Comment' => array()
+			),
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that hasOne and belongsTo fields act the same in a contain array.
+ *
+ * @return void
+ */
+	function testHasOneFieldsInContain() {
+		$this->Article->unbindModel(array(
+			'hasMany' => array('Comment')
+		), true);
+		unset($this->Article->Comment);
+		$this->Article->bindModel(array(
+			'hasOne' => array('Comment')
+		));
+
+		$result = $this->Article->find('all', array(
+			'fields' => array('title', 'body'),
+			'contain' => array(
+				'Comment' => array(
+					'fields' => array('comment')
+				),
+				'User' => array(
+					'fields' => array('user')
+				)
+			)
+		));
+		$this->assertTrue(isset($result[0]['Article']['title']), 'title missing %s');
+		$this->assertTrue(isset($result[0]['Article']['body']), 'body missing %s');
+		$this->assertTrue(isset($result[0]['Comment']['comment']), 'comment missing %s');
+		$this->assertTrue(isset($result[0]['User']['user']), 'body missing %s');
+		$this->assertFalse(isset($result[0]['Comment']['published']), 'published found %s');
+		$this->assertFalse(isset($result[0]['User']['password']), 'password found %s');
+	}
+/**
+ * testFindConditionalBinding method
+ *
+ * @access public
+ * @return void
+ */
+	function testFindConditionalBinding() {
+		$this->Article->contain(array(
+			'User(user)',
+			'Tag' => array(
+				'fields' => array('tag', 'created'),
+				'conditions' => array('created >=' => '2007-03-18 12:24')
+			)
+		));
+		$result = $this->Article->find('all', array('fields' => array('title')));
+		$expected = array(
+			array(
+				'Article' => array('id' => 1, 'title' => 'First Article'),
+				'User' => array('id' => 1, 'user' => 'mariano'),
+				'Tag' => array(array('tag' => 'tag2', 'created' => '2007-03-18 12:24:23'))
+			),
+			array(
+				'Article' => array('id' => 2, 'title' => 'Second Article'),
+				'User' => array('id' => 3, 'user' => 'larry'),
+				'Tag' => array(array('tag' => 'tag3', 'created' => '2007-03-18 12:26:23'))
+			),
+			array(
+				'Article' => array('id' => 3, 'title' => 'Third Article'),
+				'User' => array('id' => 1, 'user' => 'mariano'),
+				'Tag' => array()
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$this->Article->contain(array('User(id,user)', 'Tag' => array('fields' => array('tag', 'created'))));
+		$result = $this->Article->find('all', array('fields' => array('title')));
+		$expected = array(
+			array(
+				'Article' => array('id' => 1, 'title' => 'First Article'),
+				'User' => array('id' => 1, 'user' => 'mariano'),
+				'Tag' => array(
+					array('tag' => 'tag1', 'created' => '2007-03-18 12:22:23'),
+					array('tag' => 'tag2', 'created' => '2007-03-18 12:24:23')
+				)
+			),
+			array(
+				'Article' => array('id' => 2, 'title' => 'Second Article'),
+				'User' => array('id' => 3, 'user' => 'larry'),
+				'Tag' => array(
+					array('tag' => 'tag1', 'created' => '2007-03-18 12:22:23'),
+					array('tag' => 'tag3', 'created' => '2007-03-18 12:26:23')
+				)
+			),
+			array(
+				'Article' => array('id' => 3, 'title' => 'Third Article'),
+				'User' => array('id' => 1, 'user' => 'mariano'),
+				'Tag' => array()
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Article->find('all', array(
+			'fields' => array('title'),
+			'contain' => array('User(id,user)', 'Tag' => array('fields' => array('tag', 'created')))
+		));
+		$expected = array(
+			array(
+				'Article' => array('id' => 1, 'title' => 'First Article'),
+				'User' => array('id' => 1, 'user' => 'mariano'),
+				'Tag' => array(
+					array('tag' => 'tag1', 'created' => '2007-03-18 12:22:23'),
+					array('tag' => 'tag2', 'created' => '2007-03-18 12:24:23')
+				)
+			),
+			array(
+				'Article' => array('id' => 2, 'title' => 'Second Article'),
+				'User' => array('id' => 3, 'user' => 'larry'),
+				'Tag' => array(
+					array('tag' => 'tag1', 'created' => '2007-03-18 12:22:23'),
+					array('tag' => 'tag3', 'created' => '2007-03-18 12:26:23')
+				)
+			),
+			array(
+				'Article' => array('id' => 3, 'title' => 'Third Article'),
+				'User' => array('id' => 1, 'user' => 'mariano'),
+				'Tag' => array()
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$this->Article->contain(array(
+			'User(id,user)',
+			'Tag' => array(
+				'fields' => array('tag', 'created'),
+				'conditions' => array('created >=' => '2007-03-18 12:24')
+			)
+		));
+		$result = $this->Article->find('all', array('fields' => array('title')));
+		$expected = array(
+			array(
+				'Article' => array('id' => 1, 'title' => 'First Article'),
+				'User' => array('id' => 1, 'user' => 'mariano'),
+				'Tag' => array(array('tag' => 'tag2', 'created' => '2007-03-18 12:24:23'))
+			),
+			array(
+				'Article' => array('id' => 2, 'title' => 'Second Article'),
+				'User' => array('id' => 3, 'user' => 'larry'),
+				'Tag' => array(array('tag' => 'tag3', 'created' => '2007-03-18 12:26:23'))
+			),
+			array(
+				'Article' => array('id' => 3, 'title' => 'Third Article'),
+				'User' => array('id' => 1, 'user' => 'mariano'),
+				'Tag' => array()
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$this->assertTrue(empty($this->User->Article->hasAndBelongsToMany['Tag']['conditions']));
+
+		$result = $this->User->find('all', array('contain' => array(
+			'Article.Tag' => array('conditions' => array('created >=' => '2007-03-18 12:24'))
+		)));
+
+		$this->assertTrue(Set::matches('/User[id=1]', $result));
+		$this->assertFalse(Set::matches('/Article[id=1]/Tag[id=1]', $result));
+		$this->assertTrue(Set::matches('/Article[id=1]/Tag[id=2]', $result));
+		$this->assertTrue(empty($this->User->Article->hasAndBelongsToMany['Tag']['conditions']));
+
+		$this->assertTrue(empty($this->User->Article->hasAndBelongsToMany['Tag']['order']));
+
+		$result = $this->User->find('all', array('contain' => array(
+			'Article.Tag' => array('order' => 'created DESC')
+		)));
+
+		$this->assertTrue(Set::matches('/User[id=1]', $result));
+		$this->assertTrue(Set::matches('/Article[id=1]/Tag[id=1]', $result));
+		$this->assertTrue(Set::matches('/Article[id=1]/Tag[id=2]', $result));
+		$this->assertTrue(empty($this->User->Article->hasAndBelongsToMany['Tag']['order']));
+	}
+
+/**
+ * testOtherFinds method
+ *
+ * @access public
+ * @return void
+ */
+	function testOtherFinds() {
+		$result = $this->Article->find('count');
+		$expected = 3;
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Article->find('count', array('conditions' => array('Article.id >' => '1')));
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Article->find('count', array('contain' => array()));
+		$expected = 3;
+		$this->assertEqual($result, $expected);
+
+		$this->Article->contain(array('User(id,user)', 'Tag' => array('fields' => array('tag', 'created'), 'conditions' => array('created >=' => '2007-03-18 12:24'))));
+		$result = $this->Article->find('first', array('fields' => array('title')));
+		$expected = array(
+			'Article' => array('id' => 1, 'title' => 'First Article'),
+			'User' => array('id' => 1, 'user' => 'mariano'),
+			'Tag' => array(array('tag' => 'tag2', 'created' => '2007-03-18 12:24:23'))
+		);
+		$this->assertEqual($result, $expected);
+
+		$this->Article->contain(array('User(id,user)', 'Tag' => array('fields' => array('tag', 'created'))));
+		$result = $this->Article->find('first', array('fields' => array('title')));
+		$expected = array(
+			'Article' => array('id' => 1, 'title' => 'First Article'),
+			'User' => array('id' => 1, 'user' => 'mariano'),
+			'Tag' => array(
+				array('tag' => 'tag1', 'created' => '2007-03-18 12:22:23'),
+				array('tag' => 'tag2', 'created' => '2007-03-18 12:24:23')
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Article->find('first', array(
+			'fields' => array('title'),
+			'order' => 'Article.id DESC',
+			'contain' => array('User(id,user)', 'Tag' => array('fields' => array('tag', 'created')))
+		));
+		$expected = array(
+			'Article' => array('id' => 3, 'title' => 'Third Article'),
+			'User' => array('id' => 1, 'user' => 'mariano'),
+			'Tag' => array()
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Article->find('list', array(
+			'contain' => array('User(id,user)'),
+			'fields' => array('Article.id', 'Article.title')
+		));
+		$expected = array(
+			1 => 'First Article',
+			2 => 'Second Article',
+			3 => 'Third Article'
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testPaginate method
+ *
+ * @access public
+ * @return void
+ */
+	function testPaginate() {
+		$Controller =& new Controller();
+		$Controller->uses = array('Article');
+		$Controller->passedArgs[] = '1';
+		$Controller->params['url'] = array();
+		$Controller->constructClasses();
+
+		$Controller->paginate = array('Article' => array('fields' => array('title'), 'contain' => array('User(user)')));
+		$result = $Controller->paginate('Article');
+		$expected = array(
+			array('Article' => array('title' => 'First Article'), 'User' => array('user' => 'mariano', 'id' => 1)),
+			array('Article' => array('title' => 'Second Article'), 'User' => array('user' => 'larry', 'id' => 3)),
+			array('Article' => array('title' => 'Third Article'), 'User' => array('user' => 'mariano', 'id' => 1)),
+		);
+		$this->assertEqual($result, $expected);
+
+		$r = $Controller->Article->find('all');
+		$this->assertTrue(Set::matches('/Article[id=1]', $r));
+		$this->assertTrue(Set::matches('/User[id=1]', $r));
+		$this->assertTrue(Set::matches('/Tag[id=1]', $r));
+
+		$Controller->paginate = array('Article' => array('contain' => array('Comment(comment)' => 'User(user)'), 'fields' => array('title')));
+		$result = $Controller->paginate('Article');
+		$expected = array(
+			array(
+				'Article' => array('title' => 'First Article', 'id' => 1),
+				'Comment' => array(
+					array(
+						'comment' => 'First Comment for First Article',
+						'user_id' => 2,
+						'article_id' => 1,
+						'User' => array('user' => 'nate')
+					),
+					array(
+						'comment' => 'Second Comment for First Article',
+						'user_id' => 4,
+						'article_id' => 1,
+						'User' => array('user' => 'garrett')
+					),
+					array(
+						'comment' => 'Third Comment for First Article',
+						'user_id' => 1,
+						'article_id' => 1,
+						'User' => array('user' => 'mariano')
+					),
+					array(
+						'comment' => 'Fourth Comment for First Article',
+						'user_id' => 1,
+						'article_id' => 1,
+						'User' => array('user' => 'mariano')
+					)
+				)
+			),
+			array(
+				'Article' => array('title' => 'Second Article', 'id' => 2),
+				'Comment' => array(
+					array(
+						'comment' => 'First Comment for Second Article',
+						'user_id' => 1,
+						'article_id' => 2,
+						'User' => array('user' => 'mariano')
+					),
+					array(
+						'comment' => 'Second Comment for Second Article',
+						'user_id' => 2,
+						'article_id' => 2,
+						'User' => array('user' => 'nate')
+					)
+				)
+			),
+			array(
+				'Article' => array('title' => 'Third Article', 'id' => 3),
+				'Comment' => array()
+			),
+		);
+		$this->assertEqual($result, $expected);
+
+		$r = $Controller->Article->find('all');
+		$this->assertTrue(Set::matches('/Article[id=1]', $r));
+		$this->assertTrue(Set::matches('/User[id=1]', $r));
+		$this->assertTrue(Set::matches('/Tag[id=1]', $r));
+
+		$Controller->Article->unbindModel(array('hasMany' => array('Comment'), 'belongsTo' => array('User'), 'hasAndBelongsToMany' => array('Tag')), false);
+		$Controller->Article->bindModel(array('hasMany' => array('Comment'), 'belongsTo' => array('User')), false);
+
+		$Controller->paginate = array('Article' => array('contain' => array('Comment(comment)', 'User(user)'), 'fields' => array('title')));
+		$r = $Controller->paginate('Article');
+		$this->assertTrue(Set::matches('/Article[id=1]', $r));
+		$this->assertTrue(Set::matches('/User[id=1]', $r));
+		$this->assertTrue(Set::matches('/Comment[article_id=1]', $r));
+		$this->assertFalse(Set::matches('/Comment[id=1]', $r));
+
+		$r = $this->Article->find('all');
+		$this->assertTrue(Set::matches('/Article[id=1]', $r));
+		$this->assertTrue(Set::matches('/User[id=1]', $r));
+		$this->assertTrue(Set::matches('/Comment[article_id=1]', $r));
+		$this->assertTrue(Set::matches('/Comment[id=1]', $r));
+	}
+
+/**
+ * testOriginalAssociations method
+ *
+ * @access public
+ * @return void
+ */
+	function testOriginalAssociations() {
+		$this->Article->Comment->Behaviors->attach('Containable');
+
+		$options = array(
+			'conditions' => array(
+				'Comment.published' => 'Y',
+			),
+			'contain' => 'User',
+			'recursive' => 1
+		);
+
+		$firstResult = $this->Article->Comment->find('all', $options);
+
+		$dummyResult = $this->Article->Comment->find('all', array(
+			'conditions' => array(
+				'User.user' => 'mariano'
+			),
+			'fields' => array('User.password'),
+			'contain' => array('User.password'),
+		));
+
+		$result = $this->Article->Comment->find('all', $options);
+		$this->assertEqual($result, $firstResult);
+
+		$this->Article->unbindModel(array('hasMany' => array('Comment'), 'belongsTo' => array('User'), 'hasAndBelongsToMany' => array('Tag')), false);
+		$this->Article->bindModel(array('hasMany' => array('Comment'), 'belongsTo' => array('User')), false);
+
+		$r = $this->Article->find('all', array('contain' => array('Comment(comment)', 'User(user)'), 'fields' => array('title')));
+		$this->assertTrue(Set::matches('/Article[id=1]', $r));
+		$this->assertTrue(Set::matches('/User[id=1]', $r));
+		$this->assertTrue(Set::matches('/Comment[article_id=1]', $r));
+		$this->assertFalse(Set::matches('/Comment[id=1]', $r));
+
+		$r = $this->Article->find('all');
+		$this->assertTrue(Set::matches('/Article[id=1]', $r));
+		$this->assertTrue(Set::matches('/User[id=1]', $r));
+		$this->assertTrue(Set::matches('/Comment[article_id=1]', $r));
+		$this->assertTrue(Set::matches('/Comment[id=1]', $r));
+
+		$this->Article->bindModel(array('hasAndBelongsToMany' => array('Tag')), false);
+
+		$this->Article->contain(false, array('User(id,user)', 'Comment' => array('fields' => array('comment'), 'conditions' => array('created >=' => '2007-03-18 10:49'))));
+		$result = $this->Article->find('all', array('fields' => array('title'), 'limit' => 1, 'page' => 1, 'order' => 'Article.id ASC'));
+		$expected = array(array(
+			'Article' => array('id' => 1, 'title' => 'First Article'),
+			'User' => array('id' => 1, 'user' => 'mariano'),
+			'Comment' => array(
+				array('comment' => 'Third Comment for First Article', 'article_id' => 1),
+				array('comment' => 'Fourth Comment for First Article', 'article_id' => 1)
+			)
+		));
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Article->find('all', array('fields' => array('title', 'User.id', 'User.user'), 'limit' => 1, 'page' => 2, 'order' => 'Article.id ASC'));
+		$expected = array(array(
+			'Article' => array('id' => 2, 'title' => 'Second Article'),
+			'User' => array('id' => 3, 'user' => 'larry'),
+			'Comment' => array(
+				array('comment' => 'First Comment for Second Article', 'article_id' => 2),
+				array('comment' => 'Second Comment for Second Article', 'article_id' => 2)
+			)
+		));
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Article->find('all', array('fields' => array('title', 'User.id', 'User.user'), 'limit' => 1, 'page' => 3, 'order' => 'Article.id ASC'));
+		$expected = array(array(
+			'Article' => array('id' => 3, 'title' => 'Third Article'),
+			'User' => array('id' => 1, 'user' => 'mariano'),
+			'Comment' => array()
+		));
+		$this->assertEqual($result, $expected);
+
+		$this->Article->contain(false, array('User' => array('fields' => 'user'), 'Comment'));
+		$result = $this->Article->find('all');
+		$this->assertTrue(Set::matches('/Article[id=1]', $result));
+		$this->assertTrue(Set::matches('/User[user=mariano]', $result));
+		$this->assertTrue(Set::matches('/Comment[article_id=1]', $result));
+		$this->Article->resetBindings();
+
+		$this->Article->contain(false, array('User' => array('fields' => array('user')), 'Comment'));
+		$result = $this->Article->find('all');
+		$this->assertTrue(Set::matches('/Article[id=1]', $result));
+		$this->assertTrue(Set::matches('/User[user=mariano]', $result));
+		$this->assertTrue(Set::matches('/Comment[article_id=1]', $result));
+		$this->Article->resetBindings();
+	}
+
+/**
+ * testResetAddedAssociation method
+ *
+ * @access public
+ */
+	function testResetAddedAssociation() {
+		$this->assertTrue(empty($this->Article->hasMany['ArticlesTag']));
+
+		$this->Article->bindModel(array(
+			'hasMany' => array('ArticlesTag')
+		));
+		$this->assertTrue(!empty($this->Article->hasMany['ArticlesTag']));
+
+		$result = $this->Article->find('first', array(
+			'conditions' => array('Article.id' => 1),
+			'contain' => array('ArticlesTag')
+		));
+		$expected = array('Article', 'ArticlesTag');
+		$this->assertTrue(!empty($result));
+		$this->assertEqual('First Article', $result['Article']['title']);
+		$this->assertTrue(!empty($result['ArticlesTag']));
+		$this->assertEqual($expected, array_keys($result));
+
+		$this->assertTrue(empty($this->Article->hasMany['ArticlesTag']));
+		
+		$this->JoinA =& ClassRegistry::init('JoinA');
+		$this->JoinB =& ClassRegistry::init('JoinB');
+		$this->JoinC =& ClassRegistry::init('JoinC');
+		
+		$this->JoinA->Behaviors->attach('Containable');
+		$this->JoinB->Behaviors->attach('Containable');
+		$this->JoinC->Behaviors->attach('Containable');
+		
+		$this->JoinA->JoinB->find('all', array('contain' => array('JoinA')));
+		$this->JoinA->bindModel(array('hasOne' => array('JoinAsJoinC' => array('joinTable' => 'as_cs'))), false);
+		$result = $this->JoinA->hasOne;
+		$this->JoinA->find('all');
+		$resultAfter = $this->JoinA->hasOne;
+		$this->assertEqual($result, $resultAfter);
+	}
+
+/**
+ * testResetAssociation method
+ *
+ * @access public
+ */
+	function testResetAssociation() {
+		$this->Article->Behaviors->attach('Containable');
+		$this->Article->Comment->Behaviors->attach('Containable');
+		$this->Article->User->Behaviors->attach('Containable');
+
+		$initialOptions = array(
+			'conditions' => array(
+				'Comment.published' => 'Y',
+			),
+			'contain' => 'User',
+			'recursive' => 1,
+		);
+
+		$initialModels = $this->Article->Comment->find('all', $initialOptions);
+
+		$findOptions = array(
+			'conditions' => array(
+				'User.user' => 'mariano',
+			),
+			'fields' => array('User.password'),
+			'contain' => array('User.password')
+		);
+		$result = $this->Article->Comment->find('all', $findOptions);
+		$result = $this->Article->Comment->find('all', $initialOptions);
+		$this->assertEqual($result, $initialModels);
+	}
+
+/**
+ * testResetDeeperHasOneAssociations method
+ *
+ * @access public
+ */
+	function testResetDeeperHasOneAssociations() {
+		$this->Article->User->unbindModel(array(
+			'hasMany' => array('ArticleFeatured', 'Comment')
+		), false);
+		$userHasOne = array('hasOne' => array('ArticleFeatured', 'Comment'));
+
+		$this->Article->User->bindModel($userHasOne, false);
+		$expected = $this->Article->User->hasOne;
+		$this->Article->find('all');
+		$this->assertEqual($expected, $this->Article->User->hasOne);
+
+		$this->Article->User->bindModel($userHasOne, false);
+		$expected = $this->Article->User->hasOne;
+		$this->Article->find('all', array(
+			'contain' => array(
+				'User' => array('ArticleFeatured', 'Comment')
+			)
+		));
+		$this->assertEqual($expected, $this->Article->User->hasOne);
+
+		$this->Article->User->bindModel($userHasOne, false);
+		$expected = $this->Article->User->hasOne;
+		$this->Article->find('all', array(
+			'contain' => array(
+				'User' => array(
+					'ArticleFeatured',
+					'Comment' => array('fields' => array('created'))
+				)
+			)
+		));
+		$this->assertEqual($expected, $this->Article->User->hasOne);
+
+		$this->Article->User->bindModel($userHasOne, false);
+		$expected = $this->Article->User->hasOne;
+		$this->Article->find('all', array(
+			'contain' => array(
+				'User' => array(
+					'Comment' => array('fields' => array('created'))
+				)
+			)
+		));
+		$this->assertEqual($expected, $this->Article->User->hasOne);
+
+		$this->Article->User->bindModel($userHasOne, false);
+		$expected = $this->Article->User->hasOne;
+		$this->Article->find('all', array(
+			'contain' => array(
+				'User.ArticleFeatured' => array(
+					'conditions' => array('ArticleFeatured.published' => 'Y')
+				),
+				'User.Comment'
+			)
+		));
+		$this->assertEqual($expected, $this->Article->User->hasOne);
+	}
+
+/**
+ * testResetMultipleHabtmAssociations method
+ *
+ * @access public
+ */
+	function testResetMultipleHabtmAssociations() {
+		$articleHabtm = array(
+			'hasAndBelongsToMany' => array(
+				'Tag' => array(
+					'className'				=> 'Tag',
+					'joinTable'				=> 'articles_tags',
+					'foreignKey'			=> 'article_id',
+					'associationForeignKey' => 'tag_id'
+				),
+				'ShortTag' => array(
+					'className'				=> 'Tag',
+					'joinTable'				=> 'articles_tags',
+					'foreignKey'			=> 'article_id',
+					'associationForeignKey' => 'tag_id',
+					// LENGHT function mysql-only, using LIKE does almost the same
+					'conditions' 			=> 'ShortTag.tag LIKE "???"'
+				)
+			)
+		);
+
+		$this->Article->resetBindings();
+		$this->Article->bindModel($articleHabtm, false);
+		$expected = $this->Article->hasAndBelongsToMany;
+		$this->Article->find('all');
+		$this->assertEqual($expected, $this->Article->hasAndBelongsToMany);
+
+		$this->Article->resetBindings();
+		$this->Article->bindModel($articleHabtm, false);
+		$expected = $this->Article->hasAndBelongsToMany;
+		$this->Article->find('all', array('contain' => 'Tag.tag'));
+		$this->assertEqual($expected, $this->Article->hasAndBelongsToMany);
+
+		$this->Article->resetBindings();
+		$this->Article->bindModel($articleHabtm, false);
+		$expected = $this->Article->hasAndBelongsToMany;
+		$this->Article->find('all', array('contain' => 'Tag'));
+		$this->assertEqual($expected, $this->Article->hasAndBelongsToMany);
+
+		$this->Article->resetBindings();
+		$this->Article->bindModel($articleHabtm, false);
+		$expected = $this->Article->hasAndBelongsToMany;
+		$this->Article->find('all', array('contain' => array('Tag' => array('fields' => array(null)))));
+		$this->assertEqual($expected, $this->Article->hasAndBelongsToMany);
+
+		$this->Article->resetBindings();
+		$this->Article->bindModel($articleHabtm, false);
+		$expected = $this->Article->hasAndBelongsToMany;
+		$this->Article->find('all', array('contain' => array('Tag' => array('fields' => array('Tag.tag')))));
+		$this->assertEqual($expected, $this->Article->hasAndBelongsToMany);
+
+		$this->Article->resetBindings();
+		$this->Article->bindModel($articleHabtm, false);
+		$expected = $this->Article->hasAndBelongsToMany;
+		$this->Article->find('all', array('contain' => array('Tag' => array('fields' => array('Tag.tag', 'Tag.created')))));
+		$this->assertEqual($expected, $this->Article->hasAndBelongsToMany);
+
+		$this->Article->resetBindings();
+		$this->Article->bindModel($articleHabtm, false);
+		$expected = $this->Article->hasAndBelongsToMany;
+		$this->Article->find('all', array('contain' => 'ShortTag.tag'));
+		$this->assertEqual($expected, $this->Article->hasAndBelongsToMany);
+
+		$this->Article->resetBindings();
+		$this->Article->bindModel($articleHabtm, false);
+		$expected = $this->Article->hasAndBelongsToMany;
+		$this->Article->find('all', array('contain' => 'ShortTag'));
+		$this->assertEqual($expected, $this->Article->hasAndBelongsToMany);
+
+		$this->Article->resetBindings();
+		$this->Article->bindModel($articleHabtm, false);
+		$expected = $this->Article->hasAndBelongsToMany;
+		$this->Article->find('all', array('contain' => array('ShortTag' => array('fields' => array(null)))));
+		$this->assertEqual($expected, $this->Article->hasAndBelongsToMany);
+
+		$this->Article->resetBindings();
+		$this->Article->bindModel($articleHabtm, false);
+		$expected = $this->Article->hasAndBelongsToMany;
+		$this->Article->find('all', array('contain' => array('ShortTag' => array('fields' => array('ShortTag.tag')))));
+		$this->assertEqual($expected, $this->Article->hasAndBelongsToMany);
+
+		$this->Article->resetBindings();
+		$this->Article->bindModel($articleHabtm, false);
+		$expected = $this->Article->hasAndBelongsToMany;
+		$this->Article->find('all', array('contain' => array('ShortTag' => array('fields' => array('ShortTag.tag', 'ShortTag.created')))));
+		$this->assertEqual($expected, $this->Article->hasAndBelongsToMany);
+	}
+
+/**
+ * test that bindModel and unbindModel work with find() calls in between.
+ */
+	function testBindMultipleTimesWithFind() {
+		$binding = array(
+			'hasOne' => array(
+				'ArticlesTag' => array(
+					'foreignKey' => false,
+					'type' => 'INNER',
+					'conditions' => array(
+						'ArticlesTag.article_id = Article.id'
+					)
+				),
+				'Tag' => array(
+					'type' => 'INNER',
+					'foreignKey' => false,
+					'conditions' => array(
+						'ArticlesTag.tag_id = Tag.id'
+					)
+				)
+			)
+		);
+		$this->Article->unbindModel(array('hasAndBelongsToMany' => array('Tag')));
+		$this->Article->bindModel($binding);
+		$result = $this->Article->find('all', array('limit' => 1, 'contain' => array('ArticlesTag', 'Tag')));
+
+		$this->Article->unbindModel(array('hasAndBelongsToMany' => array('Tag')));
+		$this->Article->bindModel($binding);
+		$result = $this->Article->find('all', array('limit' => 1, 'contain' => array('ArticlesTag', 'Tag')));
+
+		$associated = $this->Article->getAssociated();
+		$this->assertEqual('hasAndBelongsToMany', $associated['Tag']);
+		$this->assertFalse(isset($associated['ArticleTag']));
+	}
+
+/**
+ * test that autoFields doesn't splice in fields from other databases.
+ *
+ * @return void
+ */
+	function testAutoFieldsWithMultipleDatabases() {
+		$config = new DATABASE_CONFIG();
+
+		$skip = $this->skipIf(
+			!isset($config->test) || !isset($config->test2),
+			 '%s Primary and secondary test databases not configured, skipping cross-database '
+			.'join tests.'
+			.' To run these tests, you must define $test and $test2 in your database configuration.'
+		);
+		if ($skip) {
+			return;
+		}
+
+		$db =& ConnectionManager::getDataSource('test2');
+		$this->_fixtures[$this->_fixtureClassMap['User']]->create($db);
+		$this->_fixtures[$this->_fixtureClassMap['User']]->insert($db);
+
+		$this->Article->User->setDataSource('test2');
+
+		$result = $this->Article->find('all', array(
+			'fields' => array('Article.title'),
+			'contain' => array('User')
+		));
+		$this->assertTrue(isset($result[0]['Article']));
+		$this->assertTrue(isset($result[0]['User']));
+
+		$this->_fixtures[$this->_fixtureClassMap['User']]->drop($db);
+	}
+/**
+ * test that autoFields doesn't splice in columns that aren't part of the join.
+ *
+ * @return void
+ */
+	function testAutoFieldsWithRecursiveNegativeOne() {
+		$this->Article->recursive = -1;
+		$result = $this->Article->field('title', array('Article.title' => 'First Article'));
+		$this->assertNoErrors();
+		$this->assertEqual($result, 'First Article', 'Field is wrong');
+	}
+
+/**
+ * test that find(all) doesn't return incorrect values when mixed with containable.
+ *
+ * @return void
+ */
+	function testFindAllReturn() {
+		$result = $this->Article->find('all', array(
+			'conditions' => array('Article.id' => 999999999)
+		));
+		$this->assertEqual($result, array(), 'Should be empty.');
+	}
+
+/**
+ * containments method
+ *
+ * @param mixed $Model
+ * @param array $contain
+ * @access private
+ * @return void
+ */
+	function __containments(&$Model, $contain = array()) {
+		if (!is_array($Model)) {
+			$result = $Model->containments($contain);
+			return $this->__containments($result['models']);
+		} else {
+			$result = $Model;
+			foreach($result as $i => $containment) {
+				$result[$i] = array_diff_key($containment, array('instance' => true));
+			}
+		}
+
+		return $result;
+	}
+
+/**
+ * assertBindings method
+ *
+ * @param mixed $Model
+ * @param array $expected
+ * @access private
+ * @return void
+ */
+	function __assertBindings(&$Model, $expected = array()) {
+		$expected = array_merge(array('belongsTo' => array(), 'hasOne' => array(), 'hasMany' => array(), 'hasAndBelongsToMany' => array()), $expected);
+
+		foreach($expected as $binding => $expect) {
+			$this->assertEqual(array_keys($Model->$binding), $expect);
+		}
+	}
+
+/**
+ * bindings method
+ *
+ * @param mixed $Model
+ * @param array $extra
+ * @param bool $output
+ * @access private
+ * @return void
+ */
+	function __bindings(&$Model, $extra = array(), $output = true) {
+		$relationTypes = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');
+
+		$debug = '[';
+		$lines = array();
+		foreach($relationTypes as $binding) {
+			if (!empty($Model->$binding)) {
+				$models = array_keys($Model->$binding);
+				foreach($models as $linkedModel) {
+					$line = $linkedModel;
+					if (!empty($extra) && !empty($Model->{$binding}[$linkedModel])) {
+						$extraData = array();
+						foreach(array_intersect_key($Model->{$binding}[$linkedModel], array_flip($extra)) as $key => $value) {
+							$extraData[] = $key . ': ' . (is_array($value) ? '(' . implode(', ', $value) . ')' : $value);
+						}
+						$line .= ' {' . implode(' - ', $extraData) . '}';
+					}
+					$lines[] = $line;
+				}
+			}
+		}
+		$debug .= implode(' | ' , $lines);
+		$debug .=  ']';
+		$debug = '<strong>' . $Model->alias . '</strong>: ' . $debug . '<br />';
+
+		if ($output) {
+			echo $debug;
+		}
+
+		return $debug;
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/model/behaviors/translate.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/model/behaviors/translate.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/model/behaviors/translate.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,917 @@
+<?php
+/**
+ * TranslateBehaviorTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.behaviors
+ * @since         CakePHP(tm) v 1.2.0.5669
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) {
+	define('CAKEPHP_UNIT_TEST_EXECUTION', 1);
+}
+
+App::import('Core', array('AppModel', 'Model'));
+require_once(dirname(dirname(__FILE__)) . DS . 'models.php');
+
+/**
+ * TranslateBehaviorTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.behaviors
+ */
+class TranslateBehaviorTest extends CakeTestCase {
+
+/**
+ * autoFixtures property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $autoFixtures = false;
+
+/**
+ * fixtures property
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array(
+		'core.translated_item', 'core.translate', 'core.translate_table',
+		'core.translated_article', 'core.translate_article', 'core.user', 'core.comment', 'core.tag', 'core.articles_tag',
+		'core.translate_with_prefix'
+	);
+
+/**
+ * endTest method
+ *
+ * @access public
+ * @return void
+ */
+	function endTest() {
+		ClassRegistry::flush();
+	}
+
+/**
+ * Test that count queries with conditions get the correct joins
+ *
+ * @return void
+ */
+	function testCountWithConditions() {
+		$this->loadFixtures('Translate', 'TranslatedItem');
+
+		$Model =& new TranslatedItem();
+		$Model->locale = 'eng';
+		$result = $Model->find('count', array(
+			'conditions' => array(
+				'I18n__content.locale' => 'eng'
+			)
+		));
+		$this->assertEqual(3, $result);
+	}
+
+/**
+ * testTranslateModel method
+ *
+ * @access public
+ * @return void
+ */
+	function testTranslateModel() {
+		$TestModel =& new Tag();
+		$TestModel->translateTable = 'another_i18n';
+		$TestModel->Behaviors->attach('Translate', array('title'));
+		$translateModel =& $TestModel->Behaviors->Translate->translateModel($TestModel);
+		$this->assertEqual($translateModel->name, 'I18nModel');
+		$this->assertEqual($translateModel->useTable, 'another_i18n');
+
+		$TestModel =& new User();
+		$TestModel->Behaviors->attach('Translate', array('title'));
+		$translateModel =& $TestModel->Behaviors->Translate->translateModel($TestModel);
+		$this->assertEqual($translateModel->name, 'I18nModel');
+		$this->assertEqual($translateModel->useTable, 'i18n');
+
+		$TestModel =& new TranslatedArticle();
+		$translateModel =& $TestModel->Behaviors->Translate->translateModel($TestModel);
+		$this->assertEqual($translateModel->name, 'TranslateArticleModel');
+		$this->assertEqual($translateModel->useTable, 'article_i18n');
+
+		$TestModel =& new TranslatedItem();
+		$translateModel =& $TestModel->Behaviors->Translate->translateModel($TestModel);
+		$this->assertEqual($translateModel->name, 'TranslateTestModel');
+		$this->assertEqual($translateModel->useTable, 'i18n');
+	}
+
+/**
+ * testLocaleFalsePlain method
+ *
+ * @access public
+ * @return void
+ */
+	function testLocaleFalsePlain() {
+		$this->loadFixtures('Translate', 'TranslatedItem');
+
+		$TestModel =& new TranslatedItem();
+		$TestModel->locale = false;
+
+		$result = $TestModel->read(null, 1);
+		$expected = array('TranslatedItem' => array('id' => 1, 'slug' => 'first_translated'));
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('all', array('fields' => array('slug')));
+		$expected = array(
+			array('TranslatedItem' => array('slug' => 'first_translated')),
+			array('TranslatedItem' => array('slug' => 'second_translated')),
+			array('TranslatedItem' => array('slug' => 'third_translated'))
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testLocaleFalseAssociations method
+ *
+ * @access public
+ * @return void
+ */
+	function testLocaleFalseAssociations() {
+		$this->loadFixtures('Translate', 'TranslatedItem');
+
+		$TestModel =& new TranslatedItem();
+		$TestModel->locale = false;
+		$TestModel->unbindTranslation();
+		$translations = array('title' => 'Title', 'content' => 'Content');
+		$TestModel->bindTranslation($translations, false);
+
+		$result = $TestModel->read(null, 1);
+		$expected = array(
+			'TranslatedItem' => array('id' => 1, 'slug' => 'first_translated'),
+			'Title' => array(
+				array('id' => 1, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Title #1'),
+				array('id' => 3, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Titel #1'),
+				array('id' => 5, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Titulek #1')
+			),
+			'Content' => array(
+				array('id' => 2, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'content', 'content' => 'Content #1'),
+				array('id' => 4, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'content', 'content' => 'Inhalt #1'),
+				array('id' => 6, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'content', 'content' => 'Obsah #1')
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$TestModel->hasMany['Title']['fields'] = $TestModel->hasMany['Content']['fields'] = array('content');
+		$TestModel->hasMany['Title']['conditions']['locale'] = $TestModel->hasMany['Content']['conditions']['locale'] = 'eng';
+
+		$result = $TestModel->find('all', array('fields' => array('TranslatedItem.slug')));
+		$expected = array(
+			array(
+				'TranslatedItem' => array('id' => 1, 'slug' => 'first_translated'),
+				'Title' => array(array('foreign_key' => 1, 'content' => 'Title #1')),
+				'Content' => array(array('foreign_key' => 1, 'content' => 'Content #1'))
+			),
+			array(
+				'TranslatedItem' => array('id' => 2, 'slug' => 'second_translated'),
+				'Title' => array(array('foreign_key' => 2, 'content' => 'Title #2')),
+				'Content' => array(array('foreign_key' => 2, 'content' => 'Content #2'))
+			),
+			array(
+				'TranslatedItem' => array('id' => 3, 'slug' => 'third_translated'),
+				'Title' => array(array('foreign_key' => 3, 'content' => 'Title #3')),
+				'Content' => array(array('foreign_key' => 3, 'content' => 'Content #3'))
+			)
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testLocaleSingle method
+ *
+ * @access public
+ * @return void
+ */
+	function testLocaleSingle() {
+		$this->loadFixtures('Translate', 'TranslatedItem');
+
+		$TestModel =& new TranslatedItem();
+		$TestModel->locale = 'eng';
+		$result = $TestModel->read(null, 1);
+		$expected = array(
+			'TranslatedItem' => array(
+				'id' => 1,
+				'slug' => 'first_translated',
+				'locale' => 'eng',
+				'title' => 'Title #1',
+				'content' => 'Content #1'
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('all');
+		$expected = array(
+			array(
+				'TranslatedItem' => array(
+					'id' => 1,
+					'slug' => 'first_translated',
+					'locale' => 'eng',
+					'title' => 'Title #1',
+					'content' => 'Content #1'
+				)
+			),
+			array(
+				'TranslatedItem' => array(
+					'id' => 2,
+					'slug' => 'second_translated',
+					'locale' => 'eng',
+					'title' => 'Title #2',
+					'content' => 'Content #2'
+				)
+			),
+			array(
+				'TranslatedItem' => array(
+					'id' => 3,
+					'slug' => 'third_translated',
+					'locale' => 'eng',
+					'title' => 'Title #3',
+					'content' => 'Content #3'
+				)
+			)
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testLocaleSingleWithConditions method
+ *
+ * @access public
+ * @return void
+ */
+	function testLocaleSingleWithConditions() {
+		$this->loadFixtures('Translate', 'TranslatedItem');
+
+		$TestModel =& new TranslatedItem();
+		$TestModel->locale = 'eng';
+		$result = $TestModel->find('all', array('conditions' => array('slug' => 'first_translated')));
+		$expected = array(
+			array(
+				'TranslatedItem' => array(
+					'id' => 1,
+					'slug' => 'first_translated',
+					'locale' => 'eng',
+					'title' => 'Title #1',
+					'content' => 'Content #1'
+				)
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('all', array('conditions' => "TranslatedItem.slug = 'first_translated'"));
+		$expected = array(
+			array(
+				'TranslatedItem' => array(
+					'id' => 1,
+					'slug' => 'first_translated',
+					'locale' => 'eng',
+					'title' => 'Title #1',
+					'content' => 'Content #1'
+				)
+			)
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testLocaleSingleAssociations method
+ *
+ * @access public
+ * @return void
+ */
+	function testLocaleSingleAssociations() {
+		$this->loadFixtures('Translate', 'TranslatedItem');
+
+		$TestModel =& new TranslatedItem();
+		$TestModel->locale = 'eng';
+		$TestModel->unbindTranslation();
+		$translations = array('title' => 'Title', 'content' => 'Content');
+		$TestModel->bindTranslation($translations, false);
+
+		$result = $TestModel->read(null, 1);
+		$expected = array(
+			'TranslatedItem' => array(
+				'id' => 1,
+				'slug' => 'first_translated',
+				'locale' => 'eng',
+				'title' => 'Title #1',
+				'content' => 'Content #1'
+			),
+			'Title' => array(
+				array('id' => 1, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Title #1'),
+				array('id' => 3, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Titel #1'),
+				array('id' => 5, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Titulek #1')
+			),
+			'Content' => array(
+				array('id' => 2, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'content', 'content' => 'Content #1'),
+				array('id' => 4, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'content', 'content' => 'Inhalt #1'),
+				array('id' => 6, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'content', 'content' => 'Obsah #1')
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$TestModel->hasMany['Title']['fields'] = $TestModel->hasMany['Content']['fields'] = array('content');
+		$TestModel->hasMany['Title']['conditions']['locale'] = $TestModel->hasMany['Content']['conditions']['locale'] = 'eng';
+
+		$result = $TestModel->find('all', array('fields' => array('TranslatedItem.title')));
+		$expected = array(
+			array(
+				'TranslatedItem' => array('id' => 1, 'locale' => 'eng', 'title' => 'Title #1'),
+				'Title' => array(array('foreign_key' => 1, 'content' => 'Title #1')),
+				'Content' => array(array('foreign_key' => 1, 'content' => 'Content #1'))
+			),
+			array(
+				'TranslatedItem' => array('id' => 2, 'locale' => 'eng', 'title' => 'Title #2'),
+				'Title' => array(array('foreign_key' => 2, 'content' => 'Title #2')),
+				'Content' => array(array('foreign_key' => 2, 'content' => 'Content #2'))
+			),
+			array(
+				'TranslatedItem' => array('id' => 3, 'locale' => 'eng', 'title' => 'Title #3'),
+				'Title' => array(array('foreign_key' => 3, 'content' => 'Title #3')),
+				'Content' => array(array('foreign_key' => 3, 'content' => 'Content #3'))
+			)
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testLocaleMultiple method
+ *
+ * @access public
+ * @return void
+ */
+	function testLocaleMultiple() {
+		$this->loadFixtures('Translate', 'TranslatedItem');
+
+		$TestModel =& new TranslatedItem();
+		$TestModel->locale = array('deu', 'eng', 'cze');
+		$delete = array(
+			array('locale' => 'deu'),
+			array('foreign_key' => 1, 'field' => 'title', 'locale' => 'eng'),
+			array('foreign_key' => 1, 'field' => 'content', 'locale' => 'cze'),
+			array('foreign_key' => 2, 'field' => 'title', 'locale' => 'cze'),
+			array('foreign_key' => 2, 'field' => 'content', 'locale' => 'eng'),
+			array('foreign_key' => 3, 'field' => 'title')
+		);
+		$I18nModel =& ClassRegistry::getObject('TranslateTestModel');
+		$I18nModel->deleteAll(array('or' => $delete));
+
+		$result = $TestModel->read(null, 1);
+		$expected = array(
+			'TranslatedItem' => array(
+				'id' => 1,
+				'slug' => 'first_translated',
+				'locale' => 'deu',
+				'title' => 'Titulek #1',
+				'content' => 'Content #1'
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('all', array('fields' => array('slug', 'title', 'content')));
+		$expected = array(
+			array(
+				'TranslatedItem' => array(
+					'slug' => 'first_translated',
+					'locale' => 'deu',
+					'title' => 'Titulek #1',
+					'content' => 'Content #1'
+				)
+			),
+			array(
+				'TranslatedItem' => array(
+					'slug' => 'second_translated',
+					'locale' => 'deu',
+					'title' => 'Title #2',
+					'content' => 'Obsah #2'
+				)
+			),
+			array(
+				'TranslatedItem' => array(
+					'slug' => 'third_translated',
+					'locale' => 'deu',
+					'title' => '',
+					'content' => 'Content #3'
+				)
+			)
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testMissingTranslation method
+ *
+ * @access public
+ * @return void
+ */
+	function testMissingTranslation() {
+		$this->loadFixtures('Translate', 'TranslatedItem');
+
+		$TestModel =& new TranslatedItem();
+		$TestModel->locale = 'rus';
+		$result = $TestModel->read(null, 1);
+		$this->assertFalse($result);
+
+		$TestModel->locale = array('rus');
+		$result = $TestModel->read(null, 1);
+		$expected = array(
+			'TranslatedItem' => array(
+				'id' => 1,
+				'slug' => 'first_translated',
+				'locale' => 'rus',
+				'title' => '',
+				'content' => ''
+			)
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testTranslatedFindList method
+ *
+ * @access public
+ * @return void
+ */
+	function testTranslatedFindList() {
+		$this->loadFixtures('Translate', 'TranslatedItem');
+
+		$TestModel =& new TranslatedItem();
+		$TestModel->locale = 'deu';
+		$TestModel->displayField = 'title';
+		$result = $TestModel->find('list', array('recursive' => 1));
+		$expected = array(1 => 'Titel #1', 2 => 'Titel #2', 3 => 'Titel #3');
+		$this->assertEqual($result, $expected);
+
+		// MSSQL trigger an error and stops the page even if the debug = 0
+		if ($this->db->config['driver'] != 'mssql') {
+			$debug = Configure::read('debug');
+			Configure::write('debug', 0);
+
+			$result = $TestModel->find('list', array('recursive' => 1, 'callbacks' => false));
+			$this->assertEqual($result, array());
+
+			$result = $TestModel->find('list', array('recursive' => 1, 'callbacks' => 'after'));
+			$this->assertEqual($result, array());
+			Configure::write('debug', $debug);
+		}
+
+		$result = $TestModel->find('list', array('recursive' => 1, 'callbacks' => 'before'));
+		$expected = array(1 => null, 2 => null, 3 => null);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testReadSelectedFields method
+ *
+ * @access public
+ * @return void
+ */
+	function testReadSelectedFields() {
+		$this->loadFixtures('Translate', 'TranslatedItem');
+
+		$TestModel =& new TranslatedItem();
+		$TestModel->locale = 'eng';
+		$result = $TestModel->find('all', array('fields' => array('slug', 'TranslatedItem.content')));
+		$expected = array(
+			array('TranslatedItem' => array('slug' => 'first_translated', 'locale' => 'eng', 'content' => 'Content #1')),
+			array('TranslatedItem' => array('slug' => 'second_translated', 'locale' => 'eng', 'content' => 'Content #2')),
+			array('TranslatedItem' => array('slug' => 'third_translated', 'locale' => 'eng', 'content' => 'Content #3'))
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('all', array('fields' => array('TranslatedItem.slug', 'content')));
+		$this->assertEqual($result, $expected);
+
+		$TestModel->locale = array('eng', 'deu', 'cze');
+		$delete = array(array('locale' => 'deu'), array('field' => 'content', 'locale' => 'eng'));
+		$I18nModel =& ClassRegistry::getObject('TranslateTestModel');
+		$I18nModel->deleteAll(array('or' => $delete));
+
+		$result = $TestModel->find('all', array('fields' => array('title', 'content')));
+		$expected = array(
+			array('TranslatedItem' => array('locale' => 'eng', 'title' => 'Title #1', 'content' => 'Obsah #1')),
+			array('TranslatedItem' => array('locale' => 'eng', 'title' => 'Title #2', 'content' => 'Obsah #2')),
+			array('TranslatedItem' => array('locale' => 'eng', 'title' => 'Title #3', 'content' => 'Obsah #3'))
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testSaveCreate method
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveCreate() {
+		$this->loadFixtures('Translate', 'TranslatedItem');
+
+		$TestModel =& new TranslatedItem();
+		$TestModel->locale = 'spa';
+		$data = array('slug' => 'fourth_translated', 'title' => 'Leyenda #4', 'content' => 'Contenido #4');
+		$TestModel->create($data);
+		$TestModel->save();
+		$result = $TestModel->read();
+		$expected = array('TranslatedItem' => array_merge($data, array('id' => $TestModel->id, 'locale' => 'spa')));
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testSaveUpdate method
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveUpdate() {
+		$this->loadFixtures('Translate', 'TranslatedItem');
+
+		$TestModel =& new TranslatedItem();
+		$TestModel->locale = 'spa';
+		$oldData = array('slug' => 'fourth_translated', 'title' => 'Leyenda #4');
+		$TestModel->create($oldData);
+		$TestModel->save();
+		$id = $TestModel->id;
+		$newData = array('id' => $id, 'content' => 'Contenido #4');
+		$TestModel->create($newData);
+		$TestModel->save();
+		$result = $TestModel->read(null, $id);
+		$expected = array('TranslatedItem' => array_merge($oldData, $newData, array('locale' => 'spa')));
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testMultipleCreate method
+ *
+ * @access public
+ * @return void
+ */
+	function testMultipleCreate() {
+		$this->loadFixtures('Translate', 'TranslatedItem');
+
+		$TestModel =& new TranslatedItem();
+		$TestModel->locale = 'deu';
+		$data = array(
+			'slug' => 'new_translated',
+			'title' => array('eng' => 'New title', 'spa' => 'Nuevo leyenda'),
+			'content' => array('eng' => 'New content', 'spa' => 'Nuevo contenido')
+		);
+		$TestModel->create($data);
+		$TestModel->save();
+
+		$TestModel->unbindTranslation();
+		$translations = array('title' => 'Title', 'content' => 'Content');
+		$TestModel->bindTranslation($translations, false);
+		$TestModel->locale = array('eng', 'spa');
+
+		$result = $TestModel->read();
+		$expected = array(
+			'TranslatedItem' => array('id' => 4, 'slug' => 'new_translated', 'locale' => 'eng', 'title' => 'New title', 'content' => 'New content'),
+			'Title' => array(
+				array('id' => 21, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 4, 'field' => 'title', 'content' => 'New title'),
+				array('id' => 22, 'locale' => 'spa', 'model' => 'TranslatedItem', 'foreign_key' => 4, 'field' => 'title', 'content' => 'Nuevo leyenda')
+			),
+			'Content' => array(
+				array('id' => 19, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 4, 'field' => 'content', 'content' => 'New content'),
+				array('id' => 20, 'locale' => 'spa', 'model' => 'TranslatedItem', 'foreign_key' => 4, 'field' => 'content', 'content' => 'Nuevo contenido')
+			)
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testMultipleUpdate method
+ *
+ * @access public
+ * @return void
+ */
+	function testMultipleUpdate() {
+		$this->loadFixtures('Translate', 'TranslatedItem');
+
+		$TestModel =& new TranslatedItem();
+		$TestModel->locale = 'eng';
+		$TestModel->validate['title'] = 'notEmpty';
+		$data = array('TranslatedItem' => array(
+			'id' => 1,
+			'title' => array('eng' => 'New Title #1', 'deu' => 'Neue Titel #1', 'cze' => 'Novy Titulek #1'),
+			'content' => array('eng' => 'New Content #1', 'deu' => 'Neue Inhalt #1', 'cze' => 'Novy Obsah #1')
+		));
+		$TestModel->create();
+		$TestModel->save($data);
+
+		$TestModel->unbindTranslation();
+		$translations = array('title' => 'Title', 'content' => 'Content');
+		$TestModel->bindTranslation($translations, false);
+		$result = $TestModel->read(null, 1);
+		$expected = array(
+			'TranslatedItem' => array('id' => '1', 'slug' => 'first_translated', 'locale' => 'eng', 'title' => 'New Title #1', 'content' => 'New Content #1'),
+			'Title' => array(
+				array('id' => 1, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'title', 'content' => 'New Title #1'),
+				array('id' => 3, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Neue Titel #1'),
+				array('id' => 5, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Novy Titulek #1')
+			),
+			'Content' => array(
+				array('id' => 2, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'content', 'content' => 'New Content #1'),
+				array('id' => 4, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'content', 'content' => 'Neue Inhalt #1'),
+				array('id' => 6, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'content', 'content' => 'Novy Obsah #1')
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$TestModel->unbindTranslation($translations);
+		$TestModel->bindTranslation(array('title', 'content'), false);
+	}
+
+/**
+ * testMixedCreateUpdateWithArrayLocale method
+ *
+ * @access public
+ * @return void
+ */
+	function testMixedCreateUpdateWithArrayLocale() {
+		$this->loadFixtures('Translate', 'TranslatedItem');
+
+		$TestModel =& new TranslatedItem();
+		$TestModel->locale = array('cze', 'deu');
+		$data = array('TranslatedItem' => array(
+			'id' => 1,
+			'title' => array('eng' => 'Updated Title #1', 'spa' => 'Nuevo leyenda #1'),
+			'content' => 'Upraveny obsah #1'
+		));
+		$TestModel->create();
+		$TestModel->save($data);
+
+		$TestModel->unbindTranslation();
+		$translations = array('title' => 'Title', 'content' => 'Content');
+		$TestModel->bindTranslation($translations, false);
+		$result = $TestModel->read(null, 1);
+		$expected = array(
+			'TranslatedItem' => array('id' => 1, 'slug' => 'first_translated', 'locale' => 'cze', 'title' => 'Titulek #1', 'content' => 'Upraveny obsah #1'),
+			'Title' => array(
+				array('id' => 1, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Updated Title #1'),
+				array('id' => 3, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Titel #1'),
+				array('id' => 5, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Titulek #1'),
+				array('id' => 19, 'locale' => 'spa', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Nuevo leyenda #1')
+			),
+			'Content' => array(
+				array('id' => 2, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'content', 'content' => 'Content #1'),
+				array('id' => 4, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'content', 'content' => 'Inhalt #1'),
+				array('id' => 6, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'content', 'content' => 'Upraveny obsah #1')
+			)
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testValidation method
+ *
+ * @access public
+ * @return void
+ */
+	function testValidation() {
+		$this->loadFixtures('Translate', 'TranslatedItem');
+
+		$TestModel =& new TranslatedItem();
+		$TestModel->locale = 'eng';
+		$TestModel->validate['title'] = '/Only this title/';
+		$data = array('TranslatedItem' => array(
+			'id' => 1,
+			'title' => array('eng' => 'New Title #1', 'deu' => 'Neue Titel #1', 'cze' => 'Novy Titulek #1'),
+			'content' => array('eng' => 'New Content #1', 'deu' => 'Neue Inhalt #1', 'cze' => 'Novy Obsah #1')
+		));
+		$TestModel->create();
+		$this->assertFalse($TestModel->save($data));
+		$this->assertEqual($TestModel->validationErrors['title'], 'This field cannot be left blank');
+
+		$TestModel->locale = 'eng';
+		$TestModel->validate['title'] = '/Only this title/';
+		$data = array('TranslatedItem' => array(
+			'id' => 1,
+			'title' => array('eng' => 'Only this title', 'deu' => 'Neue Titel #1', 'cze' => 'Novy Titulek #1'),
+			'content' => array('eng' => 'New Content #1', 'deu' => 'Neue Inhalt #1', 'cze' => 'Novy Obsah #1')
+		));
+		$TestModel->create();
+		$this->assertTrue($TestModel->save($data));
+	}
+
+/**
+ * testAttachDetach method
+ *
+ * @access public
+ * @return void
+ */
+	function testAttachDetach() {
+		$this->loadFixtures('Translate', 'TranslatedItem');
+
+		$TestModel =& new TranslatedItem();
+		$Behavior =& $this->Model->Behaviors->Translate;
+
+		$TestModel->unbindTranslation();
+		$translations = array('title' => 'Title', 'content' => 'Content');
+		$TestModel->bindTranslation($translations, false);
+
+		$result = array_keys($TestModel->hasMany);
+		$expected = array('Title', 'Content');
+		$this->assertEqual($result, $expected);
+
+		$TestModel->Behaviors->detach('Translate');
+		$result = array_keys($TestModel->hasMany);
+		$expected = array();
+		$this->assertEqual($result, $expected);
+
+		$result = isset($TestModel->Behaviors->Translate);
+		$this->assertFalse($result);
+
+		$result = isset($Behavior->settings[$TestModel->alias]);
+		$this->assertFalse($result);
+
+		$result = isset($Behavior->runtime[$TestModel->alias]);
+		$this->assertFalse($result);
+
+		$TestModel->Behaviors->attach('Translate', array('title' => 'Title', 'content' => 'Content'));
+		$result = array_keys($TestModel->hasMany);
+		$expected = array('Title', 'Content');
+		$this->assertEqual($result, $expected);
+
+		$result = isset($TestModel->Behaviors->Translate);
+		$this->assertTrue($result);
+
+		$Behavior = $TestModel->Behaviors->Translate;
+
+		$result = isset($Behavior->settings[$TestModel->alias]);
+		$this->assertTrue($result);
+
+		$result = isset($Behavior->runtime[$TestModel->alias]);
+		$this->assertTrue($result);
+	}
+
+/**
+ * testAnotherTranslateTable method
+ *
+ * @access public
+ * @return void
+ */
+	function testAnotherTranslateTable() {
+		$this->loadFixtures('Translate', 'TranslatedItem', 'TranslateTable');
+
+		$TestModel =& new TranslatedItemWithTable();
+		$TestModel->locale = 'eng';
+		$result = $TestModel->read(null, 1);
+		$expected = array(
+			'TranslatedItemWithTable' => array(
+				'id' => 1,
+				'slug' => 'first_translated',
+				'locale' => 'eng',
+				'title' => 'Another Title #1',
+				'content' => 'Another Content #1'
+			)
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testTranslateWithAssociations method
+ *
+ * @access public
+ * @return void
+ */
+	function testTranslateWithAssociations() {
+		$this->loadFixtures('TranslateArticle', 'TranslatedArticle', 'User', 'Comment', 'ArticlesTag', 'Tag');
+
+		$TestModel =& new TranslatedArticle();
+		$TestModel->locale = 'eng';
+		$recursive = $TestModel->recursive;
+
+		$result = $TestModel->read(null, 1);
+		$expected = array(
+			'TranslatedArticle' => array(
+				'id' => 1,
+				'user_id' => 1,
+				'published' => 'Y',
+				'created' => '2007-03-18 10:39:23',
+				'updated' => '2007-03-18 10:41:31',
+				'locale' => 'eng',
+				'title' => 'Title (eng) #1',
+				'body' => 'Body (eng) #1'
+			),
+			'User' => array(
+				'id' => 1,
+				'user' => 'mariano',
+				'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+				'created' => '2007-03-17 01:16:23',
+            	'updated' => '2007-03-17 01:18:31'
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('all', array('recursive' => -1));
+		$expected = array(
+			array(
+				'TranslatedArticle' => array(
+					'id' => 1,
+					'user_id' => 1,
+					'published' => 'Y',
+					'created' => '2007-03-18 10:39:23',
+					'updated' => '2007-03-18 10:41:31',
+					'locale' => 'eng',
+					'title' => 'Title (eng) #1',
+					'body' => 'Body (eng) #1'
+				)
+			),
+			array(
+				'TranslatedArticle' => array(
+					'id' => 2,
+					'user_id' => 3,
+					'published' => 'Y',
+					'created' => '2007-03-18 10:41:23',
+					'updated' => '2007-03-18 10:43:31',
+					'locale' => 'eng',
+					'title' => 'Title (eng) #2',
+					'body' => 'Body (eng) #2'
+				)
+			),
+			array(
+				'TranslatedArticle' => array(
+					'id' => 3,
+					'user_id' => 1,
+					'published' => 'Y',
+					'created' => '2007-03-18 10:43:23',
+					'updated' => '2007-03-18 10:45:31',
+					'locale' => 'eng',
+					'title' => 'Title (eng) #3',
+					'body' => 'Body (eng) #3'
+				)
+			)
+		);
+		$this->assertEqual($result, $expected);
+		$this->assertEqual($TestModel->recursive, $recursive);
+
+		$TestModel->recursive = -1;
+		$result = $TestModel->read(null, 1);
+		$expected = array(
+			'TranslatedArticle' => array(
+				'id' => 1,
+				'user_id' => 1,
+				'published' => 'Y',
+				'created' => '2007-03-18 10:39:23',
+				'updated' => '2007-03-18 10:41:31',
+				'locale' => 'eng',
+				'title' => 'Title (eng) #1',
+				'body' => 'Body (eng) #1'
+			)
+		);
+		$this->assertEqual($result, $expected);
+	}
+/**
+ * testTranslateTableWithPrefix method
+ * Tests that is possible to have a translation model with a custom tablePrefix
+ *
+ * @access public
+ * @return void
+ */
+	function testTranslateTableWithPrefix() {
+		$this->loadFixtures('TranslateWithPrefix', 'TranslatedItem');
+		$TestModel =& new TranslatedItem2;
+		$TestModel->locale = 'eng';
+		$result = $TestModel->read(null, 1);
+		$expected = array('TranslatedItem' => array(
+			'id' => 1,
+			'slug' => 'first_translated',
+			'locale' => 'eng',
+			'content' => 'Content #1',
+			'title' => 'Title #1'
+		));
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * Test infinite loops not occuring with unbindTranslation()
+ *
+ * @return void
+ */
+	function testUnbindTranslationInfinteLoop() {
+		$this->loadFixtures('Translate', 'TranslatedItem');
+
+		$TestModel =& new TranslatedItem();
+		$TestModel->Behaviors->detach('Translate');
+		$TestModel->actsAs = array();
+		$TestModel->Behaviors->attach('Translate');
+		$TestModel->bindTranslation(array('title', 'content'), true);
+		$result = $TestModel->unbindTranslation();
+
+		$this->assertFalse($result);
+	}
+
+}

Added: trunk/src/Web/cake/tests/cases/libs/model/behaviors/tree.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/model/behaviors/tree.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/model/behaviors/tree.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,1932 @@
+<?php
+/**
+ * TreeBehaviorTest file
+ *
+ * Holds several Test Cases
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.behaviors
+ * @since         CakePHP(tm) v 1.2.0.5330
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', array('AppModel', 'Model'));
+require_once(dirname(dirname(__FILE__)) . DS . 'models.php');
+
+/**
+ * NumberTreeTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.behaviors
+ */
+class NumberTreeTest extends CakeTestCase {
+
+/**
+ * settings property
+ *
+ * @var array
+ * @access public
+ */
+	var $settings = array(
+		'modelClass' => 'NumberTree',
+		'leftField' => 'lft',
+		'rightField' => 'rght',
+		'parentField' => 'parent_id'
+	);
+
+/**
+ * fixtures property
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array('core.number_tree');
+
+/**
+ * testInitialize method
+ *
+ * @access public
+ * @return void
+ */
+	function testInitialize() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+
+		$result = $this->Tree->find('count');
+		$this->assertEqual($result, 7);
+
+		$validTree = $this->Tree->verify();
+		$this->assertIdentical($validTree, true);
+	}
+
+/**
+ * testDetectInvalidLeft method
+ *
+ * @access public
+ * @return void
+ */
+	function testDetectInvalidLeft() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+
+		$result = $this->Tree->findByName('1.1');
+
+		$save[$modelClass]['id'] = $result[$modelClass]['id'];
+		$save[$modelClass][$leftField] = 0;
+
+		$this->Tree->save($save);
+		$result = $this->Tree->verify();
+		$this->assertNotIdentical($result, true);
+
+		$result = $this->Tree->recover();
+		$this->assertIdentical($result, true);
+
+		$result = $this->Tree->verify();
+		$this->assertIdentical($result, true);
+	}
+
+/**
+ * testDetectInvalidRight method
+ *
+ * @access public
+ * @return void
+ */
+	function testDetectInvalidRight() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+
+		$result = $this->Tree->findByName('1.1');
+
+		$save[$modelClass]['id'] = $result[$modelClass]['id'];
+		$save[$modelClass][$rightField] = 0;
+
+		$this->Tree->save($save);
+		$result = $this->Tree->verify();
+		$this->assertNotIdentical($result, true);
+
+		$result = $this->Tree->recover();
+		$this->assertIdentical($result, true);
+
+		$result = $this->Tree->verify();
+		$this->assertIdentical($result, true);
+	}
+
+/**
+ * testDetectInvalidParent method
+ *
+ * @access public
+ * @return void
+ */
+	function testDetectInvalidParent() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+
+		$result = $this->Tree->findByName('1.1');
+
+		// Bypass behavior and any other logic
+		$this->Tree->updateAll(array($parentField => null), array('id' => $result[$modelClass]['id']));
+
+		$result = $this->Tree->verify();
+		$this->assertNotIdentical($result, true);
+
+		$result = $this->Tree->recover();
+		$this->assertIdentical($result, true);
+
+		$result = $this->Tree->verify();
+		$this->assertIdentical($result, true);
+	}
+
+/**
+ * testDetectNoneExistantParent method
+ *
+ * @access public
+ * @return void
+ */
+	function testDetectNoneExistantParent() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+
+		$result = $this->Tree->findByName('1.1');
+		$this->Tree->updateAll(array($parentField => 999999), array('id' => $result[$modelClass]['id']));
+
+		$result = $this->Tree->verify();
+		$this->assertNotIdentical($result, true);
+
+		$result = $this->Tree->recover('MPTT');
+		$this->assertIdentical($result, true);
+
+		$result = $this->Tree->verify();
+		$this->assertIdentical($result, true);
+	}
+
+/**
+ * testRecoverUsingParentMode method
+ *
+ * @return void
+ */
+	public function testRecoverUsingParentMode() {
+		extract($this->settings);
+		$this->Tree = new $modelClass();
+		$this->Tree->Behaviors->disable('Tree');
+
+		$this->Tree->save(array('parent_id' => null, 'name' => 'Main', $parentField => null, $leftField => 0, $rightField => 0));
+		$node1	= $this->Tree->id;
+
+		$this->Tree->create(false);
+		$this->Tree->save(array('parent_id' => null, 'name' => 'About Us', $parentField => $node1, $leftField => 0, $rightField => 0));
+		$node11	= $this->Tree->id;
+		$this->Tree->create(false);
+		$this->Tree->save(array('parent_id' => null, 'name' => 'Programs', $parentField => $node1, $leftField => 0, $rightField => 0));
+		$node12	= $this->Tree->id;
+		$this->Tree->create(false);
+		$this->Tree->save(array('parent_id' => null, 'name' => 'Mission and History', $parentField => $node11, $leftField => 0, $rightField => 0));
+		$this->Tree->create(false);
+		$this->Tree->save(array('parent_id' => null, 'name' => 'Overview', $parentField => $node12, $leftField => 0, $rightField => 0));
+
+		$this->Tree->Behaviors->enable('Tree');
+
+		$result = $this->Tree->verify();
+		$this->assertNotIdentical($result, false);
+
+		$result = $this->Tree->recover();
+		$this->assertTrue($result);
+
+		$result = $this->Tree->verify();
+		$this->assertTrue($result);
+
+		$result = $this->Tree->find('first', array(
+			'fields' => array('name', $parentField, $leftField, $rightField),
+			'conditions' => array('name' => 'Main'),
+			'recursive' => -1
+		));
+		$expected = array(
+			$modelClass => array(
+				'name' => 'Main',
+				$parentField => null,
+				$leftField => 1,
+				$rightField => 10
+			)
+		);
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * testRecoverFromMissingParent method
+ *
+ * @access public
+ * @return void
+ */
+	function testRecoverFromMissingParent() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+
+		$result = $this->Tree->findByName('1.1');
+		$this->Tree->updateAll(array($parentField => 999999), array('id' => $result[$modelClass]['id']));
+
+		$result = $this->Tree->verify();
+		$this->assertNotIdentical($result, true);
+
+		$result = $this->Tree->recover();
+		$this->assertIdentical($result, true);
+
+		$result = $this->Tree->verify();
+		$this->assertIdentical($result, true);
+	}
+
+/**
+ * testDetectInvalidParents method
+ *
+ * @access public
+ * @return void
+ */
+	function testDetectInvalidParents() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+
+		$this->Tree->updateAll(array($parentField => null));
+
+		$result = $this->Tree->verify();
+		$this->assertNotIdentical($result, true);
+
+		$result = $this->Tree->recover();
+		$this->assertIdentical($result, true);
+
+		$result = $this->Tree->verify();
+		$this->assertIdentical($result, true);
+	}
+
+/**
+ * testDetectInvalidLftsRghts method
+ *
+ * @access public
+ * @return void
+ */
+	function testDetectInvalidLftsRghts() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+
+		$this->Tree->updateAll(array($leftField => 0, $rightField => 0));
+
+		$result = $this->Tree->verify();
+		$this->assertNotIdentical($result, true);
+
+		$this->Tree->recover();
+
+		$result = $this->Tree->verify();
+		$this->assertIdentical($result, true);
+	}
+
+/**
+ * Reproduces a situation where a single node has lft= rght, and all other lft and rght fields follow sequentially
+ *
+ * @access public
+ * @return void
+ */
+	function testDetectEqualLftsRghts() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(1, 3);
+
+		$result = $this->Tree->findByName('1.1');
+		$this->Tree->updateAll(array($rightField => $result[$modelClass][$leftField]), array('id' => $result[$modelClass]['id']));
+		$this->Tree->updateAll(array($leftField => $this->Tree->escapeField($leftField) . ' -1'),
+			array($leftField . ' >' => $result[$modelClass][$leftField]));
+		$this->Tree->updateAll(array($rightField => $this->Tree->escapeField($rightField) . ' -1'),
+			array($rightField . ' >' => $result[$modelClass][$leftField]));
+
+		$result = $this->Tree->verify();
+		$this->assertNotIdentical($result, true);
+
+		$result = $this->Tree->recover();
+		$this->assertTrue($result);
+
+		$result = $this->Tree->verify();
+		$this->assertTrue($result);
+	}
+
+/**
+ * testAddOrphan method
+ *
+ * @access public
+ * @return void
+ */
+	function testAddOrphan() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+
+		$this->Tree->save(array($modelClass => array('name' => 'testAddOrphan', $parentField => null)));
+		$result = $this->Tree->find(null, array('name', $parentField), $modelClass . '.' . $leftField . ' desc');
+		$expected = array($modelClass => array('name' => 'testAddOrphan', $parentField => null));
+		$this->assertEqual($result, $expected);
+
+		$validTree = $this->Tree->verify();
+		$this->assertIdentical($validTree, true);
+	}
+
+/**
+ * testAddMiddle method
+ *
+ * @access public
+ * @return void
+ */
+	function testAddMiddle() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+
+		$data= $this->Tree->find(array($modelClass . '.name' => '1.1'), array('id'));
+		$initialCount = $this->Tree->find('count');
+
+		$this->Tree->create();
+		$result = $this->Tree->save(array($modelClass => array('name' => 'testAddMiddle', $parentField => $data[$modelClass]['id'])));
+		$expected = array_merge(array($modelClass => array('name' => 'testAddMiddle', $parentField => '2')), $result);
+		$this->assertIdentical($result, $expected);
+
+		$laterCount = $this->Tree->find('count');
+
+		$laterCount = $this->Tree->find('count');
+		$this->assertEqual($initialCount + 1, $laterCount);
+
+		$children = $this->Tree->children($data[$modelClass]['id'], true, array('name'));
+		$expects = array(array($modelClass => array('name' => '1.1.1')),
+			array($modelClass => array('name' => '1.1.2')),
+			array($modelClass => array('name' => 'testAddMiddle')));
+		$this->assertIdentical($children, $expects);
+
+		$validTree = $this->Tree->verify();
+		$this->assertIdentical($validTree, true);
+	}
+
+/**
+ * testAddInvalid method
+ *
+ * @access public
+ * @return void
+ */
+	function testAddInvalid() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+		$this->Tree->id = null;
+
+		$initialCount = $this->Tree->find('count');
+		//$this->expectError('Trying to save a node under a none-existant node in TreeBehavior::beforeSave');
+
+		$saveSuccess = $this->Tree->save(array($modelClass => array('name' => 'testAddInvalid', $parentField => 99999)));
+		$this->assertIdentical($saveSuccess, false);
+
+		$laterCount = $this->Tree->find('count');
+		$this->assertIdentical($initialCount, $laterCount);
+
+		$validTree = $this->Tree->verify();
+		$this->assertIdentical($validTree, true);
+	}
+
+/**
+ * testAddNotIndexedByModel method
+ *
+ * @access public
+ * @return void
+ */
+	function testAddNotIndexedByModel() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+
+		$this->Tree->save(array('name' => 'testAddNotIndexed', $parentField => null));
+		$result = $this->Tree->find(null, array('name', $parentField), $modelClass . '.' . $leftField . ' desc');
+		$expected = array($modelClass => array('name' => 'testAddNotIndexed', $parentField => null));
+		$this->assertEqual($result, $expected);
+
+		$validTree = $this->Tree->verify();
+		$this->assertIdentical($validTree, true);
+}
+
+/**
+ * testMovePromote method
+ *
+ * @access public
+ * @return void
+ */
+	function testMovePromote() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+		$this->Tree->id = null;
+
+		$parent = $this->Tree->find(array($modelClass . '.name' => '1. Root'));
+		$parent_id = $parent[$modelClass]['id'];
+
+		$data = $this->Tree->find(array($modelClass . '.name' => '1.1.1'), array('id'));
+		$this->Tree->id= $data[$modelClass]['id'];
+		$this->Tree->saveField($parentField, $parent_id);
+		$direct = $this->Tree->children($parent_id, true, array('id', 'name', $parentField, $leftField, $rightField));
+		$expects = array(array($modelClass => array('id' => 2, 'name' => '1.1', $parentField => 1, $leftField => 2, $rightField => 5)),
+			array($modelClass => array('id' => 5, 'name' => '1.2', $parentField => 1, $leftField => 6, $rightField => 11)),
+			array($modelClass => array('id' => 3, 'name' => '1.1.1', $parentField => 1, $leftField => 12, $rightField => 13)));
+		$this->assertEqual($direct, $expects);
+		$validTree = $this->Tree->verify();
+		$this->assertIdentical($validTree, true);
+	}
+
+/**
+ * testMoveWithWhitelist method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoveWithWhitelist() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+		$this->Tree->id = null;
+
+		$parent = $this->Tree->find(array($modelClass . '.name' => '1. Root'));
+		$parent_id = $parent[$modelClass]['id'];
+
+		$data = $this->Tree->find(array($modelClass . '.name' => '1.1.1'), array('id'));
+		$this->Tree->id = $data[$modelClass]['id'];
+		$this->Tree->whitelist = array($parentField, 'name', 'description');
+		$this->Tree->saveField($parentField, $parent_id);
+
+		$result = $this->Tree->children($parent_id, true, array('id', 'name', $parentField, $leftField, $rightField));
+		$expected = array(array($modelClass => array('id' => 2, 'name' => '1.1', $parentField => 1, $leftField => 2, $rightField => 5)),
+			array($modelClass => array('id' => 5, 'name' => '1.2', $parentField => 1, $leftField => 6, $rightField => 11)),
+			array($modelClass => array('id' => 3, 'name' => '1.1.1', $parentField => 1, $leftField => 12, $rightField => 13)));
+		$this->assertEqual($result, $expected);
+		$this->assertTrue($this->Tree->verify());
+	}
+
+/**
+ * testInsertWithWhitelist method
+ *
+ * @access public
+ * @return void
+ */
+	function testInsertWithWhitelist() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+
+		$this->Tree->whitelist = array('name', $parentField);
+		$this->Tree->save(array($modelClass => array('name' => 'testAddOrphan', $parentField => null)));
+		$result = $this->Tree->findByName('testAddOrphan', array('name', $parentField, $leftField, $rightField));
+		$expected = array('name' => 'testAddOrphan', $parentField => null, $leftField => '15', $rightField => 16);
+		$this->assertEqual($result[$modelClass], $expected);
+		$this->assertIdentical($this->Tree->verify(), true);
+	}
+
+/**
+ * testMoveBefore method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoveBefore() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+		$this->Tree->id = null;
+
+		$parent = $this->Tree->find(array($modelClass . '.name' => '1.1'));
+		$parent_id = $parent[$modelClass]['id'];
+
+		$data= $this->Tree->find(array($modelClass . '.name' => '1.2'), array('id'));
+		$this->Tree->id = $data[$modelClass]['id'];
+		$this->Tree->saveField($parentField, $parent_id);
+
+		$result = $this->Tree->children($parent_id, true, array('name'));
+		$expects = array(array($modelClass => array('name' => '1.1.1')),
+			array($modelClass => array('name' => '1.1.2')),
+			array($modelClass => array('name' => '1.2')));
+		$this->assertEqual($result, $expects);
+
+		$validTree = $this->Tree->verify();
+		$this->assertIdentical($validTree, true);
+	}
+
+/**
+ * testMoveAfter method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoveAfter() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+		$this->Tree->id = null;
+
+		$parent = $this->Tree->find(array($modelClass . '.name' => '1.2'));
+		$parent_id = $parent[$modelClass]['id'];
+
+		$data= $this->Tree->find(array($modelClass . '.name' => '1.1'), array('id'));
+		$this->Tree->id = $data[$modelClass]['id'];
+		$this->Tree->saveField($parentField, $parent_id);
+
+		$result = $this->Tree->children($parent_id, true, array('name'));
+		$expects = array(array($modelClass => array('name' => '1.2.1')),
+			array($modelClass => array('name' => '1.2.2')),
+			array($modelClass => array('name' => '1.1')));
+		$this->assertEqual($result, $expects);
+
+		$validTree = $this->Tree->verify();
+		$this->assertIdentical($validTree, true);
+	}
+
+/**
+ * testMoveDemoteInvalid method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoveDemoteInvalid() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+		$this->Tree->id = null;
+
+		$parent = $this->Tree->find(array($modelClass . '.name' => '1. Root'));
+		$parent_id = $parent[$modelClass]['id'];
+
+		$data = $this->Tree->find(array($modelClass . '.name' => '1.1.1'), array('id'));
+
+		$expects = $this->Tree->find('all');
+		$before = $this->Tree->read(null, $data[$modelClass]['id']);
+
+		$this->Tree->id = $parent_id;
+		//$this->expectError('Trying to save a node under itself in TreeBehavior::beforeSave');
+		$this->Tree->saveField($parentField, $data[$modelClass]['id']);
+
+		$results = $this->Tree->find('all');
+		$after = $this->Tree->read(null, $data[$modelClass]['id']);
+
+		$this->assertEqual($results, $expects);
+		$this->assertEqual($before, $after);
+
+		$validTree = $this->Tree->verify();
+		$this->assertIdentical($validTree, true);
+	}
+
+/**
+ * testMoveInvalid method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoveInvalid() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+		$this->Tree->id = null;
+
+		$initialCount = $this->Tree->find('count');
+		$data= $this->Tree->findByName('1.1');
+
+		//$this->expectError('Trying to save a node under a none-existant node in TreeBehavior::beforeSave');
+		$this->Tree->id = $data[$modelClass]['id'];
+		$this->Tree->saveField($parentField, 999999);
+
+		//$this->assertIdentical($saveSuccess, false);
+		$laterCount = $this->Tree->find('count');
+		$this->assertIdentical($initialCount, $laterCount);
+
+		$validTree = $this->Tree->verify();
+		$this->assertIdentical($validTree, true);
+	}
+
+/**
+ * testMoveSelfInvalid method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoveSelfInvalid() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+		$this->Tree->id = null;
+
+		$initialCount = $this->Tree->find('count');
+		$data= $this->Tree->findByName('1.1');
+
+		//$this->expectError('Trying to set a node to be the parent of itself in TreeBehavior::beforeSave');
+		$this->Tree->id = $data[$modelClass]['id'];
+		$saveSuccess = $this->Tree->saveField($parentField, $this->Tree->id);
+
+		$this->assertIdentical($saveSuccess, false);
+		$laterCount = $this->Tree->find('count');
+		$this->assertIdentical($initialCount, $laterCount);
+
+		$validTree = $this->Tree->verify();
+		$this->assertIdentical($validTree, true);
+	}
+
+/**
+ * testMoveUpSuccess method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoveUpSuccess() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+
+		$data = $this->Tree->find(array($modelClass . '.name' => '1.2'), array('id'));
+		$this->Tree->moveUp($data[$modelClass]['id']);
+
+		$parent = $this->Tree->findByName('1. Root', array('id'));
+		$this->Tree->id = $parent[$modelClass]['id'];
+		$result = $this->Tree->children(null, true, array('name'));
+		$expected = array(array($modelClass => array('name' => '1.2', )),
+			array($modelClass => array('name' => '1.1', )));
+		$this->assertIdentical($result, $expected);
+	}
+
+/**
+ * testMoveUpFail method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoveUpFail() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+
+		$data = $this->Tree->find(array($modelClass . '.name' => '1.1'));
+
+		$this->Tree->moveUp($data[$modelClass]['id']);
+
+		$parent = $this->Tree->findByName('1. Root', array('id'));
+		$this->Tree->id = $parent[$modelClass]['id'];
+		$result = $this->Tree->children(null, true, array('name'));
+		$expected = array(array($modelClass => array('name' => '1.1', )),
+			array($modelClass => array('name' => '1.2', )));
+		$this->assertIdentical($result, $expected);
+	}
+
+/**
+ * testMoveUp2 method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoveUp2() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(1, 10);
+
+		$data = $this->Tree->find(array($modelClass . '.name' => '1.5'), array('id'));
+		$this->Tree->moveUp($data[$modelClass]['id'], 2);
+
+		$parent = $this->Tree->findByName('1. Root', array('id'));
+		$this->Tree->id = $parent[$modelClass]['id'];
+		$result = $this->Tree->children(null, true, array('name'));
+		$expected = array(
+			array($modelClass => array('name' => '1.1', )),
+			array($modelClass => array('name' => '1.2', )),
+			array($modelClass => array('name' => '1.5', )),
+			array($modelClass => array('name' => '1.3', )),
+			array($modelClass => array('name' => '1.4', )),
+			array($modelClass => array('name' => '1.6', )),
+			array($modelClass => array('name' => '1.7', )),
+			array($modelClass => array('name' => '1.8', )),
+			array($modelClass => array('name' => '1.9', )),
+			array($modelClass => array('name' => '1.10', )));
+		$this->assertIdentical($result, $expected);
+	}
+
+/**
+ * testMoveUpFirst method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoveUpFirst() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(1, 10);
+
+		$data = $this->Tree->find(array($modelClass . '.name' => '1.5'), array('id'));
+		$this->Tree->moveUp($data[$modelClass]['id'], true);
+
+		$parent = $this->Tree->findByName('1. Root', array('id'));
+		$this->Tree->id = $parent[$modelClass]['id'];
+		$result = $this->Tree->children(null, true, array('name'));
+		$expected = array(
+			array($modelClass => array('name' => '1.5', )),
+			array($modelClass => array('name' => '1.1', )),
+			array($modelClass => array('name' => '1.2', )),
+			array($modelClass => array('name' => '1.3', )),
+			array($modelClass => array('name' => '1.4', )),
+			array($modelClass => array('name' => '1.6', )),
+			array($modelClass => array('name' => '1.7', )),
+			array($modelClass => array('name' => '1.8', )),
+			array($modelClass => array('name' => '1.9', )),
+			array($modelClass => array('name' => '1.10', )));
+		$this->assertIdentical($result, $expected);
+	}
+
+/**
+ * testMoveDownSuccess method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoveDownSuccess() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+
+		$data = $this->Tree->find(array($modelClass . '.name' => '1.1'), array('id'));
+		$this->Tree->moveDown($data[$modelClass]['id']);
+
+		$parent = $this->Tree->findByName('1. Root', array('id'));
+		$this->Tree->id = $parent[$modelClass]['id'];
+		$result = $this->Tree->children(null, true, array('name'));
+		$expected = array(array($modelClass => array('name' => '1.2', )),
+			array($modelClass => array('name' => '1.1', )));
+		$this->assertIdentical($result, $expected);
+	}
+
+/**
+ * testMoveDownFail method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoveDownFail() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+
+		$data = $this->Tree->find(array($modelClass . '.name' => '1.2'));
+		$this->Tree->moveDown($data[$modelClass]['id']);
+
+		$parent = $this->Tree->findByName('1. Root', array('id'));
+		$this->Tree->id = $parent[$modelClass]['id'];
+		$result = $this->Tree->children(null, true, array('name'));
+		$expected = array(array($modelClass => array('name' => '1.1', )),
+			array($modelClass => array('name' => '1.2', )));
+		$this->assertIdentical($result, $expected);
+	}
+
+/**
+ * testMoveDownLast method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoveDownLast() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(1, 10);
+
+		$data = $this->Tree->find(array($modelClass . '.name' => '1.5'), array('id'));
+		$this->Tree->moveDown($data[$modelClass]['id'], true);
+
+		$parent = $this->Tree->findByName('1. Root', array('id'));
+		$this->Tree->id = $parent[$modelClass]['id'];
+		$result = $this->Tree->children(null, true, array('name'));
+		$expected = array(
+			array($modelClass => array('name' => '1.1', )),
+			array($modelClass => array('name' => '1.2', )),
+			array($modelClass => array('name' => '1.3', )),
+			array($modelClass => array('name' => '1.4', )),
+			array($modelClass => array('name' => '1.6', )),
+			array($modelClass => array('name' => '1.7', )),
+			array($modelClass => array('name' => '1.8', )),
+			array($modelClass => array('name' => '1.9', )),
+			array($modelClass => array('name' => '1.10', )),
+			array($modelClass => array('name' => '1.5', )));
+		$this->assertIdentical($result, $expected);
+	}
+
+/**
+ * testMoveDown2 method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoveDown2() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(1, 10);
+
+		$data = $this->Tree->find(array($modelClass . '.name' => '1.5'), array('id'));
+		$this->Tree->moveDown($data[$modelClass]['id'], 2);
+
+		$parent = $this->Tree->findByName('1. Root', array('id'));
+		$this->Tree->id = $parent[$modelClass]['id'];
+		$result = $this->Tree->children(null, true, array('name'));
+		$expected = array(
+			array($modelClass => array('name' => '1.1', )),
+			array($modelClass => array('name' => '1.2', )),
+			array($modelClass => array('name' => '1.3', )),
+			array($modelClass => array('name' => '1.4', )),
+			array($modelClass => array('name' => '1.6', )),
+			array($modelClass => array('name' => '1.7', )),
+			array($modelClass => array('name' => '1.5', )),
+			array($modelClass => array('name' => '1.8', )),
+			array($modelClass => array('name' => '1.9', )),
+			array($modelClass => array('name' => '1.10', )));
+		$this->assertIdentical($result, $expected);
+	}
+
+/**
+ * testSaveNoMove method
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveNoMove() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(1, 10);
+
+		$data = $this->Tree->find(array($modelClass . '.name' => '1.5'), array('id'));
+		$this->Tree->id = $data[$modelClass]['id'];
+		$this->Tree->saveField('name', 'renamed');
+		$parent = $this->Tree->findByName('1. Root', array('id'));
+		$this->Tree->id = $parent[$modelClass]['id'];
+		$result = $this->Tree->children(null, true, array('name'));
+		$expected = array(
+			array($modelClass => array('name' => '1.1', )),
+			array($modelClass => array('name' => '1.2', )),
+			array($modelClass => array('name' => '1.3', )),
+			array($modelClass => array('name' => '1.4', )),
+			array($modelClass => array('name' => 'renamed', )),
+			array($modelClass => array('name' => '1.6', )),
+			array($modelClass => array('name' => '1.7', )),
+			array($modelClass => array('name' => '1.8', )),
+			array($modelClass => array('name' => '1.9', )),
+			array($modelClass => array('name' => '1.10', )));
+		$this->assertIdentical($result, $expected);
+	}
+
+/**
+ * testMoveToRootAndMoveUp method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoveToRootAndMoveUp() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(1, 1);
+		$data = $this->Tree->find(array($modelClass . '.name' => '1.1'), array('id'));
+		$this->Tree->id = $data[$modelClass]['id'];
+		$this->Tree->save(array($parentField => null));
+
+		$result = $this->Tree->verify();
+		$this->assertIdentical($result, true);
+
+		$this->Tree->moveup();
+
+		$result = $this->Tree->find('all', array('fields' => 'name', 'order' => $modelClass . '.' . $leftField . ' ASC'));
+		$expected = array(array($modelClass => array('name' => '1.1')),
+			array($modelClass => array('name' => '1. Root')));
+		$this->assertIdentical($result, $expected);
+	}
+
+/**
+ * testDelete method
+ *
+ * @access public
+ * @return void
+ */
+	function testDelete() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+
+		$initialCount = $this->Tree->find('count');
+		$result = $this->Tree->findByName('1.1.1');
+
+		$return = $this->Tree->delete($result[$modelClass]['id']);
+		$this->assertEqual($return, true);
+
+		$laterCount = $this->Tree->find('count');
+		$this->assertEqual($initialCount - 1, $laterCount);
+
+		$validTree= $this->Tree->verify();
+		$this->assertIdentical($validTree, true);
+
+		$initialCount = $this->Tree->find('count');
+		$result= $this->Tree->findByName('1.1');
+
+		$return = $this->Tree->delete($result[$modelClass]['id']);
+		$this->assertEqual($return, true);
+
+		$laterCount = $this->Tree->find('count');
+		$this->assertEqual($initialCount - 2, $laterCount);
+
+		$validTree = $this->Tree->verify();
+		$this->assertIdentical($validTree, true);
+	}
+
+/**
+ * testRemove method
+ *
+ * @access public
+ * @return void
+ */
+	function testRemove() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+		$initialCount = $this->Tree->find('count');
+		$result = $this->Tree->findByName('1.1');
+
+		$this->Tree->removeFromTree($result[$modelClass]['id']);
+
+		$laterCount = $this->Tree->find('count');
+		$this->assertEqual($initialCount, $laterCount);
+
+		$children = $this->Tree->children($result[$modelClass][$parentField], true, array('name'));
+		$expects = array(array($modelClass => array('name' => '1.1.1')),
+			array($modelClass => array('name' => '1.1.2')),
+			array($modelClass => array('name' => '1.2')));
+		$this->assertEqual($children, $expects);
+
+		$topNodes = $this->Tree->children(false, true,array('name'));
+		$expects = array(array($modelClass => array('name' => '1. Root')),
+			array($modelClass => array('name' => '1.1')));
+		$this->assertEqual($topNodes, $expects);
+
+		$validTree = $this->Tree->verify();
+		$this->assertIdentical($validTree, true);
+	}
+
+/**
+ * testRemoveLastTopParent method
+ *
+ * @access public
+ * @return void
+ */
+	function testRemoveLastTopParent() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+
+		$initialCount = $this->Tree->find('count');
+		$initialTopNodes = $this->Tree->childCount(false);
+
+		$result = $this->Tree->findByName('1. Root');
+		$this->Tree->removeFromTree($result[$modelClass]['id']);
+
+		$laterCount = $this->Tree->find('count');
+		$laterTopNodes = $this->Tree->childCount(false);
+
+		$this->assertEqual($initialCount, $laterCount);
+		$this->assertEqual($initialTopNodes, $laterTopNodes);
+
+		$topNodes = $this->Tree->children(false, true,array('name'));
+		$expects = array(array($modelClass => array('name' => '1.1')),
+			array($modelClass => array('name' => '1.2')),
+			array($modelClass => array('name' => '1. Root')));
+
+		$this->assertEqual($topNodes, $expects);
+
+		$validTree = $this->Tree->verify();
+		$this->assertIdentical($validTree, true);
+	}
+
+/**
+ * testRemoveNoChildren method
+ *
+ * @return void
+ * @access public
+ */
+	function testRemoveNoChildren() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+		$initialCount = $this->Tree->find('count');
+
+		$result = $this->Tree->findByName('1.1.1');
+		$this->Tree->removeFromTree($result[$modelClass]['id']);
+
+		$laterCount = $this->Tree->find('count');
+		$this->assertEqual($initialCount, $laterCount);
+
+		$nodes = $this->Tree->find('list', array('order' => $leftField));
+		$expects = array(
+			1 => '1. Root',
+			2 => '1.1',
+			4 => '1.1.2',
+			5 => '1.2',
+			6 => '1.2.1',
+			7 => '1.2.2',
+			3 => '1.1.1',
+		);
+
+		$this->assertEqual($nodes, $expects);
+
+		$validTree = $this->Tree->verify();
+		$this->assertIdentical($validTree, true);
+	}
+
+/**
+ * testRemoveAndDelete method
+ *
+ * @access public
+ * @return void
+ */
+	function testRemoveAndDelete() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+
+		$initialCount = $this->Tree->find('count');
+		$result = $this->Tree->findByName('1.1');
+
+		$this->Tree->removeFromTree($result[$modelClass]['id'], true);
+
+		$laterCount = $this->Tree->find('count');
+		$this->assertEqual($initialCount-1, $laterCount);
+
+		$children = $this->Tree->children($result[$modelClass][$parentField], true, array('name'), $leftField . ' asc');
+		$expects= array(array($modelClass => array('name' => '1.1.1')),
+			array($modelClass => array('name' => '1.1.2')),
+			array($modelClass => array('name' => '1.2')));
+		$this->assertEqual($children, $expects);
+
+		$topNodes = $this->Tree->children(false, true,array('name'));
+		$expects = array(array($modelClass => array('name' => '1. Root')));
+		$this->assertEqual($topNodes, $expects);
+
+		$validTree = $this->Tree->verify();
+		$this->assertIdentical($validTree, true);
+	}
+
+/**
+ * testRemoveAndDeleteNoChildren method
+ *
+ * @return void
+ * @access public
+ */
+	function testRemoveAndDeleteNoChildren() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+		$initialCount = $this->Tree->find('count');
+
+		$result = $this->Tree->findByName('1.1.1');
+		$this->Tree->removeFromTree($result[$modelClass]['id'], true);
+
+		$laterCount = $this->Tree->find('count');
+		$this->assertEqual($initialCount - 1, $laterCount);
+
+		$nodes = $this->Tree->find('list', array('order' => $leftField));
+		$expects = array(
+			1 => '1. Root',
+			2 => '1.1',
+			4 => '1.1.2',
+			5 => '1.2',
+			6 => '1.2.1',
+			7 => '1.2.2',
+		);
+		$this->assertEqual($nodes, $expects);
+
+		$validTree = $this->Tree->verify();
+		$this->assertIdentical($validTree, true);
+	}
+
+/**
+ * testChildren method
+ *
+ * @access public
+ * @return void
+ */
+	function testChildren() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+
+		$data = $this->Tree->find(array($modelClass . '.name' => '1. Root'));
+		$this->Tree->id= $data[$modelClass]['id'];
+
+		$direct = $this->Tree->children(null, true, array('id', 'name', $parentField, $leftField, $rightField));
+		$expects = array(array($modelClass => array('id' => 2, 'name' => '1.1', $parentField => 1, $leftField => 2, $rightField => 7)),
+			array($modelClass => array('id' => 5, 'name' => '1.2', $parentField => 1, $leftField => 8, $rightField => 13)));
+		$this->assertEqual($direct, $expects);
+
+		$total = $this->Tree->children(null, null, array('id', 'name', $parentField, $leftField, $rightField));
+		$expects = array(array($modelClass => array('id' => 2, 'name' => '1.1', $parentField => 1, $leftField => 2, $rightField => 7)),
+			array($modelClass => array('id' => 3, 'name' => '1.1.1', $parentField => 2, $leftField => 3, $rightField => 4)),
+			array($modelClass => array('id' => 4, 'name' => '1.1.2', $parentField => 2, $leftField => 5, $rightField => 6)),
+			array($modelClass => array('id' => 5, 'name' => '1.2', $parentField => 1, $leftField => 8, $rightField => 13)),
+			array($modelClass => array( 'id' => 6, 'name' => '1.2.1', $parentField => 5, $leftField => 9, $rightField => 10)),
+			array($modelClass => array('id' => 7, 'name' => '1.2.2', $parentField => 5, $leftField => 11, $rightField => 12)));
+		$this->assertEqual($total, $expects);
+
+		$this->assertEqual(array(), $this->Tree->children(10000));
+	}
+
+/**
+ * testCountChildren method
+ *
+ * @access public
+ * @return void
+ */
+	function testCountChildren() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+
+		$data = $this->Tree->find(array($modelClass . '.name' => '1. Root'));
+		$this->Tree->id = $data[$modelClass]['id'];
+
+		$direct = $this->Tree->childCount(null, true);
+		$this->assertEqual($direct, 2);
+
+		$total = $this->Tree->childCount();
+		$this->assertEqual($total, 6);
+
+		$this->Tree->read(null, $data[$modelClass]['id']);
+		$id = $this->Tree->field('id', array($modelClass . '.name' => '1.2'));
+		$total = $this->Tree->childCount($id);
+		$this->assertEqual($total, 2);
+	}
+
+/**
+ * testGetParentNode method
+ *
+ * @access public
+ * @return void
+ */
+	function testGetParentNode() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+
+		$data = $this->Tree->find(array($modelClass . '.name' => '1.2.2'));
+		$this->Tree->id= $data[$modelClass]['id'];
+
+		$result = $this->Tree->getparentNode(null, array('name'));
+		$expects = array($modelClass => array('name' => '1.2'));
+		$this->assertIdentical($result, $expects);
+	}
+
+/**
+ * testGetPath method
+ *
+ * @access public
+ * @return void
+ */
+	function testGetPath() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+
+		$data = $this->Tree->find(array($modelClass . '.name' => '1.2.2'));
+		$this->Tree->id= $data[$modelClass]['id'];
+
+		$result = $this->Tree->getPath(null, array('name'));
+		$expects = array(array($modelClass => array('name' => '1. Root')),
+			array($modelClass => array('name' => '1.2')),
+			array($modelClass => array('name' => '1.2.2')));
+		$this->assertIdentical($result, $expects);
+	}
+
+/**
+ * testNoAmbiguousColumn method
+ *
+ * @access public
+ * @return void
+ */
+	function testNoAmbiguousColumn() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->bindModel(array('belongsTo' => array('Dummy' =>
+			array('className' => $modelClass, 'foreignKey' => $parentField, 'conditions' => array('Dummy.id' => null)))), false);
+		$this->Tree->initialize(2, 2);
+
+		$data = $this->Tree->find(array($modelClass . '.name' => '1. Root'));
+		$this->Tree->id= $data[$modelClass]['id'];
+
+		$direct = $this->Tree->children(null, true, array('id', 'name', $parentField, $leftField, $rightField));
+		$expects = array(array($modelClass => array('id' => 2, 'name' => '1.1', $parentField => 1, $leftField => 2, $rightField => 7)),
+			array($modelClass => array('id' => 5, 'name' => '1.2', $parentField => 1, $leftField => 8, $rightField => 13)));
+		$this->assertEqual($direct, $expects);
+
+		$total = $this->Tree->children(null, null, array('id', 'name', $parentField, $leftField, $rightField));
+		$expects = array(
+			array($modelClass => array('id' => 2, 'name' => '1.1', $parentField => 1, $leftField => 2, $rightField => 7)),
+			array($modelClass => array('id' => 3, 'name' => '1.1.1', $parentField => 2, $leftField => 3, $rightField => 4)),
+			array($modelClass => array('id' => 4, 'name' => '1.1.2', $parentField => 2, $leftField => 5, $rightField => 6)),
+			array($modelClass => array('id' => 5, 'name' => '1.2', $parentField => 1, $leftField => 8, $rightField => 13)),
+			array($modelClass => array( 'id' => 6, 'name' => '1.2.1', $parentField => 5, $leftField => 9, $rightField => 10)),
+			array($modelClass => array('id' => 7, 'name' => '1.2.2', $parentField => 5, $leftField => 11, $rightField => 12))
+		);
+		$this->assertEqual($total, $expects);
+	}
+
+/**
+ * testReorderTree method
+ *
+ * @access public
+ * @return void
+ */
+	function testReorderTree() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(3, 3);
+		$nodes = $this->Tree->find('list', array('order' => $leftField));
+
+		$data = $this->Tree->find(array($modelClass . '.name' => '1.1'), array('id'));
+		$this->Tree->moveDown($data[$modelClass]['id']);
+
+		$data = $this->Tree->find(array($modelClass . '.name' => '1.2.1'), array('id'));
+		$this->Tree->moveDown($data[$modelClass]['id']);
+
+		$data = $this->Tree->find(array($modelClass . '.name' => '1.3.2.2'), array('id'));
+		$this->Tree->moveDown($data[$modelClass]['id']);
+
+		$unsortedNodes = $this->Tree->find('list', array('order' => $leftField));
+		$this->assertNotIdentical($nodes, $unsortedNodes);
+
+		$this->Tree->reorder();
+		$sortedNodes = $this->Tree->find('list', array('order' => $leftField));
+		$this->assertIdentical($nodes, $sortedNodes);
+	}
+
+/**
+ * test reordering large-ish trees with cacheQueries = true.
+ * This caused infinite loops when moving down elements as stale data is returned
+ * from the memory cache
+ *
+ * @access public
+ * @return void
+ */
+	function testReorderBigTreeWithQueryCaching() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 10);
+
+		$original = $this->Tree->cacheQueries;
+		$this->Tree->cacheQueries = true;
+		$this->Tree->reorder(array('field' => 'name', 'direction' => 'DESC'));
+		$this->assertTrue($this->Tree->cacheQueries, 'cacheQueries was not restored after reorder(). %s');
+		$this->Tree->cacheQueries = $original;
+	}
+/**
+ * testGenerateTreeListWithSelfJoin method
+ *
+ * @access public
+ * @return void
+ */
+	function testGenerateTreeListWithSelfJoin() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->bindModel(array('belongsTo' => array('Dummy' =>
+			array('className' => $modelClass, 'foreignKey' => $parentField, 'conditions' => array('Dummy.id' => null)))), false);
+		$this->Tree->initialize(2, 2);
+
+		$result = $this->Tree->generateTreeList();
+		$expected = array(1 => '1. Root', 2 => '_1.1', 3 => '__1.1.1', 4 => '__1.1.2', 5 => '_1.2', 6 => '__1.2.1', 7 => '__1.2.2');
+		$this->assertIdentical($result, $expected);
+	}
+
+/**
+ * testArraySyntax method
+ *
+ * @access public
+ * @return void
+ */
+	function testArraySyntax() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(3, 3);
+		$this->assertIdentical($this->Tree->childCount(2), $this->Tree->childCount(array('id' => 2)));
+		$this->assertIdentical($this->Tree->getParentNode(2), $this->Tree->getParentNode(array('id' => 2)));
+		$this->assertIdentical($this->Tree->getPath(4), $this->Tree->getPath(array('id' => 4)));
+	}
+}
+
+/**
+ * ScopedTreeTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.behaviors
+ */
+class ScopedTreeTest extends NumberTreeTest {
+
+/**
+ * settings property
+ *
+ * @var array
+ * @access public
+ */
+	var $settings = array(
+		'modelClass' => 'FlagTree',
+		'leftField' => 'lft',
+		'rightField' => 'rght',
+		'parentField' => 'parent_id'
+	);
+
+/**
+ * fixtures property
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array('core.flag_tree', 'core.ad', 'core.campaign', 'core.translate', 'core.number_tree_two');
+
+/**
+ * testStringScope method
+ *
+ * @access public
+ * @return void
+ */
+	function testStringScope() {
+		$this->Tree =& new FlagTree();
+		$this->Tree->initialize(2, 3);
+
+		$this->Tree->id = 1;
+		$this->Tree->saveField('flag', 1);
+		$this->Tree->id = 2;
+		$this->Tree->saveField('flag', 1);
+
+		$result = $this->Tree->children();
+		$expected = array(
+			array('FlagTree' => array('id' => '3', 'name' => '1.1.1', 'parent_id' => '2', 'lft' => '3', 'rght' => '4', 'flag' => '0')),
+			array('FlagTree' => array('id' => '4', 'name' => '1.1.2', 'parent_id' => '2', 'lft' => '5', 'rght' => '6', 'flag' => '0')),
+			array('FlagTree' => array('id' => '5', 'name' => '1.1.3', 'parent_id' => '2', 'lft' => '7', 'rght' => '8', 'flag' => '0'))
+		);
+		$this->assertEqual($result, $expected);
+
+		$this->Tree->Behaviors->attach('Tree', array('scope' => 'FlagTree.flag = 1'));
+		$this->assertEqual($this->Tree->children(), array());
+
+		$this->Tree->id = 1;
+		$this->Tree->Behaviors->attach('Tree', array('scope' => 'FlagTree.flag = 1'));
+
+		$result = $this->Tree->children();
+		$expected = array(array('FlagTree' => array('id' => '2', 'name' => '1.1', 'parent_id' => '1', 'lft' => '2', 'rght' => '9', 'flag' => '1')));
+		$this->assertEqual($result, $expected);
+
+		$this->assertTrue($this->Tree->delete());
+		$this->assertEqual($this->Tree->find('count'), 11);
+	}
+
+/**
+ * testArrayScope method
+ *
+ * @access public
+ * @return void
+ */
+	function testArrayScope() {
+		$this->Tree =& new FlagTree();
+		$this->Tree->initialize(2, 3);
+
+		$this->Tree->id = 1;
+		$this->Tree->saveField('flag', 1);
+		$this->Tree->id = 2;
+		$this->Tree->saveField('flag', 1);
+
+		$result = $this->Tree->children();
+		$expected = array(
+			array('FlagTree' => array('id' => '3', 'name' => '1.1.1', 'parent_id' => '2', 'lft' => '3', 'rght' => '4', 'flag' => '0')),
+			array('FlagTree' => array('id' => '4', 'name' => '1.1.2', 'parent_id' => '2', 'lft' => '5', 'rght' => '6', 'flag' => '0')),
+			array('FlagTree' => array('id' => '5', 'name' => '1.1.3', 'parent_id' => '2', 'lft' => '7', 'rght' => '8', 'flag' => '0'))
+		);
+		$this->assertEqual($result, $expected);
+
+		$this->Tree->Behaviors->attach('Tree', array('scope' => array('FlagTree.flag' => 1)));
+		$this->assertEqual($this->Tree->children(), array());
+
+		$this->Tree->id = 1;
+		$this->Tree->Behaviors->attach('Tree', array('scope' => array('FlagTree.flag' => 1)));
+
+		$result = $this->Tree->children();
+		$expected = array(array('FlagTree' => array('id' => '2', 'name' => '1.1', 'parent_id' => '1', 'lft' => '2', 'rght' => '9', 'flag' => '1')));
+		$this->assertEqual($result, $expected);
+
+		$this->assertTrue($this->Tree->delete());
+		$this->assertEqual($this->Tree->find('count'), 11);
+	}
+
+/**
+ * testMoveUpWithScope method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoveUpWithScope() {
+		$this->Ad =& new Ad();
+		$this->Ad->Behaviors->attach('Tree', array('scope'=>'Campaign'));
+		$this->Ad->moveUp(6);
+
+		$this->Ad->id = 4;
+		$result = $this->Ad->children();
+		$this->assertEqual(Set::extract('/Ad/id', $result), array(6, 5));
+		$this->assertEqual(Set::extract('/Campaign/id', $result), array(2, 2));
+	}
+
+/**
+ * testMoveDownWithScope method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoveDownWithScope() {
+		$this->Ad =& new Ad();
+		$this->Ad->Behaviors->attach('Tree', array('scope' => 'Campaign'));
+		$this->Ad->moveDown(6);
+
+		$this->Ad->id = 4;
+		$result = $this->Ad->children();
+		$this->assertEqual(Set::extract('/Ad/id', $result), array(5, 6));
+		$this->assertEqual(Set::extract('/Campaign/id', $result), array(2, 2));
+	}
+
+/**
+ * Tests the interaction (non-interference) between TreeBehavior and other behaviors with respect
+ * to callback hooks
+ *
+ * @access public
+ * @return void
+ */
+	function testTranslatingTree() {
+		$this->Tree =& new FlagTree();
+		$this->Tree->cacheQueries = false;
+		$this->Tree->translateModel = 'TranslateTreeTestModel';
+		$this->Tree->Behaviors->attach('Translate', array('name'));
+
+		//Save
+		$this->Tree->locale = 'eng';
+		$data = array('FlagTree' => array(
+			'name' => 'name #1',
+			'locale' => 'eng',
+			'parent_id' => null,
+		));
+		$this->Tree->save($data);
+		$result = $this->Tree->find('all');
+		$expected = array(array('FlagTree' => array(
+			'id' => 1,
+			'name' => 'name #1',
+			'parent_id' => null,
+			'lft' => 1,
+			'rght' => 2,
+			'flag' => 0,
+			'locale' => 'eng',
+		)));
+		$this->assertEqual($result, $expected);
+
+		//update existing record, same locale
+		$this->Tree->create();
+		$data['FlagTree']['name'] = 'Named 2';
+		$this->Tree->id = 1;
+		$this->Tree->save($data);
+		$result = $this->Tree->find('all');
+		$expected = array(array('FlagTree' => array(
+			'id' => 1,
+			'name' => 'Named 2',
+			'parent_id' => null,
+			'lft' => 1,
+			'rght' => 2,
+			'flag' => 0,
+			'locale' => 'eng',
+		)));
+		$this->assertEqual($result, $expected);
+
+		//update different locale, same record
+		$this->Tree->create();
+		$this->Tree->locale = 'deu';
+		$this->Tree->id = 1;
+		$data = array('FlagTree' => array(
+			'id' => 1,
+			'parent_id' => null,
+			'name' => 'namen #1',
+			'locale' => 'deu',
+		));
+		$this->Tree->save($data);
+
+		$this->Tree->locale = 'deu';
+		$result = $this->Tree->find('all');
+		$expected = array(array('FlagTree' => array(
+			'id' => 1,
+			'name' => 'namen #1',
+			'parent_id' => null,
+			'lft' => 1,
+			'rght' => 2,
+			'flag' => 0,
+			'locale' => 'deu',
+		)));
+		$this->assertEqual($result, $expected);
+
+		//Save with bindTranslation
+		$this->Tree->locale = 'eng';
+		$data = array(
+			'name' => array('eng' => 'New title', 'spa' => 'Nuevo leyenda'),
+			'parent_id' => null
+		);
+		$this->Tree->create($data);
+		$this->Tree->save();
+
+		$this->Tree->unbindTranslation();
+		$translations = array('name' => 'Name');
+		$this->Tree->bindTranslation($translations, false);
+		$this->Tree->locale = array('eng', 'spa');
+
+		$result = $this->Tree->read();
+		$expected = array(
+			'FlagTree' => array('id' => 2, 'parent_id' => null, 'locale' => 'eng', 'name' => 'New title', 'flag' => 0, 'lft' => 3, 'rght' => 4),
+			'Name' => array(
+			array('id' => 21, 'locale' => 'eng', 'model' => 'FlagTree', 'foreign_key' => 2, 'field' => 'name', 'content' => 'New title'),
+			array('id' => 22, 'locale' => 'spa', 'model' => 'FlagTree', 'foreign_key' => 2, 'field' => 'name', 'content' => 'Nuevo leyenda')
+			),
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testGenerateTreeListWithSelfJoin method
+ *
+ * @return void
+ * @access public
+ */
+	function testAliasesWithScopeInTwoTreeAssociations() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+
+		$this->TreeTwo =& new NumberTreeTwo();
+
+		$record = $this->Tree->find('first');
+
+		$this->Tree->bindModel(array(
+			'hasMany' => array(
+				'SecondTree' => array(
+					'className' => 'NumberTreeTwo',
+					'foreignKey' => 'number_tree_id'
+				)
+			)
+		));
+		$this->TreeTwo->bindModel(array(
+			'belongsTo' => array(
+				'FirstTree' => array(
+					'className' => $modelClass,
+					'foreignKey' => 'number_tree_id'
+				)
+			)
+		));
+		$this->TreeTwo->Behaviors->attach('Tree', array(
+			'scope' => 'FirstTree'
+		));
+
+		$data = array(
+			'NumberTreeTwo' => array(
+				'name' => 'First',
+				'number_tree_id' => $record['FlagTree']['id']
+			)
+		);
+		$this->TreeTwo->create();
+		$this->assertTrue($this->TreeTwo->save($data));
+
+		$result = $this->TreeTwo->find('first');
+		$expected = array('NumberTreeTwo' => array(
+			'id' => 1,
+			'name' => 'First',
+			'number_tree_id' => $record['FlagTree']['id'],
+			'parent_id' => null,
+			'lft' => 1,
+			'rght' => 2
+		));
+		$this->assertEqual($result, $expected);
+	}
+}
+
+/**
+ * AfterTreeTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.behaviors
+ */
+class AfterTreeTest extends NumberTreeTest {
+
+/**
+ * settings property
+ *
+ * @var array
+ * @access public
+ */
+	var $settings = array(
+		'modelClass' => 'AfterTree',
+		'leftField' => 'lft',
+		'rightField' => 'rght',
+		'parentField' => 'parent_id'
+	);
+
+/**
+ * fixtures property
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array('core.after_tree');
+
+/**
+ * Tests the afterSave callback in the model
+ *
+ * @access public
+ * @return void
+ */
+	function testAftersaveCallback() {
+		$this->Tree =& new AfterTree();
+
+		$expected = array('AfterTree' => array('name' => 'Six and One Half Changed in AfterTree::afterSave() but not in database', 'parent_id' => 6, 'lft' => 11, 'rght' => 12));
+		$result = $this->Tree->save(array('AfterTree' => array('name' => 'Six and One Half', 'parent_id' => 6)));
+		$this->assertEqual($result, $expected);
+
+		$expected = array('AfterTree' => array('name' => 'Six and One Half', 'parent_id' => 6, 'lft' => 11, 'rght' => 12, 'id' => 8));
+		$result = $this->Tree->find('all');
+		$this->assertEqual($result[7], $expected);
+	}
+}
+
+/**
+ * UnconventionalTreeTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.behaviors
+ */
+class UnconventionalTreeTest extends NumberTreeTest {
+
+/**
+ * settings property
+ *
+ * @var array
+ * @access public
+ */
+	var $settings = array(
+		'modelClass' => 'UnconventionalTree',
+		'leftField' => 'left',
+		'rightField' => 'right',
+		'parentField' => 'join'
+	);
+
+/**
+ * fixtures property
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array('core.unconventional_tree');
+}
+
+/**
+ * UuidTreeTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.behaviors
+ */
+class UuidTreeTest extends NumberTreeTest {
+
+/**
+ * settings property
+ *
+ * @var array
+ * @access public
+ */
+	var $settings = array(
+		'modelClass' => 'UuidTree',
+		'leftField' => 'lft',
+		'rightField' => 'rght',
+		'parentField' => 'parent_id'
+	);
+
+/**
+ * fixtures property
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array('core.uuid_tree');
+
+/**
+ * testMovePromote method
+ *
+ * @return void
+ * @access public
+ */
+	function testMovePromote() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+		$this->Tree->id = null;
+
+		$parent = $this->Tree->find(array($modelClass . '.name' => '1. Root'));
+		$parent_id = $parent[$modelClass]['id'];
+
+		$data = $this->Tree->find(array($modelClass . '.name' => '1.1.1'), array('id'));
+		$this->Tree->id= $data[$modelClass]['id'];
+		$this->Tree->saveField($parentField, $parent_id);
+		$direct = $this->Tree->children($parent_id, true, array('name', $leftField, $rightField));
+		$expects = array(array($modelClass => array('name' => '1.1', $leftField => 2, $rightField => 5)),
+			array($modelClass => array('name' => '1.2', $leftField => 6, $rightField => 11)),
+			array($modelClass => array('name' => '1.1.1', $leftField => 12, $rightField => 13)));
+		$this->assertEqual($direct, $expects);
+		$validTree = $this->Tree->verify();
+		$this->assertIdentical($validTree, true);
+	}
+
+/**
+ * testMoveWithWhitelist method
+ *
+ * @return void
+ * @access public
+ */
+	function testMoveWithWhitelist() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+		$this->Tree->id = null;
+
+		$parent = $this->Tree->find(array($modelClass . '.name' => '1. Root'));
+		$parent_id = $parent[$modelClass]['id'];
+
+		$data = $this->Tree->find(array($modelClass . '.name' => '1.1.1'), array('id'));
+		$this->Tree->id = $data[$modelClass]['id'];
+		$this->Tree->whitelist = array($parentField, 'name', 'description');
+		$this->Tree->saveField($parentField, $parent_id);
+
+		$result = $this->Tree->children($parent_id, true, array('name', $leftField, $rightField));
+		$expected = array(array($modelClass => array('name' => '1.1', $leftField => 2, $rightField => 5)),
+			array($modelClass => array('name' => '1.2', $leftField => 6, $rightField => 11)),
+			array($modelClass => array('name' => '1.1.1', $leftField => 12, $rightField => 13)));
+		$this->assertEqual($result, $expected);
+		$this->assertTrue($this->Tree->verify());
+	}
+
+/**
+ * testRemoveNoChildren method
+ *
+ * @return void
+ * @access public
+ */
+	function testRemoveNoChildren() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+		$initialCount = $this->Tree->find('count');
+
+		$result = $this->Tree->findByName('1.1.1');
+		$this->Tree->removeFromTree($result[$modelClass]['id']);
+
+		$laterCount = $this->Tree->find('count');
+		$this->assertEqual($initialCount, $laterCount);
+
+		$nodes = $this->Tree->find('list', array('order' => $leftField));
+		$expects = array(
+			'1. Root',
+			'1.1',
+			'1.1.2',
+			'1.2',
+			'1.2.1',
+			'1.2.2',
+			'1.1.1',
+		);
+
+		$this->assertEqual(array_values($nodes), $expects);
+
+		$validTree = $this->Tree->verify();
+		$this->assertIdentical($validTree, true);
+	}
+
+/**
+ * testRemoveAndDeleteNoChildren method
+ *
+ * @return void
+ * @access public
+ */
+	function testRemoveAndDeleteNoChildren() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+		$initialCount = $this->Tree->find('count');
+
+		$result = $this->Tree->findByName('1.1.1');
+		$this->Tree->removeFromTree($result[$modelClass]['id'], true);
+
+		$laterCount = $this->Tree->find('count');
+		$this->assertEqual($initialCount - 1, $laterCount);
+
+		$nodes = $this->Tree->find('list', array('order' => $leftField));
+		$expects = array(
+			'1. Root',
+			'1.1',
+			'1.1.2',
+			'1.2',
+			'1.2.1',
+			'1.2.2',
+		);
+		$this->assertEqual(array_values($nodes), $expects);
+
+		$validTree = $this->Tree->verify();
+		$this->assertIdentical($validTree, true);
+	}
+
+/**
+ * testChildren method
+ *
+ * @return void
+ * @access public
+ */
+	function testChildren() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+
+		$data = $this->Tree->find(array($modelClass . '.name' => '1. Root'));
+		$this->Tree->id = $data[$modelClass]['id'];
+
+		$direct = $this->Tree->children(null, true, array('name', $leftField, $rightField));
+		$expects = array(array($modelClass => array('name' => '1.1', $leftField => 2, $rightField => 7)),
+			array($modelClass => array('name' => '1.2', $leftField => 8, $rightField => 13)));
+		$this->assertEqual($direct, $expects);
+
+		$total = $this->Tree->children(null, null, array('name', $leftField, $rightField));
+		$expects = array(array($modelClass => array('name' => '1.1', $leftField => 2, $rightField => 7)),
+			array($modelClass => array('name' => '1.1.1', $leftField => 3, $rightField => 4)),
+			array($modelClass => array('name' => '1.1.2', $leftField => 5, $rightField => 6)),
+			array($modelClass => array('name' => '1.2', $leftField => 8, $rightField => 13)),
+			array($modelClass => array('name' => '1.2.1', $leftField => 9, $rightField => 10)),
+			array($modelClass => array('name' => '1.2.2', $leftField => 11, $rightField => 12)));
+		$this->assertEqual($total, $expects);
+	}
+
+/**
+ * testNoAmbiguousColumn method
+ *
+ * @return void
+ * @access public
+ */
+	function testNoAmbiguousColumn() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->initialize(2, 2);
+
+		$this->Tree->bindModel(array('belongsTo' => array('Dummy' =>
+			array('className' => $modelClass, 'foreignKey' => $parentField, 'conditions' => array('Dummy.id' => null)))), false);
+
+		$data = $this->Tree->find(array($modelClass . '.name' => '1. Root'));
+		$this->Tree->id = $data[$modelClass]['id'];
+
+		$direct = $this->Tree->children(null, true, array('name', $leftField, $rightField));
+		$expects = array(array($modelClass => array('name' => '1.1', $leftField => 2, $rightField => 7)),
+			array($modelClass => array('name' => '1.2', $leftField => 8, $rightField => 13)));
+		$this->assertEqual($direct, $expects);
+
+		$total = $this->Tree->children(null, null, array('name', $leftField, $rightField));
+		$expects = array(
+			array($modelClass => array('name' => '1.1', $leftField => 2, $rightField => 7)),
+			array($modelClass => array('name' => '1.1.1', $leftField => 3, $rightField => 4)),
+			array($modelClass => array('name' => '1.1.2', $leftField => 5, $rightField => 6)),
+			array($modelClass => array('name' => '1.2', $leftField => 8, $rightField => 13)),
+			array($modelClass => array('name' => '1.2.1', $leftField => 9, $rightField => 10)),
+			array($modelClass => array('name' => '1.2.2', $leftField => 11, $rightField => 12))
+		);
+		$this->assertEqual($total, $expects);
+	}
+
+/**
+ * testGenerateTreeListWithSelfJoin method
+ *
+ * @return void
+ * @access public
+ */
+	function testGenerateTreeListWithSelfJoin() {
+		extract($this->settings);
+		$this->Tree =& new $modelClass();
+		$this->Tree->bindModel(array('belongsTo' => array('Dummy' =>
+			array('className' => $modelClass, 'foreignKey' => $parentField, 'conditions' => array('Dummy.id' => null)))), false);
+		$this->Tree->initialize(2, 2);
+
+		$result = $this->Tree->generateTreeList();
+		$expected = array('1. Root', '_1.1', '__1.1.1', '__1.1.2', '_1.2', '__1.2.1', '__1.2.2');
+		$this->assertIdentical(array_values($result), $expected);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/model/cake_schema.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/model/cake_schema.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/model/cake_schema.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,989 @@
+<?php
+/**
+ * Test for Schema database management
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.5550
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Model', 'CakeSchema', false);
+
+/**
+ * Test for Schema database management
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class MyAppSchema extends CakeSchema {
+
+/**
+ * name property
+ *
+ * @var string 'MyApp'
+ * @access public
+ */
+	var $name = 'MyApp';
+
+/**
+ * connection property
+ *
+ * @var string 'test_suite'
+ * @access public
+ */
+	var $connection = 'test_suite';
+
+/**
+ * comments property
+ *
+ * @var array
+ * @access public
+ */
+	var $comments = array(
+		'id' => array('type' => 'integer', 'null' => false, 'default' => 0, 'key' => 'primary'),
+		'post_id' => array('type' => 'integer', 'null' => false, 'default' => 0),
+		'user_id' => array('type' => 'integer', 'null' => false),
+		'title' => array('type' => 'string', 'null' => false, 'length' => 100),
+		'comment' => array('type' => 'text', 'null' => false, 'default' => null),
+		'published' => array('type' => 'string', 'null' => true, 'default' => 'N', 'length' => 1),
+		'created' => array('type' => 'datetime', 'null' => true, 'default' => null),
+		'updated' => array('type' => 'datetime', 'null' => true, 'default' => null),
+		'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => true)),
+	);
+
+/**
+ * posts property
+ *
+ * @var array
+ * @access public
+ */
+	var $posts = array(
+		'id' => array('type' => 'integer', 'null' => false, 'default' => 0, 'key' => 'primary'),
+		'author_id' => array('type' => 'integer', 'null' => true, 'default' => ''),
+		'title' => array('type' => 'string', 'null' => false, 'default' => 'Title'),
+		'body' => array('type' => 'text', 'null' => true, 'default' => null),
+		'summary' => array('type' => 'text', 'null' => true),
+		'published' => array('type' => 'string', 'null' => true, 'default' => 'Y', 'length' => 1),
+		'created' => array('type' => 'datetime', 'null' => true, 'default' => null),
+		'updated' => array('type' => 'datetime', 'null' => true, 'default' => null),
+		'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => true)),
+	);
+
+/**
+ * setup method
+ *
+ * @param mixed $version
+ * @access public
+ * @return void
+ */
+	function setup($version) {
+	}
+
+/**
+ * teardown method
+ *
+ * @param mixed $version
+ * @access public
+ * @return void
+ */
+	function teardown($version) {
+	}
+}
+
+/**
+ * TestAppSchema class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class TestAppSchema extends CakeSchema {
+
+/**
+ * name property
+ *
+ * @var string 'MyApp'
+ * @access public
+ */
+	var $name = 'MyApp';
+
+/**
+ * comments property
+ *
+ * @var array
+ * @access public
+ */
+	var $comments = array(
+		'id' => array('type' => 'integer', 'null' => false, 'default' => 0,'key' => 'primary'),
+		'article_id' => array('type' => 'integer', 'null' => false),
+		'user_id' => array('type' => 'integer', 'null' => false),
+		'comment' => array('type' => 'text', 'null' => true, 'default' => null),
+		'published' => array('type' => 'string', 'null' => true, 'default' => 'N', 'length' => 1),
+		'created' => array('type' => 'datetime', 'null' => true, 'default' => null),
+		'updated' => array('type' => 'datetime', 'null' => true, 'default' => null),
+		'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => true)),
+		'tableParameters' => array(),
+	);
+
+/**
+ * posts property
+ *
+ * @var array
+ * @access public
+ */
+	var $posts = array(
+		'id' => array('type' => 'integer', 'null' => false, 'default' => 0, 'key' => 'primary'),
+		'author_id' => array('type' => 'integer', 'null' => false),
+		'title' => array('type' => 'string', 'null' => false),
+		'body' => array('type' => 'text', 'null' => true, 'default' => null),
+		'published' => array('type' => 'string', 'null' => true, 'default' => 'N', 'length' => 1),
+		'created' => array('type' => 'datetime', 'null' => true, 'default' => null),
+		'updated' => array('type' => 'datetime', 'null' => true, 'default' => null),
+		'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => true)),
+		'tableParameters' => array(),
+	);
+
+/**
+ * posts_tags property
+ *
+ * @var array
+ * @access public
+ */
+	var $posts_tags = array(
+		'post_id' => array('type' => 'integer', 'null' => false, 'key' => 'primary'),
+		'tag_id' => array('type' => 'string', 'null' => false, 'key' => 'primary'),
+		'indexes' => array('posts_tag' => array('column' => array('tag_id', 'post_id'), 'unique' => 1)),
+		'tableParameters' => array()
+	);
+
+/**
+ * tags property
+ *
+ * @var array
+ * @access public
+ */
+	var $tags = array(
+		'id' => array('type' => 'integer', 'null' => false, 'default' => 0, 'key' => 'primary'),
+		'tag' => array('type' => 'string', 'null' => false),
+		'created' => array('type' => 'datetime', 'null' => true, 'default' => null),
+		'updated' => array('type' => 'datetime', 'null' => true, 'default' => null),
+		'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => true)),
+		'tableParameters' => array()
+	);
+
+/**
+ * datatypes property
+ *
+ * @var array
+ * @access public
+ */
+	var $datatypes = array(
+		'id' => array('type' => 'integer', 'null' => false, 'default' => 0, 'key' => 'primary'),
+		'float_field' => array('type' => 'float', 'null' => false, 'length' => '5,2', 'default' => ''),
+		'bool' => array('type' => 'boolean', 'null' => false, 'default' => false),
+		'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => true)),
+		'tableParameters' => array()
+	);
+
+/**
+ * setup method
+ *
+ * @param mixed $version
+ * @access public
+ * @return void
+ */
+	function setup($version) {
+	}
+
+/**
+ * teardown method
+ *
+ * @param mixed $version
+ * @access public
+ * @return void
+ */
+	function teardown($version) {
+	}
+}
+
+/**
+ * SchmeaPost class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class SchemaPost extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'SchemaPost'
+ * @access public
+ */
+	var $name = 'SchemaPost';
+
+/**
+ * useTable property
+ *
+ * @var string 'posts'
+ * @access public
+ */
+	var $useTable = 'posts';
+
+/**
+ * hasMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasMany = array('SchemaComment');
+
+/**
+ * hasAndBelongsToMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('SchemaTag');
+}
+
+/**
+ * SchemaComment class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class SchemaComment extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'SchemaComment'
+ * @access public
+ */
+	var $name = 'SchemaComment';
+
+/**
+ * useTable property
+ *
+ * @var string 'comments'
+ * @access public
+ */
+	var $useTable = 'comments';
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('SchemaPost');
+}
+
+/**
+ * SchemaTag class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class SchemaTag extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'SchemaTag'
+ * @access public
+ */
+	var $name = 'SchemaTag';
+
+/**
+ * useTable property
+ *
+ * @var string 'tags'
+ * @access public
+ */
+	var $useTable = 'tags';
+
+/**
+ * hasAndBelongsToMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('SchemaPost');
+}
+
+/**
+ * SchemaDatatype class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class SchemaDatatype extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'SchemaDatatype'
+ * @access public
+ */
+	var $name = 'SchemaDatatype';
+
+/**
+ * useTable property
+ *
+ * @var string 'datatypes'
+ * @access public
+ */
+	var $useTable = 'datatypes';
+}
+
+/**
+ * Testdescribe class
+ *
+ * This class is defined purely to inherit the cacheSources variable otherwise
+ * testSchemaCreatTable will fail if listSources has already been called and
+ * its source cache populated - I.e. if the test is run within a group
+ *
+ * @uses          CakeTestModel
+ * @package
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Testdescribe extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Testdescribe'
+ * @access public
+ */
+	var $name = 'Testdescribe';
+}
+
+/**
+ * SchemaCrossDatabase class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class SchemaCrossDatabase extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'SchemaCrossDatabase'
+ * @access public
+ */
+	var $name = 'SchemaCrossDatabase';
+
+/**
+ * useTable property
+ *
+ * @var string 'posts'
+ * @access public
+ */
+	var $useTable = 'cross_database';
+
+/**
+ * useDbConfig property
+ *
+ * @var string 'test2'
+ * @access public
+ */
+	var $useDbConfig = 'test2';
+}
+
+/**
+ * SchemaCrossDatabaseFixture class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class SchemaCrossDatabaseFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'CrossDatabase'
+ * @access public
+ */
+	var $name = 'CrossDatabase';
+
+/**
+ * table property
+ *
+ * @access public
+ */
+	var $table = 'cross_database';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'name' => 'string'
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('id' => 1, 'name' => 'First'),
+		array('id' => 2, 'name' => 'Second'),
+	);
+}
+
+/**
+ * SchemaPrefixAuthUser class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class SchemaPrefixAuthUser extends CakeTestModel {
+/**
+ * name property
+ *
+ * @var string
+ */
+	var $name = 'SchemaPrefixAuthUser';
+/**
+ * table prefix
+ *
+ * @var string
+ */
+	var $tablePrefix = 'auth_';
+/**
+ * useTable
+ *
+ * @var string
+ */
+	var $useTable = 'users';
+}
+
+/**
+ * CakeSchemaTest
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class CakeSchemaTest extends CakeTestCase {
+
+/**
+ * fixtures property
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array(
+		'core.post', 'core.tag', 'core.posts_tag', 'core.test_plugin_comment',
+		'core.datatype', 'core.auth_user', 'core.author',
+		'core.test_plugin_article', 'core.user', 'core.comment',
+		'core.prefix_test'
+	);
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function startTest() {
+		$this->Schema = new TestAppSchema();
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		@unlink(TMP . 'tests' . DS .'schema.php');
+		unset($this->Schema);
+		ClassRegistry::flush();
+	}
+
+/**
+ * testSchemaName method
+ *
+ * @access public
+ * @return void
+ */
+	function testSchemaName() {
+		$Schema = new CakeSchema();
+		$this->assertEqual(strtolower($Schema->name), strtolower(APP_DIR));
+
+		Configure::write('App.dir', 'Some.name.with.dots');
+		$Schema = new CakeSchema();
+		$this->assertEqual($Schema->name, 'SomeNameWithDots');
+
+		Configure::write('App.dir', 'app');
+	}
+
+/**
+ * testSchemaRead method
+ *
+ * @access public
+ * @return void
+ */
+	function testSchemaRead() {
+		$read = $this->Schema->read(array(
+			'connection' => 'test_suite',
+			'name' => 'TestApp',
+			'models' => array('SchemaPost', 'SchemaComment', 'SchemaTag', 'SchemaDatatype')
+		));
+		unset($read['tables']['missing']);
+
+		$expected = array('comments', 'datatypes', 'posts', 'posts_tags', 'tags');
+		$this->assertEqual(array_keys($read['tables']), $expected);
+		foreach ($read['tables'] as $table => $fields) {
+			$this->assertEqual(array_keys($fields), array_keys($this->Schema->tables[$table]));
+		}
+
+		$this->assertEqual(
+			$read['tables']['datatypes']['float_field'],
+			$this->Schema->tables['datatypes']['float_field']
+		);
+
+		$SchemaPost =& ClassRegistry::init('SchemaPost');
+		$SchemaPost->table = 'sts';
+		$SchemaPost->tablePrefix = 'po';
+		$read = $this->Schema->read(array(
+			'connection' => 'test_suite',
+			'name' => 'TestApp',
+			'models' => array('SchemaPost')
+		));
+		$this->assertFalse(isset($read['tables']['missing']['posts']), 'Posts table was not read from tablePrefix %s');
+
+		$read = $this->Schema->read(array(
+			'connection' => 'test_suite',
+			'name' => 'TestApp',
+			'models' => array('SchemaComment', 'SchemaTag', 'SchemaPost')
+		));
+		$this->assertFalse(isset($read['tables']['missing']['posts_tags']), 'Join table marked as missing %s');
+	}
+
+/**
+ * test read() with tablePrefix properties.
+ *
+ * @return void
+ */
+	function testSchemaReadWithTablePrefix() {
+		$model =& new SchemaPrefixAuthUser();
+
+		$Schema =& new CakeSchema();
+		$read = $Schema->read(array(
+			'connection' => 'test_suite',
+			'name' => 'TestApp',
+			'models' => array('SchemaPrefixAuthUser')
+		));
+		unset($read['tables']['missing']);
+		$this->assertTrue(isset($read['tables']['auth_users']), 'auth_users key missing %s');
+
+	}
+
+/**
+ * test reading schema with config prefix.
+ *
+ * @return void
+ */
+	function testSchemaReadWithConfigPrefix() {
+		$db =& ConnectionManager::getDataSource('test_suite');
+		$config = $db->config;
+		$config['prefix'] = 'schema_test_prefix_';
+		ConnectionManager::create('schema_prefix', $config);
+		$read = $this->Schema->read(array('connection' => 'schema_prefix', 'models' => false));
+		$this->assertTrue(empty($read['tables']));
+
+		$config['prefix'] = 'prefix_';
+		ConnectionManager::create('schema_prefix2', $config);
+		$read = $this->Schema->read(array(
+			'connection' => 'schema_prefix2',
+			'name' => 'TestApp',
+			'models' => false));
+		$this->assertTrue(isset($read['tables']['prefix_tests']));
+	}
+
+/**
+ * test reading schema from plugins.
+ *
+ * @return void
+ */
+	function testSchemaReadWithPlugins() {
+		App::objects('model', null, false);
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)
+		));
+
+		$Schema =& new CakeSchema();
+		$Schema->plugin = 'TestPlugin';
+		$read = $Schema->read(array(
+			'connection' => 'test_suite',
+			'name' => 'TestApp',
+			'models' => true
+		));
+		unset($read['tables']['missing']);
+		$this->assertTrue(isset($read['tables']['auth_users']));
+		$this->assertTrue(isset($read['tables']['authors']));
+		$this->assertTrue(isset($read['tables']['test_plugin_comments']));
+		$this->assertTrue(isset($read['tables']['posts']));
+		$this->assertEqual(count($read['tables']), 4);
+
+		App::build();
+	}
+
+/**
+ * test reading schema with tables from another database.
+ *
+ * @return void
+ */
+	function testSchemaReadWithCrossDatabase() {
+		$config = new DATABASE_CONFIG();
+		$skip = $this->skipIf(
+			!isset($config->test) || !isset($config->test2),
+			 '%s Primary and secondary test databases not configured, skipping cross-database '
+			.'join tests.'
+			.' To run these tests, you must define $test and $test2 in your database configuration.'
+		);
+		if ($skip) {
+			return;
+		}
+
+		$db2 =& ConnectionManager::getDataSource('test2');
+		$fixture = new SchemaCrossDatabaseFixture();
+		$fixture->create($db2);
+		$fixture->insert($db2);
+
+		$read = $this->Schema->read(array(
+			'connection' => 'test_suite',
+			'name' => 'TestApp',
+			'models' => array('SchemaCrossDatabase', 'SchemaPost')
+		));
+		$this->assertTrue(isset($read['tables']['posts']));
+		$this->assertFalse(isset($read['tables']['cross_database']), 'Cross database should not appear');
+		$this->assertFalse(isset($read['tables']['missing']['cross_database']), 'Cross database should not appear');
+
+		$read = $this->Schema->read(array(
+			'connection' => 'test2',
+			'name' => 'TestApp',
+			'models' => array('SchemaCrossDatabase', 'SchemaPost')
+		));
+		$this->assertFalse(isset($read['tables']['posts']), 'Posts should not appear');
+		$this->assertFalse(isset($read['tables']['posts']), 'Posts should not appear');
+		$this->assertTrue(isset($read['tables']['cross_database']));
+
+		$fixture->drop($db2);
+	}
+
+/**
+ * test that tables are generated correctly
+ *
+ * @return void
+ */
+	function testGenerateTable() {
+		$fields = array(
+			'id' => array('type' => 'integer', 'null' => false, 'default' => 0, 'key' => 'primary'),
+			'author_id' => array('type' => 'integer', 'null' => false),
+			'title' => array('type' => 'string', 'null' => false),
+			'body' => array('type' => 'text', 'null' => true, 'default' => null),
+			'published' => array('type' => 'string', 'null' => true, 'default' => 'N', 'length' => 1),
+			'created' => array('type' => 'datetime', 'null' => true, 'default' => null),
+			'updated' => array('type' => 'datetime', 'null' => true, 'default' => null),
+			'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => true)),
+		);
+		$result = $this->Schema->generateTable('posts', $fields);
+		$this->assertPattern('/var \$posts/', $result);
+
+		eval(substr($result, 4));
+		$this->assertEqual($posts, $fields);
+	}
+/**
+ * testSchemaWrite method
+ *
+ * @access public
+ * @return void
+ */
+	function testSchemaWrite() {
+		$write = $this->Schema->write(array('name' => 'MyOtherApp', 'tables' => $this->Schema->tables, 'path' => TMP . 'tests'));
+		$file = file_get_contents(TMP . 'tests' . DS .'schema.php');
+		$this->assertEqual($write, $file);
+
+		require_once( TMP . 'tests' . DS .'schema.php');
+		$OtherSchema = new MyOtherAppSchema();
+		$this->assertEqual($this->Schema->tables, $OtherSchema->tables);
+	}
+
+/**
+ * testSchemaComparison method
+ *
+ * @access public
+ * @return void
+ */
+	function testSchemaComparison() {
+		$New = new MyAppSchema();
+		$compare = $New->compare($this->Schema);
+		$expected = array(
+			'comments' => array(
+				'add' => array(
+					'post_id' => array('type' => 'integer', 'null' => false, 'default' => 0, 'after' => 'id'),
+					'title' => array('type' => 'string', 'null' => false, 'length' => 100, 'after' => 'user_id'),
+				),
+				'drop' => array(
+					'article_id' => array('type' => 'integer', 'null' => false),
+					'tableParameters' => array(),
+				),
+				'change' => array(
+					'comment' => array('type' => 'text', 'null' => false, 'default' => null),
+				)
+			),
+			'posts' => array(
+				'add' => array(
+					'summary' => array('type' => 'text', 'null' => 1, 'after' => 'body'),
+				),
+				'drop' => array(
+					'tableParameters' => array(),
+				),
+				'change' => array(
+					'author_id' => array('type' => 'integer', 'null' => true, 'default' => ''),
+					'title' => array('type' => 'string', 'null' => false, 'default' => 'Title'),
+					'published' => array('type' => 'string', 'null' => true, 'default' => 'Y', 'length' => '1')
+				)
+			),
+		);
+		$this->assertEqual($expected, $compare);
+
+		$tables = array(
+			'missing' => array(
+				'categories' => array(
+					'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'),
+					'created' => array('type' => 'datetime', 'null' => false, 'default' => NULL),
+					'modified' => array('type' => 'datetime', 'null' => false, 'default' => NULL),
+					'name' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 100),
+					'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)),
+					'tableParameters' => array('charset' => 'latin1', 'collate' => 'latin1_swedish_ci', 'engine' => 'MyISAM')
+				)
+			),
+			'ratings' => array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'),
+				'foreign_key' => array('type' => 'integer', 'null' => false, 'default' => NULL),
+				'model' => array('type' => 'varchar', 'null' => false, 'default' => NULL),
+				'value' => array('type' => 'float', 'null' => false, 'length' => '5,2', 'default' => NULL),
+				'created' => array('type' => 'datetime', 'null' => false, 'default' => NULL),
+				'modified' => array('type' => 'datetime', 'null' => false, 'default' => NULL),
+				'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)),
+				'tableParameters' => array('charset' => 'latin1', 'collate' => 'latin1_swedish_ci', 'engine' => 'MyISAM')
+			)
+		);
+		$compare = $New->compare($this->Schema, $tables);
+		$expected = array(
+			'ratings' => array(
+				'add' => array(
+					'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'),
+					'foreign_key' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'after' => 'id'),
+					'model' => array('type' => 'varchar', 'null' => false, 'default' => NULL, 'after' => 'foreign_key'),
+					'value' => array('type' => 'float', 'null' => false, 'length' => '5,2', 'default' => NULL, 'after' => 'model'),
+					'created' => array('type' => 'datetime', 'null' => false, 'default' => NULL, 'after' => 'value'),
+					'modified' => array('type' => 'datetime', 'null' => false, 'default' => NULL, 'after' => 'created'),
+					'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)),
+					'tableParameters' => array('charset' => 'latin1', 'collate' => 'latin1_swedish_ci', 'engine' => 'MyISAM')
+				)
+			)
+		);
+		$this->assertEqual($expected, $compare);
+	}
+
+/**
+ * test comparing '' and null and making sure they are different.
+ *
+ * @return void
+ */
+	function testCompareEmptyStringAndNull() {
+		$One =& new CakeSchema(array(
+			'posts' => array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'),
+				'name' => array('type' => 'string', 'null' => false, 'default' => '')
+			)
+		));
+		$Two =& new CakeSchema(array(
+			'posts' => array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'),
+				'name' => array('type' => 'string', 'null' => false, 'default' => null)
+			)
+		));
+		$compare = $One->compare($Two);
+		$expected = array(
+			'posts' => array(
+				'change' => array(
+					'name' => array('type' => 'string', 'null' => false, 'default' => null)
+				)
+			)
+		);
+		$this->assertEqual($expected, $compare);
+	}
+
+/**
+ * Test comparing tableParameters and indexes.
+ *
+ * @return void
+ */
+	function testTableParametersAndIndexComparison() {
+		$old = array(
+			'posts' => array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => 0, 'key' => 'primary'),
+				'author_id' => array('type' => 'integer', 'null' => false),
+				'title' => array('type' => 'string', 'null' => false),
+				'indexes' => array(
+					'PRIMARY' => array('column' => 'id', 'unique' => true)
+				),
+				'tableParameters' => array(
+					'charset' => 'latin1',
+					'collate' => 'latin1_general_ci'
+				)
+			),
+			'comments' => array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => 0, 'key' => 'primary'),
+				'post_id' => array('type' => 'integer', 'null' => false, 'default' => 0),
+				'comment' => array('type' => 'text'),
+				'indexes' => array(
+					'PRIMARY' => array('column' => 'id', 'unique' => true),
+					'post_id' => array('column' => 'post_id'),
+				),
+				'tableParameters' => array(
+					'engine' => 'InnoDB',
+					'charset' => 'latin1',
+					'collate' => 'latin1_general_ci'
+				)
+			)
+		);
+		$new = array(
+			'posts' => array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => 0, 'key' => 'primary'),
+				'author_id' => array('type' => 'integer', 'null' => false),
+				'title' => array('type' => 'string', 'null' => false),
+				'indexes' => array(
+					'PRIMARY' => array('column' => 'id', 'unique' => true),
+					'author_id' => array('column' => 'author_id'),
+				),
+				'tableParameters' => array(
+					'charset' => 'utf8',
+					'collate' => 'utf8_general_ci',
+					'engine' => 'MyISAM'
+				)
+			),
+			'comments' => array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => 0, 'key' => 'primary'),
+				'post_id' => array('type' => 'integer', 'null' => false, 'default' => 0),
+				'comment' => array('type' => 'text'),
+				'indexes' => array(
+					'PRIMARY' => array('column' => 'id', 'unique' => true),
+				),
+				'tableParameters' => array(
+					'charset' => 'utf8',
+					'collate' => 'utf8_general_ci'
+				)
+			)
+		);
+		$compare = $this->Schema->compare($old, $new);
+		$expected = array(
+			'posts' => array(
+				'add' => array(
+					'indexes' => array('author_id' => array('column' => 'author_id')),
+				),
+				'change' => array(
+					'tableParameters' => array(
+						'charset' => 'utf8',
+						'collate' => 'utf8_general_ci',
+						'engine' => 'MyISAM'
+					)
+				)
+			),
+			'comments' => array(
+				'drop' => array(
+					'indexes' => array('post_id' => array('column' => 'post_id')),
+				),
+				'change' => array(
+					'tableParameters' => array(
+						'charset' => 'utf8',
+						'collate' => 'utf8_general_ci',
+					)
+				)
+			)
+		);
+		$this->assertEqual($compare, $expected);
+	}
+
+/**
+ * testSchemaLoading method
+ *
+ * @access public
+ * @return void
+ */
+	function testSchemaLoading() {
+		$Other =& $this->Schema->load(array('name' => 'MyOtherApp', 'path' => TMP . 'tests'));
+		$this->assertEqual($Other->name, 'MyOtherApp');
+		$this->assertEqual($Other->tables, $this->Schema->tables);
+	}
+
+/**
+ * test loading schema files inside of plugins.
+ *
+ * @return void
+ */
+	function testSchemaLoadingFromPlugin() {
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)
+		));
+		$Other =& $this->Schema->load(array('name' => 'TestPluginApp', 'plugin' => 'TestPlugin'));
+		$this->assertEqual($Other->name, 'TestPluginApp');
+		$this->assertEqual(array_keys($Other->tables), array('acos'));
+
+		App::build();
+	}
+
+/**
+ * testSchemaCreateTable method
+ *
+ * @access public
+ * @return void
+ */
+	function testSchemaCreateTable() {
+		$db =& ConnectionManager::getDataSource('test_suite');
+		$db->cacheSources = false;
+
+		$Schema =& new CakeSchema(array(
+			'connection' => 'test_suite',
+			'testdescribes' => array(
+				'id' => array('type' => 'integer', 'key' => 'primary'),
+				'int_null' => array('type' => 'integer', 'null' => true),
+				'int_not_null' => array('type' => 'integer', 'null' => false),
+			),
+		));
+		$sql = $db->createSchema($Schema);
+
+		$col = $Schema->tables['testdescribes']['int_null'];
+		$col['name'] = 'int_null';
+		$column = $this->db->buildColumn($col);
+		$this->assertPattern('/' . preg_quote($column, '/') . '/', $sql);
+
+		$col = $Schema->tables['testdescribes']['int_not_null'];
+		$col['name'] = 'int_not_null';
+		$column = $this->db->buildColumn($col);
+		$this->assertPattern('/' . preg_quote($column, '/') . '/', $sql);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/model/connection_manager.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/model/connection_manager.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/model/connection_manager.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,340 @@
+<?php
+/**
+ * Connection Manager tests
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.5550
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', 'ConnectionManager');
+
+/**
+ * ConnectionManagerTest
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.models
+ */
+class ConnectionManagerTest extends CakeTestCase {
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		$this->ConnectionManager =& ConnectionManager::getInstance();
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		unset($this->ConnectionManager);
+	}
+
+/**
+ * testInstantiation method
+ *
+ * @access public
+ * @return void
+ */
+	function testInstantiation() {
+		$this->assertTrue(is_a($this->ConnectionManager, 'ConnectionManager'));
+	}
+
+/**
+ * testEnumConnectionObjects method
+ *
+ * @access public
+ * @return void
+ */
+	function testEnumConnectionObjects() {
+		$sources = ConnectionManager::enumConnectionObjects();
+		$this->assertTrue(count($sources) >= 1);
+
+		$connections = array('default', 'test', 'test_suite');
+		$this->assertTrue(count(array_intersect(array_keys($sources), $connections)) >= 1);
+	}
+
+/**
+ * testGetDataSource method
+ *
+ * @access public
+ * @return void
+ */
+	function testGetDataSource() {
+		$connections = ConnectionManager::enumConnectionObjects();
+		$this->assertTrue(count(array_keys($connections) >= 1));
+
+		$source = ConnectionManager::getDataSource(key($connections));
+		$this->assertTrue(is_object($source));
+
+		$this->expectError(new PatternExpectation('/Non-existent data source/i'));
+
+		$source = ConnectionManager::getDataSource('non_existent_source');
+		$this->assertEqual($source, null);
+
+	}
+
+/**
+ * testGetPluginDataSource method
+ *
+ * @access public
+ * @return void
+ */
+	function testGetPluginDataSource() {
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)
+		));
+
+		$name = 'test_source';
+		$config = array('datasource' => 'TestPlugin.TestSource');
+		$connection = ConnectionManager::create($name, $config);
+
+		$this->assertTrue(class_exists('TestSource'));
+		$this->assertEqual($connection->configKeyName, $name);
+		$this->assertEqual($connection->config, $config);
+
+		App::build();
+	}
+
+/**
+ * testGetPluginDataSourceAndPluginDriver method
+ *
+ * @access public
+ * @return void
+ */
+	function testGetPluginDataSourceAndPluginDriver() {
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)
+		));
+
+		$name = 'test_plugin_source_and_driver';
+		$config = array('datasource' => 'TestPlugin.TestSource', 'driver' => 'TestPlugin.TestDriver');
+
+		$connection = ConnectionManager::create($name, $config);
+
+		$this->assertTrue(class_exists('TestSource'));
+		$this->assertTrue(class_exists('TestDriver'));
+		$this->assertEqual($connection->configKeyName, $name);
+		$this->assertEqual($connection->config, $config);
+
+		App::build();
+	}
+
+/**
+ * testGetLocalDataSourceAndPluginDriver method
+ *
+ * @access public
+ * @return void
+ */
+	function testGetLocalDataSourceAndPluginDriver() {
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)
+		));
+
+		$name = 'test_local_source_and_plugin_driver';
+		$config = array('datasource' => 'dbo', 'driver' => 'TestPlugin.DboDummy');
+
+		$connection = ConnectionManager::create($name, $config);
+
+		$this->assertTrue(class_exists('DboSource'));
+		$this->assertTrue(class_exists('DboDummy'));
+		$this->assertEqual($connection->configKeyName, $name);
+
+		App::build();
+	}
+
+/**
+ * testGetPluginDataSourceAndLocalDriver method
+ *
+ * @access public
+ * @return void
+ */
+	function testGetPluginDataSourceAndLocalDriver() {
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS),
+			'datasources' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'models' . DS . 'datasources' . DS)
+		));
+
+		$name = 'test_plugin_source_and_local_driver';
+		$config = array('datasource' => 'TestPlugin.TestSource', 'driver' => 'local_driver');
+
+		$connection = ConnectionManager::create($name, $config);
+
+		$this->assertTrue(class_exists('TestSource'));
+		$this->assertTrue(class_exists('TestLocalDriver'));
+		$this->assertEqual($connection->configKeyName, $name);
+		$this->assertEqual($connection->config, $config);
+		App::build();
+	}
+
+/**
+ * testSourceList method
+ *
+ * @access public
+ * @return void
+ */
+	function testSourceList() {
+		$sources = ConnectionManager::sourceList();
+		$this->assertTrue(count($sources) >= 1);
+
+		$connections = array('default', 'test', 'test_suite');
+		$this->assertTrue(count(array_intersect($sources, $connections)) >= 1);
+	}
+
+/**
+ * testGetSourceName method
+ *
+ * @access public
+ * @return void
+ */
+	function testGetSourceName() {
+		$connections = ConnectionManager::enumConnectionObjects();
+		$name = key($connections);
+		$source = ConnectionManager::getDataSource($name);
+		$result = ConnectionManager::getSourceName($source);
+
+		$this->assertEqual($result, $name);
+
+		$source =& new StdClass();
+		$result = ConnectionManager::getSourceName($source);
+		$this->assertEqual($result, null);
+	}
+
+/**
+ * testLoadDataSource method
+ *
+ * @access public
+ * @return void
+ */
+	function testLoadDataSource() {
+		$connections = array(
+			array('classname' => 'DboMysql', 'filename' => 'dbo' . DS . 'dbo_mysql'),
+			array('classname' => 'DboMysqli', 'filename' => 'dbo' . DS . 'dbo_mysqli'),
+			array('classname' => 'DboMssql', 'filename' => 'dbo' . DS . 'dbo_mssql'),
+			array('classname' => 'DboOracle', 'filename' => 'dbo' . DS . 'dbo_oracle'),
+		);
+
+		foreach ($connections as $connection) {
+			$exists = class_exists($connection['classname']);
+			$loaded = ConnectionManager::loadDataSource($connection);
+			$this->assertEqual($loaded, !$exists, "%s Failed loading the {$connection['classname']} datasource");
+		}
+
+		$connection = array('classname' => 'NonExistentDataSource', 'filename' => 'non_existent');
+		$this->expectError(new PatternExpectation('/Unable to import DataSource class/i'));
+
+		$loaded = ConnectionManager::loadDataSource($connection);
+		$this->assertEqual($loaded, null);
+	}
+
+/**
+ * testCreateDataSource method
+ *
+ * @access public
+ * @return void
+ */
+	function testCreateDataSourceWithIntegrationTests() {
+		$name = 'test_created_connection';
+
+		$connections = ConnectionManager::enumConnectionObjects();
+		$this->assertTrue(count(array_keys($connections) >= 1));
+
+		$source = ConnectionManager::getDataSource(key($connections));
+		$this->assertTrue(is_object($source));
+
+		$config = $source->config;
+		$connection = ConnectionManager::create($name, $config);
+
+		$this->assertTrue(is_object($connection));
+		$this->assertEqual($name, $connection->configKeyName);
+		$this->assertEqual($name, ConnectionManager::getSourceName($connection));
+
+		$source = ConnectionManager::create(null, array());
+		$this->assertEqual($source, null);
+
+		$source = ConnectionManager::create('another_test', array());
+		$this->assertEqual($source, null);
+
+		$config = array('classname' => 'DboMysql', 'filename' => 'dbo' . DS . 'dbo_mysql');
+		$source = ConnectionManager::create(null, $config);
+		$this->assertEqual($source, null);
+	}
+
+/**
+ * testConnectionData method
+ *
+ * @access public
+ * @return void
+ */
+	function testConnectionData() {
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS),
+			'datasources' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'models' . DS . 'datasources' . DS)
+		));
+
+		$expected = array(
+		    'filename' => 'test2_source',
+		    'classname' => 'Test2Source',
+		    'parent' => '',
+		    'plugin' => ''
+		);
+
+		ConnectionManager::create('connection1', array('datasource' => 'Test2'));
+		$connections = ConnectionManager::enumConnectionObjects();
+		$this->assertEqual($expected, $connections['connection1']);
+
+		ConnectionManager::create('connection2', array('datasource' => 'Test2Source'));
+		$connections = ConnectionManager::enumConnectionObjects();
+		$this->assertEqual($expected, $connections['connection2']);
+
+		ConnectionManager::create('connection3', array('datasource' => 'TestPlugin.Test'));
+		$connections = ConnectionManager::enumConnectionObjects();
+		$expected['filename'] = 'test_source';
+		$expected['classname'] = 'TestSource';
+		$expected['plugin'] = 'TestPlugin';
+		$this->assertEqual($expected, $connections['connection3']);
+
+		ConnectionManager::create('connection4', array('datasource' => 'TestPlugin.TestSource'));
+		$connections = ConnectionManager::enumConnectionObjects();
+		$this->assertEqual($expected, $connections['connection4']);
+
+		ConnectionManager::create('connection5', array('datasource' => 'Test2Other'));
+		$connections = ConnectionManager::enumConnectionObjects();
+		$expected['filename'] = 'test2_other_source';
+		$expected['classname'] = 'Test2OtherSource';
+		$expected['plugin'] = '';
+		$this->assertEqual($expected, $connections['connection5']);
+
+		ConnectionManager::create('connection6', array('datasource' => 'Test2OtherSource'));
+		$connections = ConnectionManager::enumConnectionObjects();
+		$this->assertEqual($expected, $connections['connection6']);
+
+		ConnectionManager::create('connection7', array('datasource' => 'TestPlugin.TestOther'));
+		$connections = ConnectionManager::enumConnectionObjects();
+		$expected['filename'] = 'test_other_source';
+		$expected['classname'] = 'TestOtherSource';
+		$expected['plugin'] = 'TestPlugin';
+		$this->assertEqual($expected, $connections['connection7']);
+
+		ConnectionManager::create('connection8', array('datasource' => 'TestPlugin.TestOtherSource'));
+		$connections = ConnectionManager::enumConnectionObjects();
+		$this->assertEqual($expected, $connections['connection8']);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo/dbo_mssql.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo/dbo_mssql.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo/dbo_mssql.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,677 @@
+<?php
+/**
+ * DboMssqlTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) {
+	define('CAKEPHP_UNIT_TEST_EXECUTION', 1);
+}
+require_once LIBS.'model'.DS.'model.php';
+require_once LIBS.'model'.DS.'datasources'.DS.'datasource.php';
+require_once LIBS.'model'.DS.'datasources'.DS.'dbo_source.php';
+require_once LIBS.'model'.DS.'datasources'.DS.'dbo'.DS.'dbo_mssql.php';
+
+/**
+ * DboMssqlTestDb class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources.dbo
+ */
+class DboMssqlTestDb extends DboMssql {
+
+/**
+ * simulated property
+ *
+ * @var array
+ * @access public
+ */
+	var $simulated = array();
+
+/**
+ * simalate property
+ *
+ * @var array
+ * @access public
+ */
+	var $simulate = true;
+/**
+ * fetchAllResultsStack
+ *
+ * @var array
+ * @access public
+ */
+	var $fetchAllResultsStack = array();
+
+/**
+ * execute method
+ *
+ * @param mixed $sql
+ * @access protected
+ * @return void
+ */
+	function _execute($sql) {
+		if ($this->simulate) {
+			$this->simulated[] = $sql;
+			return null;
+		} else {
+			return parent::_execute($sql);
+		}
+	}
+
+/**
+ * fetchAll method
+ *
+ * @param mixed $sql
+ * @access protected
+ * @return void
+ */
+	function _matchRecords(&$model, $conditions = null) {
+		return $this->conditions(array('id' => array(1, 2)));
+	}
+
+/**
+ * fetchAll method
+ *
+ * @param mixed $sql
+ * @access protected
+ * @return void
+ */
+	function fetchAll($sql, $cache = true, $modelName = null) {
+		$result = parent::fetchAll($sql, $cache, $modelName);
+		if (!empty($this->fetchAllResultsStack)) {
+    		return array_pop($this->fetchAllResultsStack);
+		}
+		return $result;
+	}
+
+/**
+ * getLastQuery method
+ *
+ * @access public
+ * @return void
+ */
+	function getLastQuery() {
+		return $this->simulated[count($this->simulated) - 1];
+	}
+
+/**
+ * getPrimaryKey method
+ *
+ * @param mixed $model
+ * @access public
+ * @return void
+ */
+	function getPrimaryKey($model) {
+		return parent::_getPrimaryKey($model);
+	}
+/**
+ * clearFieldMappings method
+ *
+ * @access public
+ * @return void
+ */
+	function clearFieldMappings() {
+		$this->__fieldMappings = array();
+	}
+}
+
+/**
+ * MssqlTestModel class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class MssqlTestModel extends Model {
+
+/**
+ * name property
+ *
+ * @var string 'MssqlTestModel'
+ * @access public
+ */
+	var $name = 'MssqlTestModel';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * _schema property
+ *
+ * @var array
+ * @access protected
+ */
+	var $_schema = array(
+		'id'		=> array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8', 'key' => 'primary'),
+		'client_id'	=> array('type' => 'integer', 'null' => '', 'default' => '0', 'length' => '11'),
+		'name'		=> array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+		'login'		=> array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+		'passwd'	=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '255'),
+		'addr_1'	=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '255'),
+		'addr_2'	=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '25'),
+		'zip_code'	=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+		'city'		=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+		'country'	=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+		'phone'		=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+		'fax'		=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+		'url'		=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '255'),
+		'email'		=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+		'comments'	=> array('type' => 'text', 'null' => '1', 'default' => '', 'length' => ''),
+		'last_login'=> array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => ''),
+		'created'	=> array('type' => 'date', 'null' => '1', 'default' => '', 'length' => ''),
+		'updated'	=> array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => null)
+	);
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array(
+		'MssqlClientTestModel' => array(
+			'foreignKey' => 'client_id'
+		)
+	);
+/**
+ * find method
+ *
+ * @param mixed $conditions
+ * @param mixed $fields
+ * @param mixed $order
+ * @param mixed $recursive
+ * @access public
+ * @return void
+ */
+	function find($conditions = null, $fields = null, $order = null, $recursive = null) {
+		return $conditions;
+	}
+
+/**
+ * findAll method
+ *
+ * @param mixed $conditions
+ * @param mixed $fields
+ * @param mixed $order
+ * @param mixed $recursive
+ * @access public
+ * @return void
+ */
+	function findAll($conditions = null, $fields = null, $order = null, $recursive = null) {
+		return $conditions;
+	}
+
+/**
+ * setSchema method
+ *
+ * @param array $schema
+ * @access public
+ * @return void
+ */
+	function setSchema($schema) {
+		$this->_schema = $schema;
+	}
+}
+
+/**
+ * MssqlClientTestModel class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class MssqlClientTestModel extends Model {
+/**
+ * name property
+ *
+ * @var string 'MssqlAssociatedTestModel'
+ * @access public
+ */
+	var $name = 'MssqlClientTestModel';
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+/**
+ * _schema property
+ *
+ * @var array
+ * @access protected
+ */
+	var $_schema = array(
+		'id'		=> array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8', 'key' => 'primary'),
+		'name'		=> array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+		'email'		=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+		'created'	=> array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => ''),
+		'updated'	=> array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => null)
+	);
+}
+/**
+ * DboMssqlTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources.dbo
+ */
+class DboMssqlTest extends CakeTestCase {
+
+/**
+ * The Dbo instance to be tested
+ *
+ * @var DboSource
+ * @access public
+ */
+	var $db = null;
+
+/**
+ * autoFixtures property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $autoFixtures = false;
+/**
+ * fixtures property
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array('core.category');
+/**
+ * Skip if cannot connect to mssql
+ *
+ * @access public
+ */
+	function skip() {
+		$this->_initDb();
+		$this->skipUnless($this->db->config['driver'] == 'mssql', '%s SQL Server connection not available');
+	}
+
+/**
+ * Make sure all fixtures tables are being created
+ *
+ * @access public
+ */
+	function start() {
+		$this->db->simulate = false;
+		parent::start();
+		$this->db->simulate = true;
+	}
+/**
+ * Make sure all fixtures tables are being dropped
+ *
+ * @access public
+ */
+	function end() {
+		$this->db->simulate = false;
+		parent::end();
+		$this->db->simulate = true;
+	}
+/**
+ * Sets up a Dbo class instance for testing
+ *
+ * @access public
+ */
+	function setUp() {
+		$db = ConnectionManager::getDataSource('test_suite');
+		$this->db = new DboMssqlTestDb($db->config);
+		$this->model = new MssqlTestModel();
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		unset($this->model);
+	}
+
+/**
+ * testQuoting method
+ *
+ * @access public
+ * @return void
+ */
+	function testQuoting() {
+		$expected = "1.2";
+		$result = $this->db->value(1.2, 'float');
+		$this->assertIdentical($expected, $result);
+
+		$expected = "'1,2'";
+		$result = $this->db->value('1,2', 'float');
+		$this->assertIdentical($expected, $result);
+
+		$expected = 'NULL';
+		$result = $this->db->value('', 'integer');
+		$this->assertIdentical($expected, $result);
+
+		$expected = 'NULL';
+		$result = $this->db->value('', 'float');
+		$this->assertIdentical($expected, $result);
+
+		$expected = 'NULL';
+		$result = $this->db->value('', 'binary');
+		$this->assertIdentical($expected, $result);
+	}
+/**
+ * testFields method
+ *
+ * @access public
+ * @return void
+ */
+	function testFields() {
+		$fields = array(
+			'[MssqlTestModel].[id] AS [MssqlTestModel__0]',
+			'[MssqlTestModel].[client_id] AS [MssqlTestModel__1]',
+			'[MssqlTestModel].[name] AS [MssqlTestModel__2]',
+			'[MssqlTestModel].[login] AS [MssqlTestModel__3]',
+			'[MssqlTestModel].[passwd] AS [MssqlTestModel__4]',
+			'[MssqlTestModel].[addr_1] AS [MssqlTestModel__5]',
+			'[MssqlTestModel].[addr_2] AS [MssqlTestModel__6]',
+			'[MssqlTestModel].[zip_code] AS [MssqlTestModel__7]',
+			'[MssqlTestModel].[city] AS [MssqlTestModel__8]',
+			'[MssqlTestModel].[country] AS [MssqlTestModel__9]',
+			'[MssqlTestModel].[phone] AS [MssqlTestModel__10]',
+			'[MssqlTestModel].[fax] AS [MssqlTestModel__11]',
+			'[MssqlTestModel].[url] AS [MssqlTestModel__12]',
+			'[MssqlTestModel].[email] AS [MssqlTestModel__13]',
+			'[MssqlTestModel].[comments] AS [MssqlTestModel__14]',
+			'CONVERT(VARCHAR(20), [MssqlTestModel].[last_login], 20) AS [MssqlTestModel__15]',
+			'[MssqlTestModel].[created] AS [MssqlTestModel__16]',
+			'CONVERT(VARCHAR(20), [MssqlTestModel].[updated], 20) AS [MssqlTestModel__17]'
+		);
+
+		$result = $this->db->fields($this->model);
+		$expected = $fields;
+		$this->assertEqual($result, $expected);
+
+		$this->db->clearFieldMappings();
+		$result = $this->db->fields($this->model, null, 'MssqlTestModel.*');
+		$expected = $fields;
+		$this->assertEqual($result, $expected);
+
+		$this->db->clearFieldMappings();
+		$result = $this->db->fields($this->model, null, array('*', 'AnotherModel.id', 'AnotherModel.name'));
+		$expected = array_merge($fields, array(
+			'[AnotherModel].[id] AS [AnotherModel__18]',
+			'[AnotherModel].[name] AS [AnotherModel__19]'));
+		$this->assertEqual($result, $expected);
+
+		$this->db->clearFieldMappings();
+		$result = $this->db->fields($this->model, null, array('*', 'MssqlClientTestModel.*'));
+		$expected = array_merge($fields, array(
+			'[MssqlClientTestModel].[id] AS [MssqlClientTestModel__18]',
+			'[MssqlClientTestModel].[name] AS [MssqlClientTestModel__19]',
+			'[MssqlClientTestModel].[email] AS [MssqlClientTestModel__20]',
+			'CONVERT(VARCHAR(20), [MssqlClientTestModel].[created], 20) AS [MssqlClientTestModel__21]',
+			'CONVERT(VARCHAR(20), [MssqlClientTestModel].[updated], 20) AS [MssqlClientTestModel__22]'));
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testDistinctFields method
+ *
+ * @access public
+ * @return void
+ */
+	function testDistinctFields() {
+		$result = $this->db->fields($this->model, null, array('DISTINCT Car.country_code'));
+		$expected = array('DISTINCT [Car].[country_code] AS [Car__0]');
+		$this->assertEqual($result, $expected);
+
+		$result = $this->db->fields($this->model, null, 'DISTINCT Car.country_code');
+		$expected = array('DISTINCT [Car].[country_code] AS [Car__1]');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testDistinctWithLimit method
+ *
+ * @access public
+ * @return void
+ */
+	function testDistinctWithLimit() {
+		$this->db->read($this->model, array(
+			'fields' => array('DISTINCT MssqlTestModel.city', 'MssqlTestModel.country'),
+			'limit' => 5
+		));
+		$result = $this->db->getLastQuery();
+		$this->assertPattern('/^SELECT DISTINCT TOP 5/', $result);
+	}
+
+/**
+ * testDescribe method
+ *
+ * @access public
+ * @return void
+ */
+	function testDescribe() {
+		$MssqlTableDescription = array(
+			0 => array(
+				0 => array(
+					'Default' => '((0))',
+					'Field' => 'count',
+					'Key' => 0,
+					'Length' => '4',
+					'Null' => 'NO',
+					'Type' => 'integer',
+				)
+			)
+		);
+		$this->db->fetchAllResultsStack = array($MssqlTableDescription);
+		$dummyModel = $this->model;
+		$result = $this->db->describe($dummyModel);
+		$expected = array(
+			'count' => array(
+				'type' => 'integer',
+				'null' => false,
+				'default' => '0',
+				'length' => 4
+			)
+		);
+		$this->assertEqual($result, $expected);
+	}
+/**
+ * testBuildColumn
+ *
+ * @return unknown_type
+ * @access public
+ */
+	function testBuildColumn() {
+		$column = array('name' => 'id', 'type' => 'integer', 'null' => '', 'default' => '', 'length' => '8', 'key' => 'primary');
+		$result = $this->db->buildColumn($column);
+		$expected = '[id] int IDENTITY (1, 1) NOT NULL';
+		$this->assertEqual($result, $expected);
+
+		$column = array('name' => 'client_id', 'type' => 'integer', 'null' => '', 'default' => '0', 'length' => '11');
+		$result = $this->db->buildColumn($column);
+		$expected = '[client_id] int DEFAULT 0 NOT NULL';
+		$this->assertEqual($result, $expected);
+
+		$column = array('name' => 'client_id', 'type' => 'integer', 'null' => true);
+		$result = $this->db->buildColumn($column);
+		$expected = '[client_id] int NULL';
+		$this->assertEqual($result, $expected);
+
+		// 'name' => 'type' format for columns
+		$column = array('type' => 'integer', 'name' => 'client_id');
+		$result = $this->db->buildColumn($column);
+		$expected = '[client_id] int NULL';
+		$this->assertEqual($result, $expected);
+
+		$column = array('type' => 'string', 'name' => 'name');
+		$result = $this->db->buildColumn($column);
+		$expected = '[name] varchar(255) NULL';
+		$this->assertEqual($result, $expected);
+
+		$column = array('name' => 'name', 'type' => 'string', 'null' => '', 'default' => '', 'length' => '255');
+		$result = $this->db->buildColumn($column);
+		$expected = '[name] varchar(255) DEFAULT \'\' NOT NULL';
+		$this->assertEqual($result, $expected);
+
+		$column = array('name' => 'name', 'type' => 'string', 'null' => false, 'length' => '255');
+		$result = $this->db->buildColumn($column);
+		$expected = '[name] varchar(255) NOT NULL';
+		$this->assertEqual($result, $expected);
+
+		$column = array('name' => 'name', 'type' => 'string', 'null' => false, 'default' => null, 'length' => '255');
+		$result = $this->db->buildColumn($column);
+		$expected = '[name] varchar(255) NOT NULL';
+		$this->assertEqual($result, $expected);
+
+		$column = array('name' => 'name', 'type' => 'string', 'null' => true, 'default' => null, 'length' => '255');
+		$result = $this->db->buildColumn($column);
+		$expected = '[name] varchar(255) NULL';
+		$this->assertEqual($result, $expected);
+
+		$column = array('name' => 'name', 'type' => 'string', 'null' => true, 'default' => '', 'length' => '255');
+		$result = $this->db->buildColumn($column);
+		$expected = '[name] varchar(255) DEFAULT \'\'';
+		$this->assertEqual($result, $expected);
+	}
+/**
+ * testBuildIndex method
+ *
+ * @return void
+ * @access public
+ */
+	function testBuildIndex() {
+		$indexes = array(
+			'PRIMARY' => array('column' => 'id', 'unique' => 1),
+			'client_id' => array('column' => 'client_id', 'unique' => 1)
+		);
+		$result = $this->db->buildIndex($indexes, 'items');
+		$expected = array(
+			'PRIMARY KEY ([id])',
+			'ALTER TABLE items ADD CONSTRAINT client_id UNIQUE([client_id]);'
+		);
+		$this->assertEqual($result, $expected);
+
+		$indexes = array('client_id' => array('column' => 'client_id'));
+		$result = $this->db->buildIndex($indexes, 'items');
+		$this->assertEqual($result, array());
+
+		$indexes = array('client_id' => array('column' => array('client_id', 'period_id'), 'unique' => 1));
+		$result = $this->db->buildIndex($indexes, 'items');
+		$expected = array('ALTER TABLE items ADD CONSTRAINT client_id UNIQUE([client_id], [period_id]);');
+		$this->assertEqual($result, $expected);
+	}
+/**
+ * testUpdateAllSyntax method
+ *
+ * @return void
+ * @access public
+ */
+	function testUpdateAllSyntax() {
+		$fields = array('MssqlTestModel.client_id' => '[MssqlTestModel].[client_id] + 1');
+		$conditions = array('MssqlTestModel.updated <' => date('2009-01-01 00:00:00'));
+		$this->db->update($this->model, $fields, null, $conditions);
+
+		$result = $this->db->getLastQuery();
+		$this->assertNoPattern('/MssqlTestModel/', $result);
+		$this->assertPattern('/^UPDATE \[mssql_test_models\]/', $result);
+		$this->assertPattern('/SET \[client_id\] = \[client_id\] \+ 1/', $result);
+	}
+
+/**
+ * testGetPrimaryKey method
+ *
+ * @return void
+ * @access public
+ */
+	function testGetPrimaryKey() {
+		// When param is a model
+		$result = $this->db->getPrimaryKey($this->model);
+		$this->assertEqual($result, 'id');
+
+		$schema = $this->model->schema();
+		unset($schema['id']['key']);
+		$this->model->setSchema($schema);
+		$result = $this->db->getPrimaryKey($this->model);
+		$this->assertNull($result);
+
+		// When param is a table name
+		$this->db->simulate = false;
+		$this->loadFixtures('Category');
+		$result = $this->db->getPrimaryKey('categories');
+		$this->assertEqual($result, 'id');
+	}
+
+/**
+ * testInsertMulti
+ *
+ * @return void
+ * @access public
+ */
+	function testInsertMulti() {
+		$fields = array('id', 'name', 'login');
+		$values = array('(1, \'Larry\', \'PhpNut\')', '(2, \'Renan\', \'renan.saddam\')');
+		$this->db->simulated = array();
+		$this->db->insertMulti($this->model, $fields, $values);
+		$result = $this->db->simulated;
+		$expected = array(
+			'SET IDENTITY_INSERT [mssql_test_models] ON',
+			'INSERT INTO [mssql_test_models] ([id], [name], [login]) VALUES (1, \'Larry\', \'PhpNut\')',
+    		'INSERT INTO [mssql_test_models] ([id], [name], [login]) VALUES (2, \'Renan\', \'renan.saddam\')',
+			'SET IDENTITY_INSERT [mssql_test_models] OFF'
+		);
+		$this->assertEqual($result, $expected);
+
+		$fields = array('name', 'login');
+		$values = array('(\'Larry\', \'PhpNut\')', '(\'Renan\', \'renan.saddam\')');
+		$this->db->simulated = array();
+		$this->db->insertMulti($this->model, $fields, $values);
+		$result = $this->db->simulated;
+		$expected = array(
+			'INSERT INTO [mssql_test_models] ([name], [login]) VALUES (\'Larry\', \'PhpNut\')',
+    		'INSERT INTO [mssql_test_models] ([name], [login]) VALUES (\'Renan\', \'renan.saddam\')'
+		);
+		$this->assertEqual($result, $expected);
+	}
+/**
+ * testLastError
+ *
+ * @return void
+ * @access public
+ */
+	function testLastError() {
+		$debug = Configure::read('debug');
+		Configure::write('debug', 0);
+
+		$this->db->simulate = false;
+		$query = 'SELECT [name] FROM [categories]';
+		$this->assertTrue($this->db->execute($query) !== false);
+		$this->assertNull($this->db->lastError());
+
+		$query = 'SELECT [inexistent_field] FROM [categories]';
+		$this->assertFalse($this->db->execute($query));
+		$this->assertNotNull($this->db->lastError());
+
+		$query = 'SELECT [name] FROM [categories]';
+		$this->assertTrue($this->db->execute($query) !== false);
+		$this->assertNull($this->db->lastError());
+
+		Configure::write('debug', $debug);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo/dbo_mysql.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo/dbo_mysql.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo/dbo_mysql.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,895 @@
+<?php
+/**
+ * DboMysqlTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Core', array('Model', 'DataSource', 'DboSource', 'DboMysql'));
+App::import('Core', array('AppModel', 'Model'));
+require_once dirname(dirname(dirname(__FILE__))) . DS . 'models.php';
+
+Mock::generatePartial('DboMysql', 'QueryMockDboMysql', array('query', 'execute'));
+
+/**
+ * MysqlTestModel class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class MysqlTestModel extends Model {
+
+/**
+ * name property
+ *
+ * @var string 'MysqlTestModel'
+ * @access public
+ */
+	var $name = 'MysqlTestModel';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * find method
+ *
+ * @param mixed $conditions
+ * @param mixed $fields
+ * @param mixed $order
+ * @param mixed $recursive
+ * @access public
+ * @return void
+ */
+	function find($conditions = null, $fields = null, $order = null, $recursive = null) {
+		return $conditions;
+	}
+
+/**
+ * findAll method
+ *
+ * @param mixed $conditions
+ * @param mixed $fields
+ * @param mixed $order
+ * @param mixed $recursive
+ * @access public
+ * @return void
+ */
+	function findAll($conditions = null, $fields = null, $order = null, $recursive = null) {
+		return $conditions;
+	}
+
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function schema() {
+		return array(
+			'id'		=> array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8'),
+			'client_id'	=> array('type' => 'integer', 'null' => '', 'default' => '0', 'length' => '11'),
+			'name'		=> array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+			'login'		=> array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+			'passwd'	=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '255'),
+			'addr_1'	=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '255'),
+			'addr_2'	=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '25'),
+			'zip_code'	=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+			'city'		=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+			'country'	=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+			'phone'		=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+			'fax'		=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+			'url'		=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '255'),
+			'email'		=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+			'comments'	=> array('type' => 'text', 'null' => '1', 'default' => '', 'length' => ''),
+			'last_login'=> array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => ''),
+			'created'	=> array('type' => 'date', 'null' => '1', 'default' => '', 'length' => ''),
+			'updated'	=> array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => null)
+		);
+	}
+}
+
+/**
+ * DboMysqlTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources.dbo
+ */
+class DboMysqlTest extends CakeTestCase {
+	var $fixtures = array('core.binary_test', 'core.post', 'core.author');
+/**
+ * The Dbo instance to be tested
+ *
+ * @var DboSource
+ * @access public
+ */
+	var $Db = null;
+
+/**
+ * Skip if cannot connect to mysql
+ *
+ * @access public
+ */
+	function skip() {
+		$this->_initDb();
+		$this->skipUnless($this->db->config['driver'] == 'mysql', '%s MySQL connection not available');
+	}
+
+/**
+ * Sets up a Dbo class instance for testing
+ *
+ * @access public
+ */
+	function startTest() {
+		$db = ConnectionManager::getDataSource('test_suite');
+		$this->model = new MysqlTestModel();
+	}
+
+/**
+ * Sets up a Dbo class instance for testing
+ *
+ * @access public
+ */
+	function tearDown() {
+		unset($this->model);
+		ClassRegistry::flush();
+	}
+
+/**
+ * startCase
+ *
+ * @return void
+ */
+	function startCase() {
+		$this->_debug = Configure::read('debug');
+		Configure::write('debug', 1);
+	}
+
+/**
+ * endCase
+ *
+ * @return void
+ */
+	function endCase() {
+		Configure::write('debug', $this->_debug);
+	}
+
+/**
+ * Test Dbo value method
+ *
+ * @access public
+ */
+	function testQuoting() {
+		$result = $this->db->fields($this->model);
+		$expected = array(
+			'`MysqlTestModel`.`id`',
+			'`MysqlTestModel`.`client_id`',
+			'`MysqlTestModel`.`name`',
+			'`MysqlTestModel`.`login`',
+			'`MysqlTestModel`.`passwd`',
+			'`MysqlTestModel`.`addr_1`',
+			'`MysqlTestModel`.`addr_2`',
+			'`MysqlTestModel`.`zip_code`',
+			'`MysqlTestModel`.`city`',
+			'`MysqlTestModel`.`country`',
+			'`MysqlTestModel`.`phone`',
+			'`MysqlTestModel`.`fax`',
+			'`MysqlTestModel`.`url`',
+			'`MysqlTestModel`.`email`',
+			'`MysqlTestModel`.`comments`',
+			'`MysqlTestModel`.`last_login`',
+			'`MysqlTestModel`.`created`',
+			'`MysqlTestModel`.`updated`'
+		);
+		$this->assertEqual($result, $expected);
+
+		$expected = 1.2;
+		$result = $this->db->value(1.2, 'float');
+		$this->assertEqual($expected, $result);
+
+		$expected = "'1,2'";
+		$result = $this->db->value('1,2', 'float');
+		$this->assertEqual($expected, $result);
+
+		$expected = "'4713e29446'";
+		$result = $this->db->value('4713e29446');
+
+		$this->assertEqual($expected, $result);
+
+		$expected = 'NULL';
+		$result = $this->db->value('', 'integer');
+		$this->assertEqual($expected, $result);
+
+		$expected = 'NULL';
+		$result = $this->db->value('', 'boolean');
+		$this->assertEqual($expected, $result);
+
+		$expected = 10010001;
+		$result = $this->db->value(10010001);
+		$this->assertEqual($expected, $result);
+
+		$expected = "'00010010001'";
+		$result = $this->db->value('00010010001');
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * test that localized floats don't cause trouble.
+ *
+ * @return void
+ */
+	function testLocalizedFloats() {
+		$restore = setlocale(LC_ALL, null);
+		setlocale(LC_ALL, 'de_DE');
+
+		$result = $this->db->value(3.141593, 'float');
+		$this->assertEqual('3.141593', $result);
+
+		$result = $this->db->value(3.141593);
+		$this->assertEqual('3.141593', $result);
+
+		$result = $this->db->value(3.141593);
+		$this->assertEqual('3.141593', $result);
+
+		$result = $this->db->value(1234567.11, 'float');
+		$this->assertEqual('1234567.11', $result);
+
+		$result = $this->db->value(123456.45464748, 'float');
+		$this->assertEqual('123456.454647', $result);
+
+		$result = $this->db->value(0.987654321, 'float');
+		$this->assertEqual('0.987654321', (string)$result);
+
+		$result = $this->db->value(2.2E-54, 'float');
+		$this->assertEqual('2.2E-54', (string)$result);
+
+		$result = $this->db->value(2.2E-54);
+		$this->assertEqual('2.2E-54', (string)$result);
+
+		setlocale(LC_ALL, $restore);
+	}
+
+/**
+ * test that scientific notations are working correctly
+ *
+ * @return void
+ */
+	function testScientificNotation() {
+		$result = $this->db->value(2.2E-54, 'float');
+		$this->assertEqual('2.2E-54', (string)$result);
+
+		$result = $this->db->value(2.2E-54, 'float');
+		$this->assertEqual('2.2E-54', (string)$result);
+
+		$result = $this->db->value(2.2E-54);
+		$this->assertEqual('2.2E-54', (string)$result);
+	}
+
+/**
+ * testTinyintCasting method
+ *
+ * @access public
+ * @return void
+ */
+	function testTinyintCasting() {
+		$this->db->cacheSources = false;
+		$this->db->query('CREATE TABLE ' . $this->db->fullTableName('tinyint') . ' (id int(11) AUTO_INCREMENT, bool tinyint(1), small_int tinyint(2), primary key(id));');
+
+		$this->model = new CakeTestModel(array(
+			'name' => 'Tinyint', 'table' => 'tinyint', 'ds' => 'test_suite'
+		));
+
+		$result = $this->model->schema();
+		$this->assertEqual($result['bool']['type'], 'boolean');
+		$this->assertEqual($result['small_int']['type'], 'integer');
+
+		$this->assertTrue($this->model->save(array('bool' => 5, 'small_int' => 5)));
+		$result = $this->model->find('first');
+		$this->assertIdentical($result['Tinyint']['bool'], '1');
+		$this->assertIdentical($result['Tinyint']['small_int'], '5');
+		$this->model->deleteAll(true);
+
+		$this->assertTrue($this->model->save(array('bool' => 0, 'small_int' => 100)));
+		$result = $this->model->find('first');
+		$this->assertIdentical($result['Tinyint']['bool'], '0');
+		$this->assertIdentical($result['Tinyint']['small_int'], '100');
+		$this->model->deleteAll(true);
+
+		$this->assertTrue($this->model->save(array('bool' => true, 'small_int' => 0)));
+		$result = $this->model->find('first');
+		$this->assertIdentical($result['Tinyint']['bool'], '1');
+		$this->assertIdentical($result['Tinyint']['small_int'], '0');
+		$this->model->deleteAll(true);
+
+		$this->db->query('DROP TABLE ' . $this->db->fullTableName('tinyint'));
+	}
+
+/**
+ * testIndexDetection method
+ *
+ * @return void
+ * @access public
+ */
+	function testIndexDetection() {
+		$this->db->cacheSources = false;
+
+		$name = $this->db->fullTableName('simple');
+		$this->db->query('CREATE TABLE ' . $name . ' (id int(11) AUTO_INCREMENT, bool tinyint(1), small_int tinyint(2), primary key(id));');
+		$expected = array('PRIMARY' => array('column' => 'id', 'unique' => 1));
+		$result = $this->db->index('simple', false);
+		$this->assertEqual($expected, $result);
+		$this->db->query('DROP TABLE ' . $name);
+
+		$name = $this->db->fullTableName('with_a_key');
+		$this->db->query('CREATE TABLE ' . $name . ' (id int(11) AUTO_INCREMENT, bool tinyint(1), small_int tinyint(2), primary key(id), KEY `pointless_bool` ( `bool` ));');
+		$expected = array(
+			'PRIMARY' => array('column' => 'id', 'unique' => 1),
+			'pointless_bool' => array('column' => 'bool', 'unique' => 0),
+		);
+		$result = $this->db->index('with_a_key', false);
+		$this->assertEqual($expected, $result);
+		$this->db->query('DROP TABLE ' . $name);
+
+		$name = $this->db->fullTableName('with_two_keys');
+		$this->db->query('CREATE TABLE ' . $name . ' (id int(11) AUTO_INCREMENT, bool tinyint(1), small_int tinyint(2), primary key(id), KEY `pointless_bool` ( `bool` ), KEY `pointless_small_int` ( `small_int` ));');
+		$expected = array(
+			'PRIMARY' => array('column' => 'id', 'unique' => 1),
+			'pointless_bool' => array('column' => 'bool', 'unique' => 0),
+			'pointless_small_int' => array('column' => 'small_int', 'unique' => 0),
+		);
+		$result = $this->db->index('with_two_keys', false);
+		$this->assertEqual($expected, $result);
+		$this->db->query('DROP TABLE ' . $name);
+
+		$name = $this->db->fullTableName('with_compound_keys');
+		$this->db->query('CREATE TABLE ' . $name . ' (id int(11) AUTO_INCREMENT, bool tinyint(1), small_int tinyint(2), primary key(id), KEY `pointless_bool` ( `bool` ), KEY `pointless_small_int` ( `small_int` ), KEY `one_way` ( `bool`, `small_int` ));');
+		$expected = array(
+			'PRIMARY' => array('column' => 'id', 'unique' => 1),
+			'pointless_bool' => array('column' => 'bool', 'unique' => 0),
+			'pointless_small_int' => array('column' => 'small_int', 'unique' => 0),
+			'one_way' => array('column' => array('bool', 'small_int'), 'unique' => 0),
+		);
+		$result = $this->db->index('with_compound_keys', false);
+		$this->assertEqual($expected, $result);
+		$this->db->query('DROP TABLE ' . $name);
+
+		$name = $this->db->fullTableName('with_multiple_compound_keys');
+		$this->db->query('CREATE TABLE ' . $name . ' (id int(11) AUTO_INCREMENT, bool tinyint(1), small_int tinyint(2), primary key(id), KEY `pointless_bool` ( `bool` ), KEY `pointless_small_int` ( `small_int` ), KEY `one_way` ( `bool`, `small_int` ), KEY `other_way` ( `small_int`, `bool` ));');
+		$expected = array(
+			'PRIMARY' => array('column' => 'id', 'unique' => 1),
+			'pointless_bool' => array('column' => 'bool', 'unique' => 0),
+			'pointless_small_int' => array('column' => 'small_int', 'unique' => 0),
+			'one_way' => array('column' => array('bool', 'small_int'), 'unique' => 0),
+			'other_way' => array('column' => array('small_int', 'bool'), 'unique' => 0),
+		);
+		$result = $this->db->index('with_multiple_compound_keys', false);
+		$this->assertEqual($expected, $result);
+		$this->db->query('DROP TABLE ' . $name);
+	}
+
+/**
+ * testBuildColumn method
+ *
+ * @access public
+ * @return void
+ */
+	function testBuildColumn() {
+		$restore = $this->db->columns;
+		$this->db->columns = array('varchar(255)' => 1);
+		$data = array(
+			'name' => 'testName',
+			'type' => 'varchar(255)',
+			'default',
+			'null' => true,
+			'key',
+			'comment' => 'test'
+		);
+		$result = $this->db->buildColumn($data);
+		$expected = '`testName`  DEFAULT NULL COMMENT \'test\'';
+		$this->assertEqual($result, $expected);
+
+		$data = array(
+			'name' => 'testName',
+			'type' => 'varchar(255)',
+			'default',
+			'null' => true,
+			'key',
+			'charset' => 'utf8',
+			'collate' => 'utf8_unicode_ci'
+		);
+		$result = $this->db->buildColumn($data);
+		$expected = '`testName`  CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL';
+		$this->assertEqual($result, $expected);
+		$this->db->columns = $restore;
+	}
+
+/**
+ * MySQL 4.x returns index data in a different format,
+ * Using a mock ensure that MySQL 4.x output is properly parsed.
+ *
+ * @return void
+ */
+	function testIndexOnMySQL4Output() {
+		$name = $this->db->fullTableName('simple');
+
+		$mockDbo =& new QueryMockDboMysql($this);
+		$columnData = array(
+			array('0' => array(
+				'Table' => 'with_compound_keys',
+				'Non_unique' => '0',
+				'Key_name' => 'PRIMARY',
+				'Seq_in_index' => '1',
+				'Column_name' => 'id',
+				'Collation' => 'A',
+				'Cardinality' => '0',
+				'Sub_part' => NULL,
+				'Packed' => NULL,
+				'Null' => '',
+				'Index_type' => 'BTREE',
+				'Comment' => ''
+			)),
+			array('0' => array(
+				'Table' => 'with_compound_keys',
+				'Non_unique' => '1',
+				'Key_name' => 'pointless_bool',
+				'Seq_in_index' => '1',
+				'Column_name' => 'bool',
+				'Collation' => 'A',
+				'Cardinality' => NULL,
+				'Sub_part' => NULL,
+				'Packed' => NULL,
+				'Null' => 'YES',
+				'Index_type' => 'BTREE',
+				'Comment' => ''
+			)),
+			array('0' => array(
+				'Table' => 'with_compound_keys',
+				'Non_unique' => '1',
+				'Key_name' => 'pointless_small_int',
+				'Seq_in_index' => '1',
+				'Column_name' => 'small_int',
+				'Collation' => 'A',
+				'Cardinality' => NULL,
+				'Sub_part' => NULL,
+				'Packed' => NULL,
+				'Null' => 'YES',
+				'Index_type' => 'BTREE',
+				'Comment' => ''
+			)),
+			array('0' => array(
+				'Table' => 'with_compound_keys',
+				'Non_unique' => '1',
+				'Key_name' => 'one_way',
+				'Seq_in_index' => '1',
+				'Column_name' => 'bool',
+				'Collation' => 'A',
+				'Cardinality' => NULL,
+				'Sub_part' => NULL,
+				'Packed' => NULL,
+				'Null' => 'YES',
+				'Index_type' => 'BTREE',
+				'Comment' => ''
+			)),
+			array('0' => array(
+				'Table' => 'with_compound_keys',
+				'Non_unique' => '1',
+				'Key_name' => 'one_way',
+				'Seq_in_index' => '2',
+				'Column_name' => 'small_int',
+				'Collation' => 'A',
+				'Cardinality' => NULL,
+				'Sub_part' => NULL,
+				'Packed' => NULL,
+				'Null' => 'YES',
+				'Index_type' => 'BTREE',
+				'Comment' => ''
+			))
+		);
+		$mockDbo->setReturnValue('query', $columnData, array('SHOW INDEX FROM ' . $name));
+
+		$result = $mockDbo->index($name, false);
+		$expected = array(
+			'PRIMARY' => array('column' => 'id', 'unique' => 1),
+			'pointless_bool' => array('column' => 'bool', 'unique' => 0),
+			'pointless_small_int' => array('column' => 'small_int', 'unique' => 0),
+			'one_way' => array('column' => array('bool', 'small_int'), 'unique' => 0),
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testColumn method
+ *
+ * @return void
+ * @access public
+ */
+	function testColumn() {
+		$result = $this->db->column('varchar(50)');
+		$expected = 'string';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->db->column('text');
+		$expected = 'text';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->db->column('int(11)');
+		$expected = 'integer';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->db->column('int(11) unsigned');
+		$expected = 'integer';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->db->column('tinyint(1)');
+		$expected = 'boolean';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->db->column('boolean');
+		$expected = 'boolean';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->db->column('float');
+		$expected = 'float';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->db->column('float unsigned');
+		$expected = 'float';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->db->column('double unsigned');
+		$expected = 'float';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->db->column('decimal(14,7) unsigned');
+		$expected = 'float';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testAlterSchemaIndexes method
+ *
+ * @access public
+ * @return void
+ */
+	function testAlterSchemaIndexes() {
+		App::import('Model', 'CakeSchema');
+		$this->db->cacheSources = false;
+
+		$schema1 =& new CakeSchema(array(
+			'name' => 'AlterTest1',
+			'connection' => 'test_suite',
+			'altertest' => array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => 0),
+				'name' => array('type' => 'string', 'null' => false, 'length' => 50),
+				'group1' => array('type' => 'integer', 'null' => true),
+				'group2' => array('type' => 'integer', 'null' => true)
+		)));
+		$this->db->query($this->db->createSchema($schema1));
+
+		$schema2 =& new CakeSchema(array(
+			'name' => 'AlterTest2',
+			'connection' => 'test_suite',
+			'altertest' => array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => 0),
+				'name' => array('type' => 'string', 'null' => false, 'length' => 50),
+				'group1' => array('type' => 'integer', 'null' => true),
+				'group2' => array('type' => 'integer', 'null' => true),
+				'indexes' => array(
+					'name_idx' => array('column' => 'name', 'unique' => 0),
+					'group_idx' => array('column' => 'group1', 'unique' => 0),
+					'compound_idx' => array('column' => array('group1', 'group2'), 'unique' => 0),
+					'PRIMARY' => array('column' => 'id', 'unique' => 1))
+		)));
+		$this->db->query($this->db->alterSchema($schema2->compare($schema1)));
+
+		$indexes = $this->db->index('altertest');
+		$this->assertEqual($schema2->tables['altertest']['indexes'], $indexes);
+
+		// Change three indexes, delete one and add another one
+		$schema3 =& new CakeSchema(array(
+			'name' => 'AlterTest3',
+			'connection' => 'test_suite',
+			'altertest' => array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => 0),
+				'name' => array('type' => 'string', 'null' => false, 'length' => 50),
+				'group1' => array('type' => 'integer', 'null' => true),
+				'group2' => array('type' => 'integer', 'null' => true),
+				'indexes' => array(
+					'name_idx' => array('column' => 'name', 'unique' => 1),
+					'group_idx' => array('column' => 'group2', 'unique' => 0),
+					'compound_idx' => array('column' => array('group2', 'group1'), 'unique' => 0),
+					'id_name_idx' => array('column' => array('id', 'name'), 'unique' => 0))
+		)));
+
+		$this->db->query($this->db->alterSchema($schema3->compare($schema2)));
+
+		$indexes = $this->db->index('altertest');
+		$this->assertEqual($schema3->tables['altertest']['indexes'], $indexes);
+
+		// Compare us to ourself.
+		$this->assertEqual($schema3->compare($schema3), array());
+
+		// Drop the indexes
+		$this->db->query($this->db->alterSchema($schema1->compare($schema3)));
+
+		$indexes = $this->db->index('altertest');
+		$this->assertEqual(array(), $indexes);
+
+		$this->db->query($this->db->dropSchema($schema1));
+	}
+/**
+ * test saving and retrieval of blobs
+ *
+ * @return void
+ */
+	function testBlobSaving() {
+		$this->db->cacheSources = false;
+		$data = "GIF87ab 
+		 Ò   4A¿¿¿ˇˇˇ   ,    b 
+		  ¢îè©ÀÌ#¥⁄ã≥fi:¯Ü‚Héá¶jV∂ÓúÎL≥çÀóËıÎ…>ï ≈ vFE%ÒâLFI<†µw˝±≈£7˘ç^H“≤«>Éâ*∑Ç nÖA•Ù|flêèj£:=ÿ6óUàµ5'∂®àA¬ñ∆ˆGE(gt’≈àÚyÁó«7	‚VìöÇ√˙Ç™
+		k”:;kÀAõ{*¡€Î˚˚[  ;;";
+
+		$model =& new AppModel(array('name' => 'BinaryTest', 'ds' => 'test_suite'));
+		$model->save(compact('data'));
+
+		$result = $model->find('first');
+		$this->assertEqual($result['BinaryTest']['data'], $data);
+	}
+
+/**
+ * test altering the table settings with schema.
+ *
+ * @return void
+ */
+	function testAlteringTableParameters() {
+		App::import('Model', 'CakeSchema');
+		$this->db->cacheSources = false;
+
+		$schema1 =& new CakeSchema(array(
+			'name' => 'AlterTest1',
+			'connection' => 'test_suite',
+			'altertest' => array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => 0),
+				'name' => array('type' => 'string', 'null' => false, 'length' => 50),
+				'tableParameters' => array(
+					'charset' => 'latin1',
+					'collate' => 'latin1_general_ci',
+					'engine' => 'MyISAM'
+				)
+			)
+		));
+		$this->db->query($this->db->createSchema($schema1));
+		$schema2 =& new CakeSchema(array(
+			'name' => 'AlterTest1',
+			'connection' => 'test_suite',
+			'altertest' => array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => 0),
+				'name' => array('type' => 'string', 'null' => false, 'length' => 50),
+				'tableParameters' => array(
+					'charset' => 'utf8',
+					'collate' => 'utf8_general_ci',
+					'engine' => 'InnoDB'
+				)
+			)
+		));
+		$result = $this->db->alterSchema($schema2->compare($schema1));
+		$this->assertPattern('/DEFAULT CHARSET=utf8/', $result);
+		$this->assertPattern('/ENGINE=InnoDB/', $result);
+		$this->assertPattern('/COLLATE=utf8_general_ci/', $result);
+
+		$this->db->query($result);
+		$result = $this->db->listDetailedSources('altertest');
+		$this->assertEqual($result['Collation'], 'utf8_general_ci');
+		$this->assertEqual($result['Engine'], 'InnoDB');
+		$this->assertEqual($result['charset'], 'utf8');
+
+		$this->db->query($this->db->dropSchema($schema1));
+	}
+
+/**
+ * test alterSchema on two tables.
+ *
+ * @return void
+ */
+	function testAlteringTwoTables() {
+		$schema1 =& new CakeSchema(array(
+			'name' => 'AlterTest1',
+			'connection' => 'test_suite',
+			'altertest' => array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => 0),
+				'name' => array('type' => 'string', 'null' => false, 'length' => 50),
+			),
+			'other_table' => array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => 0),
+				'name' => array('type' => 'string', 'null' => false, 'length' => 50),
+			)
+		));
+		$schema2 =& new CakeSchema(array(
+			'name' => 'AlterTest1',
+			'connection' => 'test_suite',
+			'altertest' => array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => 0),
+				'field_two' => array('type' => 'string', 'null' => false, 'length' => 50),
+			),
+			'other_table' => array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => 0),
+				'field_two' => array('type' => 'string', 'null' => false, 'length' => 50),
+			)
+		));
+		$result = $this->db->alterSchema($schema2->compare($schema1));
+		$this->assertEqual(2, substr_count($result, 'field_two'), 'Too many fields');
+	}
+
+/**
+ * testReadTableParameters method
+ *
+ * @access public
+ * @return void
+ */
+	function testReadTableParameters() {
+		$this->db->cacheSources = false;
+		$this->db->query('CREATE TABLE ' . $this->db->fullTableName('tinyint') . ' (id int(11) AUTO_INCREMENT, bool tinyint(1), small_int tinyint(2), primary key(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;');
+		$result = $this->db->readTableParameters('tinyint');
+		$expected = array(
+			'charset' => 'utf8',
+			'collate' => 'utf8_unicode_ci',
+			'engine' => 'InnoDB');
+		$this->assertEqual($result, $expected);
+
+		$this->db->query('DROP TABLE ' . $this->db->fullTableName('tinyint'));
+		$this->db->query('CREATE TABLE ' . $this->db->fullTableName('tinyint') . ' (id int(11) AUTO_INCREMENT, bool tinyint(1), small_int tinyint(2), primary key(id)) ENGINE=MyISAM DEFAULT CHARSET=cp1250 COLLATE=cp1250_general_ci;');
+		$result = $this->db->readTableParameters('tinyint');
+		$expected = array(
+			'charset' => 'cp1250',
+			'collate' => 'cp1250_general_ci',
+			'engine' => 'MyISAM');
+		$this->assertEqual($result, $expected);
+		$this->db->query('DROP TABLE ' . $this->db->fullTableName('tinyint'));
+	}
+
+/**
+ * testBuildTableParameters method
+ *
+ * @access public
+ * @return void
+ */
+	function testBuildTableParameters() {
+		$this->db->cacheSources = false;
+		$data = array(
+			'charset' => 'utf8',
+			'collate' => 'utf8_unicode_ci',
+			'engine' => 'InnoDB');
+		$result = $this->db->buildTableParameters($data);
+		$expected = array(
+			'DEFAULT CHARSET=utf8',
+			'COLLATE=utf8_unicode_ci',
+			'ENGINE=InnoDB');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testBuildTableParameters method
+ *
+ * @access public
+ * @return void
+ */
+	function testGetCharsetName() {
+		$this->db->cacheSources = false;
+		$result = $this->db->getCharsetName('utf8_unicode_ci');
+		$this->assertEqual($result, 'utf8');
+		$result = $this->db->getCharsetName('cp1250_general_ci');
+		$this->assertEqual($result, 'cp1250');
+	}
+
+/**
+ * test that changing the virtualFieldSeparator allows for __ fields.
+ *
+ * @return void
+ */
+	function testVirtualFieldSeparators() {
+		$model =& new CakeTestModel(array('table' => 'binary_tests', 'ds' => 'test_suite', 'name' => 'BinaryTest'));
+		$model->virtualFields = array(
+			'other__field' => 'SUM(id)'
+		);
+		
+		$this->db->virtualFieldSeparator = '_$_';
+		$result = $this->db->fields($model, null, array('data', 'other__field'));
+		$expected = array('`BinaryTest`.`data`', '(SUM(id)) AS  `BinaryTest_$_other__field`');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that a describe() gets additional fieldParameters
+ *
+ * @return void
+ */
+	function testDescribeGettingFieldParameters() {
+		$schema =& new CakeSchema(array(
+			'connection' => 'test_suite',
+			'testdescribes' => array(
+				'id' => array('type' => 'integer', 'key' => 'primary'),
+				'stringy' => array(
+					'type' => 'string',
+					'null' => true,
+					'charset' => 'cp1250',
+					'collate' => 'cp1250_general_ci',
+				),
+				'other_col' => array(
+					'type' => 'string',
+					'null' => false,
+					'charset' => 'latin1',
+					'comment' => 'Test Comment'
+				)
+			)
+		));
+		$this->db->execute($this->db->createSchema($schema));
+
+		$model =& new CakeTestModel(array('table' => 'testdescribes', 'name' => 'Testdescribes'));
+		$result = $this->db->describe($model);
+		$this->assertEqual($result['stringy']['collate'], 'cp1250_general_ci');
+		$this->assertEqual($result['stringy']['charset'], 'cp1250');
+		$this->assertEqual($result['other_col']['comment'], 'Test Comment');
+
+		$this->db->execute($this->db->dropSchema($schema));
+	}
+
+/**
+ * test that simple delete conditions don't create joins using a mock.
+ *
+ * @return void
+ */
+	function testSimpleDeleteConditionsNoJoins() {
+		$model =& new Post();
+		$mockDbo =& new QueryMockDboMysql($this);
+		$mockDbo->expectAt(0, 'execute', array(new PatternExpectation('/AS\s+`Post`\s+WHERE\s+`Post/')));
+		$mockDbo->setReturnValue('execute', true);
+
+		$mockDbo->delete($model, array('Post.id' => 1));
+	}
+
+/**
+ * test deleting with joins, a MySQL specific feature.
+ *
+ * @return void
+ */
+	function testDeleteWithJoins() {
+		$model =& new Post();
+		$mockDbo =& new QueryMockDboMysql($this);
+		$mockDbo->expectAt(0, 'execute', array(new PatternExpectation('/LEFT JOIN `authors`/')));
+		$mockDbo->setReturnValue('execute', true);
+
+		$mockDbo->delete($model, array('Author.id' => 1));
+	}
+
+/**
+ * test joins on delete with multiple conditions.
+ *
+ * @return void
+ */
+	function testDeleteWithJoinsAndMultipleConditions() {
+		$model =& new Post();
+		$mockDbo =& new QueryMockDboMysql($this);
+		$mockDbo->expectAt(0, 'execute', array(new PatternExpectation('/LEFT JOIN `authors`/')));
+		$mockDbo->expectAt(1, 'execute', array(new PatternExpectation('/LEFT JOIN `authors`/')));
+		$mockDbo->setReturnValue('execute', true);
+
+		$mockDbo->delete($model, array('Author.id' => 1, 'Post.id' => 2));
+		$mockDbo->delete($model, array('Post.id' => 2, 'Author.id' => 1));
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo/dbo_mysqli.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo/dbo_mysqli.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo/dbo_mysqli.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,339 @@
+<?php
+/**
+ * DboMysqliTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) {
+	define('CAKEPHP_UNIT_TEST_EXECUTION', 1);
+}
+App::import('Core', array('Model', 'DataSource', 'DboSource', 'DboMysqli'));
+
+/**
+ * DboMysqliTestDb class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class DboMysqliTestDb extends DboMysqli {
+
+/**
+ * simulated property
+ *
+ * @var array
+ * @access public
+ */
+	var $simulated = array();
+
+/**
+ * testing property
+ *
+ * @var bool true
+ * @access public
+ */
+	var $testing = true;
+
+/**
+ * execute method
+ *
+ * @param mixed $sql
+ * @access protected
+ * @return void
+ */
+	function _execute($sql) {
+		if ($this->testing) {
+			$this->simulated[] = $sql;
+			return null;
+		}
+		return parent::_execute($sql);
+	}
+
+/**
+ * getLastQuery method
+ *
+ * @access public
+ * @return void
+ */
+	function getLastQuery() {
+		return $this->simulated[count($this->simulated) - 1];
+	}
+}
+
+/**
+ * MysqliTestModel class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class MysqliTestModel extends Model {
+
+/**
+ * name property
+ *
+ * @var string 'MysqlTestModel'
+ * @access public
+ */
+	var $name = 'MysqliTestModel';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function schema() {
+		return array(
+			'id'		=> array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8'),
+			'client_id'	=> array('type' => 'integer', 'null' => '', 'default' => '0', 'length' => '11'),
+			'name'		=> array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+			'login'		=> array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+			'passwd'	=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '255'),
+			'addr_1'	=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '255'),
+			'addr_2'	=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '25'),
+			'zip_code'	=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+			'city'		=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+			'country'	=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+			'phone'		=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+			'fax'		=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+			'url'		=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '255'),
+			'email'		=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+			'comments'	=> array('type' => 'text', 'null' => '1', 'default' => '', 'length' => ''),
+			'last_login'=> array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => ''),
+			'created'	=> array('type' => 'date', 'null' => '1', 'default' => '', 'length' => ''),
+			'updated'	=> array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => null)
+		);
+	}
+}
+
+/**
+ * DboMysqliTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources.dbo
+ */
+class DboMysqliTest extends CakeTestCase {
+	var $fixtures = array('core.datatype');
+/**
+ * The Dbo instance to be tested
+ *
+ * @var DboSource
+ * @access public
+ */
+	var $Db = null;
+
+/**
+ * Skip if cannot connect to mysqli
+ *
+ * @access public
+ */
+	function skip() {
+		$this->_initDb();
+		$this->skipUnless($this->db->config['driver'] == 'mysqli', '%s MySQLi connection not available');
+	}
+
+/**
+ * Sets up a Dbo class instance for testing
+ *
+ * @access public
+ */
+	function setUp() {
+		$this->model = new MysqliTestModel();
+	}
+
+/**
+ * Sets up a Dbo class instance for testing
+ *
+ * @access public
+ */
+	function tearDown() {
+		unset($this->model);
+		ClassRegistry::flush();
+	}
+
+/**
+ * testIndexDetection method
+ *
+ * @return void
+ * @access public
+ */
+	function testIndexDetection() {
+		$this->db->cacheSources = false;
+
+		$name = $this->db->fullTableName('simple');
+		$this->db->query('CREATE TABLE ' . $name . ' (id int(11) AUTO_INCREMENT, bool tinyint(1), small_int tinyint(2), primary key(id));');
+		$expected = array('PRIMARY' => array('column' => 'id', 'unique' => 1));
+		$result = $this->db->index($name, false);
+		$this->assertEqual($expected, $result);
+		$this->db->query('DROP TABLE ' . $name);
+
+		$name = $this->db->fullTableName('with_a_key');
+		$this->db->query('CREATE TABLE ' . $name . ' (id int(11) AUTO_INCREMENT, bool tinyint(1), small_int tinyint(2), primary key(id), KEY `pointless_bool` ( `bool` ));');
+		$expected = array(
+			'PRIMARY' => array('column' => 'id', 'unique' => 1),
+			'pointless_bool' => array('column' => 'bool', 'unique' => 0),
+		);
+		$result = $this->db->index($name, false);
+		$this->assertEqual($expected, $result);
+		$this->db->query('DROP TABLE ' . $name);
+
+		$name = $this->db->fullTableName('with_two_keys');
+		$this->db->query('CREATE TABLE ' . $name . ' (id int(11) AUTO_INCREMENT, bool tinyint(1), small_int tinyint(2), primary key(id), KEY `pointless_bool` ( `bool` ), KEY `pointless_small_int` ( `small_int` ));');
+		$expected = array(
+			'PRIMARY' => array('column' => 'id', 'unique' => 1),
+			'pointless_bool' => array('column' => 'bool', 'unique' => 0),
+			'pointless_small_int' => array('column' => 'small_int', 'unique' => 0),
+		);
+		$result = $this->db->index($name, false);
+		$this->assertEqual($expected, $result);
+		$this->db->query('DROP TABLE ' . $name);
+
+		$name = $this->db->fullTableName('with_compound_keys');
+		$this->db->query('CREATE TABLE ' . $name . ' (id int(11) AUTO_INCREMENT, bool tinyint(1), small_int tinyint(2), primary key(id), KEY `pointless_bool` ( `bool` ), KEY `pointless_small_int` ( `small_int` ), KEY `one_way` ( `bool`, `small_int` ));');
+		$expected = array(
+			'PRIMARY' => array('column' => 'id', 'unique' => 1),
+			'pointless_bool' => array('column' => 'bool', 'unique' => 0),
+			'pointless_small_int' => array('column' => 'small_int', 'unique' => 0),
+			'one_way' => array('column' => array('bool', 'small_int'), 'unique' => 0),
+		);
+		$result = $this->db->index($name, false);
+		$this->assertEqual($expected, $result);
+		$this->db->query('DROP TABLE ' . $name);
+
+		$name = $this->db->fullTableName('with_multiple_compound_keys');
+		$this->db->query('CREATE TABLE ' . $name . ' (id int(11) AUTO_INCREMENT, bool tinyint(1), small_int tinyint(2), primary key(id), KEY `pointless_bool` ( `bool` ), KEY `pointless_small_int` ( `small_int` ), KEY `one_way` ( `bool`, `small_int` ), KEY `other_way` ( `small_int`, `bool` ));');
+		$expected = array(
+			'PRIMARY' => array('column' => 'id', 'unique' => 1),
+			'pointless_bool' => array('column' => 'bool', 'unique' => 0),
+			'pointless_small_int' => array('column' => 'small_int', 'unique' => 0),
+			'one_way' => array('column' => array('bool', 'small_int'), 'unique' => 0),
+			'other_way' => array('column' => array('small_int', 'bool'), 'unique' => 0),
+		);
+		$result = $this->db->index($name, false);
+		$this->assertEqual($expected, $result);
+		$this->db->query('DROP TABLE ' . $name);
+	}
+
+/**
+ * testColumn method
+ *
+ * @return void
+ * @access public
+ */
+	function testColumn() {
+		$result = $this->db->column('varchar(50)');
+		$expected = 'string';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->db->column('text');
+		$expected = 'text';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->db->column('int(11)');
+		$expected = 'integer';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->db->column('int(11) unsigned');
+		$expected = 'integer';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->db->column('tinyint(1)');
+		$expected = 'boolean';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->db->column('boolean');
+		$expected = 'boolean';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->db->column('float');
+		$expected = 'float';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->db->column('float unsigned');
+		$expected = 'float';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->db->column('double unsigned');
+		$expected = 'float';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->db->column('decimal(14,7) unsigned');
+		$expected = 'float';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test transaction commands.
+ *
+ * @return void
+ * @access public
+ */
+	function testTransactions() {
+		$this->db->testing = false;
+		$result = $this->db->begin($this->model);
+		$this->assertTrue($result);
+
+		$beginSqlCalls = Set::extract('/.[query=START TRANSACTION]', $this->db->_queriesLog);
+		$this->assertEqual(1, count($beginSqlCalls));
+
+		$result = $this->db->commit($this->model);
+		$this->assertTrue($result);
+	}
+/**
+ * test that float values are correctly identified
+ *
+ * @return void
+ */
+	function testFloatParsing() {
+		$model =& new Model(array('ds' => 'test_suite', 'table' => 'datatypes', 'name' => 'Datatype'));
+		$result = $this->db->describe($model);
+		$this->assertEqual((string)$result['float_field']['length'], '5,2');
+	}
+
+/**
+ * test that tableParameters like collation, charset and engine are functioning.
+ *
+ * @access public
+ * @return void
+ */
+	function testReadTableParameters() {
+		$this->db->cacheSources = $this->db->testing = false;
+		$this->db->query('CREATE TABLE ' . $this->db->fullTableName('tinyint') . ' (id int(11) AUTO_INCREMENT, bool tinyint(1), small_int tinyint(2), primary key(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;');
+		$result = $this->db->readTableParameters('tinyint');
+		$expected = array(
+			'charset' => 'utf8',
+			'collate' => 'utf8_unicode_ci',
+			'engine' => 'InnoDB');
+		$this->assertEqual($result, $expected);
+
+		$this->db->query('DROP TABLE ' . $this->db->fullTableName('tinyint'));
+		$this->db->query('CREATE TABLE ' . $this->db->fullTableName('tinyint') . ' (id int(11) AUTO_INCREMENT, bool tinyint(1), small_int tinyint(2), primary key(id)) ENGINE=MyISAM DEFAULT CHARSET=cp1250 COLLATE=cp1250_general_ci;');
+		$result = $this->db->readTableParameters('tinyint');
+		$expected = array(
+			'charset' => 'cp1250',
+			'collate' => 'cp1250_general_ci',
+			'engine' => 'MyISAM');
+		$this->assertEqual($result, $expected);
+		$this->db->query('DROP TABLE ' . $this->db->fullTableName('tinyint'));
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo/dbo_oracle.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo/dbo_oracle.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo/dbo_oracle.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,131 @@
+<?php
+/**
+ * DboOracleTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) {
+	define('CAKEPHP_UNIT_TEST_EXECUTION', 1);
+}
+require_once LIBS . 'model' . DS . 'datasources' . DS . 'dbo_source.php';
+require_once LIBS . 'model' . DS . 'datasources' . DS . 'dbo' . DS . 'dbo_oracle.php';
+
+/**
+ * DboOracleTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources.dbo
+ */
+class DboOracleTest extends CakeTestCase {
+
+/**
+ * fixtures property
+ */
+	var $fixtures = array('core.oracle_user');
+
+/**
+ * setup method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		$this->_initDb();
+	}
+
+/**
+ * skip method
+ *
+ * @access public
+ * @return void
+ */
+    function skip() {
+    	$this->_initDb();
+    	$this->skipUnless($this->db->config['driver'] == 'oracle', '%s Oracle connection not available');
+    }
+
+/**
+ * testLastErrorStatement method
+ *
+ * @access public
+ * @return void
+ */
+	function testLastErrorStatement() {
+		if ($this->skip('testLastErrorStatement')) {
+			return;
+		}
+
+		$this->expectError();
+		$this->db->execute("SELECT ' FROM dual");
+		$e = $this->db->lastError();
+		$r = 'ORA-01756: quoted string not properly terminated';
+		$this->assertEqual($e, $r);
+	}
+
+/**
+ * testLastErrorConnect method
+ *
+ * @access public
+ * @return void
+ */
+	function testLastErrorConnect() {
+		if ($this->skip('testLastErrorConnect')) {
+			return;
+		}
+
+		$config = $this->db->config;
+		$old_pw = $this->db->config['password'];
+		$this->db->config['password'] = 'keepmeout';
+		$this->db->connect();
+		$e = $this->db->lastError();
+		$r = 'ORA-01017: invalid username/password; logon denied';
+		$this->assertEqual($e, $r);
+		$this->db->config['password'] = $old_pw;
+		$this->db->connect();
+	}
+
+/**
+ * testName method
+ *
+ * @access public
+ * @return void
+ */
+	function testName() {
+		$Db = $this->db;
+		#$Db =& new DboOracle($config = null, $autoConnect = false);
+
+		$r = $Db->name($Db->name($Db->name('foo.last_update_date')));
+		$e = 'foo.last_update_date';
+		$this->assertEqual($e, $r);
+
+		$r = $Db->name($Db->name($Db->name('foo._update')));
+		$e = 'foo."_update"';
+		$this->assertEqual($e, $r);
+
+		$r = $Db->name($Db->name($Db->name('foo.last_update_date')));
+		$e = 'foo.last_update_date';
+		$this->assertEqual($e, $r);
+
+		$r = $Db->name($Db->name($Db->name('last_update_date')));
+		$e = 'last_update_date';
+		$this->assertEqual($e, $r);
+
+		$r = $Db->name($Db->name($Db->name('_update')));
+		$e = '"_update"';
+		$this->assertEqual($e, $r);
+
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo/dbo_postgres.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo/dbo_postgres.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo/dbo_postgres.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,918 @@
+<?php
+/**
+ * DboPostgresTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Core', array('Model', 'DataSource', 'DboSource', 'DboPostgres'));
+App::import('Model', 'App');
+require_once dirname(dirname(dirname(__FILE__))) . DS . 'models.php';
+
+/**
+ * DboPostgresTestDb class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class DboPostgresTestDb extends DboPostgres {
+
+/**
+ * simulated property
+ *
+ * @var array
+ * @access public
+ */
+	var $simulated = array();
+
+/**
+ * execute method
+ *
+ * @param mixed $sql
+ * @access protected
+ * @return void
+ */
+	function _execute($sql) {
+		$this->simulated[] = $sql;
+		return null;
+	}
+
+/**
+ * getLastQuery method
+ *
+ * @access public
+ * @return void
+ */
+	function getLastQuery() {
+		return $this->simulated[count($this->simulated) - 1];
+	}
+}
+
+/**
+ * PostgresTestModel class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class PostgresTestModel extends Model {
+
+/**
+ * name property
+ *
+ * @var string 'PostgresTestModel'
+ * @access public
+ */
+	var $name = 'PostgresTestModel';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array(
+		'PostgresClientTestModel' => array(
+			'foreignKey' => 'client_id'
+		)
+	);
+
+/**
+ * find method
+ *
+ * @param mixed $conditions
+ * @param mixed $fields
+ * @param mixed $order
+ * @param mixed $recursive
+ * @access public
+ * @return void
+ */
+	function find($conditions = null, $fields = null, $order = null, $recursive = null) {
+		return $conditions;
+	}
+
+/**
+ * findAll method
+ *
+ * @param mixed $conditions
+ * @param mixed $fields
+ * @param mixed $order
+ * @param mixed $recursive
+ * @access public
+ * @return void
+ */
+	function findAll($conditions = null, $fields = null, $order = null, $recursive = null) {
+		return $conditions;
+	}
+
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function schema() {
+		return array(
+			'id'		=> array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8'),
+			'client_id' => array('type' => 'integer', 'null' => '', 'default' => '0', 'length' => '11'),
+			'name'		=> array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+			'login'		=> array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+			'passwd'	=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '255'),
+			'addr_1'	=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '255'),
+			'addr_2'	=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '25'),
+			'zip_code'	=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+			'city'		=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+			'country'	=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+			'phone'		=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+			'fax'		=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+			'url'		=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '255'),
+			'email'		=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+			'comments'	=> array('type' => 'text', 'null' => '1', 'default' => '', 'length' => ''),
+			'last_login'=> array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => ''),
+			'created'	=> array('type' => 'date', 'null' => '1', 'default' => '', 'length' => ''),
+			'updated'	=> array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => null)
+		);
+	}
+}
+
+/**
+ * PostgresClientTestModel class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class PostgresClientTestModel extends Model {
+
+/**
+ * name property
+ *
+ * @var string 'PostgresClientTestModel'
+ * @access public
+ */
+	var $name = 'PostgresClientTestModel';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function schema() {
+		return array(
+			'id'		=> array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8', 'key' => 'primary'),
+			'name'		=> array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+			'email'		=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+			'created'	=> array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => ''),
+			'updated'	=> array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => null)
+		);
+	}
+}
+
+/**
+ * DboPostgresTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources.dbo
+ */
+class DboPostgresTest extends CakeTestCase {
+
+/**
+ * Do not automatically load fixtures for each test, they will be loaded manually
+ * using CakeTestCase::loadFixtures
+ *
+ * @var boolean
+ * @access public
+ */
+	var $autoFixtures = false;
+
+/**
+ * Fixtures
+ *
+ * @var object
+ * @access public
+ */
+	var $fixtures = array('core.user', 'core.binary_test', 'core.comment', 'core.article',
+		'core.tag', 'core.articles_tag', 'core.attachment', 'core.person', 'core.post', 'core.author',
+		'core.datatype',
+	);
+/**
+ * Actual DB connection used in testing
+ *
+ * @var DboSource
+ * @access public
+ */
+	var $db = null;
+
+/**
+ * Simulated DB connection used in testing
+ *
+ * @var DboSource
+ * @access public
+ */
+	var $db2 = null;
+
+/**
+ * Skip if cannot connect to postgres
+ *
+ * @access public
+ */
+	function skip() {
+		$this->_initDb();
+		$this->skipUnless($this->db->config['driver'] == 'postgres', '%s PostgreSQL connection not available');
+	}
+
+/**
+ * Set up test suite database connection
+ *
+ * @access public
+ */
+	function startTest() {
+		$this->_initDb();
+	}
+
+/**
+ * Sets up a Dbo class instance for testing
+ *
+ * @access public
+ */
+	function setUp() {
+		Configure::write('Cache.disable', true);
+		$this->startTest();
+		$this->db =& ConnectionManager::getDataSource('test_suite');
+		$this->db2 = new DboPostgresTestDb($this->db->config, false);
+		$this->model = new PostgresTestModel();
+	}
+
+/**
+ * Sets up a Dbo class instance for testing
+ *
+ * @access public
+ */
+	function tearDown() {
+		Configure::write('Cache.disable', false);
+		unset($this->db2);
+	}
+
+/**
+ * Test field quoting method
+ *
+ * @access public
+ */
+	function testFieldQuoting() {
+		$fields = array(
+			'"PostgresTestModel"."id" AS "PostgresTestModel__id"',
+			'"PostgresTestModel"."client_id" AS "PostgresTestModel__client_id"',
+			'"PostgresTestModel"."name" AS "PostgresTestModel__name"',
+			'"PostgresTestModel"."login" AS "PostgresTestModel__login"',
+			'"PostgresTestModel"."passwd" AS "PostgresTestModel__passwd"',
+			'"PostgresTestModel"."addr_1" AS "PostgresTestModel__addr_1"',
+			'"PostgresTestModel"."addr_2" AS "PostgresTestModel__addr_2"',
+			'"PostgresTestModel"."zip_code" AS "PostgresTestModel__zip_code"',
+			'"PostgresTestModel"."city" AS "PostgresTestModel__city"',
+			'"PostgresTestModel"."country" AS "PostgresTestModel__country"',
+			'"PostgresTestModel"."phone" AS "PostgresTestModel__phone"',
+			'"PostgresTestModel"."fax" AS "PostgresTestModel__fax"',
+			'"PostgresTestModel"."url" AS "PostgresTestModel__url"',
+			'"PostgresTestModel"."email" AS "PostgresTestModel__email"',
+			'"PostgresTestModel"."comments" AS "PostgresTestModel__comments"',
+			'"PostgresTestModel"."last_login" AS "PostgresTestModel__last_login"',
+			'"PostgresTestModel"."created" AS "PostgresTestModel__created"',
+			'"PostgresTestModel"."updated" AS "PostgresTestModel__updated"'
+		);
+
+		$result = $this->db->fields($this->model);
+		$expected = $fields;
+		$this->assertEqual($result, $expected);
+
+		$result = $this->db->fields($this->model, null, 'PostgresTestModel.*');
+		$expected = $fields;
+		$this->assertEqual($result, $expected);
+
+		$result = $this->db->fields($this->model, null, array('*', 'AnotherModel.id', 'AnotherModel.name'));
+		$expected = array_merge($fields, array(
+			'"AnotherModel"."id" AS "AnotherModel__id"',
+			'"AnotherModel"."name" AS "AnotherModel__name"'));
+		$this->assertEqual($result, $expected);
+
+		$result = $this->db->fields($this->model, null, array('*', 'PostgresClientTestModel.*'));
+		$expected = array_merge($fields, array(
+			'"PostgresClientTestModel"."id" AS "PostgresClientTestModel__id"',
+    		'"PostgresClientTestModel"."name" AS "PostgresClientTestModel__name"',
+    		'"PostgresClientTestModel"."email" AS "PostgresClientTestModel__email"',
+    		'"PostgresClientTestModel"."created" AS "PostgresClientTestModel__created"',
+    		'"PostgresClientTestModel"."updated" AS "PostgresClientTestModel__updated"'));
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testColumnParsing method
+ *
+ * @access public
+ * @return void
+ */
+	function testColumnParsing() {
+		$this->assertEqual($this->db2->column('text'), 'text');
+		$this->assertEqual($this->db2->column('date'), 'date');
+		$this->assertEqual($this->db2->column('boolean'), 'boolean');
+		$this->assertEqual($this->db2->column('character varying'), 'string');
+		$this->assertEqual($this->db2->column('time without time zone'), 'time');
+		$this->assertEqual($this->db2->column('timestamp without time zone'), 'datetime');
+	}
+
+/**
+ * testValueQuoting method
+ *
+ * @access public
+ * @return void
+ */
+	function testValueQuoting() {
+		$this->assertIdentical($this->db2->value(1.2, 'float'), "'1.200000'");
+		$this->assertEqual($this->db2->value('1,2', 'float'), "'1,2'");
+
+		$this->assertEqual($this->db2->value('0', 'integer'), "'0'");
+		$this->assertEqual($this->db2->value('', 'integer'), 'NULL');
+		$this->assertEqual($this->db2->value('', 'float'), 'NULL');
+		$this->assertEqual($this->db2->value('', 'integer', false), "DEFAULT");
+		$this->assertEqual($this->db2->value('', 'float', false), "DEFAULT");
+		$this->assertEqual($this->db2->value('0.0', 'float'), "'0.0'");
+
+		$this->assertEqual($this->db2->value('t', 'boolean'), "TRUE");
+		$this->assertEqual($this->db2->value('f', 'boolean'), "FALSE");
+		$this->assertEqual($this->db2->value(true), "TRUE");
+		$this->assertEqual($this->db2->value(false), "FALSE");
+		$this->assertEqual($this->db2->value('t'), "'t'");
+		$this->assertEqual($this->db2->value('f'), "'f'");
+		$this->assertEqual($this->db2->value('true', 'boolean'), 'TRUE');
+		$this->assertEqual($this->db2->value('false', 'boolean'), 'FALSE');
+		$this->assertEqual($this->db2->value('', 'boolean'), 'FALSE');
+		$this->assertEqual($this->db2->value(0, 'boolean'), 'FALSE');
+		$this->assertEqual($this->db2->value(1, 'boolean'), 'TRUE');
+		$this->assertEqual($this->db2->value('1', 'boolean'), 'TRUE');
+		$this->assertEqual($this->db2->value(null, 'boolean'), "NULL");
+		$this->assertEqual($this->db2->value(array()), "NULL");
+	}
+
+/**
+ * test that localized floats don't cause trouble.
+ *
+ * @return void
+ */
+	function testLocalizedFloats() {
+		$restore = setlocale(LC_ALL, null);
+		setlocale(LC_ALL, 'de_DE');
+
+		$result = $this->db->value(3.141593, 'float');
+		$this->assertEqual((string)$result, "'3.141593'");
+
+		$result = $this->db->value(3.14);
+		$this->assertEqual((string)$result, "'3.140000'");
+
+		setlocale(LC_ALL, $restore);
+	}
+
+/**
+ * test that date and time columns do not generate errors with null and nullish values.
+ *
+ * @return void
+ */
+	function testDateAndTimeAsNull() {
+		$this->assertEqual($this->db2->value(null, 'date'), 'NULL');
+		$this->assertEqual($this->db2->value('', 'date'), 'NULL');
+
+		$this->assertEqual($this->db2->value('', 'datetime'), 'NULL');
+		$this->assertEqual($this->db2->value(null, 'datetime'), 'NULL');
+
+		$this->assertEqual($this->db2->value('', 'timestamp'), 'NULL');
+		$this->assertEqual($this->db2->value(null, 'timestamp'), 'NULL');
+
+		$this->assertEqual($this->db2->value('', 'time'), 'NULL');
+		$this->assertEqual($this->db2->value(null, 'time'), 'NULL');
+	}
+
+/**
+ * Tests that different Postgres boolean 'flavors' are properly returned as native PHP booleans
+ *
+ * @access public
+ * @return void
+ */
+	function testBooleanNormalization() {
+		$this->assertTrue($this->db2->boolean('t'));
+		$this->assertTrue($this->db2->boolean('true'));
+		$this->assertTrue($this->db2->boolean('TRUE'));
+		$this->assertTrue($this->db2->boolean(true));
+		$this->assertTrue($this->db2->boolean(1));
+		$this->assertTrue($this->db2->boolean(" "));
+
+		$this->assertFalse($this->db2->boolean('f'));
+		$this->assertFalse($this->db2->boolean('false'));
+		$this->assertFalse($this->db2->boolean('FALSE'));
+		$this->assertFalse($this->db2->boolean(false));
+		$this->assertFalse($this->db2->boolean(0));
+		$this->assertFalse($this->db2->boolean(''));
+	}
+
+/**
+ * test that default -> false in schemas works correctly.
+ *
+ * @return void
+ */
+	function testBooleanDefaultFalseInSchema() {
+		$model = new Model(array('name' => 'Datatype', 'table' => 'datatypes', 'ds' => 'test_suite'));
+		$model->create();
+		$this->assertIdentical(false, $model->data['Datatype']['bool']);
+	}
+
+/**
+ * testLastInsertIdMultipleInsert method
+ *
+ * @access public
+ * @return void
+ */
+	function testLastInsertIdMultipleInsert() {
+		$db1 = ConnectionManager::getDataSource('test_suite');
+
+		$table = $db1->fullTableName('users', false);
+		$password = '5f4dcc3b5aa765d61d8327deb882cf99';
+		$db1->execute(
+			"INSERT INTO {$table} (\"user\", password) VALUES ('mariano', '{$password}')"
+		);
+		$this->assertEqual($db1->lastInsertId($table), 1);
+
+		$db1->execute("INSERT INTO {$table} (\"user\", password) VALUES ('hoge', '{$password}')");
+		$this->assertEqual($db1->lastInsertId($table), 2);
+	}
+
+/**
+ * Tests that table lists and descriptions are scoped to the proper Postgres schema
+ *
+ * @access public
+ * @return void
+ */
+	function testSchemaScoping() {
+		$db1 =& ConnectionManager::getDataSource('test_suite');
+		$db1->cacheSources = false;
+		$db1->reconnect(array('persistent' => false));
+		$db1->query('CREATE SCHEMA _scope_test');
+
+		$db2 =& ConnectionManager::create(
+			'test_suite_2',
+			array_merge($db1->config, array('driver' => 'postgres', 'schema' => '_scope_test'))
+		);
+		$db2->cacheSources = false;
+
+		$db2->query('DROP SCHEMA _scope_test');
+	}
+
+/**
+ * Tests that column types without default lengths in $columns do not have length values
+ * applied when generating schemas.
+ *
+ * @access public
+ * @return void
+ */
+	function testColumnUseLength() {
+		$result = array('name' => 'foo', 'type' => 'string', 'length' => 100, 'default' => 'FOO');
+		$expected = '"foo" varchar(100) DEFAULT \'FOO\'';
+		$this->assertEqual($this->db->buildColumn($result), $expected);
+
+		$result = array('name' => 'foo', 'type' => 'text', 'length' => 100, 'default' => 'FOO');
+		$expected = '"foo" text DEFAULT \'FOO\'';
+		$this->assertEqual($this->db->buildColumn($result), $expected);
+	}
+
+/**
+ * Tests that binary data is escaped/unescaped properly on reads and writes
+ *
+ * @access public
+ * @return void
+ */
+	function testBinaryDataIntegrity() {
+		$data = '%PDF-1.3
+		%ƒÂÚÂÎßÛ†–ƒ∆
+		4 0 obj
+		<< /Length 5 0 R /Filter /FlateDecode >>
+		stream
+		xµYMì€∆Ω„WÃ%)nï0¯îâ-«é]Q"πXµáÿ•Ip	-	P V,]Ú#c˚ˇ‰ut¥†∏Ti9 Ü=”›Ø_˜4>à∑‚Épcé¢Pxæ®2q\'
+		1UªbUᡒ+ö«√[ıµ⁄ão"R∑"HiGæä€(å≠≈^Ãøsm?YlƒÃõªfi‹âEÚB&‚Î◊7bÒ^¸m°÷˛?2±Øs“fiu#®U√ˇú÷g¥C;ä")n})JºIÔ3ËSnÑÎ¥≤ıD∆¢∂Msx1üèG˚±Œ™⁄>¶ySïufØ ˝¸?UπÃã√6flÌÚC=øK?˝…s
+		˛§¯ˇ:-˜ò7€ÓFæ∂∑Õ˛∆“V’>ılflëÅd«ÜQdI›ÎB%W¿ΩıÉn~hvêCS>«é
˛(ØôK!€¡zB!√
+		[œÜ"ûß ·iH¸[Àºæ∑¯¡L,ÀÚAlS∫ˆ=∫Œ≤cÄr&ˆÈ:√ÿ£˚È«4fl•À]vc›bÅôÿî=siXe4/¡p]
ã]ôÆIœ™ Ω
flà_ƒ‚G?«
7	ùÿ ı¯K4ïIpV◊÷·\'éµóªÚæ>î
+		;›sú!2fl¬F•/f∑j£
+		dw"IÊÜπ<ôÿ
ˆ%IG1ytÛDflXg|Éòa§˜}C˛¿ÿe°G´Ú±jÍm~¿/∂hã<#-¥•ıùe87€t˜õ6w}´{æ
+		m‹ê–	∆¡ 6⁄\
+		rAÀBùZ3aË‚r$G·$ó0ÑüâUY4È™¡%C∑Ÿ2rc<Iõ-cï.
+		[ŒöâFA†É‡+QglMÉîÉÄúÌ|¸»#x7¥
«MgVÎ-GGÚ• I?Á‘”Lzw∞pHů◊nefqCî.nÕeè∆ÿÛy¡˙fb≤üŒHÜAëÕNq=´@	’cQdÖúAÉIqñŸ˘+2&∏  Àù.gÅ‚ƒœ3EPƒOi—‰:>ÍCäı
+		=Õec=ëR˝”eñ=<V$ì˙+x+¢ïÒÕ<àeWå»–˚∫Õd§&£àf ]fPA´âtënöå∏◊ó„Ë@∆≠K´÷˘}a_CI˚©y
òHg,ôSSVìB
ƒl4 L.ÈY…á,2∂íäÙ.$ó¸CäŸ*€óy
+		π?G,_√·ÆÎç=^Vkvo±ó{§ƒ2»±¨
Ïüo»ëD-ãé fió¥cVÙ\'™G~\'p¢%* ã˚÷
+		ªºnh
˚ºO^∏…®[Ó“‚ÅfıÌ≥∫F!Eœ(π∑T6`¬tΩÆ0ì»rTÎ`»Ñ«
+		]≈åp˝)=¿Ô0∆öVÂmˇˆ„ø~¯ÁÔ∏b*fc»‡Îı„Ú}∆tœs∂Y∫ÜaÆ˙X∏~<ÿ·Ùvé1‹p¿TD∆ÔîÄ“úhˆ*Ú€îe)K–p¨ÚJ3Ÿ∞ã>ÊuNê°“√Ü 
‹Ê9iÙ0
˙AAEÍ ˙`∂£\'ûce•åƒX›ŸÁ´1SK{qdá"tÏ[wQ#SµBe∞∑µó…ÌV`B"Ñ≥„!è_Óφ-º*º
ú¿Ë0ˆeê∂´ë+HFj…‡zv
HÓN|ÔL÷ûñ3õÜ$z%sá…pÎóV38âs	Çoµ•ß3†<9B·¨û~¢3)ÂxóÿÁCÕòÆ∫Í=»ÿSπS;∆~±êÆTEp∑óÈ÷ÀuìDHÈ$ÉõæÜjû§"≤ÃONM®RËíRr{õS	∏Ê™op±W;ÂUÔ P∫kÔˇflTæ∑óflË”ÆC©Ô[≥◊HÁ˚¨hê"ÆbF?ú%h˙ˇ4xèÕ(ó2ÙáíM])Ñd|=fë-cI0ñL¢kÖêk‰Rƒ«ıÄWñ8mO3∏&√æËX¯Hó—ì]yF2»–˜ádàà‡‹Çο„≥7mªHAS∑¶.;Œx(1} _kd©.fidç48M\'àáªCp^Krí<ɉXÓıïl!Ì$N<ı∞B»G]…∂Ó¯>˛ÔbõÒπÀ•:ôO<j∂™œ%âÏ—>@È$pÖu‹Ê´-Q
qV ?V≥JÆÍqÛX8(l
πï@zgÖ}Fe<ˇ‡Sñ“ÿ˜ê?6‡L∫Oß~µ 
–?ËeäÚ
 ®YîÕ=Ü=¢DÁu*GvBk;)L¬N«î:flö∂≠ÇΩq„Ñm하Ë∂‚"û≥§:±≤i^ΩÑ!)Wıyŧô á„RÄ÷Òôc’≠—s™rı‚Pdêãh˘ßHVç5fifiÈF€çÌÛuçÖ/M=gëµ±ÿGû1coÔuñæ‘z®. õ∑7ÉÏÜÆ,°’H†ÍÉÌ∂7e	º® íˆ⁄◊øNWK”ÂYµ‚ñé;µ¶gV-fl>µtË¥áßN2 ¯¶BaP-)eW.àôt^∏1›C∑Ö?
L„&”5’4jvã–ªZ	÷+4% ´0l…»ú^°´© ûiπ∑é®óܱÒÿ‰ïˆÌ–dˆ◊Æ19rQ=Í|ı•rMæ¬;ò‰Y‰é9.”‹˝V«ã¯∏,+ë®j*¡·/';
+
+		$model =& new AppModel(array('name' => 'BinaryTest', 'ds' => 'test_suite'));
+		$model->save(compact('data'));
+
+		$result = $model->find('first');
+		$this->assertEqual($result['BinaryTest']['data'], $data);
+	}
+
+/**
+ * Tests the syntax of generated schema indexes
+ *
+ * @access public
+ * @return void
+ */
+	function testSchemaIndexSyntax() {
+		$schema = new CakeSchema();
+		$schema->tables = array('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)
+			)
+		));
+
+		$result = $this->db->createSchema($schema);
+		$this->assertNoPattern('/^CREATE INDEX(.+);,$/', $result);
+	}
+
+/**
+ * testCakeSchema method
+ *
+ * Test that schema generated postgresql queries are valid. ref #5696
+ * Check that the create statement for a schema generated table is the same as the original sql
+ *
+ * @return void
+ * @access public
+ */
+	function testCakeSchema() {
+		$db1 =& ConnectionManager::getDataSource('test_suite');
+		$db1->cacheSources = false;
+		$db1->reconnect(array('persistent' => false));
+		$db1->query('CREATE TABLE ' .  $db1->fullTableName('datatype_tests') . ' (
+			id serial NOT NULL,
+			"varchar" character varying(40) NOT NULL,
+			"full_length" character varying NOT NULL,
+			"timestamp" timestamp without time zone,
+			date date,
+			CONSTRAINT test_suite_data_types_pkey PRIMARY KEY (id)
+		)');
+		$model = new Model(array('name' => 'DatatypeTest', 'ds' => 'test_suite'));
+		$schema = new CakeSchema(array('connection' => 'test_suite'));
+		$result = $schema->read(array(
+			'connection' => 'test_suite',
+		));
+		$schema->tables = array('datatype_tests' => $result['tables']['missing']['datatype_tests']);
+		$result = $db1->createSchema($schema, 'datatype_tests');
+
+		$this->assertNoPattern('/timestamp DEFAULT/', $result);
+		$this->assertPattern('/\"full_length\"\s*text\s.*,/', $result);
+		$this->assertPattern('/timestamp\s*,/', $result);
+
+		$db1->query('DROP TABLE ' . $db1->fullTableName('datatype_tests'));
+
+		$db1->query($result);
+		$result2 = $schema->read(array(
+			'connection' => 'test_suite',
+			'models' => array('DatatypeTest')
+		));
+		$schema->tables = array('datatype_tests' => $result2['tables']['missing']['datatype_tests']);
+		$result2 = $db1->createSchema($schema, 'datatype_tests');
+		$this->assertEqual($result, $result2);
+
+		$db1->query('DROP TABLE ' . $db1->fullTableName('datatype_tests'));
+	}
+
+/**
+ * Test index generation from table info.
+ *
+ * @return void
+ */
+	function testIndexGeneration() {
+		$name = $this->db->fullTableName('index_test', false);
+		$this->db->query('CREATE TABLE ' . $name . ' ("id" serial NOT NULL PRIMARY KEY, "bool" integer, "small_char" varchar(50), "description" varchar(40) )');
+		$this->db->query('CREATE INDEX pointless_bool ON ' . $name . '("bool")');
+		$this->db->query('CREATE UNIQUE INDEX char_index ON ' . $name . '("small_char")');
+		$expected = array(
+			'PRIMARY' => array('column' => 'id', 'unique' => 1),
+			'pointless_bool' => array('column' => 'bool', 'unique' => 0),
+			'char_index' => array('column' => 'small_char', 'unique' => 1),
+
+		);
+		$result = $this->db->index($name);
+		$this->assertEqual($expected, $result);
+
+		$this->db->query('DROP TABLE ' . $name);
+		$name = $this->db->fullTableName('index_test_2', false);
+		$this->db->query('CREATE TABLE ' . $name . ' ("id" serial NOT NULL PRIMARY KEY, "bool" integer, "small_char" varchar(50), "description" varchar(40) )');
+		$this->db->query('CREATE UNIQUE INDEX multi_col ON ' . $name . '("small_char", "bool")');
+		$expected = array(
+			'PRIMARY' => array('column' => 'id', 'unique' => 1),
+			'multi_col' => array('column' => array('small_char', 'bool'), 'unique' => 1),
+		);
+		$result = $this->db->index($name);
+		$this->assertEqual($expected, $result);
+		$this->db->query('DROP TABLE ' . $name);
+	}
+
+/**
+ * Test the alterSchema capabilities of postgres
+ *
+ * @access public
+ * @return void
+ */
+	function testAlterSchema() {
+		$Old =& new CakeSchema(array(
+			'connection' => 'test_suite',
+			'name' => 'AlterPosts',
+			'alter_posts' => array(
+				'id' => array('type' => 'integer', 'key' => 'primary'),
+				'author_id' => array('type' => 'integer', 'null' => false),
+				'title' => array('type' => 'string', 'null' => true),
+				'body' => array('type' => 'text'),
+				'published' => array('type' => 'string', 'length' => 1, 'default' => 'N'),
+				'created' => array('type' => 'datetime'),
+				'updated' => array('type' => 'datetime'),
+			)
+		));
+		$this->db->query($this->db->createSchema($Old));
+
+		$New =& new CakeSchema(array(
+			'connection' => 'test_suite',
+			'name' => 'AlterPosts',
+			'alter_posts' => array(
+				'id' => array('type' => 'integer', 'key' => 'primary'),
+				'author_id' => array('type' => 'integer', 'null' => true),
+				'title' => array('type' => 'string', 'null' => false, 'default' => 'my title'),
+				'body' => array('type' => 'string', 'length' => 500),
+				'status' => array('type' => 'integer', 'length' => 3, 'default' => 1),
+				'created' => array('type' => 'datetime'),
+				'updated' => array('type' => 'datetime'),
+			)
+		));
+		$this->db->query($this->db->alterSchema($New->compare($Old), 'alter_posts'));
+
+		$model = new CakeTestModel(array('table' => 'alter_posts', 'ds' => 'test_suite'));
+		$result = $model->schema();
+		$this->assertTrue(isset($result['status']));
+		$this->assertFalse(isset($result['published']));
+		$this->assertEqual($result['body']['type'], 'string');
+		$this->assertEqual($result['status']['default'], 1);
+		$this->assertEqual($result['author_id']['null'], true);
+		$this->assertEqual($result['title']['null'], false);
+
+		$this->db->query($this->db->dropSchema($New));
+
+		$New =& new CakeSchema(array(
+			'connection' => 'test_suite',
+			'name' => 'AlterPosts',
+			'alter_posts' => array(
+				'id' => array('type' => 'string', 'length' => 36, 'key' => 'primary'),
+				'author_id' => array('type' => 'integer', 'null' => false),
+				'title' => array('type' => 'string', 'null' => true),
+				'body' => array('type' => 'text'),
+				'published' => array('type' => 'string', 'length' => 1, 'default' => 'N'),
+				'created' => array('type' => 'datetime'),
+				'updated' => array('type' => 'datetime'),
+			)
+		));
+		$result = $this->db->alterSchema($New->compare($Old), 'alter_posts');
+		$this->assertNoPattern('/varchar\(36\) NOT NULL/i', $result);
+	}
+
+/**
+ * Test the alter index capabilities of postgres
+ *
+ * @access public
+ * @return void
+ */
+	function testAlterIndexes() {
+		$this->db->cacheSources = false;
+
+		$schema1 =& new CakeSchema(array(
+			'name' => 'AlterTest1',
+			'connection' => 'test_suite',
+			'altertest' => array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => 0),
+				'name' => array('type' => 'string', 'null' => false, 'length' => 50),
+				'group1' => array('type' => 'integer', 'null' => true),
+				'group2' => array('type' => 'integer', 'null' => true)
+			)
+		));
+		$this->db->query($this->db->createSchema($schema1));
+
+		$schema2 =& new CakeSchema(array(
+			'name' => 'AlterTest2',
+			'connection' => 'test_suite',
+			'altertest' => array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => 0),
+				'name' => array('type' => 'string', 'null' => false, 'length' => 50),
+				'group1' => array('type' => 'integer', 'null' => true),
+				'group2' => array('type' => 'integer', 'null' => true),
+				'indexes' => array(
+					'name_idx' => array('column' => 'name', 'unique' => 0),
+					'group_idx' => array('column' => 'group1', 'unique' => 0),
+					'compound_idx' => array('column' => array('group1', 'group2'), 'unique' => 0),
+					'PRIMARY' => array('column' => 'id', 'unique' => 1)
+				)
+			)
+		));
+		$this->db->query($this->db->alterSchema($schema2->compare($schema1)));
+
+		$indexes = $this->db->index('altertest');
+		$this->assertEqual($schema2->tables['altertest']['indexes'], $indexes);
+
+		// Change three indexes, delete one and add another one
+		$schema3 =& new CakeSchema(array(
+			'name' => 'AlterTest3',
+			'connection' => 'test_suite',
+			'altertest' => array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => 0),
+				'name' => array('type' => 'string', 'null' => false, 'length' => 50),
+				'group1' => array('type' => 'integer', 'null' => true),
+				'group2' => array('type' => 'integer', 'null' => true),
+				'indexes' => array(
+					'name_idx' => array('column' => 'name', 'unique' => 1),
+					'group_idx' => array('column' => 'group2', 'unique' => 0),
+					'compound_idx' => array('column' => array('group2', 'group1'), 'unique' => 0),
+					'another_idx' => array('column' => array('group1', 'name'), 'unique' => 0))
+		)));
+
+		$this->db->query($this->db->alterSchema($schema3->compare($schema2)));
+
+		$indexes = $this->db->index('altertest');
+		$this->assertEqual($schema3->tables['altertest']['indexes'], $indexes);
+
+		// Compare us to ourself.
+		$this->assertEqual($schema3->compare($schema3), array());
+
+		// Drop the indexes
+		$this->db->query($this->db->alterSchema($schema1->compare($schema3)));
+
+		$indexes = $this->db->index('altertest');
+		$this->assertEqual(array(), $indexes);
+
+		$this->db->query($this->db->dropSchema($schema1));
+	}
+
+/*
+ * Test it is possible to use virtual field with postgresql
+ *
+ * @access public
+ * @return void
+ */
+	function testVirtualFields() {
+		$this->loadFixtures('Article', 'Comment');
+		$Article = new Article;
+		$Article->virtualFields = array(
+			'next_id' => 'Article.id + 1',
+			'complex' => 'Article.title || Article.body',
+			'functional' => 'COALESCE(User.user, Article.title)',
+			'subquery' => 'SELECT count(*) FROM ' . $Article->Comment->table
+		);
+		$result = $Article->find('first');
+		$this->assertEqual($result['Article']['next_id'], 2);
+		$this->assertEqual($result['Article']['complex'], $result['Article']['title'] . $result['Article']['body']);
+		$this->assertEqual($result['Article']['functional'], $result['Article']['title']);
+		$this->assertEqual($result['Article']['subquery'], 6);
+	}
+
+/**
+ * Test that virtual fields work with SQL constants
+ *
+ * @return void
+ */
+	function testVirtualFieldAsAConstant() {
+		$this->loadFixtures('Article', 'Comment');
+		$Article =& ClassRegistry::init('Article');
+		$Article->virtualFields = array(
+			'empty' => "NULL",
+			'number' => 43,
+			'truth' => 'TRUE'
+		);
+		$result = $Article->find('first');
+		$this->assertNull($result['Article']['empty']);
+		$this->assertTrue($result['Article']['truth']);
+		$this->assertIdentical('43', $result['Article']['number']);
+	}
+
+/**
+ * Tests additional order options for postgres
+ *
+ * @access public
+ * @return void
+ */
+	function testOrderAdditionalParams() {
+		$result = $this->db->order(array('title' => 'DESC NULLS FIRST', 'body' => 'DESC'));
+		$expected = ' ORDER BY "title" DESC NULLS FIRST, "body" DESC';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+* Test it is possible to do a SELECT COUNT(DISTINCT Model.field) query in postgres and it gets correctly quoted
+*/
+	function testQuoteDistinctInFunction() {
+		$this->loadFixtures('Article');
+		$Article = new Article;
+		$result = $this->db->fields($Article, null, array('COUNT(DISTINCT Article.id)'));
+		$expected = array('COUNT(DISTINCT "Article"."id")');
+		$this->assertEqual($result, $expected);
+
+		$result = $this->db->fields($Article, null, array('COUNT(DISTINCT id)'));
+		$expected = array('COUNT(DISTINCT "id")');
+		$this->assertEqual($result, $expected);
+
+		$result = $this->db->fields($Article, null, array('COUNT(DISTINCT FUNC(id))'));
+		$expected = array('COUNT(DISTINCT FUNC("id"))');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that saveAll works even with conditions that lack a model name.
+ *
+ * @return void
+ */
+	function testUpdateAllWithNonQualifiedConditions() {
+		$this->loadFixtures('Article');
+		$Article =& new Article();
+		$result = $Article->updateAll(array('title' => "'Awesome'"), array('title' => 'Third Article'));
+		$this->assertTrue($result);
+
+		$result = $Article->find('count', array(
+			'conditions' => array('Article.title' => 'Awesome')
+		));
+		$this->assertEqual($result, 1, 'Article count is wrong or fixture has changed.');
+	}
+
+/**
+ * test alterSchema on two tables.
+ *
+ * @return void
+ */
+	function testAlteringTwoTables() {
+		$schema1 =& new CakeSchema(array(
+			'name' => 'AlterTest1',
+			'connection' => 'test_suite',
+			'altertest' => array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => 0),
+				'name' => array('type' => 'string', 'null' => false, 'length' => 50),
+			),
+			'other_table' => array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => 0),
+				'name' => array('type' => 'string', 'null' => false, 'length' => 50),
+			)
+		));
+		$schema2 =& new CakeSchema(array(
+			'name' => 'AlterTest1',
+			'connection' => 'test_suite',
+			'altertest' => array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => 0),
+				'field_two' => array('type' => 'string', 'null' => false, 'length' => 50),
+			),
+			'other_table' => array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => 0),
+				'field_two' => array('type' => 'string', 'null' => false, 'length' => 50),
+			)
+		));
+		$result = $this->db->alterSchema($schema2->compare($schema1));
+		$this->assertEqual(2, substr_count($result, 'field_two'), 'Too many fields');
+		$this->assertFalse(strpos(';ALTER', $result), 'Too many semi colons');
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo/dbo_sqlite.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo/dbo_sqlite.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo/dbo_sqlite.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,355 @@
+<?php
+/**
+ * DboSqliteTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs
+ * @since         CakePHP(tm) v 1.2.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Core', array('Model', 'DataSource', 'DboSource', 'DboSqlite'));
+
+/**
+ * DboSqliteTestDb class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class DboSqliteTestDb extends DboSqlite {
+
+/**
+ * simulated property
+ *
+ * @var array
+ * @access public
+ */
+	var $simulated = array();
+
+/**
+ * execute method
+ *
+ * @param mixed $sql
+ * @access protected
+ * @return void
+ */
+	function _execute($sql) {
+		$this->simulated[] = $sql;
+		return null;
+	}
+
+/**
+ * getLastQuery method
+ *
+ * @access public
+ * @return void
+ */
+	function getLastQuery() {
+		return $this->simulated[count($this->simulated) - 1];
+	}
+}
+
+/**
+ * DboSqliteTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources.dbo
+ */
+class DboSqliteTest extends CakeTestCase {
+
+/**
+ * Do not automatically load fixtures for each test, they will be loaded manually using CakeTestCase::loadFixtures
+ *
+ * @var boolean
+ * @access public
+ */
+	var $autoFixtures = false;
+
+/**
+ * Fixtures
+ *
+ * @var object
+ * @access public
+ */
+	var $fixtures = array('core.user');
+
+/**
+ * Actual DB connection used in testing
+ *
+ * @var DboSource
+ * @access public
+ */
+	var $db = null;
+
+/**
+ * Simulated DB connection used in testing
+ *
+ * @var DboSource
+ * @access public
+ */
+	var $db2 = null;
+
+/**
+ * Skip if cannot connect to SQLite
+ *
+ * @access public
+ */
+	function skip() {
+		$this->_initDb();
+		$this->skipUnless($this->db->config['driver'] == 'sqlite', '%s SQLite connection not available');
+	}
+
+/**
+ * Set up test suite database connection
+ *
+ * @access public
+ */
+	function startTest() {
+		$this->_initDb();
+	}
+
+/**
+ * Sets up a Dbo class instance for testing
+ *
+ * @access public
+ */
+	function setUp() {
+		Configure::write('Cache.disable', true);
+		$this->startTest();
+		$this->db =& ConnectionManager::getDataSource('test_suite');
+		$this->db2 = new DboSqliteTestDb($this->db->config, false);
+	}
+
+/**
+ * Sets up a Dbo class instance for testing
+ *
+ * @access public
+ */
+	function tearDown() {
+		Configure::write('Cache.disable', false);
+		unset($this->db2);
+	}
+
+/**
+ * Tests that SELECT queries from DboSqlite::listSources() are not cached
+ *
+ * @access public
+ */
+	function testTableListCacheDisabling() {
+		$this->assertFalse(in_array('foo_test', $this->db->listSources()));
+
+		$this->db->query('CREATE TABLE foo_test (test VARCHAR(255));');
+		$this->assertTrue(in_array('foo_test', $this->db->listSources()));
+
+		$this->db->query('DROP TABLE foo_test;');
+		$this->assertFalse(in_array('foo_test', $this->db->listSources()));
+	}
+
+/**
+ * test Index introspection.
+ *
+ * @access public
+ * @return void
+ */
+	function testIndex() {
+		$name = $this->db->fullTableName('with_a_key');
+		$this->db->query('CREATE TABLE ' . $name . ' ("id" int(11) PRIMARY KEY, "bool" int(1), "small_char" varchar(50), "description" varchar(40) );');
+		$this->db->query('CREATE INDEX pointless_bool ON ' . $name . '("bool")');
+		$this->db->query('CREATE UNIQUE INDEX char_index ON ' . $name . '("small_char")');
+		$expected = array(
+			'PRIMARY' => array('column' => 'id', 'unique' => 1),
+			'pointless_bool' => array('column' => 'bool', 'unique' => 0),
+			'char_index' => array('column' => 'small_char', 'unique' => 1),
+
+		);
+		$result = $this->db->index($name);
+		$this->assertEqual($expected, $result);
+		$this->db->query('DROP TABLE ' . $name);
+
+		$this->db->query('CREATE TABLE ' . $name . ' ("id" int(11) PRIMARY KEY, "bool" int(1), "small_char" varchar(50), "description" varchar(40) );');
+		$this->db->query('CREATE UNIQUE INDEX multi_col ON ' . $name . '("small_char", "bool")');
+		$expected = array(
+			'PRIMARY' => array('column' => 'id', 'unique' => 1),
+			'multi_col' => array('column' => array('small_char', 'bool'), 'unique' => 1),
+		);
+		$result = $this->db->index($name);
+		$this->assertEqual($expected, $result);
+		$this->db->query('DROP TABLE ' . $name);
+	}
+
+/**
+ * Tests that cached table descriptions are saved under the sanitized key name
+ *
+ * @access public
+ */
+	function testCacheKeyName() {
+		Configure::write('Cache.disable', false);
+
+		$dbName = 'db' . rand() . '$(*%&).db';
+		$this->assertFalse(file_exists(TMP . $dbName));
+
+		$config = $this->db->config;
+		$db = new DboSqlite(array_merge($this->db->config, array('database' => TMP . $dbName)));
+		$this->assertTrue(file_exists(TMP . $dbName));
+
+		$db->execute("CREATE TABLE test_list (id VARCHAR(255));");
+
+		$db->cacheSources = true;
+		$this->assertEqual($db->listSources(), array('test_list'));
+		$db->cacheSources = false;
+
+		$fileName = '_' . preg_replace('/[^A-Za-z0-9_\-+]/', '_', TMP . $dbName) . '_list';
+
+		$result = Cache::read($fileName, '_cake_model_');
+		$this->assertEqual($result, array('test_list'));
+
+		Cache::delete($fileName, '_cake_model_');
+		Configure::write('Cache.disable', true);
+	}
+
+/**
+ * test building columns with SQLite
+ *
+ * @return void
+ */
+	function testBuildColumn() {
+		$data = array(
+			'name' => 'int_field',
+			'type' => 'integer',
+			'null' => false,
+		);
+		$result = $this->db->buildColumn($data);
+		$expected = '"int_field" integer(11) NOT NULL';
+		$this->assertEqual($result, $expected);
+
+		$data = array(
+			'name' => 'name',
+			'type' => 'string',
+			'length' => 20,
+			'null' => false,
+		);
+		$result = $this->db->buildColumn($data);
+		$expected = '"name" varchar(20) NOT NULL';
+		$this->assertEqual($result, $expected);
+
+		$data = array(
+			'name' => 'testName',
+			'type' => 'string',
+			'length' => 20,
+			'default' => null,
+			'null' => true,
+			'collate' => 'NOCASE'
+		);
+		$result = $this->db->buildColumn($data);
+		$expected = '"testName" varchar(20) DEFAULT NULL COLLATE NOCASE';
+		$this->assertEqual($result, $expected);
+
+		$data = array(
+			'name' => 'testName',
+			'type' => 'string',
+			'length' => 20,
+			'default' => 'test-value',
+			'null' => false,
+		);
+		$result = $this->db->buildColumn($data);
+		$expected = '"testName" varchar(20) DEFAULT \'test-value\' NOT NULL';
+		$this->assertEqual($result, $expected);
+
+		$data = array(
+			'name' => 'testName',
+			'type' => 'integer',
+			'length' => 10,
+			'default' => 10,
+			'null' => false,
+		);
+		$result = $this->db->buildColumn($data);
+		$expected = '"testName" integer(10) DEFAULT \'10\' NOT NULL';
+		$this->assertEqual($result, $expected);
+		
+		$data = array(
+			'name' => 'testName',
+			'type' => 'integer',
+			'length' => 10,
+			'default' => 10,
+			'null' => false,
+			'collate' => 'BADVALUE'
+		);
+		$result = $this->db->buildColumn($data);
+		$expected = '"testName" integer(10) DEFAULT \'10\' NOT NULL';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test describe() and normal results.
+ *
+ * @return void
+ */
+	function testDescribe() {
+		$Model =& new Model(array('name' => 'User', 'ds' => 'test_suite', 'table' => 'users'));
+		$result = $this->db->describe($Model);
+		$expected = array(
+			'id' => array(
+				'type' => 'integer',
+				'key' => 'primary',
+				'null' => false,
+				'default' => null,
+				'length' => 11
+			),
+			'user' => array(
+				'type' => 'string',
+				'length' => 255,
+				'null' => false,
+				'default' => null
+			),
+			'password' => array(
+				'type' => 'string',
+				'length' => 255,
+				'null' => false,
+				'default' => null
+			),
+			'created' => array(
+				'type' => 'datetime',
+				'null' => true,
+				'default' => null,
+				'length' => null,
+			),
+			'updated' => array(
+				'type' => 'datetime',
+				'null' => true,
+				'default' => null,
+				'length' => null,
+			)
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that describe does not corrupt UUID primary keys
+ *
+ * @return void
+ */
+	function testDescribeWithUuidPrimaryKey() {
+		$tableName = 'uuid_tests';
+		$this->db->query("CREATE TABLE {$tableName} (id VARCHAR(36) PRIMARY KEY, name VARCHAR, created DATETIME, modified DATETIME)");
+		$Model =& new Model(array('name' => 'UuidTest', 'ds' => 'test_suite', 'table' => 'uuid_tests'));
+		$result = $this->db->describe($Model);
+		$expected = array(
+			'type' => 'string',
+			'length' => 36,
+			'null' => false,
+			'default' => null,
+			'key' => 'primary',
+		);
+		$this->assertEqual($result['id'], $expected);
+		$this->db->query('DROP TABLE ' . $tableName);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo_source.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo_source.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/model/datasources/dbo_source.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,4646 @@
+<?php
+/**
+ * DboSourceTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *	Licensed under The Open Group Test Suite License
+ *	Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) {
+	define('CAKEPHP_UNIT_TEST_EXECUTION', 1);
+}
+App::import('Model', array('Model', 'DataSource', 'DboSource', 'DboMysql', 'App'));
+require_once dirname(dirname(__FILE__)) . DS . 'models.php';
+
+/**
+ * TestModel class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class TestModel extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'TestModel'
+ * @access public
+ */
+	var $name = 'TestModel';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * schema property
+ *
+ * @var array
+ * @access protected
+ */
+	var $_schema = array(
+		'id' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8'),
+		'client_id' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '11'),
+		'name' => array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+		'login' => array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+		'passwd' => array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '255'),
+		'addr_1' => array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '255'),
+		'addr_2' => array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '25'),
+		'zip_code' => array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+		'city' => array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+		'country' => array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+		'phone' => array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+		'fax' => array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+		'url' => array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '255'),
+		'email' => array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+		'comments' => array('type' => 'text', 'null' => '1', 'default' => '', 'length' => '155'),
+		'last_login' => array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => ''),
+		'created' => array('type' => 'date', 'null' => '1', 'default' => '', 'length' => ''),
+		'updated' => array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => null)
+	);
+
+/**
+ * find method
+ *
+ * @param mixed $conditions
+ * @param mixed $fields
+ * @param mixed $order
+ * @param mixed $recursive
+ * @access public
+ * @return void
+ */
+	function find($conditions = null, $fields = null, $order = null, $recursive = null) {
+		return array($conditions, $fields);
+	}
+
+/**
+ * findAll method
+ *
+ * @param mixed $conditions
+ * @param mixed $fields
+ * @param mixed $order
+ * @param mixed $recursive
+ * @access public
+ * @return void
+ */
+	function findAll($conditions = null, $fields = null, $order = null, $recursive = null) {
+		return $conditions;
+	}
+}
+
+/**
+ * TestModel2 class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class TestModel2 extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'TestModel2'
+ * @access public
+ */
+	var $name = 'TestModel2';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+}
+
+/**
+ * TestModel4 class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class TestModel3 extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'TestModel3'
+ * @access public
+ */
+	var $name = 'TestModel3';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+}
+
+/**
+ * TestModel4 class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class TestModel4 extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'TestModel4'
+ * @access public
+ */
+	var $name = 'TestModel4';
+
+/**
+ * table property
+ *
+ * @var string 'test_model4'
+ * @access public
+ */
+	var $table = 'test_model4';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array(
+		'TestModel4Parent' => array(
+			'className' => 'TestModel4',
+			'foreignKey' => 'parent_id'
+		)
+	);
+
+/**
+ * hasOne property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasOne = array(
+		'TestModel5' => array(
+			'className' => 'TestModel5',
+			'foreignKey' => 'test_model4_id'
+		)
+	);
+
+/**
+ * hasAndBelongsToMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('TestModel7' => array(
+		'className' => 'TestModel7',
+		'joinTable' => 'test_model4_test_model7',
+		'foreignKey' => 'test_model4_id',
+		'associationForeignKey' => 'test_model7_id',
+		'with' => 'TestModel4TestModel7'
+	));
+
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function schema() {
+		if (!isset($this->_schema)) {
+			$this->_schema = array(
+				'id' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8'),
+				'name' => array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+				'created' => array('type' => 'date', 'null' => '1', 'default' => '', 'length' => ''),
+				'updated' => array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => null)
+			);
+		}
+		return $this->_schema;
+	}
+}
+
+/**
+ * TestModel4TestModel7 class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class TestModel4TestModel7 extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'TestModel4TestModel7'
+ * @access public
+ */
+	var $name = 'TestModel4TestModel7';
+
+/**
+ * table property
+ *
+ * @var string 'test_model4_test_model7'
+ * @access public
+ */
+	var $table = 'test_model4_test_model7';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function schema() {
+		if (!isset($this->_schema)) {
+			$this->_schema = array(
+				'test_model4_id' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8'),
+				'test_model7_id' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8')
+			);
+		}
+		return $this->_schema;
+	}
+}
+
+/**
+ * TestModel5 class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class TestModel5 extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'TestModel5'
+ * @access public
+ */
+	var $name = 'TestModel5';
+
+/**
+ * table property
+ *
+ * @var string 'test_model5'
+ * @access public
+ */
+	var $table = 'test_model5';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('TestModel4' => array(
+		'className' => 'TestModel4',
+		'foreignKey' => 'test_model4_id'
+	));
+
+/**
+ * hasMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasMany = array('TestModel6' => array(
+		'className' => 'TestModel6',
+		'foreignKey' => 'test_model5_id'
+	));
+
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function schema() {
+		if (!isset($this->_schema)) {
+			$this->_schema = array(
+				'id' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8'),
+				'test_model4_id' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8'),
+				'name' => array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+				'created' => array('type' => 'date', 'null' => '1', 'default' => '', 'length' => ''),
+				'updated' => array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => null)
+			);
+		}
+		return $this->_schema;
+	}
+}
+
+/**
+ * TestModel6 class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class TestModel6 extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'TestModel6'
+ * @access public
+ */
+	var $name = 'TestModel6';
+
+/**
+ * table property
+ *
+ * @var string 'test_model6'
+ * @access public
+ */
+	var $table = 'test_model6';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('TestModel5' => array(
+		'className' => 'TestModel5',
+		'foreignKey' => 'test_model5_id'
+	));
+
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function schema() {
+		if (!isset($this->_schema)) {
+			$this->_schema = array(
+				'id' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8'),
+				'test_model5_id' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8'),
+				'name' => array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+				'created' => array('type' => 'date', 'null' => '1', 'default' => '', 'length' => ''),
+				'updated' => array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => null)
+			);
+		}
+		return $this->_schema;
+	}
+}
+
+/**
+ * TestModel7 class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class TestModel7 extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'TestModel7'
+ * @access public
+ */
+	var $name = 'TestModel7';
+
+/**
+ * table property
+ *
+ * @var string 'test_model7'
+ * @access public
+ */
+	var $table = 'test_model7';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function schema() {
+		if (!isset($this->_schema)) {
+			$this->_schema = array(
+				'id' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8'),
+				'name' => array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+				'created' => array('type' => 'date', 'null' => '1', 'default' => '', 'length' => ''),
+				'updated' => array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => null)
+			);
+		}
+		return $this->_schema;
+	}
+}
+
+/**
+ * TestModel8 class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class TestModel8 extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'TestModel8'
+ * @access public
+ */
+	var $name = 'TestModel8';
+
+/**
+ * table property
+ *
+ * @var string 'test_model8'
+ * @access public
+ */
+	var $table = 'test_model8';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * hasOne property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasOne = array(
+		'TestModel9' => array(
+			'className' => 'TestModel9',
+			'foreignKey' => 'test_model8_id',
+			'conditions' => 'TestModel9.name != \'mariano\''
+		)
+	);
+
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function schema() {
+		if (!isset($this->_schema)) {
+			$this->_schema = array(
+				'id' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8'),
+				'test_model9_id' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8'),
+				'name' => array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+				'created' => array('type' => 'date', 'null' => '1', 'default' => '', 'length' => ''),
+				'updated' => array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => null)
+			);
+		}
+		return $this->_schema;
+	}
+}
+
+/**
+ * TestModel9 class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class TestModel9 extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'TestModel9'
+ * @access public
+ */
+	var $name = 'TestModel9';
+
+/**
+ * table property
+ *
+ * @var string 'test_model9'
+ * @access public
+ */
+	var $table = 'test_model9';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('TestModel8' => array(
+		'className' => 'TestModel8',
+		'foreignKey' => 'test_model8_id',
+		'conditions' => 'TestModel8.name != \'larry\''
+	));
+
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function schema() {
+		if (!isset($this->_schema)) {
+			$this->_schema = array(
+				'id' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8'),
+				'test_model8_id' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '11'),
+				'name' => array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+				'created' => array('type' => 'date', 'null' => '1', 'default' => '', 'length' => ''),
+				'updated' => array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => null)
+			);
+		}
+		return $this->_schema;
+	}
+}
+
+/**
+ * Level class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class Level extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Level'
+ * @access public
+ */
+	var $name = 'Level';
+
+/**
+ * table property
+ *
+ * @var string 'level'
+ * @access public
+ */
+	var $table = 'level';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * hasMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasMany = array(
+		'Group'=> array(
+			'className' => 'Group'
+		),
+		'User2' => array(
+			'className' => 'User2'
+		)
+	);
+
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function schema() {
+		if (!isset($this->_schema)) {
+			$this->_schema = array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => null, 'length' => '10'),
+				'name' => array('type' => 'string', 'null' => true, 'default' => null, 'length' => '20'),
+			);
+		}
+		return $this->_schema;
+	}
+}
+
+/**
+ * Group class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class Group extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Group'
+ * @access public
+ */
+	var $name = 'Group';
+
+/**
+ * table property
+ *
+ * @var string 'group'
+ * @access public
+ */
+	var $table = 'group';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('Level');
+
+/**
+ * hasMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasMany = array('Category2', 'User2');
+
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function schema() {
+		if (!isset($this->_schema)) {
+			$this->_schema = array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => null, 'length' => '10'),
+				'level_id' => array('type' => 'integer', 'null' => false, 'default' => null, 'length' => '10'),
+				'name' => array('type' => 'string', 'null' => true, 'default' => null, 'length' => '20'),
+			);
+		}
+		return $this->_schema;
+	}
+
+}
+
+/**
+ * User2 class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class User2 extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'User2'
+ * @access public
+ */
+	var $name = 'User2';
+
+/**
+ * table property
+ *
+ * @var string 'user'
+ * @access public
+ */
+	var $table = 'user';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array(
+		'Group' => array(
+			'className' => 'Group'
+		),
+		'Level' => array(
+			'className' => 'Level'
+		)
+	);
+
+/**
+ * hasMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasMany = array(
+		'Article2' => array(
+			'className' => 'Article2'
+		),
+	);
+
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function schema() {
+		if (!isset($this->_schema)) {
+			$this->_schema = array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => null, 'length' => '10'),
+				'group_id' => array('type' => 'integer', 'null' => false, 'default' => null, 'length' => '10'),
+				'level_id' => array('type' => 'integer', 'null' => false, 'default' => null, 'length' => '10'),
+				'name' => array('type' => 'string', 'null' => true, 'default' => null, 'length' => '20'),
+			);
+		}
+		return $this->_schema;
+	}
+}
+
+/**
+ * Category2 class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class Category2 extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Category2'
+ * @access public
+ */
+	var $name = 'Category2';
+
+/**
+ * table property
+ *
+ * @var string 'category'
+ * @access public
+ */
+	var $table = 'category';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array(
+		'Group' => array(
+			'className' => 'Group',
+			'foreignKey' => 'group_id'
+		),
+		'ParentCat' => array(
+			'className' => 'Category2',
+			'foreignKey' => 'parent_id'
+		)
+	);
+
+/**
+ * hasMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasMany = array(
+		'ChildCat' => array(
+			'className' => 'Category2',
+			'foreignKey' => 'parent_id'
+		),
+		'Article2' => array(
+			'className' => 'Article2',
+			'order'=>'Article2.published_date DESC',
+			'foreignKey' => 'category_id',
+			'limit'=>'3')
+	);
+
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function schema() {
+		if (!isset($this->_schema)) {
+			$this->_schema = array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => '', 'length' => '10'),
+				'group_id' => array('type' => 'integer', 'null' => false, 'default' => '', 'length' => '10'),
+				'parent_id' => array('type' => 'integer', 'null' => false, 'default' => '', 'length' => '10'),
+				'name' => array('type' => 'string', 'null' => false, 'default' => '', 'length' => '255'),
+				'icon' => array('type' => 'string', 'null' => false, 'default' => '', 'length' => '255'),
+				'description' => array('type' => 'text', 'null' => false, 'default' => '', 'length' => null),
+
+			);
+		}
+		return $this->_schema;
+	}
+}
+
+/**
+ * Article2 class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class Article2 extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Article2'
+ * @access public
+ */
+	var $name = 'Article2';
+
+/**
+ * table property
+ *
+ * @var string 'article'
+ * @access public
+ */
+	var $table = 'article';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array(
+		'Category2' => array('className' => 'Category2'),
+		'User2' => array('className' => 'User2')
+	);
+
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function schema() {
+		if (!isset($this->_schema)) {
+			$this->_schema = array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => '', 'length' => '10'),
+				'category_id' => array('type' => 'integer', 'null' => false, 'default' => '0', 'length' => '10'),
+				'user_id' => array('type' => 'integer', 'null' => false, 'default' => '0', 'length' => '10'),
+				'rate_count' => array('type' => 'integer', 'null' => false, 'default' => '0', 'length' => '10'),
+				'rate_sum' => array('type' => 'integer', 'null' => false, 'default' => '0', 'length' => '10'),
+				'viewed' => array('type' => 'integer', 'null' => false, 'default' => '0', 'length' => '10'),
+				'version' => array('type' => 'string', 'null' => true, 'default' => '', 'length' => '45'),
+				'title' => array('type' => 'string', 'null' => false, 'default' => '', 'length' => '200'),
+				'intro' => array('text' => 'string', 'null' => true, 'default' => '', 'length' => null),
+				'comments' => array('type' => 'integer', 'null' => false, 'default' => '0', 'length' => '4'),
+				'body' => array('text' => 'string', 'null' => true, 'default' => '', 'length' => null),
+				'isdraft' => array('type' => 'boolean', 'null' => false, 'default' => '0', 'length' => '1'),
+				'allow_comments' => array('type' => 'boolean', 'null' => false, 'default' => '1', 'length' => '1'),
+				'moderate_comments' => array('type' => 'boolean', 'null' => false, 'default' => '1', 'length' => '1'),
+				'published' => array('type' => 'boolean', 'null' => false, 'default' => '0', 'length' => '1'),
+				'multipage' => array('type' => 'boolean', 'null' => false, 'default' => '0', 'length' => '1'),
+				'published_date' => array('type' => 'datetime', 'null' => true, 'default' => '', 'length' => null),
+				'created' => array('type' => 'datetime', 'null' => false, 'default' => '0000-00-00 00:00:00', 'length' => null),
+				'modified' => array('type' => 'datetime', 'null' => false, 'default' => '0000-00-00 00:00:00', 'length' => null)
+			);
+		}
+		return $this->_schema;
+	}
+}
+
+/**
+ * CategoryFeatured2 class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class CategoryFeatured2 extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'CategoryFeatured2'
+ * @access public
+ */
+	var $name = 'CategoryFeatured2';
+
+/**
+ * table property
+ *
+ * @var string 'category_featured'
+ * @access public
+ */
+	var $table = 'category_featured';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function schema() {
+		if (!isset($this->_schema)) {
+			$this->_schema = array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => '', 'length' => '10'),
+				'parent_id' => array('type' => 'integer', 'null' => false, 'default' => '', 'length' => '10'),
+				'name' => array('type' => 'string', 'null' => false, 'default' => '', 'length' => '255'),
+				'icon' => array('type' => 'string', 'null' => false, 'default' => '', 'length' => '255'),
+				'description' => array('text' => 'string', 'null' => false, 'default' => '', 'length' => null)
+			);
+		}
+		return $this->_schema;
+	}
+}
+
+/**
+ * Featured2 class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class Featured2 extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Featured2'
+ * @access public
+ */
+	var $name = 'Featured2';
+
+/**
+ * table property
+ *
+ * @var string 'featured2'
+ * @access public
+ */
+	var $table = 'featured2';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array(
+		'CategoryFeatured2' => array(
+			'className' => 'CategoryFeatured2'
+		)
+	);
+
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function schema() {
+		if (!isset($this->_schema)) {
+			$this->_schema = array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => null, 'length' => '10'),
+				'article_id' => array('type' => 'integer', 'null' => false, 'default' => '0', 'length' => '10'),
+				'category_id' => array('type' => 'integer', 'null' => false, 'default' => '0', 'length' => '10'),
+				'name' => array('type' => 'string', 'null' => true, 'default' => null, 'length' => '20')
+			);
+		}
+		return $this->_schema;
+	}
+}
+
+/**
+ * Comment2 class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class Comment2 extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Comment2'
+ * @access public
+ */
+	var $name = 'Comment2';
+
+/**
+ * table property
+ *
+ * @var string 'comment'
+ * @access public
+ */
+	var $table = 'comment';
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('ArticleFeatured2', 'User2');
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function schema() {
+		if (!isset($this->_schema)) {
+			$this->_schema = array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => null, 'length' => '10'),
+				'article_featured_id' => array('type' => 'integer', 'null' => false, 'default' => '0', 'length' => '10'),
+				'user_id' => array('type' => 'integer', 'null' => false, 'default' => '0', 'length' => '10'),
+				'name' => array('type' => 'string', 'null' => true, 'default' => null, 'length' => '20')
+			);
+		}
+		return $this->_schema;
+	}
+}
+
+/**
+ * ArticleFeatured2 class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class ArticleFeatured2 extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'ArticleFeatured2'
+ * @access public
+ */
+	var $name = 'ArticleFeatured2';
+
+/**
+ * table property
+ *
+ * @var string 'article_featured'
+ * @access public
+ */
+	var $table = 'article_featured';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array(
+		'CategoryFeatured2' => array('className' => 'CategoryFeatured2'),
+		'User2' => array('className' => 'User2')
+	);
+
+/**
+ * hasOne property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasOne = array(
+		'Featured2' => array('className' => 'Featured2')
+	);
+
+/**
+ * hasMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasMany = array(
+		'Comment2' => array('className'=>'Comment2', 'dependent' => true)
+	);
+
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function schema() {
+		if (!isset($this->_schema)) {
+			$this->_schema = array(
+				'id' => array('type' => 'integer', 'null' => false, 'default' => null, 'length' => '10'),
+				'category_featured_id' => array('type' => 'integer', 'null' => false, 'default' => '0', 'length' => '10'),
+				'user_id' => array('type' => 'integer', 'null' => false, 'default' => '0', 'length' => '10'),
+				'title' => array('type' => 'string', 'null' => true, 'default' => null, 'length' => '20'),
+				'body' => array('text' => 'string', 'null' => true, 'default' => '', 'length' => null),
+				'published' => array('type' => 'boolean', 'null' => false, 'default' => '0', 'length' => '1'),
+				'published_date' => array('type' => 'datetime', 'null' => true, 'default' => '', 'length' => null),
+				'created' => array('type' => 'datetime', 'null' => false, 'default' => '0000-00-00 00:00:00', 'length' => null),
+				'modified' => array('type' => 'datetime', 'null' => false, 'default' => '0000-00-00 00:00:00', 'length' => null)
+			);
+		}
+		return $this->_schema;
+	}
+}
+
+/**
+ * DboSourceTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.datasources
+ */
+class DboSourceTest extends CakeTestCase {
+
+/**
+ * debug property
+ *
+ * @var mixed null
+ * @access public
+ */
+	var $debug = null;
+
+/**
+ * autoFixtures property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $autoFixtures = false;
+
+/**
+ * fixtures property
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array(
+		'core.apple', 'core.article', 'core.articles_tag', 'core.attachment', 'core.comment',
+		'core.sample', 'core.tag', 'core.user', 'core.post', 'core.author', 'core.data_test'
+	);
+
+/**
+ * startTest method
+ *
+ * @access public
+ * @return void
+ */
+	function startTest() {
+		$this->__config = $this->db->config;
+
+		if (!class_exists('DboTest')) {
+			$db = ConnectionManager::getDataSource('test_suite');
+			$class = get_class($db);
+			eval("class DboTest extends $class {
+				var \$simulated = array();
+
+/**
+ * execute method
+ *
+ * @param \$sql
+ * @access protected
+ * @return void
+ */
+				function _execute(\$sql) {
+					\$this->simulated[] = \$sql;
+					return null;
+				}
+
+/**
+ * getLastQuery method
+ *
+ * @access public
+ * @return void
+ */
+				function getLastQuery() {
+					return \$this->simulated[count(\$this->simulated) - 1];
+				}
+			}");
+		}
+
+		$this->testDb =& new DboTest($this->__config);
+		$this->testDb->cacheSources = false;
+		$this->testDb->startQuote = '`';
+		$this->testDb->endQuote = '`';
+		Configure::write('debug', 1);
+		$this->debug = Configure::read('debug');
+		$this->Model =& new TestModel();
+	}
+
+/**
+ * endTest method
+ *
+ * @access public
+ * @return void
+ */
+	function endTest() {
+		unset($this->Model);
+		Configure::write('debug', $this->debug);
+		ClassRegistry::flush();
+		unset($this->debug);
+	}
+
+/**
+ * testFieldDoubleEscaping method
+ *
+ * @access public
+ * @return void
+ */
+	function testFieldDoubleEscaping() {
+		$config = array_merge($this->__config, array('driver' => 'test'));
+		$test =& ConnectionManager::create('quoteTest', $config);
+		$test->simulated = array();
+
+		$this->Model =& new Article2(array('alias' => 'Article', 'ds' => 'quoteTest'));
+		$this->Model->setDataSource('quoteTest');
+
+		$this->assertEqual($this->Model->escapeField(), '`Article`.`id`');
+		$result = $test->fields($this->Model, null, $this->Model->escapeField());
+		$this->assertEqual($result, array('`Article`.`id`'));
+
+		$result = $test->read($this->Model, array(
+			'fields' => $this->Model->escapeField(),
+			'conditions' => null,
+			'recursive' => -1
+		));
+		$this->assertEqual(trim($test->simulated[0]), 'SELECT `Article`.`id` FROM `' . $this->testDb->fullTableName('article', false) . '` AS `Article`   WHERE 1 = 1');
+
+		$test->startQuote = '[';
+		$test->endQuote = ']';
+		$this->assertEqual($this->Model->escapeField(), '[Article].[id]');
+
+		$result = $test->fields($this->Model, null, $this->Model->escapeField());
+		$this->assertEqual($result, array('[Article].[id]'));
+
+		$result = $test->read($this->Model, array(
+			'fields' => $this->Model->escapeField(),
+			'conditions' => null,
+			'recursive' => -1
+		));
+		$this->assertEqual(trim($test->simulated[1]), 'SELECT [Article].[id] FROM [' . $this->testDb->fullTableName('article', false) . '] AS [Article]   WHERE 1 = 1');
+
+		ClassRegistry::removeObject('Article');
+	}
+
+/**
+ * testGenerateAssociationQuerySelfJoin method
+ *
+ * @access public
+ * @return void
+ */
+	function testGenerateAssociationQuerySelfJoin() {
+		$this->startTime = microtime(true);
+		$this->Model =& new Article2();
+		$this->_buildRelatedModels($this->Model);
+		$this->_buildRelatedModels($this->Model->Category2);
+		$this->Model->Category2->ChildCat =& new Category2();
+		$this->Model->Category2->ParentCat =& new Category2();
+
+		$queryData = array();
+
+		foreach ($this->Model->Category2->__associations as $type) {
+			foreach ($this->Model->Category2->{$type} as $assoc => $assocData) {
+				$linkModel =& $this->Model->Category2->{$assoc};
+				$external = isset($assocData['external']);
+
+				if ($this->Model->Category2->alias == $linkModel->alias && $type != 'hasAndBelongsToMany' && $type != 'hasMany') {
+					$result = $this->testDb->generateAssociationQuery($this->Model->Category2, $linkModel, $type, $assoc, $assocData, $queryData, $external, $null);
+					$this->assertTrue($result);
+				} else {
+					if ($this->Model->Category2->useDbConfig == $linkModel->useDbConfig) {
+						$result = $this->testDb->generateAssociationQuery($this->Model->Category2, $linkModel, $type, $assoc, $assocData, $queryData, $external, $null);
+						$this->assertTrue($result);
+					}
+				}
+			}
+		}
+
+		$query = $this->testDb->generateAssociationQuery($this->Model->Category2, $null, null, null, null, $queryData, false, $null);
+		$this->assertPattern('/^SELECT\s+(.+)FROM(.+)`Category2`\.`group_id`\s+=\s+`Group`\.`id`\)\s+LEFT JOIN(.+)WHERE\s+1 = 1\s*$/', $query);
+
+		$this->Model =& new TestModel4();
+		$this->Model->schema();
+		$this->_buildRelatedModels($this->Model);
+
+		$binding = array('type' => 'belongsTo', 'model' => 'TestModel4Parent');
+		$queryData = array();
+		$resultSet = null;
+		$null = null;
+
+		$params = &$this->_prepareAssociationQuery($this->Model, $queryData, $binding);
+
+		$_queryData = $queryData;
+		$result = $this->testDb->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
+		$this->assertTrue($result);
+
+		$expected = array(
+			'fields' => array(
+				'`TestModel4`.`id`',
+				'`TestModel4`.`name`',
+				'`TestModel4`.`created`',
+				'`TestModel4`.`updated`',
+				'`TestModel4Parent`.`id`',
+				'`TestModel4Parent`.`name`',
+				'`TestModel4Parent`.`created`',
+				'`TestModel4Parent`.`updated`'
+			),
+			'joins' => array(
+				array(
+					'table' => '`test_model4`',
+					'alias' => 'TestModel4Parent',
+					'type' => 'LEFT',
+					'conditions' => '`TestModel4`.`parent_id` = `TestModel4Parent`.`id`'
+				)
+			),
+			'limit' => array(),
+			'offset' => array(),
+			'conditions' => array(),
+			'order' => array(),
+			'group' => null,
+			'callbacks' => null
+		);
+		$queryData['joins'][0]['table'] = $this->testDb->fullTableName($queryData['joins'][0]['table']);
+		$this->assertEqual($queryData, $expected);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $null, null, null, null, $queryData, false, $null);
+		$this->assertPattern('/^SELECT\s+`TestModel4`\.`id`, `TestModel4`\.`name`, `TestModel4`\.`created`, `TestModel4`\.`updated`, `TestModel4Parent`\.`id`, `TestModel4Parent`\.`name`, `TestModel4Parent`\.`created`, `TestModel4Parent`\.`updated`\s+/', $result);
+		$this->assertPattern('/FROM\s+`test_model4` AS `TestModel4`\s+LEFT JOIN\s+`test_model4` AS `TestModel4Parent`/', $result);
+		$this->assertPattern('/\s+ON\s+\(`TestModel4`.`parent_id` = `TestModel4Parent`.`id`\)\s+WHERE/', $result);
+		$this->assertPattern('/\s+WHERE\s+1 = 1\s+$/', $result);
+
+		$params['assocData']['type'] = 'INNER';
+		$this->Model->belongsTo['TestModel4Parent']['type'] = 'INNER';
+		$result = $this->testDb->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $_queryData, $params['external'], $resultSet);
+		$this->assertTrue($result);
+		$this->assertEqual($_queryData['joins'][0]['type'], 'INNER');
+	}
+
+/**
+ * testGenerateInnerJoinAssociationQuery method
+ *
+ * @access public
+ * @return void
+ */
+	function testGenerateInnerJoinAssociationQuery() {
+		$this->Model =& new TestModel9();
+		$test =& ConnectionManager::create('test2', $this->__config);
+		$this->Model->setDataSource('test2');
+		$this->Model->TestModel8 =& new TestModel8();
+		$this->Model->TestModel8->setDataSource('test2');
+
+		$this->testDb->read($this->Model, array('recursive' => 1));
+		$result = $this->testDb->getLastQuery();
+		$this->assertPattern('/`TestModel9` LEFT JOIN `' . $this->testDb->fullTableName('test_model8', false) . '`/', $result);
+
+		$this->Model->belongsTo['TestModel8']['type'] = 'INNER';
+		$this->testDb->read($this->Model, array('recursive' => 1));
+		$result = $this->testDb->getLastQuery();
+		$this->assertPattern('/`TestModel9` INNER JOIN `' . $this->testDb->fullTableName('test_model8', false) . '`/', $result);
+
+	}
+
+/**
+ * testGenerateAssociationQuerySelfJoinWithConditionsInHasOneBinding method
+ *
+ * @access public
+ * @return void
+ */
+	function testGenerateAssociationQuerySelfJoinWithConditionsInHasOneBinding() {
+		$this->Model =& new TestModel8();
+		$this->Model->schema();
+		$this->_buildRelatedModels($this->Model);
+
+		$binding = array('type' => 'hasOne', 'model' => 'TestModel9');
+		$queryData = array();
+		$resultSet = null;
+		$null = null;
+
+		$params = &$this->_prepareAssociationQuery($this->Model, $queryData, $binding);
+		$_queryData = $queryData;
+		$result = $this->testDb->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
+		$this->assertTrue($result);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $null, null, null, null, $queryData, false, $null);
+		$this->assertPattern('/^SELECT\s+`TestModel8`\.`id`, `TestModel8`\.`test_model9_id`, `TestModel8`\.`name`, `TestModel8`\.`created`, `TestModel8`\.`updated`, `TestModel9`\.`id`, `TestModel9`\.`test_model8_id`, `TestModel9`\.`name`, `TestModel9`\.`created`, `TestModel9`\.`updated`\s+/', $result);
+		$this->assertPattern('/FROM\s+`test_model8` AS `TestModel8`\s+LEFT JOIN\s+`test_model9` AS `TestModel9`/', $result);
+		$this->assertPattern('/\s+ON\s+\(`TestModel9`\.`name` != \'mariano\'\s+AND\s+`TestModel9`.`test_model8_id` = `TestModel8`.`id`\)\s+WHERE/', $result);
+		$this->assertPattern('/\s+WHERE\s+(?:\()?1\s+=\s+1(?:\))?\s*$/', $result);
+	}
+
+/**
+ * testGenerateAssociationQuerySelfJoinWithConditionsInBelongsToBinding method
+ *
+ * @access public
+ * @return void
+ */
+	function testGenerateAssociationQuerySelfJoinWithConditionsInBelongsToBinding() {
+		$this->Model =& new TestModel9();
+		$this->Model->schema();
+		$this->_buildRelatedModels($this->Model);
+
+		$binding = array('type' => 'belongsTo', 'model' => 'TestModel8');
+		$queryData = array();
+		$resultSet = null;
+		$null = null;
+
+		$params = &$this->_prepareAssociationQuery($this->Model, $queryData, $binding);
+		$result = $this->testDb->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
+		$this->assertTrue($result);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $null, null, null, null, $queryData, false, $null);
+		$this->assertPattern('/^SELECT\s+`TestModel9`\.`id`, `TestModel9`\.`test_model8_id`, `TestModel9`\.`name`, `TestModel9`\.`created`, `TestModel9`\.`updated`, `TestModel8`\.`id`, `TestModel8`\.`test_model9_id`, `TestModel8`\.`name`, `TestModel8`\.`created`, `TestModel8`\.`updated`\s+/', $result);
+		$this->assertPattern('/FROM\s+`test_model9` AS `TestModel9`\s+LEFT JOIN\s+`test_model8` AS `TestModel8`/', $result);
+		$this->assertPattern('/\s+ON\s+\(`TestModel8`\.`name` != \'larry\'\s+AND\s+`TestModel9`.`test_model8_id` = `TestModel8`.`id`\)\s+WHERE/', $result);
+		$this->assertPattern('/\s+WHERE\s+(?:\()?1\s+=\s+1(?:\))?\s*$/', $result);
+	}
+
+/**
+ * testGenerateAssociationQuerySelfJoinWithConditions method
+ *
+ * @access public
+ * @return void
+ */
+	function testGenerateAssociationQuerySelfJoinWithConditions() {
+		$this->Model =& new TestModel4();
+		$this->Model->schema();
+		$this->_buildRelatedModels($this->Model);
+
+		$binding = array('type' => 'belongsTo', 'model' => 'TestModel4Parent');
+		$queryData = array('conditions' => array('TestModel4Parent.name !=' => 'mariano'));
+		$resultSet = null;
+		$null = null;
+
+		$params = &$this->_prepareAssociationQuery($this->Model, $queryData, $binding);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
+		$this->assertTrue($result);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $null, null, null, null, $queryData, false, $null);
+		$this->assertPattern('/^SELECT\s+`TestModel4`\.`id`, `TestModel4`\.`name`, `TestModel4`\.`created`, `TestModel4`\.`updated`, `TestModel4Parent`\.`id`, `TestModel4Parent`\.`name`, `TestModel4Parent`\.`created`, `TestModel4Parent`\.`updated`\s+/', $result);
+		$this->assertPattern('/FROM\s+`test_model4` AS `TestModel4`\s+LEFT JOIN\s+`test_model4` AS `TestModel4Parent`/', $result);
+		$this->assertPattern('/\s+ON\s+\(`TestModel4`.`parent_id` = `TestModel4Parent`.`id`\)\s+WHERE/', $result);
+		$this->assertPattern('/\s+WHERE\s+(?:\()?`TestModel4Parent`.`name`\s+!=\s+\'mariano\'(?:\))?\s*$/', $result);
+
+		$this->Featured2 =& new Featured2();
+		$this->Featured2->schema();
+
+		$this->Featured2->bindModel(array(
+			'belongsTo' => array(
+				'ArticleFeatured2' => array(
+					'conditions' => 'ArticleFeatured2.published = \'Y\'',
+					'fields' => 'id, title, user_id, published'
+				)
+			)
+		));
+
+		$this->_buildRelatedModels($this->Featured2);
+
+		$binding = array('type' => 'belongsTo', 'model' => 'ArticleFeatured2');
+		$queryData = array('conditions' => array());
+		$resultSet = null;
+		$null = null;
+
+		$params = &$this->_prepareAssociationQuery($this->Featured2, $queryData, $binding);
+
+		$result = $this->testDb->generateAssociationQuery($this->Featured2, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
+		$this->assertTrue($result);
+		$result = $this->testDb->generateAssociationQuery($this->Featured2, $null, null, null, null, $queryData, false, $null);
+
+		$this->assertPattern(
+			'/^SELECT\s+`Featured2`\.`id`, `Featured2`\.`article_id`, `Featured2`\.`category_id`, `Featured2`\.`name`,\s+'.
+			'`ArticleFeatured2`\.`id`, `ArticleFeatured2`\.`title`, `ArticleFeatured2`\.`user_id`, `ArticleFeatured2`\.`published`\s+' .
+			'FROM\s+`featured2` AS `Featured2`\s+LEFT JOIN\s+`article_featured` AS `ArticleFeatured2`' .
+			'\s+ON\s+\(`ArticleFeatured2`.`published` = \'Y\'\s+AND\s+`Featured2`\.`article_featured2_id` = `ArticleFeatured2`\.`id`\)' .
+			'\s+WHERE\s+1\s+=\s+1\s*$/',
+			$result
+		);
+	}
+
+/**
+ * testGenerateAssociationQueryHasOne method
+ *
+ * @access public
+ * @return void
+ */
+	function testGenerateAssociationQueryHasOne() {
+		$this->Model =& new TestModel4();
+		$this->Model->schema();
+		$this->_buildRelatedModels($this->Model);
+
+		$binding = array('type' => 'hasOne', 'model' => 'TestModel5');
+
+		$queryData = array();
+		$resultSet = null;
+		$null = null;
+
+		$params = &$this->_prepareAssociationQuery($this->Model, $queryData, $binding);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
+		$this->assertTrue($result);
+
+		$result = $this->testDb->buildJoinStatement($queryData['joins'][0]);
+		$expected = ' LEFT JOIN `test_model5` AS `TestModel5` ON (`TestModel5`.`test_model4_id` = `TestModel4`.`id`)';
+		$this->assertEqual(trim($result), trim($expected));
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $null, null, null, null, $queryData, false, $null);
+		$this->assertPattern('/^SELECT\s+`TestModel4`\.`id`, `TestModel4`\.`name`, `TestModel4`\.`created`, `TestModel4`\.`updated`, `TestModel5`\.`id`, `TestModel5`\.`test_model4_id`, `TestModel5`\.`name`, `TestModel5`\.`created`, `TestModel5`\.`updated`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model4` AS `TestModel4`\s+LEFT JOIN\s+/', $result);
+		$this->assertPattern('/`test_model5` AS `TestModel5`\s+ON\s+\(`TestModel5`.`test_model4_id` = `TestModel4`.`id`\)\s+WHERE/', $result);
+		$this->assertPattern('/\s+WHERE\s+(?:\()?\s*1 = 1\s*(?:\))?\s*$/', $result);
+	}
+
+/**
+ * testGenerateAssociationQueryHasOneWithConditions method
+ *
+ * @access public
+ * @return void
+ */
+	function testGenerateAssociationQueryHasOneWithConditions() {
+		$this->Model =& new TestModel4();
+		$this->Model->schema();
+		$this->_buildRelatedModels($this->Model);
+
+		$binding = array('type' => 'hasOne', 'model' => 'TestModel5');
+
+		$queryData = array('conditions' => array('TestModel5.name !=' => 'mariano'));
+		$resultSet = null;
+		$null = null;
+
+		$params = &$this->_prepareAssociationQuery($this->Model, $queryData, $binding);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
+		$this->assertTrue($result);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $null, null, null, null, $queryData, false, $null);
+
+		$this->assertPattern('/^SELECT\s+`TestModel4`\.`id`, `TestModel4`\.`name`, `TestModel4`\.`created`, `TestModel4`\.`updated`, `TestModel5`\.`id`, `TestModel5`\.`test_model4_id`, `TestModel5`\.`name`, `TestModel5`\.`created`, `TestModel5`\.`updated`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model4` AS `TestModel4`\s+LEFT JOIN\s+`test_model5` AS `TestModel5`/', $result);
+		$this->assertPattern('/\s+ON\s+\(`TestModel5`.`test_model4_id`\s+=\s+`TestModel4`.`id`\)\s+WHERE/', $result);
+		$this->assertPattern('/\s+WHERE\s+(?:\()?\s*`TestModel5`.`name`\s+!=\s+\'mariano\'\s*(?:\))?\s*$/', $result);
+	}
+
+/**
+ * testGenerateAssociationQueryBelongsTo method
+ *
+ * @access public
+ * @return void
+ */
+	function testGenerateAssociationQueryBelongsTo() {
+		$this->Model =& new TestModel5();
+		$this->Model->schema();
+		$this->_buildRelatedModels($this->Model);
+
+		$binding = array('type'=>'belongsTo', 'model'=>'TestModel4');
+		$queryData = array();
+		$resultSet = null;
+		$null = null;
+
+		$params = &$this->_prepareAssociationQuery($this->Model, $queryData, $binding);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
+		$this->assertTrue($result);
+
+		$result = $this->testDb->buildJoinStatement($queryData['joins'][0]);
+		$expected = ' LEFT JOIN `test_model4` AS `TestModel4` ON (`TestModel5`.`test_model4_id` = `TestModel4`.`id`)';
+		$this->assertEqual(trim($result), trim($expected));
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $null, null, null, null, $queryData, false, $null);
+		$this->assertPattern('/^SELECT\s+`TestModel5`\.`id`, `TestModel5`\.`test_model4_id`, `TestModel5`\.`name`, `TestModel5`\.`created`, `TestModel5`\.`updated`, `TestModel4`\.`id`, `TestModel4`\.`name`, `TestModel4`\.`created`, `TestModel4`\.`updated`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model5` AS `TestModel5`\s+LEFT JOIN\s+`test_model4` AS `TestModel4`/', $result);
+		$this->assertPattern('/\s+ON\s+\(`TestModel5`.`test_model4_id` = `TestModel4`.`id`\)\s+WHERE\s+/', $result);
+		$this->assertPattern('/\s+WHERE\s+(?:\()?\s*1 = 1\s*(?:\))?\s*$/', $result);
+	}
+
+/**
+ * testGenerateAssociationQueryBelongsToWithConditions method
+ *
+ * @access public
+ * @return void
+ */
+	function testGenerateAssociationQueryBelongsToWithConditions() {
+		$this->Model =& new TestModel5();
+		$this->Model->schema();
+		$this->_buildRelatedModels($this->Model);
+
+		$binding = array('type' => 'belongsTo', 'model' => 'TestModel4');
+		$queryData = array('conditions' => array('TestModel5.name !=' => 'mariano'));
+		$resultSet = null;
+		$null = null;
+
+		$params = &$this->_prepareAssociationQuery($this->Model, $queryData, $binding);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
+		$this->assertTrue($result);
+
+		$result = $this->testDb->buildJoinStatement($queryData['joins'][0]);
+		$expected = ' LEFT JOIN `test_model4` AS `TestModel4` ON (`TestModel5`.`test_model4_id` = `TestModel4`.`id`)';
+		$this->assertEqual(trim($result), trim($expected));
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $null, null, null, null, $queryData, false, $null);
+		$this->assertPattern('/^SELECT\s+`TestModel5`\.`id`, `TestModel5`\.`test_model4_id`, `TestModel5`\.`name`, `TestModel5`\.`created`, `TestModel5`\.`updated`, `TestModel4`\.`id`, `TestModel4`\.`name`, `TestModel4`\.`created`, `TestModel4`\.`updated`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model5` AS `TestModel5`\s+LEFT JOIN\s+`test_model4` AS `TestModel4`/', $result);
+		$this->assertPattern('/\s+ON\s+\(`TestModel5`.`test_model4_id` = `TestModel4`.`id`\)\s+WHERE\s+/', $result);
+		$this->assertPattern('/\s+WHERE\s+`TestModel5`.`name` != \'mariano\'\s*$/', $result);
+	}
+
+/**
+ * testGenerateAssociationQueryHasMany method
+ *
+ * @access public
+ * @return void
+ */
+	function testGenerateAssociationQueryHasMany() {
+		$this->Model =& new TestModel5();
+		$this->Model->schema();
+		$this->_buildRelatedModels($this->Model);
+
+		$binding = array('type' => 'hasMany', 'model' => 'TestModel6');
+		$queryData = array();
+		$resultSet = null;
+		$null = null;
+
+		$params = &$this->_prepareAssociationQuery($this->Model, $queryData, $binding);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
+
+		$this->assertPattern('/^SELECT\s+`TestModel6`\.`id`, `TestModel6`\.`test_model5_id`, `TestModel6`\.`name`, `TestModel6`\.`created`, `TestModel6`\.`updated`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model6` AS `TestModel6`\s+WHERE/', $result);
+		$this->assertPattern('/\s+WHERE\s+`TestModel6`.`test_model5_id`\s+=\s+\({\$__cakeID__\$}\)/', $result);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $null, null, null, null, $queryData, false, $null);
+		$this->assertPattern('/^SELECT\s+`TestModel5`\.`id`, `TestModel5`\.`test_model4_id`, `TestModel5`\.`name`, `TestModel5`\.`created`, `TestModel5`\.`updated`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model5` AS `TestModel5`\s+WHERE\s+/', $result);
+		$this->assertPattern('/\s+WHERE\s+(?:\()?\s*1 = 1\s*(?:\))?\s*$/', $result);
+	}
+
+/**
+ * testGenerateAssociationQueryHasManyWithLimit method
+ *
+ * @access public
+ * @return void
+ */
+	function testGenerateAssociationQueryHasManyWithLimit() {
+		$this->Model =& new TestModel5();
+		$this->Model->schema();
+		$this->_buildRelatedModels($this->Model);
+
+		$this->Model->hasMany['TestModel6']['limit'] = 2;
+
+		$binding = array('type' => 'hasMany', 'model' => 'TestModel6');
+		$queryData = array();
+		$resultSet = null;
+		$null = null;
+
+		$params = &$this->_prepareAssociationQuery($this->Model, $queryData, $binding);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
+		$this->assertPattern(
+			'/^SELECT\s+' .
+			'`TestModel6`\.`id`, `TestModel6`\.`test_model5_id`, `TestModel6`\.`name`, `TestModel6`\.`created`, `TestModel6`\.`updated`\s+'.
+			'FROM\s+`test_model6` AS `TestModel6`\s+WHERE\s+' .
+			'`TestModel6`.`test_model5_id`\s+=\s+\({\$__cakeID__\$}\)\s*'.
+			'LIMIT \d*'.
+			'\s*$/', $result
+		);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $null, null, null, null, $queryData, false, $null);
+		$this->assertPattern(
+			'/^SELECT\s+'.
+			'`TestModel5`\.`id`, `TestModel5`\.`test_model4_id`, `TestModel5`\.`name`, `TestModel5`\.`created`, `TestModel5`\.`updated`\s+'.
+			'FROM\s+`test_model5` AS `TestModel5`\s+WHERE\s+'.
+			'(?:\()?\s*1 = 1\s*(?:\))?'.
+			'\s*$/', $result
+		);
+	}
+
+/**
+ * testGenerateAssociationQueryHasManyWithConditions method
+ *
+ * @access public
+ * @return void
+ */
+	function testGenerateAssociationQueryHasManyWithConditions() {
+		$this->Model =& new TestModel5();
+		$this->Model->schema();
+		$this->_buildRelatedModels($this->Model);
+
+		$binding = array('type' => 'hasMany', 'model' => 'TestModel6');
+		$queryData = array('conditions' => array('TestModel5.name !=' => 'mariano'));
+		$resultSet = null;
+		$null = null;
+
+		$params = &$this->_prepareAssociationQuery($this->Model, $queryData, $binding);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
+		$this->assertPattern('/^SELECT\s+`TestModel6`\.`id`, `TestModel6`\.`test_model5_id`, `TestModel6`\.`name`, `TestModel6`\.`created`, `TestModel6`\.`updated`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model6` AS `TestModel6`\s+WHERE\s+/', $result);
+		$this->assertPattern('/WHERE\s+(?:\()?`TestModel6`\.`test_model5_id`\s+=\s+\({\$__cakeID__\$}\)(?:\))?/', $result);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $null, null, null, null, $queryData, false, $null);
+		$this->assertPattern('/^SELECT\s+`TestModel5`\.`id`, `TestModel5`\.`test_model4_id`, `TestModel5`\.`name`, `TestModel5`\.`created`, `TestModel5`\.`updated`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model5` AS `TestModel5`\s+WHERE\s+/', $result);
+		$this->assertPattern('/\s+WHERE\s+(?:\()?`TestModel5`.`name`\s+!=\s+\'mariano\'(?:\))?\s*$/', $result);
+	}
+
+/**
+ * testGenerateAssociationQueryHasManyWithOffsetAndLimit method
+ *
+ * @access public
+ * @return void
+ */
+	function testGenerateAssociationQueryHasManyWithOffsetAndLimit() {
+		$this->Model =& new TestModel5();
+		$this->Model->schema();
+		$this->_buildRelatedModels($this->Model);
+
+		$__backup = $this->Model->hasMany['TestModel6'];
+
+		$this->Model->hasMany['TestModel6']['offset'] = 2;
+		$this->Model->hasMany['TestModel6']['limit'] = 5;
+
+		$binding = array('type' => 'hasMany', 'model' => 'TestModel6');
+		$queryData = array();
+		$resultSet = null;
+		$null = null;
+
+		$params = &$this->_prepareAssociationQuery($this->Model, $queryData, $binding);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
+
+		$this->assertPattern('/^SELECT\s+`TestModel6`\.`id`, `TestModel6`\.`test_model5_id`, `TestModel6`\.`name`, `TestModel6`\.`created`, `TestModel6`\.`updated`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model6` AS `TestModel6`\s+WHERE\s+/', $result);
+		$this->assertPattern('/WHERE\s+(?:\()?`TestModel6`\.`test_model5_id`\s+=\s+\({\$__cakeID__\$}\)(?:\))?/', $result);
+		$this->assertPattern('/\s+LIMIT 2,\s*5\s*$/', $result);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $null, null, null, null, $queryData, false, $null);
+		$this->assertPattern('/^SELECT\s+`TestModel5`\.`id`, `TestModel5`\.`test_model4_id`, `TestModel5`\.`name`, `TestModel5`\.`created`, `TestModel5`\.`updated`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model5` AS `TestModel5`\s+WHERE\s+/', $result);
+		$this->assertPattern('/\s+WHERE\s+(?:\()?1\s+=\s+1(?:\))?\s*$/', $result);
+
+		$this->Model->hasMany['TestModel6'] = $__backup;
+	}
+
+/**
+ * testGenerateAssociationQueryHasManyWithPageAndLimit method
+ *
+ * @access public
+ * @return void
+ */
+	function testGenerateAssociationQueryHasManyWithPageAndLimit() {
+		$this->Model =& new TestModel5();
+		$this->Model->schema();
+		$this->_buildRelatedModels($this->Model);
+
+		$__backup = $this->Model->hasMany['TestModel6'];
+
+		$this->Model->hasMany['TestModel6']['page'] = 2;
+		$this->Model->hasMany['TestModel6']['limit'] = 5;
+
+		$binding = array('type' => 'hasMany', 'model' => 'TestModel6');
+		$queryData = array();
+		$resultSet = null;
+		$null = null;
+
+		$params = &$this->_prepareAssociationQuery($this->Model, $queryData, $binding);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
+		$this->assertPattern('/^SELECT\s+`TestModel6`\.`id`, `TestModel6`\.`test_model5_id`, `TestModel6`\.`name`, `TestModel6`\.`created`, `TestModel6`\.`updated`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model6` AS `TestModel6`\s+WHERE\s+/', $result);
+		$this->assertPattern('/WHERE\s+(?:\()?`TestModel6`\.`test_model5_id`\s+=\s+\({\$__cakeID__\$}\)(?:\))?/', $result);
+		$this->assertPattern('/\s+LIMIT 5,\s*5\s*$/', $result);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $null, null, null, null, $queryData, false, $null);
+		$this->assertPattern('/^SELECT\s+`TestModel5`\.`id`, `TestModel5`\.`test_model4_id`, `TestModel5`\.`name`, `TestModel5`\.`created`, `TestModel5`\.`updated`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model5` AS `TestModel5`\s+WHERE\s+/', $result);
+		$this->assertPattern('/\s+WHERE\s+(?:\()?1\s+=\s+1(?:\))?\s*$/', $result);
+
+		$this->Model->hasMany['TestModel6'] = $__backup;
+	}
+
+/**
+ * testGenerateAssociationQueryHasManyWithFields method
+ *
+ * @access public
+ * @return void
+ */
+	function testGenerateAssociationQueryHasManyWithFields() {
+		$this->Model =& new TestModel5();
+		$this->Model->schema();
+		$this->_buildRelatedModels($this->Model);
+
+		$binding = array('type' => 'hasMany', 'model' => 'TestModel6');
+		$queryData = array('fields' => array('`TestModel5`.`name`'));
+		$resultSet = null;
+		$null = null;
+
+		$params = &$this->_prepareAssociationQuery($this->Model, $queryData, $binding);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
+		$this->assertPattern('/^SELECT\s+`TestModel6`\.`id`, `TestModel6`\.`test_model5_id`, `TestModel6`\.`name`, `TestModel6`\.`created`, `TestModel6`\.`updated`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model6` AS `TestModel6`\s+WHERE\s+/', $result);
+		$this->assertPattern('/WHERE\s+(?:\()?`TestModel6`\.`test_model5_id`\s+=\s+\({\$__cakeID__\$}\)(?:\))?/', $result);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $null, null, null, null, $queryData, false, $null);
+		$this->assertPattern('/^SELECT\s+`TestModel5`\.`name`, `TestModel5`\.`id`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model5` AS `TestModel5`\s+WHERE\s+/', $result);
+		$this->assertPattern('/\s+WHERE\s+(?:\()?1\s+=\s+1(?:\))?\s*$/', $result);
+
+		$binding = array('type' => 'hasMany', 'model' => 'TestModel6');
+		$queryData = array('fields' => array('`TestModel5`.`id`, `TestModel5`.`name`'));
+		$resultSet = null;
+		$null = null;
+
+		$params = &$this->_prepareAssociationQuery($this->Model, $queryData, $binding);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
+		$this->assertPattern('/^SELECT\s+`TestModel6`\.`id`, `TestModel6`\.`test_model5_id`, `TestModel6`\.`name`, `TestModel6`\.`created`, `TestModel6`\.`updated`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model6` AS `TestModel6`\s+WHERE\s+/', $result);
+		$this->assertPattern('/WHERE\s+(?:\()?`TestModel6`\.`test_model5_id`\s+=\s+\({\$__cakeID__\$}\)(?:\))?/', $result);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $null, null, null, null, $queryData, false, $null);
+		$this->assertPattern('/^SELECT\s+`TestModel5`\.`id`, `TestModel5`\.`name`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model5` AS `TestModel5`\s+WHERE\s+/', $result);
+		$this->assertPattern('/\s+WHERE\s+(?:\()?1\s+=\s+1(?:\))?\s*$/', $result);
+
+		$binding = array('type' => 'hasMany', 'model' => 'TestModel6');
+		$queryData = array('fields' => array('`TestModel5`.`name`', '`TestModel5`.`created`'));
+		$resultSet = null;
+		$null = null;
+
+		$params = &$this->_prepareAssociationQuery($this->Model, $queryData, $binding);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
+		$this->assertPattern('/^SELECT\s+`TestModel6`\.`id`, `TestModel6`\.`test_model5_id`, `TestModel6`\.`name`, `TestModel6`\.`created`, `TestModel6`\.`updated`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model6` AS `TestModel6`\s+WHERE\s+/', $result);
+		$this->assertPattern('/WHERE\s+(?:\()?`TestModel6`\.`test_model5_id`\s+=\s+\({\$__cakeID__\$}\)(?:\))?/', $result);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $null, null, null, null, $queryData, false, $null);
+		$this->assertPattern('/^SELECT\s+`TestModel5`\.`name`, `TestModel5`\.`created`, `TestModel5`\.`id`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model5` AS `TestModel5`\s+WHERE\s+/', $result);
+		$this->assertPattern('/\s+WHERE\s+(?:\()?1\s+=\s+1(?:\))?\s*$/', $result);
+
+		$this->Model->hasMany['TestModel6']['fields'] = array('name');
+
+		$binding = array('type' => 'hasMany', 'model' => 'TestModel6');
+		$queryData = array('fields' => array('`TestModel5`.`id`', '`TestModel5`.`name`'));
+		$resultSet = null;
+		$null = null;
+
+		$params = &$this->_prepareAssociationQuery($this->Model, $queryData, $binding);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
+		$this->assertPattern('/^SELECT\s+`TestModel6`\.`name`, `TestModel6`\.`test_model5_id`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model6` AS `TestModel6`\s+WHERE\s+/', $result);
+		$this->assertPattern('/WHERE\s+(?:\()?`TestModel6`\.`test_model5_id`\s+=\s+\({\$__cakeID__\$}\)(?:\))?/', $result);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $null, null, null, null, $queryData, false, $null);
+		$this->assertPattern('/^SELECT\s+`TestModel5`\.`id`, `TestModel5`\.`name`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model5` AS `TestModel5`\s+WHERE\s+/', $result);
+		$this->assertPattern('/\s+WHERE\s+(?:\()?1\s+=\s+1(?:\))?\s*$/', $result);
+
+		unset($this->Model->hasMany['TestModel6']['fields']);
+
+		$this->Model->hasMany['TestModel6']['fields'] = array('id', 'name');
+
+		$binding = array('type' => 'hasMany', 'model' => 'TestModel6');
+		$queryData = array('fields' => array('`TestModel5`.`id`', '`TestModel5`.`name`'));
+		$resultSet = null;
+		$null = null;
+
+		$params = &$this->_prepareAssociationQuery($this->Model, $queryData, $binding);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
+		$this->assertPattern('/^SELECT\s+`TestModel6`\.`id`, `TestModel6`\.`name`, `TestModel6`\.`test_model5_id`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model6` AS `TestModel6`\s+WHERE\s+/', $result);
+		$this->assertPattern('/WHERE\s+(?:\()?`TestModel6`\.`test_model5_id`\s+=\s+\({\$__cakeID__\$}\)(?:\))?/', $result);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $null, null, null, null, $queryData, false, $null);
+		$this->assertPattern('/^SELECT\s+`TestModel5`\.`id`, `TestModel5`\.`name`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model5` AS `TestModel5`\s+WHERE\s+/', $result);
+		$this->assertPattern('/\s+WHERE\s+(?:\()?1\s+=\s+1(?:\))?\s*$/', $result);
+
+		unset($this->Model->hasMany['TestModel6']['fields']);
+
+		$this->Model->hasMany['TestModel6']['fields'] = array('test_model5_id', 'name');
+
+		$binding = array('type' => 'hasMany', 'model' => 'TestModel6');
+		$queryData = array('fields' => array('`TestModel5`.`id`', '`TestModel5`.`name`'));
+		$resultSet = null;
+		$null = null;
+
+		$params = &$this->_prepareAssociationQuery($this->Model, $queryData, $binding);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
+		$this->assertPattern('/^SELECT\s+`TestModel6`\.`test_model5_id`, `TestModel6`\.`name`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model6` AS `TestModel6`\s+WHERE\s+/', $result);
+		$this->assertPattern('/WHERE\s+(?:\()?`TestModel6`\.`test_model5_id`\s+=\s+\({\$__cakeID__\$}\)(?:\))?/', $result);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $null, null, null, null, $queryData, false, $null);
+		$this->assertPattern('/^SELECT\s+`TestModel5`\.`id`, `TestModel5`\.`name`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model5` AS `TestModel5`\s+WHERE\s+/', $result);
+		$this->assertPattern('/\s+WHERE\s+(?:\()?1\s+=\s+1(?:\))?\s*$/', $result);
+
+		unset($this->Model->hasMany['TestModel6']['fields']);
+	}
+
+/**
+ * test generateAssociationQuery with a hasMany and an aggregate function.
+ *
+ * @return void
+ */
+	function testGenerateAssociationQueryHasManyAndAggregateFunction() {
+		$this->Model =& new TestModel5();
+		$this->Model->schema();
+		$this->_buildRelatedModels($this->Model);
+
+		$binding = array('type' => 'hasMany', 'model' => 'TestModel6');
+		$queryData = array('fields' => array('MIN(TestModel5.test_model4_id)'));
+		$resultSet = null;
+		$null = null;
+
+		$params = &$this->_prepareAssociationQuery($this->Model, $queryData, $binding);
+		$this->Model->recursive = 0;
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $null, $params['type'], $params['assoc'], $params['assocData'], $queryData, false, $resultSet);
+		$this->assertPattern('/^SELECT\s+MIN\(`TestModel5`\.`test_model4_id`\)\s+FROM/', $result);
+	}
+
+/**
+ * testGenerateAssociationQueryHasAndBelongsToMany method
+ *
+ * @access public
+ * @return void
+ */
+	function testGenerateAssociationQueryHasAndBelongsToMany() {
+		$this->Model =& new TestModel4();
+		$this->Model->schema();
+		$this->_buildRelatedModels($this->Model);
+
+		$binding = array('type' => 'hasAndBelongsToMany', 'model' => 'TestModel7');
+		$queryData = array();
+		$resultSet = null;
+		$null = null;
+
+		$params =& $this->_prepareAssociationQuery($this->Model, $queryData, $binding);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
+		$this->assertPattern('/^SELECT\s+`TestModel7`\.`id`, `TestModel7`\.`name`, `TestModel7`\.`created`, `TestModel7`\.`updated`, `TestModel4TestModel7`\.`test_model4_id`, `TestModel4TestModel7`\.`test_model7_id`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model7` AS `TestModel7`\s+JOIN\s+`' . $this->testDb->fullTableName('test_model4_test_model7', false) . '`/', $result);
+		$this->assertPattern('/\s+ON\s+\(`TestModel4TestModel7`\.`test_model4_id`\s+=\s+{\$__cakeID__\$}\s+AND/', $result);
+		$this->assertPattern('/\s+AND\s+`TestModel4TestModel7`\.`test_model7_id`\s+=\s+`TestModel7`\.`id`\)/', $result);
+		$this->assertPattern('/WHERE\s+(?:\()?1 = 1(?:\))?\s*$/', $result);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $null, null, null, null, $queryData, false, $null);
+		$this->assertPattern('/^SELECT\s+`TestModel4`\.`id`, `TestModel4`\.`name`, `TestModel4`\.`created`, `TestModel4`\.`updated`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model4` AS `TestModel4`\s+WHERE/', $result);
+		$this->assertPattern('/\s+WHERE\s+(?:\()?1 = 1(?:\))?\s*$/', $result);
+	}
+
+/**
+ * testGenerateAssociationQueryHasAndBelongsToManyWithConditions method
+ *
+ * @access public
+ * @return void
+ */
+	function testGenerateAssociationQueryHasAndBelongsToManyWithConditions() {
+		$this->Model =& new TestModel4();
+		$this->Model->schema();
+		$this->_buildRelatedModels($this->Model);
+
+		$binding = array('type'=>'hasAndBelongsToMany', 'model'=>'TestModel7');
+		$queryData = array('conditions' => array('TestModel4.name !=' => 'mariano'));
+		$resultSet = null;
+		$null = null;
+
+		$params =& $this->_prepareAssociationQuery($this->Model, $queryData, $binding);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
+		$this->assertPattern('/^SELECT\s+`TestModel7`\.`id`, `TestModel7`\.`name`, `TestModel7`\.`created`, `TestModel7`\.`updated`, `TestModel4TestModel7`\.`test_model4_id`, `TestModel4TestModel7`\.`test_model7_id`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model7`\s+AS\s+`TestModel7`\s+JOIN\s+`test_model4_test_model7`\s+AS\s+`TestModel4TestModel7`/', $result);
+		$this->assertPattern('/\s+ON\s+\(`TestModel4TestModel7`\.`test_model4_id`\s+=\s+{\$__cakeID__\$}/', $result);
+		$this->assertPattern('/\s+AND\s+`TestModel4TestModel7`\.`test_model7_id`\s+=\s+`TestModel7`\.`id`\)\s+WHERE\s+/', $result);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $null, null, null, null, $queryData, false, $null);
+		$this->assertPattern('/^SELECT\s+`TestModel4`\.`id`, `TestModel4`\.`name`, `TestModel4`\.`created`, `TestModel4`\.`updated`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model4` AS `TestModel4`\s+WHERE\s+(?:\()?`TestModel4`.`name`\s+!=\s+\'mariano\'(?:\))?\s*$/', $result);
+	}
+
+/**
+ * testGenerateAssociationQueryHasAndBelongsToManyWithOffsetAndLimit method
+ *
+ * @access public
+ * @return void
+ */
+	function testGenerateAssociationQueryHasAndBelongsToManyWithOffsetAndLimit() {
+		$this->Model =& new TestModel4();
+		$this->Model->schema();
+		$this->_buildRelatedModels($this->Model);
+
+		$__backup = $this->Model->hasAndBelongsToMany['TestModel7'];
+
+		$this->Model->hasAndBelongsToMany['TestModel7']['offset'] = 2;
+		$this->Model->hasAndBelongsToMany['TestModel7']['limit'] = 5;
+
+		$binding = array('type'=>'hasAndBelongsToMany', 'model'=>'TestModel7');
+		$queryData = array();
+		$resultSet = null;
+		$null = null;
+
+		$params = &$this->_prepareAssociationQuery($this->Model, $queryData, $binding);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
+		$this->assertPattern('/^SELECT\s+`TestModel7`\.`id`, `TestModel7`\.`name`, `TestModel7`\.`created`, `TestModel7`\.`updated`, `TestModel4TestModel7`\.`test_model4_id`, `TestModel4TestModel7`\.`test_model7_id`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model7`\s+AS\s+`TestModel7`\s+JOIN\s+`test_model4_test_model7`\s+AS\s+`TestModel4TestModel7`/', $result);
+		$this->assertPattern('/\s+ON\s+\(`TestModel4TestModel7`\.`test_model4_id`\s+=\s+{\$__cakeID__\$}\s+/', $result);
+		$this->assertPattern('/\s+AND\s+`TestModel4TestModel7`\.`test_model7_id`\s+=\s+`TestModel7`\.`id`\)\s+WHERE\s+/', $result);
+		$this->assertPattern('/\s+(?:\()?1\s+=\s+1(?:\))?\s*\s+LIMIT 2,\s*5\s*$/', $result);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $null, null, null, null, $queryData, false, $null);
+		$this->assertPattern('/^SELECT\s+`TestModel4`\.`id`, `TestModel4`\.`name`, `TestModel4`\.`created`, `TestModel4`\.`updated`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model4` AS `TestModel4`\s+WHERE\s+(?:\()?1\s+=\s+1(?:\))?\s*$/', $result);
+
+		$this->Model->hasAndBelongsToMany['TestModel7'] = $__backup;
+	}
+
+/**
+ * testGenerateAssociationQueryHasAndBelongsToManyWithPageAndLimit method
+ *
+ * @access public
+ * @return void
+ */
+	function testGenerateAssociationQueryHasAndBelongsToManyWithPageAndLimit() {
+		$this->Model =& new TestModel4();
+		$this->Model->schema();
+		$this->_buildRelatedModels($this->Model);
+
+		$__backup = $this->Model->hasAndBelongsToMany['TestModel7'];
+
+		$this->Model->hasAndBelongsToMany['TestModel7']['page'] = 2;
+		$this->Model->hasAndBelongsToMany['TestModel7']['limit'] = 5;
+
+		$binding = array('type'=>'hasAndBelongsToMany', 'model'=>'TestModel7');
+		$queryData = array();
+		$resultSet = null;
+		$null = null;
+
+		$params = &$this->_prepareAssociationQuery($this->Model, $queryData, $binding);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $params['linkModel'], $params['type'], $params['assoc'], $params['assocData'], $queryData, $params['external'], $resultSet);
+		$this->assertPattern('/^SELECT\s+`TestModel7`\.`id`, `TestModel7`\.`name`, `TestModel7`\.`created`, `TestModel7`\.`updated`, `TestModel4TestModel7`\.`test_model4_id`, `TestModel4TestModel7`\.`test_model7_id`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model7`\s+AS\s+`TestModel7`\s+JOIN\s+`test_model4_test_model7`\s+AS\s+`TestModel4TestModel7`/', $result);
+		$this->assertPattern('/\s+ON\s+\(`TestModel4TestModel7`\.`test_model4_id`\s+=\s+{\$__cakeID__\$}/', $result);
+		$this->assertPattern('/\s+AND\s+`TestModel4TestModel7`\.`test_model7_id`\s+=\s+`TestModel7`\.`id`\)\s+WHERE\s+/', $result);
+		$this->assertPattern('/\s+(?:\()?1\s+=\s+1(?:\))?\s*\s+LIMIT 5,\s*5\s*$/', $result);
+
+		$result = $this->testDb->generateAssociationQuery($this->Model, $null, null, null, null, $queryData, false, $null);
+		$this->assertPattern('/^SELECT\s+`TestModel4`\.`id`, `TestModel4`\.`name`, `TestModel4`\.`created`, `TestModel4`\.`updated`\s+/', $result);
+		$this->assertPattern('/\s+FROM\s+`test_model4` AS `TestModel4`\s+WHERE\s+(?:\()?1\s+=\s+1(?:\))?\s*$/', $result);
+
+		$this->Model->hasAndBelongsToMany['TestModel7'] = $__backup;
+	}
+
+/**
+ * buildRelatedModels method
+ *
+ * @param mixed $model
+ * @access protected
+ * @return void
+ */
+	function _buildRelatedModels(&$model) {
+		foreach ($model->__associations as $type) {
+			foreach ($model->{$type} as $assoc => $assocData) {
+				if (is_string($assocData)) {
+					$className = $assocData;
+				} elseif (isset($assocData['className'])) {
+					$className = $assocData['className'];
+				}
+				$model->$className =& new $className();
+				$model->$className->schema();
+			}
+		}
+	}
+
+/**
+ * &_prepareAssociationQuery method
+ *
+ * @param mixed $model
+ * @param mixed $queryData
+ * @param mixed $binding
+ * @access public
+ * @return void
+ */
+	function &_prepareAssociationQuery(&$model, &$queryData, $binding) {
+		$type = $binding['type'];
+		$assoc = $binding['model'];
+		$assocData = $model->{$type}[$assoc];
+		$className = $assocData['className'];
+
+		$linkModel =& $model->{$className};
+		$external = isset($assocData['external']);
+		$queryData = $this->testDb->__scrubQueryData($queryData);
+
+		$result = array_merge(array('linkModel' => &$linkModel), compact('type', 'assoc', 'assocData', 'external'));
+		return $result;
+	}
+
+/**
+ * testSelectDistict method
+ *
+ * @access public
+ * @return void
+ */
+	function testSelectDistict() {
+		$result = $this->testDb->fields($this->Model, 'Vendor', "DISTINCT Vendor.id, Vendor.name");
+		$expected = array('DISTINCT `Vendor`.`id`', '`Vendor`.`name`');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that booleans and null make logical condition strings.
+ *
+ * @return void
+ */
+	function testBooleanNullConditionsParsing() {
+		$result = $this->testDb->conditions(true);
+		$this->assertEqual($result, ' WHERE 1 = 1', 'true conditions failed %s');
+
+		$result = $this->testDb->conditions(false);
+		$this->assertEqual($result, ' WHERE 0 = 1', 'false conditions failed %s');
+
+		$result = $this->testDb->conditions(null);
+		$this->assertEqual($result, ' WHERE 1 = 1', 'null conditions failed %s');
+
+		$result = $this->testDb->conditions(array());
+		$this->assertEqual($result, ' WHERE 1 = 1', 'array() conditions failed %s');
+
+		$result = $this->testDb->conditions('');
+		$this->assertEqual($result, ' WHERE 1 = 1', '"" conditions failed %s');
+
+		$result = $this->testDb->conditions(' ', '"  " conditions failed %s');
+		$this->assertEqual($result, ' WHERE 1 = 1');
+	}
+/**
+ * testStringConditionsParsing method
+ *
+ * @access public
+ * @return void
+ */
+	function testStringConditionsParsing() {
+		$result = $this->testDb->conditions("ProjectBid.project_id = Project.id");
+		$expected = " WHERE `ProjectBid`.`project_id` = `Project`.`id`";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions("Candy.name LIKE 'a' AND HardCandy.name LIKE 'c'");
+		$expected = " WHERE `Candy`.`name` LIKE 'a' AND `HardCandy`.`name` LIKE 'c'";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions("HardCandy.name LIKE 'a' AND Candy.name LIKE 'c'");
+		$expected = " WHERE `HardCandy`.`name` LIKE 'a' AND `Candy`.`name` LIKE 'c'";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions("Post.title = '1.1'");
+		$expected = " WHERE `Post`.`title` = '1.1'";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions("User.id != 0 AND User.user LIKE '%arr%'");
+		$expected = " WHERE `User`.`id` != 0 AND `User`.`user` LIKE '%arr%'";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions("SUM(Post.comments_count) > 500");
+		$expected = " WHERE SUM(`Post`.`comments_count`) > 500";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions("(Post.created < '" . date('Y-m-d H:i:s') . "') GROUP BY YEAR(Post.created), MONTH(Post.created)");
+		$expected = " WHERE (`Post`.`created` < '" . date('Y-m-d H:i:s') . "') GROUP BY YEAR(`Post`.`created`), MONTH(`Post`.`created`)";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions("score BETWEEN 90.1 AND 95.7");
+		$expected = " WHERE score BETWEEN 90.1 AND 95.7";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('score' => array(2=>1, 2, 10)));
+		$expected = " WHERE score IN (1, 2, 10)";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions("Aro.rght = Aro.lft + 1.1");
+		$expected = " WHERE `Aro`.`rght` = `Aro`.`lft` + 1.1";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions("(Post.created < '" . date('Y-m-d H:i:s') . "') GROUP BY YEAR(Post.created), MONTH(Post.created)");
+		$expected = " WHERE (`Post`.`created` < '" . date('Y-m-d H:i:s') . "') GROUP BY YEAR(`Post`.`created`), MONTH(`Post`.`created`)";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions('Sportstaette.sportstaette LIKE "%ru%" AND Sportstaette.sportstaettenart_id = 2');
+		$expected = ' WHERE `Sportstaette`.`sportstaette` LIKE "%ru%" AND `Sportstaette`.`sportstaettenart_id` = 2';
+		$this->assertPattern('/\s*WHERE\s+`Sportstaette`\.`sportstaette`\s+LIKE\s+"%ru%"\s+AND\s+`Sports/', $result);
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions('Sportstaette.sportstaettenart_id = 2 AND Sportstaette.sportstaette LIKE "%ru%"');
+		$expected = ' WHERE `Sportstaette`.`sportstaettenart_id` = 2 AND `Sportstaette`.`sportstaette` LIKE "%ru%"';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions('SUM(Post.comments_count) > 500 AND NOT Post.title IS NULL AND NOT Post.extended_title IS NULL');
+		$expected = ' WHERE SUM(`Post`.`comments_count`) > 500 AND NOT `Post`.`title` IS NULL AND NOT `Post`.`extended_title` IS NULL';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions('NOT Post.title IS NULL AND NOT Post.extended_title IS NULL AND SUM(Post.comments_count) > 500');
+		$expected = ' WHERE NOT `Post`.`title` IS NULL AND NOT `Post`.`extended_title` IS NULL AND SUM(`Post`.`comments_count`) > 500';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions('NOT Post.extended_title IS NULL AND NOT Post.title IS NULL AND Post.title != "" AND SPOON(SUM(Post.comments_count) + 1.1) > 500');
+		$expected = ' WHERE NOT `Post`.`extended_title` IS NULL AND NOT `Post`.`title` IS NULL AND `Post`.`title` != "" AND SPOON(SUM(`Post`.`comments_count`) + 1.1) > 500';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions('NOT Post.title_extended IS NULL AND NOT Post.title IS NULL AND Post.title_extended != Post.title');
+		$expected = ' WHERE NOT `Post`.`title_extended` IS NULL AND NOT `Post`.`title` IS NULL AND `Post`.`title_extended` != `Post`.`title`';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions("Comment.id = 'a'");
+		$expected = " WHERE `Comment`.`id` = 'a'";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions("lower(Article.title) LIKE 'a%'");
+		$expected = " WHERE lower(`Article`.`title`) LIKE 'a%'";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions('((MATCH(Video.title) AGAINST(\'My Search*\' IN BOOLEAN MODE) * 2) + (MATCH(Video.description) AGAINST(\'My Search*\' IN BOOLEAN MODE) * 0.4) + (MATCH(Video.tags) AGAINST(\'My Search*\' IN BOOLEAN MODE) * 1.5))');
+		$expected = ' WHERE ((MATCH(`Video`.`title`) AGAINST(\'My Search*\' IN BOOLEAN MODE) * 2) + (MATCH(`Video`.`description`) AGAINST(\'My Search*\' IN BOOLEAN MODE) * 0.4) + (MATCH(`Video`.`tags`) AGAINST(\'My Search*\' IN BOOLEAN MODE) * 1.5))';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions('DATEDIFF(NOW(),Article.published) < 1 && Article.live=1');
+		$expected = " WHERE DATEDIFF(NOW(),`Article`.`published`) < 1 && `Article`.`live`=1";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions('file = "index.html"');
+		$expected = ' WHERE file = "index.html"';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions("file = 'index.html'");
+		$expected = " WHERE file = 'index.html'";
+		$this->assertEqual($result, $expected);
+
+		$letter = $letter = 'd.a';
+		$conditions = array('Company.name like ' => $letter . '%');
+		$result = $this->testDb->conditions($conditions);
+		$expected = " WHERE `Company`.`name` like 'd.a%'";
+		$this->assertEqual($result, $expected);
+
+		$conditions = array('Artist.name' => 'JUDY and MARY');
+		$result = $this->testDb->conditions($conditions);
+		$expected = " WHERE `Artist`.`name` = 'JUDY and MARY'";
+		$this->assertEqual($result, $expected);
+
+		$conditions = array('Artist.name' => 'JUDY AND MARY');
+		$result = $this->testDb->conditions($conditions);
+		$expected = " WHERE `Artist`.`name` = 'JUDY AND MARY'";
+		$this->assertEqual($result, $expected);
+
+		$conditions = array('Company.name similar to ' => 'a word');
+		$result = $this->testDb->conditions($conditions);
+		$expected = " WHERE `Company`.`name` similar to 'a word'";
+		$this->assertEqual($result, $expected);
+
+	}
+
+/**
+ * testQuotesInStringConditions method
+ *
+ * @access public
+ * @return void
+ */
+	function testQuotesInStringConditions() {
+		$result = $this->testDb->conditions('Member.email = \'maria****@crica*****\'');
+		$expected = ' WHERE `Member`.`email` = \'maria****@crica*****\'';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions('Member.email = "maria****@crica*****"');
+		$expected = ' WHERE `Member`.`email` = "maria****@crica*****"';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions('Member.email = \'maria****@crica*****\' AND Member.user LIKE \'mariano.iglesias%\'');
+		$expected = ' WHERE `Member`.`email` = \'maria****@crica*****\' AND `Member`.`user` LIKE \'mariano.iglesias%\'';
+		$this->assertEqual($result, $expected);
+
+
+		$result = $this->testDb->conditions('Member.email = "maria****@crica*****" AND Member.user LIKE "mariano.iglesias%"');
+		$expected = ' WHERE `Member`.`email` = "maria****@crica*****" AND `Member`.`user` LIKE "mariano.iglesias%"';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testParenthesisInStringConditions method
+ *
+ * @access public
+ * @return void
+ */
+	function testParenthesisInStringConditions() {
+		$result = $this->testDb->conditions('Member.name = \'(lu\'');
+		$this->assertPattern('/^\s+WHERE\s+`Member`.`name`\s+=\s+\'\(lu\'$/', $result);
+
+		$result = $this->testDb->conditions('Member.name = \')lu\'');
+		$this->assertPattern('/^\s+WHERE\s+`Member`.`name`\s+=\s+\'\)lu\'$/', $result);
+
+		$result = $this->testDb->conditions('Member.name = \'va(lu\'');
+		$this->assertPattern('/^\s+WHERE\s+`Member`.`name`\s+=\s+\'va\(lu\'$/', $result);
+
+		$result = $this->testDb->conditions('Member.name = \'va)lu\'');
+		$this->assertPattern('/^\s+WHERE\s+`Member`.`name`\s+=\s+\'va\)lu\'$/', $result);
+
+		$result = $this->testDb->conditions('Member.name = \'va(lu)\'');
+		$this->assertPattern('/^\s+WHERE\s+`Member`.`name`\s+=\s+\'va\(lu\)\'$/', $result);
+
+		$result = $this->testDb->conditions('Member.name = \'va(lu)e\'');
+		$this->assertPattern('/^\s+WHERE\s+`Member`.`name`\s+=\s+\'va\(lu\)e\'$/', $result);
+
+		$result = $this->testDb->conditions('Member.name = \'(mariano)\'');
+		$this->assertPattern('/^\s+WHERE\s+`Member`.`name`\s+=\s+\'\(mariano\)\'$/', $result);
+
+		$result = $this->testDb->conditions('Member.name = \'(mariano)iglesias\'');
+		$this->assertPattern('/^\s+WHERE\s+`Member`.`name`\s+=\s+\'\(mariano\)iglesias\'$/', $result);
+
+		$result = $this->testDb->conditions('Member.name = \'(mariano) iglesias\'');
+		$this->assertPattern('/^\s+WHERE\s+`Member`.`name`\s+=\s+\'\(mariano\) iglesias\'$/', $result);
+
+		$result = $this->testDb->conditions('Member.name = \'(mariano word) iglesias\'');
+		$this->assertPattern('/^\s+WHERE\s+`Member`.`name`\s+=\s+\'\(mariano word\) iglesias\'$/', $result);
+
+		$result = $this->testDb->conditions('Member.name = \'(mariano.iglesias)\'');
+		$this->assertPattern('/^\s+WHERE\s+`Member`.`name`\s+=\s+\'\(mariano.iglesias\)\'$/', $result);
+
+		$result = $this->testDb->conditions('Member.name = \'Mariano Iglesias (mariano.iglesias)\'');
+		$this->assertPattern('/^\s+WHERE\s+`Member`.`name`\s+=\s+\'Mariano Iglesias \(mariano.iglesias\)\'$/', $result);
+
+		$result = $this->testDb->conditions('Member.name = \'Mariano Iglesias (mariano.iglesias) CakePHP\'');
+		$this->assertPattern('/^\s+WHERE\s+`Member`.`name`\s+=\s+\'Mariano Iglesias \(mariano.iglesias\) CakePHP\'$/', $result);
+
+		$result = $this->testDb->conditions('Member.name = \'(mariano.iglesias) CakePHP\'');
+		$this->assertPattern('/^\s+WHERE\s+`Member`.`name`\s+=\s+\'\(mariano.iglesias\) CakePHP\'$/', $result);
+	}
+
+/**
+ * testParenthesisInArrayConditions method
+ *
+ * @access public
+ * @return void
+ */
+	function testParenthesisInArrayConditions() {
+		$result = $this->testDb->conditions(array('Member.name' => '(lu'));
+		$this->assertPattern('/^\s+WHERE\s+`Member`.`name`\s+=\s+\'\(lu\'$/', $result);
+
+		$result = $this->testDb->conditions(array('Member.name' => ')lu'));
+		$this->assertPattern('/^\s+WHERE\s+`Member`.`name`\s+=\s+\'\)lu\'$/', $result);
+
+		$result = $this->testDb->conditions(array('Member.name' => 'va(lu'));
+		$this->assertPattern('/^\s+WHERE\s+`Member`.`name`\s+=\s+\'va\(lu\'$/', $result);
+
+		$result = $this->testDb->conditions(array('Member.name' => 'va)lu'));
+		$this->assertPattern('/^\s+WHERE\s+`Member`.`name`\s+=\s+\'va\)lu\'$/', $result);
+
+		$result = $this->testDb->conditions(array('Member.name' => 'va(lu)'));
+		$this->assertPattern('/^\s+WHERE\s+`Member`.`name`\s+=\s+\'va\(lu\)\'$/', $result);
+
+		$result = $this->testDb->conditions(array('Member.name' => 'va(lu)e'));
+		$this->assertPattern('/^\s+WHERE\s+`Member`.`name`\s+=\s+\'va\(lu\)e\'$/', $result);
+
+		$result = $this->testDb->conditions(array('Member.name' => '(mariano)'));
+		$this->assertPattern('/^\s+WHERE\s+`Member`.`name`\s+=\s+\'\(mariano\)\'$/', $result);
+
+		$result = $this->testDb->conditions(array('Member.name' => '(mariano)iglesias'));
+		$this->assertPattern('/^\s+WHERE\s+`Member`.`name`\s+=\s+\'\(mariano\)iglesias\'$/', $result);
+
+		$result = $this->testDb->conditions(array('Member.name' => '(mariano) iglesias'));
+		$this->assertPattern('/^\s+WHERE\s+`Member`.`name`\s+=\s+\'\(mariano\) iglesias\'$/', $result);
+
+		$result = $this->testDb->conditions(array('Member.name' => '(mariano word) iglesias'));
+		$this->assertPattern('/^\s+WHERE\s+`Member`.`name`\s+=\s+\'\(mariano word\) iglesias\'$/', $result);
+
+		$result = $this->testDb->conditions(array('Member.name' => '(mariano.iglesias)'));
+		$this->assertPattern('/^\s+WHERE\s+`Member`.`name`\s+=\s+\'\(mariano.iglesias\)\'$/', $result);
+
+		$result = $this->testDb->conditions(array('Member.name' => 'Mariano Iglesias (mariano.iglesias)'));
+		$this->assertPattern('/^\s+WHERE\s+`Member`.`name`\s+=\s+\'Mariano Iglesias \(mariano.iglesias\)\'$/', $result);
+
+		$result = $this->testDb->conditions(array('Member.name' => 'Mariano Iglesias (mariano.iglesias) CakePHP'));
+		$this->assertPattern('/^\s+WHERE\s+`Member`.`name`\s+=\s+\'Mariano Iglesias \(mariano.iglesias\) CakePHP\'$/', $result);
+
+		$result = $this->testDb->conditions(array('Member.name' => '(mariano.iglesias) CakePHP'));
+		$this->assertPattern('/^\s+WHERE\s+`Member`.`name`\s+=\s+\'\(mariano.iglesias\) CakePHP\'$/', $result);
+	}
+
+/**
+ * testArrayConditionsParsing method
+ *
+ * @access public
+ * @return void
+ */
+	function testArrayConditionsParsing() {
+		$result = $this->testDb->conditions(array('Stereo.type' => 'in dash speakers'));
+		$this->assertPattern("/^\s+WHERE\s+`Stereo`.`type`\s+=\s+'in dash speakers'/", $result);
+
+		$result = $this->testDb->conditions(array('Candy.name LIKE' => 'a', 'HardCandy.name LIKE' => 'c'));
+		$this->assertPattern("/^\s+WHERE\s+`Candy`.`name` LIKE\s+'a'\s+AND\s+`HardCandy`.`name`\s+LIKE\s+'c'/", $result);
+
+		$result = $this->testDb->conditions(array('HardCandy.name LIKE' => 'a', 'Candy.name LIKE' => 'c'));
+		$expected = " WHERE `HardCandy`.`name` LIKE 'a' AND `Candy`.`name` LIKE 'c'";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('HardCandy.name LIKE' => 'a%', 'Candy.name LIKE' => '%c%'));
+		$expected = " WHERE `HardCandy`.`name` LIKE 'a%' AND `Candy`.`name` LIKE '%c%'";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('HardCandy.name LIKE' => 'to be or%', 'Candy.name LIKE' => '%not to be%'));
+		$expected = " WHERE `HardCandy`.`name` LIKE 'to be or%' AND `Candy`.`name` LIKE '%not to be%'";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('score BETWEEN ? AND ?' => array(90.1, 95.7)));
+		$expected = " WHERE `score` BETWEEN 90.1 AND 95.7";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('Post.title' => 1.1));
+		$expected = " WHERE `Post`.`title` = 1.1";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('Post.title' => 1.1), true, true, new Post());
+		$expected = " WHERE `Post`.`title` = '1.1'";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('SUM(Post.comments_count) >' => '500'));
+		$expected = " WHERE SUM(`Post`.`comments_count`) > '500'";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('MAX(Post.rating) >' => '50'));
+		$expected = " WHERE MAX(`Post`.`rating`) > '50'";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('lower(Article.title)' =>  'secrets'));
+		$expected = " WHERE lower(`Article`.`title`) = 'secrets'";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('title LIKE' => '%hello'));
+		$expected = " WHERE `title` LIKE '%hello'";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('Post.name' => 'mad(g)ik'));
+		$expected = " WHERE `Post`.`name` = 'mad(g)ik'";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('score' => array(1, 2, 10)));
+		$expected = " WHERE score IN (1, 2, 10)";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('score' => array()));
+		$expected = " WHERE `score` IS NULL";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('score !=' => array()));
+		$expected = " WHERE `score` IS NOT NULL";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('score !=' => '20'));
+		$expected = " WHERE `score` != '20'";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('score >' => '20'));
+		$expected = " WHERE `score` > '20'";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('client_id >' => '20'), true, true, new TestModel());
+		$expected = " WHERE `client_id` > 20";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('OR' => array(
+			array('User.user' => 'mariano'),
+			array('User.user' => 'nate')
+		)));
+
+		$expected = " WHERE ((`User`.`user` = 'mariano') OR (`User`.`user` = 'nate'))";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('or' => array(
+			'score BETWEEN ? AND ?' => array('4', '5'), 'rating >' => '20'
+		)));
+		$expected = " WHERE ((`score` BETWEEN '4' AND '5') OR (`rating` > '20'))";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('or' => array(
+			'score BETWEEN ? AND ?' => array('4', '5'), array('score >' => '20')
+		)));
+		$expected = " WHERE ((`score` BETWEEN '4' AND '5') OR (`score` > '20'))";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('and' => array(
+			'score BETWEEN ? AND ?' => array('4', '5'), array('score >' => '20')
+		)));
+		$expected = " WHERE ((`score` BETWEEN '4' AND '5') AND (`score` > '20'))";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array(
+			'published' => 1, 'or' => array('score >' => '2', array('score >' => '20'))
+		));
+		$expected = " WHERE `published` = 1 AND ((`score` > '2') OR (`score` > '20'))";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array(array('Project.removed' => false)));
+		$expected = " WHERE `Project`.`removed` = 0";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array(array('Project.removed' => true)));
+		$expected = " WHERE `Project`.`removed` = 1";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array(array('Project.removed' => null)));
+		$expected = " WHERE `Project`.`removed` IS NULL";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array(array('Project.removed !=' => null)));
+		$expected = " WHERE `Project`.`removed` IS NOT NULL";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('(Usergroup.permissions) & 4' => 4));
+		$expected = " WHERE (`Usergroup`.`permissions`) & 4 = 4";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('((Usergroup.permissions) & 4)' => 4));
+		$expected = " WHERE ((`Usergroup`.`permissions`) & 4) = 4";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('Post.modified >=' => 'DATE_SUB(NOW(), INTERVAL 7 DAY)'));
+		$expected = " WHERE `Post`.`modified` >= 'DATE_SUB(NOW(), INTERVAL 7 DAY)'";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('Post.modified >= DATE_SUB(NOW(), INTERVAL 7 DAY)'));
+		$expected = " WHERE `Post`.`modified` >= DATE_SUB(NOW(), INTERVAL 7 DAY)";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array(
+			'NOT' => array('Course.id' => null, 'Course.vet' => 'N', 'level_of_education_id' => array(912,999)),
+			'Enrollment.yearcompleted >' => '0')
+		);
+		$this->assertPattern('/^\s*WHERE\s+\(NOT\s+\(`Course`\.`id` IS NULL\)\s+AND NOT\s+\(`Course`\.`vet`\s+=\s+\'N\'\)\s+AND NOT\s+\(level_of_education_id IN \(912, 999\)\)\)\s+AND\s+`Enrollment`\.`yearcompleted`\s+>\s+\'0\'\s*$/', $result);
+
+		$result = $this->testDb->conditions(array('id <>' => '8'));
+		$this->assertPattern('/^\s*WHERE\s+`id`\s+<>\s+\'8\'\s*$/', $result);
+
+		$result = $this->testDb->conditions(array('TestModel.field =' => 'gribe$@()lu'));
+		$expected = " WHERE `TestModel`.`field` = 'gribe$@()lu'";
+		$this->assertEqual($result, $expected);
+
+		$conditions['NOT'] = array('Listing.expiration BETWEEN ? AND ?' => array("1", "100"));
+		$conditions[0]['OR'] = array(
+			"Listing.title LIKE" => "%term%",
+			"Listing.description LIKE" => "%term%"
+		);
+		$conditions[1]['OR'] = array(
+			"Listing.title LIKE" => "%term_2%",
+			"Listing.description LIKE" => "%term_2%"
+		);
+		$result = $this->testDb->conditions($conditions);
+		$expected = " WHERE NOT (`Listing`.`expiration` BETWEEN '1' AND '100') AND" .
+		" ((`Listing`.`title` LIKE '%term%') OR (`Listing`.`description` LIKE '%term%')) AND" .
+		" ((`Listing`.`title` LIKE '%term_2%') OR (`Listing`.`description` LIKE '%term_2%'))";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('MD5(CONCAT(Reg.email,Reg.id))' => 'blah'));
+		$expected = " WHERE MD5(CONCAT(`Reg`.`email`,`Reg`.`id`)) = 'blah'";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array(
+			'MD5(CONCAT(Reg.email,Reg.id))' => array('blah', 'blahblah')
+		));
+		$expected = " WHERE MD5(CONCAT(`Reg`.`email`,`Reg`.`id`)) IN ('blah', 'blahblah')";
+		$this->assertEqual($result, $expected);
+
+		$conditions = array('id' => array(2, 5, 6, 9, 12, 45, 78, 43, 76));
+		$result = $this->testDb->conditions($conditions);
+		$expected = " WHERE id IN (2, 5, 6, 9, 12, 45, 78, 43, 76)";
+		$this->assertEqual($result, $expected);
+
+		$conditions = array('title' => 'user(s)');
+		$result = $this->testDb->conditions($conditions);
+		$expected = " WHERE `title` = 'user(s)'";
+		$this->assertEqual($result, $expected);
+
+		$conditions = array('title' => 'user(s) data');
+		$result = $this->testDb->conditions($conditions);
+		$expected = " WHERE `title` = 'user(s) data'";
+		$this->assertEqual($result, $expected);
+
+		$conditions = array('title' => 'user(s,arg) data');
+		$result = $this->testDb->conditions($conditions);
+		$expected = " WHERE `title` = 'user(s,arg) data'";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array("Book.book_name" => 'Java(TM)'));
+		$expected = " WHERE `Book`.`book_name` = 'Java(TM)'";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array("Book.book_name" => 'Java(TM) '));
+		$expected = " WHERE `Book`.`book_name` = 'Java(TM) '";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array("Book.id" => 0));
+		$expected = " WHERE `Book`.`id` = 0";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array("Book.id" => NULL));
+		$expected = " WHERE `Book`.`id` IS NULL";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('Listing.beds >=' => 0));
+		$expected = " WHERE `Listing`.`beds` >= 0";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array(
+			'ASCII(SUBSTRING(keyword, 1, 1)) BETWEEN ? AND ?' => array(65, 90)
+		));
+		$expected = ' WHERE ASCII(SUBSTRING(keyword, 1, 1)) BETWEEN 65 AND 90';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('or' => array(
+			'? BETWEEN Model.field1 AND Model.field2' => '2009-03-04'
+		)));
+		$expected = " WHERE '2009-03-04' BETWEEN Model.field1 AND Model.field2";
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testArrayConditionsParsingComplexKeys method
+ *
+ * @access public
+ * @return void
+ */
+	function testArrayConditionsParsingComplexKeys() {
+		$result = $this->testDb->conditions(array(
+			'CAST(Book.created AS DATE)' => '2008-08-02'
+		));
+		$expected = " WHERE CAST(`Book`.`created` AS DATE) = '2008-08-02'";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array(
+			'CAST(Book.created AS DATE) <=' => '2008-08-02'
+		));
+		$expected = " WHERE CAST(`Book`.`created` AS DATE) <= '2008-08-02'";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array(
+			'(Stats.clicks * 100) / Stats.views >' => 50
+		));
+		$expected = " WHERE (`Stats`.`clicks` * 100) / `Stats`.`views` > 50";
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testMixedConditionsParsing method
+ *
+ * @access public
+ * @return void
+ */
+	function testMixedConditionsParsing() {
+		$conditions[] = 'User.first_name = \'Firstname\'';
+		$conditions[] = array('User.last_name' => 'Lastname');
+		$result = $this->testDb->conditions($conditions);
+		$expected = " WHERE `User`.`first_name` = 'Firstname' AND `User`.`last_name` = 'Lastname'";
+		$this->assertEqual($result, $expected);
+
+		$conditions = array(
+			'Thread.project_id' => 5,
+			'Thread.buyer_id' => 14,
+			'1=1 GROUP BY Thread.project_id'
+		);
+		$result = $this->testDb->conditions($conditions);
+		$this->assertPattern('/^\s*WHERE\s+`Thread`.`project_id`\s*=\s*5\s+AND\s+`Thread`.`buyer_id`\s*=\s*14\s+AND\s+1\s*=\s*1\s+GROUP BY `Thread`.`project_id`$/', $result);
+	}
+
+/**
+ * testConditionsOptionalArguments method
+ *
+ * @access public
+ * @return void
+ */
+	function testConditionsOptionalArguments() {
+		$result = $this->testDb->conditions( array('Member.name' => 'Mariano'), true, false);
+		$this->assertPattern('/^\s*`Member`.`name`\s*=\s*\'Mariano\'\s*$/', $result);
+
+		$result = $this->testDb->conditions( array(), true, false);
+		$this->assertPattern('/^\s*1\s*=\s*1\s*$/', $result);
+	}
+
+/**
+ * testConditionsWithModel
+ *
+ * @access public
+ * @return void
+ */
+	function testConditionsWithModel() {
+		$this->Model =& new Article2();
+
+		$result = $this->testDb->conditions(array('Article2.viewed >=' => 0), true, true, $this->Model);
+		$expected = " WHERE `Article2`.`viewed` >= 0";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('Article2.viewed >=' => '0'), true, true, $this->Model);
+		$expected = " WHERE `Article2`.`viewed` >= 0";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('Article2.viewed >=' => '1'), true, true, $this->Model);
+		$expected = " WHERE `Article2`.`viewed` >= 1";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('Article2.rate_sum BETWEEN ? AND ?' => array(0, 10)), true, true, $this->Model);
+		$expected = " WHERE `Article2`.`rate_sum` BETWEEN 0 AND 10";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('Article2.rate_sum BETWEEN ? AND ?' => array('0', '10')), true, true, $this->Model);
+		$expected = " WHERE `Article2`.`rate_sum` BETWEEN 0 AND 10";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->conditions(array('Article2.rate_sum BETWEEN ? AND ?' => array('1', '10')), true, true, $this->Model);
+		$expected = " WHERE `Article2`.`rate_sum` BETWEEN 1 AND 10";
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testFieldParsing method
+ *
+ * @access public
+ * @return void
+ */
+	function testFieldParsing() {
+		$result = $this->testDb->fields($this->Model, 'Vendor', "Vendor.id, COUNT(Model.vendor_id) AS `Vendor`.`count`");
+		$expected = array('`Vendor`.`id`', 'COUNT(`Model`.`vendor_id`) AS `Vendor`.`count`');
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->fields($this->Model, 'Vendor', "`Vendor`.`id`, COUNT(`Model`.`vendor_id`) AS `Vendor`.`count`");
+		$expected = array('`Vendor`.`id`', 'COUNT(`Model`.`vendor_id`) AS `Vendor`.`count`');
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->fields($this->Model, 'Post', "CONCAT(REPEAT(' ', COUNT(Parent.name) - 1), Node.name) AS name, Node.created");
+		$expected = array("CONCAT(REPEAT(' ', COUNT(`Parent`.`name`) - 1), Node.name) AS name", "`Node`.`created`");
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->fields($this->Model, null, 'round( (3.55441 * fooField), 3 ) AS test');
+		$this->assertEqual($result, array('round( (3.55441 * fooField), 3 ) AS test'));
+
+		$result = $this->testDb->fields($this->Model, null, 'ROUND(`Rating`.`rate_total` / `Rating`.`rate_count`,2) AS rating');
+		$this->assertEqual($result, array('ROUND(`Rating`.`rate_total` / `Rating`.`rate_count`,2) AS rating'));
+
+		$result = $this->testDb->fields($this->Model, null, 'ROUND(Rating.rate_total / Rating.rate_count,2) AS rating');
+		$this->assertEqual($result, array('ROUND(Rating.rate_total / Rating.rate_count,2) AS rating'));
+
+		$result = $this->testDb->fields($this->Model, 'Post', "Node.created, CONCAT(REPEAT(' ', COUNT(Parent.name) - 1), Node.name) AS name");
+		$expected = array("`Node`.`created`", "CONCAT(REPEAT(' ', COUNT(`Parent`.`name`) - 1), Node.name) AS name");
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->fields($this->Model, 'Post', "2.2,COUNT(*), SUM(Something.else) as sum, Node.created, CONCAT(REPEAT(' ', COUNT(Parent.name) - 1), Node.name) AS name,Post.title,Post.1,1.1");
+		$expected = array(
+			'2.2', 'COUNT(*)', 'SUM(`Something`.`else`) as sum', '`Node`.`created`',
+			"CONCAT(REPEAT(' ', COUNT(`Parent`.`name`) - 1), Node.name) AS name", '`Post`.`title`', '`Post`.`1`', '1.1'
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->fields($this->Model, null, "(`Provider`.`star_total` / `Provider`.`total_ratings`) as `rating`");
+		$expected = array("(`Provider`.`star_total` / `Provider`.`total_ratings`) as `rating`");
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->fields($this->Model, 'Post');
+		$expected = array(
+			'`Post`.`id`', '`Post`.`client_id`', '`Post`.`name`', '`Post`.`login`',
+			'`Post`.`passwd`', '`Post`.`addr_1`', '`Post`.`addr_2`', '`Post`.`zip_code`',
+			'`Post`.`city`', '`Post`.`country`', '`Post`.`phone`', '`Post`.`fax`',
+			'`Post`.`url`', '`Post`.`email`', '`Post`.`comments`', '`Post`.`last_login`',
+			'`Post`.`created`', '`Post`.`updated`'
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->fields($this->Model, 'Other');
+		$expected = array(
+			'`Other`.`id`', '`Other`.`client_id`', '`Other`.`name`', '`Other`.`login`',
+			'`Other`.`passwd`', '`Other`.`addr_1`', '`Other`.`addr_2`', '`Other`.`zip_code`',
+			'`Other`.`city`', '`Other`.`country`', '`Other`.`phone`', '`Other`.`fax`',
+			'`Other`.`url`', '`Other`.`email`', '`Other`.`comments`', '`Other`.`last_login`',
+			'`Other`.`created`', '`Other`.`updated`'
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->fields($this->Model, null, array(), false);
+		$expected = array('id', 'client_id', 'name', 'login', 'passwd', 'addr_1', 'addr_2', 'zip_code', 'city', 'country', 'phone', 'fax', 'url', 'email', 'comments', 'last_login', 'created', 'updated');
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->fields($this->Model, null, 'COUNT(*)');
+		$expected = array('COUNT(*)');
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->fields($this->Model, null, 'SUM(Thread.unread_buyer) AS ' . $this->testDb->name('sum_unread_buyer'));
+		$expected = array('SUM(`Thread`.`unread_buyer`) AS `sum_unread_buyer`');
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->fields($this->Model, null, 'name, count(*)');
+		$expected = array('`TestModel`.`name`', 'count(*)');
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->fields($this->Model, null, 'count(*), name');
+		$expected = array('count(*)', '`TestModel`.`name`');
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->fields(
+			$this->Model, null, 'field1, field2, field3, count(*), name'
+		);
+		$expected = array(
+			'`TestModel`.`field1`', '`TestModel`.`field2`',
+			'`TestModel`.`field3`', 'count(*)', '`TestModel`.`name`'
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->fields($this->Model, null, array('dayofyear(now())'));
+		$expected = array('dayofyear(now())');
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->fields($this->Model, null, array('MAX(Model.field) As Max'));
+		$expected = array('MAX(`Model`.`field`) As Max');
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->fields($this->Model, null, array('Model.field AS AnotherName'));
+		$expected = array('`Model`.`field` AS `AnotherName`');
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->fields($this->Model, null, array('field AS AnotherName'));
+		$expected = array('`field` AS `AnotherName`');
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->fields($this->Model, null, array(
+			'TestModel.field AS AnotherName'
+		));
+		$expected = array('`TestModel`.`field` AS `AnotherName`');
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->fields($this->Model, 'Foo', array(
+			'id', 'title', '(user_count + discussion_count + post_count) AS score'
+		));
+		$expected = array(
+			'`Foo`.`id`',
+			'`Foo`.`title`',
+			'(user_count + discussion_count + post_count) AS score'
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that fields() will accept objects made from DboSource::expression
+ *
+ * @return void
+ */
+	function testFieldsWithExpression() {
+		$expression = $this->testDb->expression("CASE Sample.id WHEN 1 THEN 'Id One' ELSE 'Other Id' END AS case_col");
+		$result = $this->testDb->fields($this->Model, null, array("id", $expression));
+		$expected = array(
+			'`TestModel`.`id`',
+			"CASE Sample.id WHEN 1 THEN 'Id One' ELSE 'Other Id' END AS case_col"
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that order() will accept objects made from DboSource::expression
+ *
+ * @return void
+ */
+	function testOrderWithExpression() {
+		$expression = $this->testDb->expression("CASE Sample.id WHEN 1 THEN 'Id One' ELSE 'Other Id' END AS case_col");
+		$result = $this->testDb->order($expression);
+		$expected = " ORDER BY CASE Sample.id WHEN 1 THEN 'Id One' ELSE 'Other Id' END AS case_col";
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testMergeAssociations method
+ *
+ * @access public
+ * @return void
+ */
+	function testMergeAssociations() {
+		$data = array('Article2' => array(
+				'id' => '1', 'user_id' => '1', 'title' => 'First Article',
+				'body' => 'First Article Body', 'published' => 'Y',
+				'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+		));
+		$merge = array('Topic' => array(array(
+			'id' => '1', 'topic' => 'Topic', 'created' => '2007-03-17 01:16:23',
+			'updated' => '2007-03-17 01:18:31'
+		)));
+		$expected = array(
+			'Article2' => array(
+				'id' => '1', 'user_id' => '1', 'title' => 'First Article',
+				'body' => 'First Article Body', 'published' => 'Y',
+				'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+			),
+			'Topic' => array(
+				'id' => '1', 'topic' => 'Topic', 'created' => '2007-03-17 01:16:23',
+				'updated' => '2007-03-17 01:18:31'
+			)
+		);
+		$this->testDb->__mergeAssociation($data, $merge, 'Topic', 'hasOne');
+		$this->assertEqual($data, $expected);
+
+		$data = array('Article2' => array(
+				'id' => '1', 'user_id' => '1', 'title' => 'First Article',
+				'body' => 'First Article Body', 'published' => 'Y',
+				'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+		));
+		$merge = array('User2' => array(array(
+			'id' => '1', 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+			'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+		)));
+
+		$expected = array(
+			'Article2' => array(
+				'id' => '1', 'user_id' => '1', 'title' => 'First Article',
+				'body' => 'First Article Body', 'published' => 'Y',
+				'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+			),
+			'User2' => array(
+				'id' => '1', 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+			)
+		);
+		$this->testDb->__mergeAssociation($data, $merge, 'User2', 'belongsTo');
+		$this->assertEqual($data, $expected);
+
+		$data = array(
+			'Article2' => array(
+				'id' => '1', 'user_id' => '1', 'title' => 'First Article', 'body' => 'First Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+			)
+		);
+		$merge = array(array('Comment' => false));
+		$expected = array(
+			'Article2' => array(
+				'id' => '1', 'user_id' => '1', 'title' => 'First Article', 'body' => 'First Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+			),
+			'Comment' => array()
+		);
+		$this->testDb->__mergeAssociation($data, $merge, 'Comment', 'hasMany');
+		$this->assertEqual($data, $expected);
+
+		$data = array(
+			'Article' => array(
+				'id' => '1', 'user_id' => '1', 'title' => 'First Article', 'body' => 'First Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+			)
+		);
+		$merge = array(
+			array(
+				'Comment' => array(
+					'id' => '1', 'comment' => 'Comment 1', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				)
+			),
+			array(
+				'Comment' => array(
+					'id' => '2', 'comment' => 'Comment 2', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				)
+			)
+		);
+		$expected = array(
+			'Article' => array(
+				'id' => '1', 'user_id' => '1', 'title' => 'First Article', 'body' => 'First Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+			),
+			'Comment' => array(
+				array(
+					'id' => '1', 'comment' => 'Comment 1', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				),
+				array(
+					'id' => '2', 'comment' => 'Comment 2', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				)
+			)
+		);
+		$this->testDb->__mergeAssociation($data, $merge, 'Comment', 'hasMany');
+		$this->assertEqual($data, $expected);
+
+		$data = array(
+			'Article' => array(
+				'id' => '1', 'user_id' => '1', 'title' => 'First Article', 'body' => 'First Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+			)
+		);
+		$merge = array(
+			array(
+				'Comment' => array(
+					'id' => '1', 'comment' => 'Comment 1', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				),
+				'User2' => array(
+					'id' => '1', 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				)
+			),
+			array(
+				'Comment' => array(
+					'id' => '2', 'comment' => 'Comment 2', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				),
+				'User2' => array(
+					'id' => '1', 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				)
+			)
+		);
+		$expected = array(
+			'Article' => array(
+				'id' => '1', 'user_id' => '1', 'title' => 'First Article', 'body' => 'First Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+			),
+			'Comment' => array(
+				array(
+					'id' => '1', 'comment' => 'Comment 1', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31',
+					'User2' => array(
+						'id' => '1', 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+					)
+				),
+				array(
+					'id' => '2', 'comment' => 'Comment 2', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31',
+					'User2' => array(
+						'id' => '1', 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+					)
+				)
+			)
+		);
+		$this->testDb->__mergeAssociation($data, $merge, 'Comment', 'hasMany');
+		$this->assertEqual($data, $expected);
+
+		$data = array(
+			'Article' => array(
+				'id' => '1', 'user_id' => '1', 'title' => 'First Article', 'body' => 'First Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+			)
+		);
+		$merge = array(
+			array(
+				'Comment' => array(
+					'id' => '1', 'comment' => 'Comment 1', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				),
+				'User2' => array(
+					'id' => '1', 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				),
+				'Tag' => array(
+					array('id' => 1, 'tag' => 'Tag 1'),
+					array('id' => 2, 'tag' => 'Tag 2')
+				)
+			),
+			array(
+				'Comment' => array(
+					'id' => '2', 'comment' => 'Comment 2', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				),
+				'User2' => array(
+					'id' => '1', 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				),
+				'Tag' => array()
+			)
+		);
+		$expected = array(
+			'Article' => array(
+				'id' => '1', 'user_id' => '1', 'title' => 'First Article', 'body' => 'First Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+			),
+			'Comment' => array(
+				array(
+					'id' => '1', 'comment' => 'Comment 1', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31',
+					'User2' => array(
+						'id' => '1', 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+					),
+					'Tag' => array(
+						array('id' => 1, 'tag' => 'Tag 1'),
+						array('id' => 2, 'tag' => 'Tag 2')
+					)
+				),
+				array(
+					'id' => '2', 'comment' => 'Comment 2', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31',
+					'User2' => array(
+						'id' => '1', 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+					),
+					'Tag' => array()
+				)
+			)
+		);
+		$this->testDb->__mergeAssociation($data, $merge, 'Comment', 'hasMany');
+		$this->assertEqual($data, $expected);
+
+		$data = array(
+			'Article' => array(
+				'id' => '1', 'user_id' => '1', 'title' => 'First Article', 'body' => 'First Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+			)
+		);
+		$merge = array(
+			array(
+				'Tag' => array(
+					'id' => '1', 'tag' => 'Tag 1', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				)
+			),
+			array(
+				'Tag' => array(
+					'id' => '2', 'tag' => 'Tag 2', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				)
+			),
+			array(
+				'Tag' => array(
+					'id' => '3', 'tag' => 'Tag 3', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				)
+			)
+		);
+		$expected = array(
+			'Article' => array(
+				'id' => '1', 'user_id' => '1', 'title' => 'First Article', 'body' => 'First Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+			),
+			'Tag' => array(
+				array(
+					'id' => '1', 'tag' => 'Tag 1', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				),
+				array(
+					'id' => '2', 'tag' => 'Tag 2', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				),
+				array(
+					'id' => '3', 'tag' => 'Tag 3', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				)
+			)
+		);
+		$this->testDb->__mergeAssociation($data, $merge, 'Tag', 'hasAndBelongsToMany');
+		$this->assertEqual($data, $expected);
+
+		$data = array(
+			'Article' => array(
+				'id' => '1', 'user_id' => '1', 'title' => 'First Article', 'body' => 'First Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+			)
+		);
+		$merge = array(
+			array(
+				'Tag' => array(
+					'id' => '1', 'tag' => 'Tag 1', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				)
+			),
+			array(
+				'Tag' => array(
+					'id' => '2', 'tag' => 'Tag 2', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				)
+			),
+			array(
+				'Tag' => array(
+					'id' => '3', 'tag' => 'Tag 3', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'
+				)
+			)
+		);
+		$expected = array(
+			'Article' => array(
+				'id' => '1', 'user_id' => '1', 'title' => 'First Article', 'body' => 'First Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+			),
+			'Tag' => array('id' => '1', 'tag' => 'Tag 1', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31')
+		);
+		$this->testDb->__mergeAssociation($data, $merge, 'Tag', 'hasOne');
+		$this->assertEqual($data, $expected);
+	}
+
+/**
+ * testRenderStatement method
+ *
+ * @access public
+ * @return void
+ */
+	function testRenderStatement() {
+		$result = $this->testDb->renderStatement('select', array(
+			'fields' => 'id', 'table' => 'table', 'conditions' => 'WHERE 1=1',
+			'alias' => '', 'joins' => '', 'order' => '', 'limit' => '', 'group' => ''
+		));
+		$this->assertPattern('/^\s*SELECT\s+id\s+FROM\s+table\s+WHERE\s+1=1\s*$/', $result);
+
+		$result = $this->testDb->renderStatement('update', array('fields' => 'value=2', 'table' => 'table', 'conditions' => 'WHERE 1=1', 'alias' => ''));
+		$this->assertPattern('/^\s*UPDATE\s+table\s+SET\s+value=2\s+WHERE\s+1=1\s*$/', $result);
+
+		$result = $this->testDb->renderStatement('update', array('fields' => 'value=2', 'table' => 'table', 'conditions' => 'WHERE 1=1', 'alias' => 'alias', 'joins' => ''));
+		$this->assertPattern('/^\s*UPDATE\s+table\s+AS\s+alias\s+SET\s+value=2\s+WHERE\s+1=1\s*$/', $result);
+
+		$result = $this->testDb->renderStatement('delete', array('fields' => 'value=2', 'table' => 'table', 'conditions' => 'WHERE 1=1', 'alias' => ''));
+		$this->assertPattern('/^\s*DELETE\s+FROM\s+table\s+WHERE\s+1=1\s*$/', $result);
+
+		$result = $this->testDb->renderStatement('delete', array('fields' => 'value=2', 'table' => 'table', 'conditions' => 'WHERE 1=1', 'alias' => 'alias', 'joins' => ''));
+		$this->assertPattern('/^\s*DELETE\s+alias\s+FROM\s+table\s+AS\s+alias\s+WHERE\s+1=1\s*$/', $result);
+	}
+
+/**
+ * testStatements method
+ *
+ * @access public
+ * @return void
+ */
+	function testStatements() {
+		$Article =& ClassRegistry::init('Article');
+
+		$result = $this->testDb->update($Article, array('field1'), array('value1'));
+		$this->assertFalse($result);
+		$result = $this->testDb->getLastQuery();
+		$this->assertPattern('/^\s*UPDATE\s+' . $this->testDb->fullTableName('articles') . '\s+SET\s+`field1`\s*=\s*\'value1\'\s+WHERE\s+1 = 1\s*$/', $result);
+
+		$result = $this->testDb->update($Article, array('field1'), array('2'), '2=2');
+		$this->assertFalse($result);
+		$result = $this->testDb->getLastQuery();
+		$this->assertPattern('/^\s*UPDATE\s+' . $this->testDb->fullTableName('articles') . ' AS `Article`\s+LEFT JOIN\s+' . $this->testDb->fullTableName('users') . ' AS `User` ON \(`Article`.`user_id` = `User`.`id`\)\s+SET\s+`Article`\.`field1`\s*=\s*2\s+WHERE\s+2\s*=\s*2\s*$/', $result);
+
+		$result = $this->testDb->delete($Article);
+		$this->assertTrue($result);
+		$result = $this->testDb->getLastQuery();
+		$this->assertPattern('/^\s*DELETE\s+FROM\s+' . $this->testDb->fullTableName('articles') . '\s+WHERE\s+1 = 1\s*$/', $result);
+
+		$result = $this->testDb->delete($Article, true);
+		$this->assertTrue($result);
+		$result = $this->testDb->getLastQuery();
+		$this->assertPattern('/^\s*DELETE\s+`Article`\s+FROM\s+' . $this->testDb->fullTableName('articles') . '\s+AS `Article`\s+LEFT JOIN\s+' . $this->testDb->fullTableName('users') . ' AS `User` ON \(`Article`.`user_id` = `User`.`id`\)\s+WHERE\s+1\s*=\s*1\s*$/', $result);
+
+		$result = $this->testDb->delete($Article, '2=2');
+		$this->assertTrue($result);
+		$result = $this->testDb->getLastQuery();
+		$this->assertPattern('/^\s*DELETE\s+`Article`\s+FROM\s+' . $this->testDb->fullTableName('articles') . '\s+AS `Article`\s+LEFT JOIN\s+' . $this->testDb->fullTableName('users') . ' AS `User` ON \(`Article`.`user_id` = `User`.`id`\)\s+WHERE\s+2\s*=\s*2\s*$/', $result);
+
+		$result = $this->testDb->hasAny($Article, '1=2');
+		$this->assertFalse($result);
+
+		$result = $this->testDb->insertMulti('articles', array('field'), array('(1)', '(2)'));
+		$this->assertFalse($result);
+		$result = $this->testDb->getLastQuery();
+		$this->assertPattern('/^\s*INSERT INTO\s+' . $this->testDb->fullTableName('articles') . '\s+\(`field`\)\s+VALUES\s+\(1\),\s*\(2\)\s*$/', $result);
+	}
+
+/**
+ * testSchema method
+ *
+ * @access public
+ * @return void
+ */
+	function testSchema() {
+		$Schema =& new CakeSchema();
+		$Schema->tables = array('table' => array(), 'anotherTable' => array());
+
+		$this->expectError();
+		$result = $this->testDb->dropSchema(null);
+		$this->assertTrue($result === null);
+
+		$result = $this->testDb->dropSchema($Schema, 'non_existing');
+		$this->assertTrue(empty($result));
+
+		$result = $this->testDb->dropSchema($Schema, 'table');
+		$this->assertPattern('/^\s*DROP TABLE IF EXISTS\s+' . $this->testDb->fullTableName('table') . ';\s*$/s', $result);
+	}
+
+/**
+ * testMagicMethodQuerying method
+ *
+ * @access public
+ * @return void
+ */
+	function testMagicMethodQuerying() {
+		$result = $this->testDb->query('findByFieldName', array('value'), $this->Model);
+		$expected = array('first', array(
+			'conditions' => array('TestModel.field_name' => 'value'),
+			'fields' => null, 'order' => null, 'recursive' => null
+		));
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->query('findByFindBy', array('value'), $this->Model);
+		$expected = array('first', array(
+			'conditions' => array('TestModel.find_by' => 'value'),
+			'fields' => null, 'order' => null, 'recursive' => null
+		));
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->query('findAllByFieldName', array('value'), $this->Model);
+		$expected = array('all', array(
+			'conditions' => array('TestModel.field_name' => 'value'),
+			'fields' => null, 'order' => null, 'limit' => null,
+			'page' => null, 'recursive' => null
+		));
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->query('findAllById', array('a'), $this->Model);
+		$expected = array('all', array(
+			'conditions' => array('TestModel.id' => 'a'),
+			'fields' => null, 'order' => null, 'limit' => null,
+			'page' => null, 'recursive' => null
+		));
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->query('findByFieldName', array(array('value1', 'value2', 'value3')), $this->Model);
+		$expected = array('first', array(
+			'conditions' => array('TestModel.field_name' => array('value1', 'value2', 'value3')),
+			'fields' => null, 'order' => null, 'recursive' => null
+		));
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->query('findByFieldName', array(null), $this->Model);
+		$expected = array('first', array(
+			'conditions' => array('TestModel.field_name' => null),
+			'fields' => null, 'order' => null, 'recursive' => null
+		));
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->query('findByFieldName', array('= a'), $this->Model);
+		$expected = array('first', array(
+			'conditions' => array('TestModel.field_name' => '= a'),
+			'fields' => null, 'order' => null, 'recursive' => null
+		));
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->query('findByFieldName', array(), $this->Model);
+		$expected = false;
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->query('directCall', array(), $this->Model);
+		$this->assertFalse($result);
+
+		$result = $this->testDb->query('directCall', true, $this->Model);
+		$this->assertFalse($result);
+
+		$result = $this->testDb->query('directCall', false, $this->Model);
+		$this->assertFalse($result);
+	}
+
+/**
+ * testOrderParsing method
+ *
+ * @access public
+ * @return void
+ */
+	function testOrderParsing() {
+		$result = $this->testDb->order("ADDTIME(Event.time_begin, '-06:00:00') ASC");
+		$expected = " ORDER BY ADDTIME(`Event`.`time_begin`, '-06:00:00') ASC";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->order("title, id");
+		$this->assertPattern('/^\s*ORDER BY\s+`title`\s+ASC,\s+`id`\s+ASC\s*$/', $result);
+
+		$result = $this->testDb->order("title desc, id desc");
+		$this->assertPattern('/^\s*ORDER BY\s+`title`\s+desc,\s+`id`\s+desc\s*$/', $result);
+
+		$result = $this->testDb->order(array("title desc, id desc"));
+		$this->assertPattern('/^\s*ORDER BY\s+`title`\s+desc,\s+`id`\s+desc\s*$/', $result);
+
+		$result = $this->testDb->order(array("title", "id"));
+		$this->assertPattern('/^\s*ORDER BY\s+`title`\s+ASC,\s+`id`\s+ASC\s*$/', $result);
+
+		$result = $this->testDb->order(array(array('title'), array('id')));
+		$this->assertPattern('/^\s*ORDER BY\s+`title`\s+ASC,\s+`id`\s+ASC\s*$/', $result);
+
+		$result = $this->testDb->order(array("Post.title" => 'asc', "Post.id" => 'desc'));
+		$this->assertPattern('/^\s*ORDER BY\s+`Post`.`title`\s+asc,\s+`Post`.`id`\s+desc\s*$/', $result);
+
+		$result = $this->testDb->order(array(array("Post.title" => 'asc', "Post.id" => 'desc')));
+		$this->assertPattern('/^\s*ORDER BY\s+`Post`.`title`\s+asc,\s+`Post`.`id`\s+desc\s*$/', $result);
+
+		$result = $this->testDb->order(array("title"));
+		$this->assertPattern('/^\s*ORDER BY\s+`title`\s+ASC\s*$/', $result);
+
+		$result = $this->testDb->order(array(array("title")));
+		$this->assertPattern('/^\s*ORDER BY\s+`title`\s+ASC\s*$/', $result);
+
+		$result = $this->testDb->order("Dealer.id = 7 desc, Dealer.id = 3 desc, Dealer.title asc");
+		$expected = " ORDER BY `Dealer`.`id` = 7 desc, `Dealer`.`id` = 3 desc, `Dealer`.`title` asc";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->order(array("Page.name" => "='test' DESC"));
+		$this->assertPattern("/^\s*ORDER BY\s+`Page`\.`name`\s*='test'\s+DESC\s*$/", $result);
+
+		$result = $this->testDb->order("Page.name = 'view' DESC");
+		$this->assertPattern("/^\s*ORDER BY\s+`Page`\.`name`\s*=\s*'view'\s+DESC\s*$/", $result);
+
+		$result = $this->testDb->order("(Post.views)");
+		$this->assertPattern("/^\s*ORDER BY\s+\(`Post`\.`views`\)\s+ASC\s*$/", $result);
+
+		$result = $this->testDb->order("(Post.views)*Post.views");
+		$this->assertPattern("/^\s*ORDER BY\s+\(`Post`\.`views`\)\*`Post`\.`views`\s+ASC\s*$/", $result);
+
+		$result = $this->testDb->order("(Post.views) * Post.views");
+		$this->assertPattern("/^\s*ORDER BY\s+\(`Post`\.`views`\) \* `Post`\.`views`\s+ASC\s*$/", $result);
+
+		$result = $this->testDb->order("(Model.field1 + Model.field2) * Model.field3");
+		$this->assertPattern("/^\s*ORDER BY\s+\(`Model`\.`field1` \+ `Model`\.`field2`\) \* `Model`\.`field3`\s+ASC\s*$/", $result);
+
+		$result = $this->testDb->order("Model.name+0 ASC");
+		$this->assertPattern("/^\s*ORDER BY\s+`Model`\.`name`\+0\s+ASC\s*$/", $result);
+
+		$result = $this->testDb->order("Anuncio.destaque & 2 DESC");
+		$expected = ' ORDER BY `Anuncio`.`destaque` & 2 DESC';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->order("3963.191 * id");
+		$expected = ' ORDER BY 3963.191 * id ASC';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->order(array('Property.sale_price IS NULL'));
+		$expected = ' ORDER BY `Property`.`sale_price` IS NULL ASC';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->order(array('Export.column-name' => 'ASC'));
+		$expected = ' ORDER BY `Export`.`column-name` ASC';
+		$this->assertEqual($result, $expected, 'Columns with -s are not working with order()');
+	}
+
+/**
+ * testComplexSortExpression method
+ *
+ * @return void
+ * @access public
+ */
+	function testComplexSortExpression() {
+		$result = $this->testDb->order(array('(Model.field > 100) DESC', 'Model.field ASC'));
+		$this->assertPattern("/^\s*ORDER BY\s+\(`Model`\.`field`\s+>\s+100\)\s+DESC,\s+`Model`\.`field`\s+ASC\s*$/", $result);
+	}
+
+/**
+ * testCalculations method
+ *
+ * @access public
+ * @return void
+ */
+	function testCalculations() {
+		$result = $this->testDb->calculate($this->Model, 'count');
+		$this->assertEqual($result, 'COUNT(*) AS `count`');
+
+		$result = $this->testDb->calculate($this->Model, 'count', array('id'));
+		$this->assertEqual($result, 'COUNT(`id`) AS `count`');
+
+		$result = $this->testDb->calculate(
+			$this->Model,
+			'count',
+			array($this->testDb->expression('DISTINCT id'))
+		);
+		$this->assertEqual($result, 'COUNT(DISTINCT id) AS `count`');
+
+		$result = $this->testDb->calculate($this->Model, 'count', array('id', 'id_count'));
+		$this->assertEqual($result, 'COUNT(`id`) AS `id_count`');
+
+		$result = $this->testDb->calculate($this->Model, 'count', array('Model.id', 'id_count'));
+		$this->assertEqual($result, 'COUNT(`Model`.`id`) AS `id_count`');
+
+		$result = $this->testDb->calculate($this->Model, 'max', array('id'));
+		$this->assertEqual($result, 'MAX(`id`) AS `id`');
+
+		$result = $this->testDb->calculate($this->Model, 'max', array('Model.id', 'id'));
+		$this->assertEqual($result, 'MAX(`Model`.`id`) AS `id`');
+
+		$result = $this->testDb->calculate($this->Model, 'max', array('`Model`.`id`', 'id'));
+		$this->assertEqual($result, 'MAX(`Model`.`id`) AS `id`');
+
+		$result = $this->testDb->calculate($this->Model, 'min', array('`Model`.`id`', 'id'));
+		$this->assertEqual($result, 'MIN(`Model`.`id`) AS `id`');
+
+		$result = $this->testDb->calculate($this->Model, 'min', 'left');
+		$this->assertEqual($result, 'MIN(`left`) AS `left`');
+	}
+
+/**
+ * testLength method
+ *
+ * @access public
+ * @return void
+ */
+	function testLength() {
+		$result = $this->testDb->length('varchar(255)');
+		$expected = 255;
+		$this->assertIdentical($result, $expected);
+
+		$result = $this->testDb->length('int(11)');
+		$expected = 11;
+		$this->assertIdentical($result, $expected);
+
+		$result = $this->testDb->length('float(5,3)');
+		$expected = '5,3';
+		$this->assertIdentical($result, $expected);
+
+		$result = $this->testDb->length('decimal(5,2)');
+		$expected = '5,2';
+		$this->assertIdentical($result, $expected);
+
+		$result = $this->testDb->length("enum('test','me','now')");
+		$expected = 4;
+		$this->assertIdentical($result, $expected);
+
+		$result = $this->testDb->length("set('a','b','cd')");
+		$expected = 2;
+		$this->assertIdentical($result, $expected);
+
+		$this->expectError();
+		$result = $this->testDb->length(false);
+		$this->assertTrue($result === null);
+
+		$result = $this->testDb->length('datetime');
+		$expected = null;
+		$this->assertIdentical($result, $expected);
+
+		$result = $this->testDb->length('text');
+		$expected = null;
+		$this->assertIdentical($result, $expected);
+	}
+
+/**
+ * testBuildIndex method
+ *
+ * @access public
+ * @return void
+ */
+	function testBuildIndex() {
+		$data = array(
+			'PRIMARY' => array('column' => 'id')
+		);
+		$result = $this->testDb->buildIndex($data);
+		$expected = array('PRIMARY KEY  (`id`)');
+		$this->assertIdentical($result, $expected);
+
+		$data = array(
+			'MyIndex' => array('column' => 'id', 'unique' => true)
+		);
+		$result = $this->testDb->buildIndex($data);
+		$expected = array('UNIQUE KEY `MyIndex` (`id`)');
+		$this->assertEqual($result, $expected);
+
+		$data = array(
+			'MyIndex' => array('column' => array('id', 'name'), 'unique' => true)
+		);
+		$result = $this->testDb->buildIndex($data);
+		$expected = array('UNIQUE KEY `MyIndex` (`id`, `name`)');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testBuildColumn method
+ *
+ * @access public
+ * @return void
+ */
+	function testBuildColumn() {
+		$this->expectError();
+		$data = array(
+			'name' => 'testName',
+			'type' => 'varchar(255)',
+			'default',
+			'null' => true,
+			'key'
+		);
+		$this->testDb->buildColumn($data);
+
+		$data = array(
+			'name' => 'testName',
+			'type' => 'string',
+			'length' => 255,
+			'default',
+			'null' => true,
+			'key'
+		);
+		$result = $this->testDb->buildColumn($data);
+		$expected = '`testName` varchar(255) DEFAULT NULL';
+		$this->assertEqual($result, $expected);
+
+		$data = array(
+			'name' => 'int_field',
+			'type' => 'integer',
+			'default' => '',
+			'null' => false,
+		);
+		$restore = $this->testDb->columns;
+
+		$this->testDb->columns = array('integer' => array('name' => 'int', 'limit' => '11', 'formatter' => 'intval'), );
+		$result = $this->testDb->buildColumn($data);
+		$expected = '`int_field` int(11) NOT NULL';
+		$this->assertEqual($result, $expected);
+
+		$this->testDb->fieldParameters['param'] = array(
+			'value' => 'COLLATE',
+			'quote' => false,
+			'join' => ' ',
+			'column' => 'Collate',
+			'position' => 'beforeDefault',
+			'options' => array('GOOD', 'OK')
+		);
+		$data = array(
+			'name' => 'int_field',
+			'type' => 'integer',
+			'default' => '',
+			'null' => false,
+			'param' => 'BAD'
+		);
+		$result = $this->testDb->buildColumn($data);
+		$expected = '`int_field` int(11) NOT NULL';
+		$this->assertEqual($result, $expected);
+
+		$data = array(
+			'name' => 'int_field',
+			'type' => 'integer',
+			'default' => '',
+			'null' => false,
+			'param' => 'GOOD'
+		);
+		$result = $this->testDb->buildColumn($data);
+		$expected = '`int_field` int(11) COLLATE GOOD NOT NULL';
+		$this->assertEqual($result, $expected);
+
+		$this->testDb->columns = $restore;
+
+		$data = array(
+			'name' => 'created',
+			'type' => 'timestamp',
+			'default' => 'current_timestamp',
+			'null' => false,
+ 		);
+		$result = $this->db->buildColumn($data);
+		$expected = '`created` timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL';
+		$this->assertEqual($result, $expected);
+
+		$data = array(
+			'name' => 'created',
+			'type' => 'timestamp',
+			'default' => 'CURRENT_TIMESTAMP',
+			'null' => true,
+		);
+		$result = $this->db->buildColumn($data);
+		$expected = '`created` timestamp DEFAULT CURRENT_TIMESTAMP';
+		$this->assertEqual($result, $expected);
+
+		$data = array(
+			'name' => 'modified',
+			'type' => 'timestamp',
+			'null' => true,
+		);
+		$result = $this->db->buildColumn($data);
+		$expected = '`modified` timestamp NULL';
+		$this->assertEqual($result, $expected);
+
+		$data = array(
+			'name' => 'modified',
+			'type' => 'timestamp',
+			'default' => null,
+			'null' => true,
+		);
+		$result = $this->db->buildColumn($data);
+		$expected = '`modified` timestamp NULL';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test hasAny()
+ *
+ * @return void
+ */
+	function testHasAny() {
+		$this->testDb->hasAny($this->Model, array());
+		$expected = 'SELECT COUNT(`TestModel`.`id`) AS count FROM `test_models` AS `TestModel` WHERE 1 = 1';
+		$this->assertEqual(end($this->testDb->simulated), $expected);
+
+		$this->testDb->hasAny($this->Model, array('TestModel.name' => 'harry'));
+		$expected = "SELECT COUNT(`TestModel`.`id`) AS count FROM `test_models` AS `TestModel` WHERE `TestModel`.`name` = 'harry'";
+		$this->assertEqual(end($this->testDb->simulated), $expected);
+	}
+
+/**
+ * testIntrospectType method
+ *
+ * @access public
+ * @return void
+ */
+	function testIntrospectType() {
+		$this->assertEqual($this->testDb->introspectType(0), 'integer');
+		$this->assertEqual($this->testDb->introspectType(2), 'integer');
+		$this->assertEqual($this->testDb->introspectType('2'), 'string');
+		$this->assertEqual($this->testDb->introspectType('2.2'), 'string');
+		$this->assertEqual($this->testDb->introspectType(2.2), 'float');
+		$this->assertEqual($this->testDb->introspectType('stringme'), 'string');
+		$this->assertEqual($this->testDb->introspectType('0stringme'), 'string');
+
+		$data = array(2.2);
+		$this->assertEqual($this->testDb->introspectType($data), 'float');
+
+		$data = array('2.2');
+		$this->assertEqual($this->testDb->introspectType($data), 'float');
+
+		$data = array(2);
+		$this->assertEqual($this->testDb->introspectType($data), 'integer');
+
+		$data = array('2');
+		$this->assertEqual($this->testDb->introspectType($data), 'integer');
+
+		$data = array('string');
+		$this->assertEqual($this->testDb->introspectType($data), 'string');
+
+		$data = array(2.2, '2.2');
+		$this->assertEqual($this->testDb->introspectType($data), 'float');
+
+		$data = array(2, '2');
+		$this->assertEqual($this->testDb->introspectType($data), 'integer');
+
+		$data = array('string one', 'string two');
+		$this->assertEqual($this->testDb->introspectType($data), 'string');
+
+		$data = array('2.2', 3);
+		$this->assertEqual($this->testDb->introspectType($data), 'integer');
+
+		$data = array('2.2', '0stringme');
+		$this->assertEqual($this->testDb->introspectType($data), 'string');
+
+		$data = array(2.2, 3);
+		$this->assertEqual($this->testDb->introspectType($data), 'integer');
+
+		$data = array(2.2, '0stringme');
+		$this->assertEqual($this->testDb->introspectType($data), 'string');
+
+		$data = array(2, 'stringme');
+		$this->assertEqual($this->testDb->introspectType($data), 'string');
+
+		$data = array(2, '2.2', 'stringgme');
+		$this->assertEqual($this->testDb->introspectType($data), 'string');
+
+		$data = array(2, '2.2');
+		$this->assertEqual($this->testDb->introspectType($data), 'integer');
+
+		$data = array(2, 2.2);
+		$this->assertEqual($this->testDb->introspectType($data), 'integer');
+
+
+		// NULL
+		$result = $this->testDb->value(null, 'boolean');
+		$this->assertEqual($result, 'NULL');
+
+		// EMPTY STRING
+		$result = $this->testDb->value('', 'boolean');
+		$this->assertEqual($result, "NULL");
+
+
+		// BOOLEAN
+		$result = $this->testDb->value('true', 'boolean');
+		$this->assertEqual($result, 1);
+
+		$result = $this->testDb->value('false', 'boolean');
+		$this->assertEqual($result, 1);
+
+		$result = $this->testDb->value(true, 'boolean');
+		$this->assertEqual($result, 1);
+
+		$result = $this->testDb->value(false, 'boolean');
+		$this->assertEqual($result, 0);
+
+		$result = $this->testDb->value(1, 'boolean');
+		$this->assertEqual($result, 1);
+
+		$result = $this->testDb->value(0, 'boolean');
+		$this->assertEqual($result, 0);
+
+		$result = $this->testDb->value('abc', 'boolean');
+		$this->assertEqual($result, 1);
+
+		$result = $this->testDb->value(1.234, 'boolean');
+		$this->assertEqual($result, 1);
+
+		$result = $this->testDb->value('1.234e05', 'boolean');
+		$this->assertEqual($result, 1);
+
+		// NUMBERS
+		$result = $this->testDb->value(123, 'integer');
+		$this->assertEqual($result, 123);
+
+		$result = $this->testDb->value('123', 'integer');
+		$this->assertEqual($result, '123');
+
+		$result = $this->testDb->value('0123', 'integer');
+		$this->assertEqual($result, "'0123'");
+
+		$result = $this->testDb->value('0x123ABC', 'integer');
+		$this->assertEqual($result, "'0x123ABC'");
+
+		$result = $this->testDb->value('0x123', 'integer');
+		$this->assertEqual($result, "'0x123'");
+
+		$result = $this->testDb->value(1.234, 'float');
+		$this->assertEqual($result, 1.234);
+
+		$result = $this->testDb->value('1.234', 'float');
+		$this->assertEqual($result, '1.234');
+
+		$result = $this->testDb->value(' 1.234 ', 'float');
+		$this->assertEqual($result, "' 1.234 '");
+
+		$result = $this->testDb->value('1.234e05', 'float');
+		$this->assertEqual($result, "'1.234e05'");
+
+		$result = $this->testDb->value('1.234e+5', 'float');
+		$this->assertEqual($result, "'1.234e+5'");
+
+		$result = $this->testDb->value('1,234', 'float');
+		$this->assertEqual($result, "'1,234'");
+
+		$result = $this->testDb->value('FFF', 'integer');
+		$this->assertEqual($result, "'FFF'");
+
+		$result = $this->testDb->value('abc', 'integer');
+		$this->assertEqual($result, "'abc'");
+
+		// STRINGS
+		$result = $this->testDb->value('123', 'string');
+		$this->assertEqual($result, "'123'");
+
+		$result = $this->testDb->value(123, 'string');
+		$this->assertEqual($result, "'123'");
+
+		$result = $this->testDb->value(1.234, 'string');
+		$this->assertEqual($result, "'1.234'");
+
+		$result = $this->testDb->value('abc', 'string');
+		$this->assertEqual($result, "'abc'");
+
+		$result = $this->testDb->value(' abc ', 'string');
+		$this->assertEqual($result, "' abc '");
+
+		$result = $this->testDb->value('a bc', 'string');
+		$this->assertEqual($result, "'a bc'");
+	}
+
+/**
+ * testValue method
+ *
+ * @access public
+ * @return void
+ */
+	function testValue() {
+		$result = $this->testDb->value('{$__cakeForeignKey__$}');
+		$this->assertEqual($result, '{$__cakeForeignKey__$}');
+
+		$result = $this->testDb->value(array('first', 2, 'third'));
+		$expected = array('\'first\'', 2, '\'third\'');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testReconnect method
+ *
+ * @access public
+ * @return void
+ */
+	function testReconnect() {
+		$this->testDb->reconnect(array('prefix' => 'foo'));
+		$this->assertTrue($this->testDb->connected);
+		$this->assertEqual($this->testDb->config['prefix'], 'foo');
+	}
+
+/**
+ * testRealQueries method
+ *
+ * @access public
+ * @return void
+ */
+	function testRealQueries() {
+		$this->loadFixtures('Apple', 'Article', 'User', 'Comment', 'Tag');
+
+		$Apple =& ClassRegistry::init('Apple');
+		$Article =& ClassRegistry::init('Article');
+
+		$result = $this->db->rawQuery('SELECT color, name FROM ' . $this->db->fullTableName('apples'));
+		$this->assertTrue(!empty($result));
+
+		$result = $this->db->fetchRow($result);
+		$expected = array($this->db->fullTableName('apples', false) => array(
+			'color' => 'Red 1',
+			'name' => 'Red Apple 1'
+		));
+		$this->assertEqual($result, $expected);
+
+		$result = $this->db->fetchAll('SELECT name FROM ' . $this->testDb->fullTableName('apples') . ' ORDER BY id');
+		$expected = array(
+			array($this->db->fullTableName('apples', false) => array('name' => 'Red Apple 1')),
+			array($this->db->fullTableName('apples', false) => array('name' => 'Bright Red Apple')),
+			array($this->db->fullTableName('apples', false) => array('name' => 'green blue')),
+			array($this->db->fullTableName('apples', false) => array('name' => 'Test Name')),
+			array($this->db->fullTableName('apples', false) => array('name' => 'Blue Green')),
+			array($this->db->fullTableName('apples', false) => array('name' => 'My new apple')),
+			array($this->db->fullTableName('apples', false) => array('name' => 'Some odd color'))
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $this->db->field($this->testDb->fullTableName('apples', false), 'SELECT color, name FROM ' . $this->testDb->fullTableName('apples') . ' ORDER BY id');
+		$expected = array(
+			'color' => 'Red 1',
+			'name' => 'Red Apple 1'
+		);
+		$this->assertEqual($result, $expected);
+
+		$Apple->unbindModel(array(), false);
+		$result = $this->db->read($Apple, array(
+			'fields' => array($Apple->escapeField('name')),
+			'conditions' => null,
+			'recursive' => -1
+		));
+		$expected = array(
+			array('Apple' => array('name' => 'Red Apple 1')),
+			array('Apple' => array('name' => 'Bright Red Apple')),
+			array('Apple' => array('name' => 'green blue')),
+			array('Apple' => array('name' => 'Test Name')),
+			array('Apple' => array('name' => 'Blue Green')),
+			array('Apple' => array('name' => 'My new apple')),
+			array('Apple' => array('name' => 'Some odd color'))
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $this->db->read($Article, array(
+			'fields' => array('id', 'user_id', 'title'),
+			'conditions' => null,
+			'recursive' => 1
+		));
+
+		$this->assertTrue(Set::matches('/Article[id=1]', $result));
+		$this->assertTrue(Set::matches('/Comment[id=1]', $result));
+		$this->assertTrue(Set::matches('/Comment[id=2]', $result));
+		$this->assertFalse(Set::matches('/Comment[id=10]', $result));
+	}
+
+/**
+ * testName method
+ *
+ * @access public
+ * @return void
+ */
+	function testName() {
+		$result = $this->testDb->name('name');
+		$expected = '`name`';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->name(array('name', 'Model.*'));
+		$expected = array('`name`', '`Model`.*');
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->name('MTD()');
+		$expected = 'MTD()';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->name('(sm)');
+		$expected = '(sm)';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->name('name AS x');
+		$expected = '`name` AS `x`';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->name('Model.name AS x');
+		$expected = '`Model`.`name` AS `x`';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->name('Function(Something.foo)');
+		$expected = 'Function(`Something`.`foo`)';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->name('Function(SubFunction(Something.foo))');
+		$expected = 'Function(SubFunction(`Something`.`foo`))';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->name('Function(Something.foo) AS x');
+		$expected = 'Function(`Something`.`foo`) AS `x`';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->name('name-with-minus');
+		$expected = '`name-with-minus`';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->name(array('my-name', 'Foo-Model.*'));
+		$expected = array('`my-name`', '`Foo-Model`.*');
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->name(array('Team.P%', 'Team.G/G'));
+		$expected = array('`Team`.`P%`', '`Team`.`G/G`');
+		$this->assertEqual($result, $expected);
+
+		$result = $this->testDb->name('Model.name as y');
+		$expected = '`Model`.`name` AS `y`';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that cacheMethod works as exepected
+ *
+ * @return void
+ */
+	function testCacheMethod() {
+		$this->testDb->cacheMethods = true;
+		$result = $this->testDb->cacheMethod('name', 'some-key', 'stuff');
+		$this->assertEqual($result, 'stuff');
+
+		$result = $this->testDb->cacheMethod('name', 'some-key');
+		$this->assertEqual($result, 'stuff');
+
+		$result = $this->testDb->cacheMethod('conditions', 'some-key');
+		$this->assertNull($result);
+
+		$result = $this->testDb->cacheMethod('name', 'other-key');
+		$this->assertNull($result);
+
+		$this->testDb->cacheMethods = false;
+		$result = $this->testDb->cacheMethod('name', 'some-key', 'stuff');
+		$this->assertEqual($result, 'stuff');
+
+		$result = $this->testDb->cacheMethod('name', 'some-key');
+		$this->assertNull($result);
+	}
+
+/**
+ * testLog method
+ *
+ * @access public
+ * @return void
+ */
+	function testLog() {
+		$this->testDb->logQuery('Query 1');
+		$this->testDb->logQuery('Query 2');
+
+		$log = $this->testDb->getLog(false, false);
+		$result = Set::extract($log['log'], '/query');
+		$expected = array('Query 1', 'Query 2');
+		$this->assertEqual($result, $expected);
+
+		$oldError = $this->testDb->error;
+		$this->testDb->error = true;
+		$result = $this->testDb->logQuery('Error 1');
+		$this->assertFalse($result);
+		$this->testDb->error = $oldError;
+
+		$log = $this->testDb->getLog(false, false);
+		$result = Set::combine($log['log'], '/query', '/error');
+		$expected = array('Query 1' => false, 'Query 2' => false, 'Error 1' => true);
+		$this->assertEqual($result, $expected);
+
+		Configure::write('debug', 2);
+		ob_start();
+		$this->testDb->showLog();
+		$contents = ob_get_clean();
+
+		$this->assertPattern('/Query 1/s', $contents);
+		$this->assertPattern('/Query 2/s', $contents);
+		$this->assertPattern('/Error 1/s', $contents);
+
+		ob_start();
+		$this->testDb->showLog(true);
+		$contents = ob_get_clean();
+
+		$this->assertPattern('/Query 1/s', $contents);
+		$this->assertPattern('/Query 2/s', $contents);
+		$this->assertPattern('/Error 1/s', $contents);
+
+		$oldError = $this->testDb->error;
+		$oldDebug = Configure::read('debug');
+		Configure::write('debug', 2);
+
+		$this->testDb->error = true;
+		ob_start();
+		$this->testDb->showQuery('Error 2');
+		$contents = ob_get_clean();
+
+		$this->assertPattern('/Error 2/s', $contents);
+
+		$this->testDb->error = $oldError;
+		Configure::write('debug', $oldDebug);
+	}
+
+/**
+ * test getting the query log as an array.
+ *
+ * @return void
+ */
+	function testGetLog() {
+		$this->testDb->logQuery('Query 1');
+		$this->testDb->logQuery('Query 2');
+
+		$oldError = $this->testDb->error;
+		$this->testDb->error = true;
+		$result = $this->testDb->logQuery('Error 1');
+		$this->assertFalse($result);
+		$this->testDb->error = $oldError;
+
+		$log = $this->testDb->getLog();
+		$expected = array('query' => 'Query 1', 'error' => '', 'affected' => '', 'numRows' => '', 'took' => '');
+		$this->assertEqual($log['log'][0], $expected);
+		$expected = array('query' => 'Query 2', 'error' => '', 'affected' => '', 'numRows' => '', 'took' => '');
+		$this->assertEqual($log['log'][1], $expected);
+		$expected = array('query' => 'Error 1', 'error' => true, 'affected' => '', 'numRows' => '', 'took' => '');
+		$this->assertEqual($log['log'][2], $expected);
+	}
+
+/**
+ * test that execute runs queries.
+ *
+ * @return void
+ */
+	function testExecute() {
+		$query = 'SELECT * FROM ' . $this->testDb->fullTableName('articles') . ' WHERE 1 = 1';
+
+		$this->db->_result = null;
+		$this->db->took = null;
+		$this->db->affected = null;
+		$result = $this->db->execute($query, array('stats' => false));
+		$this->assertNotNull($result, 'No query performed! %s');
+		$this->assertNull($this->db->took, 'Stats were set %s');
+		$this->assertNull($this->db->affected, 'Stats were set %s');
+
+		$result = $this->db->execute($query);
+		$this->assertNotNull($result, 'No query performed! %s');
+		$this->assertNotNull($this->db->took, 'Stats were not set %s');
+		$this->assertNotNull($this->db->affected, 'Stats were not set %s');
+	}
+
+/**
+ * test that query() returns boolean values from operations like CREATE TABLE
+ *
+ * @return void
+ */
+	function testFetchAllBooleanReturns() {
+		$name = $this->db->fullTableName('test_query');
+		$query = "CREATE TABLE {$name} (name varchar(10));";
+		$result = $this->db->query($query);
+		$this->assertTrue($result, 'Query did not return a boolean. %s');
+
+		$query = "DROP TABLE {$name};";
+		$result = $this->db->fetchAll($query);
+		$this->assertTrue($result, 'Query did not return a boolean. %s');
+	}
+
+/**
+ * test that query propery caches/doesn't cache selects
+ *
+ * @return void
+ * @author David Kullmann
+ */
+	function testFetchAllCaching() {
+		$query = "SELECT NOW() as TIME";
+		$result = $this->db->fetchAll($query);
+		
+		$this->assertTrue(is_array($this->db->_queryCache[$query]));
+		$this->assertEqual($this->db->_queryCache[$query][0][0]['TIME'], $result[0][0]['TIME']);
+
+		$query = "DROP TABLE IF EXISTS select_test";
+		$result = $this->db->fetchAll($query);
+		
+		$this->assertTrue(!isset($this->db->_queryCache[$query]));
+	}
+
+/**
+ * test ShowQuery generation of regular and error messages
+ *
+ * @return void
+ */
+	function testShowQuery() {
+		$this->testDb->error = false;
+		ob_start();
+		$this->testDb->showQuery('Some Query');
+		$contents = ob_get_clean();
+		$this->assertPattern('/Some Query/s', $contents);
+		$this->assertPattern('/Aff:/s', $contents);
+		$this->assertPattern('/Num:/s', $contents);
+		$this->assertPattern('/Took:/s', $contents);
+
+		$this->expectError();
+		$this->testDb->error = true;
+		ob_start();
+		$this->testDb->showQuery('Another Query');
+		$contents = ob_get_clean();
+		$this->assertPattern('/Another Query/s', $contents);
+		$this->assertNoPattern('/Aff:/s', $contents);
+		$this->assertNoPattern('/Num:/s', $contents);
+		$this->assertNoPattern('/Took:/s', $contents);
+	}
+
+/**
+ * test fields generating usable virtual fields to use in query
+ *
+ * @return void
+ */
+	function testVirtualFields() {
+		$this->loadFixtures('Article');
+
+		$Article =& ClassRegistry::init('Article');
+		$Article->virtualFields = array(
+			'this_moment' => 'NOW()',
+			'two' => '1 + 1',
+			'comment_count' => 'SELECT COUNT(*) FROM ' . $this->db->fullTableName('comments') .
+				' WHERE Article.id = ' . $this->db->fullTableName('comments') . '.article_id'
+		);
+		$result = $this->db->fields($Article);
+		$expected = array(
+			'`Article`.`id`',
+			'`Article`.`user_id`',
+			'`Article`.`title`',
+			'`Article`.`body`',
+			'`Article`.`published`',
+			'`Article`.`created`',
+			'`Article`.`updated`',
+			'(NOW()) AS  `Article__this_moment`',
+			'(1 + 1) AS  `Article__two`',
+			'(SELECT COUNT(*) FROM comments WHERE `Article`.`id` = `comments`.`article_id`) AS  `Article__comment_count`'
+		);
+		$this->assertEqual($expected, $result);
+
+		$result = $this->db->fields($Article, null, array('this_moment', 'title'));
+		$expected = array(
+			'`Article`.`title`',
+			'(NOW()) AS  `Article__this_moment`',
+		);
+		$this->assertEqual($expected, $result);
+
+		$result = $this->db->fields($Article, null, array('Article.title', 'Article.this_moment'));
+		$expected = array(
+			'`Article`.`title`',
+			'(NOW()) AS  `Article__this_moment`',
+		);
+		$this->assertEqual($expected, $result);
+
+		$result = $this->db->fields($Article, null, array('Article.this_moment', 'Article.title'));
+		$expected = array(
+			'`Article`.`title`',
+			'(NOW()) AS  `Article__this_moment`',
+		);
+		$this->assertEqual($expected, $result);
+
+		$result = $this->db->fields($Article, null, array('Article.*'));
+		$expected = array(
+			'`Article`.*',
+			'(NOW()) AS  `Article__this_moment`',
+			'(1 + 1) AS  `Article__two`',
+			'(SELECT COUNT(*) FROM comments WHERE `Article`.`id` = `comments`.`article_id`) AS  `Article__comment_count`'
+		);
+		$this->assertEqual($expected, $result);
+
+		$result = $this->db->fields($Article, null, array('*'));
+		$expected = array(
+			'*',
+			'(NOW()) AS  `Article__this_moment`',
+			'(1 + 1) AS  `Article__two`',
+			'(SELECT COUNT(*) FROM comments WHERE `Article`.`id` = `comments`.`article_id`) AS  `Article__comment_count`'
+		);
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * test conditions to generate query conditions for virtual fields
+ *
+ * @return void
+ */
+	function testVirtualFieldsInConditions() {
+		$Article =& ClassRegistry::init('Article');
+		$Article->virtualFields = array(
+			'this_moment' => 'NOW()',
+			'two' => '1 + 1',
+			'comment_count' => 'SELECT COUNT(*) FROM ' . $this->db->fullTableName('comments') .
+				' WHERE Article.id = ' . $this->db->fullTableName('comments') . '.article_id'
+		);
+		$conditions = array('two' => 2);
+		$result = $this->db->conditions($conditions, true, false, $Article);
+		$expected = '(1 + 1) = 2';
+		$this->assertEqual($expected, $result);
+
+		$conditions = array('this_moment BETWEEN ? AND ?' => array(1,2));
+		$expected = 'NOW() BETWEEN 1 AND 2';
+		$result = $this->db->conditions($conditions, true, false, $Article);
+		$this->assertEqual($expected, $result);
+
+		$conditions = array('comment_count >' => 5);
+		$expected = '(SELECT COUNT(*) FROM comments WHERE `Article`.`id` = `comments`.`article_id`) > 5';
+		$result = $this->db->conditions($conditions, true, false, $Article);
+		$this->assertEqual($expected, $result);
+
+		$conditions = array('NOT' => array('two' => 2));
+		$result = $this->db->conditions($conditions, true, false, $Article);
+		$expected = 'NOT ((1 + 1) = 2)';
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * test that virtualFields with complex functions and aliases work.
+ *
+ * @return void
+ */
+	function testConditionsWithComplexVirtualFields() {
+		$Article =& ClassRegistry::init('Article');
+		$Article->virtualFields = array(
+			'distance' => 'ACOS(SIN(20 * PI() / 180)
+					* SIN(Article.latitude * PI() / 180)
+					+ COS(20 * PI() / 180)
+					* COS(Article.latitude * PI() / 180)
+					* COS((50 - Article.longitude) * PI() / 180)
+				) * 180 / PI() * 60 * 1.1515 * 1.609344'
+		);
+		$conditions = array('distance >=' => 20);
+		$result = $this->db->conditions($conditions, true, true, $Article);
+
+		$this->assertPattern('/\) >= 20/', $result);
+		$this->assertPattern('/[`\'"]Article[`\'"].[`\'"]latitude[`\'"]/', $result);
+		$this->assertPattern('/[`\'"]Article[`\'"].[`\'"]longitude[`\'"]/', $result);
+	}
+
+/**
+ * test order to generate query order clause for virtual fields
+ *
+ * @return void
+ */
+	function testVirtualFieldsInOrder() {
+		$Article =& ClassRegistry::init('Article');
+		$Article->virtualFields = array(
+			'this_moment' => 'NOW()',
+			'two' => '1 + 1',
+		);
+		$order = array('two', 'this_moment');
+		$result = $this->db->order($order, 'ASC', $Article);
+		$expected = ' ORDER BY (1 + 1) ASC, (NOW()) ASC';
+		$this->assertEqual($expected, $result);
+
+		$order = array('Article.two', 'Article.this_moment');
+		$result = $this->db->order($order, 'ASC', $Article);
+		$expected = ' ORDER BY (1 + 1) ASC, (NOW()) ASC';
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * test calculate to generate claculate statements on virtual fields
+ *
+ * @return void
+ */
+	function testVirtualFieldsInCalculate() {
+		$Article =& ClassRegistry::init('Article');
+		$Article->virtualFields = array(
+			'this_moment' => 'NOW()',
+			'two' => '1 + 1',
+			'comment_count' => 'SELECT COUNT(*) FROM ' . $this->db->fullTableName('comments') .
+				' WHERE Article.id = ' . $this->db->fullTableName('comments'). '.article_id'
+		);
+
+		$result = $this->db->calculate($Article, 'count', array('this_moment'));
+		$expected = 'COUNT(NOW()) AS `count`';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->db->calculate($Article, 'max', array('comment_count'));
+		$expected = 'MAX(SELECT COUNT(*) FROM comments WHERE `Article`.`id` = `comments`.`article_id`) AS `comment_count`';
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * test a full example of using virtual fields
+ *
+ * @return void
+ */
+	function testVirtualFieldsFetch() {
+		$this->loadFixtures('Article', 'Comment');
+
+		$Article =& ClassRegistry::init('Article');
+		$Article->virtualFields = array(
+			'comment_count' => 'SELECT COUNT(*) FROM ' . $this->db->fullTableName('comments') .
+				' WHERE Article.id = ' . $this->db->fullTableName('comments') . '.article_id'
+		);
+
+		$conditions = array('comment_count >' => 2);
+		$query = 'SELECT ' . join(',',$this->db->fields($Article, null, array('id', 'comment_count'))) .
+				' FROM ' .  $this->db->fullTableName($Article) . ' Article ' . $this->db->conditions($conditions, true, true, $Article);
+		$result = $this->db->fetchAll($query);
+		$expected = array(array(
+			'Article' => array('id' => 1, 'comment_count' => 4)
+		));
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * test reading complex virtualFields with subqueries.
+ *
+ * @return void
+ */
+	function testVirtualFieldsComplexRead() {
+		$this->loadFixtures('DataTest', 'Article', 'Comment');
+
+		$Article =& ClassRegistry::init('Article');
+		$commentTable = $this->db->fullTableName('comments');
+		$Article =& ClassRegistry::init('Article');
+		$Article->virtualFields = array(
+			'comment_count' => 'SELECT COUNT(*) FROM ' . $commentTable .
+				' AS Comment WHERE Article.id = Comment.article_id'
+		);
+		$result = $Article->find('all');
+		$this->assertTrue(count($result) > 0);
+		$this->assertTrue($result[0]['Article']['comment_count'] > 0);
+
+		$DataTest =& ClassRegistry::init('DataTest');
+		$DataTest->virtualFields = array(
+			'complicated' => 'ACOS(SIN(20 * PI() / 180)
+				* SIN(DataTest.float * PI() / 180)
+				+ COS(20 * PI() / 180)
+				* COS(DataTest.count * PI() / 180)
+				* COS((50 - DataTest.float) * PI() / 180)
+				) * 180 / PI() * 60 * 1.1515 * 1.609344'
+		);
+		$result = $DataTest->find('all');
+		$this->assertTrue(count($result) > 0);
+		$this->assertTrue($result[0]['DataTest']['complicated'] > 0);
+	}
+
+/**
+ * test that virtualFields with complex functions and aliases work.
+ *
+ * @return void
+ */
+	function testFieldsWithComplexVirtualFields() {
+		$Article =& new Article();
+		$Article->virtualFields = array(
+			'distance' => 'ACOS(SIN(20 * PI() / 180)
+					* SIN(Article.latitude * PI() / 180)
+					+ COS(20 * PI() / 180)
+					* COS(Article.latitude * PI() / 180)
+					* COS((50 - Article.longitude) * PI() / 180)
+				) * 180 / PI() * 60 * 1.1515 * 1.609344'
+		);
+
+		$fields = array('id', 'distance');
+		$result = $this->db->fields($Article, null, $fields);
+		$qs = $this->db->startQuote;
+		$qe = $this->db->endQuote;
+
+		$this->assertEqual($result[0], "{$qs}Article{$qe}.{$qs}id{$qe}");
+		$this->assertPattern('/Article__distance/', $result[1]);
+		$this->assertPattern('/[`\'"]Article[`\'"].[`\'"]latitude[`\'"]/', $result[1]);
+		$this->assertPattern('/[`\'"]Article[`\'"].[`\'"]longitude[`\'"]/', $result[1]);
+	}
+
+/**
+ * test reading virtual fields containing newlines when recursive > 0
+ *
+ * @return void
+ */
+	function testReadVirtualFieldsWithNewLines() {
+		$Article =& new Article();
+		$Article->recursive = 1;
+		$Article->virtualFields = array(
+			'test' => '
+			User.id + User.id
+			'
+		);
+		$result = $this->db->fields($Article, null, array());
+		$result = $this->db->fields($Article, $Article->alias, $result);
+		$this->assertPattern('/[`\"]User[`\"]\.[`\"]id[`\"] \+ [`\"]User[`\"]\.[`\"]id[`\"]/', $result[7]);
+	}
+
+/**
+ * test group to generate GROUP BY statements on virtual fields
+ *
+ * @return void
+ */
+	function testVirtualFieldsInGroup() {
+		$Article =& ClassRegistry::init('Article');
+		$Article->virtualFields = array(
+			'this_year' => 'YEAR(Article.created)'
+		);
+
+		$result = $this->db->group('this_year',$Article);
+		$expected = " GROUP BY (YEAR(`Article`.`created`))";
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * Test that group works without a model
+ *
+ * @return void
+ */
+	function testGroupNoModel() {
+		$result = $this->db->group('created');
+		$this->assertEqual(' GROUP BY created', $result);
+	}
+
+/**
+ * test the permutations of fullTableName()
+ *
+ * @return void
+ */
+	function testFullTablePermutations() {
+		$Article =& ClassRegistry::init('Article');
+		$result = $this->testDb->fullTableName($Article, false);
+		$this->assertEqual($result, 'articles');
+
+		$Article->tablePrefix = 'tbl_';
+		$result = $this->testDb->fullTableName($Article, false);
+		$this->assertEqual($result, 'tbl_articles');
+
+		$Article->useTable = $Article->table = 'with spaces';
+		$Article->tablePrefix = '';
+		$result = $this->testDb->fullTableName($Article);
+		$this->assertEqual($result, '`with spaces`');
+	}
+
+/**
+ * test that read() only calls queryAssociation on db objects when the method is defined.
+ *
+ * @return void
+ */
+	function testReadOnlyCallingQueryAssociationWhenDefined() {
+		ConnectionManager::create('test_no_queryAssociation', array(
+			'datasource' => 'data'
+		));
+		$Article =& ClassRegistry::init('Article');
+		$Article->Comment->useDbConfig = 'test_no_queryAssociation';
+		$result = $Article->find('all');
+		$this->assertTrue(is_array($result));
+	}
+
+/**
+ * test that fields() is using methodCache()
+ *
+ * @return void
+ */
+	function testFieldsUsingMethodCache() {
+		$this->testDb->cacheMethods = false;
+		$this->assertTrue(empty($this->testDb->methodCache['fields']), 'Cache not empty');
+
+		$Article =& ClassRegistry::init('Article');
+		$this->testDb->fields($Article, null, array('title', 'body', 'published'));
+		$this->assertTrue(empty($this->testDb->methodCache['fields']), 'Cache not empty');
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/model/db_acl.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/model/db_acl.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/model/db_acl.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,397 @@
+<?php
+/**
+ * DbAclTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components.dbacl.models
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) {
+	define('CAKEPHP_UNIT_TEST_EXECUTION', 1);
+}
+App::import('Component', 'Acl');
+App::import('Core', 'db_acl');
+
+/**
+ * DB ACL wrapper test class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ */
+class DbAclNodeTestBase extends AclNode {
+
+/**
+ * useDbConfig property
+ *
+ * @var string 'test_suite'
+ * @access public
+ */
+	var $useDbConfig = 'test_suite';
+
+/**
+ * cacheSources property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $cacheSources = false;
+}
+
+/**
+ * Aro Test Wrapper
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ */
+class DbAroTest extends DbAclNodeTestBase {
+
+/**
+ * name property
+ *
+ * @var string 'DbAroTest'
+ * @access public
+ */
+	var $name = 'DbAroTest';
+
+/**
+ * useTable property
+ *
+ * @var string 'aros'
+ * @access public
+ */
+	var $useTable = 'aros';
+
+/**
+ * hasAndBelongsToMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('DbAcoTest' => array('with' => 'DbPermissionTest'));
+}
+
+/**
+ * Aco Test Wrapper
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ */
+class DbAcoTest extends DbAclNodeTestBase {
+
+/**
+ * name property
+ *
+ * @var string 'DbAcoTest'
+ * @access public
+ */
+	var $name = 'DbAcoTest';
+
+/**
+ * useTable property
+ *
+ * @var string 'acos'
+ * @access public
+ */
+	var $useTable = 'acos';
+
+/**
+ * hasAndBelongsToMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('DbAroTest' => array('with' => 'DbPermissionTest'));
+}
+
+/**
+ * Permission Test Wrapper
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ */
+class DbPermissionTest extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'DbPermissionTest'
+ * @access public
+ */
+	var $name = 'DbPermissionTest';
+
+/**
+ * useTable property
+ *
+ * @var string 'aros_acos'
+ * @access public
+ */
+	var $useTable = 'aros_acos';
+
+/**
+ * cacheQueries property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $cacheQueries = false;
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('DbAroTest' => array('foreignKey' => 'aro_id'), 'DbAcoTest' => array('foreignKey' => 'aco_id'));
+}
+
+/**
+ * DboActionTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ */
+class DbAcoActionTest extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'DbAcoActionTest'
+ * @access public
+ */
+	var $name = 'DbAcoActionTest';
+
+/**
+ * useTable property
+ *
+ * @var string 'aco_actions'
+ * @access public
+ */
+	var $useTable = 'aco_actions';
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('DbAcoTest' => array('foreignKey' => 'aco_id'));
+}
+
+/**
+ * DbAroUserTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ */
+class DbAroUserTest extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'AuthUser'
+ * @access public
+ */
+	var $name = 'AuthUser';
+
+/**
+ * useTable property
+ *
+ * @var string 'auth_users'
+ * @access public
+ */
+	var $useTable = 'auth_users';
+
+/**
+ * bindNode method
+ *
+ * @param mixed $ref
+ * @access public
+ * @return void
+ */
+	function bindNode($ref = null) {
+		if (Configure::read('DbAclbindMode') == 'string') {
+			return 'ROOT/admins/Gandalf';
+		} elseif (Configure::read('DbAclbindMode') == 'array') {
+			return array('DbAroTest' => array('DbAroTest.model' => 'AuthUser', 'DbAroTest.foreign_key' => 2));
+		}
+	}
+}
+
+/**
+ * DbAclTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components
+ */
+class DbAclTest extends DbAcl {
+
+/**
+ * construct method
+ *
+ * @access private
+ * @return void
+ */
+	function __construct() {
+		$this->Aro =& new DbAroTest();
+		$this->Aro->Permission =& new DbPermissionTest();
+		$this->Aco =& new DbAcoTest();
+		$this->Aro->Permission =& new DbPermissionTest();
+	}
+}
+
+/**
+ * AclNodeTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.controller.components.dbacl.models
+ */
+class AclNodeTest extends CakeTestCase {
+
+/**
+ * fixtures property
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array('core.aro', 'core.aco', 'core.aros_aco', 'core.aco_action', 'core.auth_user');
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		Configure::write('Acl.classname', 'DbAclTest');
+		Configure::write('Acl.database', 'test_suite');
+	}
+
+/**
+ * testNode method
+ *
+ * @access public
+ * @return void
+ */
+	function testNode() {
+		$Aco =& new DbAcoTest();
+		$result = Set::extract($Aco->node('Controller1'), '{n}.DbAcoTest.id');
+		$expected = array(2, 1);
+		$this->assertEqual($result, $expected);
+
+		$result = Set::extract($Aco->node('Controller1/action1'), '{n}.DbAcoTest.id');
+		$expected = array(3, 2, 1);
+		$this->assertEqual($result, $expected);
+
+		$result = Set::extract($Aco->node('Controller2/action1'), '{n}.DbAcoTest.id');
+		$expected = array(7, 6, 1);
+		$this->assertEqual($result, $expected);
+
+		$result = Set::extract($Aco->node('Controller1/action2'), '{n}.DbAcoTest.id');
+		$expected = array(5, 2, 1);
+		$this->assertEqual($result, $expected);
+
+		$result = Set::extract($Aco->node('Controller1/action1/record1'), '{n}.DbAcoTest.id');
+		$expected = array(4, 3, 2, 1);
+		$this->assertEqual($result, $expected);
+
+		$result = Set::extract($Aco->node('Controller2/action1/record1'), '{n}.DbAcoTest.id');
+		$expected = array(8, 7, 6, 1);
+		$this->assertEqual($result, $expected);
+
+		$result = Set::extract($Aco->node('Controller2/action3'), '{n}.DbAcoTest.id');
+		$this->assertFalse($result);
+
+		$result = Set::extract($Aco->node('Controller2/action3/record5'), '{n}.DbAcoTest.id');
+		$this->assertFalse($result);
+
+		$result = $Aco->node('');
+		$this->assertEqual($result, null);
+	}
+
+/**
+ * test that node() doesn't dig deeper than it should.
+ *
+ * @return void
+ */
+	function testNodeWithDuplicatePathSegments() {
+		$Aco =& new DbAcoTest();
+		$nodes = $Aco->node('ROOT/Users');
+		$this->assertEqual($nodes[0]['DbAcoTest']['parent_id'], 1, 'Parent id does not point at ROOT. %s');
+	}
+
+/**
+ * testNodeArrayFind method
+ *
+ * @access public
+ * @return void
+ */
+	function testNodeArrayFind() {
+		$Aro = new DbAroTest();
+		Configure::write('DbAclbindMode', 'string');
+		$result = Set::extract($Aro->node(array('DbAroUserTest' => array('id' => '1', 'foreign_key' => '1'))), '{n}.DbAroTest.id');
+		$expected = array(3, 2, 1);
+		$this->assertEqual($result, $expected);
+
+		Configure::write('DbAclbindMode', 'array');
+		$result = Set::extract($Aro->node(array('DbAroUserTest' => array('id' => 4, 'foreign_key' => 2))), '{n}.DbAroTest.id');
+		$expected = array(4);
+		$this->assertEqual($result, $expected);
+	}
+	/**
+ * testNodeObjectFind method
+ *
+ * @access public
+ * @return void
+ */
+	function testNodeObjectFind() {
+		$Aro = new DbAroTest();
+		$Model = new DbAroUserTest();
+		$Model->id = 1;
+		$result = Set::extract($Aro->node($Model), '{n}.DbAroTest.id');
+		$expected = array(3, 2, 1);
+		$this->assertEqual($result, $expected);
+
+		$Model->id = 2;
+		$result = Set::extract($Aro->node($Model), '{n}.DbAroTest.id');
+		$expected = array(4, 2, 1);
+		$this->assertEqual($result, $expected);
+
+	}
+
+/**
+ * testNodeAliasParenting method
+ *
+ * @access public
+ * @return void
+ */
+	function testNodeAliasParenting() {
+		$Aco = new DbAcoTest();
+		$db =& ConnectionManager::getDataSource('test_suite');
+		$db->truncate($Aco);
+		$db->_queriesLog = array();
+
+		$Aco->create(array('model' => null, 'foreign_key' => null, 'parent_id' => null, 'alias' => 'Application'));
+		$Aco->save();
+
+		$Aco->create(array('model' => null, 'foreign_key' => null, 'parent_id' => $Aco->id, 'alias' => 'Pages'));
+		$Aco->save();
+
+		$result = $Aco->find('all');
+		$expected = array(
+			array('DbAcoTest' => array('id' => '1', 'parent_id' => null, 'model' => null, 'foreign_key' => null, 'alias' => 'Application', 'lft' => '1', 'rght' => '4'), 'DbAroTest' => array()),
+			array('DbAcoTest' => array('id' => '2', 'parent_id' => '1', 'model' => null, 'foreign_key' => null, 'alias' => 'Pages', 'lft' => '2', 'rght' => '3', ), 'DbAroTest' => array())
+		);
+		$this->assertEqual($result, $expected);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/model/model.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/model/model.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/model/model.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,104 @@
+<?php
+/**
+ * ModelTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', array('AppModel', 'Model'));
+require_once dirname(__FILE__) . DS . 'models.php';
+
+SimpleTest::ignore('BaseModelTest');
+
+/**
+ * ModelBaseTest
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class BaseModelTest extends CakeTestCase {
+
+/**
+ * autoFixtures property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $autoFixtures = false;
+
+/**
+ * fixtures property
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array(
+		'core.category', 'core.category_thread', 'core.user', 'core.my_category', 'core.my_product',
+		'core.my_user', 'core.my_categories_my_users', 'core.my_categories_my_products',
+		'core.article', 'core.featured', 'core.article_featureds_tags', 'core.article_featured',
+		'core.articles', 'core.numeric_article', 'core.tag', 'core.articles_tag', 'core.comment',
+		'core.attachment', 'core.apple', 'core.sample', 'core.another_article', 'core.item',
+		'core.advertisement', 'core.home', 'core.post', 'core.author', 'core.bid', 'core.portfolio',
+		'core.product', 'core.project', 'core.thread', 'core.message', 'core.items_portfolio',
+		'core.syfile', 'core.image', 'core.device_type', 'core.device_type_category',
+		'core.feature_set', 'core.exterior_type_category', 'core.document', 'core.device',
+		'core.document_directory', 'core.primary_model', 'core.secondary_model', 'core.something',
+		'core.something_else', 'core.join_thing', 'core.join_a', 'core.join_b', 'core.join_c',
+		'core.join_a_b', 'core.join_a_c', 'core.uuid', 'core.data_test', 'core.posts_tag',
+		'core.the_paper_monkies', 'core.person', 'core.underscore_field', 'core.node',
+		'core.dependency', 'core.story', 'core.stories_tag', 'core.cd', 'core.book', 'core.basket',
+		'core.overall_favorite', 'core.account', 'core.content', 'core.content_account',
+		'core.film_file', 'core.test_plugin_article', 'core.test_plugin_comment', 'core.uuiditem',
+		'core.counter_cache_user', 'core.counter_cache_post',
+		'core.counter_cache_user_nonstandard_primary_key',
+		'core.counter_cache_post_nonstandard_primary_key', 'core.uuidportfolio',
+		'core.uuiditems_uuidportfolio', 'core.uuiditems_uuidportfolio_numericid', 'core.fruit',
+		'core.fruits_uuid_tag', 'core.uuid_tag', 'core.product_update_all', 'core.group_update_all',
+		'core.bidding', 'core.bidding_message'
+	);
+
+/**
+ * start method
+ *
+ * @access public
+ * @return void
+ */
+	function start() {
+		parent::start();
+		$this->debug = Configure::read('debug');
+		Configure::write('debug', 2);
+	}
+
+/**
+ * end method
+ *
+ * @access public
+ * @return void
+ */
+	function end() {
+		parent::end();
+		Configure::write('debug', $this->debug);
+	}
+
+/**
+ * endTest method
+ *
+ * @access public
+ * @return void
+ */
+	function endTest() {
+		ClassRegistry::flush();
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/model/model_behavior.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/model/model_behavior.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/model/model_behavior.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,1138 @@
+<?php
+/**
+ * BehaviorTest file
+ *
+ * Long description for behavior.test.php
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ * @since         1.2
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Model', 'AppModel');
+require_once dirname(__FILE__) . DS . 'models.php';
+
+Mock::generatePartial('BehaviorCollection', 'MockModelBehaviorCollection', array('cakeError', '_stop'));
+
+/**
+ * TestBehavior class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class TestBehavior extends ModelBehavior {
+
+/**
+ * mapMethods property
+ *
+ * @var array
+ * @access public
+ */
+	var $mapMethods = array('/test(\w+)/' => 'testMethod', '/look for\s+(.+)/' => 'speakEnglish');
+
+/**
+ * setup method
+ *
+ * @param mixed $model
+ * @param array $config
+ * @access public
+ * @return void
+ */
+	function setup(&$model, $config = array()) {
+		parent::setup($model, $config);
+		if (isset($config['mangle'])) {
+			$config['mangle'] .= ' mangled';
+		}
+		$this->settings[$model->alias] = array_merge(array('beforeFind' => 'on', 'afterFind' => 'off'), $config);
+	}
+
+/**
+ * beforeFind method
+ *
+ * @param mixed $model
+ * @param mixed $query
+ * @access public
+ * @return void
+ */
+	function beforeFind(&$model, $query) {
+		$settings = $this->settings[$model->alias];
+		if (!isset($settings['beforeFind']) || $settings['beforeFind'] == 'off') {
+			return parent::beforeFind($model, $query);
+		}
+		switch ($settings['beforeFind']) {
+			case 'on':
+				return false;
+			break;
+			case 'test':
+				return null;
+			break;
+			case 'modify':
+				$query['fields'] = array($model->alias . '.id', $model->alias . '.name', $model->alias . '.mytime');
+				$query['recursive'] = -1;
+				return $query;
+			break;
+		}
+	}
+
+/**
+ * afterFind method
+ *
+ * @param mixed $model
+ * @param mixed $results
+ * @param mixed $primary
+ * @access public
+ * @return void
+ */
+	function afterFind(&$model, $results, $primary) {
+		$settings = $this->settings[$model->alias];
+		if (!isset($settings['afterFind']) || $settings['afterFind'] == 'off') {
+			return parent::afterFind($model, $results, $primary);
+		}
+		switch ($settings['afterFind']) {
+			case 'on':
+				return array();
+			break;
+			case 'test':
+				return true;
+			break;
+			case 'test2':
+				return null;
+			break;
+			case 'modify':
+				return Set::extract($results, "{n}.{$model->alias}");
+			break;
+		}
+	}
+
+/**
+ * beforeSave method
+ *
+ * @param mixed $model
+ * @access public
+ * @return void
+ */
+	function beforeSave(&$model) {
+		$settings = $this->settings[$model->alias];
+		if (!isset($settings['beforeSave']) || $settings['beforeSave'] == 'off') {
+			return parent::beforeSave($model);
+		}
+		switch ($settings['beforeSave']) {
+			case 'on':
+				return false;
+			break;
+			case 'test':
+				return null;
+			break;
+			case 'modify':
+				$model->data[$model->alias]['name'] .= ' modified before';
+				return true;
+			break;
+		}
+	}
+
+/**
+ * afterSave method
+ *
+ * @param mixed $model
+ * @param mixed $created
+ * @access public
+ * @return void
+ */
+	function afterSave(&$model, $created) {
+		$settings = $this->settings[$model->alias];
+		if (!isset($settings['afterSave']) || $settings['afterSave'] == 'off') {
+			return parent::afterSave($model, $created);
+		}
+		$string = 'modified after';
+		if ($created) {
+			$string .= ' on create';
+		}
+		switch ($settings['afterSave']) {
+			case 'on':
+				$model->data[$model->alias]['aftersave'] = $string;
+			break;
+			case 'test':
+				unset($model->data[$model->alias]['name']);
+			break;
+			case 'test2':
+				return false;
+			break;
+			case 'modify':
+				$model->data[$model->alias]['name'] .= ' ' . $string;
+			break;
+		}
+	}
+
+/**
+ * beforeValidate method
+ *
+ * @param mixed $model
+ * @access public
+ * @return void
+ */
+	function beforeValidate(&$model) {
+		$settings = $this->settings[$model->alias];
+		if (!isset($settings['validate']) || $settings['validate'] == 'off') {
+			return parent::beforeValidate($model);
+		}
+		switch ($settings['validate']) {
+			case 'on':
+				$model->invalidate('name');
+				return true;
+			break;
+			case 'test':
+				return null;
+			break;
+			case 'whitelist':
+				$this->_addToWhitelist($model, array('name'));
+				return true;
+			break;
+			case 'stop':
+				$model->invalidate('name');
+				return false;
+			break;
+		}
+	}
+
+/**
+ * beforeDelete method
+ *
+ * @param mixed $model
+ * @param bool $cascade
+ * @access public
+ * @return void
+ */
+	function beforeDelete(&$model, $cascade = true) {
+		$settings =& $this->settings[$model->alias];
+		if (!isset($settings['beforeDelete']) || $settings['beforeDelete'] == 'off') {
+			return parent::beforeDelete($model, $cascade);
+		}
+		switch ($settings['beforeDelete']) {
+			case 'on':
+				return false;
+			break;
+			case 'test':
+				return null;
+			break;
+			case 'test2':
+				echo 'beforeDelete success';
+				if ($cascade) {
+					echo ' (cascading) ';
+				}
+			break;
+		}
+	}
+
+/**
+ * afterDelete method
+ *
+ * @param mixed $model
+ * @access public
+ * @return void
+ */
+	function afterDelete(&$model) {
+		$settings =& $this->settings[$model->alias];
+		if (!isset($settings['afterDelete']) || $settings['afterDelete'] == 'off') {
+			return parent::afterDelete($model);
+		}
+		switch ($settings['afterDelete']) {
+			case 'on':
+				echo 'afterDelete success';
+			break;
+		}
+	}
+
+/**
+ * onError method
+ *
+ * @param mixed $model
+ * @access public
+ * @return void
+ */
+	function onError(&$model) {
+		$settings = $this->settings[$model->alias];
+		if (!isset($settings['onError']) || $settings['onError'] == 'off') {
+			return parent::onError($model, $cascade);
+		}
+		echo "onError trigger success";
+	}
+/**
+ * beforeTest method
+ *
+ * @param mixed $model
+ * @access public
+ * @return void
+ */
+	function beforeTest(&$model) {
+		$model->beforeTestResult[] = strtolower(get_class($this));
+		return strtolower(get_class($this));
+	}
+
+/**
+ * testMethod method
+ *
+ * @param mixed $model
+ * @param bool $param
+ * @access public
+ * @return void
+ */
+	function testMethod(&$model, $param = true) {
+		if ($param === true) {
+			return 'working';
+		}
+	}
+
+/**
+ * testData method
+ *
+ * @param mixed $model
+ * @access public
+ * @return void
+ */
+	function testData(&$model) {
+		if (!isset($model->data['Apple']['field'])) {
+			return false;
+		}
+		$model->data['Apple']['field_2'] = true;
+		return true;
+	}
+
+/**
+ * validateField method
+ *
+ * @param mixed $model
+ * @param mixed $field
+ * @access public
+ * @return void
+ */
+	function validateField(&$model, $field) {
+		return current($field) === 'Orange';
+	}
+
+/**
+ * speakEnglish method
+ *
+ * @param mixed $model
+ * @param mixed $method
+ * @param mixed $query
+ * @access public
+ * @return void
+ */
+	function speakEnglish(&$model, $method, $query) {
+		$method = preg_replace('/look for\s+/', 'Item.name = \'', $method);
+		$query = preg_replace('/^in\s+/', 'Location.name = \'', $query);
+		return $method . '\' AND ' . $query . '\'';
+	}
+}
+
+/**
+ * Test2Behavior class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Test2Behavior extends TestBehavior{
+}
+
+/**
+ * Test3Behavior class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Test3Behavior extends TestBehavior{
+}
+
+/**
+ * Test4Behavior class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Test4Behavior extends ModelBehavior{
+	function setup(&$model, $config = null) {
+		$model->bindModel(
+			array('hasMany' => array('Comment'))
+		);
+	}
+}
+
+/**
+ * Test5Behavior class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Test5Behavior extends ModelBehavior{
+	function setup(&$model, $config = null) {
+		$model->bindModel(
+			array('belongsTo' => array('User'))
+		);
+	}
+}
+
+/**
+ * Test6Behavior class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Test6Behavior extends ModelBehavior{
+	function setup(&$model, $config = null) {
+		$model->bindModel(
+			array('hasAndBelongsToMany' => array('Tag'))
+		);
+	}
+}
+
+/**
+ * Test7Behavior class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Test7Behavior extends ModelBehavior{
+	function setup(&$model, $config = null) {
+		$model->bindModel(
+			array('hasOne' => array('Attachment'))
+		);
+	}
+}
+
+/**
+ * BehaviorTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class BehaviorTest extends CakeTestCase {
+
+/**
+ * fixtures property
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array(
+		'core.apple', 'core.sample', 'core.article', 'core.user', 'core.comment',
+		'core.attachment', 'core.tag', 'core.articles_tag'
+	);
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function endTest() {
+		ClassRegistry::flush();
+	}
+
+/**
+ * testBehaviorBinding method
+ *
+ * @access public
+ * @return void
+ */
+	function testBehaviorBinding() {
+		$Apple = new Apple();
+		$this->assertIdentical($Apple->Behaviors->attached(), array());
+
+		$Apple->Behaviors->attach('Test', array('key' => 'value'));
+		$this->assertIdentical($Apple->Behaviors->attached(), array('Test'));
+		$this->assertEqual(strtolower(get_class($Apple->Behaviors->Test)), 'testbehavior');
+		$expected = array('beforeFind' => 'on', 'afterFind' => 'off', 'key' => 'value');
+		$this->assertEqual($Apple->Behaviors->Test->settings['Apple'], $expected);
+		$this->assertEqual(array_keys($Apple->Behaviors->Test->settings), array('Apple'));
+
+		$this->assertIdentical($Apple->Sample->Behaviors->attached(), array());
+		$Apple->Sample->Behaviors->attach('Test', array('key2' => 'value2'));
+		$this->assertIdentical($Apple->Sample->Behaviors->attached(), array('Test'));
+		$this->assertEqual($Apple->Sample->Behaviors->Test->settings['Sample'], array('beforeFind' => 'on', 'afterFind' => 'off', 'key2' => 'value2'));
+
+		$this->assertEqual(array_keys($Apple->Behaviors->Test->settings), array('Apple', 'Sample'));
+		$this->assertIdentical(
+			$Apple->Sample->Behaviors->Test->settings,
+			$Apple->Behaviors->Test->settings
+		);
+		$this->assertNotIdentical($Apple->Behaviors->Test->settings['Apple'], $Apple->Sample->Behaviors->Test->settings['Sample']);
+
+		$Apple->Behaviors->attach('Test', array('key2' => 'value2', 'key3' => 'value3', 'beforeFind' => 'off'));
+		$Apple->Sample->Behaviors->attach('Test', array('key' => 'value', 'key3' => 'value3', 'beforeFind' => 'off'));
+		$this->assertEqual($Apple->Behaviors->Test->settings['Apple'], array('beforeFind' => 'off', 'afterFind' => 'off', 'key' => 'value', 'key2' => 'value2', 'key3' => 'value3'));
+		$this->assertEqual($Apple->Behaviors->Test->settings['Apple'], $Apple->Sample->Behaviors->Test->settings['Sample']);
+
+		$this->assertFalse(isset($Apple->Child->Behaviors->Test));
+		$Apple->Child->Behaviors->attach('Test', array('key' => 'value', 'key2' => 'value2', 'key3' => 'value3', 'beforeFind' => 'off'));
+		$this->assertEqual($Apple->Child->Behaviors->Test->settings['Child'], $Apple->Sample->Behaviors->Test->settings['Sample']);
+
+		$this->assertFalse(isset($Apple->Parent->Behaviors->Test));
+		$Apple->Parent->Behaviors->attach('Test', array('key' => 'value', 'key2' => 'value2', 'key3' => 'value3', 'beforeFind' => 'off'));
+		$this->assertEqual($Apple->Parent->Behaviors->Test->settings['Parent'], $Apple->Sample->Behaviors->Test->settings['Sample']);
+
+		$Apple->Parent->Behaviors->attach('Test', array('key' => 'value', 'key2' => 'value', 'key3' => 'value', 'beforeFind' => 'off'));
+		$this->assertNotEqual($Apple->Parent->Behaviors->Test->settings['Parent'], $Apple->Sample->Behaviors->Test->settings['Sample']);
+
+		$Apple->Behaviors->attach('Plugin.Test', array('key' => 'new value'));
+		$expected = array(
+			'beforeFind' => 'off', 'afterFind' => 'off', 'key' => 'new value',
+			'key2' => 'value2', 'key3' => 'value3'
+		);
+		$this->assertEqual($Apple->Behaviors->Test->settings['Apple'], $expected);
+
+		$current = $Apple->Behaviors->Test->settings['Apple'];
+		$expected = array_merge($current, array('mangle' => 'trigger mangled'));
+		$Apple->Behaviors->attach('Test', array('mangle' => 'trigger'));
+		$this->assertEqual($Apple->Behaviors->Test->settings['Apple'], $expected);
+
+		$Apple->Behaviors->attach('Test');
+		$expected = array_merge($current, array('mangle' => 'trigger mangled mangled'));
+
+		$this->assertEqual($Apple->Behaviors->Test->settings['Apple'], $expected);
+		$Apple->Behaviors->attach('Test', array('mangle' => 'trigger'));
+		$expected = array_merge($current, array('mangle' => 'trigger mangled'));
+		$this->assertEqual($Apple->Behaviors->Test->settings['Apple'], $expected);
+	}
+
+/**
+ * test that attach()/detach() works with plugin.banana
+ *
+ * @return void
+ */
+	function testDetachWithPluginNames() {
+		$Apple = new Apple();
+		$Apple->Behaviors->attach('Plugin.Test');
+		$this->assertTrue(isset($Apple->Behaviors->Test), 'Missing behavior');
+		$this->assertEqual($Apple->Behaviors->attached(), array('Test'));
+
+		$Apple->Behaviors->detach('Plugin.Test');
+		$this->assertEqual($Apple->Behaviors->attached(), array());
+
+		$Apple->Behaviors->attach('Plugin.Test');
+		$this->assertTrue(isset($Apple->Behaviors->Test), 'Missing behavior');
+		$this->assertEqual($Apple->Behaviors->attached(), array('Test'));
+
+		$Apple->Behaviors->detach('Test');
+		$this->assertEqual($Apple->Behaviors->attached(), array());
+	}
+
+/**
+ * test that attaching a non existant Behavior triggers a cake error.
+ *
+ * @return void
+ */
+	function testInvalidBehaviorCausingCakeError() {
+		$Apple =& new Apple();
+		$Apple->Behaviors =& new MockModelBehaviorCollection();
+		$Apple->Behaviors->expectOnce('cakeError');
+		$Apple->Behaviors->expectAt(0, 'cakeError', array('missingBehaviorFile', '*'));
+		$this->assertFalse($Apple->Behaviors->attach('NoSuchBehavior'));
+	}
+
+/**
+ * testBehaviorToggling method
+ *
+ * @access public
+ * @return void
+ */
+	function testBehaviorToggling() {
+		$Apple = new Apple();
+		$this->assertIdentical($Apple->Behaviors->enabled(), array());
+
+		$Apple->Behaviors->init('Apple', array('Test' => array('key' => 'value')));
+		$this->assertIdentical($Apple->Behaviors->enabled(), array('Test'));
+
+		$Apple->Behaviors->disable('Test');
+		$this->assertIdentical($Apple->Behaviors->attached(), array('Test'));
+		$this->assertIdentical($Apple->Behaviors->enabled(), array());
+
+		$Apple->Sample->Behaviors->attach('Test');
+		$this->assertIdentical($Apple->Sample->Behaviors->enabled('Test'), true);
+		$this->assertIdentical($Apple->Behaviors->enabled(), array());
+
+		$Apple->Behaviors->enable('Test');
+		$this->assertIdentical($Apple->Behaviors->attached('Test'), true);
+		$this->assertIdentical($Apple->Behaviors->enabled(), array('Test'));
+
+		$Apple->Behaviors->disable('Test');
+		$this->assertIdentical($Apple->Behaviors->enabled(), array());
+		$Apple->Behaviors->attach('Test', array('enabled' => true));
+		$this->assertIdentical($Apple->Behaviors->enabled(), array('Test'));
+		$Apple->Behaviors->attach('Test', array('enabled' => false));
+		$this->assertIdentical($Apple->Behaviors->enabled(), array());
+		$Apple->Behaviors->detach('Test');
+		$this->assertIdentical($Apple->Behaviors->enabled(), array());
+	}
+
+/**
+ * testBehaviorFindCallbacks method
+ *
+ * @access public
+ * @return void
+ */
+	function testBehaviorFindCallbacks() {
+		$Apple = new Apple();
+		$expected = $Apple->find('all');
+
+		$Apple->Behaviors->attach('Test');
+		$this->assertIdentical($Apple->find('all'), null);
+
+		$Apple->Behaviors->attach('Test', array('beforeFind' => 'off'));
+		$this->assertIdentical($Apple->find('all'), $expected);
+
+		$Apple->Behaviors->attach('Test', array('beforeFind' => 'test'));
+		$this->assertIdentical($Apple->find('all'), $expected);
+
+		$Apple->Behaviors->attach('Test', array('beforeFind' => 'modify'));
+		$expected2 = array(
+			array('Apple' => array('id' => '1', 'name' => 'Red Apple 1', 'mytime' => '22:57:17')),
+			array('Apple' => array('id' => '2', 'name' => 'Bright Red Apple', 'mytime' => '22:57:17')),
+			array('Apple' => array('id' => '3', 'name' => 'green blue', 'mytime' => '22:57:17'))
+		);
+		$result = $Apple->find('all', array('conditions' => array('Apple.id <' => '4')));
+		$this->assertEqual($result, $expected2);
+
+		$Apple->Behaviors->disable('Test');
+		$result = $Apple->find('all');
+		$this->assertEqual($result, $expected);
+
+		$Apple->Behaviors->attach('Test', array('beforeFind' => 'off', 'afterFind' => 'on'));
+		$this->assertIdentical($Apple->find('all'), array());
+
+		$Apple->Behaviors->attach('Test', array('afterFind' => 'off'));
+		$this->assertEqual($Apple->find('all'), $expected);
+
+		$Apple->Behaviors->attach('Test', array('afterFind' => 'test'));
+		$this->assertEqual($Apple->find('all'), $expected);
+
+		$Apple->Behaviors->attach('Test', array('afterFind' => 'test2'));
+		$this->assertEqual($Apple->find('all'), $expected);
+
+		$Apple->Behaviors->attach('Test', array('afterFind' => 'modify'));
+		$expected = array(
+			array('id' => '1', 'apple_id' => '2', 'color' => 'Red 1', 'name' => 'Red Apple 1', 'created' => '2006-11-22 10:38:58', 'date' => '1951-01-04', 'modified' => '2006-12-01 13:31:26', 'mytime' => '22:57:17'),
+			array('id' => '2', 'apple_id' => '1', 'color' => 'Bright Red 1', 'name' => 'Bright Red Apple', 'created' => '2006-11-22 10:43:13', 'date' => '2014-01-01', 'modified' => '2006-11-30 18:38:10', 'mytime' => '22:57:17'),
+			array('id' => '3', 'apple_id' => '2', 'color' => 'blue green', 'name' => 'green blue', 'created' => '2006-12-25 05:13:36', 'date' => '2006-12-25', 'modified' => '2006-12-25 05:23:24', 'mytime' => '22:57:17'),
+			array('id' => '4', 'apple_id' => '2', 'color' => 'Blue Green', 'name' => 'Test Name', 'created' => '2006-12-25 05:23:36', 'date' => '2006-12-25', 'modified' => '2006-12-25 05:23:36', 'mytime' => '22:57:17'),
+			array('id' => '5', 'apple_id' => '5', 'color' => 'Green', 'name' => 'Blue Green', 'created' => '2006-12-25 05:24:06', 'date' => '2006-12-25', 'modified' => '2006-12-25 05:29:16', 'mytime' => '22:57:17'),
+			array('id' => '6', 'apple_id' => '4', 'color' => 'My new appleOrange', 'name' => 'My new apple', 'created' => '2006-12-25 05:29:39', 'date' => '2006-12-25', 'modified' => '2006-12-25 05:29:39', 'mytime' => '22:57:17'),
+			array('id' => '7', 'apple_id' => '6', 'color' => 'Some wierd color', 'name' => 'Some odd color', 'created' => '2006-12-25 05:34:21', 'date' => '2006-12-25', 'modified' => '2006-12-25 05:34:21', 'mytime' => '22:57:17')
+		);
+		$this->assertEqual($Apple->find('all'), $expected);
+	}
+
+/**
+ * testBehaviorHasManyFindCallbacks method
+ *
+ * @access public
+ * @return void
+ */
+	function testBehaviorHasManyFindCallbacks() {
+		$Apple = new Apple();
+		$Apple->unbindModel(array('hasOne' => array('Sample'), 'belongsTo' => array('Parent')), false);
+		$expected = $Apple->find('all');
+
+		$Apple->unbindModel(array('hasMany' => array('Child')));
+		$wellBehaved = $Apple->find('all');
+		$Apple->Child->Behaviors->attach('Test', array('afterFind' => 'modify'));
+		$Apple->unbindModel(array('hasMany' => array('Child')));
+		$this->assertIdentical($Apple->find('all'), $wellBehaved);
+
+		$Apple->Child->Behaviors->attach('Test', array('before' => 'off'));
+		$this->assertIdentical($Apple->find('all'), $expected);
+
+		$Apple->Child->Behaviors->attach('Test', array('before' => 'test'));
+		$this->assertIdentical($Apple->find('all'), $expected);
+
+		$expected2 = array(
+			array(
+				'Apple' => array('id' => 1),
+				'Child' => array(
+					array('id' => 2,'name' => 'Bright Red Apple', 'mytime' => '22:57:17'))),
+			array(
+				'Apple' => array('id' => 2),
+				'Child' => array(
+					array('id' => 1, 'name' => 'Red Apple 1', 'mytime' => '22:57:17'),
+					array('id' => 3, 'name' => 'green blue', 'mytime' => '22:57:17'),
+					array('id' => 4, 'name' => 'Test Name', 'mytime' => '22:57:17'))),
+			array(
+				'Apple' => array('id' => 3),
+				'Child' => array())
+		);
+
+		$Apple->Child->Behaviors->attach('Test', array('before' => 'modify'));
+		$result = $Apple->find('all', array('fields' => array('Apple.id'), 'conditions' => array('Apple.id <' => '4')));
+		//$this->assertEqual($result, $expected2);
+
+		$Apple->Child->Behaviors->disable('Test');
+		$result = $Apple->find('all');
+		$this->assertEqual($result, $expected);
+
+		$Apple->Child->Behaviors->attach('Test', array('before' => 'off', 'after' => 'on'));
+		//$this->assertIdentical($Apple->find('all'), array());
+
+		$Apple->Child->Behaviors->attach('Test', array('after' => 'off'));
+		$this->assertEqual($Apple->find('all'), $expected);
+
+		$Apple->Child->Behaviors->attach('Test', array('after' => 'test'));
+		$this->assertEqual($Apple->find('all'), $expected);
+
+		$Apple->Child->Behaviors->attach('Test', array('after' => 'test2'));
+		$this->assertEqual($Apple->find('all'), $expected);
+
+		$Apple->Child->Behaviors->attach('Test', array('after' => 'modify'));
+		$expected = array(
+			array('id' => '1', 'apple_id' => '2', 'color' => 'Red 1', 'name' => 'Red Apple 1', 'created' => '2006-11-22 10:38:58', 'date' => '1951-01-04', 'modified' => '2006-12-01 13:31:26', 'mytime' => '22:57:17'),
+			array('id' => '2', 'apple_id' => '1', 'color' => 'Bright Red 1', 'name' => 'Bright Red Apple', 'created' => '2006-11-22 10:43:13', 'date' => '2014-01-01', 'modified' => '2006-11-30 18:38:10', 'mytime' => '22:57:17'),
+			array('id' => '3', 'apple_id' => '2', 'color' => 'blue green', 'name' => 'green blue', 'created' => '2006-12-25 05:13:36', 'date' => '2006-12-25', 'modified' => '2006-12-25 05:23:24', 'mytime' => '22:57:17'),
+			array('id' => '4', 'apple_id' => '2', 'color' => 'Blue Green', 'name' => 'Test Name', 'created' => '2006-12-25 05:23:36', 'date' => '2006-12-25', 'modified' => '2006-12-25 05:23:36', 'mytime' => '22:57:17'),
+			array('id' => '5', 'apple_id' => '5', 'color' => 'Green', 'name' => 'Blue Green', 'created' => '2006-12-25 05:24:06', 'date' => '2006-12-25', 'modified' => '2006-12-25 05:29:16', 'mytime' => '22:57:17'),
+			array('id' => '6', 'apple_id' => '4', 'color' => 'My new appleOrange', 'name' => 'My new apple', 'created' => '2006-12-25 05:29:39', 'date' => '2006-12-25', 'modified' => '2006-12-25 05:29:39', 'mytime' => '22:57:17'),
+			array('id' => '7', 'apple_id' => '6', 'color' => 'Some wierd color', 'name' => 'Some odd color', 'created' => '2006-12-25 05:34:21', 'date' => '2006-12-25', 'modified' => '2006-12-25 05:34:21', 'mytime' => '22:57:17')
+		);
+		//$this->assertEqual($Apple->find('all'), $expected);
+
+	}
+	/**
+ * testBehaviorHasOneFindCallbacks method
+ *
+ * @access public
+ * @return void
+ */
+	function testBehaviorHasOneFindCallbacks() {
+		$Apple = new Apple();
+		$Apple->unbindModel(array('hasMany' => array('Child'), 'belongsTo' => array('Parent')), false);
+		$expected = $Apple->find('all');
+
+		$Apple->unbindModel(array('hasOne' => array('Sample')));
+		$wellBehaved = $Apple->find('all');
+		$Apple->Sample->Behaviors->attach('Test');
+		$Apple->unbindModel(array('hasOne' => array('Sample')));
+		$this->assertIdentical($Apple->find('all'), $wellBehaved);
+
+		$Apple->Sample->Behaviors->attach('Test', array('before' => 'off'));
+		$this->assertIdentical($Apple->find('all'), $expected);
+
+		$Apple->Sample->Behaviors->attach('Test', array('before' => 'test'));
+		$this->assertIdentical($Apple->find('all'), $expected);
+
+		$Apple->Sample->Behaviors->attach('Test', array('before' => 'modify'));
+		$expected2 = array(
+			array(
+				'Apple' => array('id' => 1),
+				'Child' => array(
+					array('id' => 2,'name' => 'Bright Red Apple', 'mytime' => '22:57:17'))),
+			array(
+				'Apple' => array('id' => 2),
+				'Child' => array(
+					array('id' => 1, 'name' => 'Red Apple 1', 'mytime' => '22:57:17'),
+					array('id' => 3, 'name' => 'green blue', 'mytime' => '22:57:17'),
+					array('id' => 4, 'name' => 'Test Name', 'mytime' => '22:57:17'))),
+			array(
+				'Apple' => array('id' => 3),
+				'Child' => array())
+		);
+		$result = $Apple->find('all', array('fields' => array('Apple.id'), 'conditions' => array('Apple.id <' => '4')));
+		//$this->assertEqual($result, $expected2);
+
+		$Apple->Sample->Behaviors->disable('Test');
+		$result = $Apple->find('all');
+		$this->assertEqual($result, $expected);
+
+		$Apple->Sample->Behaviors->attach('Test', array('before' => 'off', 'after' => 'on'));
+		//$this->assertIdentical($Apple->find('all'), array());
+
+		$Apple->Sample->Behaviors->attach('Test', array('after' => 'off'));
+		$this->assertEqual($Apple->find('all'), $expected);
+
+		$Apple->Sample->Behaviors->attach('Test', array('after' => 'test'));
+		$this->assertEqual($Apple->find('all'), $expected);
+
+		$Apple->Sample->Behaviors->attach('Test', array('after' => 'test2'));
+		$this->assertEqual($Apple->find('all'), $expected);
+
+		$Apple->Sample->Behaviors->attach('Test', array('after' => 'modify'));
+		$expected = array(
+			array('id' => '1', 'apple_id' => '2', 'color' => 'Red 1', 'name' => 'Red Apple 1', 'created' => '2006-11-22 10:38:58', 'date' => '1951-01-04', 'modified' => '2006-12-01 13:31:26', 'mytime' => '22:57:17'),
+			array('id' => '2', 'apple_id' => '1', 'color' => 'Bright Red 1', 'name' => 'Bright Red Apple', 'created' => '2006-11-22 10:43:13', 'date' => '2014-01-01', 'modified' => '2006-11-30 18:38:10', 'mytime' => '22:57:17'),
+			array('id' => '3', 'apple_id' => '2', 'color' => 'blue green', 'name' => 'green blue', 'created' => '2006-12-25 05:13:36', 'date' => '2006-12-25', 'modified' => '2006-12-25 05:23:24', 'mytime' => '22:57:17'),
+			array('id' => '4', 'apple_id' => '2', 'color' => 'Blue Green', 'name' => 'Test Name', 'created' => '2006-12-25 05:23:36', 'date' => '2006-12-25', 'modified' => '2006-12-25 05:23:36', 'mytime' => '22:57:17'),
+			array('id' => '5', 'apple_id' => '5', 'color' => 'Green', 'name' => 'Blue Green', 'created' => '2006-12-25 05:24:06', 'date' => '2006-12-25', 'modified' => '2006-12-25 05:29:16', 'mytime' => '22:57:17'),
+			array('id' => '6', 'apple_id' => '4', 'color' => 'My new appleOrange', 'name' => 'My new apple', 'created' => '2006-12-25 05:29:39', 'date' => '2006-12-25', 'modified' => '2006-12-25 05:29:39', 'mytime' => '22:57:17'),
+			array('id' => '7', 'apple_id' => '6', 'color' => 'Some wierd color', 'name' => 'Some odd color', 'created' => '2006-12-25 05:34:21', 'date' => '2006-12-25', 'modified' => '2006-12-25 05:34:21', 'mytime' => '22:57:17')
+		);
+		//$this->assertEqual($Apple->find('all'), $expected);
+	}
+	/**
+ * testBehaviorBelongsToFindCallbacks method
+ *
+ * @access public
+ * @return void
+ */
+	function testBehaviorBelongsToFindCallbacks() {
+		$Apple = new Apple();
+		$Apple->unbindModel(array('hasMany' => array('Child'), 'hasOne' => array('Sample')), false);
+		$expected = $Apple->find('all');
+
+		$Apple->unbindModel(array('belongsTo' => array('Parent')));
+		$wellBehaved = $Apple->find('all');
+		$Apple->Parent->Behaviors->attach('Test');
+		$Apple->unbindModel(array('belongsTo' => array('Parent')));
+		$this->assertIdentical($Apple->find('all'), $wellBehaved);
+
+		$Apple->Parent->Behaviors->attach('Test', array('before' => 'off'));
+		$this->assertIdentical($Apple->find('all'), $expected);
+
+		$Apple->Parent->Behaviors->attach('Test', array('before' => 'test'));
+		$this->assertIdentical($Apple->find('all'), $expected);
+
+		$Apple->Parent->Behaviors->attach('Test', array('before' => 'modify'));
+		$expected2 = array(
+			array(
+				'Apple' => array('id' => 1),
+				'Parent' => array('id' => 2,'name' => 'Bright Red Apple', 'mytime' => '22:57:17')),
+			array(
+				'Apple' => array('id' => 2),
+				'Parent' => array('id' => 1, 'name' => 'Red Apple 1', 'mytime' => '22:57:17')),
+			array(
+				'Apple' => array('id' => 3),
+				'Parent' => array('id' => 2,'name' => 'Bright Red Apple', 'mytime' => '22:57:17'))
+		);
+		$result = $Apple->find('all', array(
+			'fields' => array('Apple.id', 'Parent.id', 'Parent.name', 'Parent.mytime'),
+			'conditions' => array('Apple.id <' => '4')
+		));
+		$this->assertEqual($result, $expected2);
+
+		$Apple->Parent->Behaviors->disable('Test');
+		$result = $Apple->find('all');
+		$this->assertEqual($result, $expected);
+
+		$Apple->Parent->Behaviors->attach('Test', array('after' => 'off'));
+		$this->assertEqual($Apple->find('all'), $expected);
+
+		$Apple->Parent->Behaviors->attach('Test', array('after' => 'test'));
+		$this->assertEqual($Apple->find('all'), $expected);
+
+		$Apple->Parent->Behaviors->attach('Test', array('after' => 'test2'));
+		$this->assertEqual($Apple->find('all'), $expected);
+
+		$Apple->Parent->Behaviors->attach('Test', array('after' => 'modify'));
+		$expected = array(
+			array('id' => '1', 'apple_id' => '2', 'color' => 'Red 1', 'name' => 'Red Apple 1', 'created' => '2006-11-22 10:38:58', 'date' => '1951-01-04', 'modified' => '2006-12-01 13:31:26', 'mytime' => '22:57:17'),
+			array('id' => '2', 'apple_id' => '1', 'color' => 'Bright Red 1', 'name' => 'Bright Red Apple', 'created' => '2006-11-22 10:43:13', 'date' => '2014-01-01', 'modified' => '2006-11-30 18:38:10', 'mytime' => '22:57:17'),
+			array('id' => '3', 'apple_id' => '2', 'color' => 'blue green', 'name' => 'green blue', 'created' => '2006-12-25 05:13:36', 'date' => '2006-12-25', 'modified' => '2006-12-25 05:23:24', 'mytime' => '22:57:17'),
+			array('id' => '4', 'apple_id' => '2', 'color' => 'Blue Green', 'name' => 'Test Name', 'created' => '2006-12-25 05:23:36', 'date' => '2006-12-25', 'modified' => '2006-12-25 05:23:36', 'mytime' => '22:57:17'),
+			array('id' => '5', 'apple_id' => '5', 'color' => 'Green', 'name' => 'Blue Green', 'created' => '2006-12-25 05:24:06', 'date' => '2006-12-25', 'modified' => '2006-12-25 05:29:16', 'mytime' => '22:57:17'),
+			array('id' => '6', 'apple_id' => '4', 'color' => 'My new appleOrange', 'name' => 'My new apple', 'created' => '2006-12-25 05:29:39', 'date' => '2006-12-25', 'modified' => '2006-12-25 05:29:39', 'mytime' => '22:57:17'),
+			array('id' => '7', 'apple_id' => '6', 'color' => 'Some wierd color', 'name' => 'Some odd color', 'created' => '2006-12-25 05:34:21', 'date' => '2006-12-25', 'modified' => '2006-12-25 05:34:21', 'mytime' => '22:57:17')
+		);
+		//$this->assertEqual($Apple->find('all'), $expected);
+	}
+
+/**
+ * testBehaviorSaveCallbacks method
+ *
+ * @access public
+ * @return void
+ */
+	function testBehaviorSaveCallbacks() {
+		$Sample = new Sample();
+		$record = array('Sample' => array('apple_id' => 6, 'name' => 'sample99'));
+
+		$Sample->Behaviors->attach('Test', array('beforeSave' => 'on'));
+		$Sample->create();
+		$this->assertIdentical($Sample->save($record), false);
+
+		$Sample->Behaviors->attach('Test', array('beforeSave' => 'off'));
+		$Sample->create();
+		$this->assertIdentical($Sample->save($record), $record);
+
+		$Sample->Behaviors->attach('Test', array('beforeSave' => 'test'));
+		$Sample->create();
+		$this->assertIdentical($Sample->save($record), $record);
+
+		$Sample->Behaviors->attach('Test', array('beforeSave' => 'modify'));
+		$expected = Set::insert($record, 'Sample.name', 'sample99 modified before');
+		$Sample->create();
+		$this->assertIdentical($Sample->save($record), $expected);
+
+		$Sample->Behaviors->disable('Test');
+		$this->assertIdentical($Sample->save($record), $record);
+
+		$Sample->Behaviors->attach('Test', array('beforeSave' => 'off', 'afterSave' => 'on'));
+		$expected = Set::merge($record, array('Sample' => array('aftersave' => 'modified after on create')));
+		$Sample->create();
+		$this->assertIdentical($Sample->save($record), $expected);
+
+		$Sample->Behaviors->attach('Test', array('beforeSave' => 'modify', 'afterSave' => 'modify'));
+		$expected = Set::merge($record, array('Sample' => array('name' => 'sample99 modified before modified after on create')));
+		$Sample->create();
+		$this->assertIdentical($Sample->save($record), $expected);
+
+		$Sample->Behaviors->attach('Test', array('beforeSave' => 'off', 'afterSave' => 'test'));
+		$Sample->create();
+		$this->assertIdentical($Sample->save($record), $record);
+
+		$Sample->Behaviors->attach('Test', array('afterSave' => 'test2'));
+		$Sample->create();
+		$this->assertIdentical($Sample->save($record), $record);
+
+		$Sample->Behaviors->attach('Test', array('beforeFind' => 'off', 'afterFind' => 'off'));
+		$Sample->recursive = -1;
+		$record2 = $Sample->read(null, 1);
+
+		$Sample->Behaviors->attach('Test', array('afterSave' => 'on'));
+		$expected = Set::merge($record2, array('Sample' => array('aftersave' => 'modified after')));
+		$Sample->create();
+		$this->assertIdentical($Sample->save($record2), $expected);
+
+		$Sample->Behaviors->attach('Test', array('afterSave' => 'modify'));
+		$expected = Set::merge($record2, array('Sample' => array('name' => 'sample1 modified after')));
+		$Sample->create();
+		$this->assertIdentical($Sample->save($record2), $expected);
+	}
+
+/**
+ * testBehaviorDeleteCallbacks method
+ *
+ * @access public
+ * @return void
+ */
+	function testBehaviorDeleteCallbacks() {
+		$Apple = new Apple();
+
+		$Apple->Behaviors->attach('Test', array('beforeFind' => 'off', 'beforeDelete' => 'off'));
+		$this->assertIdentical($Apple->delete(6), true);
+
+		$Apple->Behaviors->attach('Test', array('beforeDelete' => 'on'));
+		$this->assertIdentical($Apple->delete(4), false);
+
+		$Apple->Behaviors->attach('Test', array('beforeDelete' => 'test2'));
+		if (ob_start()) {
+			$results = $Apple->delete(4);
+			$this->assertIdentical(trim(ob_get_clean()), 'beforeDelete success (cascading)');
+			$this->assertIdentical($results, true);
+		}
+		if (ob_start()) {
+			$results = $Apple->delete(3, false);
+			$this->assertIdentical(trim(ob_get_clean()), 'beforeDelete success');
+			$this->assertIdentical($results, true);
+		}
+
+		$Apple->Behaviors->attach('Test', array('beforeDelete' => 'off', 'afterDelete' => 'on'));
+		if (ob_start()) {
+			$results = $Apple->delete(2, false);
+			$this->assertIdentical(trim(ob_get_clean()), 'afterDelete success');
+			$this->assertIdentical($results, true);
+		}
+	}
+	/**
+ * testBehaviorOnErrorCallback method
+ *
+ * @access public
+ * @return void
+ */
+	function testBehaviorOnErrorCallback() {
+		$Apple = new Apple();
+
+		$Apple->Behaviors->attach('Test', array('beforeFind' => 'off', 'onError' => 'on'));
+		if (ob_start()) {
+			$Apple->Behaviors->Test->onError($Apple);
+			$this->assertIdentical(trim(ob_get_clean()), 'onError trigger success');
+		}
+
+		if (ob_start()) {
+			$Apple->delete(99);
+			//$this->assertIdentical(trim(ob_get_clean()), 'onError trigger success');
+		}
+	}
+	/**
+ * testBehaviorValidateCallback method
+ *
+ * @access public
+ * @return void
+ */
+	function testBehaviorValidateCallback() {
+		$Apple = new Apple();
+
+		$Apple->Behaviors->attach('Test');
+		$this->assertIdentical($Apple->validates(), true);
+
+		$Apple->Behaviors->attach('Test', array('validate' => 'on'));
+		$this->assertIdentical($Apple->validates(), false);
+		$this->assertIdentical($Apple->validationErrors, array('name' => true));
+
+		$Apple->Behaviors->attach('Test', array('validate' => 'stop'));
+		$this->assertIdentical($Apple->validates(), false);
+		$this->assertIdentical($Apple->validationErrors, array('name' => true));
+
+		$Apple->Behaviors->attach('Test', array('validate' => 'whitelist'));
+		$Apple->validates();
+		$this->assertIdentical($Apple->whitelist, array());
+
+		$Apple->whitelist = array('unknown');
+		$Apple->validates();
+		$this->assertIdentical($Apple->whitelist, array('unknown', 'name'));
+	}
+
+/**
+ * testBehaviorValidateMethods method
+ *
+ * @access public
+ * @return void
+ */
+	function testBehaviorValidateMethods() {
+		$Apple = new Apple();
+		$Apple->Behaviors->attach('Test');
+		$Apple->validate['color'] = 'validateField';
+
+		$result = $Apple->save(array('name' => 'Genetically Modified Apple', 'color' => 'Orange'));
+		$this->assertEqual(array_keys($result['Apple']), array('name', 'color', 'modified', 'created'));
+
+		$Apple->create();
+		$result = $Apple->save(array('name' => 'Regular Apple', 'color' => 'Red'));
+		$this->assertFalse($result);
+	}
+
+/**
+ * testBehaviorMethodDispatching method
+ *
+ * @access public
+ * @return void
+ */
+	function testBehaviorMethodDispatching() {
+		$Apple = new Apple();
+		$Apple->Behaviors->attach('Test');
+
+		$expected = 'working';
+		$this->assertEqual($Apple->testMethod(), $expected);
+		$this->assertEqual($Apple->Behaviors->dispatchMethod($Apple, 'testMethod'), $expected);
+
+		$result = $Apple->Behaviors->dispatchMethod($Apple, 'wtf');
+		$this->assertEqual($result, array('unhandled'));
+
+		$result = $Apple->{'look for the remote'}('in the couch');
+		$expected = "Item.name = 'the remote' AND Location.name = 'the couch'";
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testBehaviorMethodDispatchingWithData method
+ *
+ * @access public
+ * @return void
+ */
+	function testBehaviorMethodDispatchingWithData() {
+		$Apple = new Apple();
+		$Apple->Behaviors->attach('Test');
+
+		$Apple->set('field', 'value');
+		$this->assertTrue($Apple->testData());
+		$this->assertTrue($Apple->data['Apple']['field_2']);
+
+		$this->assertTrue($Apple->testData('one', 'two', 'three', 'four', 'five', 'six'));
+	}
+
+/**
+ * testBehaviorTrigger method
+ *
+ * @access public
+ * @return void
+ */
+	function testBehaviorTrigger() {
+		$Apple =& new Apple();
+		$Apple->Behaviors->attach('Test');
+		$Apple->Behaviors->attach('Test2');
+		$Apple->Behaviors->attach('Test3');
+
+		$Apple->beforeTestResult = array();
+		$Apple->Behaviors->trigger($Apple, 'beforeTest');
+		$expected = array('testbehavior', 'test2behavior', 'test3behavior');
+		$this->assertIdentical($Apple->beforeTestResult, $expected);
+
+		$Apple->beforeTestResult = array();
+		$Apple->Behaviors->trigger($Apple, 'beforeTest', array(), array('break' => true, 'breakOn' => 'test2behavior'));
+		$expected = array('testbehavior', 'test2behavior');
+		$this->assertIdentical($Apple->beforeTestResult, $expected);
+
+		$Apple->beforeTestResult = array();
+		$Apple->Behaviors->trigger($Apple, 'beforeTest', array(), array('break' => true, 'breakOn' => array('test2behavior', 'test3behavior')));
+		$expected = array('testbehavior', 'test2behavior');
+		$this->assertIdentical($Apple->beforeTestResult, $expected);
+	}
+
+/**
+ * undocumented function
+ *
+ * @return void
+ * @access public
+ */
+	function testBindModelCallsInBehaviors() {
+		$this->loadFixtures('Article', 'Comment');
+
+		// hasMany
+		$Article = new Article();
+		$Article->unbindModel(array('hasMany' => array('Comment')));
+		$result = $Article->find('first');
+		$this->assertFalse(array_key_exists('Comment', $result));
+
+		$Article->Behaviors->attach('Test4');
+		$result = $Article->find('first');
+		$this->assertTrue(array_key_exists('Comment', $result));
+
+		// belongsTo
+		$Article->unbindModel(array('belongsTo' => array('User')));
+		$result = $Article->find('first');
+		$this->assertFalse(array_key_exists('User', $result));
+
+		$Article->Behaviors->attach('Test5');
+		$result = $Article->find('first');
+		$this->assertTrue(array_key_exists('User', $result));
+
+		// hasAndBelongsToMany
+		$Article->unbindModel(array('hasAndBelongsToMany' => array('Tag')));
+		$result = $Article->find('first');
+		$this->assertFalse(array_key_exists('Tag', $result));
+
+		$Article->Behaviors->attach('Test6');
+		$result = $Article->find('first');
+		$this->assertTrue(array_key_exists('Comment', $result));
+
+		// hasOne
+		$Comment = new Comment();
+		$Comment->unbindModel(array('hasOne' => array('Attachment')));
+		$result = $Comment->find('first');
+		$this->assertFalse(array_key_exists('Attachment', $result));
+
+		$Comment->Behaviors->attach('Test7');
+		$result = $Comment->find('first');
+		$this->assertTrue(array_key_exists('Attachment', $result));
+	}
+
+/**
+ * Test attach and detaching
+ *
+ * @access public
+ * @return void
+ */
+	function testBehaviorAttachAndDetach() {
+		$Sample =& new Sample();
+		$Sample->actsAs = array('Test3' => array('bar'), 'Test2' => array('foo', 'bar'));
+		$Sample->Behaviors->init($Sample->alias, $Sample->actsAs);
+		$Sample->Behaviors->attach('Test2');
+		$Sample->Behaviors->detach('Test3');
+
+		$Sample->Behaviors->trigger($Sample, 'beforeTest');
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/model/model_delete.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/model/model_delete.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/model/model_delete.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,834 @@
+<?php
+/**
+ * ModelDeleteTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+require_once dirname(__FILE__) . DS . 'model.test.php';
+
+/**
+ * ModelDeleteTest
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.operations
+ */
+class ModelDeleteTest extends BaseModelTest {
+
+/**
+ * testDeleteHabtmReferenceWithConditions method
+ *
+ * @access public
+ * @return void
+ */
+	function testDeleteHabtmReferenceWithConditions() {
+		$this->loadFixtures('Portfolio', 'Item', 'ItemsPortfolio');
+
+		$Portfolio =& new Portfolio();
+		$Portfolio->hasAndBelongsToMany['Item']['conditions'] = array('ItemsPortfolio.item_id >' => 1);
+
+		$result = $Portfolio->find('first', array(
+			'conditions' => array('Portfolio.id' => 1)
+		));
+		$expected = array(
+			array(
+				'id' => 3,
+				'syfile_id' => 3,
+				'published' => 0,
+				'name' => 'Item 3',
+				'ItemsPortfolio' => array(
+					'id' => 3,
+					'item_id' => 3,
+					'portfolio_id' => 1
+			)),
+			array(
+				'id' => 4,
+				'syfile_id' => 4,
+				'published' => 0,
+				'name' => 'Item 4',
+				'ItemsPortfolio' => array(
+					'id' => 4,
+					'item_id' => 4,
+					'portfolio_id' => 1
+			)),
+			array(
+				'id' => 5,
+				'syfile_id' => 5,
+				'published' => 0,
+				'name' => 'Item 5',
+				'ItemsPortfolio' => array(
+					'id' => 5,
+					'item_id' => 5,
+					'portfolio_id' => 1
+		)));
+		$this->assertEqual($result['Item'], $expected);
+
+		$result = $Portfolio->ItemsPortfolio->find('all', array(
+			'conditions' => array('ItemsPortfolio.portfolio_id' => 1)
+		));
+		$expected = array(
+			array(
+				'ItemsPortfolio' => array(
+					'id' => 1,
+					'item_id' => 1,
+					'portfolio_id' => 1
+			)),
+			array(
+				'ItemsPortfolio' => array(
+					'id' => 3,
+					'item_id' => 3,
+					'portfolio_id' => 1
+			)),
+			array(
+				'ItemsPortfolio' => array(
+					'id' => 4,
+					'item_id' => 4,
+					'portfolio_id' => 1
+			)),
+			array(
+				'ItemsPortfolio' => array(
+					'id' => 5,
+					'item_id' => 5,
+					'portfolio_id' => 1
+		)));
+		$this->assertEqual($result, $expected);
+
+		$Portfolio->delete(1);
+
+		$result = $Portfolio->find('first', array(
+			'conditions' => array('Portfolio.id' => 1)
+		));
+		$this->assertFalse($result);
+
+		$result = $Portfolio->ItemsPortfolio->find('all', array(
+			'conditions' => array('ItemsPortfolio.portfolio_id' => 1)
+		));
+		$this->assertFalse($result);
+	}
+
+/**
+ * testDeleteArticleBLinks method
+ *
+ * @access public
+ * @return void
+ */
+	function testDeleteArticleBLinks() {
+		$this->loadFixtures('Article', 'ArticlesTag', 'Tag');
+		$TestModel =& new ArticleB();
+
+		$result = $TestModel->ArticlesTag->find('all');
+		$expected = array(
+			array('ArticlesTag' => array('article_id' => '1', 'tag_id' => '1')),
+			array('ArticlesTag' => array('article_id' => '1', 'tag_id' => '2')),
+			array('ArticlesTag' => array('article_id' => '2', 'tag_id' => '1')),
+			array('ArticlesTag' => array('article_id' => '2', 'tag_id' => '3'))
+			);
+		$this->assertEqual($result, $expected);
+
+		$TestModel->delete(1);
+		$result = $TestModel->ArticlesTag->find('all');
+
+		$expected = array(
+			array('ArticlesTag' => array('article_id' => '2', 'tag_id' => '1')),
+			array('ArticlesTag' => array('article_id' => '2', 'tag_id' => '3'))
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testDeleteDependentWithConditions method
+ *
+ * @access public
+ * @return void
+ */
+	function testDeleteDependentWithConditions() {
+		$this->loadFixtures('Cd','Book','OverallFavorite');
+
+		$Cd =& new Cd();
+		$Book =& new Book();
+		$OverallFavorite =& new OverallFavorite();
+
+		$Cd->delete(1);
+
+		$result = $OverallFavorite->find('all', array(
+			'fields' => array('model_type', 'model_id', 'priority')
+		));
+		$expected = array(
+			array(
+				'OverallFavorite' => array(
+					'model_type' => 'Book',
+					'model_id' => 1,
+					'priority' => 2
+		)));
+
+		$this->assertTrue(is_array($result));
+		$this->assertEqual($result, $expected);
+
+		$Book->delete(1);
+
+		$result = $OverallFavorite->find('all', array(
+			'fields' => array('model_type', 'model_id', 'priority')
+		));
+		$expected = array();
+
+		$this->assertTrue(is_array($result));
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testDel method
+ *
+ * @access public
+ * @return void
+ */
+	function testDelete() {
+		$this->loadFixtures('Article');
+		$TestModel =& new Article();
+
+		$result = $TestModel->delete(2);
+		$this->assertTrue($result);
+
+		$result = $TestModel->read(null, 2);
+		$this->assertFalse($result);
+
+		$TestModel->recursive = -1;
+		$result = $TestModel->find('all', array(
+			'fields' => array('id', 'title')
+		));
+		$expected = array(
+			array('Article' => array(
+				'id' => 1,
+				'title' => 'First Article'
+			)),
+			array('Article' => array(
+				'id' => 3,
+				'title' => 'Third Article'
+		)));
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->delete(3);
+		$this->assertTrue($result);
+
+		$result = $TestModel->read(null, 3);
+		$this->assertFalse($result);
+
+		$TestModel->recursive = -1;
+		$result = $TestModel->find('all', array(
+			'fields' => array('id', 'title')
+		));
+		$expected = array(
+			array('Article' => array(
+				'id' => 1,
+				'title' => 'First Article'
+		)));
+
+		$this->assertEqual($result, $expected);
+
+		// make sure deleting a non-existent record doesn't break save()
+		// ticket #6293
+		$this->loadFixtures('Uuid');
+		$Uuid =& new Uuid();
+		$data = array(
+			'B607DAB9-88A2-46CF-B57C-842CA9E3B3B3',
+			'52C8865C-10EE-4302-AE6C-6E7D8E12E2C8',
+			'8208C7FE-E89C-47C5-B378-DED6C271F9B8');
+		foreach ($data as $id) {
+			$Uuid->save(array('id' => $id));
+		}
+		$Uuid->delete('52C8865C-10EE-4302-AE6C-6E7D8E12E2C8');
+		$Uuid->delete('52C8865C-10EE-4302-AE6C-6E7D8E12E2C8');
+		foreach ($data as $id) {
+			$Uuid->save(array('id' => $id));
+		}
+		$result = $Uuid->find('all', array(
+			'conditions' => array('id' => $data),
+			'fields' => array('id'),
+			'order' => 'id'));
+		$expected = array(
+			array('Uuid' => array(
+				'id' => '52C8865C-10EE-4302-AE6C-6E7D8E12E2C8')),
+			array('Uuid' => array(
+				'id' => '8208C7FE-E89C-47C5-B378-DED6C271F9B8')),
+			array('Uuid' => array(
+				'id' => 'B607DAB9-88A2-46CF-B57C-842CA9E3B3B3')));
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that delete() updates the correct records counterCache() records.
+ *
+ * @return void
+ */
+	function testDeleteUpdatingCounterCacheCorrectly() {
+		$this->loadFixtures('CounterCacheUser', 'CounterCachePost');
+		$User =& new CounterCacheUser();
+
+		$User->Post->delete(3);
+		$result = $User->read(null, 301);
+		$this->assertEqual($result['User']['post_count'], 0);
+
+		$result = $User->read(null, 66);
+		$this->assertEqual($result['User']['post_count'], 2);
+	}
+
+/**
+ * testDeleteAll method
+ *
+ * @access public
+ * @return void
+ */
+	function testDeleteAll() {
+		$this->loadFixtures('Article');
+		$TestModel =& new Article();
+
+		$data = array('Article' => array(
+			'user_id' => 2,
+			'id' => 4,
+			'title' => 'Fourth Article',
+			'published' => 'N'
+		));
+		$result = $TestModel->set($data) && $TestModel->save();
+		$this->assertTrue($result);
+
+		$data = array('Article' => array(
+			'user_id' => 2,
+			'id' => 5,
+			'title' => 'Fifth Article',
+			'published' => 'Y'
+		));
+		$result = $TestModel->set($data) && $TestModel->save();
+		$this->assertTrue($result);
+
+		$data = array('Article' => array(
+			'user_id' => 1,
+			'id' => 6,
+			'title' => 'Sixth Article',
+			'published' => 'N'
+		));
+		$result = $TestModel->set($data) && $TestModel->save();
+		$this->assertTrue($result);
+
+		$TestModel->recursive = -1;
+		$result = $TestModel->find('all', array(
+			'fields' => array('id', 'user_id', 'title', 'published')
+		));
+
+		$expected = array(
+			array('Article' => array(
+				'id' => 1,
+				'user_id' => 1,
+				'title' => 'First Article',
+				'published' => 'Y'
+			)),
+			array('Article' => array(
+				'id' => 2,
+				'user_id' => 3,
+				'title' => 'Second Article',
+				'published' => 'Y'
+			)),
+			array('Article' => array(
+				'id' => 3,
+				'user_id' => 1,
+				'title' => 'Third Article',
+				'published' => 'Y')),
+			array('Article' => array(
+				'id' => 4,
+				'user_id' => 2,
+				'title' => 'Fourth Article',
+				'published' => 'N'
+			)),
+			array('Article' => array(
+				'id' => 5,
+				'user_id' => 2,
+				'title' => 'Fifth Article',
+				'published' => 'Y'
+			)),
+			array('Article' => array(
+				'id' => 6,
+				'user_id' => 1,
+				'title' => 'Sixth Article',
+				'published' => 'N'
+		)));
+
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->deleteAll(array('Article.published' => 'N'));
+		$this->assertTrue($result);
+
+		$TestModel->recursive = -1;
+		$result = $TestModel->find('all', array(
+			'fields' => array('id', 'user_id', 'title', 'published')
+		));
+		$expected = array(
+			array('Article' => array(
+				'id' => 1,
+				'user_id' => 1,
+				'title' => 'First Article',
+				'published' => 'Y'
+			)),
+			array('Article' => array(
+				'id' => 2,
+				'user_id' => 3,
+				'title' => 'Second Article',
+				'published' => 'Y'
+			)),
+			array('Article' => array(
+				'id' => 3,
+				'user_id' => 1,
+				'title' => 'Third Article',
+				'published' => 'Y'
+			)),
+			array('Article' => array(
+				'id' => 5,
+				'user_id' => 2,
+				'title' => 'Fifth Article',
+				'published' => 'Y'
+		)));
+		$this->assertEqual($result, $expected);
+
+		$data = array('Article.user_id' => array(2, 3));
+		$result = $TestModel->deleteAll($data, true, true);
+		$this->assertTrue($result);
+
+		$TestModel->recursive = -1;
+		$result = $TestModel->find('all', array(
+			'fields' => array('id', 'user_id', 'title', 'published')
+		));
+		$expected = array(
+			array('Article' => array(
+				'id' => 1,
+				'user_id' => 1,
+				'title' => 'First Article',
+				'published' => 'Y'
+			)),
+			array('Article' => array(
+				'id' => 3,
+				'user_id' => 1,
+				'title' => 'Third Article',
+				'published' => 'Y'
+		)));
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->deleteAll(array('Article.user_id' => 999));
+		$this->assertTrue($result, 'deleteAll returned false when all no records matched conditions. %s');
+
+		$this->expectError();
+		ob_start();
+		$result = $TestModel->deleteAll(array('Article.non_existent_field' => 999));
+		ob_get_clean();
+		$this->assertFalse($result, 'deleteAll returned true when find query generated sql error. %s');
+	}
+
+/**
+ * testRecursiveDel method
+ *
+ * @access public
+ * @return void
+ */
+	function testRecursiveDel() {
+		$this->loadFixtures('Article', 'Comment', 'Attachment');
+		$TestModel =& new Article();
+
+		$result = $TestModel->delete(2);
+		$this->assertTrue($result);
+
+		$TestModel->recursive = 2;
+		$result = $TestModel->read(null, 2);
+		$this->assertFalse($result);
+
+		$result = $TestModel->Comment->read(null, 5);
+		$this->assertFalse($result);
+
+		$result = $TestModel->Comment->read(null, 6);
+		$this->assertFalse($result);
+
+		$result = $TestModel->Comment->Attachment->read(null, 1);
+		$this->assertFalse($result);
+
+		$result = $TestModel->find('count');
+		$this->assertEqual($result, 2);
+
+		$result = $TestModel->Comment->find('count');
+		$this->assertEqual($result, 4);
+
+		$result = $TestModel->Comment->Attachment->find('count');
+		$this->assertEqual($result, 0);
+	}
+
+/**
+ * testDependentExclusiveDelete method
+ *
+ * @access public
+ * @return void
+ */
+	function testDependentExclusiveDelete() {
+		$this->loadFixtures('Article', 'Comment');
+		$TestModel =& new Article10();
+
+		$result = $TestModel->find('all');
+		$this->assertEqual(count($result[0]['Comment']), 4);
+		$this->assertEqual(count($result[1]['Comment']), 2);
+		$this->assertEqual($TestModel->Comment->find('count'), 6);
+
+		$TestModel->delete(1);
+		$this->assertEqual($TestModel->Comment->find('count'), 2);
+	}
+
+/**
+ * testDeleteLinks method
+ *
+ * @access public
+ * @return void
+ */
+	function testDeleteLinks() {
+		$this->loadFixtures('Article', 'ArticlesTag', 'Tag');
+		$TestModel =& new Article();
+
+		$result = $TestModel->ArticlesTag->find('all');
+		$expected = array(
+			array('ArticlesTag' => array(
+				'article_id' => '1',
+				'tag_id' => '1'
+			)),
+			array('ArticlesTag' => array(
+				'article_id' => '1',
+				'tag_id' => '2'
+			)),
+			array('ArticlesTag' => array(
+				'article_id' => '2',
+				'tag_id' => '1'
+			)),
+			array('ArticlesTag' => array(
+				'article_id' => '2',
+				'tag_id' => '3'
+		)));
+		$this->assertEqual($result, $expected);
+
+		$TestModel->delete(1);
+		$result = $TestModel->ArticlesTag->find('all');
+
+		$expected = array(
+			array('ArticlesTag' => array(
+				'article_id' => '2',
+				'tag_id' => '1'
+			)),
+			array('ArticlesTag' => array(
+				'article_id' => '2',
+				'tag_id' => '3'
+		)));
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->deleteAll(array('Article.user_id' => 999));
+		$this->assertTrue($result, 'deleteAll returned false when all no records matched conditions. %s');
+	}
+
+/**
+ * testDeleteDependent method
+ *
+ * @access public
+ * @return void
+ */
+	function testDeleteDependent() {
+		$this->loadFixtures('Bidding', 'BiddingMessage');
+		$Bidding = new Bidding();
+		$result = $Bidding->find('all');
+		$expected = array(
+			array(
+				'Bidding' => array('id' => 1, 'bid' => 'One', 'name' => 'Bid 1'),
+				'BiddingMessage' => array('bidding' => 'One', 'name' => 'Message 1'),
+			),
+			array(
+				'Bidding' => array('id' => 2, 'bid' => 'Two', 'name' => 'Bid 2'),
+				'BiddingMessage' => array('bidding' => 'Two', 'name' => 'Message 2'),
+			),
+			array(
+				'Bidding' => array('id' => 3, 'bid' => 'Three', 'name' => 'Bid 3'),
+				'BiddingMessage' => array('bidding' => 'Three', 'name' => 'Message 3'),
+			),
+			array(
+				'Bidding' => array('id' => 4, 'bid' => 'Five', 'name' => 'Bid 5'),
+				'BiddingMessage' => array('bidding' => '', 'name' => ''),
+			),
+		);
+		$this->assertEqual($result, $expected);
+
+		$Bidding->delete(4, true);
+		$result = $Bidding->find('all');
+		$expected = array(
+			array(
+				'Bidding' => array('id' => 1, 'bid' => 'One', 'name' => 'Bid 1'),
+				'BiddingMessage' => array('bidding' => 'One', 'name' => 'Message 1'),
+			),
+			array(
+				'Bidding' => array('id' => 2, 'bid' => 'Two', 'name' => 'Bid 2'),
+				'BiddingMessage' => array('bidding' => 'Two', 'name' => 'Message 2'),
+			),
+			array(
+				'Bidding' => array('id' => 3, 'bid' => 'Three', 'name' => 'Bid 3'),
+				'BiddingMessage' => array('bidding' => 'Three', 'name' => 'Message 3'),
+			),
+		);
+		$this->assertEqual($result, $expected);
+
+		$Bidding->delete(2, true);
+		$result = $Bidding->find('all');
+		$expected = array(
+			array(
+				'Bidding' => array('id' => 1, 'bid' => 'One', 'name' => 'Bid 1'),
+				'BiddingMessage' => array('bidding' => 'One', 'name' => 'Message 1'),
+			),
+			array(
+				'Bidding' => array('id' => 3, 'bid' => 'Three', 'name' => 'Bid 3'),
+				'BiddingMessage' => array('bidding' => 'Three', 'name' => 'Message 3'),
+			),
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $Bidding->BiddingMessage->find('all', array('order' => array('BiddingMessage.name' => 'ASC')));
+		$expected = array(
+			array(
+				'BiddingMessage' => array('bidding' => 'One', 'name' => 'Message 1'),
+				'Bidding' => array('id' => 1, 'bid' => 'One', 'name' => 'Bid 1'),
+			),
+			array(
+				'BiddingMessage' => array('bidding' => 'Three', 'name' => 'Message 3'),
+				'Bidding' => array('id' => 3, 'bid' => 'Three', 'name' => 'Bid 3'),
+			),
+			array(
+				'BiddingMessage' => array('bidding' => 'Four', 'name' => 'Message 4'),
+				'Bidding' => array('id' => '', 'bid' => '', 'name' => ''),
+			),
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test deleteLinks with Multiple habtm associations
+ *
+ * @return void
+ */
+	function testDeleteLinksWithMultipleHabtmAssociations() {
+		$this->loadFixtures('JoinA', 'JoinB', 'JoinC', 'JoinAB', 'JoinAC');
+		$JoinA =& new JoinA();
+
+		//create two new join records to expose the issue.
+		$JoinA->JoinAsJoinC->create(array(
+			'join_a_id' => 1,
+			'join_c_id' => 2,
+		));
+		$JoinA->JoinAsJoinC->save();
+		$JoinA->JoinAsJoinB->create(array(
+			'join_a_id' => 1,
+			'join_b_id' => 2,
+		));
+		$JoinA->JoinAsJoinB->save();
+
+		$result = $JoinA->delete(1);
+		$this->assertTrue($result, 'Delete failed %s');
+
+		$joinedBs = $JoinA->JoinAsJoinB->find('count', array(
+			'conditions' => array('JoinAsJoinB.join_a_id' => 1)
+		));
+		$this->assertEqual($joinedBs, 0, 'JoinA/JoinB link records left over. %s');
+
+		$joinedBs = $JoinA->JoinAsJoinC->find('count', array(
+			'conditions' => array('JoinAsJoinC.join_a_id' => 1)
+		));
+		$this->assertEqual($joinedBs, 0, 'JoinA/JoinC link records left over. %s');
+	}
+
+/**
+ * testHabtmDeleteLinksWhenNoPrimaryKeyInJoinTable method
+ *
+ * @access public
+ * @return void
+ */
+	function testHabtmDeleteLinksWhenNoPrimaryKeyInJoinTable() {
+
+		$this->loadFixtures('Apple', 'Device', 'ThePaperMonkies');
+		$ThePaper =& new ThePaper();
+		$ThePaper->id = 1;
+		$ThePaper->save(array('Monkey' => array(2, 3)));
+
+		$result = $ThePaper->findById(1);
+		$expected = array(
+			array(
+				'id' => '2',
+				'device_type_id' => '1',
+				'name' => 'Device 2',
+				'typ' => '1'
+			),
+			array(
+				'id' => '3',
+				'device_type_id' => '1',
+				'name' => 'Device 3',
+				'typ' => '2'
+		));
+		$this->assertEqual($result['Monkey'], $expected);
+
+		$ThePaper =& new ThePaper();
+		$ThePaper->id = 2;
+		$ThePaper->save(array('Monkey' => array(2, 3)));
+
+		$result = $ThePaper->findById(2);
+		$expected = array(
+			array(
+				'id' => '2',
+				'device_type_id' => '1',
+				'name' => 'Device 2',
+				'typ' => '1'
+			),
+			array(
+				'id' => '3',
+				'device_type_id' => '1',
+				'name' => 'Device 3',
+				'typ' => '2'
+		));
+		$this->assertEqual($result['Monkey'], $expected);
+
+		$ThePaper->delete(1);
+		$result = $ThePaper->findById(2);
+		$expected = array(
+			array(
+				'id' => '2',
+				'device_type_id' => '1',
+				'name' => 'Device 2',
+				'typ' => '1'
+			),
+			array(
+				'id' => '3',
+				'device_type_id' => '1',
+				'name' => 'Device 3',
+				'typ' => '2'
+		));
+		$this->assertEqual($result['Monkey'], $expected);
+	}
+
+/**
+ * test that beforeDelete returning false can abort deletion.
+ *
+ * @return void
+ */
+	function testBeforeDeleteDeleteAbortion() {
+		$this->loadFixtures('Post');
+		$Model =& new CallbackPostTestModel();
+		$Model->beforeDeleteReturn = false;
+
+		$result = $Model->delete(1);
+		$this->assertFalse($result);
+
+		$exists = $Model->findById(1);
+		$this->assertTrue(is_array($exists));
+	}
+
+/**
+ * test for a habtm deletion error that occurs in postgres but should not.
+ * And should not occur in any dbo.
+ *
+ * @return void
+ */
+	function testDeleteHabtmPostgresFailure() {
+		$this->loadFixtures('Article', 'Tag', 'ArticlesTag');
+
+		$Article =& ClassRegistry::init('Article');
+		$Article->hasAndBelongsToMany['Tag']['unique'] = true;
+
+		$Tag =& ClassRegistry::init('Tag');
+		$Tag->bindModel(array('hasAndBelongsToMany' => array(
+			'Article' => array(
+				'className' => 'Article',
+				'unique' => true
+			)
+		)), true);
+
+		// Article 1 should have Tag.1 and Tag.2
+	    $before = $Article->find("all", array(
+			"conditions" => array("Article.id" => 1),
+		));
+		$this->assertEqual(count($before[0]['Tag']), 2, 'Tag count for Article.id = 1 is incorrect, should be 2 %s');
+
+		// From now on, Tag #1 is only associated with Post #1
+		$submitted_data = array(
+			"Tag" => array("id" => 1, 'tag' => 'tag1'),
+			"Article" => array(
+				"Article" => array(1)
+			)
+		);
+		$Tag->save($submitted_data);
+
+	    // One more submission (The other way around) to make sure the reverse save looks good.
+	    $submitted_data = array(
+			"Article" => array("id" => 2, 'title' => 'second article'),
+			"Tag" => array(
+				"Tag" => array(2, 3)
+			)
+		);
+	    // ERROR:
+	    // Postgresql: DELETE FROM "articles_tags" WHERE tag_id IN ('1', '3')
+	    // MySQL: DELETE `ArticlesTag` FROM `articles_tags` AS `ArticlesTag` WHERE `ArticlesTag`.`article_id` = 2 AND `ArticlesTag`.`tag_id` IN (1, 3)
+	    $Article->save($submitted_data);
+
+		// Want to make sure Article #1 has Tag #1 and Tag #2 still.
+		$after = $Article->find("all", array(
+			"conditions" => array("Article.id" => 1),
+		));
+
+		// Removing Article #2 from Tag #1 is all that should have happened.
+		$this->assertEqual(count($before[0]["Tag"]), count($after[0]["Tag"]));
+	}
+
+/**
+ * test that deleting records inside the beforeDelete doesn't truncate the table.
+ *
+ * @return void
+ */
+	function testBeforeDeleteWipingTable() {
+		$this->loadFixtures('Comment');
+
+		$Comment =& new BeforeDeleteComment();
+		// Delete 3 records.
+		$Comment->delete(4);
+		$result = $Comment->find('count');
+
+		$this->assertTrue($result > 1, 'Comments are all gone.');
+		$Comment->create(array(
+			'article_id' => 1,
+			'user_id' => 2,
+			'comment' => 'new record',
+			'published' => 'Y'
+		));
+		$Comment->save();
+
+		$Comment->delete(5);
+		$result = $Comment->find('count');
+
+		$this->assertTrue($result > 1, 'Comments are all gone.');
+	}
+
+/**
+ * test that deleting the same record from the beforeDelete and the delete doesn't truncate the table.
+ *
+ * @return void
+ */
+	function testBeforeDeleteWipingTableWithDuplicateDelete() {
+		$this->loadFixtures('Comment');
+
+		$Comment =& new BeforeDeleteComment();
+		$Comment->delete(1);
+
+		$result = $Comment->find('count');
+		$this->assertTrue($result > 1, 'Comments are all gone.');
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/model/model_integration.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/model/model_integration.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/model/model_integration.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,1968 @@
+<?php
+/* SVN FILE: $Id: model.test.php 8225 2009-07-08 03:25:30Z mark_story $ */
+
+/**
+ * ModelIntegrationTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+require_once dirname(__FILE__) . DS . 'model.test.php';
+App::import('Core', 'DboSource');
+
+/**
+ * DboMock class
+ * A Dbo Source driver to mock a connection and a identity name() method
+ */
+class DboMock extends DboSource {
+
+/**
+* Returns the $field without modifications
+*/
+	function name($field) {
+		return $field;
+	}
+/**
+* Returns true to fake a database connection
+*/
+	function connect() {
+		return true;
+	}
+}
+
+/**
+ * ModelIntegrationTest
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.operations
+ */
+class ModelIntegrationTest extends BaseModelTest {
+
+/**
+ * testPkInHAbtmLinkModelArticleB
+ *
+ * @access public
+ * @return void
+ */
+	function testPkInHabtmLinkModelArticleB() {
+		$this->loadFixtures('Article', 'Tag');
+		$TestModel2 =& new ArticleB();
+		$this->assertEqual($TestModel2->ArticlesTag->primaryKey, 'article_id');
+	}
+
+/**
+ * Tests that $cacheSources can only be disabled in the db using model settings, not enabled
+ *
+ * @access public
+ * @return void
+ */
+	function testCacheSourcesDisabling() {
+		$this->db->cacheSources = true;
+		$TestModel = new JoinA();
+		$TestModel->cacheSources = false;
+		$TestModel->setSource('join_as');
+		$this->assertFalse($this->db->cacheSources);
+
+		$this->db->cacheSources = false;
+		$TestModel = new JoinA();
+		$TestModel->cacheSources = true;
+		$TestModel->setSource('join_as');
+		$this->assertFalse($this->db->cacheSources);
+	}
+
+/**
+ * testPkInHabtmLinkModel method
+ *
+ * @access public
+	 * @return void
+ */
+	function testPkInHabtmLinkModel() {
+		//Test Nonconformant Models
+		$this->loadFixtures('Content', 'ContentAccount', 'Account');
+		$TestModel =& new Content();
+		$this->assertEqual($TestModel->ContentAccount->primaryKey, 'iContentAccountsId');
+
+		//test conformant models with no PK in the join table
+		$this->loadFixtures('Article', 'Tag');
+		$TestModel2 =& new Article();
+		$this->assertEqual($TestModel2->ArticlesTag->primaryKey, 'article_id');
+
+		//test conformant models with PK in join table
+		$this->loadFixtures('Item', 'Portfolio', 'ItemsPortfolio');
+		$TestModel3 =& new Portfolio();
+		$this->assertEqual($TestModel3->ItemsPortfolio->primaryKey, 'id');
+
+		//test conformant models with PK in join table - join table contains extra field
+		$this->loadFixtures('JoinA', 'JoinB', 'JoinAB');
+		$TestModel4 =& new JoinA();
+		$this->assertEqual($TestModel4->JoinAsJoinB->primaryKey, 'id');
+
+	}
+
+/**
+ * testDynamicBehaviorAttachment method
+ *
+ * @access public
+ * @return void
+ */
+	function testDynamicBehaviorAttachment() {
+		$this->loadFixtures('Apple');
+		$TestModel =& new Apple();
+		$this->assertEqual($TestModel->Behaviors->attached(), array());
+
+		$TestModel->Behaviors->attach('Tree', array('left' => 'left_field', 'right' => 'right_field'));
+		$this->assertTrue(is_object($TestModel->Behaviors->Tree));
+		$this->assertEqual($TestModel->Behaviors->attached(), array('Tree'));
+
+		$expected = array(
+			'parent' => 'parent_id',
+			'left' => 'left_field',
+			'right' => 'right_field',
+			'scope' => '1 = 1',
+			'type' => 'nested',
+			'__parentChange' => false,
+			'recursive' => -1
+		);
+
+		$this->assertEqual($TestModel->Behaviors->Tree->settings['Apple'], $expected);
+
+		$expected['enabled'] = false;
+		$TestModel->Behaviors->attach('Tree', array('enabled' => false));
+		$this->assertEqual($TestModel->Behaviors->Tree->settings['Apple'], $expected);
+		$this->assertEqual($TestModel->Behaviors->attached(), array('Tree'));
+
+		$TestModel->Behaviors->detach('Tree');
+		$this->assertEqual($TestModel->Behaviors->attached(), array());
+		$this->assertFalse(isset($TestModel->Behaviors->Tree));
+	}
+
+/**
+ * testFindWithJoinsOption method
+ *
+ * @access public
+ * @return void
+ */
+	function testFindWithJoinsOption() {
+		$this->loadFixtures('Article', 'User');
+		$TestUser =& new User();
+
+		$options = array (
+			'fields' => array(
+				'user',
+				'Article.published',
+			),
+			'joins' => array (
+				array (
+					'table' => 'articles',
+					'alias' => 'Article',
+					'type'  => 'LEFT',
+					'conditions' => array(
+						'User.id = Article.user_id',
+					),
+				),
+			),
+			'group' => array('User.user'),
+			'recursive' => -1,
+		);
+		$result = $TestUser->find('all', $options);
+		$expected = array(
+			array('User' => array('user' => 'garrett'), 'Article' => array('published' => '')),
+			array('User' => array('user' => 'larry'), 'Article' => array('published' => 'Y')),
+			array('User' => array('user' => 'mariano'), 'Article' => array('published' => 'Y')),
+			array('User' => array('user' => 'nate'), 'Article' => array('published' => ''))
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * Tests cross database joins.  Requires $test and $test2 to both be set in DATABASE_CONFIG
+ * NOTE: When testing on MySQL, you must set 'persistent' => false on *both* database connections,
+ * or one connection will step on the other.
+ */
+	function testCrossDatabaseJoins() {
+		$config = new DATABASE_CONFIG();
+
+		$skip = $this->skipIf(
+			!isset($config->test) || !isset($config->test2),
+			 '%s Primary and secondary test databases not configured, skipping cross-database '
+			.'join tests.'
+			.' To run these tests, you must define $test and $test2 in your database configuration.'
+		);
+
+		if ($skip) {
+			return;
+		}
+
+		$this->loadFixtures('Article', 'Tag', 'ArticlesTag', 'User', 'Comment');
+		$TestModel =& new Article();
+
+		$expected = array(
+			array(
+				'Article' => array(
+					'id' => '1',
+					'user_id' => '1',
+					'title' => 'First Article',
+					'body' => 'First Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:39:23',
+					'updated' => '2007-03-18 10:41:31'
+				),
+				'User' => array(
+					'id' => '1',
+					'user' => 'mariano',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23',
+					'updated' => '2007-03-17 01:18:31'
+				),
+				'Comment' => array(
+					array(
+						'id' => '1',
+						'article_id' => '1',
+						'user_id' => '2',
+						'comment' => 'First Comment for First Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:45:23',
+						'updated' => '2007-03-18 10:47:31'
+					),
+					array(
+						'id' => '2',
+						'article_id' => '1',
+						'user_id' => '4',
+						'comment' => 'Second Comment for First Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:47:23',
+						'updated' => '2007-03-18 10:49:31'
+					),
+					array(
+						'id' => '3',
+						'article_id' => '1',
+						'user_id' => '1',
+						'comment' => 'Third Comment for First Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:49:23',
+						'updated' => '2007-03-18 10:51:31'
+					),
+					array(
+						'id' => '4',
+						'article_id' => '1',
+						'user_id' => '1',
+						'comment' => 'Fourth Comment for First Article',
+						'published' => 'N',
+						'created' => '2007-03-18 10:51:23',
+						'updated' => '2007-03-18 10:53:31'
+				)),
+				'Tag' => array(
+					array(
+						'id' => '1',
+						'tag' => 'tag1',
+						'created' => '2007-03-18 12:22:23',
+						'updated' => '2007-03-18 12:24:31'
+					),
+					array(
+						'id' => '2',
+						'tag' => 'tag2',
+						'created' => '2007-03-18 12:24:23',
+						'updated' => '2007-03-18 12:26:31'
+			))),
+			array(
+				'Article' => array(
+					'id' => '2',
+					'user_id' => '3',
+					'title' => 'Second Article',
+					'body' => 'Second Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:41:23',
+					'updated' => '2007-03-18 10:43:31'
+				),
+				'User' => array(
+					'id' => '3',
+					'user' => 'larry',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23',
+					'updated' => '2007-03-17 01:22:31'
+				),
+				'Comment' => array(
+					array(
+						'id' => '5',
+						'article_id' => '2',
+						'user_id' => '1',
+						'comment' => 'First Comment for Second Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:53:23',
+						'updated' => '2007-03-18 10:55:31'
+					),
+					array(
+						'id' => '6',
+						'article_id' => '2',
+						'user_id' => '2',
+						'comment' => 'Second Comment for Second Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:55:23',
+						'updated' => '2007-03-18 10:57:31'
+				)),
+				'Tag' => array(
+					array(
+						'id' => '1',
+						'tag' => 'tag1',
+						'created' => '2007-03-18 12:22:23',
+						'updated' => '2007-03-18 12:24:31'
+					),
+					array(
+						'id' => '3',
+						'tag' => 'tag3',
+						'created' => '2007-03-18 12:26:23',
+						'updated' => '2007-03-18 12:28:31'
+			))),
+			array(
+				'Article' => array(
+					'id' => '3',
+					'user_id' => '1',
+					'title' => 'Third Article',
+					'body' => 'Third Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:43:23',
+					'updated' => '2007-03-18 10:45:31'
+				),
+				'User' => array(
+					'id' => '1',
+					'user' => 'mariano',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23',
+					'updated' => '2007-03-17 01:18:31'
+				),
+				'Comment' => array(),
+				'Tag' => array()
+		));
+		$this->assertEqual($TestModel->find('all'), $expected);
+
+		$db2 =& ConnectionManager::getDataSource('test2');
+
+		foreach (array('User', 'Comment') as $class) {
+			$this->_fixtures[$this->_fixtureClassMap[$class]]->create($db2);
+			$this->_fixtures[$this->_fixtureClassMap[$class]]->insert($db2);
+			$this->db->truncate(Inflector::pluralize(Inflector::underscore($class)));
+		}
+
+		$this->assertEqual($TestModel->User->find('all'), array());
+		$this->assertEqual($TestModel->Comment->find('all'), array());
+		$this->assertEqual($TestModel->find('count'), 3);
+
+		$TestModel->User->setDataSource('test2');
+		$TestModel->Comment->setDataSource('test2');
+
+		foreach ($expected as $key => $value) {
+			unset($value['Comment'], $value['Tag']);
+			$expected[$key] = $value;
+		}
+
+		$TestModel->recursive = 0;
+		$result = $TestModel->find('all');
+		$this->assertEqual($result, $expected);
+
+		foreach ($expected as $key => $value) {
+			unset($value['Comment'], $value['Tag']);
+			$expected[$key] = $value;
+		}
+
+		$TestModel->recursive = 0;
+		$result = $TestModel->find('all');
+		$this->assertEqual($result, $expected);
+
+		$result = Set::extract($TestModel->User->find('all'), '{n}.User.id');
+		$this->assertEqual($result, array('1', '2', '3', '4'));
+		$this->assertEqual($TestModel->find('all'), $expected);
+
+		$TestModel->Comment->unbindModel(array('hasOne' => array('Attachment')));
+		$expected = array(
+			array(
+				'Comment' => array(
+					'id' => '1',
+					'article_id' => '1',
+					'user_id' => '2',
+					'comment' => 'First Comment for First Article',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:45:23',
+					'updated' => '2007-03-18 10:47:31'
+				),
+				'User' => array(
+					'id' => '2',
+					'user' => 'nate',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:18:23',
+					'updated' => '2007-03-17 01:20:31'
+				),
+				'Article' => array(
+					'id' => '1',
+					'user_id' => '1',
+					'title' => 'First Article',
+					'body' => 'First Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:39:23',
+					'updated' => '2007-03-18 10:41:31'
+			)),
+			array(
+				'Comment' => array(
+					'id' => '2',
+					'article_id' => '1',
+					'user_id' => '4',
+					'comment' => 'Second Comment for First Article',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:47:23',
+					'updated' => '2007-03-18 10:49:31'
+				),
+				'User' => array(
+					'id' => '4',
+					'user' => 'garrett',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:22:23',
+					'updated' => '2007-03-17 01:24:31'
+				),
+				'Article' => array(
+					'id' => '1',
+					'user_id' => '1',
+					'title' => 'First Article',
+					'body' => 'First Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:39:23',
+					'updated' => '2007-03-18 10:41:31'
+			)),
+			array(
+				'Comment' => array(
+					'id' => '3',
+					'article_id' => '1',
+					'user_id' => '1',
+					'comment' => 'Third Comment for First Article',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:49:23',
+					'updated' => '2007-03-18 10:51:31'
+				),
+				'User' => array(
+					'id' => '1',
+					'user' => 'mariano',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23',
+					'updated' => '2007-03-17 01:18:31'
+				),
+				'Article' => array(
+					'id' => '1',
+					'user_id' => '1',
+					'title' => 'First Article',
+					'body' => 'First Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:39:23',
+					'updated' => '2007-03-18 10:41:31'
+			)),
+			array(
+				'Comment' => array(
+					'id' => '4',
+					'article_id' => '1',
+					'user_id' => '1',
+					'comment' => 'Fourth Comment for First Article',
+					'published' => 'N',
+					'created' => '2007-03-18 10:51:23',
+					'updated' => '2007-03-18 10:53:31'
+				),
+				'User' => array(
+					'id' => '1',
+					'user' => 'mariano',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23',
+					'updated' => '2007-03-17 01:18:31'
+				),
+				'Article' => array(
+					'id' => '1',
+					'user_id' => '1',
+					'title' => 'First Article',
+					'body' => 'First Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:39:23',
+					'updated' => '2007-03-18 10:41:31'
+			)),
+			array(
+				'Comment' => array(
+					'id' => '5',
+					'article_id' => '2',
+					'user_id' => '1',
+					'comment' => 'First Comment for Second Article',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:53:23',
+					'updated' => '2007-03-18 10:55:31'
+				),
+				'User' => array(
+					'id' => '1',
+					'user' => 'mariano',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23',
+					'updated' => '2007-03-17 01:18:31'
+				),
+				'Article' => array(
+					'id' => '2',
+					'user_id' => '3',
+					'title' => 'Second Article',
+					'body' => 'Second Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:41:23',
+					'updated' => '2007-03-18 10:43:31'
+			)),
+			array(
+				'Comment' => array(
+					'id' => '6',
+					'article_id' => '2',
+					'user_id' => '2',
+					'comment' => 'Second Comment for Second Article',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:55:23',
+					'updated' => '2007-03-18 10:57:31'
+				),
+				'User' => array(
+					'id' => '2',
+					'user' => 'nate',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:18:23',
+					'updated' => '2007-03-17 01:20:31'
+				),
+				'Article' => array(
+					'id' => '2',
+					'user_id' => '3',
+					'title' => 'Second Article',
+					'body' => 'Second Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:41:23',
+					'updated' => '2007-03-18 10:43:31'
+		)));
+		$this->assertEqual($TestModel->Comment->find('all'), $expected);
+
+		foreach (array('User', 'Comment') as $class) {
+			$this->_fixtures[$this->_fixtureClassMap[$class]]->drop($db2);
+		}
+	}
+
+/**
+ * testDisplayField method
+ *
+ * @access public
+ * @return void
+ */
+	function testDisplayField() {
+		$this->loadFixtures('Post', 'Comment', 'Person');
+		$Post = new Post();
+		$Comment = new Comment();
+		$Person = new Person();
+
+		$this->assertEqual($Post->displayField, 'title');
+		$this->assertEqual($Person->displayField, 'name');
+		$this->assertEqual($Comment->displayField, 'id');
+	}
+
+/**
+ * testSchema method
+ *
+ * @access public
+ * @return void
+ */
+	function testSchema() {
+		$Post = new Post();
+
+		$result = $Post->schema();
+		$columns = array('id', 'author_id', 'title', 'body', 'published', 'created', 'updated');
+		$this->assertEqual(array_keys($result), $columns);
+
+		$types = array('integer', 'integer', 'string', 'text', 'string', 'datetime', 'datetime');
+		$this->assertEqual(Set::extract(array_values($result), '{n}.type'), $types);
+
+		$result = $Post->schema('body');
+		$this->assertEqual($result['type'], 'text');
+		$this->assertNull($Post->schema('foo'));
+
+		$this->assertEqual($Post->getColumnTypes(), array_combine($columns, $types));
+	}
+
+/**
+ * test deconstruct() with time fields.
+ *
+ * @return void
+ */
+	function testDeconstructFieldsTime() {
+		$this->loadFixtures('Apple');
+		$TestModel =& new Apple();
+
+		$data = array();
+		$data['Apple']['mytime']['hour'] = '';
+		$data['Apple']['mytime']['min'] = '';
+		$data['Apple']['mytime']['sec'] = '';
+
+		$TestModel->data = null;
+		$TestModel->set($data);
+		$expected = array('Apple'=> array('mytime'=> ''));
+		$this->assertEqual($TestModel->data, $expected);
+
+		$data = array();
+		$data['Apple']['mytime']['hour'] = '';
+		$data['Apple']['mytime']['min'] = '';
+		$data['Apple']['mytime']['meridan'] = '';
+
+		$TestModel->data = null;
+		$TestModel->set($data);
+		$expected = array('Apple'=> array('mytime'=> ''));
+		$this->assertEqual($TestModel->data, $expected, 'Empty values are not returning properly. %s');
+
+		$data = array();
+		$data['Apple']['mytime']['hour'] = '12';
+		$data['Apple']['mytime']['min'] = '0';
+		$data['Apple']['mytime']['meridian'] = 'am';
+
+		$TestModel->data = null;
+		$TestModel->set($data);
+		$expected = array('Apple'=> array('mytime'=> '00:00:00'));
+		$this->assertEqual($TestModel->data, $expected, 'Midnight is not returning proper values. %s');
+
+		$data = array();
+		$data['Apple']['mytime']['hour'] = '00';
+		$data['Apple']['mytime']['min'] = '00';
+
+		$TestModel->data = null;
+		$TestModel->set($data);
+		$expected = array('Apple'=> array('mytime'=> '00:00:00'));
+		$this->assertEqual($TestModel->data, $expected, 'Midnight is not returning proper values. %s');
+
+		$data = array();
+		$data['Apple']['mytime']['hour'] = '03';
+		$data['Apple']['mytime']['min'] = '04';
+		$data['Apple']['mytime']['sec'] = '04';
+
+		$TestModel->data = null;
+		$TestModel->set($data);
+		$expected = array('Apple'=> array('mytime'=> '03:04:04'));
+		$this->assertEqual($TestModel->data, $expected);
+
+		$data = array();
+		$data['Apple']['mytime']['hour'] = '3';
+		$data['Apple']['mytime']['min'] = '4';
+		$data['Apple']['mytime']['sec'] = '4';
+
+		$TestModel->data = null;
+		$TestModel->set($data);
+		$expected = array('Apple' => array('mytime'=> '03:04:04'));
+		$this->assertEqual($TestModel->data, $expected);
+
+		$data = array();
+		$data['Apple']['mytime']['hour'] = '03';
+		$data['Apple']['mytime']['min'] = '4';
+		$data['Apple']['mytime']['sec'] = '4';
+
+		$TestModel->data = null;
+		$TestModel->set($data);
+		$expected = array('Apple'=> array('mytime'=> '03:04:04'));
+		$this->assertEqual($TestModel->data, $expected);
+
+		$db = ConnectionManager::getDataSource('test_suite');
+		$data = array();
+		$data['Apple']['mytime'] = $db->expression('NOW()');
+		$TestModel->data = null;
+		$TestModel->set($data);
+		$this->assertEqual($TestModel->data, $data);
+	}
+
+/**
+ * testDeconstructFields with datetime, timestamp, and date fields
+ *
+ * @access public
+ * @return void
+ */
+	function testDeconstructFieldsDateTime() {
+		$this->loadFixtures('Apple');
+		$TestModel =& new Apple();
+
+		//test null/empty values first
+		$data['Apple']['created']['year'] = '';
+		$data['Apple']['created']['month'] = '';
+		$data['Apple']['created']['day'] = '';
+		$data['Apple']['created']['hour'] = '';
+		$data['Apple']['created']['min'] = '';
+		$data['Apple']['created']['sec'] = '';
+
+		$TestModel->data = null;
+		$TestModel->set($data);
+		$expected = array('Apple'=> array('created'=> ''));
+		$this->assertEqual($TestModel->data, $expected);
+
+		$data = array();
+		$data['Apple']['date']['year'] = '';
+		$data['Apple']['date']['month'] = '';
+		$data['Apple']['date']['day'] = '';
+
+		$TestModel->data = null;
+		$TestModel->set($data);
+		$expected = array('Apple'=> array('date'=> ''));
+		$this->assertEqual($TestModel->data, $expected);
+
+		$data = array();
+		$data['Apple']['created']['year'] = '2007';
+		$data['Apple']['created']['month'] = '08';
+		$data['Apple']['created']['day'] = '20';
+		$data['Apple']['created']['hour'] = '';
+		$data['Apple']['created']['min'] = '';
+		$data['Apple']['created']['sec'] = '';
+
+		$TestModel->data = null;
+		$TestModel->set($data);
+		$expected = array('Apple'=> array('created'=> '2007-08-20 00:00:00'));
+		$this->assertEqual($TestModel->data, $expected);
+
+		$data = array();
+		$data['Apple']['created']['year'] = '2007';
+		$data['Apple']['created']['month'] = '08';
+		$data['Apple']['created']['day'] = '20';
+		$data['Apple']['created']['hour'] = '10';
+		$data['Apple']['created']['min'] = '12';
+		$data['Apple']['created']['sec'] = '';
+
+		$TestModel->data = null;
+		$TestModel->set($data);
+		$expected = array('Apple'=> array('created'=> '2007-08-20 10:12:00'));
+		$this->assertEqual($TestModel->data, $expected);
+
+		$data = array();
+		$data['Apple']['created']['year'] = '2007';
+		$data['Apple']['created']['month'] = '';
+		$data['Apple']['created']['day'] = '12';
+		$data['Apple']['created']['hour'] = '20';
+		$data['Apple']['created']['min'] = '';
+		$data['Apple']['created']['sec'] = '';
+
+		$TestModel->data = null;
+		$TestModel->set($data);
+		$expected = array('Apple'=> array('created'=> ''));
+		$this->assertEqual($TestModel->data, $expected);
+
+		$data = array();
+		$data['Apple']['created']['hour'] = '20';
+		$data['Apple']['created']['min'] = '33';
+
+		$TestModel->data = null;
+		$TestModel->set($data);
+		$expected = array('Apple'=> array('created'=> ''));
+		$this->assertEqual($TestModel->data, $expected);
+
+		$data = array();
+		$data['Apple']['created']['hour'] = '20';
+		$data['Apple']['created']['min'] = '33';
+		$data['Apple']['created']['sec'] = '33';
+
+		$TestModel->data = null;
+		$TestModel->set($data);
+		$expected = array('Apple'=> array('created'=> ''));
+		$this->assertEqual($TestModel->data, $expected);
+
+		$data = array();
+		$data['Apple']['created']['hour'] = '13';
+		$data['Apple']['created']['min'] = '00';
+		$data['Apple']['date']['year'] = '2006';
+		$data['Apple']['date']['month'] = '12';
+		$data['Apple']['date']['day'] = '25';
+
+		$TestModel->data = null;
+		$TestModel->set($data);
+		$expected = array(
+			'Apple'=> array(
+			'created'=> '',
+			'date'=> '2006-12-25'
+		));
+		$this->assertEqual($TestModel->data, $expected);
+
+		$data = array();
+		$data['Apple']['created']['year'] = '2007';
+		$data['Apple']['created']['month'] = '08';
+		$data['Apple']['created']['day'] = '20';
+		$data['Apple']['created']['hour'] = '10';
+		$data['Apple']['created']['min'] = '12';
+		$data['Apple']['created']['sec'] = '09';
+		$data['Apple']['date']['year'] = '2006';
+		$data['Apple']['date']['month'] = '12';
+		$data['Apple']['date']['day'] = '25';
+
+		$TestModel->data = null;
+		$TestModel->set($data);
+		$expected = array(
+			'Apple'=> array(
+				'created'=> '2007-08-20 10:12:09',
+				'date'=> '2006-12-25'
+		));
+		$this->assertEqual($TestModel->data, $expected);
+
+		$data = array();
+		$data['Apple']['created']['year'] = '--';
+		$data['Apple']['created']['month'] = '--';
+		$data['Apple']['created']['day'] = '--';
+		$data['Apple']['created']['hour'] = '--';
+		$data['Apple']['created']['min'] = '--';
+		$data['Apple']['created']['sec'] = '--';
+		$data['Apple']['date']['year'] = '--';
+		$data['Apple']['date']['month'] = '--';
+		$data['Apple']['date']['day'] = '--';
+
+		$TestModel->data = null;
+		$TestModel->set($data);
+		$expected = array('Apple'=> array('created'=> '', 'date'=> ''));
+		$this->assertEqual($TestModel->data, $expected);
+
+		$data = array();
+		$data['Apple']['created']['year'] = '2007';
+		$data['Apple']['created']['month'] = '--';
+		$data['Apple']['created']['day'] = '20';
+		$data['Apple']['created']['hour'] = '10';
+		$data['Apple']['created']['min'] = '12';
+		$data['Apple']['created']['sec'] = '09';
+		$data['Apple']['date']['year'] = '2006';
+		$data['Apple']['date']['month'] = '12';
+		$data['Apple']['date']['day'] = '25';
+
+		$TestModel->data = null;
+		$TestModel->set($data);
+		$expected = array('Apple'=> array('created'=> '', 'date'=> '2006-12-25'));
+		$this->assertEqual($TestModel->data, $expected);
+
+		$data = array();
+		$data['Apple']['date']['year'] = '2006';
+		$data['Apple']['date']['month'] = '12';
+		$data['Apple']['date']['day'] = '25';
+
+		$TestModel->data = null;
+		$TestModel->set($data);
+		$expected = array('Apple'=> array('date'=> '2006-12-25'));
+		$this->assertEqual($TestModel->data, $expected);
+
+		$db = ConnectionManager::getDataSource('test_suite');
+		$data = array();
+		$data['Apple']['modified'] = $db->expression('NOW()');
+		$TestModel->data = null;
+		$TestModel->set($data);
+		$this->assertEqual($TestModel->data, $data);
+	}
+
+/**
+ * testTablePrefixSwitching method
+ *
+ * @access public
+ * @return void
+ */
+	function testTablePrefixSwitching() {
+		ConnectionManager::create('database1',
+				array_merge($this->db->config, array('prefix' => 'aaa_')
+		));
+		ConnectionManager::create('database2',
+			array_merge($this->db->config, array('prefix' => 'bbb_')
+		));
+
+		$db1 = ConnectionManager::getDataSource('database1');
+		$db2 = ConnectionManager::getDataSource('database2');
+
+		$TestModel = new Apple();
+		$TestModel->setDataSource('database1');
+		$this->assertEqual($this->db->fullTableName($TestModel, false), 'aaa_apples');
+		$this->assertEqual($db1->fullTableName($TestModel, false), 'aaa_apples');
+		$this->assertEqual($db2->fullTableName($TestModel, false), 'aaa_apples');
+
+		$TestModel->setDataSource('database2');
+		$this->assertEqual($this->db->fullTableName($TestModel, false), 'bbb_apples');
+		$this->assertEqual($db1->fullTableName($TestModel, false), 'bbb_apples');
+		$this->assertEqual($db2->fullTableName($TestModel, false), 'bbb_apples');
+
+		$TestModel = new Apple();
+		$TestModel->tablePrefix = 'custom_';
+		$this->assertEqual($this->db->fullTableName($TestModel, false), 'custom_apples');
+		$TestModel->setDataSource('database1');
+		$this->assertEqual($this->db->fullTableName($TestModel, false), 'custom_apples');
+		$this->assertEqual($db1->fullTableName($TestModel, false), 'custom_apples');
+
+		$TestModel = new Apple();
+		$TestModel->setDataSource('database1');
+		$this->assertEqual($this->db->fullTableName($TestModel, false), 'aaa_apples');
+		$TestModel->tablePrefix = '';
+		$TestModel->setDataSource('database2');
+		$this->assertEqual($db2->fullTableName($TestModel, false), 'apples');
+		$this->assertEqual($db1->fullTableName($TestModel, false), 'apples');
+
+		$TestModel->tablePrefix = null;
+		$TestModel->setDataSource('database1');
+		$this->assertEqual($db2->fullTableName($TestModel, false), 'aaa_apples');
+		$this->assertEqual($db1->fullTableName($TestModel, false), 'aaa_apples');
+
+		$TestModel->tablePrefix = false;
+		$TestModel->setDataSource('database2');
+		$this->assertEqual($db2->fullTableName($TestModel, false), 'apples');
+		$this->assertEqual($db1->fullTableName($TestModel, false), 'apples');
+	}
+
+/**
+ * Tests validation parameter order in custom validation methods
+ *
+ * @access public
+ * @return void
+ */
+	function testInvalidAssociation() {
+		$TestModel =& new ValidationTest1();
+		$this->assertNull($TestModel->getAssociated('Foo'));
+	}
+
+/**
+ * testLoadModelSecondIteration method
+ *
+ * @access public
+ * @return void
+ */
+	function testLoadModelSecondIteration() {
+		$model = new ModelA();
+		$this->assertIsA($model,'ModelA');
+
+		$this->assertIsA($model->ModelB, 'ModelB');
+		$this->assertIsA($model->ModelB->ModelD, 'ModelD');
+
+		$this->assertIsA($model->ModelC, 'ModelC');
+		$this->assertIsA($model->ModelC->ModelD, 'ModelD');
+	}
+
+/**
+ * ensure that exists() does not persist between method calls reset on create
+ *
+ * @return void
+ */
+	function testResetOfExistsOnCreate() {
+		$this->loadFixtures('Article');
+		$Article =& new Article();
+		$Article->id = 1;
+		$Article->saveField('title', 'Reset me');
+		$Article->delete();
+		$Article->id = 1;
+		$this->assertFalse($Article->exists());
+
+		$Article->create();
+		$this->assertFalse($Article->exists());
+		$Article->id = 2;
+		$Article->saveField('title', 'Staying alive');
+		$result = $Article->read(null, 2);
+		$this->assertEqual($result['Article']['title'], 'Staying alive');
+	}
+
+/**
+ * testUseTableFalseExistsCheck method
+ *
+ * @return void
+ */
+	function testUseTableFalseExistsCheck() {
+		$this->loadFixtures('Article');
+		$Article =& new Article();
+		$Article->id = 1337;
+		$result = $Article->exists();
+		$this->assertFalse($result);
+
+		$Article->useTable = false;
+		$Article->id = null;
+		$result = $Article->exists();
+		$this->assertFalse($result);
+
+		// An article with primary key of '1' has been loaded by the fixtures.
+		$Article->useTable = false;
+		$Article->id = 1;
+		$result = $Article->exists();
+		$this->assertTrue($result);
+	}
+
+/**
+ * testPluginAssociations method
+ *
+ * @access public
+ * @return void
+ */
+	function testPluginAssociations() {
+		$this->loadFixtures('TestPluginArticle', 'User', 'TestPluginComment');
+		$TestModel =& new TestPluginArticle();
+
+		$result = $TestModel->find('all');
+		$expected = array(
+			array(
+				'TestPluginArticle' => array(
+					'id' => 1,
+					'user_id' => 1,
+					'title' => 'First Plugin Article',
+					'body' => 'First Plugin Article Body',
+					'published' => 'Y',
+					'created' => '2008-09-24 10:39:23',
+					'updated' => '2008-09-24 10:41:31'
+				),
+				'User' => array(
+					'id' => 1,
+					'user' => 'mariano',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23',
+					'updated' => '2007-03-17 01:18:31'
+				),
+				'TestPluginComment' => array(
+					array(
+						'id' => 1,
+						'article_id' => 1,
+						'user_id' => 2,
+						'comment' => 'First Comment for First Plugin Article',
+						'published' => 'Y',
+						'created' => '2008-09-24 10:45:23',
+						'updated' => '2008-09-24 10:47:31'
+					),
+					array(
+						'id' => 2,
+						'article_id' => 1,
+						'user_id' => 4,
+						'comment' => 'Second Comment for First Plugin Article',
+						'published' => 'Y',
+						'created' => '2008-09-24 10:47:23',
+						'updated' => '2008-09-24 10:49:31'
+					),
+					array(
+						'id' => 3,
+						'article_id' => 1,
+						'user_id' => 1,
+						'comment' => 'Third Comment for First Plugin Article',
+						'published' => 'Y',
+						'created' => '2008-09-24 10:49:23',
+						'updated' => '2008-09-24 10:51:31'
+					),
+					array(
+						'id' => 4,
+						'article_id' => 1,
+						'user_id' => 1,
+						'comment' => 'Fourth Comment for First Plugin Article',
+						'published' => 'N',
+						'created' => '2008-09-24 10:51:23',
+						'updated' => '2008-09-24 10:53:31'
+			))),
+			array(
+				'TestPluginArticle' => array(
+					'id' => 2,
+					'user_id' => 3,
+					'title' => 'Second Plugin Article',
+					'body' => 'Second Plugin Article Body',
+					'published' => 'Y',
+					'created' => '2008-09-24 10:41:23',
+					'updated' => '2008-09-24 10:43:31'
+				),
+				'User' => array(
+					'id' => 3,
+					'user' => 'larry',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23',
+					'updated' => '2007-03-17 01:22:31'
+				),
+				'TestPluginComment' => array(
+					array(
+						'id' => 5,
+						'article_id' => 2,
+						'user_id' => 1,
+						'comment' => 'First Comment for Second Plugin Article',
+						'published' => 'Y',
+						'created' => '2008-09-24 10:53:23',
+						'updated' => '2008-09-24 10:55:31'
+					),
+					array(
+						'id' => 6,
+						'article_id' => 2,
+						'user_id' => 2,
+						'comment' => 'Second Comment for Second Plugin Article',
+						'published' => 'Y',
+						'created' => '2008-09-24 10:55:23',
+						'updated' => '2008-09-24 10:57:31'
+			))),
+			array(
+				'TestPluginArticle' => array(
+					'id' => 3,
+					'user_id' => 1,
+					'title' => 'Third Plugin Article',
+					'body' => 'Third Plugin Article Body',
+					'published' => 'Y',
+					'created' => '2008-09-24 10:43:23',
+					'updated' => '2008-09-24 10:45:31'
+				),
+				'User' => array(
+					'id' => 1,
+					'user' => 'mariano',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23',
+					'updated' => '2007-03-17 01:18:31'
+				),
+				'TestPluginComment' => array()
+		));
+
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * Tests getAssociated method
+ *
+ * @access public
+ * @return void
+ */
+	function testGetAssociated() {
+		$this->loadFixtures('Article');
+		$Article = ClassRegistry::init('Article');
+
+		$assocTypes = array('hasMany', 'hasOne', 'belongsTo', 'hasAndBelongsToMany');
+		foreach ($assocTypes as $type) {
+			 $this->assertEqual($Article->getAssociated($type), array_keys($Article->{$type}));
+		}
+
+		$Article->bindModel(array('hasMany' => array('Category')));
+		$this->assertEqual($Article->getAssociated('hasMany'), array('Comment', 'Category'));
+
+		$results = $Article->getAssociated();
+		$this->assertEqual(sort(array_keys($results)), array('Category', 'Comment', 'Tag'));
+
+		$Article->unbindModel(array('hasAndBelongsToMany' => array('Tag')));
+		$this->assertEqual($Article->getAssociated('hasAndBelongsToMany'), array());
+
+		$result = $Article->getAssociated('Category');
+		$expected = array(
+			'className' => 'Category',
+			'foreignKey' => 'article_id',
+			'conditions' => '',
+			'fields' => '',
+			'order' => '',
+			'limit' => '',
+			'offset' => '',
+			'dependent' => '',
+			'exclusive' => '',
+			'finderQuery' => '',
+			'counterQuery' => '',
+			'association' => 'hasMany',
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testAutoConstructAssociations method
+ *
+ * @access public
+ * @return void
+ */
+	function testAutoConstructAssociations() {
+		$this->loadFixtures('User', 'ArticleFeatured');
+		$TestModel =& new AssociationTest1();
+
+		$result = $TestModel->hasAndBelongsToMany;
+		$expected = array('AssociationTest2' => array(
+				'unique' => false,
+				'joinTable' => 'join_as_join_bs',
+				'foreignKey' => false,
+				'className' => 'AssociationTest2',
+				'with' => 'JoinAsJoinB',
+				'associationForeignKey' => 'join_b_id',
+				'conditions' => '', 'fields' => '', 'order' => '', 'limit' => '', 'offset' => '',
+				'finderQuery' => '', 'deleteQuery' => '', 'insertQuery' => ''
+		));
+		$this->assertEqual($result, $expected);
+
+		// Tests related to ticket https://trac.cakephp.org/ticket/5594
+		$TestModel =& new ArticleFeatured();
+		$TestFakeModel =& new ArticleFeatured(array('table' => false));
+
+		$expected = array(
+			'User' => array(
+				'className' => 'User', 'foreignKey' => 'user_id',
+				'conditions' => '', 'fields' => '', 'order' => '', 'counterCache' => ''
+			),
+			'Category' => array(
+				'className' => 'Category', 'foreignKey' => 'category_id',
+				'conditions' => '', 'fields' => '', 'order' => '', 'counterCache' => ''
+			)
+		);
+		$this->assertIdentical($TestModel->belongsTo, $expected);
+		$this->assertIdentical($TestFakeModel->belongsTo, $expected);
+
+		$this->assertEqual($TestModel->User->name, 'User');
+		$this->assertEqual($TestFakeModel->User->name, 'User');
+		$this->assertEqual($TestModel->Category->name, 'Category');
+		$this->assertEqual($TestFakeModel->Category->name, 'Category');
+
+		$expected = array(
+			'Featured' => array(
+				'className' => 'Featured',
+				'foreignKey' => 'article_featured_id',
+				'conditions' => '',
+				'fields' => '',
+				'order' => '',
+				'dependent' => ''
+		));
+
+		$this->assertIdentical($TestModel->hasOne, $expected);
+		$this->assertIdentical($TestFakeModel->hasOne, $expected);
+
+		$this->assertEqual($TestModel->Featured->name, 'Featured');
+		$this->assertEqual($TestFakeModel->Featured->name, 'Featured');
+
+		$expected = array(
+			'Comment' => array(
+				'className' => 'Comment',
+				'dependent' => true,
+				'foreignKey' => 'article_featured_id',
+				'conditions' => '',
+				'fields' => '',
+				'order' => '',
+				'limit' => '',
+				'offset' => '',
+				'exclusive' => '',
+				'finderQuery' => '',
+				'counterQuery' => ''
+		));
+
+		$this->assertIdentical($TestModel->hasMany, $expected);
+		$this->assertIdentical($TestFakeModel->hasMany, $expected);
+
+		$this->assertEqual($TestModel->Comment->name, 'Comment');
+		$this->assertEqual($TestFakeModel->Comment->name, 'Comment');
+
+		$expected = array(
+			'Tag' => array(
+				'className' => 'Tag',
+				'joinTable' => 'article_featureds_tags',
+				'with' => 'ArticleFeaturedsTag',
+				'foreignKey' => 'article_featured_id',
+				'associationForeignKey' => 'tag_id',
+				'conditions' => '',
+				'fields' => '',
+				'order' => '',
+				'limit' => '',
+				'offset' => '',
+				'unique' => true,
+				'finderQuery' => '',
+				'deleteQuery' => '',
+				'insertQuery' => ''
+		));
+
+		$this->assertIdentical($TestModel->hasAndBelongsToMany, $expected);
+		$this->assertIdentical($TestFakeModel->hasAndBelongsToMany, $expected);
+
+		$this->assertEqual($TestModel->Tag->name, 'Tag');
+		$this->assertEqual($TestFakeModel->Tag->name, 'Tag');
+	}
+
+/**
+ * test Model::__construct
+ *
+ * ensure that $actsAS and $_findMethods are merged.
+ *
+ * @return void
+ */
+	function testConstruct() {
+		$this->loadFixtures('Post', 'Comment');
+
+		$TestModel =& ClassRegistry::init('MergeVarPluginPost');
+		$this->assertEqual($TestModel->actsAs, array('Containable', 'Tree'));
+		$this->assertTrue(isset($TestModel->Behaviors->Containable));
+		$this->assertTrue(isset($TestModel->Behaviors->Tree));
+
+		$TestModel =& ClassRegistry::init('MergeVarPluginComment');
+		$expected = array('Containable', 'Containable' => array('some_settings'));
+		$this->assertEqual($TestModel->actsAs, $expected);
+		$this->assertTrue(isset($TestModel->Behaviors->Containable));
+	}
+
+/**
+ * test Model::__construct
+ *
+ * ensure that $actsAS and $_findMethods are merged.
+ *
+ * @return void
+ */
+	function testConstructWithAlternateDataSource() {
+		$TestModel =& ClassRegistry::init(array(
+			'class' => 'DoesntMatter', 'ds' => 'test_suite', 'table' => false
+		));
+		$this->assertEqual('test_suite', $TestModel->useDbConfig);
+
+		//deprecated but test it anyway
+		$NewVoid =& new TheVoid(null, false, 'other');
+		$this->assertEqual('other', $NewVoid->useDbConfig);
+	}
+
+/**
+ * testColumnTypeFetching method
+ *
+ * @access public
+ * @return void
+ */
+	function testColumnTypeFetching() {
+		$model =& new Test();
+		$this->assertEqual($model->getColumnType('id'), 'integer');
+		$this->assertEqual($model->getColumnType('notes'), 'text');
+		$this->assertEqual($model->getColumnType('updated'), 'datetime');
+		$this->assertEqual($model->getColumnType('unknown'), null);
+
+		$model =& new Article();
+		$this->assertEqual($model->getColumnType('User.created'), 'datetime');
+		$this->assertEqual($model->getColumnType('Tag.id'), 'integer');
+		$this->assertEqual($model->getColumnType('Article.id'), 'integer');
+	}
+
+/**
+ * testHabtmUniqueKey method
+ *
+ * @access public
+ * @return void
+ */
+	function testHabtmUniqueKey() {
+		$model =& new Item();
+		$this->assertFalse($model->hasAndBelongsToMany['Portfolio']['unique']);
+	}
+
+/**
+ * testIdentity method
+ *
+ * @access public
+ * @return void
+ */
+	function testIdentity() {
+		$TestModel =& new Test();
+		$result = $TestModel->alias;
+		$expected = 'Test';
+		$this->assertEqual($result, $expected);
+
+		$TestModel =& new TestAlias();
+		$result = $TestModel->alias;
+		$expected = 'TestAlias';
+		$this->assertEqual($result, $expected);
+
+		$TestModel =& new Test(array('alias' => 'AnotherTest'));
+		$result = $TestModel->alias;
+		$expected = 'AnotherTest';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testWithAssociation method
+ *
+ * @access public
+ * @return void
+ */
+	function testWithAssociation() {
+		$this->loadFixtures('Something', 'SomethingElse', 'JoinThing');
+		$TestModel =& new Something();
+		$result = $TestModel->SomethingElse->find('all');
+
+		$expected = array(
+			array(
+				'SomethingElse' => array(
+					'id' => '1',
+					'title' => 'First Post',
+					'body' => 'First Post Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:39:23',
+					'updated' => '2007-03-18 10:41:31'
+				),
+				'Something' => array(
+					array(
+						'id' => '3',
+						'title' => 'Third Post',
+						'body' => 'Third Post Body',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:43:23',
+						'updated' => '2007-03-18 10:45:31',
+						'JoinThing' => array(
+							'id' => '3',
+							'something_id' => '3',
+							'something_else_id' => '1',
+							'doomed' => '1',
+							'created' => '2007-03-18 10:43:23',
+							'updated' => '2007-03-18 10:45:31'
+			)))),
+			array(
+				'SomethingElse' => array(
+					'id' => '2',
+					'title' => 'Second Post',
+					'body' => 'Second Post Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:41:23',
+					'updated' => '2007-03-18 10:43:31'
+				),
+				'Something' => array(
+					array(
+						'id' => '1',
+						'title' => 'First Post',
+						'body' => 'First Post Body',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:39:23',
+						'updated' => '2007-03-18 10:41:31',
+						'JoinThing' => array(
+							'id' => '1',
+							'something_id' => '1',
+							'something_else_id' => '2',
+							'doomed' => '1',
+							'created' => '2007-03-18 10:39:23',
+							'updated' => '2007-03-18 10:41:31'
+			)))),
+			array(
+				'SomethingElse' => array(
+					'id' => '3',
+					'title' => 'Third Post',
+					'body' => 'Third Post Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:43:23',
+					'updated' => '2007-03-18 10:45:31'
+				),
+				'Something' => array(
+					array(
+						'id' => '2',
+						'title' => 'Second Post',
+						'body' => 'Second Post Body',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:41:23',
+						'updated' => '2007-03-18 10:43:31',
+						'JoinThing' => array(
+							'id' => '2',
+							'something_id' => '2',
+							'something_else_id' => '3',
+							'doomed' => '0',
+							'created' => '2007-03-18 10:41:23',
+							'updated' => '2007-03-18 10:43:31'
+		)))));
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('all');
+		$expected = array(
+			array(
+				'Something' => array(
+					'id' => '1',
+					'title' => 'First Post',
+					'body' => 'First Post Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:39:23',
+					'updated' => '2007-03-18 10:41:31'
+				),
+				'SomethingElse' => array(
+					array(
+						'id' => '2',
+						'title' => 'Second Post',
+						'body' => 'Second Post Body',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:41:23',
+						'updated' => '2007-03-18 10:43:31',
+						'JoinThing' => array(
+							'doomed' => '1',
+							'something_id' => '1',
+							'something_else_id' => '2'
+			)))),
+			array(
+				'Something' => array(
+					'id' => '2',
+					'title' => 'Second Post',
+					'body' => 'Second Post Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:41:23',
+					'updated' => '2007-03-18 10:43:31'
+				),
+				'SomethingElse' => array(
+					array(
+						'id' => '3',
+						'title' => 'Third Post',
+						'body' => 'Third Post Body',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:43:23',
+						'updated' => '2007-03-18 10:45:31',
+						'JoinThing' => array(
+							'doomed' => '0',
+							'something_id' => '2',
+							'something_else_id' => '3'
+			)))),
+			array(
+				'Something' => array(
+					'id' => '3',
+					'title' => 'Third Post',
+					'body' => 'Third Post Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:43:23',
+					'updated' => '2007-03-18 10:45:31'
+				),
+				'SomethingElse' => array(
+					array(
+						'id' => '1',
+						'title' => 'First Post',
+						'body' => 'First Post Body',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:39:23',
+						'updated' => '2007-03-18 10:41:31',
+						'JoinThing' => array(
+							'doomed' => '1',
+							'something_id' => '3',
+							'something_else_id' => '1'
+		)))));
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->findById(1);
+		$expected = array(
+			'Something' => array(
+				'id' => '1',
+				'title' => 'First Post',
+				'body' => 'First Post Body',
+				'published' => 'Y',
+				'created' => '2007-03-18 10:39:23',
+				'updated' => '2007-03-18 10:41:31'
+			),
+			'SomethingElse' => array(
+				array(
+					'id' => '2',
+					'title' => 'Second Post',
+					'body' => 'Second Post Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:41:23',
+					'updated' => '2007-03-18 10:43:31',
+					'JoinThing' => array(
+						'doomed' => '1',
+						'something_id' => '1',
+						'something_else_id' => '2'
+		))));
+		$this->assertEqual($result, $expected);
+
+		$expected = $TestModel->findById(1);
+		$TestModel->set($expected);
+		$TestModel->save();
+		$result = $TestModel->findById(1);
+		$this->assertEqual($result, $expected);
+
+		$TestModel->hasAndBelongsToMany['SomethingElse']['unique'] = false;
+		$TestModel->create(array(
+			'Something' => array('id' => 1),
+			'SomethingElse' => array(3, array(
+				'something_else_id' => 1,
+				'doomed' => '1'
+		))));
+
+		$ts = date('Y-m-d H:i:s');
+		$TestModel->save();
+
+		$TestModel->hasAndBelongsToMany['SomethingElse']['order'] = 'SomethingElse.id ASC';
+		$result = $TestModel->findById(1);
+		$expected = array(
+			'Something' => array(
+				'id' => '1',
+				'title' => 'First Post',
+				'body' => 'First Post Body',
+				'published' => 'Y',
+				'created' => '2007-03-18 10:39:23',
+				'updated' => $ts),
+				'SomethingElse' => array(
+					array(
+						'id' => '1',
+						'title' => 'First Post',
+						'body' => 'First Post Body',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:39:23',
+						'updated' => '2007-03-18 10:41:31',
+						'JoinThing' => array(
+							'doomed' => '1',
+							'something_id' => '1',
+							'something_else_id' => '1'
+					)),
+					array(
+						'id' => '2',
+						'title' => 'Second Post',
+						'body' => 'Second Post Body',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:41:23',
+						'updated' => '2007-03-18 10:43:31',
+						'JoinThing' => array(
+							'doomed' => '1',
+							'something_id' => '1',
+							'something_else_id' => '2'
+					)),
+					array(
+						'id' => '3',
+						'title' => 'Third Post',
+						'body' => 'Third Post Body',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:43:23',
+						'updated' => '2007-03-18 10:45:31',
+						'JoinThing' => array(
+							'doomed' => '0',
+							'something_id' => '1',
+							'something_else_id' => '3'
+		))));
+
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testFindSelfAssociations method
+ *
+ * @access public
+ * @return void
+ */
+	function testFindSelfAssociations() {
+		$this->loadFixtures('Person');
+
+		$TestModel =& new Person();
+		$TestModel->recursive = 2;
+		$result = $TestModel->read(null, 1);
+		$expected = array(
+			'Person' => array(
+				'id' => 1,
+				'name' => 'person',
+				'mother_id' => 2,
+				'father_id' => 3
+			),
+			'Mother' => array(
+				'id' => 2,
+				'name' => 'mother',
+				'mother_id' => 4,
+				'father_id' => 5,
+				'Mother' => array(
+					'id' => 4,
+					'name' => 'mother - grand mother',
+					'mother_id' => 0,
+					'father_id' => 0
+				),
+				'Father' => array(
+					'id' => 5,
+					'name' => 'mother - grand father',
+					'mother_id' => 0,
+					'father_id' => 0
+			)),
+			'Father' => array(
+				'id' => 3,
+				'name' => 'father',
+				'mother_id' => 6,
+				'father_id' => 7,
+				'Father' => array(
+					'id' => 7,
+					'name' => 'father - grand father',
+					'mother_id' => 0,
+					'father_id' => 0
+				),
+				'Mother' => array(
+					'id' => 6,
+					'name' => 'father - grand mother',
+					'mother_id' => 0,
+					'father_id' => 0
+		)));
+
+		$this->assertEqual($result, $expected);
+
+		$TestModel->recursive = 3;
+		$result = $TestModel->read(null, 1);
+		$expected = array(
+			'Person' => array(
+				'id' => 1,
+				'name' => 'person',
+				'mother_id' => 2,
+				'father_id' => 3
+			),
+			'Mother' => array(
+				'id' => 2,
+				'name' => 'mother',
+				'mother_id' => 4,
+				'father_id' => 5,
+				'Mother' => array(
+					'id' => 4,
+					'name' => 'mother - grand mother',
+					'mother_id' => 0,
+					'father_id' => 0,
+					'Mother' => array(),
+					'Father' => array()),
+				'Father' => array(
+					'id' => 5,
+					'name' => 'mother - grand father',
+					'mother_id' => 0,
+					'father_id' => 0,
+					'Father' => array(),
+					'Mother' => array()
+			)),
+			'Father' => array(
+				'id' => 3,
+				'name' => 'father',
+				'mother_id' => 6,
+				'father_id' => 7,
+				'Father' => array(
+					'id' => 7,
+					'name' => 'father - grand father',
+					'mother_id' => 0,
+					'father_id' => 0,
+					'Father' => array(),
+					'Mother' => array()
+				),
+				'Mother' => array(
+					'id' => 6,
+					'name' => 'father - grand mother',
+					'mother_id' => 0,
+					'father_id' => 0,
+					'Mother' => array(),
+					'Father' => array()
+		)));
+
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testDynamicAssociations method
+ *
+ * @access public
+ * @return void
+ */
+	function testDynamicAssociations() {
+		$this->loadFixtures('Article', 'Comment');
+		$TestModel =& new Article();
+
+		$TestModel->belongsTo = $TestModel->hasAndBelongsToMany = $TestModel->hasOne = array();
+		$TestModel->hasMany['Comment'] = array_merge($TestModel->hasMany['Comment'], array(
+			'foreignKey' => false,
+			'conditions' => array('Comment.user_id =' => '2')
+		));
+		$result = $TestModel->find('all');
+		$expected = array(
+			array(
+				'Article' => array(
+					'id' => '1',
+					'user_id' => '1',
+					'title' => 'First Article',
+					'body' => 'First Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:39:23',
+					'updated' => '2007-03-18 10:41:31'
+				),
+				'Comment' => array(
+					array(
+						'id' => '1',
+						'article_id' => '1',
+						'user_id' => '2',
+						'comment' => 'First Comment for First Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:45:23',
+						'updated' => '2007-03-18 10:47:31'
+					),
+					array(
+						'id' => '6',
+						'article_id' => '2',
+						'user_id' => '2',
+						'comment' => 'Second Comment for Second Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:55:23',
+						'updated' => '2007-03-18 10:57:31'
+			))),
+			array(
+				'Article' => array(
+					'id' => '2',
+					'user_id' => '3',
+					'title' => 'Second Article',
+					'body' => 'Second Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:41:23',
+					'updated' => '2007-03-18 10:43:31'
+				),
+				'Comment' => array(
+					array(
+						'id' => '1',
+						'article_id' => '1',
+						'user_id' => '2',
+						'comment' => 'First Comment for First Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:45:23',
+						'updated' => '2007-03-18 10:47:31'
+					),
+					array(
+						'id' => '6',
+						'article_id' => '2',
+						'user_id' => '2',
+						'comment' => 'Second Comment for Second Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:55:23',
+						'updated' => '2007-03-18 10:57:31'
+			))),
+			array(
+				'Article' => array(
+					'id' => '3',
+					'user_id' => '1',
+					'title' => 'Third Article',
+					'body' => 'Third Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:43:23',
+					'updated' => '2007-03-18 10:45:31'
+				),
+				'Comment' => array(
+					array(
+						'id' => '1',
+						'article_id' => '1',
+						'user_id' => '2',
+						'comment' => 'First Comment for First Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:45:23',
+						'updated' => '2007-03-18 10:47:31'
+					),
+					array(
+						'id' => '6',
+						'article_id' => '2',
+						'user_id' => '2',
+						'comment' => 'Second Comment for Second Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:55:23',
+						'updated' => '2007-03-18 10:57:31'
+		))));
+
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testCreation method
+ *
+ * @access public
+ * @return void
+ */
+	function testCreation() {
+		$this->loadFixtures('Article');
+		$TestModel =& new Test();
+		$result = $TestModel->create();
+		$expected = array('Test' => array('notes' => 'write some notes here'));
+		$this->assertEqual($result, $expected);
+		$TestModel =& new User();
+		$result = $TestModel->schema();
+
+		if (isset($this->db->columns['primary_key']['length'])) {
+			$intLength = $this->db->columns['primary_key']['length'];
+		} elseif (isset($this->db->columns['integer']['length'])) {
+			$intLength = $this->db->columns['integer']['length'];
+		} else {
+			$intLength = 11;
+		}
+		foreach (array('collate', 'charset') as $type) {
+			unset($result['user'][$type]);
+			unset($result['password'][$type]);
+		}
+
+		$expected = array(
+			'id' => array(
+				'type' => 'integer',
+				'null' => false,
+				'default' => null,
+				'length' => $intLength,
+				'key' => 'primary'
+			),
+			'user' => array(
+				'type' => 'string',
+				'null' => false,
+				'default' => '',
+				'length' => 255
+			),
+			'password' => array(
+				'type' => 'string',
+				'null' => false,
+				'default' => '',
+				'length' => 255
+			),
+			'created' => array(
+				'type' => 'datetime',
+				'null' => true,
+				'default' => null,
+				'length' => null
+			),
+			'updated'=> array(
+				'type' => 'datetime',
+				'null' => true,
+				'default' => null,
+				'length' => null
+		));
+
+		$this->assertEqual($result, $expected);
+
+		$TestModel =& new Article();
+		$result = $TestModel->create();
+		$expected = array('Article' => array('published' => 'N'));
+		$this->assertEqual($result, $expected);
+
+		$FeaturedModel =& new Featured();
+		$data = array(
+			'article_featured_id' => 1,
+			'category_id' => 1,
+			'published_date' => array(
+				'year' => 2008,
+				'month' => 06,
+				'day' => 11
+			),
+			'end_date' => array(
+				'year' => 2008,
+				'month' => 06,
+				'day' => 20
+		));
+
+		$expected = array(
+			'Featured' => array(
+				'article_featured_id' => 1,
+				'category_id' => 1,
+				'published_date' => '2008-6-11 00:00:00',
+				'end_date' => '2008-6-20 00:00:00'
+		));
+
+		$this->assertEqual($FeaturedModel->create($data), $expected);
+
+		$data = array(
+			'published_date' => array(
+				'year' => 2008,
+				'month' => 06,
+				'day' => 11
+			),
+			'end_date' => array(
+				'year' => 2008,
+				'month' => 06,
+				'day' => 20
+			),
+			'article_featured_id' => 1,
+			'category_id' => 1
+		);
+
+		$expected = array(
+			'Featured' => array(
+				'published_date' => '2008-6-11 00:00:00',
+				'end_date' => '2008-6-20 00:00:00',
+				'article_featured_id' => 1,
+				'category_id' => 1
+		));
+
+		$this->assertEqual($FeaturedModel->create($data), $expected);
+	}
+
+/**
+ * testEscapeField to prove it escapes the field well even when it has part of the alias on it
+ * @see ttp://cakephp.lighthouseapp.com/projects/42648-cakephp-1x/tickets/473-escapefield-doesnt-consistently-prepend-modelname
+ *
+ * @access public
+ * @return void
+ */
+	function testEscapeField() {
+		$TestModel =& new Test();
+		$db =& $TestModel->getDataSource();
+
+		$result = $TestModel->escapeField('test_field');
+		$expected = $db->name('Test.test_field');
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->escapeField('TestField');
+		$expected = $db->name('Test.TestField');
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->escapeField('DomainHandle', 'Domain');
+		$expected = $db->name('Domain.DomainHandle');
+		$this->assertEqual($result, $expected);
+
+		ConnectionManager::create('mock', array('driver' => 'mock'));
+		$TestModel->setDataSource('mock');
+		$db =& $TestModel->getDataSource();
+
+		$result = $TestModel->escapeField('DomainHandle', 'Domain');
+		$expected = $db->name('Domain.DomainHandle');
+		$this->assertEqual($result, $expected);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/model/model_read.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/model/model_read.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/model/model_read.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,7559 @@
+<?php
+/**
+ * ModelReadTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+require_once dirname(__FILE__) . DS . 'model.test.php';
+/**
+ * ModelReadTest
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.operations
+ */
+class ModelReadTest extends BaseModelTest {
+
+/**
+ * testFetchingNonUniqueFKJoinTableRecords()
+ *
+ * Tests if the results are properly returned in the case there are non-unique FK's
+ * in the join table but another fields value is different. For example:
+ * something_id | something_else_id | doomed = 1
+ * something_id | something_else_id | doomed = 0
+ * Should return both records and not just one.
+ *
+ * @access public
+ * @return void
+ */
+	function testFetchingNonUniqueFKJoinTableRecords() {
+		$this->loadFixtures('Something', 'SomethingElse', 'JoinThing');
+		$Something = new Something();
+
+		$joinThingData = array(
+			'JoinThing' => array(
+				'something_id' => 1,
+				'something_else_id' => 2,
+				'doomed' => '0',
+				'created' => '2007-03-18 10:39:23',
+				'updated' => '2007-03-18 10:41:31'
+			)
+		);
+
+		$Something->JoinThing->create($joinThingData);
+		$Something->JoinThing->save();
+
+		$result = $Something->JoinThing->find('all', array('conditions' => array('something_else_id' => 2)));
+		$this->assertEqual($result[0]['JoinThing']['doomed'], true);
+		$this->assertEqual($result[1]['JoinThing']['doomed'], false);
+
+		$result = $Something->find('first');
+		$this->assertEqual(count($result['SomethingElse']), 2);
+
+		$doomed = Set::extract('/JoinThing/doomed', $result['SomethingElse']);
+		$this->assertTrue(in_array(true, $doomed));
+		$this->assertTrue(in_array(false, $doomed));
+	}
+
+/**
+ * testGroupBy method
+ *
+ * These tests will never pass with Postgres or Oracle as all fields in a select must be
+ * part of an aggregate function or in the GROUP BY statement.
+ *
+ * @access public
+ * @return void
+ */
+	function testGroupBy() {
+		$db = ConnectionManager::getDataSource('test_suite');
+		$isStrictGroupBy = in_array($db->config['driver'], array('postgres', 'oracle'));
+		$message = '%s Postgres and Oracle have strict GROUP BY and are incompatible with this test.';
+
+		if ($this->skipIf($isStrictGroupBy, $message )) {
+			return;
+		}
+
+		$this->loadFixtures('Project', 'Product', 'Thread', 'Message', 'Bid');
+		$Thread =& new Thread();
+		$Product =& new Product();
+
+		$result = $Thread->find('all', array(
+			'group' => 'Thread.project_id',
+			'order' => 'Thread.id ASC'
+		));
+
+		$expected = array(
+			array(
+				'Thread' => array(
+					'id' => 1,
+					'project_id' => 1,
+					'name' => 'Project 1, Thread 1'
+				),
+				'Project' => array(
+					'id' => 1,
+					'name' => 'Project 1'
+				),
+				'Message' => array(
+					array(
+						'id' => 1,
+						'thread_id' => 1,
+						'name' => 'Thread 1, Message 1'
+			))),
+			array(
+				'Thread' => array(
+					'id' => 3,
+					'project_id' => 2,
+					'name' => 'Project 2, Thread 1'
+				),
+				'Project' => array(
+					'id' => 2,
+					'name' => 'Project 2'
+				),
+				'Message' => array(
+					array(
+						'id' => 3,
+						'thread_id' => 3,
+						'name' => 'Thread 3, Message 1'
+		))));
+		$this->assertEqual($result, $expected);
+
+		$rows = $Thread->find('all', array(
+			'group' => 'Thread.project_id',
+			'fields' => array('Thread.project_id', 'COUNT(*) AS total')
+		));
+		$result = array();
+		foreach($rows as $row) {
+			$result[$row['Thread']['project_id']] = $row[0]['total'];
+		}
+		$expected = array(
+			1 => 2,
+			2 => 1
+		);
+		$this->assertEqual($result, $expected);
+
+		$rows = $Thread->find('all', array(
+			'group' => 'Thread.project_id',
+			'fields' => array('Thread.project_id', 'COUNT(*) AS total'),
+			'order'=> 'Thread.project_id'
+		));
+		$result = array();
+		foreach($rows as $row) {
+			$result[$row['Thread']['project_id']] = $row[0]['total'];
+		}
+		$expected = array(
+			1 => 2,
+			2 => 1
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $Thread->find('all', array(
+			'conditions' => array('Thread.project_id' => 1),
+			'group' => 'Thread.project_id'
+		));
+		$expected = array(
+			array(
+				'Thread' => array(
+					'id' => 1,
+					'project_id' => 1,
+					'name' => 'Project 1, Thread 1'
+				),
+				'Project' => array(
+					'id' => 1,
+					'name' => 'Project 1'
+				),
+				'Message' => array(
+					array(
+						'id' => 1,
+						'thread_id' => 1,
+						'name' => 'Thread 1, Message 1'
+		))));
+		$this->assertEqual($result, $expected);
+
+		$result = $Thread->find('all', array(
+			'conditions' => array('Thread.project_id' => 1),
+			'group' => 'Thread.project_id, Project.id'
+		));
+		$this->assertEqual($result, $expected);
+
+		$result = $Thread->find('all', array(
+			'conditions' => array('Thread.project_id' => 1),
+			'group' => 'project_id'
+		));
+		$this->assertEqual($result, $expected);
+
+		$result = $Thread->find('all', array(
+			'conditions' => array('Thread.project_id' => 1),
+			'group' => array('project_id')
+		));
+		$this->assertEqual($result, $expected);
+
+		$result = $Thread->find('all', array(
+			'conditions' => array('Thread.project_id' => 1),
+			'group' => array('project_id', 'Project.id')
+		));
+		$this->assertEqual($result, $expected);
+
+		$result = $Thread->find('all', array(
+			'conditions' => array('Thread.project_id' => 1),
+			'group' => array('Thread.project_id', 'Project.id')
+		));
+		$this->assertEqual($result, $expected);
+
+		$expected = array(
+			array('Product' => array('type' => 'Clothing'), array('price' => 32)),
+			array('Product' => array('type' => 'Food'), array('price' => 9)),
+			array('Product' => array('type' => 'Music'), array( 'price' => 4)),
+			array('Product' => array('type' => 'Toy'), array('price' => 3))
+		);
+		$result = $Product->find('all',array(
+			'fields'=>array('Product.type', 'MIN(Product.price) as price'),
+			'group'=> 'Product.type',
+			'order' => 'Product.type ASC'
+			));
+		$this->assertEqual($result, $expected);
+
+		$result = $Product->find('all', array(
+			'fields'=>array('Product.type', 'MIN(Product.price) as price'),
+			'group'=> array('Product.type'),
+			'order' => 'Product.type ASC'));
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testOldQuery method
+ *
+ * @access public
+ * @return void
+ */
+	function testOldQuery() {
+		$this->loadFixtures('Article');
+		$Article =& new Article();
+
+		$query  = 'SELECT title FROM ';
+		$query .= $this->db->fullTableName('articles');
+		$query .= ' WHERE ' . $this->db->fullTableName('articles') . '.id IN (1,2)';
+
+		$results = $Article->query($query);
+		$this->assertTrue(is_array($results));
+		$this->assertEqual(count($results), 2);
+
+		$query  = 'SELECT title, body FROM ';
+		$query .= $this->db->fullTableName('articles');
+		$query .= ' WHERE ' . $this->db->fullTableName('articles') . '.id = 1';
+
+		$results = $Article->query($query, false);
+		$this->assertTrue(!isset($this->db->_queryCache[$query]));
+		$this->assertTrue(is_array($results));
+
+		$query  = 'SELECT title, id FROM ';
+		$query .= $this->db->fullTableName('articles');
+		$query .= ' WHERE ' . $this->db->fullTableName('articles');
+		$query .= '.published = ' . $this->db->value('Y');
+
+		$results = $Article->query($query, true);
+		$this->assertTrue(isset($this->db->_queryCache[$query]));
+		$this->assertTrue(is_array($results));
+	}
+
+/**
+ * testPreparedQuery method
+ *
+ * @access public
+ * @return void
+ */
+	function testPreparedQuery() {
+		$this->loadFixtures('Article');
+		$Article =& new Article();
+		$this->db->_queryCache = array();
+
+		$finalQuery = 'SELECT title, published FROM ';
+		$finalQuery .= $this->db->fullTableName('articles');
+		$finalQuery .= ' WHERE ' . $this->db->fullTableName('articles');
+		$finalQuery .= '.id = ' . $this->db->value(1);
+		$finalQuery .= ' AND ' . $this->db->fullTableName('articles');
+		$finalQuery .= '.published = ' . $this->db->value('Y');
+
+		$query = 'SELECT title, published FROM ';
+		$query .= $this->db->fullTableName('articles');
+		$query .= ' WHERE ' . $this->db->fullTableName('articles');
+		$query .= '.id = ? AND ' . $this->db->fullTableName('articles') . '.published = ?';
+
+		$params = array(1, 'Y');
+		$result = $Article->query($query, $params);
+		$expected = array(
+			'0' => array(
+				$this->db->fullTableName('articles', false) => array(
+					'title' => 'First Article', 'published' => 'Y')
+		));
+
+		if (isset($result[0][0])) {
+			$expected[0][0] = $expected[0][$this->db->fullTableName('articles', false)];
+			unset($expected[0][$this->db->fullTableName('articles', false)]);
+		}
+
+		$this->assertEqual($result, $expected);
+		$this->assertTrue(isset($this->db->_queryCache[$finalQuery]));
+
+		$finalQuery = 'SELECT id, created FROM ';
+		$finalQuery .= $this->db->fullTableName('articles');
+		$finalQuery .= ' WHERE ' . $this->db->fullTableName('articles');
+		$finalQuery .= '.title = ' . $this->db->value('First Article');
+
+		$query  = 'SELECT id, created FROM ';
+		$query .= $this->db->fullTableName('articles');
+		$query .= '  WHERE ' . $this->db->fullTableName('articles') . '.title = ?';
+
+		$params = array('First Article');
+		$result = $Article->query($query, $params, false);
+		$this->assertTrue(is_array($result));
+		$this->assertTrue(
+			   isset($result[0][$this->db->fullTableName('articles', false)])
+			|| isset($result[0][0])
+		);
+		$this->assertFalse(isset($this->db->_queryCache[$finalQuery]));
+
+		$query  = 'SELECT title FROM ';
+		$query .= $this->db->fullTableName('articles');
+		$query .= ' WHERE ' . $this->db->fullTableName('articles') . '.title LIKE ?';
+
+		$params = array('%First%');
+		$result = $Article->query($query, $params);
+		$this->assertTrue(is_array($result));
+		$this->assertTrue(
+			   isset($result[0][$this->db->fullTableName('articles', false)]['title'])
+			|| isset($result[0][0]['title'])
+		);
+
+		//related to ticket #5035
+		$query  = 'SELECT title FROM ';
+		$query .= $this->db->fullTableName('articles') . ' WHERE title = ? AND published = ?';
+		$params = array('First? Article', 'Y');
+		$Article->query($query, $params);
+
+		$expected  = 'SELECT title FROM ';
+		$expected .= $this->db->fullTableName('articles');
+		$expected .= " WHERE title = 'First? Article' AND published = 'Y'";
+		$this->assertTrue(isset($this->db->_queryCache[$expected]));
+
+	}
+
+/**
+ * testParameterMismatch method
+ *
+ * @access public
+ * @return void
+ */
+	function testParameterMismatch() {
+		$this->loadFixtures('Article');
+		$Article =& new Article();
+
+		$query  = 'SELECT * FROM ' . $this->db->fullTableName('articles');
+		$query .= ' WHERE ' . $this->db->fullTableName('articles');
+		$query .= '.published = ? AND ' . $this->db->fullTableName('articles') . '.user_id = ?';
+		$params = array('Y');
+		$this->expectError();
+
+		ob_start();
+		$result = $Article->query($query, $params);
+		ob_end_clean();
+		$this->assertEqual($result, null);
+	}
+
+/**
+ * testVeryStrangeUseCase method
+ *
+ * @access public
+ * @return void
+ */
+	function testVeryStrangeUseCase() {
+		$message = "%s skipping SELECT * FROM ? WHERE ? = ? AND ? = ?; prepared query.";
+		$message .= " MSSQL is incompatible with this test.";
+
+		if ($this->skipIf($this->db->config['driver'] == 'mssql', $message)) {
+			return;
+		}
+
+		$this->loadFixtures('Article');
+		$Article =& new Article();
+
+		$query = 'SELECT * FROM ? WHERE ? = ? AND ? = ?';
+		$param = array(
+			$this->db->fullTableName('articles'),
+			$this->db->fullTableName('articles') . '.user_id', '3',
+			$this->db->fullTableName('articles') . '.published', 'Y'
+		);
+		$this->expectError();
+
+		ob_start();
+		$result = $Article->query($query, $param);
+		ob_end_clean();
+	}
+
+/**
+ * testRecursiveUnbind method
+ *
+ * @access public
+ * @return void
+ */
+	function testRecursiveUnbind() {
+		$this->loadFixtures('Apple', 'Sample');
+		$TestModel =& new Apple();
+		$TestModel->recursive = 2;
+
+		$result = $TestModel->find('all');
+		$expected = array(
+			array(
+				'Apple' => array (
+					'id' => 1,
+					'apple_id' => 2,
+					'color' => 'Red 1',
+					'name' => 'Red Apple 1',
+					'created' => '2006-11-22 10:38:58',
+					'date' => '1951-01-04',
+					'modified' => '2006-12-01 13:31:26',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => 2,
+					'apple_id' => 1,
+					'color' => 'Bright Red 1',
+					'name' => 'Bright Red Apple',
+					'created' => '2006-11-22 10:43:13',
+					'date' => '2014-01-01',
+					'modified' => '2006-11-30 18:38:10',
+					'mytime' => '22:57:17',
+					'Parent' => array(
+						'id' => 1,
+						'apple_id' => 2,
+						'color' => 'Red 1',
+						'name' => 'Red Apple 1',
+						'created' => '2006-11-22 10:38:58',
+						'date' => '1951-01-04',
+						'modified' => '2006-12-01 13:31:26',
+						'mytime' => '22:57:17'
+					),
+					'Sample' => array(
+						'id' => 2,
+						'apple_id' => 2,
+						'name' => 'sample2'
+					),
+					'Child' => array(
+						array(
+							'id' => 1,
+							'apple_id' => 2,
+							'color' => 'Red 1',
+							'name' => 'Red Apple 1',
+							'created' => '2006-11-22 10:38:58',
+							'date' => '1951-01-04',
+							'modified' => '2006-12-01 13:31:26',
+							'mytime' => '22:57:17'
+						),
+						array(
+							'id' => 3,
+							'apple_id' => 2,
+							'color' => 'blue green',
+							'name' => 'green blue',
+							'created' => '2006-12-25 05:13:36',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:23:24',
+							'mytime' => '22:57:17'
+						),
+						array(
+							'id' => 4,
+							'apple_id' => 2,
+							'color' => 'Blue Green',
+							'name' => 'Test Name',
+							'created' => '2006-12-25 05:23:36',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:23:36',
+							'mytime' => '22:57:17'
+					))),
+					'Sample' => array(
+						'id' =>'',
+						'apple_id' => '',
+						'name' => ''
+					),
+					'Child' => array(
+						array(
+							'id' => 2,
+							'apple_id' => 1,
+							'color' => 'Bright Red 1',
+							'name' => 'Bright Red Apple',
+							'created' => '2006-11-22 10:43:13',
+							'date' => '2014-01-01',
+							'modified' => '2006-11-30 18:38:10',
+							'mytime' => '22:57:17',
+							'Parent' => array(
+								'id' => 1,
+								'apple_id' => 2,
+								'color' => 'Red 1',
+								'name' => 'Red Apple 1',
+								'created' => '2006-11-22 10:38:58',
+								'date' => '1951-01-04',
+								'modified' => '2006-12-01 13:31:26',
+								'mytime' => '22:57:17'
+							),
+							'Sample' => array(
+								'id' => 2,
+								'apple_id' => 2,
+								'name' => 'sample2'
+							),
+							'Child' => array(
+								array(
+									'id' => 1,
+									'apple_id' => 2,
+									'color' => 'Red 1',
+									'name' => 'Red Apple 1',
+									'created' => '2006-11-22 10:38:58',
+									'date' => '1951-01-04',
+									'modified' => '2006-12-01 13:31:26',
+									'mytime' => '22:57:17'
+								),
+								array(
+									'id' => 3,
+									'apple_id' => 2,
+									'color' => 'blue green',
+									'name' => 'green blue',
+									'created' => '2006-12-25 05:13:36',
+									'date' => '2006-12-25',
+									'modified' => '2006-12-25 05:23:24',
+									'mytime' => '22:57:17'
+								),
+								array(
+									'id' => 4,
+									'apple_id' => 2,
+									'color' => 'Blue Green',
+									'name' => 'Test Name',
+									'created' => '2006-12-25 05:23:36',
+									'date' => '2006-12-25',
+									'modified' => '2006-12-25 05:23:36',
+									'mytime' => '22:57:17'
+			))))),
+			array(
+				'Apple' => array(
+					'id' => 2,
+					'apple_id' => 1,
+					'color' => 'Bright Red 1',
+					'name' => 'Bright Red Apple',
+					'created' => '2006-11-22 10:43:13',
+					'date' => '2014-01-01',
+					'modified' => '2006-11-30 18:38:10',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+						'id' => 1,
+						'apple_id' => 2,
+						'color' => 'Red 1',
+						'name' => 'Red Apple 1',
+						'created' => '2006-11-22 10:38:58',
+						'date' => '1951-01-04',
+						'modified' => '2006-12-01 13:31:26',
+						'mytime' => '22:57:17',
+						'Parent' => array(
+							'id' => 2,
+							'apple_id' => 1,
+							'color' => 'Bright Red 1',
+							'name' => 'Bright Red Apple',
+							'created' => '2006-11-22 10:43:13',
+							'date' => '2014-01-01',
+							'modified' => '2006-11-30 18:38:10',
+							'mytime' => '22:57:17'
+						),
+						'Sample' => array(),
+						'Child' => array(
+							array(
+								'id' => 2,
+								'apple_id' => 1,
+								'color' => 'Bright Red 1',
+								'name' => 'Bright Red Apple',
+								'created' => '2006-11-22 10:43:13',
+								'date' => '2014-01-01',
+								'modified' => '2006-11-30 18:38:10',
+								'mytime' => '22:57:17'
+					))),
+					'Sample' => array(
+						'id' => 2,
+						'apple_id' => 2,
+						'name' => 'sample2',
+						'Apple' => array(
+							'id' => 2,
+							'apple_id' => 1,
+							'color' => 'Bright Red 1',
+							'name' => 'Bright Red Apple',
+							'created' => '2006-11-22 10:43:13',
+							'date' => '2014-01-01',
+							'modified' => '2006-11-30 18:38:10',
+							'mytime' => '22:57:17'
+					)),
+					'Child' => array(
+						array(
+							'id' => 1,
+							'apple_id' => 2,
+							'color' => 'Red 1',
+							'name' => 'Red Apple 1',
+							'created' => '2006-11-22 10:38:58',
+							'date' => '1951-01-04',
+							'modified' => '2006-12-01 13:31:26',
+							'mytime' => '22:57:17',
+							'Parent' => array(
+								'id' => 2,
+								'apple_id' => 1,
+								'color' => 'Bright Red 1',
+								'name' => 'Bright Red Apple',
+								'created' => '2006-11-22 10:43:13',
+								'date' => '2014-01-01',
+								'modified' => '2006-11-30 18:38:10',
+								'mytime' => '22:57:17'
+							),
+							'Sample' => array(),
+							'Child' => array(
+								array(
+									'id' => 2,
+									'apple_id' => 1,
+									'color' => 'Bright Red 1',
+									'name' => 'Bright Red Apple',
+									'created' => '2006-11-22 10:43:13',
+									'date' => '2014-01-01',
+									'modified' => '2006-11-30 18:38:10',
+									'mytime' => '22:57:17'
+						))),
+						array(
+							'id' => 3,
+							'apple_id' => 2,
+							'color' => 'blue green',
+							'name' => 'green blue',
+							'created' => '2006-12-25 05:13:36',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:23:24',
+							'mytime' => '22:57:17',
+							'Parent' => array(
+								'id' => 2,
+								'apple_id' => 1,
+								'color' => 'Bright Red 1',
+								'name' => 'Bright Red Apple',
+								'created' => '2006-11-22 10:43:13',
+								'date' => '2014-01-01',
+								'modified' => '2006-11-30 18:38:10',
+								'mytime' => '22:57:17'
+							),
+							'Sample' => array(
+								'id' => 1,
+								'apple_id' => 3,
+								'name' => 'sample1'
+						)),
+						array(
+							'id' => 4,
+							'apple_id' => 2,
+							'color' => 'Blue Green',
+							'name' => 'Test Name',
+							'created' => '2006-12-25 05:23:36',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:23:36',
+							'mytime' => '22:57:17',
+							'Parent' => array(
+								'id' => 2,
+								'apple_id' => 1,
+								'color' => 'Bright Red 1',
+								'name' => 'Bright Red Apple',
+								'created' => '2006-11-22 10:43:13',
+								'date' => '2014-01-01',
+								'modified' => '2006-11-30 18:38:10',
+								'mytime' => '22:57:17'
+							),
+							'Sample' => array(
+								'id' => 3,
+								'apple_id' => 4,
+								'name' => 'sample3'
+							),
+							'Child' => array(
+								array(
+									'id' => 6,
+									'apple_id' => 4,
+									'color' => 'My new appleOrange',
+									'name' => 'My new apple',
+									'created' => '2006-12-25 05:29:39',
+									'date' => '2006-12-25',
+									'modified' => '2006-12-25 05:29:39',
+									'mytime' => '22:57:17'
+			))))),
+			array(
+				'Apple' => array(
+					'id' => 3,
+					'apple_id' => 2,
+					'color' => 'blue green',
+					'name' => 'green blue',
+					'created' => '2006-12-25 05:13:36',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:23:24',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => 2,
+					'apple_id' => 1,
+					'color' => 'Bright Red 1',
+					'name' => 'Bright Red Apple',
+					'created' => '2006-11-22 10:43:13',
+					'date' => '2014-01-01',
+					'modified' => '2006-11-30 18:38:10',
+					'mytime' => '22:57:17',
+					'Parent' => array(
+						'id' => 1,
+						'apple_id' => 2,
+						'color' => 'Red 1',
+						'name' => 'Red Apple 1',
+						'created' => '2006-11-22 10:38:58',
+						'date' => '1951-01-04',
+						'modified' => '2006-12-01 13:31:26',
+						'mytime' => '22:57:17'
+					),
+					'Sample' => array(
+						'id' => 2,
+						'apple_id' => 2,
+						'name' => 'sample2'
+					),
+					'Child' => array(
+						array(
+							'id' => 1,
+							'apple_id' => 2,
+							'color' => 'Red 1',
+							'name' => 'Red Apple 1',
+							'created' => '2006-11-22 10:38:58',
+							'date' => '1951-01-04',
+							'modified' => '2006-12-01 13:31:26',
+							'mytime' => '22:57:17'
+						),
+						array(
+							'id' => 3,
+							'apple_id' => 2,
+							'color' => 'blue green',
+							'name' => 'green blue',
+							'created' => '2006-12-25 05:13:36',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:23:24',
+							'mytime' => '22:57:17'
+						),
+						array(
+							'id' => 4,
+							'apple_id' => 2,
+							'color' => 'Blue Green',
+							'name' => 'Test Name',
+							'created' => '2006-12-25 05:23:36',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:23:36',
+							'mytime' => '22:57:17'
+				))),
+				'Sample' => array(
+					'id' => 1,
+					'apple_id' => 3,
+					'name' => 'sample1',
+					'Apple' => array(
+						'id' => 3,
+						'apple_id' => 2,
+						'color' => 'blue green',
+						'name' => 'green blue',
+						'created' => '2006-12-25 05:13:36',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:23:24',
+						'mytime' => '22:57:17'
+				)),
+				'Child' => array()
+			),
+			array(
+				'Apple' => array(
+					'id' => 4,
+					'apple_id' => 2,
+					'color' => 'Blue Green',
+					'name' => 'Test Name',
+					'created' => '2006-12-25 05:23:36',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:23:36',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => 2,
+					'apple_id' => 1,
+					'color' => 'Bright Red 1',
+					'name' => 'Bright Red Apple',
+					'created' => '2006-11-22 10:43:13',
+					'date' => '2014-01-01',
+					'modified' => '2006-11-30 18:38:10',
+					'mytime' => '22:57:17',
+					'Parent' => array(
+						'id' => 1,
+						'apple_id' => 2,
+						'color' => 'Red 1',
+						'name' => 'Red Apple 1',
+						'created' => '2006-11-22 10:38:58',
+						'date' => '1951-01-04',
+						'modified' => '2006-12-01 13:31:26', 'mytime' => '22:57:17'),
+						'Sample' => array('id' => 2, 'apple_id' => 2, 'name' => 'sample2'),
+						'Child' => array(
+							array(
+								'id' => 1,
+								'apple_id' => 2,
+								'color' => 'Red 1',
+								'name' => 'Red Apple 1',
+								'created' => '2006-11-22 10:38:58',
+								'date' => '1951-01-04',
+								'modified' => '2006-12-01 13:31:26',
+								'mytime' => '22:57:17'
+							),
+							array(
+								'id' => 3,
+								'apple_id' => 2,
+								'color' => 'blue green',
+								'name' => 'green blue',
+								'created' => '2006-12-25 05:13:36',
+								'date' => '2006-12-25',
+								'modified' => '2006-12-25 05:23:24',
+								'mytime' => '22:57:17'
+							),
+							array(
+								'id' => 4,
+								'apple_id' => 2,
+								'color' => 'Blue Green',
+								'name' => 'Test Name',
+								'created' => '2006-12-25 05:23:36',
+								'date' => '2006-12-25',
+								'modified' => '2006-12-25 05:23:36',
+								'mytime' => '22:57:17'
+				))),
+				'Sample' => array(
+					'id' => 3,
+					'apple_id' => 4,
+					'name' => 'sample3',
+					'Apple' => array(
+						'id' => 4,
+						'apple_id' => 2,
+						'color' => 'Blue Green',
+						'name' => 'Test Name',
+						'created' => '2006-12-25 05:23:36',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:23:36',
+						'mytime' => '22:57:17'
+				)),
+				'Child' => array(
+					array(
+						'id' => 6,
+						'apple_id' => 4,
+						'color' => 'My new appleOrange',
+						'name' => 'My new apple',
+						'created' => '2006-12-25 05:29:39',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:29:39',
+						'mytime' => '22:57:17',
+						'Parent' => array(
+							'id' => 4,
+							'apple_id' => 2,
+							'color' => 'Blue Green',
+							'name' => 'Test Name',
+							'created' => '2006-12-25 05:23:36',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:23:36',
+							'mytime' => '22:57:17'
+						),
+						'Sample' => array(),
+						'Child' => array(
+							array(
+								'id' => 7,
+								'apple_id' => 6,
+								'color' => 'Some wierd color',
+								'name' => 'Some odd color',
+								'created' => '2006-12-25 05:34:21',
+								'date' => '2006-12-25',
+								'modified' => '2006-12-25 05:34:21',
+								'mytime' => '22:57:17'
+			))))),
+			array(
+				'Apple' => array(
+					'id' => 5,
+					'apple_id' => 5,
+					'color' => 'Green',
+					'name' => 'Blue Green',
+					'created' => '2006-12-25 05:24:06',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:29:16',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => 5,
+					'apple_id' => 5,
+					'color' => 'Green',
+					'name' => 'Blue Green',
+					'created' => '2006-12-25 05:24:06',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:29:16',
+					'mytime' => '22:57:17',
+					'Parent' => array(
+						'id' => 5,
+						'apple_id' => 5,
+						'color' => 'Green',
+						'name' => 'Blue Green',
+						'created' => '2006-12-25 05:24:06',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:29:16',
+						'mytime' => '22:57:17'
+					),
+					'Sample' => array(
+						'id' => 4,
+						'apple_id' => 5,
+						'name' => 'sample4'
+					),
+					'Child' => array(
+						array(
+							'id' => 5,
+							'apple_id' => 5,
+							'color' => 'Green',
+							'name' => 'Blue Green',
+							'created' => '2006-12-25 05:24:06',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:29:16',
+							'mytime' => '22:57:17'
+				))),
+				'Sample' => array(
+					'id' => 4,
+					'apple_id' => 5,
+					'name' => 'sample4',
+					'Apple' => array(
+						'id' => 5,
+						'apple_id' => 5,
+						'color' => 'Green',
+						'name' => 'Blue Green',
+						'created' => '2006-12-25 05:24:06',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:29:16',
+						'mytime' => '22:57:17'
+					)),
+					'Child' => array(
+						array(
+							'id' => 5,
+							'apple_id' => 5,
+							'color' => 'Green',
+							'name' => 'Blue Green',
+							'created' => '2006-12-25 05:24:06',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:29:16',
+							'mytime' => '22:57:17',
+							'Parent' => array(
+								'id' => 5,
+								'apple_id' => 5,
+								'color' => 'Green',
+								'name' => 'Blue Green',
+								'created' => '2006-12-25 05:24:06',
+								'date' => '2006-12-25',
+								'modified' => '2006-12-25 05:29:16',
+								'mytime' => '22:57:17'
+							),
+							'Sample' => array(
+								'id' => 4,
+								'apple_id' => 5,
+								'name' => 'sample4'
+							),
+							'Child' => array(
+								array(
+									'id' => 5,
+									'apple_id' => 5,
+									'color' => 'Green',
+									'name' => 'Blue Green',
+									'created' => '2006-12-25 05:24:06',
+									'date' => '2006-12-25',
+									'modified' => '2006-12-25 05:29:16',
+									'mytime' => '22:57:17'
+			))))),
+			array(
+				'Apple' => array(
+					'id' => 6,
+					'apple_id' => 4,
+					'color' => 'My new appleOrange',
+					'name' => 'My new apple',
+					'created' => '2006-12-25 05:29:39',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:29:39',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => 4,
+					'apple_id' => 2,
+					'color' => 'Blue Green',
+					'name' => 'Test Name',
+					'created' => '2006-12-25 05:23:36',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:23:36',
+					'mytime' => '22:57:17',
+					'Parent' => array(
+						'id' => 2,
+						'apple_id' => 1,
+						'color' => 'Bright Red 1',
+						'name' => 'Bright Red Apple',
+						'created' => '2006-11-22 10:43:13',
+						'date' => '2014-01-01',
+						'modified' => '2006-11-30 18:38:10',
+						'mytime' => '22:57:17'
+					),
+					'Sample' => array(
+						'id' => 3,
+						'apple_id' => 4,
+						'name' => 'sample3'
+					),
+					'Child' => array(
+						array(
+							'id' => 6,
+							'apple_id' => 4,
+							'color' => 'My new appleOrange',
+							'name' => 'My new apple',
+							'created' => '2006-12-25 05:29:39',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:29:39',
+							'mytime' => '22:57:17'
+				))),
+				'Sample' => array(
+					'id' => '',
+					'apple_id' => '',
+					'name' => ''
+				),
+				'Child' => array(
+					array(
+						'id' => 7,
+						'apple_id' => 6,
+						'color' => 'Some wierd color',
+						'name' => 'Some odd color',
+						'created' => '2006-12-25 05:34:21',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:34:21',
+						'mytime' => '22:57:17',
+						'Parent' => array(
+							'id' => 6,
+							'apple_id' => 4,
+							'color' => 'My new appleOrange',
+							'name' => 'My new apple',
+							'created' => '2006-12-25 05:29:39',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:29:39',
+							'mytime' => '22:57:17'
+						),
+						'Sample' => array()
+			))),
+			array(
+				'Apple' => array(
+					'id' => 7,
+					'apple_id' => 6,
+					'color' =>
+					'Some wierd color',
+					'name' => 'Some odd color',
+					'created' => '2006-12-25 05:34:21',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:34:21',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => 6,
+					'apple_id' => 4,
+					'color' => 'My new appleOrange',
+					'name' => 'My new apple',
+					'created' => '2006-12-25 05:29:39',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:29:39',
+					'mytime' => '22:57:17',
+					'Parent' => array(
+						'id' => 4,
+						'apple_id' => 2,
+						'color' => 'Blue Green',
+						'name' => 'Test Name',
+						'created' => '2006-12-25 05:23:36',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:23:36',
+						'mytime' => '22:57:17'
+					),
+					'Sample' => array(),
+					'Child' => array(
+						array(
+							'id' => 7,
+							'apple_id' => 6,
+							'color' => 'Some wierd color',
+							'name' => 'Some odd color',
+							'created' => '2006-12-25 05:34:21',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:34:21',
+							'mytime' => '22:57:17'
+				))),
+				'Sample' => array(
+					'id' => '',
+					'apple_id' => '',
+					'name' => ''
+				),
+				'Child' => array()));
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->Parent->unbindModel(array('hasOne' => array('Sample')));
+		$this->assertTrue($result);
+
+		$result = $TestModel->find('all');
+		$expected = array(
+			array(
+				'Apple' => array(
+					'id' => 1,
+					'apple_id' => 2,
+					'color' => 'Red 1',
+					'name' => 'Red Apple 1',
+					'created' => '2006-11-22 10:38:58',
+					'date' => '1951-01-04',
+					'modified' => '2006-12-01 13:31:26',
+					'mytime' => '22:57:17'),
+					'Parent' => array(
+						'id' => 2,
+						'apple_id' => 1,
+						'color' => 'Bright Red 1',
+						'name' => 'Bright Red Apple',
+						'created' => '2006-11-22 10:43:13',
+						'date' => '2014-01-01',
+						'modified' => '2006-11-30 18:38:10',
+						'mytime' => '22:57:17',
+						'Parent' => array(
+							'id' => 1,
+							'apple_id' => 2,
+							'color' => 'Red 1',
+							'name' => 'Red Apple 1',
+							'created' => '2006-11-22 10:38:58',
+							'date' => '1951-01-04',
+							'modified' => '2006-12-01 13:31:26',
+							'mytime' => '22:57:17'
+						),
+						'Child' => array(
+							array(
+								'id' => 1,
+								'apple_id' => 2,
+								'color' => 'Red 1',
+								'name' => 'Red Apple 1',
+								'created' => '2006-11-22 10:38:58',
+								'date' => '1951-01-04',
+								'modified' => '2006-12-01 13:31:26',
+								'mytime' => '22:57:17'
+							),
+							array(
+								'id' => 3,
+								'apple_id' => 2,
+								'color' => 'blue green',
+								'name' => 'green blue',
+								'created' => '2006-12-25 05:13:36',
+								'date' => '2006-12-25',
+								'modified' => '2006-12-25 05:23:24',
+								'mytime' => '22:57:17'
+							),
+							array(
+								'id' => 4,
+								'apple_id' => 2,
+								'color' => 'Blue Green',
+								'name' => 'Test Name',
+								'created' => '2006-12-25 05:23:36',
+								'date' => '2006-12-25',
+								'modified' => '2006-12-25 05:23:36',
+								'mytime' => '22:57:17'
+					))),
+					'Sample' => array(
+						'id' =>'',
+						'apple_id' => '',
+						'name' => ''
+					),
+					'Child' => array(
+						array(
+							'id' => 2,
+							'apple_id' => 1,
+							'color' => 'Bright Red 1',
+							'name' => 'Bright Red Apple',
+							'created' => '2006-11-22 10:43:13',
+							'date' => '2014-01-01',
+							'modified' => '2006-11-30 18:38:10',
+							'mytime' => '22:57:17',
+							'Parent' => array(
+								'id' => 1,
+								'apple_id' => 2,
+								'color' => 'Red 1',
+								'name' => 'Red Apple 1',
+								'created' => '2006-11-22 10:38:58',
+								'date' => '1951-01-04',
+								'modified' => '2006-12-01 13:31:26',
+								'mytime' => '22:57:17'
+							),
+							'Sample' => array(
+								'id' => 2,
+								'apple_id' => 2,
+								'name' => 'sample2'
+							),
+							'Child' => array(
+								array(
+									'id' => 1,
+									'apple_id' => 2,
+									'color' => 'Red 1',
+									'name' => 'Red Apple 1',
+									'created' => '2006-11-22 10:38:58',
+									'date' => '1951-01-04',
+									'modified' => '2006-12-01 13:31:26',
+									'mytime' => '22:57:17'
+								),
+								array(
+									'id' => 3,
+									'apple_id' => 2,
+									'color' => 'blue green',
+									'name' => 'green blue',
+									'created' => '2006-12-25 05:13:36',
+									'date' => '2006-12-25',
+									'modified' => '2006-12-25 05:23:24',
+									'mytime' => '22:57:17'
+								),
+								array(
+									'id' => 4,
+									'apple_id' => 2,
+									'color' => 'Blue Green',
+									'name' => 'Test Name',
+									'created' => '2006-12-25 05:23:36',
+									'date' => '2006-12-25',
+									'modified' => '2006-12-25 05:23:36',
+									'mytime' => '22:57:17'
+			))))),
+			array(
+				'Apple' => array(
+					'id' => 2,
+					'apple_id' => 1,
+					'color' => 'Bright Red 1',
+					'name' => 'Bright Red Apple',
+					'created' => '2006-11-22 10:43:13',
+					'date' => '2014-01-01',
+					'modified' => '2006-11-30 18:38:10',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => 1,
+					'apple_id' => 2,
+					'color' => 'Red 1',
+					'name' => 'Red Apple 1',
+					'created' => '2006-11-22 10:38:58',
+					'date' => '1951-01-04',
+					'modified' => '2006-12-01 13:31:26',
+					'mytime' => '22:57:17',
+					'Parent' => array(
+						'id' => 2,
+						'apple_id' => 1,
+						'color' => 'Bright Red 1',
+						'name' => 'Bright Red Apple',
+						'created' => '2006-11-22 10:43:13',
+						'date' => '2014-01-01',
+						'modified' => '2006-11-30 18:38:10',
+						'mytime' => '22:57:17'
+					),
+					'Child' => array(
+						array(
+							'id' => 2,
+							'apple_id' => 1,
+							'color' => 'Bright Red 1',
+							'name' => 'Bright Red Apple',
+							'created' => '2006-11-22 10:43:13',
+							'date' => '2014-01-01',
+							'modified' => '2006-11-30 18:38:10',
+							'mytime' => '22:57:17'
+				))),
+				'Sample' => array(
+					'id' => 2,
+					'apple_id' => 2,
+					'name' => 'sample2',
+					'Apple' => array(
+						'id' => 2,
+						'apple_id' => 1,
+						'color' => 'Bright Red 1',
+						'name' => 'Bright Red Apple',
+						'created' => '2006-11-22 10:43:13',
+						'date' => '2014-01-01',
+						'modified' => '2006-11-30 18:38:10',
+						'mytime' => '22:57:17'
+				)),
+				'Child' => array(
+					array(
+						'id' => 1,
+						'apple_id' => 2,
+						'color' => 'Red 1',
+						'name' => 'Red Apple 1',
+						'created' => '2006-11-22 10:38:58',
+						'date' => '1951-01-04',
+						'modified' => '2006-12-01 13:31:26',
+						'mytime' => '22:57:17',
+						'Parent' => array(
+							'id' => 2,
+							'apple_id' => 1,
+							'color' => 'Bright Red 1',
+							'name' => 'Bright Red Apple',
+							'created' => '2006-11-22 10:43:13',
+							'date' => '2014-01-01',
+							'modified' => '2006-11-30 18:38:10',
+							'mytime' => '22:57:17'
+						),
+						'Sample' => array(),
+						'Child' => array(
+							array(
+								'id' => 2,
+								'apple_id' => 1,
+								'color' => 'Bright Red 1',
+								'name' => 'Bright Red Apple',
+								'created' => '2006-11-22 10:43:13',
+								'date' => '2014-01-01', 'modified' =>
+								'2006-11-30 18:38:10',
+								'mytime' => '22:57:17'
+					))),
+					array(
+						'id' => 3,
+						'apple_id' => 2,
+						'color' => 'blue green',
+						'name' => 'green blue',
+						'created' => '2006-12-25 05:13:36',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:23:24',
+						'mytime' => '22:57:17',
+						'Parent' => array(
+							'id' => 2,
+							'apple_id' => 1,
+							'color' => 'Bright Red 1',
+							'name' => 'Bright Red Apple',
+							'created' => '2006-11-22 10:43:13',
+							'date' => '2014-01-01',
+							'modified' => '2006-11-30 18:38:10',
+							'mytime' => '22:57:17'
+						),
+						'Sample' => array(
+							'id' => 1,
+							'apple_id' => 3,
+							'name' => 'sample1'
+					)),
+					array(
+						'id' => 4,
+						'apple_id' => 2,
+						'color' => 'Blue Green',
+						'name' => 'Test Name',
+						'created' => '2006-12-25 05:23:36',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:23:36',
+						'mytime' => '22:57:17',
+						'Parent' => array(
+							'id' => 2,
+							'apple_id' => 1,
+							'color' => 'Bright Red 1',
+							'name' => 'Bright Red Apple',
+							'created' => '2006-11-22 10:43:13',
+							'date' => '2014-01-01',
+							'modified' => '2006-11-30 18:38:10',
+							'mytime' => '22:57:17'
+						),
+						'Sample' => array(
+							'id' => 3,
+							'apple_id' => 4,
+							'name' => 'sample3'
+						),
+						'Child' => array(
+							array(
+								'id' => 6,
+								'apple_id' => 4,
+								'color' => 'My new appleOrange',
+								'name' => 'My new apple',
+								'created' => '2006-12-25 05:29:39',
+								'date' => '2006-12-25',
+								'modified' => '2006-12-25 05:29:39',
+								'mytime' => '22:57:17'
+			))))),
+			array(
+				'Apple' => array(
+					'id' => 3,
+					'apple_id' => 2,
+					'color' => 'blue green',
+					'name' => 'green blue',
+					'created' => '2006-12-25 05:13:36',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:23:24',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => 2,
+					'apple_id' => 1,
+					'color' => 'Bright Red 1',
+					'name' => 'Bright Red Apple',
+					'created' => '2006-11-22 10:43:13',
+					'date' => '2014-01-01',
+					'modified' => '2006-11-30 18:38:10',
+					'mytime' => '22:57:17',
+					'Parent' => array(
+						'id' => 1,
+						'apple_id' => 2,
+						'color' => 'Red 1',
+						'name' => 'Red Apple 1',
+						'created' => '2006-11-22 10:38:58',
+						'date' => '1951-01-04',
+						'modified' => '2006-12-01 13:31:26',
+						'mytime' => '22:57:17'
+					),
+					'Child' => array(
+						array(
+							'id' => 1,
+							'apple_id' => 2,
+							'color' => 'Red 1',
+							'name' => 'Red Apple 1',
+							'created' => '2006-11-22 10:38:58',
+							'date' => '1951-01-04',
+							'modified' => '2006-12-01 13:31:26',
+							'mytime' => '22:57:17'
+						),
+						array(
+							'id' => 3,
+							'apple_id' => 2,
+							'color' => 'blue green',
+							'name' => 'green blue',
+							'created' => '2006-12-25 05:13:36',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:23:24',
+							'mytime' => '22:57:17'
+						),
+						array(
+							'id' => 4,
+							'apple_id' => 2,
+							'color' => 'Blue Green',
+							'name' => 'Test Name',
+							'created' => '2006-12-25 05:23:36',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:23:36',
+							'mytime' => '22:57:17'
+				))),
+				'Sample' => array(
+					'id' => 1,
+					'apple_id' => 3,
+					'name' => 'sample1',
+					'Apple' => array(
+						'id' => 3,
+						'apple_id' => 2,
+						'color' => 'blue green',
+						'name' => 'green blue',
+						'created' => '2006-12-25 05:13:36',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:23:24',
+						'mytime' => '22:57:17'
+				)),
+				'Child' => array()
+			),
+			array(
+				'Apple' => array(
+					'id' => 4,
+					'apple_id' => 2,
+					'color' => 'Blue Green',
+					'name' => 'Test Name',
+					'created' => '2006-12-25 05:23:36',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:23:36',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => 2,
+					'apple_id' => 1,
+					'color' => 'Bright Red 1',
+					'name' => 'Bright Red Apple',
+					'created' => '2006-11-22 10:43:13',
+					'date' => '2014-01-01',
+					'modified' => '2006-11-30 18:38:10',
+					'mytime' => '22:57:17',
+					'Parent' => array(
+						'id' => 1,
+						'apple_id' => 2,
+						'color' => 'Red 1',
+						'name' => 'Red Apple 1',
+						'created' => '2006-11-22 10:38:58',
+						'date' => '1951-01-04',
+						'modified' => '2006-12-01 13:31:26',
+						'mytime' => '22:57:17'
+					),
+					'Child' => array(
+						array(
+							'id' => 1,
+							'apple_id' => 2,
+							'color' => 'Red 1',
+							'name' => 'Red Apple 1',
+							'created' => '2006-11-22 10:38:58',
+							'date' => '1951-01-04',
+							'modified' => '2006-12-01 13:31:26',
+							'mytime' => '22:57:17'
+						),
+						array(
+							'id' => 3,
+							'apple_id' => 2,
+							'color' => 'blue green',
+							'name' => 'green blue',
+							'created' => '2006-12-25 05:13:36',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:23:24',
+							'mytime' => '22:57:17'
+						),
+						array(
+							'id' => 4,
+							'apple_id' => 2,
+							'color' => 'Blue Green',
+							'name' => 'Test Name',
+							'created' => '2006-12-25 05:23:36',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:23:36',
+							'mytime' => '22:57:17'
+				))),
+				'Sample' => array(
+					'id' => 3,
+					'apple_id' => 4,
+					'name' => 'sample3',
+					'Apple' => array(
+						'id' => 4,
+						'apple_id' => 2,
+						'color' => 'Blue Green',
+						'name' => 'Test Name',
+						'created' => '2006-12-25 05:23:36',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:23:36',
+						'mytime' => '22:57:17'
+				)),
+				'Child' => array(
+					array(
+						'id' => 6,
+						'apple_id' => 4,
+						'color' => 'My new appleOrange',
+						'name' => 'My new apple',
+						'created' => '2006-12-25 05:29:39',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:29:39',
+						'mytime' => '22:57:17',
+						'Parent' => array(
+							'id' => 4,
+							'apple_id' => 2,
+							'color' => 'Blue Green',
+							'name' => 'Test Name',
+							'created' => '2006-12-25 05:23:36',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:23:36',
+							'mytime' => '22:57:17'
+						),
+						'Sample' => array(),
+							'Child' => array(
+								array(
+									'id' => 7,
+									'apple_id' => 6,
+									'color' => 'Some wierd color',
+									'name' => 'Some odd color',
+									'created' => '2006-12-25 05:34:21',
+									'date' => '2006-12-25',
+									'modified' => '2006-12-25 05:34:21',
+									'mytime' => '22:57:17'
+			))))),
+			array(
+				'Apple' => array(
+					'id' => 5,
+					'apple_id' => 5,
+					'color' => 'Green',
+					'name' => 'Blue Green',
+					'created' => '2006-12-25 05:24:06',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:29:16',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => 5,
+					'apple_id' => 5,
+					'color' => 'Green',
+					'name' => 'Blue Green',
+					'created' => '2006-12-25 05:24:06',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:29:16',
+					'mytime' => '22:57:17',
+					'Parent' => array(
+						'id' => 5,
+						'apple_id' => 5,
+						'color' => 'Green',
+						'name' => 'Blue Green',
+						'created' => '2006-12-25 05:24:06',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:29:16',
+						'mytime' => '22:57:17'
+					),
+					'Child' => array(
+						array(
+							'id' => 5,
+							'apple_id' => 5,
+							'color' => 'Green',
+							'name' => 'Blue Green',
+							'created' => '2006-12-25 05:24:06',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:29:16',
+							'mytime' => '22:57:17'
+				))),
+				'Sample' => array(
+					'id' => 4,
+					'apple_id' => 5,
+					'name' => 'sample4',
+					'Apple' => array(
+						'id' => 5,
+						'apple_id' => 5,
+						'color' => 'Green',
+						'name' => 'Blue Green',
+						'created' => '2006-12-25 05:24:06',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:29:16',
+						'mytime' => '22:57:17'
+				)),
+				'Child' => array(
+					array(
+						'id' => 5,
+						'apple_id' => 5,
+						'color' => 'Green',
+						'name' => 'Blue Green',
+						'created' => '2006-12-25 05:24:06',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:29:16',
+						'mytime' => '22:57:17',
+						'Parent' => array(
+							'id' => 5,
+							'apple_id' => 5,
+							'color' => 'Green',
+							'name' => 'Blue Green',
+							'created' => '2006-12-25 05:24:06',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:29:16',
+							'mytime' => '22:57:17'
+						),
+						'Sample' => array(
+							'id' => 4,
+							'apple_id' => 5,
+							'name' => 'sample4'
+						),
+						'Child' => array(
+							array(
+								'id' => 5,
+								'apple_id' => 5,
+								'color' => 'Green',
+								'name' => 'Blue Green',
+								'created' => '2006-12-25 05:24:06',
+								'date' => '2006-12-25',
+								'modified' => '2006-12-25 05:29:16',
+								'mytime' => '22:57:17'
+			))))),
+			array(
+				'Apple' => array(
+					'id' => 6,
+					'apple_id' => 4,
+					'color' => 'My new appleOrange',
+					'name' => 'My new apple',
+					'created' => '2006-12-25 05:29:39',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:29:39',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => 4,
+					'apple_id' => 2,
+					'color' => 'Blue Green',
+					'name' => 'Test Name',
+					'created' => '2006-12-25 05:23:36',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:23:36',
+					'mytime' => '22:57:17',
+					'Parent' => array(
+						'id' => 2,
+						'apple_id' => 1,
+						'color' => 'Bright Red 1',
+						'name' => 'Bright Red Apple',
+						'created' => '2006-11-22 10:43:13',
+						'date' => '2014-01-01',
+						'modified' => '2006-11-30 18:38:10',
+						'mytime' => '22:57:17'
+					),
+					'Child' => array(
+						array(
+							'id' => 6,
+							'apple_id' => 4,
+							'color' => 'My new appleOrange',
+							'name' => 'My new apple',
+							'created' => '2006-12-25 05:29:39',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:29:39',
+							'mytime' => '22:57:17'
+				))),
+				'Sample' => array(
+					'id' => '',
+					'apple_id' => '',
+					'name' => ''
+				),
+				'Child' => array(
+					array(
+						'id' => 7,
+						'apple_id' => 6,
+						'color' => 'Some wierd color',
+						'name' => 'Some odd color',
+						'created' => '2006-12-25 05:34:21',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:34:21',
+						'mytime' => '22:57:17',
+						'Parent' => array(
+							'id' => 6,
+							'apple_id' => 4,
+							'color' => 'My new appleOrange',
+							'name' => 'My new apple',
+							'created' => '2006-12-25 05:29:39',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:29:39',
+							'mytime' => '22:57:17'
+						),
+						'Sample' => array()
+			))),
+			array(
+				'Apple' => array(
+					'id' => 7,
+					'apple_id' => 6,
+					'color' => 'Some wierd color',
+					'name' => 'Some odd color',
+					'created' => '2006-12-25 05:34:21',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:34:21',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => 6,
+					'apple_id' => 4,
+					'color' => 'My new appleOrange',
+					'name' => 'My new apple',
+					'created' => '2006-12-25 05:29:39',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:29:39',
+					'mytime' => '22:57:17',
+					'Parent' => array(
+						'id' => 4,
+						'apple_id' => 2,
+						'color' => 'Blue Green',
+						'name' => 'Test Name',
+						'created' => '2006-12-25 05:23:36',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:23:36',
+						'mytime' => '22:57:17'
+					),
+					'Child' => array(
+						array(
+							'id' => 7,
+							'apple_id' => 6,
+							'color' => 'Some wierd color',
+							'name' => 'Some odd color',
+							'created' => '2006-12-25 05:34:21',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:34:21',
+							'mytime' => '22:57:17'
+				))),
+				'Sample' => array(
+					'id' => '',
+					'apple_id' => '',
+					'name' => ''
+				),
+				'Child' => array()
+		));
+
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->Parent->unbindModel(array('hasOne' => array('Sample')));
+		$this->assertTrue($result);
+
+		$result = $TestModel->unbindModel(array('hasMany' => array('Child')));
+		$this->assertTrue($result);
+
+		$result = $TestModel->find('all');
+		$expected = array(
+			array(
+				'Apple' => array (
+					'id' => 1,
+					'apple_id' => 2,
+					'color' => 'Red 1',
+					'name' => 'Red Apple 1',
+					'created' => '2006-11-22 10:38:58',
+					'date' => '1951-01-04',
+					'modified' => '2006-12-01 13:31:26',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => 2,
+					'apple_id' => 1,
+					'color' => 'Bright Red 1',
+					'name' => 'Bright Red Apple',
+					'created' => '2006-11-22 10:43:13',
+					'date' => '2014-01-01',
+					'modified' => '2006-11-30 18:38:10',
+					'mytime' => '22:57:17',
+					'Parent' => array(
+						'id' => 1,
+						'apple_id' => 2,
+						'color' => 'Red 1',
+						'name' => 'Red Apple 1',
+						'created' => '2006-11-22 10:38:58',
+						'date' => '1951-01-04',
+						'modified' => '2006-12-01 13:31:26',
+						'mytime' => '22:57:17'
+					),
+					'Child' => array(
+						array(
+							'id' => 1,
+							'apple_id' => 2,
+							'color' => 'Red 1',
+							'name' => 'Red Apple 1',
+							'created' => '2006-11-22 10:38:58',
+							'date' => '1951-01-04',
+							'modified' => '2006-12-01 13:31:26',
+							'mytime' => '22:57:17'
+						),
+						array(
+							'id' => 3,
+							'apple_id' => 2,
+							'color' => 'blue green',
+							'name' => 'green blue',
+							'created' => '2006-12-25 05:13:36',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:23:24',
+							'mytime' => '22:57:17'
+						),
+						array(
+							'id' => 4,
+							'apple_id' => 2,
+							'color' => 'Blue Green',
+							'name' => 'Test Name',
+							'created' => '2006-12-25 05:23:36',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:23:36',
+							'mytime' => '22:57:17'
+				))),
+				'Sample' => array(
+					'id' =>'',
+					'apple_id' => '',
+					'name' => ''
+			)),
+			array(
+				'Apple' => array(
+					'id' => 2,
+					'apple_id' => 1,
+					'color' => 'Bright Red 1',
+					'name' => 'Bright Red Apple',
+					'created' => '2006-11-22 10:43:13',
+					'date' => '2014-01-01',
+					'modified' => '2006-11-30 18:38:10',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => 1,
+					'apple_id' => 2,
+					'color' => 'Red 1',
+					'name' => 'Red Apple 1',
+					'created' => '2006-11-22 10:38:58',
+					'date' => '1951-01-04',
+					'modified' => '2006-12-01 13:31:26',
+					'mytime' => '22:57:17',
+					'Parent' => array(
+						'id' => 2,
+						'apple_id' => 1,
+						'color' => 'Bright Red 1',
+						'name' => 'Bright Red Apple',
+						'created' => '2006-11-22 10:43:13',
+						'date' => '2014-01-01',
+						'modified' => '2006-11-30 18:38:10',
+						'mytime' => '22:57:17'
+					),
+					'Child' => array(
+						array(
+							'id' => 2,
+							'apple_id' => 1,
+							'color' => 'Bright Red 1',
+							'name' => 'Bright Red Apple',
+							'created' => '2006-11-22 10:43:13',
+							'date' => '2014-01-01',
+							'modified' => '2006-11-30 18:38:10',
+							'mytime' => '22:57:17'
+				))),
+				'Sample' => array(
+					'id' => 2,
+					'apple_id' => 2,
+					'name' => 'sample2',
+					'Apple' => array(
+						'id' => 2,
+						'apple_id' => 1,
+						'color' => 'Bright Red 1',
+						'name' => 'Bright Red Apple',
+						'created' => '2006-11-22 10:43:13',
+						'date' => '2014-01-01',
+						'modified' => '2006-11-30 18:38:10',
+						'mytime' => '22:57:17'
+			))),
+			array(
+				'Apple' => array(
+				'id' => 3,
+				'apple_id' => 2,
+				'color' => 'blue green',
+				'name' => 'green blue',
+				'created' => '2006-12-25 05:13:36',
+				'date' => '2006-12-25',
+				'modified' => '2006-12-25 05:23:24',
+				'mytime' => '22:57:17'
+			),
+			'Parent' => array(
+				'id' => 2,
+				'apple_id' => 1,
+				'color' => 'Bright Red 1',
+				'name' => 'Bright Red Apple',
+				'created' => '2006-11-22 10:43:13',
+				'date' => '2014-01-01',
+				'modified' => '2006-11-30 18:38:10',
+				'mytime' => '22:57:17',
+				'Parent' => array(
+					'id' => 1,
+					'apple_id' => 2,
+					'color' => 'Red 1',
+					'name' => 'Red Apple 1',
+					'created' => '2006-11-22 10:38:58',
+					'date' => '1951-01-04',
+					'modified' => '2006-12-01 13:31:26',
+					'mytime' => '22:57:17'
+				),
+				'Child' => array(
+					array(
+						'id' => 1,
+						'apple_id' => 2,
+						'color' => 'Red 1',
+						'name' => 'Red Apple 1',
+						'created' => '2006-11-22 10:38:58',
+						'date' => '1951-01-04',
+						'modified' => '2006-12-01 13:31:26',
+						'mytime' => '22:57:17'
+					),
+					array(
+						'id' => 3,
+						'apple_id' => 2,
+						'color' => 'blue green',
+						'name' => 'green blue',
+						'created' => '2006-12-25 05:13:36',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:23:24',
+						'mytime' => '22:57:17'
+					),
+					array(
+						'id' => 4,
+						'apple_id' => 2,
+						'color' => 'Blue Green',
+						'name' => 'Test Name',
+						'created' => '2006-12-25 05:23:36',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:23:36',
+						'mytime' => '22:57:17'
+			))),
+			'Sample' => array(
+				'id' => 1,
+				'apple_id' => 3,
+				'name' => 'sample1',
+				'Apple' => array(
+					'id' => 3,
+					'apple_id' => 2,
+					'color' => 'blue green',
+					'name' => 'green blue',
+					'created' => '2006-12-25 05:13:36',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:23:24',
+					'mytime' => '22:57:17'
+		))),
+		array(
+			'Apple' => array(
+				'id' => 4,
+				'apple_id' => 2,
+				'color' => 'Blue Green',
+				'name' => 'Test Name',
+				'created' => '2006-12-25 05:23:36',
+				'date' => '2006-12-25',
+				'modified' => '2006-12-25 05:23:36',
+				'mytime' => '22:57:17'
+			),
+			'Parent' => array(
+				'id' => 2,
+				'apple_id' => 1,
+				'color' => 'Bright Red 1',
+				'name' => 'Bright Red Apple',
+				'created' => '2006-11-22 10:43:13',
+				'date' => '2014-01-01',
+				'modified' => '2006-11-30 18:38:10',
+				'mytime' => '22:57:17',
+				'Parent' => array(
+					'id' => 1,
+					'apple_id' => 2,
+					'color' => 'Red 1',
+					'name' => 'Red Apple 1',
+					'created' => '2006-11-22 10:38:58',
+					'date' => '1951-01-04',
+					'modified' => '2006-12-01 13:31:26',
+					'mytime' => '22:57:17'
+				),
+				'Child' => array(
+					array(
+						'id' => 1,
+						'apple_id' => 2,
+						'color' => 'Red 1',
+						'name' => 'Red Apple 1',
+						'created' => '2006-11-22 10:38:58',
+						'date' => '1951-01-04',
+						'modified' => '2006-12-01 13:31:26',
+						'mytime' => '22:57:17'
+					),
+					array(
+						'id' => 3,
+						'apple_id' => 2,
+						'color' => 'blue green',
+						'name' => 'green blue',
+						'created' => '2006-12-25 05:13:36',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:23:24',
+						'mytime' => '22:57:17'
+					),
+					array(
+						'id' => 4,
+						'apple_id' => 2,
+						'color' => 'Blue Green',
+						'name' => 'Test Name',
+						'created' => '2006-12-25 05:23:36',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:23:36',
+						'mytime' => '22:57:17'
+			))),
+			'Sample' => array(
+				'id' => 3,
+				'apple_id' => 4,
+				'name' => 'sample3',
+				'Apple' => array(
+					'id' => 4,
+					'apple_id' => 2,
+					'color' => 'Blue Green',
+					'name' => 'Test Name',
+					'created' => '2006-12-25 05:23:36',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:23:36',
+					'mytime' => '22:57:17'
+		))),
+		array(
+			'Apple' => array(
+				'id' => 5,
+				'apple_id' => 5,
+				'color' => 'Green',
+				'name' => 'Blue Green',
+				'created' => '2006-12-25 05:24:06',
+				'date' => '2006-12-25',
+				'modified' => '2006-12-25 05:29:16',
+				'mytime' => '22:57:17'
+			),
+			'Parent' => array(
+				'id' => 5,
+				'apple_id' => 5,
+				'color' => 'Green',
+				'name' => 'Blue Green',
+				'created' => '2006-12-25 05:24:06',
+				'date' => '2006-12-25',
+				'modified' => '2006-12-25 05:29:16',
+				'mytime' => '22:57:17',
+				'Parent' => array(
+					'id' => 5,
+					'apple_id' => 5,
+					'color' => 'Green',
+					'name' => 'Blue Green',
+					'created' => '2006-12-25 05:24:06',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:29:16',
+					'mytime' => '22:57:17'
+				),
+				'Child' => array(
+					array(
+						'id' => 5,
+						'apple_id' => 5,
+						'color' => 'Green',
+						'name' => 'Blue Green',
+						'created' => '2006-12-25 05:24:06',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:29:16',
+						'mytime' => '22:57:17'
+			))),
+			'Sample' => array(
+				'id' => 4,
+				'apple_id' => 5,
+				'name' => 'sample4',
+				'Apple' => array(
+					'id' => 5,
+					'apple_id' => 5,
+					'color' => 'Green',
+					'name' => 'Blue Green',
+					'created' => '2006-12-25 05:24:06',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:29:16',
+					'mytime' => '22:57:17'
+		))),
+		array(
+			'Apple' => array(
+				'id' => 6,
+				'apple_id' => 4,
+				'color' => 'My new appleOrange',
+				'name' => 'My new apple',
+				'created' => '2006-12-25 05:29:39',
+				'date' => '2006-12-25',
+				'modified' => '2006-12-25 05:29:39',
+				'mytime' => '22:57:17'
+			),
+			'Parent' => array(
+				'id' => 4,
+				'apple_id' => 2,
+				'color' => 'Blue Green',
+				'name' => 'Test Name',
+				'created' => '2006-12-25 05:23:36',
+				'date' => '2006-12-25',
+				'modified' => '2006-12-25 05:23:36',
+				'mytime' => '22:57:17',
+				'Parent' => array(
+					'id' => 2,
+					'apple_id' => 1,
+					'color' => 'Bright Red 1',
+					'name' => 'Bright Red Apple',
+					'created' => '2006-11-22 10:43:13',
+					'date' => '2014-01-01',
+					'modified' => '2006-11-30 18:38:10',
+					'mytime' => '22:57:17'
+				),
+				'Child' => array(
+					array(
+						'id' => 6,
+						'apple_id' => 4,
+						'color' => 'My new appleOrange',
+						'name' => 'My new apple',
+						'created' => '2006-12-25 05:29:39',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:29:39',
+						'mytime' => '22:57:17'
+			))),
+			'Sample' => array(
+				'id' => '',
+				'apple_id' => '',
+				'name' => ''
+		)),
+		array(
+			'Apple' => array(
+				'id' => 7,
+				'apple_id' => 6,
+				'color' => 'Some wierd color',
+				'name' => 'Some odd color',
+				'created' => '2006-12-25 05:34:21',
+				'date' => '2006-12-25',
+				'modified' => '2006-12-25 05:34:21',
+				'mytime' => '22:57:17'
+			),
+			'Parent' => array(
+				'id' => 6,
+				'apple_id' => 4,
+				'color' => 'My new appleOrange',
+				'name' => 'My new apple',
+				'created' => '2006-12-25 05:29:39',
+				'date' => '2006-12-25',
+				'modified' => '2006-12-25 05:29:39',
+				'mytime' => '22:57:17',
+				'Parent' => array(
+					'id' => 4,
+					'apple_id' => 2,
+					'color' => 'Blue Green',
+					'name' => 'Test Name',
+					'created' => '2006-12-25 05:23:36',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:23:36',
+					'mytime' => '22:57:17'
+				),
+				'Child' => array(
+					array(
+						'id' => 7,
+						'apple_id' => 6,
+						'color' => 'Some wierd color',
+						'name' => 'Some odd color',
+						'created' => '2006-12-25 05:34:21',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:34:21',
+						'mytime' => '22:57:17'
+			))),
+			'Sample' => array(
+				'id' => '',
+				'apple_id' => '',
+				'name' => ''
+		)));
+
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->unbindModel(array('hasMany' => array('Child')));
+		$this->assertTrue($result);
+
+		$result = $TestModel->Sample->unbindModel(array('belongsTo' => array('Apple')));
+		$this->assertTrue($result);
+
+		$result = $TestModel->find('all');
+		$expected = array(
+			array(
+				'Apple' => array(
+					'id' => 1,
+					'apple_id' => 2,
+					'color' => 'Red 1',
+					'name' => 'Red Apple 1',
+					'created' => '2006-11-22 10:38:58',
+					'date' => '1951-01-04',
+					'modified' => '2006-12-01 13:31:26',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => 2,
+					'apple_id' => 1,
+					'color' => 'Bright Red 1',
+					'name' => 'Bright Red Apple',
+					'created' => '2006-11-22 10:43:13',
+					'date' => '2014-01-01',
+					'modified' => '2006-11-30 18:38:10',
+					'mytime' => '22:57:17',
+					'Parent' => array(
+						'id' => 1,
+						'apple_id' => 2,
+						'color' => 'Red 1',
+						'name' => 'Red Apple 1',
+						'created' => '2006-11-22 10:38:58',
+						'date' => '1951-01-04',
+						'modified' => '2006-12-01 13:31:26',
+						'mytime' => '22:57:17'
+					),
+					'Sample' => array(
+						'id' => 2,
+						'apple_id' => 2,
+						'name' => 'sample2'
+					),
+					'Child' => array(
+						array(
+							'id' => 1,
+							'apple_id' => 2,
+							'color' => 'Red 1',
+							'name' => 'Red Apple 1',
+							'created' => '2006-11-22 10:38:58',
+							'date' => '1951-01-04',
+							'modified' => '2006-12-01 13:31:26',
+							'mytime' => '22:57:17'
+						),
+						array(
+							'id' => 3,
+							'apple_id' => 2,
+							'color' => 'blue green',
+							'name' => 'green blue',
+							'created' => '2006-12-25 05:13:36',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:23:24',
+							'mytime' => '22:57:17'
+						),
+						array(
+							'id' => 4,
+							'apple_id' => 2,
+							'color' => 'Blue Green',
+							'name' => 'Test Name',
+							'created' => '2006-12-25 05:23:36',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:23:36',
+							'mytime' => '22:57:17'
+				))),
+				'Sample' => array(
+					'id' =>'',
+					'apple_id' => '',
+					'name' => ''
+			)),
+			array(
+				'Apple' => array(
+					'id' => 2,
+					'apple_id' => 1,
+					'color' => 'Bright Red 1',
+					'name' => 'Bright Red Apple',
+					'created' => '2006-11-22 10:43:13',
+					'date' => '2014-01-01',
+					'modified' => '2006-11-30 18:38:10',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => 1,
+					'apple_id' => 2,
+					'color' => 'Red 1',
+					'name' => 'Red Apple 1',
+					'created' => '2006-11-22 10:38:58',
+					'date' => '1951-01-04',
+					'modified' => '2006-12-01 13:31:26',
+					'mytime' => '22:57:17',
+					'Parent' => array(
+						'id' => 2,
+						'apple_id' => 1,
+						'color' => 'Bright Red 1',
+						'name' => 'Bright Red Apple',
+						'created' => '2006-11-22 10:43:13',
+						'date' => '2014-01-01',
+						'modified' => '2006-11-30 18:38:10',
+						'mytime' => '22:57:17'
+					),
+					'Sample' => array(),
+					'Child' => array(
+						array(
+							'id' => 2,
+							'apple_id' => 1,
+							'color' => 'Bright Red 1',
+							'name' => 'Bright Red Apple',
+							'created' => '2006-11-22 10:43:13',
+							'date' => '2014-01-01',
+							'modified' => '2006-11-30 18:38:10',
+							'mytime' => '22:57:17'
+				))),
+				'Sample' => array(
+					'id' => 2,
+					'apple_id' => 2,
+					'name' => 'sample2'
+			)),
+			array(
+				'Apple' => array(
+					'id' => 3,
+					'apple_id' => 2,
+					'color' => 'blue green',
+					'name' => 'green blue',
+					'created' => '2006-12-25 05:13:36',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:23:24',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => 2,
+					'apple_id' => 1,
+					'color' => 'Bright Red 1',
+					'name' => 'Bright Red Apple',
+					'created' => '2006-11-22 10:43:13',
+					'date' => '2014-01-01',
+					'modified' => '2006-11-30 18:38:10',
+					'mytime' => '22:57:17',
+					'Parent' => array(
+						'id' => 1,
+						'apple_id' => 2,
+						'color' => 'Red 1',
+						'name' => 'Red Apple 1',
+						'created' => '2006-11-22 10:38:58',
+						'date' => '1951-01-04',
+						'modified' => '2006-12-01 13:31:26',
+						'mytime' => '22:57:17'
+					),
+					'Sample' => array(
+						'id' => 2,
+						'apple_id' => 2,
+						'name' => 'sample2'
+					),
+					'Child' => array(
+						array(
+							'id' => 1,
+							'apple_id' => 2,
+							'color' => 'Red 1',
+							'name' => 'Red Apple 1',
+							'created' => '2006-11-22 10:38:58',
+							'date' => '1951-01-04',
+							'modified' => '2006-12-01 13:31:26',
+							'mytime' => '22:57:17'
+						),
+						array(
+							'id' => 3,
+							'apple_id' => 2,
+							'color' => 'blue green',
+							'name' => 'green blue',
+							'created' => '2006-12-25 05:13:36',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:23:24',
+							'mytime' => '22:57:17'
+						),
+						array(
+							'id' => 4,
+							'apple_id' => 2,
+							'color' => 'Blue Green',
+							'name' => 'Test Name',
+							'created' => '2006-12-25 05:23:36',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:23:36',
+							'mytime' => '22:57:17'
+				))),
+				'Sample' => array(
+					'id' => 1,
+					'apple_id' => 3,
+					'name' => 'sample1'
+			)),
+			array(
+				'Apple' => array(
+					'id' => 4,
+					'apple_id' => 2,
+					'color' => 'Blue Green',
+					'name' => 'Test Name',
+					'created' => '2006-12-25 05:23:36',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:23:36',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => 2,
+					'apple_id' => 1,
+					'color' => 'Bright Red 1',
+					'name' => 'Bright Red Apple',
+					'created' => '2006-11-22 10:43:13',
+					'date' => '2014-01-01',
+					'modified' => '2006-11-30 18:38:10',
+					'mytime' => '22:57:17',
+					'Parent' => array(
+						'id' => 1,
+						'apple_id' => 2,
+						'color' => 'Red 1',
+						'name' => 'Red Apple 1',
+						'created' => '2006-11-22 10:38:58',
+						'date' => '1951-01-04',
+						'modified' => '2006-12-01 13:31:26',
+						'mytime' => '22:57:17'
+					),
+					'Sample' => array(
+						'id' => 2,
+						'apple_id' => 2,
+						'name' => 'sample2'
+					),
+					'Child' => array(
+						array(
+							'id' => 1,
+							'apple_id' => 2,
+							'color' => 'Red 1',
+							'name' => 'Red Apple 1',
+							'created' => '2006-11-22 10:38:58',
+							'date' => '1951-01-04',
+							'modified' => '2006-12-01 13:31:26',
+							'mytime' => '22:57:17'
+						),
+						array(
+							'id' => 3,
+							'apple_id' => 2,
+							'color' => 'blue green',
+							'name' => 'green blue',
+							'created' => '2006-12-25 05:13:36',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:23:24',
+							'mytime' => '22:57:17'
+						),
+						array(
+							'id' => 4,
+							'apple_id' => 2,
+							'color' => 'Blue Green',
+							'name' => 'Test Name',
+							'created' => '2006-12-25 05:23:36',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:23:36',
+							'mytime' => '22:57:17'
+				))),
+				'Sample' => array(
+					'id' => 3,
+					'apple_id' => 4,
+					'name' => 'sample3'
+			)),
+			array(
+				'Apple' => array(
+					'id' => 5,
+					'apple_id' => 5,
+					'color' => 'Green',
+					'name' => 'Blue Green',
+					'created' => '2006-12-25 05:24:06',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:29:16',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => 5,
+					'apple_id' => 5,
+					'color' => 'Green',
+					'name' => 'Blue Green',
+					'created' => '2006-12-25 05:24:06',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:29:16',
+					'mytime' => '22:57:17',
+					'Parent' => array(
+						'id' => 5,
+						'apple_id' => 5,
+						'color' => 'Green',
+						'name' => 'Blue Green',
+						'created' => '2006-12-25 05:24:06',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:29:16',
+						'mytime' => '22:57:17'
+					),
+					'Sample' => array(
+						'id' => 4,
+						'apple_id' => 5,
+						'name' => 'sample4'
+					),
+					'Child' => array(
+						array(
+							'id' => 5,
+							'apple_id' => 5,
+							'color' => 'Green',
+							'name' => 'Blue Green',
+							'created' => '2006-12-25 05:24:06',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:29:16',
+							'mytime' => '22:57:17'
+				))),
+				'Sample' => array(
+					'id' => 4,
+					'apple_id' => 5,
+					'name' => 'sample4'
+			)),
+			array(
+				'Apple' => array(
+					'id' => 6,
+					'apple_id' => 4,
+					'color' => 'My new appleOrange',
+					'name' => 'My new apple',
+					'created' => '2006-12-25 05:29:39',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:29:39',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => 4,
+					'apple_id' => 2,
+					'color' => 'Blue Green',
+					'name' => 'Test Name',
+					'created' => '2006-12-25 05:23:36',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:23:36',
+					'mytime' => '22:57:17',
+					'Parent' => array(
+						'id' => 2,
+						'apple_id' => 1,
+						'color' => 'Bright Red 1',
+						'name' => 'Bright Red Apple',
+						'created' => '2006-11-22 10:43:13',
+						'date' => '2014-01-01',
+						'modified' => '2006-11-30 18:38:10',
+						'mytime' => '22:57:17'
+					),
+					'Sample' => array(
+						'id' => 3,
+						'apple_id' => 4,
+						'name' => 'sample3'
+					),
+					'Child' => array(
+						array(
+							'id' => 6,
+							'apple_id' => 4,
+							'color' => 'My new appleOrange',
+							'name' => 'My new apple',
+							'created' => '2006-12-25 05:29:39',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:29:39',
+							'mytime' => '22:57:17'
+				))),
+				'Sample' => array(
+					'id' => '',
+					'apple_id' => '',
+					'name' => ''
+			)),
+			array(
+				'Apple' => array(
+					'id' => 7,
+					'apple_id' => 6,
+					'color' => 'Some wierd color',
+					'name' => 'Some odd color',
+					'created' => '2006-12-25 05:34:21',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:34:21',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => 6,
+					'apple_id' => 4,
+					'color' => 'My new appleOrange',
+					'name' => 'My new apple',
+					'created' => '2006-12-25 05:29:39',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:29:39',
+					'mytime' => '22:57:17',
+					'Parent' => array(
+						'id' => 4,
+						'apple_id' => 2,
+						'color' => 'Blue Green',
+						'name' => 'Test Name',
+						'created' => '2006-12-25 05:23:36',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:23:36',
+						'mytime' => '22:57:17'
+					),
+					'Sample' => array(),
+					'Child' => array(
+						array(
+							'id' => 7,
+							'apple_id' => 6,
+							'color' => 'Some wierd color',
+							'name' => 'Some odd color',
+							'created' => '2006-12-25 05:34:21',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:34:21',
+							'mytime' => '22:57:17'
+				))),
+				'Sample' => array(
+					'id' => '',
+					'apple_id' => '',
+					'name' => ''
+		)));
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->Parent->unbindModel(array('belongsTo' => array('Parent')));
+		$this->assertTrue($result);
+
+		$result = $TestModel->unbindModel(array('hasMany' => array('Child')));
+		$this->assertTrue($result);
+
+		$result = $TestModel->find('all');
+		$expected = array(
+			array(
+				'Apple' => array(
+					'id' => 1,
+					'apple_id' => 2,
+					'color' => 'Red 1',
+					'name' => 'Red Apple 1',
+					'created' => '2006-11-22 10:38:58',
+					'date' => '1951-01-04',
+					'modified' => '2006-12-01 13:31:26',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => 2,
+					'apple_id' => 1,
+					'color' => 'Bright Red 1',
+					'name' => 'Bright Red Apple',
+					'created' => '2006-11-22 10:43:13',
+					'date' => '2014-01-01',
+					'modified' => '2006-11-30 18:38:10',
+					'mytime' => '22:57:17',
+					'Sample' => array(
+						'id' => 2,
+						'apple_id' => 2,
+						'name' => 'sample2'
+					),
+					'Child' => array(
+						array(
+							'id' => 1,
+							'apple_id' => 2,
+							'color' => 'Red 1',
+							'name' => 'Red Apple 1',
+							'created' => '2006-11-22 10:38:58',
+							'date' => '1951-01-04',
+							'modified' => '2006-12-01 13:31:26',
+							'mytime' => '22:57:17'
+						),
+						array(
+							'id' => 3,
+							'apple_id' => 2,
+							'color' => 'blue green',
+							'name' => 'green blue',
+							'created' => '2006-12-25 05:13:36',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:23:24',
+							'mytime' => '22:57:17'
+						),
+						array(
+							'id' => 4,
+							'apple_id' => 2,
+							'color' => 'Blue Green',
+							'name' => 'Test Name',
+							'created' => '2006-12-25 05:23:36',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:23:36',
+							'mytime' => '22:57:17'
+				))),
+				'Sample' => array(
+					'id' =>'',
+					'apple_id' => '',
+					'name' => ''
+			)),
+			array(
+				'Apple' => array(
+					'id' => 2,
+					'apple_id' => 1,
+					'color' => 'Bright Red 1',
+					'name' => 'Bright Red Apple',
+					'created' => '2006-11-22 10:43:13',
+					'date' => '2014-01-01',
+					'modified' => '2006-11-30 18:38:10',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => 1,
+					'apple_id' => 2,
+					'color' => 'Red 1',
+					'name' => 'Red Apple 1',
+					'created' => '2006-11-22 10:38:58',
+					'date' => '1951-01-04',
+					'modified' => '2006-12-01 13:31:26',
+					'mytime' => '22:57:17',
+					'Sample' => array(),
+						'Child' => array(
+							array(
+								'id' => 2,
+								'apple_id' => 1,
+								'color' => 'Bright Red 1',
+								'name' => 'Bright Red Apple',
+								'created' => '2006-11-22 10:43:13',
+								'date' => '2014-01-01',
+								'modified' => '2006-11-30 18:38:10',
+								'mytime' => '22:57:17'
+				))),
+				'Sample' => array(
+					'id' => 2,
+					'apple_id' => 2,
+					'name' => 'sample2',
+					'Apple' => array(
+						'id' => 2,
+						'apple_id' => 1,
+						'color' => 'Bright Red 1',
+						'name' => 'Bright Red Apple',
+						'created' => '2006-11-22 10:43:13',
+						'date' => '2014-01-01',
+						'modified' => '2006-11-30 18:38:10',
+						'mytime' => '22:57:17'
+			))),
+			array(
+				'Apple' => array(
+					'id' => 3,
+					'apple_id' => 2,
+					'color' => 'blue green',
+					'name' => 'green blue',
+					'created' => '2006-12-25 05:13:36',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:23:24',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => 2,
+					'apple_id' => 1,
+					'color' => 'Bright Red 1',
+					'name' => 'Bright Red Apple',
+					'created' => '2006-11-22 10:43:13',
+					'date' => '2014-01-01',
+					'modified' => '2006-11-30 18:38:10',
+					'mytime' => '22:57:17',
+					'Sample' => array(
+						'id' => 2,
+						'apple_id' => 2,
+						'name' => 'sample2'
+					),
+					'Child' => array(
+						array(
+							'id' => 1,
+							'apple_id' => 2,
+							'color' => 'Red 1',
+							'name' => 'Red Apple 1',
+							'created' => '2006-11-22 10:38:58',
+							'date' => '1951-01-04',
+							'modified' => '2006-12-01 13:31:26',
+							'mytime' => '22:57:17'
+						),
+						array(
+							'id' => 3,
+							'apple_id' => 2,
+							'color' => 'blue green',
+							'name' => 'green blue',
+							'created' => '2006-12-25 05:13:36',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:23:24',
+							'mytime' => '22:57:17'
+						),
+						array(
+							'id' => 4,
+							'apple_id' => 2,
+							'color' => 'Blue Green',
+							'name' => 'Test Name',
+							'created' => '2006-12-25 05:23:36',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:23:36',
+							'mytime' => '22:57:17'
+				))),
+				'Sample' => array(
+					'id' => 1,
+					'apple_id' => 3,
+					'name' => 'sample1',
+					'Apple' => array(
+						'id' => 3,
+						'apple_id' => 2,
+						'color' => 'blue green',
+						'name' => 'green blue',
+						'created' => '2006-12-25 05:13:36',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:23:24',
+						'mytime' => '22:57:17'
+			))),
+			array(
+				'Apple' => array(
+					'id' => 4,
+					'apple_id' => 2,
+					'color' => 'Blue Green',
+					'name' => 'Test Name',
+					'created' => '2006-12-25 05:23:36',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:23:36',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => 2,
+					'apple_id' => 1,
+					'color' => 'Bright Red 1',
+					'name' => 'Bright Red Apple',
+					'created' => '2006-11-22 10:43:13',
+					'date' => '2014-01-01',
+					'modified' => '2006-11-30 18:38:10',
+					'mytime' => '22:57:17',
+					'Sample' => array(
+						'id' => 2,
+						'apple_id' => 2,
+						'name' => 'sample2'
+					),
+					'Child' => array(
+						array(
+							'id' => 1,
+							'apple_id' => 2,
+							'color' => 'Red 1',
+							'name' => 'Red Apple 1',
+							'created' => '2006-11-22 10:38:58',
+							'date' => '1951-01-04',
+							'modified' => '2006-12-01 13:31:26',
+							'mytime' => '22:57:17'
+						),
+						array(
+							'id' => 3,
+							'apple_id' => 2,
+							'color' => 'blue green',
+							'name' => 'green blue',
+							'created' => '2006-12-25 05:13:36',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:23:24',
+							'mytime' => '22:57:17'
+						),
+						array(
+							'id' => 4,
+							'apple_id' => 2,
+							'color' => 'Blue Green',
+							'name' => 'Test Name',
+							'created' => '2006-12-25 05:23:36',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:23:36',
+							'mytime' => '22:57:17'
+				))),
+				'Sample' => array(
+					'id' => 3,
+					'apple_id' => 4,
+					'name' => 'sample3',
+					'Apple' => array(
+						'id' => 4,
+						'apple_id' => 2,
+						'color' => 'Blue Green',
+						'name' => 'Test Name',
+						'created' => '2006-12-25 05:23:36',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:23:36',
+						'mytime' => '22:57:17'
+			))),
+			array(
+				'Apple' => array(
+					'id' => 5,
+					'apple_id' => 5,
+					'color' => 'Green',
+					'name' => 'Blue Green',
+					'created' => '2006-12-25 05:24:06',
+					'date' => '2006-12-25',
+					'modified' =>
+					'2006-12-25 05:29:16',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => 5,
+					'apple_id' => 5,
+					'color' => 'Green',
+					'name' => 'Blue Green',
+					'created' => '2006-12-25 05:24:06',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:29:16',
+					'mytime' => '22:57:17',
+					'Sample' => array(
+						'id' => 4,
+						'apple_id' => 5,
+						'name' => 'sample4'
+					),
+					'Child' => array(
+						array(
+							'id' => 5,
+							'apple_id' => 5,
+							'color' => 'Green',
+							'name' => 'Blue Green',
+							'created' => '2006-12-25 05:24:06',
+							'date' => '2006-12-25',
+							'modified' => '2006-12-25 05:29:16',
+							'mytime' => '22:57:17'
+				))),
+				'Sample' => array(
+					'id' => 4,
+					'apple_id' => 5,
+					'name' => 'sample4',
+					'Apple' => array(
+						'id' => 5,
+						'apple_id' => 5,
+						'color' => 'Green',
+						'name' => 'Blue Green',
+						'created' => '2006-12-25 05:24:06',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:29:16',
+						'mytime' => '22:57:17'
+			))),
+			array(
+				'Apple' => array(
+					'id' => 6,
+					'apple_id' => 4,
+					'color' => 'My new appleOrange',
+					'name' => 'My new apple',
+					'created' => '2006-12-25 05:29:39',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:29:39',
+					'mytime' => '22:57:17'),
+					'Parent' => array(
+						'id' => 4,
+						'apple_id' => 2,
+						'color' => 'Blue Green',
+						'name' => 'Test Name',
+						'created' => '2006-12-25 05:23:36',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:23:36',
+						'mytime' => '22:57:17',
+						'Sample' => array(
+							'id' => 3,
+							'apple_id' => 4,
+							'name' => 'sample3'
+						),
+						'Child' => array(
+							array(
+								'id' => 6,
+								'apple_id' => 4,
+								'color' => 'My new appleOrange',
+								'name' => 'My new apple',
+								'created' => '2006-12-25 05:29:39',
+								'date' => '2006-12-25',
+								'modified' => '2006-12-25 05:29:39',
+								'mytime' => '22:57:17'
+					))),
+					'Sample' => array(
+						'id' => '',
+						'apple_id' => '',
+						'name' => ''
+			)),
+			array(
+				'Apple' => array(
+					'id' => 7,
+					'apple_id' => 6,
+					'color' => 'Some wierd color',
+					'name' => 'Some odd color',
+					'created' => '2006-12-25 05:34:21',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:34:21',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => 6,
+					'apple_id' => 4,
+					'color' => 'My new appleOrange',
+					'name' => 'My new apple',
+					'created' => '2006-12-25 05:29:39',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:29:39',
+					'mytime' => '22:57:17',
+					'Sample' => array(),
+					'Child' => array(
+						array(
+							'id' => 7,
+							'apple_id' => 6,
+							'color' => 'Some wierd color',
+							'name' => 'Some odd color',
+							'created' => '2006-12-25 05:34:21',
+							'date' => '2006-12-25', 'modified' =>
+							'2006-12-25 05:34:21',
+							'mytime' => '22:57:17'
+				))),
+				'Sample' => array(
+					'id' => '',
+					'apple_id' => '',
+					'name' => ''
+		)));
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testSelfAssociationAfterFind method
+ *
+ * @access public
+ * @return void
+ */
+	function testSelfAssociationAfterFind() {
+		$this->loadFixtures('Apple');
+		$afterFindModel = new NodeAfterFind();
+		$afterFindModel->recursive = 3;
+		$afterFindData = $afterFindModel->find('all');
+
+		$duplicateModel = new NodeAfterFind();
+		$duplicateModel->recursive = 3;
+		$duplicateModelData = $duplicateModel->find('all');
+
+		$noAfterFindModel = new NodeNoAfterFind();
+		$noAfterFindModel->recursive = 3;
+		$noAfterFindData = $noAfterFindModel->find('all');
+
+		$this->assertFalse($afterFindModel == $noAfterFindModel);
+		// Limitation of PHP 4 and PHP 5 > 5.1.6 when comparing recursive objects
+		if (PHP_VERSION === '5.1.6') {
+			$this->assertFalse($afterFindModel != $duplicateModel);
+		}
+		$this->assertEqual($afterFindData, $noAfterFindData);
+	}
+
+/**
+ * testFindAllThreaded method
+ *
+ * @access public
+ * @return void
+ */
+	function testFindAllThreaded() {
+		$this->loadFixtures('Category');
+		$TestModel =& new Category();
+
+		$result = $TestModel->find('threaded');
+		$expected = array(
+			array(
+				'Category' => array(
+					'id' => '1',
+					'parent_id' => '0',
+					'name' => 'Category 1',
+					'created' => '2007-03-18 15:30:23',
+					'updated' => '2007-03-18 15:32:31'
+				),
+				'children' => array(
+					array(
+						'Category' => array(
+							'id' => '2',
+							'parent_id' => '1',
+							'name' => 'Category 1.1',
+							'created' => '2007-03-18 15:30:23',
+							'updated' => '2007-03-18 15:32:31'
+						),
+						'children' => array(
+							array('Category' => array(
+								'id' => '7',
+								'parent_id' => '2',
+								'name' => 'Category 1.1.1',
+								'created' => '2007-03-18 15:30:23',
+								'updated' => '2007-03-18 15:32:31'),
+								'children' => array()),
+							array('Category' => array(
+								'id' => '8',
+								'parent_id' => '2',
+								'name' => 'Category 1.1.2',
+								'created' => '2007-03-18 15:30:23',
+								'updated' => '2007-03-18 15:32:31'),
+								'children' => array()))
+					),
+					array(
+						'Category' => array(
+							'id' => '3',
+							'parent_id' => '1',
+							'name' => 'Category 1.2',
+							'created' => '2007-03-18 15:30:23',
+							'updated' => '2007-03-18 15:32:31'
+						),
+						'children' => array()
+					)
+				)
+			),
+			array(
+				'Category' => array(
+					'id' => '4',
+					'parent_id' => '0',
+					'name' => 'Category 2',
+					'created' => '2007-03-18 15:30:23',
+					'updated' => '2007-03-18 15:32:31'
+				),
+				'children' => array()
+			),
+			array(
+				'Category' => array(
+					'id' => '5',
+					'parent_id' => '0',
+					'name' => 'Category 3',
+					'created' => '2007-03-18 15:30:23',
+					'updated' => '2007-03-18 15:32:31'
+				),
+				'children' => array(
+					array(
+						'Category' => array(
+							'id' => '6',
+							'parent_id' => '5',
+							'name' => 'Category 3.1',
+							'created' => '2007-03-18 15:30:23',
+							'updated' => '2007-03-18 15:32:31'
+						),
+						'children' => array()
+					)
+				)
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('threaded', array(
+			'conditions' => array('Category.name LIKE' => 'Category 1%')
+		));
+
+		$expected = array(
+			array(
+				'Category' => array(
+					'id' => '1',
+					'parent_id' => '0',
+					'name' => 'Category 1',
+					'created' => '2007-03-18 15:30:23',
+					'updated' => '2007-03-18 15:32:31'
+				),
+				'children' => array(
+					array(
+						'Category' => array(
+							'id' => '2',
+							'parent_id' => '1',
+							'name' => 'Category 1.1',
+							'created' => '2007-03-18 15:30:23',
+							'updated' => '2007-03-18 15:32:31'
+						),
+						'children' => array(
+							array('Category' => array(
+								'id' => '7',
+								'parent_id' => '2',
+								'name' => 'Category 1.1.1',
+								'created' => '2007-03-18 15:30:23',
+								'updated' => '2007-03-18 15:32:31'),
+								'children' => array()),
+							array('Category' => array(
+								'id' => '8',
+								'parent_id' => '2',
+								'name' => 'Category 1.1.2',
+								'created' => '2007-03-18 15:30:23',
+								'updated' => '2007-03-18 15:32:31'),
+								'children' => array()))
+					),
+					array(
+						'Category' => array(
+							'id' => '3',
+							'parent_id' => '1',
+							'name' => 'Category 1.2',
+							'created' => '2007-03-18 15:30:23',
+							'updated' => '2007-03-18 15:32:31'
+						),
+						'children' => array()
+					)
+				)
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('threaded', array(
+			'fields' => 'id, parent_id, name'
+		));
+
+		$expected = array(
+			array(
+				'Category' => array(
+					'id' => '1',
+					'parent_id' => '0',
+					'name' => 'Category 1'
+				),
+				'children' => array(
+					array(
+						'Category' => array(
+							'id' => '2',
+							'parent_id' => '1',
+							'name' => 'Category 1.1'
+						),
+						'children' => array(
+							array('Category' => array(
+								'id' => '7',
+								'parent_id' => '2',
+								'name' => 'Category 1.1.1'),
+								'children' => array()),
+							array('Category' => array(
+								'id' => '8',
+								'parent_id' => '2',
+								'name' => 'Category 1.1.2'),
+								'children' => array()))
+					),
+					array(
+						'Category' => array(
+							'id' => '3',
+							'parent_id' => '1',
+							'name' => 'Category 1.2'
+						),
+						'children' => array()
+					)
+				)
+			),
+			array(
+				'Category' => array(
+					'id' => '4',
+					'parent_id' => '0',
+					'name' => 'Category 2'
+				),
+				'children' => array()
+			),
+			array(
+				'Category' => array(
+					'id' => '5',
+					'parent_id' => '0',
+					'name' => 'Category 3'
+				),
+				'children' => array(
+					array(
+						'Category' => array(
+							'id' => '6',
+							'parent_id' => '5',
+							'name' => 'Category 3.1'
+						),
+						'children' => array()
+					)
+				)
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('threaded', array('order' => 'id DESC'));
+
+		$expected = array(
+			array(
+				'Category' => array(
+					'id' => 5,
+					'parent_id' => 0,
+					'name' => 'Category 3',
+					'created' => '2007-03-18 15:30:23',
+					'updated' => '2007-03-18 15:32:31'
+				),
+				'children' => array(
+					array(
+						'Category' => array(
+							'id' => 6,
+							'parent_id' => 5,
+							'name' => 'Category 3.1',
+							'created' => '2007-03-18 15:30:23',
+							'updated' => '2007-03-18 15:32:31'
+						),
+						'children' => array()
+					)
+				)
+			),
+			array(
+				'Category' => array(
+					'id' => 4,
+					'parent_id' => 0,
+					'name' => 'Category 2',
+					'created' => '2007-03-18 15:30:23',
+					'updated' => '2007-03-18 15:32:31'
+				),
+				'children' => array()
+			),
+			array(
+				'Category' => array(
+					'id' => 1,
+					'parent_id' => 0,
+					'name' => 'Category 1',
+					'created' => '2007-03-18 15:30:23',
+					'updated' => '2007-03-18 15:32:31'
+				),
+				'children' => array(
+					array(
+						'Category' => array(
+							'id' => 3,
+							'parent_id' => 1,
+							'name' => 'Category 1.2',
+							'created' => '2007-03-18 15:30:23',
+							'updated' => '2007-03-18 15:32:31'
+						),
+						'children' => array()
+					),
+					array(
+						'Category' => array(
+							'id' => 2,
+							'parent_id' => 1,
+							'name' => 'Category 1.1',
+							'created' => '2007-03-18 15:30:23',
+							'updated' => '2007-03-18 15:32:31'
+						),
+						'children' => array(
+							array('Category' => array(
+								'id' => '8',
+								'parent_id' => '2',
+								'name' => 'Category 1.1.2',
+								'created' => '2007-03-18 15:30:23',
+								'updated' => '2007-03-18 15:32:31'),
+								'children' => array()),
+							array('Category' => array(
+								'id' => '7',
+								'parent_id' => '2',
+								'name' => 'Category 1.1.1',
+								'created' => '2007-03-18 15:30:23',
+								'updated' => '2007-03-18 15:32:31'),
+								'children' => array()))
+					)
+				)
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('threaded', array(
+			'conditions' => array('Category.name LIKE' => 'Category 3%')
+		));
+		$expected = array(
+			array(
+				'Category' => array(
+					'id' => '5',
+					'parent_id' => '0',
+					'name' => 'Category 3',
+					'created' => '2007-03-18 15:30:23',
+					'updated' => '2007-03-18 15:32:31'
+				),
+				'children' => array(
+					array(
+						'Category' => array(
+							'id' => '6',
+							'parent_id' => '5',
+							'name' => 'Category 3.1',
+							'created' => '2007-03-18 15:30:23',
+							'updated' => '2007-03-18 15:32:31'
+						),
+						'children' => array()
+					)
+				)
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('threaded', array(
+			'conditions' => array('Category.name LIKE' => 'Category 1.1%')
+		));
+		$expected = array(
+				array('Category' =>
+					array(
+						'id' => '2',
+						'parent_id' => '1',
+						'name' => 'Category 1.1',
+						'created' => '2007-03-18 15:30:23',
+						'updated' => '2007-03-18 15:32:31'),
+						'children' => array(
+							array('Category' => array(
+								'id' => '7',
+								'parent_id' => '2',
+								'name' => 'Category 1.1.1',
+								'created' => '2007-03-18 15:30:23',
+								'updated' => '2007-03-18 15:32:31'),
+								'children' => array()),
+							array('Category' => array(
+								'id' => '8',
+								'parent_id' => '2',
+								'name' => 'Category 1.1.2',
+								'created' => '2007-03-18 15:30:23',
+								'updated' => '2007-03-18 15:32:31'),
+								'children' => array()))));
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('threaded', array(
+			'fields' => 'id, parent_id, name',
+			'conditions' => array('Category.id !=' => 2)
+		));
+		$expected = array(
+			array(
+				'Category' => array(
+					'id' => '1',
+					'parent_id' => '0',
+					'name' => 'Category 1'
+				),
+				'children' => array(
+					array(
+						'Category' => array(
+							'id' => '3',
+							'parent_id' => '1',
+							'name' => 'Category 1.2'
+						),
+						'children' => array()
+					)
+				)
+			),
+			array(
+				'Category' => array(
+					'id' => '4',
+					'parent_id' => '0',
+					'name' => 'Category 2'
+				),
+				'children' => array()
+			),
+			array(
+				'Category' => array(
+					'id' => '5',
+					'parent_id' => '0',
+					'name' => 'Category 3'
+				),
+				'children' => array(
+					array(
+						'Category' => array(
+							'id' => '6',
+							'parent_id' => '5',
+							'name' => 'Category 3.1'
+						),
+						'children' => array()
+					)
+				)
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('all', array(
+			'fields' => 'id, name, parent_id',
+			'conditions' => array('Category.id !=' => 1)
+		));
+		$expected = array (
+			array ('Category' => array(
+				'id' => '2',
+				'name' => 'Category 1.1',
+				'parent_id' => '1'
+			)),
+			array ('Category' => array(
+				'id' => '3',
+				'name' => 'Category 1.2',
+				'parent_id' => '1'
+			)),
+			array ('Category' => array(
+				'id' => '4',
+				'name' => 'Category 2',
+				'parent_id' => '0'
+			)),
+			array ('Category' => array(
+				'id' => '5',
+				'name' => 'Category 3',
+				'parent_id' => '0'
+			)),
+			array ('Category' => array(
+				'id' => '6',
+				'name' => 'Category 3.1',
+				'parent_id' => '5'
+			)),
+			array ('Category' => array(
+				'id' => '7',
+				'name' => 'Category 1.1.1',
+				'parent_id' => '2'
+			)),
+			array ('Category' => array(
+				'id' => '8',
+				'name' => 'Category 1.1.2',
+				'parent_id' => '2'
+		)));
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('threaded', array(
+			'fields' => 'id, parent_id, name',
+			'conditions' => array('Category.id !=' => 1)
+		));
+		$expected = array(
+			array(
+				'Category' => array(
+					'id' => '2',
+					'parent_id' => '1',
+					'name' => 'Category 1.1'
+				),
+				'children' => array(
+					array('Category' => array(
+						'id' => '7',
+						'parent_id' => '2',
+						'name' => 'Category 1.1.1'),
+						'children' => array()),
+					array('Category' => array(
+						'id' => '8',
+						'parent_id' => '2',
+						'name' => 'Category 1.1.2'),
+						'children' => array()))
+			),
+			array(
+				'Category' => array(
+					'id' => '3',
+					'parent_id' => '1',
+					'name' => 'Category 1.2'
+				),
+				'children' => array()
+			)
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test find('neighbors')
+ *
+ * @return void
+ * @access public
+ */
+	function testFindNeighbors() {
+		$this->loadFixtures('User', 'Article');
+		$TestModel =& new Article();
+
+		$TestModel->id = 1;
+		$result = $TestModel->find('neighbors', array('fields' => array('id')));
+		$expected = array(
+			'prev' => null,
+			'next' => array(
+				'Article' => array('id' => 2)
+		));
+		$this->assertEqual($result, $expected);
+
+		$TestModel->id = 2;
+		$result = $TestModel->find('neighbors', array(
+			'fields' => array('id')
+		));
+
+		$expected = array(
+			'prev' => array(
+				'Article' => array(
+					'id' => 1
+			)),
+			'next' => array(
+				'Article' => array(
+					'id' => 3
+		)));
+		$this->assertEqual($result, $expected);
+
+		$TestModel->id = 3;
+		$result = $TestModel->find('neighbors', array('fields' => array('id')));
+		$expected = array(
+			'prev' => array(
+				'Article' => array(
+					'id' => 2
+			)),
+			'next' => null
+		);
+		$this->assertEqual($result, $expected);
+
+		$TestModel->id = 1;
+		$result = $TestModel->find('neighbors', array('recursive' => -1));
+		$expected = array(
+			'prev' => null,
+			'next' => array(
+				'Article' => array(
+					'id' => 2,
+					'user_id' => 3,
+					'title' => 'Second Article',
+					'body' => 'Second Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:41:23',
+					'updated' => '2007-03-18 10:43:31'
+				)
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$TestModel->id = 2;
+		$result = $TestModel->find('neighbors', array('recursive' => -1));
+		$expected = array(
+			'prev' => array(
+				'Article' => array(
+					'id' => 1,
+					'user_id' => 1,
+					'title' => 'First Article',
+					'body' => 'First Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:39:23',
+					'updated' => '2007-03-18 10:41:31'
+				)
+			),
+			'next' => array(
+				'Article' => array(
+					'id' => 3,
+					'user_id' => 1,
+					'title' => 'Third Article',
+					'body' => 'Third Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:43:23',
+					'updated' => '2007-03-18 10:45:31'
+				)
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$TestModel->id = 3;
+		$result = $TestModel->find('neighbors', array('recursive' => -1));
+		$expected = array(
+			'prev' => array(
+				'Article' => array(
+					'id' => 2,
+					'user_id' => 3,
+					'title' => 'Second Article',
+					'body' => 'Second Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:41:23',
+					'updated' => '2007-03-18 10:43:31'
+				)
+			),
+			'next' => null
+		);
+		$this->assertEqual($result, $expected);
+
+		$TestModel->recursive = 0;
+		$TestModel->id = 1;
+		$one = $TestModel->read();
+		$TestModel->id = 2;
+		$two = $TestModel->read();
+		$TestModel->id = 3;
+		$three = $TestModel->read();
+
+		$TestModel->id = 1;
+		$result = $TestModel->find('neighbors');
+		$expected = array('prev' => null, 'next' => $two);
+		$this->assertEqual($result, $expected);
+
+		$TestModel->id = 2;
+		$result = $TestModel->find('neighbors');
+		$expected = array('prev' => $one, 'next' => $three);
+		$this->assertEqual($result, $expected);
+
+		$TestModel->id = 3;
+		$result = $TestModel->find('neighbors');
+		$expected = array('prev' => $two, 'next' => null);
+		$this->assertEqual($result, $expected);
+
+		$TestModel->recursive = 2;
+		$TestModel->id = 1;
+		$one = $TestModel->read();
+		$TestModel->id = 2;
+		$two = $TestModel->read();
+		$TestModel->id = 3;
+		$three = $TestModel->read();
+
+		$TestModel->id = 1;
+		$result = $TestModel->find('neighbors', array('recursive' => 2));
+		$expected = array('prev' => null, 'next' => $two);
+		$this->assertEqual($result, $expected);
+
+		$TestModel->id = 2;
+		$result = $TestModel->find('neighbors', array('recursive' => 2));
+		$expected = array('prev' => $one, 'next' => $three);
+		$this->assertEqual($result, $expected);
+
+		$TestModel->id = 3;
+		$result = $TestModel->find('neighbors', array('recursive' => 2));
+		$expected = array('prev' => $two, 'next' => null);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testFindCombinedRelations method
+ *
+ * @access public
+ * @return void
+ */
+	function testFindCombinedRelations() {
+		$this->loadFixtures('Apple', 'Sample');
+		$TestModel =& new Apple();
+
+		$result = $TestModel->find('all');
+
+		$expected = array(
+			array(
+				'Apple' => array(
+					'id' => '1',
+					'apple_id' => '2',
+					'color' => 'Red 1',
+					'name' => 'Red Apple 1',
+					'created' => '2006-11-22 10:38:58',
+					'date' => '1951-01-04',
+					'modified' => '2006-12-01 13:31:26',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => '2',
+					'apple_id' => '1',
+					'color' => 'Bright Red 1',
+					'name' => 'Bright Red Apple',
+					'created' => '2006-11-22 10:43:13',
+					'date' => '2014-01-01',
+					'modified' => '2006-11-30 18:38:10',
+					'mytime' => '22:57:17'
+				),
+				'Sample' => array(
+					'id' => null,
+					'apple_id' => null,
+					'name' => null
+				),
+				'Child' => array(
+					array(
+						'id' => '2',
+						'apple_id' => '1',
+						'color' => 'Bright Red 1',
+						'name' => 'Bright Red Apple',
+						'created' => '2006-11-22 10:43:13',
+						'date' => '2014-01-01',
+						'modified' => '2006-11-30 18:38:10',
+						'mytime' => '22:57:17'
+			))),
+			array(
+				'Apple' => array(
+					'id' => '2',
+					'apple_id' => '1',
+					'color' => 'Bright Red 1',
+					'name' => 'Bright Red Apple',
+					'created' => '2006-11-22 10:43:13',
+					'date' => '2014-01-01',
+					'modified' => '2006-11-30 18:38:10',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => '1',
+					'apple_id' => '2',
+					'color' => 'Red 1',
+					'name' => 'Red Apple 1',
+					'created' => '2006-11-22 10:38:58',
+					'date' => '1951-01-04',
+					'modified' => '2006-12-01 13:31:26',
+					'mytime' => '22:57:17'
+				),
+				'Sample' => array(
+					'id' => '2',
+					'apple_id' => '2',
+					'name' => 'sample2'
+				),
+				'Child' => array(
+					array(
+						'id' => '1',
+						'apple_id' => '2',
+						'color' => 'Red 1',
+						'name' => 'Red Apple 1',
+						'created' => '2006-11-22 10:38:58',
+						'date' => '1951-01-04',
+						'modified' => '2006-12-01 13:31:26',
+						'mytime' => '22:57:17'
+					),
+					array(
+						'id' => '3',
+						'apple_id' => '2',
+						'color' => 'blue green',
+						'name' => 'green blue',
+						'created' => '2006-12-25 05:13:36',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:23:24',
+						'mytime' => '22:57:17'
+					),
+					array(
+						'id' => '4',
+						'apple_id' => '2',
+						'color' => 'Blue Green',
+						'name' => 'Test Name',
+						'created' => '2006-12-25 05:23:36',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:23:36',
+						'mytime' => '22:57:17'
+			))),
+			array(
+				'Apple' => array(
+					'id' => '3',
+					'apple_id' => '2',
+					'color' => 'blue green',
+					'name' => 'green blue',
+					'created' => '2006-12-25 05:13:36',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:23:24',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => '2',
+					'apple_id' => '1',
+					'color' => 'Bright Red 1',
+					'name' => 'Bright Red Apple',
+					'created' => '2006-11-22 10:43:13',
+					'date' => '2014-01-01',
+					'modified' => '2006-11-30 18:38:10',
+					'mytime' => '22:57:17'
+				),
+				'Sample' => array(
+					'id' => '1',
+					'apple_id' => '3',
+					'name' => 'sample1'
+				),
+				'Child' => array()
+			),
+			array(
+				'Apple' => array(
+					'id' => '4',
+					'apple_id' => '2',
+					'color' => 'Blue Green',
+					'name' => 'Test Name',
+					'created' => '2006-12-25 05:23:36',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:23:36',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => '2',
+					'apple_id' => '1',
+					'color' => 'Bright Red 1',
+					'name' => 'Bright Red Apple',
+					'created' => '2006-11-22 10:43:13',
+					'date' => '2014-01-01',
+					'modified' => '2006-11-30 18:38:10',
+					'mytime' => '22:57:17'
+				),
+				'Sample' => array(
+					'id' => '3',
+					'apple_id' => '4',
+					'name' => 'sample3'
+				),
+				'Child' => array(
+					array(
+						'id' => '6',
+						'apple_id' => '4',
+						'color' => 'My new appleOrange',
+						'name' => 'My new apple',
+						'created' => '2006-12-25 05:29:39',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:29:39',
+						'mytime' => '22:57:17'
+			))),
+			array(
+				'Apple' => array(
+					'id' => '5',
+					'apple_id' => '5',
+					'color' => 'Green',
+					'name' => 'Blue Green',
+					'created' => '2006-12-25 05:24:06',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:29:16',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => '5',
+					'apple_id' => '5',
+					'color' => 'Green',
+					'name' => 'Blue Green',
+					'created' => '2006-12-25 05:24:06',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:29:16',
+					'mytime' => '22:57:17'
+				),
+				'Sample' => array(
+					'id' => '4',
+					'apple_id' => '5',
+					'name' => 'sample4'
+				),
+				'Child' => array(
+					array(
+						'id' => '5',
+						'apple_id' => '5',
+						'color' => 'Green',
+						'name' => 'Blue Green',
+						'created' => '2006-12-25 05:24:06',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:29:16',
+						'mytime' => '22:57:17'
+			))),
+			array(
+				'Apple' => array(
+					'id' => '6',
+					'apple_id' => '4',
+					'color' => 'My new appleOrange',
+					'name' => 'My new apple',
+					'created' => '2006-12-25 05:29:39',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:29:39',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => '4',
+					'apple_id' => '2',
+					'color' => 'Blue Green',
+					'name' => 'Test Name',
+					'created' => '2006-12-25 05:23:36',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:23:36',
+					'mytime' => '22:57:17'
+				),
+				'Sample' => array(
+					'id' => null,
+					'apple_id' => null,
+					'name' => null
+				),
+				'Child' => array(
+					array(
+						'id' => '7',
+						'apple_id' => '6',
+						'color' => 'Some wierd color',
+						'name' => 'Some odd color',
+						'created' => '2006-12-25 05:34:21',
+						'date' => '2006-12-25',
+						'modified' => '2006-12-25 05:34:21',
+						'mytime' => '22:57:17'
+			))),
+			array(
+				'Apple' => array(
+					'id' => '7',
+					'apple_id' => '6',
+					'color' => 'Some wierd color',
+					'name' => 'Some odd color',
+					'created' => '2006-12-25 05:34:21',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:34:21',
+					'mytime' => '22:57:17'
+				),
+				'Parent' => array(
+					'id' => '6',
+					'apple_id' => '4',
+					'color' => 'My new appleOrange',
+					'name' => 'My new apple',
+					'created' => '2006-12-25 05:29:39',
+					'date' => '2006-12-25',
+					'modified' => '2006-12-25 05:29:39',
+					'mytime' => '22:57:17'
+				),
+				'Sample' => array(
+					'id' => null,
+					'apple_id' => null,
+					'name' => null
+				),
+				'Child' => array()
+		));
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testSaveEmpty method
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveEmpty() {
+		$this->loadFixtures('Thread');
+		$TestModel =& new Thread();
+		$data = array();
+		$expected = $TestModel->save($data);
+		$this->assertFalse($expected);
+	}
+
+/**
+ * testFindAllWithConditionInChildQuery
+ *
+ * @todo external conditions like this are going to need to be revisited at some point
+ * @access public
+ * @return void
+ */
+	function testFindAllWithConditionInChildQuery() {
+		$this->loadFixtures('Basket', 'FilmFile');
+
+		$TestModel =& new Basket();
+		$recursive = 3;
+		$result = $TestModel->find('all', compact('conditions', 'recursive'));
+
+		$expected = array(
+			array(
+				'Basket' => array(
+					'id' => 1,
+					'type' => 'nonfile',
+					'name' => 'basket1',
+					'object_id' => 1,
+					'user_id' => 1,
+				),
+				'FilmFile' => array(
+					'id' => '',
+					'name' => '',
+				)
+			),
+			array(
+				'Basket' => array(
+					'id' => 2,
+					'type' => 'file',
+					'name' => 'basket2',
+					'object_id' => 2,
+					'user_id' => 1,
+				),
+				'FilmFile' => array(
+					'id' => 2,
+					'name' => 'two',
+				)
+			),
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testFindAllWithConditionsHavingMixedDataTypes method
+ *
+ * @access public
+ * @return void
+ */
+	function testFindAllWithConditionsHavingMixedDataTypes() {
+		$this->loadFixtures('Article');
+		$TestModel =& new Article();
+		$expected = array(
+			array(
+				'Article' => array(
+					'id' => 1,
+					'user_id' => 1,
+					'title' => 'First Article',
+					'body' => 'First Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:39:23',
+					'updated' => '2007-03-18 10:41:31'
+				)
+			),
+			array(
+				'Article' => array(
+					'id' => 2,
+					'user_id' => 3,
+					'title' => 'Second Article',
+					'body' => 'Second Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:41:23',
+					'updated' => '2007-03-18 10:43:31'
+				)
+			)
+		);
+		$conditions = array('id' => array('1', 2));
+		$recursive = -1;
+		$order = 'Article.id ASC';
+		$result = $TestModel->find('all', compact('conditions', 'recursive', 'order'));
+		$this->assertEqual($result, $expected);
+
+		if ($this->skipIf($this->db->config['driver'] == 'postgres', 'The rest of testFindAllWithConditionsHavingMixedDataTypes test is not compatible with Postgres')) {
+			return;
+		}
+		$conditions = array('id' => array('1', 2, '3.0'));
+		$order = 'Article.id ASC';
+		$result = $TestModel->find('all', compact('recursive', 'conditions', 'order'));
+		$expected = array(
+			array(
+				'Article' => array(
+					'id' => 1,
+					'user_id' => 1,
+					'title' => 'First Article',
+					'body' => 'First Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:39:23',
+					'updated' => '2007-03-18 10:41:31'
+				)
+			),
+			array(
+				'Article' => array(
+					'id' => 2,
+					'user_id' => 3,
+					'title' => 'Second Article',
+					'body' => 'Second Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:41:23',
+					'updated' => '2007-03-18 10:43:31'
+				)
+			),
+			array(
+				'Article' => array(
+					'id' => 3,
+					'user_id' => 1,
+					'title' => 'Third Article',
+					'body' => 'Third Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:43:23',
+					'updated' => '2007-03-18 10:45:31'
+				)
+			)
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testBindUnbind method
+ *
+ * @access public
+ * @return void
+ */
+	function testBindUnbind() {
+		$this->loadFixtures('User', 'Comment', 'FeatureSet');
+		$TestModel =& new User();
+
+		$result = $TestModel->hasMany;
+		$expected = array();
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->bindModel(array('hasMany' => array('Comment')));
+		$this->assertTrue($result);
+
+		$result = $TestModel->find('all', array(
+			'fields' => 'User.id, User.user'
+		));
+		$expected = array(
+			array(
+				'User' => array(
+					'id' => '1',
+					'user' => 'mariano'
+				),
+				'Comment' => array(
+					array(
+						'id' => '3',
+						'article_id' => '1',
+						'user_id' => '1',
+						'comment' => 'Third Comment for First Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:49:23',
+						'updated' => '2007-03-18 10:51:31'
+					),
+					array(
+						'id' => '4',
+						'article_id' => '1',
+						'user_id' => '1',
+						'comment' => 'Fourth Comment for First Article',
+						'published' => 'N',
+						'created' => '2007-03-18 10:51:23',
+						'updated' => '2007-03-18 10:53:31'
+					),
+					array(
+						'id' => '5',
+						'article_id' => '2',
+						'user_id' => '1',
+						'comment' => 'First Comment for Second Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:53:23',
+						'updated' => '2007-03-18 10:55:31'
+			))),
+			array(
+				'User' => array(
+					'id' => '2',
+					'user' => 'nate'
+				),
+				'Comment' => array(
+					array(
+						'id' => '1',
+						'article_id' => '1',
+						'user_id' => '2',
+						'comment' => 'First Comment for First Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:45:23',
+						'updated' => '2007-03-18 10:47:31'
+					),
+					array(
+						'id' => '6',
+						'article_id' => '2',
+						'user_id' => '2',
+						'comment' => 'Second Comment for Second Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:55:23',
+						'updated' => '2007-03-18 10:57:31'
+			))),
+			array(
+				'User' => array(
+					'id' => '3',
+					'user' => 'larry'
+				),
+				'Comment' => array()
+			),
+			array(
+				'User' => array(
+					'id' => '4',
+					'user' => 'garrett'
+				),
+				'Comment' => array(
+					array(
+						'id' => '2',
+						'article_id' => '1',
+						'user_id' => '4',
+						'comment' => 'Second Comment for First Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:47:23',
+						'updated' => '2007-03-18 10:49:31'
+		))));
+
+		$this->assertEqual($result, $expected);
+
+		$TestModel->resetAssociations();
+		$result = $TestModel->hasMany;
+		$this->assertEqual($result, array());
+
+		$result = $TestModel->bindModel(array('hasMany' => array('Comment')), false);
+		$this->assertTrue($result);
+
+		$result = $TestModel->find('all', array(
+			'fields' => 'User.id, User.user'
+		));
+
+		$expected = array(
+			array(
+				'User' => array(
+					'id' => '1',
+					'user' => 'mariano'
+				),
+				'Comment' => array(
+					array(
+						'id' => '3',
+						'article_id' => '1',
+						'user_id' => '1',
+						'comment' => 'Third Comment for First Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:49:23',
+						'updated' => '2007-03-18 10:51:31'
+					),
+					array(
+						'id' => '4',
+						'article_id' => '1',
+						'user_id' => '1',
+						'comment' => 'Fourth Comment for First Article',
+						'published' => 'N',
+						'created' => '2007-03-18 10:51:23',
+						'updated' => '2007-03-18 10:53:31'
+					),
+					array(
+						'id' => '5',
+						'article_id' => '2',
+						'user_id' => '1',
+						'comment' => 'First Comment for Second Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:53:23',
+						'updated' => '2007-03-18 10:55:31'
+			))),
+			array(
+				'User' => array(
+					'id' => '2',
+					'user' => 'nate'
+				),
+				'Comment' => array(
+					array(
+						'id' => '1',
+						'article_id' => '1',
+						'user_id' => '2',
+						'comment' => 'First Comment for First Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:45:23',
+						'updated' => '2007-03-18 10:47:31'
+					),
+					array(
+						'id' => '6',
+						'article_id' => '2',
+						'user_id' => '2',
+						'comment' => 'Second Comment for Second Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:55:23',
+						'updated' => '2007-03-18 10:57:31'
+			))),
+			array(
+				'User' => array(
+					'id' => '3',
+					'user' => 'larry'
+				),
+				'Comment' => array()
+			),
+			array(
+				'User' => array(
+					'id' => '4',
+					'user' => 'garrett'
+				),
+				'Comment' => array(
+					array(
+						'id' => '2',
+						'article_id' => '1',
+						'user_id' => '4',
+						'comment' => 'Second Comment for First Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:47:23',
+						'updated' => '2007-03-18 10:49:31'
+		))));
+
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->hasMany;
+		$expected = array(
+			'Comment' => array(
+				'className' => 'Comment',
+				'foreignKey' => 'user_id',
+				'conditions' => null,
+				'fields' => null,
+				'order' => null,
+				'limit' => null,
+				'offset' => null,
+				'dependent' => null,
+				'exclusive' => null,
+				'finderQuery' => null,
+				'counterQuery' => null
+		));
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->unbindModel(array('hasMany' => array('Comment')));
+		$this->assertTrue($result);
+
+		$result = $TestModel->hasMany;
+		$expected = array();
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('all', array(
+			'fields' => 'User.id, User.user'
+		));
+		$expected = array(
+			array('User' => array('id' => '1', 'user' => 'mariano')),
+			array('User' => array('id' => '2', 'user' => 'nate')),
+			array('User' => array('id' => '3', 'user' => 'larry')),
+			array('User' => array('id' => '4', 'user' => 'garrett')));
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('all', array(
+			'fields' => 'User.id, User.user'
+		));
+		$expected = array(
+			array(
+				'User' => array(
+					'id' => '1',
+					'user' => 'mariano'
+				),
+				'Comment' => array(
+					array(
+						'id' => '3',
+						'article_id' => '1',
+						'user_id' => '1',
+						'comment' => 'Third Comment for First Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:49:23',
+						'updated' => '2007-03-18 10:51:31'
+					),
+					array(
+						'id' => '4',
+						'article_id' => '1',
+						'user_id' => '1',
+						'comment' => 'Fourth Comment for First Article',
+						'published' => 'N',
+						'created' => '2007-03-18 10:51:23',
+						'updated' => '2007-03-18 10:53:31'
+					),
+					array(
+						'id' => '5',
+						'article_id' => '2',
+						'user_id' => '1',
+						'comment' => 'First Comment for Second Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:53:23',
+						'updated' => '2007-03-18 10:55:31'
+			))),
+			array(
+				'User' => array(
+					'id' => '2',
+					'user' => 'nate'
+				),
+				'Comment' => array(
+					array(
+						'id' => '1',
+						'article_id' => '1',
+						'user_id' => '2',
+						'comment' => 'First Comment for First Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:45:23',
+						'updated' => '2007-03-18 10:47:31'
+					),
+					array(
+						'id' => '6',
+						'article_id' => '2',
+						'user_id' => '2',
+						'comment' => 'Second Comment for Second Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:55:23',
+						'updated' => '2007-03-18 10:57:31'
+			))),
+			array(
+				'User' => array(
+					'id' => '3',
+					'user' => 'larry'
+				),
+				'Comment' => array()
+			),
+			array(
+				'User' => array(
+					'id' => '4',
+					'user' => 'garrett'
+				),
+				'Comment' => array(
+					array(
+						'id' => '2',
+						'article_id' => '1',
+						'user_id' => '4',
+						'comment' =>
+						'Second Comment for First Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:47:23',
+						'updated' => '2007-03-18 10:49:31'
+		))));
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->unbindModel(array('hasMany' => array('Comment')), false);
+		$this->assertTrue($result);
+
+		$result = $TestModel->find('all', array('fields' => 'User.id, User.user'));
+		$expected = array(
+			array('User' => array('id' => '1', 'user' => 'mariano')),
+			array('User' => array('id' => '2', 'user' => 'nate')),
+			array('User' => array('id' => '3', 'user' => 'larry')),
+			array('User' => array('id' => '4', 'user' => 'garrett')));
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->hasMany;
+		$expected = array();
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->bindModel(array('hasMany' => array(
+			'Comment' => array('className' => 'Comment', 'conditions' => 'Comment.published = \'Y\'')
+		)));
+		$this->assertTrue($result);
+
+		$result = $TestModel->find('all', array('fields' => 'User.id, User.user'));
+		$expected = array(
+			array(
+				'User' => array(
+					'id' => '1',
+					'user' => 'mariano'
+				),
+				'Comment' => array(
+					array(
+						'id' => '3',
+						'article_id' => '1',
+						'user_id' => '1',
+						'comment' => 'Third Comment for First Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:49:23',
+						'updated' => '2007-03-18 10:51:31'
+					),
+					array(
+						'id' => '5',
+						'article_id' => '2',
+						'user_id' => '1',
+						'comment' => 'First Comment for Second Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:53:23',
+						'updated' => '2007-03-18 10:55:31'
+			))),
+			array(
+				'User' => array(
+					'id' => '2',
+					'user' => 'nate'
+				),
+				'Comment' => array(
+					array(
+						'id' => '1',
+						'article_id' => '1',
+						'user_id' => '2',
+						'comment' => 'First Comment for First Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:45:23',
+						'updated' => '2007-03-18 10:47:31'
+					),
+					array(
+						'id' => '6',
+						'article_id' => '2',
+						'user_id' => '2',
+						'comment' => 'Second Comment for Second Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:55:23',
+						'updated' => '2007-03-18 10:57:31'
+			))),
+			array(
+				'User' => array(
+					'id' => '3',
+					'user' => 'larry'
+				),
+				'Comment' => array()
+			),
+			array(
+				'User' => array(
+					'id' => '4',
+					'user' => 'garrett'
+				),
+				'Comment' => array(
+					array(
+						'id' => '2',
+						'article_id' => '1',
+						'user_id' => '4',
+						'comment' => 'Second Comment for First Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:47:23',
+						'updated' => '2007-03-18 10:49:31'
+		))));
+
+		$this->assertEqual($result, $expected);
+
+		$TestModel2 =& new DeviceType();
+
+		$expected = array(
+			'className' => 'FeatureSet',
+			'foreignKey' => 'feature_set_id',
+			'conditions' => '',
+			'fields' => '',
+			'order' => '',
+			'counterCache' => ''
+		);
+		$this->assertEqual($TestModel2->belongsTo['FeatureSet'], $expected);
+
+		$TestModel2->bindModel(array(
+			'belongsTo' => array(
+				'FeatureSet' => array(
+					'className' => 'FeatureSet',
+					'conditions' => array('active' => true)
+				)
+			)
+		));
+		$expected['conditions'] = array('active' => true);
+		$this->assertEqual($TestModel2->belongsTo['FeatureSet'], $expected);
+
+		$TestModel2->bindModel(array(
+			'belongsTo' => array(
+				'FeatureSet' => array(
+					'className' => 'FeatureSet',
+					'foreignKey' => false,
+					'conditions' => array('Feature.name' => 'DeviceType.name')
+				)
+			)
+		));
+		$expected['conditions'] = array('Feature.name' => 'DeviceType.name');
+		$expected['foreignKey'] = false;
+		$this->assertEqual($TestModel2->belongsTo['FeatureSet'], $expected);
+
+		$TestModel2->bindModel(array(
+			'hasMany' => array(
+				'NewFeatureSet' => array(
+					'className' => 'FeatureSet',
+					'conditions' => array('active' => true)
+				)
+			)
+		));
+
+		$expected = array(
+			'className' => 'FeatureSet',
+			'conditions' => array('active' => true),
+			'foreignKey' => 'device_type_id',
+			'fields' => '',
+			'order' => '',
+			'limit' => '',
+			'offset' => '',
+			'dependent' => '',
+			'exclusive' => '',
+			'finderQuery' => '',
+			'counterQuery' => ''
+		);
+		$this->assertEqual($TestModel2->hasMany['NewFeatureSet'], $expected);
+		$this->assertTrue(is_object($TestModel2->NewFeatureSet));
+	}
+
+/**
+ * testBindMultipleTimes method
+ *
+ * @access public
+ * @return void
+ */
+	function testBindMultipleTimes() {
+		$this->loadFixtures('User', 'Comment', 'Article');
+		$TestModel =& new User();
+
+		$result = $TestModel->hasMany;
+		$expected = array();
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->bindModel(array(
+			'hasMany' => array(
+				'Items' => array('className' => 'Comment')
+		)));
+		$this->assertTrue($result);
+
+		$result = $TestModel->find('all', array(
+			'fields' => 'User.id, User.user'
+		));
+		$expected = array(
+			array(
+				'User' => array(
+					'id' => '1',
+					'user' => 'mariano'
+				),
+				'Items' => array(
+					array(
+						'id' => '3',
+						'article_id' => '1',
+						'user_id' => '1',
+						'comment' => 'Third Comment for First Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:49:23',
+						'updated' => '2007-03-18 10:51:31'
+					),
+					array(
+						'id' => '4',
+						'article_id' => '1',
+						'user_id' => '1',
+						'comment' => 'Fourth Comment for First Article',
+						'published' => 'N',
+						'created' => '2007-03-18 10:51:23',
+						'updated' => '2007-03-18 10:53:31'
+					),
+					array(
+						'id' => '5',
+						'article_id' => '2',
+						'user_id' => '1',
+						'comment' => 'First Comment for Second Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:53:23',
+						'updated' => '2007-03-18 10:55:31'
+			))),
+			array(
+				'User' => array(
+					'id' => '2',
+					'user' => 'nate'
+				),
+				'Items' => array(
+					array(
+						'id' => '1',
+						'article_id' => '1',
+						'user_id' => '2',
+						'comment' => 'First Comment for First Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:45:23',
+						'updated' => '2007-03-18 10:47:31'
+					),
+					array(
+						'id' => '6',
+						'article_id' => '2',
+						'user_id' => '2',
+						'comment' => 'Second Comment for Second Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:55:23',
+						'updated' => '2007-03-18 10:57:31'
+			))),
+			array(
+				'User' => array(
+					'id' => '3',
+					'user' => 'larry'
+				),
+				'Items' => array()
+			),
+			array(
+				'User' => array(
+					'id' => '4', 'user' => 'garrett'),
+					'Items' => array(
+						array(
+							'id' => '2',
+							'article_id' => '1',
+							'user_id' => '4',
+							'comment' => 'Second Comment for First Article',
+							'published' => 'Y',
+							'created' => '2007-03-18 10:47:23',
+							'updated' => '2007-03-18 10:49:31'
+		))));
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->bindModel(array(
+			'hasMany' => array(
+				'Items' => array('className' => 'Article')
+		)));
+		$this->assertTrue($result);
+
+		$result = $TestModel->find('all', array(
+			'fields' => 'User.id, User.user'
+		));
+		$expected = array(
+			array(
+				'User' => array(
+					'id' => '1',
+					'user' => 'mariano'
+				),
+				'Items' => array(
+					array(
+						'id' => 1,
+						'user_id' => 1,
+						'title' => 'First Article',
+						'body' => 'First Article Body',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:39:23',
+						'updated' => '2007-03-18 10:41:31'
+					),
+					array(
+						'id' => 3,
+						'user_id' => 1,
+						'title' => 'Third Article',
+						'body' => 'Third Article Body',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:43:23',
+						'updated' => '2007-03-18 10:45:31'
+			))),
+			array(
+				'User' => array(
+					'id' => '2',
+					'user' => 'nate'
+				),
+				'Items' => array()
+			),
+			array(
+				'User' => array(
+					'id' => '3',
+					'user' => 'larry'
+				),
+				'Items' => array(
+					array(
+						'id' => 2,
+						'user_id' => 3,
+						'title' => 'Second Article',
+						'body' => 'Second Article Body',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:41:23',
+						'updated' => '2007-03-18 10:43:31'
+			))),
+			array(
+				'User' => array(
+					'id' => '4',
+					'user' => 'garrett'
+				),
+				'Items' => array()
+		));
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that multiple reset = true calls to bindModel() result in the original associations.
+ *
+ * @return void
+ */
+	function testBindModelMultipleTimesResetCorrectly() {
+		$this->loadFixtures('User', 'Comment', 'Article');
+		$TestModel =& new User();
+
+		$TestModel->bindModel(array('hasMany' => array('Comment')));
+		$TestModel->bindModel(array('hasMany' => array('Comment')));
+		$TestModel->resetAssociations();
+
+		$this->assertFalse(isset($TestModel->hasMany['Comment']), 'Association left behind');
+	}
+
+/**
+ * testBindMultipleTimes method with different reset settings
+ *
+ * @access public
+ * @return void
+ */
+	function testBindMultipleTimesWithDifferentResetSettings() {
+		$this->loadFixtures('User', 'Comment', 'Article');
+		$TestModel =& new User();
+
+		$result = $TestModel->hasMany;
+		$expected = array();
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->bindModel(array(
+			'hasMany' => array('Comment')
+		));
+		$this->assertTrue($result);
+		$result = $TestModel->bindModel(
+			array('hasMany' => array('Article')),
+			false
+		);
+		$this->assertTrue($result);
+
+		$result = array_keys($TestModel->hasMany);
+		$expected = array('Comment', 'Article');
+		$this->assertEqual($result, $expected);
+
+		$TestModel->resetAssociations();
+
+		$result = array_keys($TestModel->hasMany);
+		$expected = array('Article');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that bindModel behaves with Custom primary Key associations
+ *
+ * @return void
+ */
+	function testBindWithCustomPrimaryKey() {
+		$this->loadFixtures('Story', 'StoriesTag', 'Tag');
+		$Model =& ClassRegistry::init('StoriesTag');
+		$Model->bindModel(array(
+			'belongsTo' => array(
+				'Tag' => array(
+					'className' => 'Tag',
+					'foreignKey' => 'story'
+		))));
+
+		$result = $Model->find('all');
+		$this->assertFalse(empty($result));
+	}
+
+/**
+ * test that calling unbindModel() with reset == true multiple times
+ * leaves associations in the correct state.
+ *
+ * @return void
+ */
+	function testUnbindMultipleTimesResetCorrectly() {
+		$this->loadFixtures('User', 'Comment', 'Article');
+		$TestModel =& new Article10();
+
+		$TestModel->unbindModel(array('hasMany' => array('Comment')));
+		$TestModel->unbindModel(array('hasMany' => array('Comment')));
+		$TestModel->resetAssociations();
+
+		$this->assertTrue(isset($TestModel->hasMany['Comment']), 'Association permanently removed');
+	}
+
+/**
+ * testBindMultipleTimes method with different reset settings
+ *
+ * @access public
+ * @return void
+ */
+	function testUnBindMultipleTimesWithDifferentResetSettings() {
+		$this->loadFixtures('User', 'Comment', 'Article');
+		$TestModel =& new Comment();
+
+		$result = array_keys($TestModel->belongsTo);
+		$expected = array('Article', 'User');
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->unbindModel(array(
+			'belongsTo' => array('User')
+		));
+		$this->assertTrue($result);
+		$result = $TestModel->unbindModel(
+			array('belongsTo' => array('Article')),
+			false
+		);
+		$this->assertTrue($result);
+
+		$result = array_keys($TestModel->belongsTo);
+		$expected = array();
+		$this->assertEqual($result, $expected);
+
+		$TestModel->resetAssociations();
+
+		$result = array_keys($TestModel->belongsTo);
+		$expected = array('User');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testAssociationAfterFind method
+ *
+ * @access public
+ * @return void
+ */
+	function testAssociationAfterFind() {
+		$this->loadFixtures('Post', 'Author', 'Comment');
+		$TestModel =& new Post();
+		$result = $TestModel->find('all');
+		$expected = array(
+			array(
+				'Post' => array(
+					'id' => '1',
+					'author_id' => '1',
+					'title' => 'First Post',
+					'body' => 'First Post Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:39:23',
+					'updated' => '2007-03-18 10:41:31'
+				),
+				'Author' => array(
+					'id' => '1',
+					'user' => 'mariano',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23',
+					'updated' => '2007-03-17 01:18:31',
+					'test' => 'working'
+			)),
+			array(
+				'Post' => array(
+					'id' => '2',
+					'author_id' => '3',
+					'title' => 'Second Post',
+					'body' => 'Second Post Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:41:23',
+					'updated' => '2007-03-18 10:43:31'
+				),
+				'Author' => array(
+					'id' => '3',
+					'user' => 'larry',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23',
+					'updated' => '2007-03-17 01:22:31',
+					'test' => 'working'
+			)),
+			array(
+				'Post' => array(
+					'id' => '3',
+					'author_id' => '1',
+					'title' => 'Third Post',
+					'body' => 'Third Post Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:43:23',
+					'updated' => '2007-03-18 10:45:31'
+				),
+				'Author' => array(
+					'id' => '1',
+					'user' => 'mariano',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23',
+					'updated' => '2007-03-17 01:18:31',
+					'test' => 'working'
+		)));
+		$this->assertEqual($result, $expected);
+		unset($TestModel);
+
+		$Author =& new Author();
+		$Author->Post->bindModel(array(
+			'hasMany' => array(
+				'Comment' => array(
+					'className' => 'ModifiedComment',
+					'foreignKey' => 'article_id',
+				)
+		)));
+		$result = $Author->find('all', array(
+			'conditions' => array('Author.id' => 1),
+			'recursive' => 2
+		));
+		$expected = array(
+			'id' => 1,
+			'article_id' => 1,
+			'user_id' => 2,
+			'comment' => 'First Comment for First Article',
+			'published' => 'Y',
+			'created' => '2007-03-18 10:45:23',
+			'updated' => '2007-03-18 10:47:31',
+			'callback' => 'Fire'
+		);
+		$this->assertEqual($result[0]['Post'][0]['Comment'][0], $expected);
+	}
+
+/**
+ * Tests that callbacks can be properly disabled
+ *
+ * @access public
+ * @return void
+ */
+	function testCallbackDisabling() {
+		$this->loadFixtures('Author');
+		$TestModel = new ModifiedAuthor();
+
+		$result = Set::extract($TestModel->find('all'), '/Author/user');
+		$expected = array('mariano (CakePHP)', 'nate (CakePHP)', 'larry (CakePHP)', 'garrett (CakePHP)');
+		$this->assertEqual($result, $expected);
+
+		$result = Set::extract($TestModel->find('all', array('callbacks' => 'after')), '/Author/user');
+		$expected = array('mariano (CakePHP)', 'nate (CakePHP)', 'larry (CakePHP)', 'garrett (CakePHP)');
+		$this->assertEqual($result, $expected);
+
+		$result = Set::extract($TestModel->find('all', array('callbacks' => 'before')), '/Author/user');
+		$expected = array('mariano', 'nate', 'larry', 'garrett');
+		$this->assertEqual($result, $expected);
+
+		$result = Set::extract($TestModel->find('all', array('callbacks' => false)), '/Author/user');
+		$expected = array('mariano', 'nate', 'larry', 'garrett');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testAssociationAfterFindCallbacksDisabled method
+ *
+ * @return void
+ */
+	public function testAssociationAfterFindCalbacksDisabled() {
+		$this->loadFixtures('Post', 'Author', 'Comment');
+		$TestModel = new Post();
+		$result = $TestModel->find('all', array('callbacks' => false));
+		$expected = array(
+			array(
+				'Post' => array(
+					'id' => '1',
+					'author_id' => '1',
+					'title' => 'First Post',
+					'body' => 'First Post Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:39:23',
+					'updated' => '2007-03-18 10:41:31'
+				),
+				'Author' => array(
+					'id' => '1',
+					'user' => 'mariano',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23',
+					'updated' => '2007-03-17 01:18:31'
+			)),
+			array(
+				'Post' => array(
+					'id' => '2',
+					'author_id' => '3',
+					'title' => 'Second Post',
+					'body' => 'Second Post Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:41:23',
+					'updated' => '2007-03-18 10:43:31'
+				),
+				'Author' => array(
+					'id' => '3',
+					'user' => 'larry',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23',
+					'updated' => '2007-03-17 01:22:31'
+			)),
+			array(
+				'Post' => array(
+					'id' => '3',
+					'author_id' => '1',
+					'title' => 'Third Post',
+					'body' => 'Third Post Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:43:23',
+					'updated' => '2007-03-18 10:45:31'
+				),
+				'Author' => array(
+					'id' => '1',
+					'user' => 'mariano',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23',
+					'updated' => '2007-03-17 01:18:31'
+		)));
+		$this->assertEqual($expected, $result);
+		unset($TestModel);
+
+		$Author = new Author();
+		$Author->Post->bindModel(array(
+			'hasMany' => array(
+				'Comment' => array(
+					'className' => 'ModifiedComment',
+					'foreignKey' => 'article_id',
+				)
+		)));
+		$result = $Author->find('all', array(
+			'conditions' => array('Author.id' => 1),
+			'recursive' => 2,
+			'callbacks' => false
+		));
+		$expected = array(
+			'id' => 1,
+			'article_id' => 1,
+			'user_id' => 2,
+			'comment' => 'First Comment for First Article',
+			'published' => 'Y',
+			'created' => '2007-03-18 10:45:23',
+			'updated' => '2007-03-18 10:47:31'
+		);
+		$this->assertEqual($result[0]['Post'][0]['Comment'][0], $expected);
+	}
+
+/**
+ * Tests that the database configuration assigned to the model can be changed using
+ * (before|after)Find callbacks
+ *
+ * @access public
+ * @return void
+ */
+	function testCallbackSourceChange() {
+		$this->loadFixtures('Post');
+		$TestModel = new Post();
+		$this->assertEqual(3, count($TestModel->find('all')));
+
+		$this->expectError(new PatternExpectation('/Non-existent data source foo/i'));
+		$this->assertFalse($TestModel->find('all', array('connection' => 'foo')));
+	}
+
+/**
+ * testMultipleBelongsToWithSameClass method
+ *
+ * @access public
+ * @return void
+ */
+	function testMultipleBelongsToWithSameClass() {
+		$this->loadFixtures(
+			'DeviceType',
+			'DeviceTypeCategory',
+			'FeatureSet',
+			'ExteriorTypeCategory',
+			'Document',
+			'Device',
+			'DocumentDirectory'
+		);
+
+		$DeviceType =& new DeviceType();
+
+		$DeviceType->recursive = 2;
+		$result = $DeviceType->read(null, 1);
+
+		$expected = array(
+			'DeviceType' => array(
+				'id' => 1,
+				'device_type_category_id' => 1,
+				'feature_set_id' => 1,
+				'exterior_type_category_id' => 1,
+				'image_id' => 1,
+				'extra1_id' => 1,
+				'extra2_id' => 1,
+				'name' => 'DeviceType 1',
+				'order' => 0
+			),
+			'Image' => array(
+				'id' => 1,
+				'document_directory_id' => 1,
+				'name' => 'Document 1',
+				'DocumentDirectory' => array(
+					'id' => 1,
+					'name' => 'DocumentDirectory 1'
+			)),
+			'Extra1' => array(
+				'id' => 1,
+				'document_directory_id' => 1,
+				'name' => 'Document 1',
+				'DocumentDirectory' => array(
+					'id' => 1,
+					'name' => 'DocumentDirectory 1'
+			)),
+			'Extra2' => array(
+				'id' => 1,
+				'document_directory_id' => 1,
+				'name' => 'Document 1',
+				'DocumentDirectory' => array(
+					'id' => 1,
+					'name' => 'DocumentDirectory 1'
+			)),
+			'DeviceTypeCategory' => array(
+				'id' => 1,
+				'name' => 'DeviceTypeCategory 1'
+			),
+			'FeatureSet' => array(
+				'id' => 1,
+				'name' => 'FeatureSet 1'
+			),
+			'ExteriorTypeCategory' => array(
+				'id' => 1,
+				'image_id' => 1,
+				'name' => 'ExteriorTypeCategory 1',
+				'Image' => array(
+					'id' => 1,
+					'device_type_id' => 1,
+					'name' => 'Device 1',
+					'typ' => 1
+			)),
+			'Device' => array(
+				array(
+					'id' => 1,
+					'device_type_id' => 1,
+					'name' => 'Device 1',
+					'typ' => 1
+				),
+				array(
+					'id' => 2,
+					'device_type_id' => 1,
+					'name' => 'Device 2',
+					'typ' => 1
+				),
+				array(
+					'id' => 3,
+					'device_type_id' => 1,
+					'name' => 'Device 3',
+					'typ' => 2
+		)));
+
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testHabtmRecursiveBelongsTo method
+ *
+ * @access public
+ * @return void
+ */
+	function testHabtmRecursiveBelongsTo() {
+		$this->loadFixtures('Portfolio', 'Item', 'ItemsPortfolio', 'Syfile', 'Image');
+		$Portfolio =& new Portfolio();
+
+		$result = $Portfolio->find(array('id' => 2), null, null, 3);
+		$expected = array(
+			'Portfolio' => array(
+				'id' => 2,
+				'seller_id' => 1,
+				'name' => 'Portfolio 2'
+			),
+			'Item' => array(
+				array(
+					'id' => 2,
+					'syfile_id' => 2,
+					'published' => 0,
+					'name' => 'Item 2',
+					'ItemsPortfolio' => array(
+						'id' => 2,
+						'item_id' => 2,
+						'portfolio_id' => 2
+					),
+					'Syfile' => array(
+						'id' => 2,
+						'image_id' => 2,
+						'name' => 'Syfile 2',
+						'item_count' => null,
+						'Image' => array(
+							'id' => 2,
+							'name' => 'Image 2'
+						)
+				)),
+				array(
+					'id' => 6,
+					'syfile_id' => 6,
+					'published' => 0,
+					'name' => 'Item 6',
+					'ItemsPortfolio' => array(
+						'id' => 6,
+						'item_id' => 6,
+						'portfolio_id' => 2
+					),
+					'Syfile' => array(
+						'id' => 6,
+						'image_id' => null,
+						'name' => 'Syfile 6',
+						'item_count' => null,
+						'Image' => array()
+		))));
+
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testHabtmFinderQuery method
+ *
+ * @access public
+ * @return void
+ */
+	function testHabtmFinderQuery() {
+		$this->loadFixtures('Article', 'Tag', 'ArticlesTag');
+		$Article =& new Article();
+
+		$sql = $this->db->buildStatement(
+			array(
+				'fields' => $this->db->fields($Article->Tag, null, array(
+					'Tag.id', 'Tag.tag', 'ArticlesTag.article_id', 'ArticlesTag.tag_id'
+				)),
+				'table' => $this->db->fullTableName('tags'),
+				'alias' => 'Tag',
+				'limit' => null,
+				'offset' => null,
+				'group' => null,
+				'joins' => array(array(
+					'alias' => 'ArticlesTag',
+					'table' => 'articles_tags',
+					'conditions' => array(
+						array("ArticlesTag.article_id" => '{$__cakeID__$}'),
+						array("ArticlesTag.tag_id" => $this->db->identifier('Tag.id'))
+					)
+				)),
+				'conditions' => array(),
+				'order' => null
+			),
+			$Article
+		);
+
+		$Article->hasAndBelongsToMany['Tag']['finderQuery'] = $sql;
+		$result = $Article->find('first');
+		$expected = array(
+			array(
+				'id' => '1',
+				'tag' => 'tag1'
+			),
+			array(
+				'id' => '2',
+				'tag' => 'tag2'
+		));
+
+		$this->assertEqual($result['Tag'], $expected);
+	}
+
+/**
+ * testHabtmLimitOptimization method
+ *
+ * @access public
+ * @return void
+ */
+	function testHabtmLimitOptimization() {
+		$this->loadFixtures('Article', 'User', 'Comment', 'Tag', 'ArticlesTag');
+		$TestModel =& new Article();
+
+		$TestModel->hasAndBelongsToMany['Tag']['limit'] = 2;
+		$result = $TestModel->read(null, 2);
+		$expected = array(
+			'Article' => array(
+				'id' => '2',
+				'user_id' => '3',
+				'title' => 'Second Article',
+				'body' => 'Second Article Body',
+				'published' => 'Y',
+				'created' => '2007-03-18 10:41:23',
+				'updated' => '2007-03-18 10:43:31'
+			),
+			'User' => array(
+				'id' => '3',
+				'user' => 'larry',
+				'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+				'created' => '2007-03-17 01:20:23',
+				'updated' => '2007-03-17 01:22:31'
+			),
+			'Comment' => array(
+				array(
+					'id' => '5',
+					'article_id' => '2',
+					'user_id' => '1',
+					'comment' => 'First Comment for Second Article',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:53:23',
+					'updated' => '2007-03-18 10:55:31'
+				),
+				array(
+					'id' => '6',
+					'article_id' => '2',
+					'user_id' => '2',
+					'comment' => 'Second Comment for Second Article',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:55:23',
+					'updated' => '2007-03-18 10:57:31'
+			)),
+			'Tag' => array(
+				array(
+					'id' => '1',
+					'tag' => 'tag1',
+					'created' => '2007-03-18 12:22:23',
+					'updated' => '2007-03-18 12:24:31'
+				),
+				array(
+					'id' => '3',
+					'tag' => 'tag3',
+					'created' => '2007-03-18 12:26:23',
+					'updated' => '2007-03-18 12:28:31'
+		)));
+
+		$this->assertEqual($result, $expected);
+
+		$TestModel->hasAndBelongsToMany['Tag']['limit'] = 1;
+		$result = $TestModel->read(null, 2);
+		unset($expected['Tag'][1]);
+
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testHasManyLimitOptimization method
+ *
+ * @access public
+ * @return void
+ */
+	function testHasManyLimitOptimization() {
+		$this->loadFixtures('Project', 'Thread', 'Message', 'Bid');
+		$Project =& new Project();
+		$Project->recursive = 3;
+
+		$result = $Project->find('all');
+		$expected = array(
+			array(
+				'Project' => array(
+					'id' => 1,
+					'name' => 'Project 1'
+				),
+				'Thread' => array(
+					array(
+						'id' => 1,
+						'project_id' => 1,
+						'name' => 'Project 1, Thread 1',
+						'Project' => array(
+							'id' => 1,
+							'name' => 'Project 1',
+							'Thread' => array(
+								array(
+									'id' => 1,
+									'project_id' => 1,
+									'name' => 'Project 1, Thread 1'
+								),
+								array(
+									'id' => 2,
+									'project_id' => 1,
+									'name' => 'Project 1, Thread 2'
+						))),
+						'Message' => array(
+							array(
+								'id' => 1,
+								'thread_id' => 1,
+								'name' => 'Thread 1, Message 1',
+								'Bid' => array(
+									'id' => 1,
+									'message_id' => 1,
+									'name' => 'Bid 1.1'
+					)))),
+					array(
+						'id' => 2,
+						'project_id' => 1,
+						'name' => 'Project 1, Thread 2',
+						'Project' => array(
+							'id' => 1,
+							'name' => 'Project 1',
+							'Thread' => array(
+								array(
+									'id' => 1,
+									'project_id' => 1,
+									'name' => 'Project 1, Thread 1'
+								),
+								array(
+									'id' => 2,
+									'project_id' => 1,
+									'name' => 'Project 1, Thread 2'
+						))),
+						'Message' => array(
+							array(
+								'id' => 2,
+								'thread_id' => 2,
+								'name' => 'Thread 2, Message 1',
+								'Bid' => array(
+									'id' => 4,
+									'message_id' => 2,
+									'name' => 'Bid 2.1'
+			)))))),
+			array(
+				'Project' => array(
+					'id' => 2,
+					'name' => 'Project 2'
+				),
+				'Thread' => array(
+					array(
+						'id' => 3,
+						'project_id' => 2,
+						'name' => 'Project 2, Thread 1',
+						'Project' => array(
+							'id' => 2,
+							'name' => 'Project 2',
+							'Thread' => array(
+								array(
+									'id' => 3,
+									'project_id' => 2,
+									'name' => 'Project 2, Thread 1'
+						))),
+						'Message' => array(
+							array(
+								'id' => 3,
+								'thread_id' => 3,
+								'name' => 'Thread 3, Message 1',
+								'Bid' => array(
+									'id' => 3,
+									'message_id' => 3,
+									'name' => 'Bid 3.1'
+			)))))),
+			array(
+				'Project' => array(
+					'id' => 3,
+					'name' => 'Project 3'
+				),
+				'Thread' => array()
+		));
+
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testFindAllRecursiveSelfJoin method
+ *
+ * @access public
+ * @return void
+ */
+	function testFindAllRecursiveSelfJoin() {
+		$this->loadFixtures('Home', 'AnotherArticle', 'Advertisement');
+		$TestModel =& new Home();
+		$TestModel->recursive = 2;
+
+		$result = $TestModel->find('all');
+		$expected = array(
+			array(
+				'Home' => array(
+					'id' => '1',
+					'another_article_id' => '1',
+					'advertisement_id' => '1',
+					'title' => 'First Home',
+					'created' => '2007-03-18 10:39:23',
+					'updated' => '2007-03-18 10:41:31'
+				),
+				'AnotherArticle' => array(
+					'id' => '1',
+					'title' => 'First Article',
+					'created' => '2007-03-18 10:39:23',
+					'updated' => '2007-03-18 10:41:31',
+					'Home' => array(
+						array(
+							'id' => '1',
+							'another_article_id' => '1',
+							'advertisement_id' => '1',
+							'title' => 'First Home',
+							'created' => '2007-03-18 10:39:23',
+							'updated' => '2007-03-18 10:41:31'
+				))),
+				'Advertisement' => array(
+					'id' => '1',
+					'title' => 'First Ad',
+					'created' => '2007-03-18 10:39:23',
+					'updated' => '2007-03-18 10:41:31',
+					'Home' => array(
+						array(
+							'id' => '1',
+							'another_article_id' => '1',
+							'advertisement_id' => '1',
+							'title' => 'First Home',
+							'created' => '2007-03-18 10:39:23',
+							'updated' => '2007-03-18 10:41:31'
+						),
+						array(
+							'id' => '2',
+							'another_article_id' => '3',
+							'advertisement_id' => '1',
+							'title' => 'Second Home',
+							'created' => '2007-03-18 10:41:23',
+							'updated' => '2007-03-18 10:43:31'
+			)))),
+			array(
+				'Home' => array(
+					'id' => '2',
+					'another_article_id' => '3',
+					'advertisement_id' => '1',
+					'title' => 'Second Home',
+					'created' => '2007-03-18 10:41:23',
+					'updated' => '2007-03-18 10:43:31'
+				),
+				'AnotherArticle' => array(
+					'id' => '3',
+					'title' => 'Third Article',
+					'created' => '2007-03-18 10:43:23',
+					'updated' => '2007-03-18 10:45:31',
+					'Home' => array(
+						array(
+							'id' => '2',
+							'another_article_id' => '3',
+							'advertisement_id' => '1',
+							'title' => 'Second Home',
+							'created' => '2007-03-18 10:41:23',
+							'updated' => '2007-03-18 10:43:31'
+				))),
+				'Advertisement' => array(
+					'id' => '1',
+					'title' => 'First Ad',
+					'created' => '2007-03-18 10:39:23',
+					'updated' => '2007-03-18 10:41:31',
+					'Home' => array(
+						array(
+							'id' => '1',
+							'another_article_id' => '1',
+							'advertisement_id' => '1',
+							'title' => 'First Home',
+							'created' => '2007-03-18 10:39:23',
+							'updated' => '2007-03-18 10:41:31'
+						),
+						array(
+							'id' => '2',
+							'another_article_id' => '3',
+							'advertisement_id' => '1',
+							'title' => 'Second Home',
+							'created' => '2007-03-18 10:41:23',
+							'updated' => '2007-03-18 10:43:31'
+		)))));
+
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testFindAllRecursiveWithHabtm method
+ *
+ * @return void
+ * @access public
+ */
+	function testFindAllRecursiveWithHabtm() {
+		$this->loadFixtures(
+			'MyCategoriesMyUsers',
+			'MyCategoriesMyProducts',
+			'MyCategory',
+			'MyUser',
+			'MyProduct'
+		);
+
+		$MyUser =& new MyUser();
+		$MyUser->recursive = 2;
+
+		$result = $MyUser->find('all');
+		$expected = array(
+			array(
+				'MyUser' => array('id' => '1', 'firstname' => 'userA'),
+				'MyCategory' => array(
+					array(
+						'id' => '1',
+						'name' => 'A',
+						'MyProduct' => array(
+							array(
+								'id' => '1',
+								'name' => 'book'
+					))),
+					array(
+						'id' => '3',
+						'name' => 'C',
+						'MyProduct' => array(
+							array(
+								'id' => '2',
+								'name' => 'computer'
+			))))),
+			array(
+				'MyUser' => array(
+					'id' => '2',
+					'firstname' => 'userB'
+				),
+				'MyCategory' => array(
+					array(
+						'id' => '1',
+						'name' => 'A',
+						'MyProduct' => array(
+							array(
+								'id' => '1',
+								'name' => 'book'
+					))),
+					array(
+						'id' => '2',
+						'name' => 'B',
+						'MyProduct' => array(
+							array(
+								'id' => '1',
+								'name' => 'book'
+							),
+							array(
+								'id' => '2',
+								'name' => 'computer'
+		))))));
+
+		$this->assertIdentical($result, $expected);
+	}
+
+/**
+ * testReadFakeThread method
+ *
+ * @access public
+ * @return void
+ */
+	function testReadFakeThread() {
+		$this->loadFixtures('CategoryThread');
+		$TestModel =& new CategoryThread();
+
+		$fullDebug = $this->db->fullDebug;
+		$this->db->fullDebug = true;
+		$TestModel->recursive = 6;
+		$TestModel->id = 7;
+		$result = $TestModel->read();
+		$expected = array(
+			'CategoryThread' => array(
+				'id' => 7,
+				'parent_id' => 6,
+				'name' => 'Category 2.1',
+				'created' => '2007-03-18 15:30:23',
+				'updated' => '2007-03-18 15:32:31'
+			),
+			'ParentCategory' => array(
+				'id' => 6,
+				'parent_id' => 5,
+				'name' => 'Category 2',
+				'created' => '2007-03-18 15:30:23',
+				'updated' => '2007-03-18 15:32:31',
+				'ParentCategory' => array(
+					'id' => 5,
+					'parent_id' => 4,
+					'name' => 'Category 1.1.1.1',
+					'created' => '2007-03-18 15:30:23',
+					'updated' => '2007-03-18 15:32:31',
+					'ParentCategory' => array(
+						'id' => 4,
+						'parent_id' => 3,
+						'name' => 'Category 1.1.2',
+						'created' => '2007-03-18 15:30:23',
+						'updated' => '2007-03-18 15:32:31',
+						'ParentCategory' => array(
+							'id' => 3,
+							'parent_id' => 2,
+							'name' => 'Category 1.1.1',
+							'created' => '2007-03-18 15:30:23',
+							'updated' => '2007-03-18 15:32:31',
+							'ParentCategory' => array(
+								'id' => 2,
+								'parent_id' => 1,
+								'name' => 'Category 1.1',
+								'created' => '2007-03-18 15:30:23',
+								'updated' => '2007-03-18 15:32:31',
+								'ParentCategory' => array(
+									'id' => 1,
+									'parent_id' => 0,
+									'name' => 'Category 1',
+									'created' => '2007-03-18 15:30:23',
+									'updated' => '2007-03-18 15:32:31'
+		)))))));
+
+		$this->db->fullDebug = $fullDebug;
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testFindFakeThread method
+ *
+ * @access public
+ * @return void
+ */
+	function testFindFakeThread() {
+		$this->loadFixtures('CategoryThread');
+		$TestModel =& new CategoryThread();
+
+		$fullDebug = $this->db->fullDebug;
+		$this->db->fullDebug = true;
+		$TestModel->recursive = 6;
+		$result = $TestModel->find(array('CategoryThread.id' => 7));
+
+		$expected = array(
+			'CategoryThread' => array(
+				'id' => 7,
+				'parent_id' => 6,
+				'name' => 'Category 2.1',
+				'created' => '2007-03-18 15:30:23',
+				'updated' => '2007-03-18 15:32:31'
+			),
+			'ParentCategory' => array(
+				'id' => 6,
+				'parent_id' => 5,
+				'name' => 'Category 2',
+				'created' => '2007-03-18 15:30:23',
+				'updated' => '2007-03-18 15:32:31',
+				'ParentCategory' => array(
+					'id' => 5,
+					'parent_id' => 4,
+					'name' => 'Category 1.1.1.1',
+					'created' => '2007-03-18 15:30:23',
+					'updated' => '2007-03-18 15:32:31',
+					'ParentCategory' => array(
+						'id' => 4,
+						'parent_id' => 3,
+						'name' => 'Category 1.1.2',
+						'created' => '2007-03-18 15:30:23',
+						'updated' => '2007-03-18 15:32:31',
+						'ParentCategory' => array(
+							'id' => 3,
+							'parent_id' => 2,
+							'name' => 'Category 1.1.1',
+							'created' => '2007-03-18 15:30:23',
+							'updated' => '2007-03-18 15:32:31',
+							'ParentCategory' => array(
+								'id' => 2,
+								'parent_id' => 1,
+								'name' => 'Category 1.1',
+								'created' => '2007-03-18 15:30:23',
+								'updated' => '2007-03-18 15:32:31',
+								'ParentCategory' => array(
+									'id' => 1,
+									'parent_id' => 0,
+									'name' => 'Category 1',
+									'created' => '2007-03-18 15:30:23',
+									'updated' => '2007-03-18 15:32:31'
+		)))))));
+
+		$this->db->fullDebug = $fullDebug;
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testFindAllFakeThread method
+ *
+ * @access public
+ * @return void
+ */
+	function testFindAllFakeThread() {
+		$this->loadFixtures('CategoryThread');
+		$TestModel =& new CategoryThread();
+
+		$fullDebug = $this->db->fullDebug;
+		$this->db->fullDebug = true;
+		$TestModel->recursive = 6;
+		$result = $TestModel->find('all', null, null, 'CategoryThread.id ASC');
+		$expected = array(
+			array(
+				'CategoryThread' => array(
+				'id' => 1,
+				'parent_id' => 0,
+				'name' => 'Category 1',
+				'created' => '2007-03-18 15:30:23',
+				'updated' => '2007-03-18 15:32:31'
+				),
+				'ParentCategory' => array(
+					'id' => null,
+					'parent_id' => null,
+					'name' => null,
+					'created' => null,
+					'updated' => null,
+					'ParentCategory' => array()
+			)),
+			array(
+				'CategoryThread' => array(
+					'id' => 2,
+					'parent_id' => 1,
+					'name' => 'Category 1.1',
+					'created' => '2007-03-18 15:30:23',
+					'updated' => '2007-03-18 15:32:31'
+				),
+				'ParentCategory' => array(
+					'id' => 1,
+					'parent_id' => 0,
+					'name' => 'Category 1',
+					'created' => '2007-03-18 15:30:23',
+					'updated' => '2007-03-18 15:32:31',
+					'ParentCategory' => array()
+				)),
+			array(
+				'CategoryThread' => array(
+					'id' => 3,
+					'parent_id' => 2,
+					'name' => 'Category 1.1.1',
+					'created' => '2007-03-18 15:30:23',
+					'updated' => '2007-03-18 15:32:31'
+				),
+				'ParentCategory' => array(
+					'id' => 2,
+					'parent_id' => 1,
+					'name' => 'Category 1.1',
+					'created' => '2007-03-18 15:30:23',
+					'updated' => '2007-03-18 15:32:31',
+					'ParentCategory' => array(
+						'id' => 1,
+						'parent_id' => 0,
+						'name' => 'Category 1',
+						'created' => '2007-03-18 15:30:23',
+						'updated' => '2007-03-18 15:32:31',
+						'ParentCategory' => array()
+			))),
+			array(
+				'CategoryThread' => array(
+					'id' => 4,
+					'parent_id' => 3,
+					'name' => 'Category 1.1.2',
+					'created' => '2007-03-18 15:30:23',
+					'updated' => '2007-03-18 15:32:31'
+				),
+				'ParentCategory' => array(
+					'id' => 3,
+					'parent_id' => 2,
+					'name' => 'Category 1.1.1',
+					'created' => '2007-03-18 15:30:23',
+					'updated' => '2007-03-18 15:32:31',
+					'ParentCategory' => array(
+						'id' => 2,
+						'parent_id' => 1,
+						'name' => 'Category 1.1',
+						'created' => '2007-03-18 15:30:23',
+						'updated' => '2007-03-18 15:32:31',
+						'ParentCategory' => array(
+							'id' => 1,
+							'parent_id' => 0,
+							'name' => 'Category 1',
+							'created' => '2007-03-18 15:30:23',
+							'updated' => '2007-03-18 15:32:31',
+							'ParentCategory' => array()
+			)))),
+			array(
+				'CategoryThread' => array(
+					'id' => 5,
+					'parent_id' => 4,
+					'name' => 'Category 1.1.1.1',
+					'created' => '2007-03-18 15:30:23',
+					'updated' => '2007-03-18 15:32:31'
+				),
+				'ParentCategory' => array(
+					'id' => 4,
+					'parent_id' => 3,
+					'name' => 'Category 1.1.2',
+					'created' => '2007-03-18 15:30:23',
+					'updated' => '2007-03-18 15:32:31',
+					'ParentCategory' => array(
+						'id' => 3,
+						'parent_id' => 2,
+						'name' => 'Category 1.1.1',
+						'created' => '2007-03-18 15:30:23',
+						'updated' => '2007-03-18 15:32:31',
+						'ParentCategory' => array(
+							'id' => 2,
+							'parent_id' => 1,
+							'name' => 'Category 1.1',
+							'created' => '2007-03-18 15:30:23',
+							'updated' => '2007-03-18 15:32:31',
+							'ParentCategory' => array(
+								'id' => 1,
+								'parent_id' => 0,
+								'name' => 'Category 1',
+								'created' => '2007-03-18 15:30:23',
+								'updated' => '2007-03-18 15:32:31',
+								'ParentCategory' => array()
+			))))),
+			array(
+				'CategoryThread' => array(
+					'id' => 6,
+					'parent_id' => 5,
+					'name' => 'Category 2',
+					'created' => '2007-03-18 15:30:23',
+					'updated' => '2007-03-18 15:32:31'
+				),
+				'ParentCategory' => array(
+					'id' => 5,
+					'parent_id' => 4,
+					'name' => 'Category 1.1.1.1',
+					'created' => '2007-03-18 15:30:23',
+					'updated' => '2007-03-18 15:32:31',
+					'ParentCategory' => array(
+						'id' => 4,
+						'parent_id' => 3,
+						'name' => 'Category 1.1.2',
+						'created' => '2007-03-18 15:30:23',
+						'updated' => '2007-03-18 15:32:31',
+						'ParentCategory' => array(
+							'id' => 3,
+							'parent_id' => 2,
+							'name' => 'Category 1.1.1',
+							'created' => '2007-03-18 15:30:23',
+							'updated' => '2007-03-18 15:32:31',
+							'ParentCategory' => array(
+								'id' => 2,
+								'parent_id' => 1,
+								'name' => 'Category 1.1',
+								'created' => '2007-03-18 15:30:23',
+								'updated' => '2007-03-18 15:32:31',
+								'ParentCategory' => array(
+									'id' => 1,
+									'parent_id' => 0,
+									'name' => 'Category 1',
+									'created' => '2007-03-18 15:30:23',
+									'updated' => '2007-03-18 15:32:31',
+									'ParentCategory' => array()
+			)))))),
+			array(
+				'CategoryThread' => array(
+					'id' => 7,
+					'parent_id' => 6,
+					'name' => 'Category 2.1',
+					'created' => '2007-03-18 15:30:23',
+					'updated' => '2007-03-18 15:32:31'
+				),
+				'ParentCategory' => array(
+					'id' => 6,
+					'parent_id' => 5,
+					'name' => 'Category 2',
+					'created' => '2007-03-18 15:30:23',
+					'updated' => '2007-03-18 15:32:31',
+					'ParentCategory' => array(
+						'id' => 5,
+						'parent_id' => 4,
+						'name' => 'Category 1.1.1.1',
+						'created' => '2007-03-18 15:30:23',
+						'updated' => '2007-03-18 15:32:31',
+						'ParentCategory' => array(
+							'id' => 4,
+							'parent_id' => 3,
+							'name' => 'Category 1.1.2',
+							'created' => '2007-03-18 15:30:23',
+							'updated' => '2007-03-18 15:32:31',
+							'ParentCategory' => array(
+								'id' => 3,
+								'parent_id' => 2,
+								'name' => 'Category 1.1.1',
+								'created' => '2007-03-18 15:30:23',
+								'updated' => '2007-03-18 15:32:31',
+							'ParentCategory' => array(
+								'id' => 2,
+								'parent_id' => 1,
+								'name' => 'Category 1.1',
+								'created' => '2007-03-18 15:30:23',
+								'updated' => '2007-03-18 15:32:31',
+								'ParentCategory' => array(
+									'id' => 1,
+									'parent_id' => 0,
+									'name' => 'Category 1',
+									'created' => '2007-03-18 15:30:23',
+									'updated' => '2007-03-18 15:32:31'
+		))))))));
+
+		$this->db->fullDebug = $fullDebug;
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testConditionalNumerics method
+ *
+ * @access public
+ * @return void
+ */
+	function testConditionalNumerics() {
+		$this->loadFixtures('NumericArticle');
+		$NumericArticle =& new NumericArticle();
+		$data = array('title' => '12345abcde');
+		$result = $NumericArticle->find($data);
+		$this->assertTrue(!empty($result));
+
+		$data = array('title' => '12345');
+		$result = $NumericArticle->find($data);
+		$this->assertTrue(empty($result));
+	}
+
+/**
+ * test find('all') method
+ *
+ * @access public
+ * @return void
+ */
+	function testFindAll() {
+		$this->loadFixtures('User');
+		$TestModel =& new User();
+		$TestModel->cacheQueries = false;
+
+		$result = $TestModel->find('all');
+		$expected = array(
+			array(
+				'User' => array(
+					'id' => '1',
+					'user' => 'mariano',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23',
+					'updated' => '2007-03-17 01:18:31'
+			)),
+			array(
+				'User' => array(
+					'id' => '2',
+					'user' => 'nate',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:18:23',
+					'updated' => '2007-03-17 01:20:31'
+			)),
+			array(
+				'User' => array(
+					'id' => '3',
+					'user' => 'larry',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23',
+					'updated' => '2007-03-17 01:22:31'
+			)),
+			array(
+				'User' => array(
+					'id' => '4',
+					'user' => 'garrett',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:22:23',
+					'updated' => '2007-03-17 01:24:31'
+		)));
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('all', array('conditions' => 'User.id > 2'));
+		$expected = array(
+			array(
+				'User' => array(
+					'id' => '3',
+					'user' => 'larry',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23',
+					'updated' => '2007-03-17 01:22:31'
+			)),
+			array(
+				'User' => array(
+					'id' => '4',
+					'user' => 'garrett',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:22:23',
+					'updated' => '2007-03-17 01:24:31'
+		)));
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('all', array(
+			'conditions' => array('User.id !=' => '0', 'User.user LIKE' => '%arr%')
+		));
+		$expected = array(
+			array(
+				'User' => array(
+					'id' => '3',
+					'user' => 'larry',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23',
+					'updated' => '2007-03-17 01:22:31'
+			)),
+			array(
+				'User' => array(
+					'id' => '4',
+					'user' => 'garrett',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:22:23',
+					'updated' => '2007-03-17 01:24:31'
+		)));
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('all', array('conditions' => array('User.id' => '0')));
+		$expected = array();
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('all', array(
+			'conditions' => array('or' => array('User.id' => '0', 'User.user LIKE' => '%a%')
+		)));
+
+		$expected = array(
+			array(
+				'User' => array(
+					'id' => '1',
+					'user' => 'mariano',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23',
+					'updated' => '2007-03-17 01:18:31'
+			)),
+			array(
+				'User' => array(
+					'id' => '2',
+					'user' => 'nate',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:18:23',
+					'updated' => '2007-03-17 01:20:31'
+			)),
+			array(
+				'User' => array(
+					'id' => '3',
+					'user' => 'larry',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23',
+					'updated' => '2007-03-17 01:22:31'
+			)),
+			array(
+				'User' => array(
+					'id' => '4',
+					'user' => 'garrett',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:22:23',
+					'updated' => '2007-03-17 01:24:31'
+		)));
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('all', array('fields' => 'User.id, User.user'));
+		$expected = array(
+				array('User' => array('id' => '1', 'user' => 'mariano')),
+				array('User' => array('id' => '2', 'user' => 'nate')),
+				array('User' => array('id' => '3', 'user' => 'larry')),
+				array('User' => array('id' => '4', 'user' => 'garrett')));
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('all', array('fields' => 'User.user', 'order' => 'User.user ASC'));
+		$expected = array(
+				array('User' => array('user' => 'garrett')),
+				array('User' => array('user' => 'larry')),
+				array('User' => array('user' => 'mariano')),
+				array('User' => array('user' => 'nate')));
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('all', array('fields' => 'User.user', 'order' => 'User.user DESC'));
+		$expected = array(
+				array('User' => array('user' => 'nate')),
+				array('User' => array('user' => 'mariano')),
+				array('User' => array('user' => 'larry')),
+				array('User' => array('user' => 'garrett')));
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('all', array('limit' => 3, 'page' => 1));
+
+		$expected = array(
+			array(
+				'User' => array(
+					'id' => '1',
+					'user' => 'mariano',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23',
+					'updated' => '2007-03-17 01:18:31'
+			)),
+			array(
+				'User' => array(
+					'id' => '2',
+					'user' => 'nate',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:18:23',
+					'updated' => '2007-03-17 01:20:31'
+			)),
+			array(
+				'User' => array(
+					'id' => '3',
+					'user' => 'larry',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23',
+					'updated' => '2007-03-17 01:22:31'
+		)));
+		$this->assertEqual($result, $expected);
+
+		$ids = array(4 => 1, 5 => 3);
+		$result = $TestModel->find('all', array(
+			'conditions' => array('User.id' => $ids),
+			'order' => 'User.id'
+		));
+		$expected = array(
+			array(
+				'User' => array(
+					'id' => '1',
+					'user' => 'mariano',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23',
+					'updated' => '2007-03-17 01:18:31'
+			)),
+			array(
+				'User' => array(
+					'id' => '3',
+					'user' => 'larry',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23',
+					'updated' => '2007-03-17 01:22:31'
+		)));
+		$this->assertEqual($result, $expected);
+
+		// These tests are expected to fail on SQL Server since the LIMIT/OFFSET
+		// hack can't handle small record counts.
+		if ($this->db->config['driver'] != 'mssql') {
+			$result = $TestModel->find('all', array('limit' => 3, 'page' => 2));
+			$expected = array(
+				array(
+					'User' => array(
+						'id' => '4',
+						'user' => 'garrett',
+						'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+						'created' => '2007-03-17 01:22:23',
+						'updated' => '2007-03-17 01:24:31'
+			)));
+			$this->assertEqual($result, $expected);
+
+			$result = $TestModel->find('all', array('limit' => 3, 'page' => 3));
+			$expected = array();
+			$this->assertEqual($result, $expected);
+		}
+	}
+
+/**
+ * test find('list') method
+ *
+ * @access public
+ * @return void
+ */
+	function testGenerateFindList() {
+		$this->loadFixtures('Article', 'Apple', 'Post', 'Author', 'User');
+
+		$TestModel =& new Article();
+		$TestModel->displayField = 'title';
+
+		$result = $TestModel->find('list', array(
+			'order' => 'Article.title ASC'
+		));
+
+		$expected = array(
+			1 => 'First Article',
+			2 => 'Second Article',
+			3 => 'Third Article'
+		);
+		$this->assertEqual($result, $expected);
+
+		$db =& ConnectionManager::getDataSource('test_suite');
+		if ($db->config['driver'] == 'mysql') {
+			$result = $TestModel->find('list', array(
+				'order' => array('FIELD(Article.id, 3, 2) ASC', 'Article.title ASC')
+			));
+			$expected = array(
+				1 => 'First Article',
+				3 => 'Third Article',
+				2 => 'Second Article'
+			);
+			$this->assertEqual($result, $expected);
+		}
+
+		$result = Set::combine(
+			$TestModel->find('all', array(
+				'order' => 'Article.title ASC',
+				'fields' => array('id', 'title')
+			)),
+			'{n}.Article.id', '{n}.Article.title'
+		);
+		$expected = array(
+			1 => 'First Article',
+			2 => 'Second Article',
+			3 => 'Third Article'
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = Set::combine(
+			$TestModel->find('all', array(
+				'order' => 'Article.title ASC'
+			)),
+			'{n}.Article.id', '{n}.Article'
+		);
+		$expected = array(
+			1 => array(
+				'id' => 1,
+				'user_id' => 1,
+				'title' => 'First Article',
+				'body' => 'First Article Body',
+				'published' => 'Y',
+				'created' => '2007-03-18 10:39:23',
+				'updated' => '2007-03-18 10:41:31'
+			),
+			2 => array(
+				'id' => 2,
+				'user_id' => 3,
+				'title' => 'Second Article',
+				'body' => 'Second Article Body',
+				'published' => 'Y',
+				'created' => '2007-03-18 10:41:23',
+				'updated' => '2007-03-18 10:43:31'
+			),
+			3 => array(
+				'id' => 3,
+				'user_id' => 1,
+				'title' => 'Third Article',
+				'body' => 'Third Article Body',
+				'published' => 'Y',
+				'created' => '2007-03-18 10:43:23',
+				'updated' => '2007-03-18 10:45:31'
+		));
+
+		$this->assertEqual($result, $expected);
+
+		$result = Set::combine(
+			$TestModel->find('all', array(
+				'order' => 'Article.title ASC'
+			)),
+			'{n}.Article.id', '{n}.Article', '{n}.Article.user_id'
+		);
+		$expected = array(
+			1 => array(
+				1 => array(
+					'id' => 1,
+					'user_id' => 1,
+					'title' => 'First Article',
+					'body' => 'First Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:39:23',
+					'updated' => '2007-03-18 10:41:31'
+				),
+				3 => array(
+					'id' => 3,
+					'user_id' => 1,
+					'title' => 'Third Article',
+					'body' => 'Third Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:43:23',
+					'updated' => '2007-03-18 10:45:31'
+				)),
+			3 => array(
+				2 => array(
+					'id' => 2,
+					'user_id' => 3,
+					'title' => 'Second Article',
+					'body' => 'Second Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:41:23',
+					'updated' => '2007-03-18 10:43:31'
+		)));
+
+		$this->assertEqual($result, $expected);
+
+		$result = Set::combine(
+			$TestModel->find('all', array(
+				'order' => 'Article.title ASC',
+				'fields' => array('id', 'title', 'user_id')
+			)),
+			'{n}.Article.id', '{n}.Article.title', '{n}.Article.user_id'
+		);
+
+		$expected = array(
+			1 => array(
+				1 => 'First Article',
+				3 => 'Third Article'
+			),
+			3 => array(
+				2 => 'Second Article'
+		));
+		$this->assertEqual($result, $expected);
+
+		$TestModel =& new Apple();
+		$expected = array(
+			1 => 'Red Apple 1',
+			2 => 'Bright Red Apple',
+			3 => 'green blue',
+			4 => 'Test Name',
+			5 => 'Blue Green',
+			6 => 'My new apple',
+			7 => 'Some odd color'
+		);
+
+		$this->assertEqual($TestModel->find('list'), $expected);
+		$this->assertEqual($TestModel->Parent->find('list'), $expected);
+
+		$TestModel =& new Post();
+		$result = $TestModel->find('list', array(
+			'fields' => 'Post.title'
+		));
+		$expected = array(
+			1 => 'First Post',
+			2 => 'Second Post',
+			3 => 'Third Post'
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('list', array(
+			'fields' => 'title'
+		));
+		$expected = array(
+			1 => 'First Post',
+			2 => 'Second Post',
+			3 => 'Third Post'
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('list', array(
+			'fields' => array('title', 'id')
+		));
+		$expected = array(
+			'First Post' => '1',
+			'Second Post' => '2',
+			'Third Post' => '3'
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('list', array(
+			'fields' => array('title', 'id', 'created')
+		));
+		$expected = array(
+			'2007-03-18 10:39:23' => array(
+				'First Post' => '1'
+			),
+			'2007-03-18 10:41:23' => array(
+				'Second Post' => '2'
+			),
+			'2007-03-18 10:43:23' => array(
+				'Third Post' => '3'
+			),
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('list', array(
+			'fields' => array('Post.body')
+		));
+		$expected = array(
+			1 => 'First Post Body',
+			2 => 'Second Post Body',
+			3 => 'Third Post Body'
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('list', array(
+			'fields' => array('Post.title', 'Post.body')
+		));
+		$expected = array(
+			'First Post' => 'First Post Body',
+			'Second Post' => 'Second Post Body',
+			'Third Post' => 'Third Post Body'
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('list', array(
+			'fields' => array('Post.id', 'Post.title', 'Author.user'),
+			'recursive' => 1
+		));
+		$expected = array(
+			'mariano' => array(
+				1 => 'First Post',
+				3 => 'Third Post'
+			),
+			'larry' => array(
+				2 => 'Second Post'
+		));
+		$this->assertEqual($result, $expected);
+
+		$TestModel =& new User();
+		$result = $TestModel->find('list', array(
+			'fields' => array('User.user', 'User.password')
+		));
+		$expected = array(
+			'mariano' => '5f4dcc3b5aa765d61d8327deb882cf99',
+			'nate' => '5f4dcc3b5aa765d61d8327deb882cf99',
+			'larry' => '5f4dcc3b5aa765d61d8327deb882cf99',
+			'garrett' => '5f4dcc3b5aa765d61d8327deb882cf99'
+		);
+		$this->assertEqual($result, $expected);
+
+		$TestModel =& new ModifiedAuthor();
+		$result = $TestModel->find('list', array(
+			'fields' => array('Author.id', 'Author.user')
+		));
+		$expected = array(
+			1 => 'mariano (CakePHP)',
+			2 => 'nate (CakePHP)',
+			3 => 'larry (CakePHP)',
+			4 => 'garrett (CakePHP)'
+		);
+		$this->assertEqual($result, $expected);
+
+		$TestModel =& new Article();
+		$TestModel->displayField = 'title';
+		$result = $TestModel->find('list', array(
+			'conditions' => array('User.user' => 'mariano'),
+			'recursive' => 0
+		));
+		$expected = array(
+			1 => 'First Article',
+			3 => 'Third Article'
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testFindField method
+ *
+ * @access public
+ * @return void
+ */
+	function testFindField() {
+		$this->loadFixtures('User');
+		$TestModel =& new User();
+
+		$TestModel->id = 1;
+		$result = $TestModel->field('user');
+		$this->assertEqual($result, 'mariano');
+
+		$result = $TestModel->field('User.user');
+		$this->assertEqual($result, 'mariano');
+
+		$TestModel->id = false;
+		$result = $TestModel->field('user', array(
+			'user' => 'mariano'
+		));
+		$this->assertEqual($result, 'mariano');
+
+		$result = $TestModel->field('COUNT(*) AS count', true);
+		$this->assertEqual($result, 4);
+
+		$result = $TestModel->field('COUNT(*)', true);
+		$this->assertEqual($result, 4);
+	}
+
+/**
+ * testFindUnique method
+ *
+ * @access public
+ * @return void
+ */
+	function testFindUnique() {
+		$this->loadFixtures('User');
+		$TestModel =& new User();
+
+		$this->assertFalse($TestModel->isUnique(array(
+			'user' => 'nate'
+		)));
+		$TestModel->id = 2;
+		$this->assertTrue($TestModel->isUnique(array(
+			'user' => 'nate'
+		)));
+		$this->assertFalse($TestModel->isUnique(array(
+			'user' => 'nate',
+			'password' => '5f4dcc3b5aa765d61d8327deb882cf99'
+		)));
+	}
+
+/**
+ * test find('count') method
+ *
+ * @access public
+ * @return void
+ */
+	function testFindCount() {
+		$this->loadFixtures('User', 'Project');
+
+		$TestModel =& new User();
+		$result = $TestModel->find('count');
+		$this->assertEqual($result, 4);
+
+		$fullDebug = $this->db->fullDebug;
+		$this->db->fullDebug = true;
+		$TestModel->order = 'User.id';
+		$this->db->_queriesLog = array();
+		$result = $TestModel->find('count');
+		$this->assertEqual($result, 4);
+
+		$this->assertTrue(isset($this->db->_queriesLog[0]['query']));
+		$this->assertNoPattern('/ORDER\s+BY/', $this->db->_queriesLog[0]['query']);
+	}
+
+/**
+ * Test that find('first') does not use the id set to the object.
+ *
+ * @return void
+ */
+	function testFindFirstNoIdUsed() {
+		$this->loadFixtures('Project');
+
+		$Project =& new Project();
+		$Project->id = 3;
+		$result = $Project->find('first');
+
+		$this->assertEqual($result['Project']['name'], 'Project 1', 'Wrong record retrieved');
+	}
+
+/**
+ * test find with COUNT(DISTINCT field)
+ *
+ * @return void
+ */
+	function testFindCountDistinct() {
+		$skip = $this->skipIf(
+			$this->db->config['driver'] == 'sqlite',
+			'SELECT COUNT(DISTINCT field) is not compatible with SQLite'
+		);
+		if ($skip) {
+			return;
+		}
+		$this->loadFixtures('Project');
+		$TestModel =& new Project();
+		$TestModel->create(array('name' => 'project')) && $TestModel->save();
+		$TestModel->create(array('name' => 'project')) && $TestModel->save();
+		$TestModel->create(array('name' => 'project')) && $TestModel->save();
+
+		$result = $TestModel->find('count', array('fields' => 'DISTINCT name'));
+		$this->assertEqual($result, 4);
+	}
+
+/**
+ * Test find(count) with Db::expression
+ *
+ * @access public
+ * @return void
+ */
+	function testFindCountWithDbExpressions() {
+		if ($this->skipIf($this->db->config['driver'] == 'postgres', '%s testFindCountWithExpressions is not compatible with Postgres')) {
+			return;
+		}
+		$this->loadFixtures('Project');
+		$db = ConnectionManager::getDataSource('test_suite');
+		$TestModel =& new Project();
+
+		$result = $TestModel->find('count', array('conditions' => array(
+			$db->expression('Project.name = \'Project 3\'')
+		)));
+		$this->assertEqual($result, 1);
+
+		$result = $TestModel->find('count', array('conditions' => array(
+			'Project.name' => $db->expression('\'Project 3\'')
+		)));
+		$this->assertEqual($result, 1);
+	}
+
+/**
+ * testFindMagic method
+ *
+ * @access public
+ * @return void
+ */
+	function testFindMagic() {
+		$this->loadFixtures('User');
+		$TestModel =& new User();
+
+		$result = $TestModel->findByUser('mariano');
+		$expected = array(
+			'User' => array(
+				'id' => '1',
+				'user' => 'mariano',
+				'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+				'created' => '2007-03-17 01:16:23',
+				'updated' => '2007-03-17 01:18:31'
+		));
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->findByPassword('5f4dcc3b5aa765d61d8327deb882cf99');
+		$expected = array('User' => array(
+			'id' => '1',
+			'user' => 'mariano',
+			'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+			'created' => '2007-03-17 01:16:23',
+			'updated' => '2007-03-17 01:18:31'
+		));
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testRead method
+ *
+ * @access public
+ * @return void
+ */
+	function testRead() {
+		$this->loadFixtures('User', 'Article');
+		$TestModel =& new User();
+
+		$result = $TestModel->read();
+		$this->assertFalse($result);
+
+		$TestModel->id = 2;
+		$result = $TestModel->read();
+		$expected = array(
+			'User' => array(
+				'id' => '2',
+				'user' => 'nate',
+				'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+				'created' => '2007-03-17 01:18:23',
+				'updated' => '2007-03-17 01:20:31'
+		));
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->read(null, 2);
+		$expected = array(
+			'User' => array(
+				'id' => '2',
+				'user' => 'nate',
+				'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+				'created' => '2007-03-17 01:18:23',
+				'updated' => '2007-03-17 01:20:31'
+		));
+		$this->assertEqual($result, $expected);
+
+		$TestModel->id = 2;
+		$result = $TestModel->read(array('id', 'user'));
+		$expected = array('User' => array('id' => '2', 'user' => 'nate'));
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->read('id, user', 2);
+		$expected = array(
+			'User' => array(
+				'id' => '2',
+				'user' => 'nate'
+		));
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->bindModel(array('hasMany' => array('Article')));
+		$this->assertTrue($result);
+
+		$TestModel->id = 1;
+		$result = $TestModel->read('id, user');
+		$expected = array(
+			'User' => array(
+				'id' => '1',
+				'user' => 'mariano'
+			),
+			'Article' => array(
+				array(
+					'id' => '1',
+					'user_id' => '1',
+					'title' => 'First Article',
+					'body' => 'First Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:39:23',
+					'updated' => '2007-03-18 10:41:31'
+				),
+				array(
+					'id' => '3',
+					'user_id' => '1',
+					'title' => 'Third Article',
+					'body' => 'Third Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:43:23',
+					'updated' => '2007-03-18 10:45:31'
+		)));
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testRecursiveRead method
+ *
+ * @access public
+ * @return void
+ */
+	function testRecursiveRead() {
+		$this->loadFixtures(
+			'User',
+			'Article',
+			'Comment',
+			'Tag',
+			'ArticlesTag',
+			'Featured',
+			'ArticleFeatured'
+		);
+		$TestModel =& new User();
+
+		$result = $TestModel->bindModel(array('hasMany' => array('Article')), false);
+		$this->assertTrue($result);
+
+		$TestModel->recursive = 0;
+		$result = $TestModel->read('id, user', 1);
+		$expected = array(
+			'User' => array('id' => '1', 'user' => 'mariano'),
+		);
+		$this->assertEqual($result, $expected);
+
+		$TestModel->recursive = 1;
+		$result = $TestModel->read('id, user', 1);
+		$expected = array(
+			'User' => array(
+				'id' => '1',
+				'user' => 'mariano'
+			),
+			'Article' => array(
+				array(
+					'id' => '1',
+					'user_id' => '1',
+					'title' => 'First Article',
+					'body' => 'First Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:39:23',
+					'updated' => '2007-03-18 10:41:31'
+				),
+				array(
+					'id' => '3',
+					'user_id' => '1',
+					'title' => 'Third Article',
+					'body' => 'Third Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:43:23',
+					'updated' => '2007-03-18 10:45:31'
+		)));
+		$this->assertEqual($result, $expected);
+
+		$TestModel->recursive = 2;
+		$result = $TestModel->read('id, user', 3);
+		$expected = array(
+			'User' => array(
+				'id' => '3',
+				'user' => 'larry'
+			),
+			'Article' => array(
+				array(
+					'id' => '2',
+					'user_id' => '3',
+					'title' => 'Second Article',
+					'body' => 'Second Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:41:23',
+					'updated' => '2007-03-18 10:43:31',
+					'User' => array(
+						'id' => '3',
+						'user' => 'larry',
+						'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+						'created' => '2007-03-17 01:20:23',
+						'updated' => '2007-03-17 01:22:31'
+					),
+					'Comment' => array(
+						array(
+							'id' => '5',
+							'article_id' => '2',
+							'user_id' => '1',
+							'comment' => 'First Comment for Second Article',
+							'published' => 'Y',
+							'created' => '2007-03-18 10:53:23',
+							'updated' => '2007-03-18 10:55:31'
+						),
+						array(
+							'id' => '6',
+							'article_id' => '2',
+							'user_id' => '2',
+							'comment' => 'Second Comment for Second Article',
+							'published' => 'Y',
+							'created' => '2007-03-18 10:55:23',
+							'updated' => '2007-03-18 10:57:31'
+					)),
+					'Tag' => array(
+						array(
+							'id' => '1',
+							'tag' => 'tag1',
+							'created' => '2007-03-18 12:22:23',
+							'updated' => '2007-03-18 12:24:31'
+						),
+						array(
+							'id' => '3',
+							'tag' => 'tag3',
+							'created' => '2007-03-18 12:26:23',
+							'updated' => '2007-03-18 12:28:31'
+		)))));
+		$this->assertEqual($result, $expected);
+	}
+
+	function testRecursiveFindAll() {
+		$this->db->truncate(new Featured());
+
+		$this->loadFixtures(
+			'User',
+			'Article',
+			'Comment',
+			'Tag',
+			'ArticlesTag',
+			'Attachment',
+			'ArticleFeatured',
+			'Featured',
+			'Category'
+		);
+		$TestModel =& new Article();
+
+		$result = $TestModel->find('all', array('conditions' => array('Article.user_id' => 1)));
+		$expected = array(
+			array(
+				'Article' => array(
+					'id' => '1',
+					'user_id' => '1',
+					'title' => 'First Article',
+					'body' => 'First Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:39:23',
+					'updated' => '2007-03-18 10:41:31'
+				),
+				'User' => array(
+					'id' => '1',
+					'user' => 'mariano',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23',
+					'updated' => '2007-03-17 01:18:31'
+				),
+				'Comment' => array(
+					array(
+						'id' => '1',
+						'article_id' => '1',
+						'user_id' => '2',
+						'comment' => 'First Comment for First Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:45:23',
+						'updated' => '2007-03-18 10:47:31'
+					),
+					array(
+						'id' => '2',
+						'article_id' => '1',
+						'user_id' => '4',
+						'comment' => 'Second Comment for First Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:47:23',
+						'updated' => '2007-03-18 10:49:31'
+					),
+					array(
+						'id' => '3',
+						'article_id' => '1',
+						'user_id' => '1',
+						'comment' => 'Third Comment for First Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:49:23',
+						'updated' => '2007-03-18 10:51:31'
+					),
+					array(
+						'id' => '4',
+						'article_id' => '1',
+						'user_id' => '1',
+						'comment' => 'Fourth Comment for First Article',
+						'published' => 'N',
+						'created' => '2007-03-18 10:51:23',
+						'updated' => '2007-03-18 10:53:31'
+					)
+				),
+				'Tag' => array(
+					array(
+						'id' => '1',
+						'tag' => 'tag1',
+						'created' => '2007-03-18 12:22:23',
+						'updated' => '2007-03-18 12:24:31'
+					),
+					array(
+						'id' => '2',
+						'tag' => 'tag2',
+						'created' => '2007-03-18 12:24:23',
+						'updated' => '2007-03-18 12:26:31'
+			))),
+			array(
+				'Article' => array(
+					'id' => '3',
+					'user_id' => '1',
+					'title' => 'Third Article',
+					'body' => 'Third Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:43:23',
+					'updated' => '2007-03-18 10:45:31'
+				),
+				'User' => array(
+					'id' => '1',
+					'user' => 'mariano',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23',
+					'updated' => '2007-03-17 01:18:31'
+				),
+				'Comment' => array(),
+				'Tag' => array()
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->find('all', array(
+			'conditions' => array('Article.user_id' => 3),
+			'limit' => 1,
+			'recursive' => 2
+		));
+
+		$expected = array(
+			array(
+				'Article' => array(
+					'id' => '2',
+					'user_id' => '3',
+					'title' => 'Second Article',
+					'body' => 'Second Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:41:23',
+					'updated' => '2007-03-18 10:43:31'
+				),
+				'User' => array(
+					'id' => '3',
+					'user' => 'larry',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23',
+					'updated' => '2007-03-17 01:22:31'
+				),
+				'Comment' => array(
+					array(
+						'id' => '5',
+						'article_id' => '2',
+						'user_id' => '1',
+						'comment' => 'First Comment for Second Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:53:23',
+						'updated' => '2007-03-18 10:55:31',
+						'Article' => array(
+							'id' => '2',
+							'user_id' => '3',
+							'title' => 'Second Article',
+							'body' => 'Second Article Body',
+							'published' => 'Y',
+							'created' => '2007-03-18 10:41:23',
+							'updated' => '2007-03-18 10:43:31'
+						),
+						'User' => array(
+							'id' => '1',
+							'user' => 'mariano',
+							'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+							'created' => '2007-03-17 01:16:23',
+							'updated' => '2007-03-17 01:18:31'
+						),
+						'Attachment' => array(
+							'id' => '1',
+							'comment_id' => 5,
+							'attachment' => 'attachment.zip',
+							'created' => '2007-03-18 10:51:23',
+							'updated' => '2007-03-18 10:53:31'
+						)
+					),
+					array(
+						'id' => '6',
+						'article_id' => '2',
+						'user_id' => '2',
+						'comment' => 'Second Comment for Second Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:55:23',
+						'updated' => '2007-03-18 10:57:31',
+						'Article' => array(
+							'id' => '2',
+							'user_id' => '3',
+							'title' => 'Second Article',
+							'body' => 'Second Article Body',
+							'published' => 'Y',
+							'created' => '2007-03-18 10:41:23',
+							'updated' => '2007-03-18 10:43:31'
+						),
+						'User' => array(
+							'id' => '2',
+							'user' => 'nate',
+							'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+							'created' => '2007-03-17 01:18:23',
+							'updated' => '2007-03-17 01:20:31'
+						),
+						'Attachment' => false
+					)
+				),
+				'Tag' => array(
+					array(
+						'id' => '1',
+						'tag' => 'tag1',
+						'created' => '2007-03-18 12:22:23',
+						'updated' => '2007-03-18 12:24:31'
+					),
+					array(
+						'id' => '3',
+						'tag' => 'tag3',
+						'created' => '2007-03-18 12:26:23',
+						'updated' => '2007-03-18 12:28:31'
+		))));
+
+		$this->assertEqual($result, $expected);
+
+		$Featured = new Featured();
+
+		$Featured->recursive = 2;
+		$Featured->bindModel(array(
+			'belongsTo' => array(
+				'ArticleFeatured' => array(
+					'conditions' => "ArticleFeatured.published = 'Y'",
+					'fields' => 'id, title, user_id, published'
+				)
+			)
+		));
+
+		$Featured->ArticleFeatured->unbindModel(array(
+			'hasMany' => array('Attachment', 'Comment'),
+			'hasAndBelongsToMany' => array('Tag'))
+		);
+
+		$orderBy = 'ArticleFeatured.id ASC';
+		$result = $Featured->find('all', array(
+			'order' => $orderBy, 'limit' => 3
+		));
+
+		$expected = array(
+			array(
+				'Featured' => array(
+					'id' => '1',
+					'article_featured_id' => '1',
+					'category_id' => '1',
+					'published_date' => '2007-03-31 10:39:23',
+					'end_date' => '2007-05-15 10:39:23',
+					'created' => '2007-03-18 10:39:23',
+					'updated' => '2007-03-18 10:41:31'
+				),
+				'ArticleFeatured' => array(
+					'id' => '1',
+					'title' => 'First Article',
+					'user_id' => '1',
+					'published' => 'Y',
+					'User' => array(
+						'id' => '1',
+						'user' => 'mariano',
+						'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+						'created' => '2007-03-17 01:16:23',
+						'updated' => '2007-03-17 01:18:31'
+					),
+					'Category' => array(),
+					'Featured' => array(
+						'id' => '1',
+						'article_featured_id' => '1',
+						'category_id' => '1',
+						'published_date' => '2007-03-31 10:39:23',
+						'end_date' => '2007-05-15 10:39:23',
+						'created' => '2007-03-18 10:39:23',
+						'updated' => '2007-03-18 10:41:31'
+				)),
+				'Category' => array(
+					'id' => '1',
+					'parent_id' => '0',
+					'name' => 'Category 1',
+					'created' => '2007-03-18 15:30:23',
+					'updated' => '2007-03-18 15:32:31'
+				)),
+			array(
+				'Featured' => array(
+					'id' => '2',
+					'article_featured_id' => '2',
+					'category_id' => '1',
+					'published_date' => '2007-03-31 10:39:23',
+					'end_date' => '2007-05-15 10:39:23',
+					'created' => '2007-03-18 10:39:23',
+					'updated' => '2007-03-18 10:41:31'
+				),
+				'ArticleFeatured' => array(
+					'id' => '2',
+					'title' => 'Second Article',
+					'user_id' => '3',
+					'published' => 'Y',
+					'User' => array(
+						'id' => '3',
+						'user' => 'larry',
+						'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+						'created' => '2007-03-17 01:20:23',
+						'updated' => '2007-03-17 01:22:31'
+					),
+					'Category' => array(),
+					'Featured' => array(
+						'id' => '2',
+						'article_featured_id' => '2',
+						'category_id' => '1',
+						'published_date' => '2007-03-31 10:39:23',
+						'end_date' => '2007-05-15 10:39:23',
+						'created' => '2007-03-18 10:39:23',
+						'updated' => '2007-03-18 10:41:31'
+				)),
+				'Category' => array(
+					'id' => '1',
+					'parent_id' => '0',
+					'name' => 'Category 1',
+					'created' => '2007-03-18 15:30:23',
+					'updated' => '2007-03-18 15:32:31'
+		)));
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testRecursiveFindAllWithLimit method
+ *
+ * @access public
+ * @return void
+ */
+	function testRecursiveFindAllWithLimit() {
+		$this->loadFixtures('Article', 'User', 'Tag', 'ArticlesTag', 'Comment', 'Attachment');
+		$TestModel =& new Article();
+
+		$TestModel->hasMany['Comment']['limit'] = 2;
+
+		$result = $TestModel->find('all', array(
+			'conditions' => array('Article.user_id' => 1)
+		));
+		$expected = array(
+			array(
+				'Article' => array(
+					'id' => '1',
+					'user_id' => '1',
+					'title' => 'First Article',
+					'body' => 'First Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:39:23',
+					'updated' => '2007-03-18 10:41:31'
+				),
+				'User' => array(
+					'id' => '1',
+					'user' => 'mariano',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23',
+					'updated' => '2007-03-17 01:18:31'
+				),
+				'Comment' => array(
+					array(
+						'id' => '1',
+						'article_id' => '1',
+						'user_id' => '2',
+						'comment' => 'First Comment for First Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:45:23',
+						'updated' => '2007-03-18 10:47:31'
+					),
+					array(
+						'id' => '2',
+						'article_id' => '1',
+						'user_id' => '4',
+						'comment' => 'Second Comment for First Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:47:23',
+						'updated' => '2007-03-18 10:49:31'
+					),
+				),
+				'Tag' => array(
+					array(
+						'id' => '1',
+						'tag' => 'tag1',
+						'created' => '2007-03-18 12:22:23',
+						'updated' => '2007-03-18 12:24:31'
+					),
+					array(
+						'id' => '2',
+						'tag' => 'tag2',
+						'created' => '2007-03-18 12:24:23',
+						'updated' => '2007-03-18 12:26:31'
+			))),
+			array(
+				'Article' => array(
+					'id' => '3',
+					'user_id' => '1',
+					'title' => 'Third Article',
+					'body' => 'Third Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:43:23',
+					'updated' => '2007-03-18 10:45:31'
+				),
+				'User' => array(
+					'id' => '1',
+					'user' => 'mariano',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:16:23',
+					'updated' => '2007-03-17 01:18:31'
+				),
+				'Comment' => array(),
+				'Tag' => array()
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$TestModel->hasMany['Comment']['limit'] = 1;
+
+		$result = $TestModel->find('all', array(
+			'conditions' => array('Article.user_id' => 3),
+			'limit' => 1,
+			'recursive' => 2
+		));
+		$expected = array(
+			array(
+				'Article' => array(
+					'id' => '2',
+					'user_id' => '3',
+					'title' => 'Second Article',
+					'body' => 'Second Article Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:41:23',
+					'updated' => '2007-03-18 10:43:31'
+				),
+				'User' => array(
+					'id' => '3',
+					'user' => 'larry',
+					'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+					'created' => '2007-03-17 01:20:23',
+					'updated' => '2007-03-17 01:22:31'
+				),
+				'Comment' => array(
+					array(
+						'id' => '5',
+						'article_id' => '2',
+						'user_id' => '1',
+						'comment' => 'First Comment for Second Article',
+						'published' => 'Y',
+						'created' => '2007-03-18 10:53:23',
+						'updated' => '2007-03-18 10:55:31',
+						'Article' => array(
+							'id' => '2',
+							'user_id' => '3',
+							'title' => 'Second Article',
+							'body' => 'Second Article Body',
+							'published' => 'Y',
+							'created' => '2007-03-18 10:41:23',
+							'updated' => '2007-03-18 10:43:31'
+						),
+						'User' => array(
+							'id' => '1',
+							'user' => 'mariano',
+							'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+							'created' => '2007-03-17 01:16:23',
+							'updated' => '2007-03-17 01:18:31'
+						),
+						'Attachment' => array(
+							'id' => '1',
+							'comment_id' => 5,
+							'attachment' => 'attachment.zip',
+							'created' => '2007-03-18 10:51:23',
+							'updated' => '2007-03-18 10:53:31'
+						)
+					)
+				),
+				'Tag' => array(
+					array(
+						'id' => '1',
+						'tag' => 'tag1',
+						'created' => '2007-03-18 12:22:23',
+						'updated' => '2007-03-18 12:24:31'
+					),
+					array(
+						'id' => '3',
+						'tag' => 'tag3',
+						'created' => '2007-03-18 12:26:23',
+						'updated' => '2007-03-18 12:28:31'
+					)
+				)
+			)
+		);
+		$this->assertEqual($result, $expected);
+	}
+/**
+ * Testing availability of $this->findQueryType in Model callbacks
+ *
+ * @return void
+ */
+	function testFindQueryTypeInCallbacks() {
+		$this->loadFixtures('Comment');
+		$Comment =& new AgainModifiedComment();
+		$comments = $Comment->find('all');
+		$this->assertEqual($comments[0]['Comment']['querytype'], 'all');
+		$comments = $Comment->find('first');
+		$this->assertEqual($comments['Comment']['querytype'], 'first');
+	}
+
+/**
+ * testVirtualFields()
+ *
+ * Test correct fetching of virtual fields
+ * currently is not possible to do Relation.virtualField
+ *
+ * @access public
+ * @return void
+ */
+	function testVirtualFields() {
+		$this->loadFixtures('Post', 'Author');
+		$Post =& ClassRegistry::init('Post');
+		$Post->virtualFields = array('two' => "1 + 1");
+		$result = $Post->find('first');
+		$this->assertEqual($result['Post']['two'], 2);
+
+		$Post->Author->virtualFields = array('false' => '1 = 2');
+		$result = $Post->find('first');
+		$this->assertEqual($result['Post']['two'], 2);
+		$this->assertEqual($result['Author']['false'], false);
+
+		$result = $Post->find('first',array('fields' => array('author_id')));
+		$this->assertFalse(isset($result['Post']['two']));
+		$this->assertFalse(isset($result['Author']['false']));
+
+		$result = $Post->find('first',array('fields' => array('author_id', 'two')));
+		$this->assertEqual($result['Post']['two'], 2);
+		$this->assertFalse(isset($result['Author']['false']));
+
+		$result = $Post->find('first',array('fields' => array('two')));
+		$this->assertEqual($result['Post']['two'], 2);
+
+		$Post->id = 1;
+		$result = $Post->field('two');
+		$this->assertEqual($result, 2);
+
+		$result = $Post->find('first',array(
+			'conditions' => array('two' => 2),
+			'limit' => 1
+		));
+		$this->assertEqual($result['Post']['two'], 2);
+
+		$result = $Post->find('first',array(
+			'conditions' => array('two <' => 3),
+			'limit' => 1
+		));
+		$this->assertEqual($result['Post']['two'], 2);
+
+		$result = $Post->find('first',array(
+			'conditions' => array('NOT' => array('two >' => 3)),
+			'limit' => 1
+		));
+		$this->assertEqual($result['Post']['two'], 2);
+
+		$dbo =& $Post->getDataSource();
+		$Post->virtualFields = array('other_field' => 'Post.id + 1');
+		$result = $Post->find('first', array(
+			'conditions' => array('other_field' => 3),
+			'limit' => 1
+		));
+		$this->assertEqual($result['Post']['id'], 2);
+
+		$Post->virtualFields = array('other_field' => 'Post.id + 1');
+		$result = $Post->find('all', array(
+			'fields' => array($dbo->calculate($Post, 'max', array('other_field')))
+		));
+		$this->assertEqual($result[0][0]['other_field'], 4);
+
+		ClassRegistry::flush();
+		$Writing =& ClassRegistry::init(array('class' => 'Post', 'alias' => 'Writing'), 'Model');
+		$Writing->virtualFields = array('two' => "1 + 1");
+		$result = $Writing->find('first');
+		$this->assertEqual($result['Writing']['two'], 2);
+
+		$Post->create();
+		$Post->virtualFields = array('other_field' => 'COUNT(Post.id) + 1');
+		$result = $Post->field('other_field');
+		$this->assertEqual($result, 4);
+
+		if ($this->skipIf($this->db->config['driver'] == 'postgres', 'The rest of virtualFieds test is not compatible with Postgres')) {
+			return;
+		}
+		ClassRegistry::flush();
+		$Post =& ClassRegistry::init('Post');
+
+		$Post->create();
+		$Post->virtualFields = array(
+			'year' => 'YEAR(Post.created)',
+			'unique_test_field' => 'COUNT(Post.id)'
+		);
+
+		$expectation = array(
+			'Post' => array(
+				'year' => 2007,
+				'unique_test_field' => 3
+			)
+		);
+
+		$result = $Post->find('first', array(
+			'fields' => array_keys($Post->virtualFields),
+			'group' => array('year')
+		));
+
+		$this->assertEqual($result, $expectation);
+
+
+		$Author =& ClassRegistry::init('Author');
+		$Author->virtualFields = array(
+			'full_name' => 'CONCAT(Author.user, " ", Author.id)'
+		);
+
+		$result = $Author->find('first', array(
+			'conditions' => array('Author.user' => 'mariano'),
+			'fields' => array('Author.password', 'Author.full_name'),
+			'recursive' => -1
+		));
+		$this->assertTrue(isset($result['Author']['full_name']));
+
+		$result = $Author->find('first', array(
+			'conditions' => array('Author.user' => 'mariano'),
+			'fields' => array('Author.full_name', 'Author.password'),
+			'recursive' => -1
+		));
+		$this->assertTrue(isset($result['Author']['full_name']));
+	}
+
+/**
+ * Test that virtual fields work with SQL constants
+ *
+ * @return void
+ */
+	function testVirtualFieldAsAConstant() {
+		$this->loadFixtures('Post', 'Author');
+		$Post =& ClassRegistry::init('Post');
+		$Post->virtualFields = array('empty' => "NULL");
+		$result = $Post->find('first');
+		$this->assertNull($result['Post']['empty']);
+	}
+
+/**
+ * test that virtual fields work when they don't contain functions.
+ *
+ * @return void
+ */
+	function testVirtualFieldAsAString() {
+		$this->loadFixtures('Post', 'Author');
+		$Post =& new Post();
+		$Post->virtualFields = array(
+		    'writer' => 'Author.user'
+		);
+		$result = $Post->find('first');
+		$this->assertTrue(isset($result['Post']['writer']), 'virtual field not fetched %s');
+	}
+
+/**
+ * test that isVirtualField will accept both aliased and non aliased fieldnames
+ *
+ * @return void
+ */
+	function testIsVirtualField() {
+		$this->loadFixtures('Post');
+		$Post =& ClassRegistry::init('Post');
+		$Post->virtualFields = array('other_field' => 'COUNT(Post.id) + 1');
+
+		$this->assertTrue($Post->isVirtualField('other_field'));
+		$this->assertTrue($Post->isVirtualField('Post.other_field'));
+		$this->assertFalse($Post->isVirtualField('Comment.other_field'), 'Other models should not match.');
+		$this->assertFalse($Post->isVirtualField('id'));
+		$this->assertFalse($Post->isVirtualField('Post.id'));
+		$this->assertFalse($Post->isVirtualField(array()));
+	}
+
+/**
+ * test that getting virtual fields works with and without model alias attached
+ *
+ * @return void
+ */
+	function testGetVirtualField() {
+		$this->loadFixtures('Post');
+		$Post =& ClassRegistry::init('Post');
+		$Post->virtualFields = array('other_field' => 'COUNT(Post.id) + 1');
+
+		$this->assertEqual($Post->getVirtualField('other_field'), $Post->virtualFields['other_field']);
+		$this->assertEqual($Post->getVirtualField('Post.other_field'), $Post->virtualFields['other_field']);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/model/model_validation.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/model/model_validation.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/model/model_validation.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,782 @@
+<?php
+/* SVN FILE: $Id: model.test.php 8225 2009-07-08 03:25:30Z mark_story $ */
+
+/**
+ * ModelValidationTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+require_once dirname(__FILE__) . DS . 'model.test.php';
+
+/**
+ * ModelValidationTest
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.operations
+ */
+class ModelValidationTest extends BaseModelTest {
+
+/**
+ * Tests validation parameter order in custom validation methods
+ *
+ * @access public
+ * @return void
+ */
+	function testValidationParams() {
+		$TestModel =& new ValidationTest1();
+		$TestModel->validate['title'] = array(
+			'rule' => 'customValidatorWithParams',
+			'required' => true
+		);
+		$TestModel->create(array('title' => 'foo'));
+		$TestModel->invalidFields();
+
+		$expected = array(
+			'data' => array(
+				'title' => 'foo'
+			),
+			'validator' => array(
+				'rule' => 'customValidatorWithParams',
+				'on' => null,
+				'last' => false,
+				'allowEmpty' => false,
+				'required' => true
+			),
+			'or' => true,
+			'ignore_on_same' => 'id'
+		);
+		$this->assertEqual($TestModel->validatorParams, $expected);
+
+		$TestModel->validate['title'] = array(
+			'rule' => 'customValidatorWithMessage',
+			'required' => true
+		);
+		$expected = array(
+			'title' => 'This field will *never* validate! Muhahaha!'
+		);
+
+		$this->assertEqual($TestModel->invalidFields(), $expected);
+
+		$TestModel->validate['title'] = array(
+			'rule' => array('customValidatorWithSixParams', 'one', 'two', null, 'four'),
+			'required' => true
+		);
+		$TestModel->create(array('title' => 'foo'));
+		$TestModel->invalidFields();
+		$expected = array(
+			'data' => array(
+				'title' => 'foo'
+			),
+			'one' => 'one',
+			'two' => 'two',
+			'three' => null,
+			'four' => 'four',
+			'five' => array(
+				'rule' => array(1 => 'one', 2 => 'two', 3 => null, 4 => 'four'),
+				'on' => null,
+				'last' => false,
+				'allowEmpty' => false,
+				'required' => true
+			),
+			'six' => 6
+		);
+		$this->assertEqual($TestModel->validatorParams, $expected);
+
+		$TestModel->validate['title'] = array(
+			'rule' => array('customValidatorWithSixParams', 'one', array('two'), null, 'four', array('five' => 5)),
+			'required' => true
+		);
+		$TestModel->create(array('title' => 'foo'));
+		$TestModel->invalidFields();
+		$expected = array(
+			'data' => array(
+				'title' => 'foo'
+			),
+			'one' => 'one',
+			'two' => array('two'),
+			'three' => null,
+			'four' => 'four',
+			'five' => array('five' => 5),
+			'six' => array(
+				'rule' => array(1 => 'one', 2 => array('two'), 3 => null, 4 => 'four', 5 => array('five' => 5)),
+				'on' => null,
+				'last' => false,
+				'allowEmpty' => false,
+				'required' => true
+			)
+		);
+		$this->assertEqual($TestModel->validatorParams, $expected);
+	}
+
+/**
+ * Tests validation parameter fieldList in invalidFields
+ *
+ * @access public
+ * @return void
+ */
+	function testInvalidFieldsWithFieldListParams() {
+		$TestModel =& new ValidationTest1();
+		$TestModel->validate = $validate = array(
+			'title' => array(
+				'rule' => 'alphaNumeric',
+				'required' => true
+			),
+			'name' => array(
+				'rule' => 'alphaNumeric',
+				'required' => true
+		));
+		$TestModel->set(array('title' => '$$', 'name' => '##'));
+		$TestModel->invalidFields(array('fieldList' => array('title')));
+		$expected = array(
+			'title' => 'This field cannot be left blank'
+		);
+		$this->assertEqual($TestModel->validationErrors, $expected);
+		$TestModel->validationErrors = array();
+
+		$TestModel->invalidFields(array('fieldList' => array('name')));
+		$expected = array(
+			'name' => 'This field cannot be left blank'
+		);
+		$this->assertEqual($TestModel->validationErrors, $expected);
+		$TestModel->validationErrors = array();
+
+		$TestModel->invalidFields(array('fieldList' => array('name', 'title')));
+		$expected = array(
+			'name' => 'This field cannot be left blank',
+			'title' => 'This field cannot be left blank'
+		);
+		$this->assertEqual($TestModel->validationErrors, $expected);
+		$TestModel->validationErrors = array();
+
+		$TestModel->whitelist = array('name');
+		$TestModel->invalidFields();
+		$expected = array('name' => 'This field cannot be left blank');
+		$this->assertEqual($TestModel->validationErrors, $expected);
+
+		$this->assertEqual($TestModel->validate, $validate);
+	}
+
+/**
+ * Test that invalidFields() integrates well with save().  And that fieldList can be an empty type.
+ *
+ * @return void
+ */
+	function testInvalidFieldsWhitelist() {
+		$TestModel =& new ValidationTest1();
+		$TestModel->validate = array(
+			'title' => array(
+				'rule' => 'alphaNumeric',
+				'required' => true
+			),
+			'name' => array(
+				'rule' => 'alphaNumeric',
+				'required' => true
+		));
+
+		$TestModel->whitelist = array('name');
+		$TestModel->save(array('name' => '#$$#', 'title' => '$$$$'));
+
+		$expected = array('name' => 'This field cannot be left blank');
+		$this->assertEqual($TestModel->validationErrors, $expected);
+	}
+
+/**
+ * testValidates method
+ *
+ * @access public
+ * @return void
+ */
+	function testValidates() {
+		$TestModel =& new TestValidate();
+
+		$TestModel->validate = array(
+			'user_id' => 'numeric',
+			'title' => array('allowEmpty' => false, 'rule' => 'notEmpty'),
+			'body' => 'notEmpty'
+		);
+
+		$data = array('TestValidate' => array(
+			'user_id' => '1',
+			'title' => '',
+			'body' => 'body'
+		));
+		$result = $TestModel->create($data);
+		$this->assertTrue($result);
+		$result = $TestModel->validates();
+		$this->assertFalse($result);
+
+		$data = array('TestValidate' => array(
+			'user_id' => '1',
+			'title' => 'title',
+			'body' => 'body'
+		));
+		$result = $TestModel->create($data) && $TestModel->validates();
+		$this->assertTrue($result);
+
+		$data = array('TestValidate' => array(
+			'user_id' => '1',
+			'title' => '0',
+			'body' => 'body'
+		));
+		$result = $TestModel->create($data);
+		$this->assertTrue($result);
+		$result = $TestModel->validates();
+		$this->assertTrue($result);
+
+		$data = array('TestValidate' => array(
+			'user_id' => '1',
+			'title' => 0,
+			'body' => 'body'
+		));
+		$result = $TestModel->create($data);
+		$this->assertTrue($result);
+		$result = $TestModel->validates();
+		$this->assertTrue($result);
+
+		$TestModel->validate['modified'] = array('allowEmpty' => true, 'rule' => 'date');
+
+		$data = array('TestValidate' => array(
+			'user_id' => '1',
+			'title' => 0,
+			'body' => 'body',
+			'modified' => ''
+		));
+		$result = $TestModel->create($data);
+		$this->assertTrue($result);
+		$result = $TestModel->validates();
+		$this->assertTrue($result);
+
+		$data = array('TestValidate' => array(
+			'user_id' => '1',
+			'title' => 0,
+			'body' => 'body',
+			'modified' => '2007-05-01'
+		));
+		$result = $TestModel->create($data);
+		$this->assertTrue($result);
+		$result = $TestModel->validates();
+		$this->assertTrue($result);
+
+		$data = array('TestValidate' => array(
+			'user_id' => '1',
+			'title' => 0,
+			'body' => 'body',
+			'modified' => 'invalid-date-here'
+		));
+		$result = $TestModel->create($data);
+		$this->assertTrue($result);
+		$result = $TestModel->validates();
+		$this->assertFalse($result);
+
+		$data = array('TestValidate' => array(
+			'user_id' => '1',
+			'title' => 0,
+			'body' => 'body',
+			'modified' => 0
+		));
+		$result = $TestModel->create($data);
+		$this->assertTrue($result);
+		$result = $TestModel->validates();
+		$this->assertFalse($result);
+
+		$data = array('TestValidate' => array(
+			'user_id' => '1',
+			'title' => 0,
+			'body' => 'body',
+			'modified' => '0'
+		));
+		$result = $TestModel->create($data);
+		$this->assertTrue($result);
+		$result = $TestModel->validates();
+		$this->assertFalse($result);
+
+		$TestModel->validate['modified'] = array('allowEmpty' => false, 'rule' => 'date');
+
+		$data = array('TestValidate' => array('modified' => null));
+		$result = $TestModel->create($data);
+		$this->assertTrue($result);
+		$result = $TestModel->validates();
+		$this->assertFalse($result);
+
+		$data = array('TestValidate' => array('modified' => false));
+		$result = $TestModel->create($data);
+		$this->assertTrue($result);
+		$result = $TestModel->validates();
+		$this->assertFalse($result);
+
+		$data = array('TestValidate' => array('modified' => ''));
+		$result = $TestModel->create($data);
+		$this->assertTrue($result);
+		$result = $TestModel->validates();
+		$this->assertFalse($result);
+
+		$data = array('TestValidate' => array(
+			'modified' => '2007-05-01'
+		));
+		$result = $TestModel->create($data);
+		$this->assertTrue($result);
+		$result = $TestModel->validates();
+		$this->assertTrue($result);
+
+		$TestModel->validate['slug'] = array('allowEmpty' => false, 'rule' => array('maxLength', 45));
+
+		$data = array('TestValidate' => array(
+			'user_id' => '1',
+			'title' => 0,
+			'body' => 'body',
+			'slug' => ''
+		));
+		$result = $TestModel->create($data);
+		$this->assertTrue($result);
+		$result = $TestModel->validates();
+		$this->assertFalse($result);
+
+		$data = array('TestValidate' => array(
+			'user_id' => '1',
+			'title' => 0,
+			'body' => 'body',
+			'slug' => 'slug-right-here'
+		));
+		$result = $TestModel->create($data);
+		$this->assertTrue($result);
+		$result = $TestModel->validates();
+		$this->assertTrue($result);
+
+		$data = array('TestValidate' => array(
+			'user_id' => '1',
+			'title' => 0,
+			'body' => 'body',
+			'slug' => 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz'
+		));
+		$result = $TestModel->create($data);
+		$this->assertTrue($result);
+		$result = $TestModel->validates();
+		$this->assertFalse($result);
+
+		$TestModel->validate = array(
+			'number' => array(
+				'rule' => 'validateNumber',
+				'min' => 3,
+				'max' => 5
+			),
+			'title' => array(
+				'allowEmpty' => false,
+				'rule' => 'notEmpty'
+		));
+
+		$data = array('TestValidate' => array(
+			'title' => 'title',
+			'number' => '0'
+		));
+		$result = $TestModel->create($data);
+		$this->assertTrue($result);
+		$result = $TestModel->validates();
+		$this->assertFalse($result);
+
+		$data = array('TestValidate' => array(
+			'title' => 'title',
+			'number' => 0
+		));
+		$result = $TestModel->create($data);
+		$this->assertTrue($result);
+		$result = $TestModel->validates();
+		$this->assertFalse($result);
+
+		$data = array('TestValidate' => array(
+			'title' => 'title',
+			'number' => '3'
+		));
+		$result = $TestModel->create($data);
+		$this->assertTrue($result);
+		$result = $TestModel->validates();
+		$this->assertTrue($result);
+
+		$data = array('TestValidate' => array(
+			'title' => 'title',
+			'number' => 3
+		));
+		$result = $TestModel->create($data);
+		$this->assertTrue($result);
+		$result = $TestModel->validates();
+		$this->assertTrue($result);
+
+		$TestModel->validate = array(
+			'number' => array(
+				'rule' => 'validateNumber',
+				'min' => 5,
+				'max' => 10
+			),
+			'title' => array(
+				'allowEmpty' => false,
+				'rule' => 'notEmpty'
+		));
+
+		$data = array('TestValidate' => array(
+			'title' => 'title',
+			'number' => '3'
+		));
+		$result = $TestModel->create($data);
+		$this->assertTrue($result);
+		$result = $TestModel->validates();
+		$this->assertFalse($result);
+
+		$data = array('TestValidate' => array(
+			'title' => 'title',
+			'number' => 3
+		));
+		$result = $TestModel->create($data);
+		$this->assertTrue($result);
+		$result = $TestModel->validates();
+		$this->assertFalse($result);
+
+		$TestModel->validate = array(
+			'title' => array(
+				'allowEmpty' => false,
+				'rule' => 'validateTitle'
+		));
+
+		$data = array('TestValidate' => array('title' => ''));
+		$result = $TestModel->create($data);
+		$this->assertTrue($result);
+		$result = $TestModel->validates();
+		$this->assertFalse($result);
+
+		$data = array('TestValidate' => array('title' => 'new title'));
+		$result = $TestModel->create($data);
+		$this->assertTrue($result);
+		$result = $TestModel->validates();
+		$this->assertFalse($result);
+
+		$data = array('TestValidate' => array('title' => 'title-new'));
+		$result = $TestModel->create($data);
+		$this->assertTrue($result);
+		$result = $TestModel->validates();
+		$this->assertTrue($result);
+
+		$TestModel->validate = array('title' => array(
+			'allowEmpty' => true,
+			'rule' => 'validateTitle'
+		));
+		$data = array('TestValidate' => array('title' => ''));
+		$result = $TestModel->create($data);
+		$this->assertTrue($result);
+		$result = $TestModel->validates();
+		$this->assertTrue($result);
+
+		$TestModel->validate = array(
+			'title' => array(
+				'length' => array(
+					'allowEmpty' => true,
+					'rule' => array('maxLength', 10)
+		)));
+		$data = array('TestValidate' => array('title' => ''));
+		$result = $TestModel->create($data);
+		$this->assertTrue($result);
+		$result = $TestModel->validates();
+		$this->assertTrue($result);
+
+		$TestModel->validate = array(
+			'title' => array(
+				'rule' => array('userDefined', 'Article', 'titleDuplicate')
+		));
+		$data = array('TestValidate' => array('title' => 'My Article Title'));
+		$result = $TestModel->create($data);
+		$this->assertTrue($result);
+		$result = $TestModel->validates();
+		$this->assertFalse($result);
+
+		$data = array('TestValidate' => array(
+			'title' => 'My Article With a Different Title'
+		));
+		$result = $TestModel->create($data);
+		$this->assertTrue($result);
+		$result = $TestModel->validates();
+		$this->assertTrue($result);
+
+		$TestModel->validate = array(
+			'title' => array(
+				'tooShort' => array('rule' => array('minLength', 50)),
+				'onlyLetters' => array('rule' => '/^[a-z]+$/i')
+			),
+		);
+		$data = array('TestValidate' => array(
+			'title' => 'I am a short string'
+		));
+		$TestModel->create($data);
+		$result = $TestModel->validates();
+		$this->assertFalse($result);
+		$result = $TestModel->validationErrors;
+		$expected = array(
+			'title' => 'onlyLetters'
+		);
+		$this->assertEqual($result, $expected);
+
+		$TestModel->validate = array(
+			'title' => array(
+				'tooShort' => array(
+					'rule' => array('minLength', 50),
+					'last' => true
+				),
+				'onlyLetters' => array('rule' => '/^[a-z]+$/i')
+			),
+		);
+		$data = array('TestValidate' => array(
+			'title' => 'I am a short string'
+		));
+		$TestModel->create($data);
+		$result = $TestModel->validates();
+		$this->assertFalse($result);
+		$result = $TestModel->validationErrors;
+		$expected = array(
+			'title' => 'tooShort'
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that validates() checks all the 'with' associations as well for validation
+ * as this can cause partial/wrong data insertion.
+ *
+ * @return void
+ */
+	function testValidatesWithAssociations() {
+		$data = array(
+			'Something' => array(
+				'id' => 5,
+				'title' => 'Extra Fields',
+				'body' => 'Extra Fields Body',
+				'published' => '1'
+			),
+			'SomethingElse' => array(
+				array('something_else_id' => 1, 'doomed' => '')
+			)
+		);
+
+		$Something =& new Something();
+		$JoinThing =& $Something->JoinThing;
+
+		$JoinThing->validate = array('doomed' => array('rule' => 'notEmpty'));
+
+		$expectedError = array('doomed' => 'This field cannot be left blank');
+
+		$Something->create();
+		$result = $Something->save($data);
+		$this->assertFalse($result, 'Save occured even when with models failed. %s');
+		$this->assertEqual($JoinThing->validationErrors, $expectedError);
+		$count = $Something->find('count', array('conditions' => array('Something.id' => $data['Something']['id'])));
+		$this->assertIdentical($count, 0);
+
+		$data = array(
+			'Something' => array(
+				'id' => 5,
+				'title' => 'Extra Fields',
+				'body' => 'Extra Fields Body',
+				'published' => '1'
+			),
+			'SomethingElse' => array(
+				array('something_else_id' => 1, 'doomed' => 1),
+				array('something_else_id' => 1, 'doomed' => '')
+			)
+		);
+		$Something->create();
+		$result = $Something->save($data);
+		$this->assertFalse($result, 'Save occured even when with models failed. %s');
+
+		$joinRecords = $JoinThing->find('count', array(
+			'conditions' => array('JoinThing.something_id' => $data['Something']['id'])
+		));
+		$this->assertEqual($joinRecords, 0, 'Records were saved on the join table. %s');
+	}
+
+/**
+ * test that saveAll and with models with validation interact well
+ *
+ * @return void
+ */
+	function testValidatesWithModelsAndSaveAll() {
+		$data = array(
+			'Something' => array(
+				'id' => 5,
+				'title' => 'Extra Fields',
+				'body' => 'Extra Fields Body',
+				'published' => '1'
+			),
+			'SomethingElse' => array(
+				array('something_else_id' => 1, 'doomed' => '')
+			)
+		);
+		$Something =& new Something();
+		$JoinThing =& $Something->JoinThing;
+
+		$JoinThing->validate = array('doomed' => array('rule' => 'notEmpty'));
+		$expectedError = array('doomed' => 'This field cannot be left blank');
+
+		$Something->create();
+		$result = $Something->saveAll($data, array('validate' => 'only'));
+		$this->assertFalse($result);
+		$this->assertEqual($JoinThing->validationErrors, $expectedError);
+
+		$Something->create();
+		$result = $Something->saveAll($data, array('validate' => 'first'));
+		$this->assertFalse($result);
+		$this->assertEqual($JoinThing->validationErrors, $expectedError);
+
+		$count = $Something->find('count', array('conditions' => array('Something.id' => $data['Something']['id'])));
+		$this->assertIdentical($count, 0);
+
+		$joinRecords = $JoinThing->find('count', array(
+			'conditions' => array('JoinThing.something_id' => $data['Something']['id'])
+		));
+		$this->assertEqual($joinRecords, 0, 'Records were saved on the join table. %s');
+	}
+
+/**
+ * test that saveAll and with models at initial insert (no id has set yet)
+ * with validation interact well
+ *
+ * @return void
+ */
+	function testValidatesWithModelsAndSaveAllWithoutId() {
+		$data = array(
+			'Article' => array(
+				'title' => 'Extra Fields',
+				'body' => 'Extra Fields Body',
+				'published' => '1'
+			),
+			'Comment' => array(
+				array('word' => 'Hello'),
+				array('word' => 'World'),
+			)
+		);
+		$Article =& new Article();
+		$Comment =& $Article->Comment;
+
+		$Comment->validate = array('article_id' => array('rule' => 'numeric'));
+
+		$Article->create();
+		$result = $Article->saveAll($data, array('validate' => 'only'));
+		$this->assertTrue($result);
+
+		$Article->create();
+		$result = $Article->saveAll($data, array('validate' => 'first'));
+		$this->assertTrue($result);
+		$this->assertFalse(is_null($Article->id));
+
+		$id = $Article->id;
+		$count = $Article->find('count', array('conditions' => array('Article.id' => $id)));
+		$this->assertIdentical($count, 1);
+
+		$count = $Comment->find('count', array(
+			'conditions' => array('Comment.article_id' => $id)
+		));
+		$this->assertEqual($count, count($data['Comment']));
+	}
+
+/**
+ * Test that missing validation methods trigger errors in development mode.
+ * Helps to make developement easier.
+ *
+ * @return void
+ */
+	function testMissingValidationErrorTriggering() {
+		$restore = Configure::read('debug');
+		Configure::write('debug', 2);
+
+		$TestModel =& new ValidationTest1();
+		$TestModel->create(array('title' => 'foo'));
+		$TestModel->validate = array(
+			'title' => array(
+				'rule' => array('thisOneBringsThePain'),
+				'required' => true
+			)
+		);
+		$this->expectError(new PatternExpectation('/thisOneBringsThePain for title/i'));
+		$TestModel->invalidFields(array('fieldList' => array('title')));
+
+		Configure::write('debug', 0);
+		$this->assertNoErrors();
+		$TestModel->invalidFields(array('fieldList' => array('title')));
+		Configure::write('debug', $restore);
+	}
+
+/**
+ * Test for 'on' => [create|update] in validation rules.
+ *
+ * @return void
+ */
+	function testStateValidation() {
+		$Article =& new Article();
+
+		$data = array(
+			'Article' => array(
+				'title' => '',
+				'body' => 'Extra Fields Body',
+				'published' => '1'
+			)
+		);
+
+		$Article->validate = array(
+			'title' => array(
+				'notempty' => array(
+					'rule' => 'notEmpty',
+					'on' => 'create'
+				)
+			),
+			'published' => array(
+				'notempty' => array(
+					'rule' => 'notEmpty',
+				)
+			)
+		);
+
+		$Article->create($data);
+		$this->assertFalse($Article->validates());
+
+		$Article->save(null, array('validate' => false));
+		$data['Article']['id'] = $Article->id;
+		$Article->set($data);
+		$this->assertTrue($Article->validates());
+
+		$Article->data['Article']['published'] = null;
+		$this->assertFalse($Article->validates());
+
+		unset($data['Article']['id']);
+		$Article->data['Article']['published'] = '1';
+		$Article->validate = array(
+			'title' => array(
+				'notempty' => array(
+					'rule' => 'notEmpty',
+					'on' => 'update'
+				)
+			),
+			'published' => array(
+				'notempty' => array(
+					'rule' => 'notEmpty',
+				)
+			)
+		);
+
+		$Article->create($data);
+		$this->assertTrue($Article->validates());
+
+		$Article->save(null, array('validate' => false));
+		$data['Article']['id'] = $Article->id;
+		$Article->set($data);
+		$this->assertFalse($Article->validates());
+
+	}
+
+}

Added: trunk/src/Web/cake/tests/cases/libs/model/model_write.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/model/model_write.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/model/model_write.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,4048 @@
+<?php
+/* SVN FILE: $Id: model.test.php 8225 2009-07-08 03:25:30Z mark_story $ */
+
+/**
+ * ModelWriteTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+require_once dirname(__FILE__) . DS . 'model.test.php';
+/**
+ * ModelWriteTest
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model.operations
+ */
+class ModelWriteTest extends BaseModelTest {
+
+/**
+ * testInsertAnotherHabtmRecordWithSameForeignKey method
+ *
+ * @access public
+ * @return void
+ */
+	function testInsertAnotherHabtmRecordWithSameForeignKey() {
+		$this->loadFixtures('JoinA', 'JoinB', 'JoinAB');
+		$TestModel = new JoinA();
+
+		$result = $TestModel->JoinAsJoinB->findById(1);
+		$expected = array(
+			'JoinAsJoinB' => array(
+				'id' => 1,
+				'join_a_id' => 1,
+				'join_b_id' => 2,
+				'other' => 'Data for Join A 1 Join B 2',
+				'created' => '2008-01-03 10:56:33',
+				'updated' => '2008-01-03 10:56:33'
+		));
+		$this->assertEqual($result, $expected);
+
+		$TestModel->JoinAsJoinB->create();
+		$result = $TestModel->JoinAsJoinB->save(array(
+			'join_a_id' => 1,
+			'join_b_id' => 1,
+			'other' => 'Data for Join A 1 Join B 1',
+			'created' => '2008-01-03 10:56:44',
+			'updated' => '2008-01-03 10:56:44'
+		));
+		$this->assertTrue($result);
+		$lastInsertId = $TestModel->JoinAsJoinB->getLastInsertID();
+		$this->assertTrue($lastInsertId != null);
+
+		$result = $TestModel->JoinAsJoinB->findById(1);
+		$expected = array(
+			'JoinAsJoinB' => array(
+				'id' => 1,
+				'join_a_id' => 1,
+				'join_b_id' => 2,
+				'other' => 'Data for Join A 1 Join B 2',
+				'created' => '2008-01-03 10:56:33',
+				'updated' => '2008-01-03 10:56:33'
+		));
+		$this->assertEqual($result, $expected);
+
+		$updatedValue = 'UPDATED Data for Join A 1 Join B 2';
+		$TestModel->JoinAsJoinB->id = 1;
+		$result = $TestModel->JoinAsJoinB->saveField('other', $updatedValue, false);
+		$this->assertTrue($result);
+
+		$result = $TestModel->JoinAsJoinB->findById(1);
+		$this->assertEqual($result['JoinAsJoinB']['other'], $updatedValue);
+	}
+
+/**
+ * testSaveDateAsFirstEntry method
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveDateAsFirstEntry() {
+		$this->loadFixtures('Article');
+
+		$Article =& new Article();
+
+		$data = array(
+			'Article' => array(
+				'created' => array(
+					'day' => '1',
+					'month' => '1',
+					'year' => '2008'
+				),
+				'title' => 'Test Title',
+				'user_id' => 1
+		));
+		$Article->create();
+		$this->assertTrue($Article->save($data));
+
+		$testResult = $Article->find(array('Article.title' => 'Test Title'));
+
+		$this->assertEqual($testResult['Article']['title'], $data['Article']['title']);
+		$this->assertEqual($testResult['Article']['created'], '2008-01-01 00:00:00');
+
+	}
+
+/**
+ * testUnderscoreFieldSave method
+ *
+ * @access public
+ * @return void
+ */
+	function testUnderscoreFieldSave() {
+		$this->loadFixtures('UnderscoreField');
+		$UnderscoreField =& new UnderscoreField();
+
+		$currentCount = $UnderscoreField->find('count');
+		$this->assertEqual($currentCount, 3);
+		$data = array('UnderscoreField' => array(
+			'user_id' => '1',
+			'my_model_has_a_field' => 'Content here',
+			'body' => 'Body',
+			'published' => 'Y',
+			'another_field' => 4
+		));
+		$ret = $UnderscoreField->save($data);
+		$this->assertTrue($ret);
+
+		$currentCount = $UnderscoreField->find('count');
+		$this->assertEqual($currentCount, 4);
+	}
+
+/**
+ * testAutoSaveUuid method
+ *
+ * @access public
+ * @return void
+ */
+	function testAutoSaveUuid() {
+		// SQLite does not support non-integer primary keys
+		$this->skipIf($this->db->config['driver'] == 'sqlite');
+
+		$this->loadFixtures('Uuid');
+		$TestModel =& new Uuid();
+
+		$TestModel->save(array('title' => 'Test record'));
+		$result = $TestModel->findByTitle('Test record');
+		$this->assertEqual(
+			array_keys($result['Uuid']),
+			array('id', 'title', 'count', 'created', 'updated')
+		);
+		$this->assertEqual(strlen($result['Uuid']['id']), 36);
+	}
+
+/**
+ * Ensure that if the id key is null but present the save doesn't fail (with an
+ * x sql error: "Column id specified twice")
+ *
+ * @return void
+ * @access public
+ */
+	function testSaveUuidNull() {
+		// SQLite does not support non-integer primary keys
+		$this->skipIf($this->db->config['driver'] == 'sqlite');
+
+		$this->loadFixtures('Uuid');
+		$TestModel =& new Uuid();
+
+		$TestModel->save(array('title' => 'Test record', 'id' => null));
+		$result = $TestModel->findByTitle('Test record');
+		$this->assertEqual(
+			array_keys($result['Uuid']),
+			array('id', 'title', 'count', 'created', 'updated')
+		);
+		$this->assertEqual(strlen($result['Uuid']['id']), 36);
+	}
+
+/**
+ * testZeroDefaultFieldValue method
+ *
+ * @access public
+ * @return void
+ */
+	function testZeroDefaultFieldValue() {
+		$this->skipIf(
+			$this->db->config['driver'] == 'sqlite',
+			'%s SQLite uses loose typing, this operation is unsupported'
+		);
+		$this->loadFixtures('DataTest');
+		$TestModel =& new DataTest();
+
+		$TestModel->create(array());
+		$TestModel->save();
+		$result = $TestModel->findById($TestModel->id);
+		$this->assertIdentical($result['DataTest']['count'], '0');
+		$this->assertIdentical($result['DataTest']['float'], '0');
+	}
+
+/**
+ * testNonNumericHabtmJoinKey method
+ *
+ * @access public
+ * @return void
+ */
+	function testNonNumericHabtmJoinKey() {
+		$this->loadFixtures('Post', 'Tag', 'PostsTag');
+		$Post =& new Post();
+		$Post->bindModel(array(
+			'hasAndBelongsToMany' => array('Tag')
+		));
+		$Post->Tag->primaryKey = 'tag';
+
+		$result = $Post->find('all');
+		$expected = array(
+			array(
+				'Post' => array(
+					'id' => '1',
+					'author_id' => '1',
+					'title' => 'First Post',
+					'body' => 'First Post Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:39:23',
+					'updated' => '2007-03-18 10:41:31'
+				),
+				'Author' => array(
+					'id' => null,
+					'user' => null,
+					'password' => null,
+					'created' => null,
+					'updated' => null,
+					'test' => 'working'
+				),
+				'Tag' => array(
+					array(
+						'id' => '1',
+						'tag' => 'tag1',
+						'created' => '2007-03-18 12:22:23',
+						'updated' => '2007-03-18 12:24:31'
+					),
+					array(
+						'id' => '2',
+						'tag' => 'tag2',
+						'created' => '2007-03-18 12:24:23',
+						'updated' => '2007-03-18 12:26:31'
+			))),
+			array(
+				'Post' => array(
+					'id' => '2',
+					'author_id' => '3',
+					'title' => 'Second Post',
+					'body' => 'Second Post Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:41:23',
+					'updated' => '2007-03-18 10:43:31'
+				),
+				'Author' => array(
+					'id' => null,
+					'user' => null,
+					'password' => null,
+					'created' => null,
+					'updated' => null,
+					'test' => 'working'
+				),
+				'Tag' => array(
+					array(
+						'id' => '1',
+						'tag' => 'tag1',
+						'created' => '2007-03-18 12:22:23',
+						'updated' => '2007-03-18 12:24:31'
+						),
+					array(
+						'id' => '3',
+						'tag' => 'tag3',
+						'created' => '2007-03-18 12:26:23',
+						'updated' => '2007-03-18 12:28:31'
+			))),
+			array(
+				'Post' => array(
+					'id' => '3',
+					'author_id' => '1',
+					'title' => 'Third Post',
+					'body' => 'Third Post Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:43:23',
+					'updated' => '2007-03-18 10:45:31'
+				),
+				'Author' => array(
+					'id' => null,
+					'user' => null,
+					'password' => null,
+					'created' => null,
+					'updated' => null,
+					'test' => 'working'
+				),
+				'Tag' => array()
+		));
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * Tests validation parameter order in custom validation methods
+ *
+ * @access public
+ * @return void
+ */
+	function testAllowSimulatedFields() {
+		$TestModel =& new ValidationTest1();
+
+		$TestModel->create(array(
+			'title' => 'foo',
+			'bar' => 'baz'
+		));
+		$expected = array(
+			'ValidationTest1' => array(
+				'title' => 'foo',
+				'bar' => 'baz'
+		));
+		$this->assertEqual($TestModel->data, $expected);
+	}
+
+/**
+ * test that Caches are getting cleared on save().
+ * ensure that both inflections of controller names are getting cleared
+ * as url for controller could be either overallFavorites/index or overall_favorites/index
+ *
+ * @return void
+ */
+	function testCacheClearOnSave() {
+		$_back = array(
+			'check' => Configure::read('Cache.check'),
+			'disable' => Configure::read('Cache.disable'),
+		);
+		Configure::write('Cache.check', true);
+		Configure::write('Cache.disable', false);
+
+		$this->loadFixtures('OverallFavorite');
+		$OverallFavorite =& new OverallFavorite();
+
+		touch(CACHE . 'views' . DS . 'some_dir_overallfavorites_index.php');
+		touch(CACHE . 'views' . DS . 'some_dir_overall_favorites_index.php');
+
+		$data = array(
+			'OverallFavorite' => array(
+				'id' => 22,
+		 		'model_type' => '8-track',
+				'model_id' => '3',
+				'priority' => '1'
+			)
+		);
+		$OverallFavorite->create($data);
+		$OverallFavorite->save();
+
+		$this->assertFalse(file_exists(CACHE . 'views' . DS . 'some_dir_overallfavorites_index.php'));
+		$this->assertFalse(file_exists(CACHE . 'views' . DS . 'some_dir_overall_favorites_index.php'));
+
+		Configure::write('Cache.check', $_back['check']);
+		Configure::write('Cache.disable', $_back['disable']);
+	}
+
+/**
+ * testSaveWithCounterCache method
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveWithCounterCache() {
+		$this->loadFixtures('Syfile', 'Item');
+		$TestModel =& new Syfile();
+		$TestModel2 =& new Item();
+
+		$result = $TestModel->findById(1);
+		$this->assertIdentical($result['Syfile']['item_count'], null);
+
+		$TestModel2->save(array(
+			'name' => 'Item 7',
+			'syfile_id' => 1,
+			'published' => false
+		));
+
+		$result = $TestModel->findById(1);
+		$this->assertIdentical($result['Syfile']['item_count'], '2');
+
+		$TestModel2->delete(1);
+		$result = $TestModel->findById(1);
+		$this->assertIdentical($result['Syfile']['item_count'], '1');
+
+		$TestModel2->id = 2;
+		$TestModel2->saveField('syfile_id', 1);
+
+		$result = $TestModel->findById(1);
+		$this->assertIdentical($result['Syfile']['item_count'], '2');
+
+		$result = $TestModel->findById(2);
+		$this->assertIdentical($result['Syfile']['item_count'], '0');
+	}
+
+/**
+ * Tests that counter caches are updated when records are added
+ *
+ * @access public
+ * @return void
+ */
+	function testCounterCacheIncrease() {
+		$this->loadFixtures('CounterCacheUser', 'CounterCachePost');
+		$User = new CounterCacheUser();
+		$Post = new CounterCachePost();
+		$data = array('Post' => array(
+			'id' => 22,
+			'title' => 'New Post',
+			'user_id' => 66
+		));
+
+		$Post->save($data);
+		$user = $User->find('first', array(
+			'conditions' => array('id' => 66),
+			'recursive' => -1
+		));
+
+		$result = $user[$User->alias]['post_count'];
+		$expected = 3;
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * Tests that counter caches are updated when records are deleted
+ *
+ * @access public
+ * @return void
+ */
+	function testCounterCacheDecrease() {
+		$this->loadFixtures('CounterCacheUser', 'CounterCachePost');
+		$User = new CounterCacheUser();
+		$Post = new CounterCachePost();
+
+		$Post->delete(2);
+		$user = $User->find('first', array(
+			'conditions' => array('id' => 66),
+			'recursive' => -1
+		));
+
+		$result = $user[$User->alias]['post_count'];
+		$expected = 1;
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * Tests that counter caches are updated when foreign keys of counted records change
+ *
+ * @access public
+ * @return void
+ */
+	function testCounterCacheUpdated() {
+		$this->loadFixtures('CounterCacheUser', 'CounterCachePost');
+		$User = new CounterCacheUser();
+		$Post = new CounterCachePost();
+
+		$data = $Post->find('first', array(
+			'conditions' => array('id' => 1),
+			'recursive' => -1
+		));
+		$data[$Post->alias]['user_id'] = 301;
+		$Post->save($data);
+
+		$users = $User->find('all',array('order' => 'User.id'));
+		$this->assertEqual($users[0]['User']['post_count'], 1);
+		$this->assertEqual($users[1]['User']['post_count'], 2);
+	}
+
+/**
+ * Test counter cache with models that use a non-standard (i.e. not using 'id')
+ * as their primary key.
+ *
+ * @access public
+ * @return void
+ */
+	function testCounterCacheWithNonstandardPrimaryKey() {
+		$this->loadFixtures(
+			'CounterCacheUserNonstandardPrimaryKey',
+			'CounterCachePostNonstandardPrimaryKey'
+		);
+
+		$User = new CounterCacheUserNonstandardPrimaryKey();
+		$Post = new CounterCachePostNonstandardPrimaryKey();
+
+		$data = $Post->find('first', array(
+			'conditions' => array('pid' => 1),
+			'recursive' => -1
+		));
+		$data[$Post->alias]['uid'] = 301;
+		$Post->save($data);
+
+		$users = $User->find('all',array('order' => 'User.uid'));
+		$this->assertEqual($users[0]['User']['post_count'], 1);
+		$this->assertEqual($users[1]['User']['post_count'], 2);
+	}
+
+/**
+ * test Counter Cache With Self Joining table
+ *
+ * @return void
+ * @access public
+ */
+	function testCounterCacheWithSelfJoin() {
+		$skip = $this->skipIf(
+			($this->db->config['driver'] == 'sqlite'),
+			'SQLite 2.x does not support ALTER TABLE ADD COLUMN'
+		);
+		if ($skip) {
+			return;
+		}
+
+		$this->loadFixtures('CategoryThread');
+		$this->db->query('ALTER TABLE '. $this->db->fullTableName('category_threads') . " ADD COLUMN child_count INTEGER");
+		$Category =& new CategoryThread();
+		$result = $Category->updateAll(array('CategoryThread.name' => "'updated'"), array('CategoryThread.parent_id' => 5));
+		$this->assertTrue($result);
+
+		$Category =& new CategoryThread();
+		$Category->belongsTo['ParentCategory']['counterCache'] = 'child_count';
+		$Category->updateCounterCache(array('parent_id' => 5));
+		$result = Set::extract($Category->find('all', array('conditions' => array('CategoryThread.id' => 5))), '{n}.CategoryThread.child_count');
+		$expected = array_fill(0, 1, 1);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testSaveWithCounterCacheScope method
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveWithCounterCacheScope() {
+		$this->loadFixtures('Syfile', 'Item');
+		$TestModel =& new Syfile();
+		$TestModel2 =& new Item();
+		$TestModel2->belongsTo['Syfile']['counterCache'] = true;
+		$TestModel2->belongsTo['Syfile']['counterScope'] = array('published' => true);
+
+		$result = $TestModel->findById(1);
+		$this->assertIdentical($result['Syfile']['item_count'], null);
+
+		$TestModel2->save(array(
+			'name' => 'Item 7',
+			'syfile_id' => 1,
+			'published'=> true
+		));
+
+		$result = $TestModel->findById(1);
+		$this->assertIdentical($result['Syfile']['item_count'], '1');
+
+		$TestModel2->id = 1;
+		$TestModel2->saveField('published', true);
+		$result = $TestModel->findById(1);
+		$this->assertIdentical($result['Syfile']['item_count'], '2');
+
+		$TestModel2->save(array(
+			'id' => 1,
+			'syfile_id' => 1,
+			'published'=> false
+		));
+
+		$result = $TestModel->findById(1);
+		$this->assertIdentical($result['Syfile']['item_count'], '1');
+	}
+
+/**
+ * test that beforeValidate returning false can abort saves.
+ *
+ * @return void
+ */
+	function testBeforeValidateSaveAbortion() {
+		$Model =& new CallbackPostTestModel();
+		$Model->beforeValidateReturn = false;
+
+		$data = array(
+			'title' => 'new article',
+			'body' => 'this is some text.'
+		);
+		$Model->create();
+		$result = $Model->save($data);
+		$this->assertFalse($result);
+	}
+/**
+ * test that beforeSave returning false can abort saves.
+ *
+ * @return void
+ */
+	function testBeforeSaveSaveAbortion() {
+		$Model =& new CallbackPostTestModel();
+		$Model->beforeSaveReturn = false;
+
+		$data = array(
+			'title' => 'new article',
+			'body' => 'this is some text.'
+		);
+		$Model->create();
+		$result = $Model->save($data);
+		$this->assertFalse($result);
+	}
+
+/**
+ * testSaveField method
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveField() {
+		$this->loadFixtures('Article');
+		$TestModel =& new Article();
+
+		$TestModel->id = 1;
+		$result = $TestModel->saveField('title', 'New First Article');
+		$this->assertTrue($result);
+
+		$TestModel->recursive = -1;
+		$result = $TestModel->read(array('id', 'user_id', 'title', 'body'), 1);
+		$expected = array('Article' => array(
+			'id' => '1',
+			'user_id' => '1',
+			'title' => 'New First Article',
+			'body' => 'First Article Body'
+		));
+		$this->assertEqual($result, $expected);
+
+		$TestModel->id = 1;
+		$result = $TestModel->saveField('title', '');
+		$this->assertTrue($result);
+
+		$TestModel->recursive = -1;
+		$result = $TestModel->read(array('id', 'user_id', 'title', 'body'), 1);
+		$expected = array('Article' => array(
+			'id' => '1',
+			'user_id' => '1',
+			'title' => '',
+			'body' => 'First Article Body'
+		));
+		$result['Article']['title'] = trim($result['Article']['title']);
+		$this->assertEqual($result, $expected);
+
+		$TestModel->id = 1;
+		$TestModel->set('body', 'Messed up data');
+		$this->assertTrue($TestModel->saveField('title', 'First Article'));
+		$result = $TestModel->read(array('id', 'user_id', 'title', 'body'), 1);
+		$expected = array('Article' => array(
+			'id' => '1',
+			'user_id' => '1',
+			'title' => 'First Article',
+			'body' => 'First Article Body'
+		));
+		$this->assertEqual($result, $expected);
+
+		$TestModel->recursive = -1;
+		$result = $TestModel->read(array('id', 'user_id', 'title', 'body'), 1);
+
+		$TestModel->id = 1;
+		$result = $TestModel->saveField('title', '', true);
+		$this->assertFalse($result);
+
+		$this->loadFixtures('Node', 'Dependency');
+		$Node =& new Node();
+		$Node->set('id', 1);
+		$result = $Node->read();
+		$this->assertEqual(Set::extract('/ParentNode/name', $result), array('Second'));
+
+		$Node->saveField('state', 10);
+		$result = $Node->read();
+		$this->assertEqual(Set::extract('/ParentNode/name', $result), array('Second'));
+	}
+
+/**
+ * testSaveWithCreate method
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveWithCreate() {
+		$this->loadFixtures(
+			'User',
+			'Article',
+			'User',
+			'Comment',
+			'Tag',
+			'ArticlesTag',
+			'Attachment'
+		);
+		$TestModel =& new User();
+
+		$data = array('User' => array(
+			'user' => 'user',
+			'password' => ''
+		));
+		$result = $TestModel->save($data);
+		$this->assertFalse($result);
+		$this->assertTrue(!empty($TestModel->validationErrors));
+
+		$TestModel =& new Article();
+
+		$data = array('Article' => array(
+			'user_id' => '',
+			'title' => '',
+			'body' => ''
+		));
+		$result = $TestModel->create($data) && $TestModel->save();
+		$this->assertFalse($result);
+		$this->assertTrue(!empty($TestModel->validationErrors));
+
+		$data = array('Article' => array(
+			'id' => 1,
+			'user_id' => '1',
+			'title' => 'New First Article',
+			'body' => ''
+		));
+		$result = $TestModel->create($data) && $TestModel->save();
+		$this->assertFalse($result);
+
+		$data = array('Article' => array(
+			'id' => 1,
+			'title' => 'New First Article'
+		));
+		$result = $TestModel->create() && $TestModel->save($data, false);
+		$this->assertTrue($result);
+
+		$TestModel->recursive = -1;
+		$result = $TestModel->read(array('id', 'user_id', 'title', 'body', 'published'), 1);
+		$expected = array('Article' => array(
+			'id' => '1',
+			'user_id' => '1',
+			'title' => 'New First Article',
+			'body' => 'First Article Body',
+			'published' => 'N'
+		));
+		$this->assertEqual($result, $expected);
+
+		$data = array('Article' => array(
+			'id' => 1,
+			'user_id' => '2',
+			'title' => 'First Article',
+			'body' => 'New First Article Body',
+			'published' => 'Y'
+		));
+		$result = $TestModel->create() && $TestModel->save($data, true, array('id', 'title', 'published'));
+		$this->assertTrue($result);
+
+		$TestModel->recursive = -1;
+		$result = $TestModel->read(array('id', 'user_id', 'title', 'body', 'published'), 1);
+		$expected = array('Article' => array(
+			'id' => '1',
+			'user_id' => '1',
+			'title' => 'First Article',
+			'body' => 'First Article Body',
+			'published' => 'Y'
+		));
+		$this->assertEqual($result, $expected);
+
+		$data = array(
+			'Article' => array(
+				'user_id' => '2',
+				'title' => 'New Article',
+				'body' => 'New Article Body',
+				'created' => '2007-03-18 14:55:23',
+				'updated' => '2007-03-18 14:57:31'
+			),
+			'Tag' => array('Tag' => array(1, 3))
+		);
+		$TestModel->create();
+		$result = $TestModel->create() && $TestModel->save($data);
+		$this->assertTrue($result);
+
+		$TestModel->recursive = 2;
+		$result = $TestModel->read(null, 4);
+		$expected = array(
+			'Article' => array(
+				'id' => '4',
+				'user_id' => '2',
+				'title' => 'New Article',
+				'body' => 'New Article Body',
+				'published' => 'N',
+				'created' => '2007-03-18 14:55:23',
+				'updated' => '2007-03-18 14:57:31'
+			),
+			'User' => array(
+				'id' => '2',
+				'user' => 'nate',
+				'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+				'created' => '2007-03-17 01:18:23',
+				'updated' => '2007-03-17 01:20:31'
+			),
+			'Comment' => array(),
+			'Tag' => array(
+				array(
+					'id' => '1',
+					'tag' => 'tag1',
+					'created' => '2007-03-18 12:22:23',
+					'updated' => '2007-03-18 12:24:31'
+				),
+				array(
+					'id' => '3',
+					'tag' => 'tag3',
+					'created' => '2007-03-18 12:26:23',
+					'updated' => '2007-03-18 12:28:31'
+		)));
+		$this->assertEqual($result, $expected);
+
+		$data = array('Comment' => array(
+			'article_id' => '4',
+			'user_id' => '1',
+			'comment' => 'Comment New Article',
+			'published' => 'Y',
+			'created' => '2007-03-18 14:57:23',
+			'updated' => '2007-03-18 14:59:31'
+		));
+		$result = $TestModel->Comment->create() && $TestModel->Comment->save($data);
+		$this->assertTrue($result);
+
+		$data = array('Attachment' => array(
+			'comment_id' => '7',
+			'attachment' => 'newattachment.zip',
+			'created' => '2007-03-18 15:02:23',
+			'updated' => '2007-03-18 15:04:31'
+		));
+		$result = $TestModel->Comment->Attachment->save($data);
+		$this->assertTrue($result);
+
+		$TestModel->recursive = 2;
+		$result = $TestModel->read(null, 4);
+		$expected = array(
+			'Article' => array(
+				'id' => '4',
+				'user_id' => '2',
+				'title' => 'New Article',
+				'body' => 'New Article Body',
+				'published' => 'N',
+				'created' => '2007-03-18 14:55:23',
+				'updated' => '2007-03-18 14:57:31'
+			),
+			'User' => array(
+				'id' => '2',
+				'user' => 'nate',
+				'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+				'created' => '2007-03-17 01:18:23',
+				'updated' => '2007-03-17 01:20:31'
+			),
+			'Comment' => array(
+				array(
+					'id' => '7',
+					'article_id' => '4',
+					'user_id' => '1',
+					'comment' => 'Comment New Article',
+					'published' => 'Y',
+					'created' => '2007-03-18 14:57:23',
+					'updated' => '2007-03-18 14:59:31',
+					'Article' => array(
+						'id' => '4',
+						'user_id' => '2',
+						'title' => 'New Article',
+						'body' => 'New Article Body',
+						'published' => 'N',
+						'created' => '2007-03-18 14:55:23',
+						'updated' => '2007-03-18 14:57:31'
+					),
+					'User' => array(
+						'id' => '1',
+						'user' => 'mariano',
+						'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+						'created' => '2007-03-17 01:16:23',
+						'updated' => '2007-03-17 01:18:31'
+					),
+					'Attachment' => array(
+						'id' => '2',
+						'comment_id' => '7',
+						'attachment' => 'newattachment.zip',
+						'created' => '2007-03-18 15:02:23',
+						'updated' => '2007-03-18 15:04:31'
+			))),
+			'Tag' => array(
+				array(
+					'id' => '1',
+					'tag' => 'tag1',
+					'created' => '2007-03-18 12:22:23',
+					'updated' => '2007-03-18 12:24:31'
+				),
+				array(
+					'id' => '3',
+					'tag' => 'tag3',
+					'created' => '2007-03-18 12:26:23',
+					'updated' => '2007-03-18 12:28:31'
+		)));
+
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that a null Id doesn't cause errors
+ *
+ * @return void
+ */
+	function testSaveWithNullId() {
+		$this->loadFixtures('User');
+		$User =& new User();
+		$User->read(null, 1);
+		$User->data['User']['id'] = null;
+		$this->assertTrue($User->save(array('password' => 'test')));
+		$this->assertTrue($User->id > 0);
+
+		$result = $User->read(null, 2);
+		$User->data['User']['id'] = null;
+		$this->assertTrue($User->save(array('password' => 'test')));
+		$this->assertTrue($User->id > 0);
+
+		$User->data['User'] = array('password' => 'something');
+		$this->assertTrue($User->save());
+		$result = $User->read();
+		$this->assertEqual($User->data['User']['password'], 'something');
+	}
+
+/**
+ * testSaveWithSet method
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveWithSet() {
+		$this->loadFixtures('Article');
+		$TestModel =& new Article();
+
+		// Create record we will be updating later
+
+		$data = array('Article' => array(
+			'user_id' => '1',
+			'title' => 'Fourth Article',
+			'body' => 'Fourth Article Body',
+			'published' => 'Y'
+		));
+		$result = $TestModel->create() && $TestModel->save($data);
+		$this->assertTrue($result);
+
+		// Check record we created
+
+		$TestModel->recursive = -1;
+		$result = $TestModel->read(array('id', 'user_id', 'title', 'body', 'published'), 4);
+		$expected = array('Article' => array(
+			'id' => '4',
+			'user_id' => '1',
+			'title' => 'Fourth Article',
+			'body' => 'Fourth Article Body',
+			'published' => 'Y'
+		));
+		$this->assertEqual($result, $expected);
+
+		// Create new record just to overlap Model->id on previously created record
+
+		$data = array('Article' => array(
+			'user_id' => '4',
+			'title' => 'Fifth Article',
+			'body' => 'Fifth Article Body',
+			'published' => 'Y'
+		));
+		$result = $TestModel->create() && $TestModel->save($data);
+		$this->assertTrue($result);
+
+		$TestModel->recursive = -1;
+		$result = $TestModel->read(array('id', 'user_id', 'title', 'body', 'published'), 5);
+		$expected = array('Article' => array(
+			'id' => '5',
+			'user_id' => '4',
+			'title' => 'Fifth Article',
+			'body' => 'Fifth Article Body',
+			'published' => 'Y'
+		));
+		$this->assertEqual($result, $expected);
+
+		// Go back and edit the first article we created, starting by checking it's still there
+
+		$TestModel->recursive = -1;
+		$result = $TestModel->read(array('id', 'user_id', 'title', 'body', 'published'), 4);
+		$expected = array('Article' => array(
+			'id' => '4',
+			'user_id' => '1',
+			'title' => 'Fourth Article',
+			'body' => 'Fourth Article Body',
+			'published' => 'Y'
+		));
+		$this->assertEqual($result, $expected);
+
+		// And now do the update with set()
+
+		$data = array('Article' => array(
+			'id' => '4',
+			'title' => 'Fourth Article - New Title',
+			'published' => 'N'
+		));
+		$result = $TestModel->set($data) && $TestModel->save();
+		$this->assertTrue($result);
+
+		$TestModel->recursive = -1;
+		$result = $TestModel->read(array('id', 'user_id', 'title', 'body', 'published'), 4);
+		$expected = array('Article' => array(
+			'id' => '4',
+			'user_id' => '1',
+			'title' => 'Fourth Article - New Title',
+			'body' => 'Fourth Article Body',
+			'published' => 'N'
+		));
+		$this->assertEqual($result, $expected);
+
+		$TestModel->recursive = -1;
+		$result = $TestModel->read(array('id', 'user_id', 'title', 'body', 'published'), 5);
+		$expected = array('Article' => array(
+			'id' => '5',
+			'user_id' => '4',
+			'title' => 'Fifth Article',
+			'body' => 'Fifth Article Body',
+			'published' => 'Y'
+		));
+		$this->assertEqual($result, $expected);
+
+		$data = array('Article' => array('id' => '5', 'title' => 'Fifth Article - New Title 5'));
+		$result = ($TestModel->set($data) && $TestModel->save());
+		$this->assertTrue($result);
+
+		$TestModel->recursive = -1;
+		$result = $TestModel->read(array('id', 'user_id', 'title', 'body', 'published'), 5);
+		$expected = array('Article' => array(
+			'id' => '5',
+			'user_id' => '4',
+			'title' => 'Fifth Article - New Title 5',
+			'body' => 'Fifth Article Body',
+			'published' => 'Y'
+		));
+		$this->assertEqual($result, $expected);
+
+		$TestModel->recursive = -1;
+		$result = $TestModel->find('all', array('fields' => array('id', 'title')));
+		$expected = array(
+			array('Article' => array('id' => 1, 'title' => 'First Article' )),
+			array('Article' => array('id' => 2, 'title' => 'Second Article' )),
+			array('Article' => array('id' => 3, 'title' => 'Third Article' )),
+			array('Article' => array('id' => 4, 'title' => 'Fourth Article - New Title' )),
+			array('Article' => array('id' => 5, 'title' => 'Fifth Article - New Title 5' ))
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testSaveWithNonExistentFields method
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveWithNonExistentFields() {
+		$this->loadFixtures('Article');
+		$TestModel =& new Article();
+		$TestModel->recursive = -1;
+
+		$data = array(
+			'non_existent' => 'This field does not exist',
+			'user_id' => '1',
+			'title' => 'Fourth Article - New Title',
+			'body' => 'Fourth Article Body',
+			'published' => 'N'
+		);
+		$result = $TestModel->create() && $TestModel->save($data);
+		$this->assertTrue($result);
+
+		$expected = array('Article' => array(
+			'id' => '4',
+			'user_id' => '1',
+			'title' => 'Fourth Article - New Title',
+			'body' => 'Fourth Article Body',
+			'published' => 'N'
+		));
+		$result = $TestModel->read(array('id', 'user_id', 'title', 'body', 'published'), 4);
+		$this->assertEqual($result, $expected);
+
+		$data = array(
+			'user_id' => '1',
+			'non_existent' => 'This field does not exist',
+			'title' => 'Fiveth Article - New Title',
+			'body' => 'Fiveth Article Body',
+			'published' => 'N'
+		);
+		$result = $TestModel->create() && $TestModel->save($data);
+		$this->assertTrue($result);
+
+		$expected = array('Article' => array(
+			'id' => '5',
+			'user_id' => '1',
+			'title' => 'Fiveth Article - New Title',
+			'body' => 'Fiveth Article Body',
+			'published' => 'N'
+		));
+		$result = $TestModel->read(array('id', 'user_id', 'title', 'body', 'published'), 5);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testSaveFromXml method
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveFromXml() {
+		$this->loadFixtures('Article');
+		App::import('Core', 'Xml');
+
+		$Article = new Article();
+		$Article->save(new Xml('<article title="test xml" user_id="5" />'));
+		$this->assertTrue($Article->save(new Xml('<article title="test xml" user_id="5" />')));
+
+		$results = $Article->find(array('Article.title' => 'test xml'));
+		$this->assertTrue($results);
+	}
+
+/**
+ * testSaveHabtm method
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveHabtm() {
+		$this->loadFixtures('Article', 'User', 'Comment', 'Tag', 'ArticlesTag');
+		$TestModel =& new Article();
+
+		$result = $TestModel->findById(2);
+		$expected = array(
+			'Article' => array(
+				'id' => '2',
+				'user_id' => '3',
+				'title' => 'Second Article',
+				'body' => 'Second Article Body',
+				'published' => 'Y',
+				'created' => '2007-03-18 10:41:23',
+				'updated' => '2007-03-18 10:43:31'
+			),
+			'User' => array(
+				'id' => '3',
+				'user' => 'larry',
+				'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+				'created' => '2007-03-17 01:20:23',
+				'updated' => '2007-03-17 01:22:31'
+			),
+			'Comment' => array(
+				array(
+					'id' => '5',
+					'article_id' => '2',
+					'user_id' => '1',
+					'comment' => 'First Comment for Second Article',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:53:23',
+					'updated' => '2007-03-18 10:55:31'
+				),
+				array(
+					'id' => '6',
+					'article_id' => '2',
+					'user_id' => '2',
+					'comment' => 'Second Comment for Second Article',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:55:23',
+					'updated' => '2007-03-18 10:57:31'
+			)),
+			'Tag' => array(
+				array(
+					'id' => '1',
+					'tag' => 'tag1',
+					'created' => '2007-03-18 12:22:23',
+					'updated' => '2007-03-18 12:24:31'
+				),
+				array(
+					'id' => '3',
+					'tag' => 'tag3',
+					'created' => '2007-03-18 12:26:23',
+					'updated' => '2007-03-18 12:28:31'
+				)
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$data = array(
+			'Article' => array(
+				'id' => '2',
+				'title' => 'New Second Article'
+			),
+			'Tag' => array('Tag' => array(1, 2))
+		);
+
+		$this->assertTrue($TestModel->set($data));
+		$this->assertTrue($TestModel->save());
+
+		$TestModel->unbindModel(array('belongsTo' => array('User'), 'hasMany' => array('Comment')));
+		$result = $TestModel->find(array('Article.id' => 2), array('id', 'user_id', 'title', 'body'));
+		$expected = array(
+			'Article' => array(
+				'id' => '2',
+				'user_id' => '3',
+				'title' => 'New Second Article',
+				'body' => 'Second Article Body'
+			),
+			'Tag' => array(
+				array(
+					'id' => '1',
+					'tag' => 'tag1',
+					'created' => '2007-03-18 12:22:23',
+					'updated' => '2007-03-18 12:24:31'
+				),
+				array(
+					'id' => '2',
+					'tag' => 'tag2',
+					'created' => '2007-03-18 12:24:23',
+					'updated' => '2007-03-18 12:26:31'
+		)));
+		$this->assertEqual($result, $expected);
+
+		$data = array('Article' => array('id' => '2'), 'Tag' => array('Tag' => array(2, 3)));
+		$result = $TestModel->set($data);
+		$this->assertTrue($result);
+
+		$result = $TestModel->save();
+		$this->assertTrue($result);
+
+		$TestModel->unbindModel(array(
+			'belongsTo' => array('User'),
+			'hasMany' => array('Comment')
+		));
+		$result = $TestModel->find(array('Article.id'=>2), array('id', 'user_id', 'title', 'body'));
+		$expected = array(
+			'Article' => array(
+				'id' => '2',
+				'user_id' => '3',
+				'title' => 'New Second Article',
+				'body' => 'Second Article Body'
+			),
+			'Tag' => array(
+				array(
+					'id' => '2',
+					'tag' => 'tag2',
+					'created' => '2007-03-18 12:24:23',
+					'updated' => '2007-03-18 12:26:31'
+				),
+				array(
+					'id' => '3',
+					'tag' => 'tag3',
+					'created' => '2007-03-18 12:26:23',
+					'updated' => '2007-03-18 12:28:31'
+		)));
+		$this->assertEqual($result, $expected);
+
+		$data = array('Tag' => array('Tag' => array(1, 2, 3)));
+
+		$result = $TestModel->set($data);
+		$this->assertTrue($result);
+
+		$result = $TestModel->save();
+		$this->assertTrue($result);
+
+		$TestModel->unbindModel(array(
+			'belongsTo' => array('User'),
+			'hasMany' => array('Comment')
+		));
+		$result = $TestModel->find(array('Article.id' => 2), array('id', 'user_id', 'title', 'body'));
+		$expected = array(
+			'Article' => array(
+				'id' => '2',
+				'user_id' => '3',
+				'title' => 'New Second Article',
+				'body' => 'Second Article Body'
+			),
+			'Tag' => array(
+				array(
+					'id' => '1',
+					'tag' => 'tag1',
+					'created' => '2007-03-18 12:22:23',
+					'updated' => '2007-03-18 12:24:31'
+				),
+				array(
+					'id' => '2',
+					'tag' => 'tag2',
+					'created' => '2007-03-18 12:24:23',
+					'updated' => '2007-03-18 12:26:31'
+				),
+				array(
+					'id' => '3',
+					'tag' => 'tag3',
+					'created' => '2007-03-18 12:26:23',
+					'updated' => '2007-03-18 12:28:31'
+		)));
+		$this->assertEqual($result, $expected);
+
+		$data = array('Tag' => array('Tag' => array()));
+		$result = $TestModel->set($data);
+		$this->assertTrue($result);
+
+		$result = $TestModel->save();
+		$this->assertTrue($result);
+
+		$data = array('Tag' => array('Tag' => ''));
+		$result = $TestModel->set($data);
+		$this->assertTrue($result);
+
+		$result = $TestModel->save();
+		$this->assertTrue($result);
+
+		$TestModel->unbindModel(array(
+			'belongsTo' => array('User'),
+			'hasMany' => array('Comment')
+		));
+		$result = $TestModel->find(array('Article.id'=>2), array('id', 'user_id', 'title', 'body'));
+		$expected = array(
+			'Article' => array(
+				'id' => '2',
+				'user_id' => '3',
+				'title' => 'New Second Article',
+				'body' => 'Second Article Body'
+			),
+			'Tag' => array()
+		);
+		$this->assertEqual($result, $expected);
+
+		$data = array('Tag' => array('Tag' => array(2, 3)));
+		$result = $TestModel->set($data);
+		$this->assertTrue($result);
+
+		$result = $TestModel->save();
+		$this->assertTrue($result);
+
+		$TestModel->unbindModel(array(
+			'belongsTo' => array('User'),
+			'hasMany' => array('Comment')
+		));
+		$result = $TestModel->find(array('Article.id'=>2), array('id', 'user_id', 'title', 'body'));
+		$expected = array(
+			'Article' => array(
+				'id' => '2',
+				'user_id' => '3',
+				'title' => 'New Second Article',
+				'body' => 'Second Article Body'
+			),
+			'Tag' => array(
+				array(
+					'id' => '2',
+					'tag' => 'tag2',
+					'created' => '2007-03-18 12:24:23',
+					'updated' => '2007-03-18 12:26:31'
+				),
+				array(
+					'id' => '3',
+					'tag' => 'tag3',
+					'created' => '2007-03-18 12:26:23',
+					'updated' => '2007-03-18 12:28:31'
+		)));
+		$this->assertEqual($result, $expected);
+
+		$data = array(
+			'Tag' => array(
+				'Tag' => array(1, 2)
+			),
+			'Article' => array(
+				'id' => '2',
+				'title' => 'New Second Article'
+		));
+		$this->assertTrue($TestModel->set($data));
+		$this->assertTrue($TestModel->save());
+
+		$TestModel->unbindModel(array(
+			'belongsTo' => array('User'),
+			'hasMany' => array('Comment')
+		));
+		$result = $TestModel->find(array('Article.id'=>2), array('id', 'user_id', 'title', 'body'));
+		$expected = array(
+			'Article' => array(
+				'id' => '2',
+				'user_id' => '3',
+				'title' => 'New Second Article',
+				'body' => 'Second Article Body'
+			),
+			'Tag' => array(
+				array(
+					'id' => '1',
+					'tag' => 'tag1',
+					'created' => '2007-03-18 12:22:23',
+					'updated' => '2007-03-18 12:24:31'
+				),
+				array(
+					'id' => '2',
+					'tag' => 'tag2',
+					'created' => '2007-03-18 12:24:23',
+					'updated' => '2007-03-18 12:26:31'
+		)));
+		$this->assertEqual($result, $expected);
+
+		$data = array(
+			'Tag' => array(
+				'Tag' => array(1, 2)
+			),
+			'Article' => array(
+				'id' => '2',
+				'title' => 'New Second Article Title'
+		));
+		$result = $TestModel->set($data);
+		$this->assertTrue($result);
+		$this->assertTrue($TestModel->save());
+
+		$TestModel->unbindModel(array(
+			'belongsTo' => array('User'),
+			'hasMany' => array('Comment')
+		));
+		$result = $TestModel->find(array('Article.id'=>2), array('id', 'user_id', 'title', 'body'));
+		$expected = array(
+			'Article' => array(
+				'id' => '2',
+				'user_id' => '3',
+				'title' => 'New Second Article Title',
+				'body' => 'Second Article Body'
+			),
+			'Tag' => array(
+				array(
+					'id' => '1',
+					'tag' => 'tag1',
+					'created' => '2007-03-18 12:22:23',
+					'updated' => '2007-03-18 12:24:31'
+				),
+				array(
+					'id' => '2',
+					'tag' => 'tag2',
+					'created' => '2007-03-18 12:24:23',
+					'updated' => '2007-03-18 12:26:31'
+				)
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$data = array(
+			'Tag' => array(
+				'Tag' => array(2, 3)
+			),
+			'Article' => array(
+				'id' => '2',
+				'title' => 'Changed Second Article'
+		));
+		$this->assertTrue($TestModel->set($data));
+		$this->assertTrue($TestModel->save());
+
+		$TestModel->unbindModel(array(
+			'belongsTo' => array('User'),
+			'hasMany' => array('Comment')
+		));
+		$result = $TestModel->find(array('Article.id'=>2), array('id', 'user_id', 'title', 'body'));
+		$expected = array(
+			'Article' => array(
+				'id' => '2',
+				'user_id' => '3',
+				'title' => 'Changed Second Article',
+				'body' => 'Second Article Body'
+			),
+			'Tag' => array(
+				array(
+					'id' => '2',
+					'tag' => 'tag2',
+					'created' => '2007-03-18 12:24:23',
+					'updated' => '2007-03-18 12:26:31'
+				),
+				array(
+					'id' => '3',
+					'tag' => 'tag3',
+					'created' => '2007-03-18 12:26:23',
+					'updated' => '2007-03-18 12:28:31'
+				)
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$data = array(
+			'Tag' => array(
+				'Tag' => array(1, 3)
+			),
+			'Article' => array('id' => '2'),
+		);
+
+		$result = $TestModel->set($data);
+		$this->assertTrue($result);
+
+		$result = $TestModel->save();
+		$this->assertTrue($result);
+
+		$TestModel->unbindModel(array(
+			'belongsTo' => array('User'),
+			'hasMany' => array('Comment')
+		));
+		$result = $TestModel->find(array('Article.id'=>2), array('id', 'user_id', 'title', 'body'));
+		$expected = array(
+			'Article' => array(
+				'id' => '2',
+				'user_id' => '3',
+				'title' => 'Changed Second Article',
+				'body' => 'Second Article Body'
+			),
+			'Tag' => array(
+				array(
+					'id' => '1',
+					'tag' => 'tag1',
+					'created' => '2007-03-18 12:22:23',
+					'updated' => '2007-03-18 12:24:31'
+				),
+				array(
+					'id' => '3',
+					'tag' => 'tag3',
+					'created' => '2007-03-18 12:26:23',
+					'updated' => '2007-03-18 12:28:31'
+		)));
+		$this->assertEqual($result, $expected);
+
+		$data = array(
+			'Article' => array(
+				'id' => 10,
+				'user_id' => '2',
+				'title' => 'New Article With Tags and fieldList',
+				'body' => 'New Article Body with Tags and fieldList',
+				'created' => '2007-03-18 14:55:23',
+				'updated' => '2007-03-18 14:57:31'
+			),
+			'Tag' => array(
+				'Tag' => array(1, 2, 3)
+		));
+		$result =  $TestModel->create()
+				&& $TestModel->save($data, true, array('user_id', 'title', 'published'));
+		$this->assertTrue($result);
+
+		$TestModel->unbindModel(array('belongsTo' => array('User'), 'hasMany' => array('Comment')));
+		$result = $TestModel->read();
+		$expected = array(
+			'Article' => array(
+				'id' => 4,
+				'user_id' => 2,
+				'title' => 'New Article With Tags and fieldList',
+				'body' => '',
+				'published' => 'N',
+				'created' => '',
+				'updated' => ''
+			),
+			'Tag' => array(
+				0 => array(
+					'id' => 1,
+					'tag' => 'tag1',
+					'created' => '2007-03-18 12:22:23',
+					'updated' => '2007-03-18 12:24:31'
+				),
+				1 => array(
+					'id' => 2,
+					'tag' => 'tag2',
+					'created' => '2007-03-18 12:24:23',
+					'updated' => '2007-03-18 12:26:31'
+				),
+				2 => array(
+					'id' => 3,
+					'tag' => 'tag3',
+					'created' => '2007-03-18 12:26:23',
+					'updated' => '2007-03-18 12:28:31'
+		)));
+		$this->assertEqual($result, $expected);
+
+
+		$this->loadFixtures('JoinA', 'JoinC', 'JoinAC', 'JoinB', 'JoinAB');
+		$TestModel = new JoinA();
+		$TestModel->hasBelongsToMany['JoinC']['unique'] = true;
+		$data = array(
+			'JoinA' => array(
+				'id' => 1,
+				'name' => 'Join A 1',
+				'body' => 'Join A 1 Body',
+			),
+			'JoinC' => array(
+				'JoinC' => array(
+					array('join_c_id' => 2, 'other' => 'new record'),
+					array('join_c_id' => 3, 'other' => 'new record')
+				)
+			)
+		);
+		$TestModel->save($data);
+		$result = $TestModel->read(null, 1);
+		$expected = array(4, 5);
+		$this->assertEqual(Set::extract('/JoinC/JoinAsJoinC/id', $result), $expected);
+		$expected = array('new record', 'new record');
+		$this->assertEqual(Set::extract('/JoinC/JoinAsJoinC/other', $result), $expected);
+	}
+
+/**
+ * testSaveHabtmCustomKeys method
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveHabtmCustomKeys() {
+		$this->loadFixtures('Story', 'StoriesTag', 'Tag');
+		$Story =& new Story();
+
+		$data = array(
+			'Story' => array('story' => '1'),
+			'Tag' => array(
+				'Tag' => array(2, 3)
+		));
+		$result = $Story->set($data);
+		$this->assertTrue($result);
+
+		$result = $Story->save();
+		$this->assertTrue($result);
+
+		$result = $Story->find('all', array('order' => array('Story.story')));
+		$expected = array(
+			array(
+				'Story' => array(
+					'story' => 1,
+					'title' => 'First Story'
+				),
+				'Tag' => array(
+					array(
+						'id' => 2,
+						'tag' => 'tag2',
+						'created' => '2007-03-18 12:24:23',
+						'updated' => '2007-03-18 12:26:31'
+					),
+					array(
+						'id' => 3,
+						'tag' => 'tag3',
+						'created' => '2007-03-18 12:26:23',
+						'updated' => '2007-03-18 12:28:31'
+			))),
+			array(
+				'Story' => array(
+					'story' => 2,
+					'title' => 'Second Story'
+				),
+				'Tag' => array()
+		));
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that saving habtm records respects conditions set in the 'conditions' key
+ * for the association.
+ *
+ * @return void
+ */
+	function testHabtmSaveWithConditionsInAssociation() {
+		$this->loadFixtures('JoinThing', 'Something', 'SomethingElse');
+		$Something =& new Something();
+		$Something->unbindModel(array('hasAndBelongsToMany' => array('SomethingElse')), false);
+
+		$Something->bindModel(array(
+			'hasAndBelongsToMany' => array(
+				'DoomedSomethingElse' => array(
+					'className' => 'SomethingElse',
+					'joinTable' => 'join_things',
+					'conditions' => 'JoinThing.doomed = true',
+					'unique' => true
+				),
+				'NotDoomedSomethingElse' => array(
+					'className' => 'SomethingElse',
+					'joinTable' => 'join_things',
+					'conditions' => array('JoinThing.doomed' => 0),
+					'unique' => true
+				)
+			)
+		), false);
+		$result = $Something->read(null, 1);
+		$this->assertTrue(empty($result['NotDoomedSomethingElse']));
+		$this->assertEqual(count($result['DoomedSomethingElse']), 1);
+
+		$data = array(
+			'Something' => array('id' => 1),
+			'NotDoomedSomethingElse' => array(
+				'NotDoomedSomethingElse' => array(
+					array('something_else_id' => 2, 'doomed' => 0),
+					array('something_else_id' => 3, 'doomed' => 0)
+				)
+			)
+		);
+		$Something->create($data);
+		$result = $Something->save();
+		$this->assertTrue($result);
+
+		$result = $Something->read(null, 1);
+		$this->assertEqual(count($result['NotDoomedSomethingElse']), 2);
+		$this->assertEqual(count($result['DoomedSomethingElse']), 1);
+	}
+/**
+ * testHabtmSaveKeyResolution method
+ *
+ * @access public
+ * @return void
+ */
+	function testHabtmSaveKeyResolution() {
+		$this->loadFixtures('Apple', 'Device', 'ThePaperMonkies');
+		$ThePaper =& new ThePaper();
+
+		$ThePaper->id = 1;
+		$ThePaper->save(array('Monkey' => array(2, 3)));
+
+		$result = $ThePaper->findById(1);
+		$expected = array(
+			array(
+				'id' => '2',
+				'device_type_id' => '1',
+				'name' => 'Device 2',
+				'typ' => '1'
+			),
+			array(
+				'id' => '3',
+				'device_type_id' => '1',
+				'name' => 'Device 3',
+				'typ' => '2'
+		));
+		$this->assertEqual($result['Monkey'], $expected);
+
+		$ThePaper->id = 2;
+		$ThePaper->save(array('Monkey' => array(1, 2, 3)));
+
+		$result = $ThePaper->findById(2);
+		$expected = array(
+			array(
+				'id' => '1',
+				'device_type_id' => '1',
+				'name' => 'Device 1',
+				'typ' => '1'
+			),
+			array(
+				'id' => '2',
+				'device_type_id' => '1',
+				'name' => 'Device 2',
+				'typ' => '1'
+			),
+			array(
+				'id' => '3',
+				'device_type_id' => '1',
+				'name' => 'Device 3',
+				'typ' => '2'
+		));
+		$this->assertEqual($result['Monkey'], $expected);
+
+		$ThePaper->id = 2;
+		$ThePaper->save(array('Monkey' => array(1, 3)));
+
+		$result = $ThePaper->findById(2);
+		$expected = array(
+			array(
+				'id' => '1',
+				'device_type_id' => '1',
+				'name' => 'Device 1',
+				'typ' => '1'
+			),
+			array(
+				'id' => '3',
+				'device_type_id' => '1',
+				'name' => 'Device 3',
+				'typ' => '2'
+			));
+		$this->assertEqual($result['Monkey'], $expected);
+
+		$result = $ThePaper->findById(1);
+		$expected = array(
+			array(
+				'id' => '2',
+				'device_type_id' => '1',
+				'name' => 'Device 2',
+				'typ' => '1'
+			),
+			array(
+				'id' => '3',
+				'device_type_id' => '1',
+				'name' => 'Device 3',
+				'typ' => '2'
+		));
+		$this->assertEqual($result['Monkey'], $expected);
+	}
+
+/**
+ * testCreationOfEmptyRecord method
+ *
+ * @access public
+ * @return void
+ */
+	function testCreationOfEmptyRecord() {
+		$this->loadFixtures('Author');
+		$TestModel =& new Author();
+		$this->assertEqual($TestModel->find('count'), 4);
+
+		$TestModel->deleteAll(true, false, false);
+		$this->assertEqual($TestModel->find('count'), 0);
+
+		$result = $TestModel->save();
+		$this->assertTrue(isset($result['Author']['created']));
+		$this->assertTrue(isset($result['Author']['updated']));
+		$this->assertEqual($TestModel->find('count'), 1);
+	}
+
+/**
+ * testCreateWithPKFiltering method
+ *
+ * @access public
+ * @return void
+ */
+	function testCreateWithPKFiltering() {
+		$TestModel =& new Article();
+		$data = array(
+			'id' => 5,
+			'user_id' => 2,
+			'title' => 'My article',
+			'body' => 'Some text'
+		);
+
+		$result = $TestModel->create($data);
+		$expected = array(
+			'Article' => array(
+				'published' => 'N',
+				'id' => 5,
+				'user_id' => 2,
+				'title' => 'My article',
+				'body' => 'Some text'
+		));
+
+		$this->assertEqual($result, $expected);
+		$this->assertEqual($TestModel->id, 5);
+
+		$result = $TestModel->create($data, true);
+		$expected = array(
+			'Article' => array(
+				'published' => 'N',
+				'id' => false,
+				'user_id' => 2,
+				'title' => 'My article',
+				'body' => 'Some text'
+		));
+
+		$this->assertEqual($result, $expected);
+		$this->assertFalse($TestModel->id);
+
+		$result = $TestModel->create(array('Article' => $data), true);
+		$expected = array(
+			'Article' => array(
+				'published' => 'N',
+				'id' => false,
+				'user_id' => 2,
+				'title' => 'My article',
+				'body' => 'Some text'
+		));
+
+		$this->assertEqual($result, $expected);
+		$this->assertFalse($TestModel->id);
+
+		$data = array(
+			'id' => 6,
+			'user_id' => 2,
+			'title' => 'My article',
+			'body' => 'Some text',
+			'created' => '1970-01-01 00:00:00',
+			'updated' => '1970-01-01 12:00:00',
+			'modified' => '1970-01-01 12:00:00'
+		);
+
+		$result = $TestModel->create($data);
+		$expected = array(
+			'Article' => array(
+				'published' => 'N',
+				'id' => 6,
+				'user_id' => 2,
+				'title' => 'My article',
+				'body' => 'Some text',
+				'created' => '1970-01-01 00:00:00',
+				'updated' => '1970-01-01 12:00:00',
+				'modified' => '1970-01-01 12:00:00'
+		));
+		$this->assertEqual($result, $expected);
+		$this->assertEqual($TestModel->id, 6);
+
+		$result = $TestModel->create(array(
+			'Article' => array_diff_key($data, array(
+				'created' => true,
+				'updated' => true,
+				'modified' => true
+		))), true);
+		$expected = array(
+			'Article' => array(
+				'published' => 'N',
+				'id' => false,
+				'user_id' => 2,
+				'title' => 'My article',
+				'body' => 'Some text'
+		));
+		$this->assertEqual($result, $expected);
+		$this->assertFalse($TestModel->id);
+	}
+
+/**
+ * testCreationWithMultipleData method
+ *
+ * @access public
+ * @return void
+ */
+	function testCreationWithMultipleData() {
+		$this->loadFixtures('Article', 'Comment');
+		$Article =& new Article();
+		$Comment =& new Comment();
+
+		$articles = $Article->find('all', array(
+			'fields' => array('id','title'),
+			'recursive' => -1
+		));
+
+		$comments = $Comment->find('all', array(
+			'fields' => array('id','article_id','user_id','comment','published'), 'recursive' => -1));
+
+		$this->assertEqual($articles, array(
+			array('Article' => array(
+				'id' => 1,
+				'title' => 'First Article'
+			)),
+			array('Article' => array(
+				'id' => 2,
+				'title' => 'Second Article'
+			)),
+			array('Article' => array(
+				'id' => 3,
+				'title' => 'Third Article'
+		))));
+
+		$this->assertEqual($comments, array(
+			array('Comment' => array(
+				'id' => 1,
+				'article_id' => 1,
+				'user_id' => 2,
+				'comment' => 'First Comment for First Article',
+				'published' => 'Y'
+			)),
+			array('Comment' => array(
+				'id' => 2,
+				'article_id' => 1,
+				'user_id' => 4,
+				'comment' => 'Second Comment for First Article',
+				'published' => 'Y'
+			)),
+			array('Comment' => array(
+				'id' => 3,
+				'article_id' => 1,
+				'user_id' => 1,
+				'comment' => 'Third Comment for First Article',
+				'published' => 'Y'
+			)),
+			array('Comment' => array(
+				'id' => 4,
+				'article_id' => 1,
+				'user_id' => 1,
+				'comment' => 'Fourth Comment for First Article',
+				'published' => 'N'
+			)),
+			array('Comment' => array(
+				'id' => 5,
+				'article_id' => 2,
+				'user_id' => 1,
+				'comment' => 'First Comment for Second Article',
+				'published' => 'Y'
+			)),
+			array('Comment' => array(
+				'id' => 6,
+				'article_id' => 2,
+				'user_id' => 2,
+				'comment' => 'Second Comment for Second Article',
+				'published' => 'Y'
+		))));
+
+		$data = array(
+			'Comment' => array(
+				'article_id' => 2,
+				'user_id' => 4,
+				'comment' => 'Brand New Comment',
+				'published' => 'N'
+			),
+			'Article' => array(
+				'id' => 2,
+				'title' => 'Second Article Modified'
+		));
+
+		$result = $Comment->create($data);
+
+		$this->assertTrue($result);
+		$result = $Comment->save();
+		$this->assertTrue($result);
+
+		$articles = $Article->find('all', array(
+			'fields' => array('id','title'),
+			'recursive' => -1
+		));
+
+		$comments = $Comment->find('all', array(
+			'fields' => array('id','article_id','user_id','comment','published'),
+			'recursive' => -1
+		));
+
+		$this->assertEqual($articles, array(
+			array('Article' => array(
+				'id' => 1,
+				'title' => 'First Article'
+			)),
+			array('Article' => array(
+				'id' => 2,
+				'title' => 'Second Article'
+			)),
+			array('Article' => array(
+				'id' => 3,
+				'title' => 'Third Article'
+		))));
+
+		$this->assertEqual($comments, array(
+			array('Comment' => array(
+				'id' => 1,
+				'article_id' => 1,
+				'user_id' => 2,
+				'comment' => 'First Comment for First Article',
+				'published' => 'Y'
+			)),
+			array('Comment' => array(
+				'id' => 2,
+				'article_id' => 1,
+				'user_id' => 4,
+				'comment' => 'Second Comment for First Article',
+				'published' => 'Y'
+			)),
+			array('Comment' => array(
+				'id' => 3,
+				'article_id' => 1,
+				'user_id' => 1,
+				'comment' => 'Third Comment for First Article',
+				'published' => 'Y'
+			)),
+			array('Comment' => array(
+				'id' => 4,
+				'article_id' => 1,
+				'user_id' => 1,
+				'comment' => 'Fourth Comment for First Article',
+				'published' => 'N'
+			)),
+			array('Comment' => array(
+				'id' => 5,
+				'article_id' => 2,
+				'user_id' => 1,
+				'comment' => 'First Comment for Second Article',
+				'published' => 'Y'
+			)),
+			array('Comment' => array(
+				'id' => 6,
+				'article_id' => 2,
+				'user_id' => 2, 'comment' =>
+				'Second Comment for Second Article',
+				'published' => 'Y'
+			)),
+			array('Comment' => array(
+				'id' => 7,
+				'article_id' => 2,
+				'user_id' => 4,
+				'comment' => 'Brand New Comment',
+				'published' => 'N'
+	))));
+
+	}
+
+/**
+ * testCreationWithMultipleDataSameModel method
+ *
+ * @access public
+ * @return void
+ */
+	function testCreationWithMultipleDataSameModel() {
+		$this->loadFixtures('Article');
+		$Article =& new Article();
+		$SecondaryArticle =& new Article();
+
+		$result = $Article->field('title', array('id' => 1));
+		$this->assertEqual($result, 'First Article');
+
+		$data = array(
+			'Article' => array(
+				'user_id' => 2,
+				'title' => 'Brand New Article',
+				'body' => 'Brand New Article Body',
+				'published' => 'Y'
+			),
+			'SecondaryArticle' => array(
+				'id' => 1
+		));
+
+		$Article->create();
+		$result = $Article->save($data);
+		$this->assertTrue($result);
+
+		$result = $Article->getInsertID();
+		$this->assertTrue(!empty($result));
+
+		$result = $Article->field('title', array('id' => 1));
+		$this->assertEqual($result, 'First Article');
+
+		$articles = $Article->find('all', array(
+			'fields' => array('id','title'),
+			'recursive' => -1
+		));
+
+		$this->assertEqual($articles, array(
+			array('Article' => array(
+				'id' => 1,
+				'title' => 'First Article'
+			)),
+			array('Article' => array(
+				'id' => 2,
+				'title' => 'Second Article'
+			)),
+			array('Article' => array(
+				'id' => 3,
+				'title' => 'Third Article'
+			)),
+			array('Article' => array(
+				'id' => 4,
+				'title' => 'Brand New Article'
+		))));
+	}
+
+/**
+ * testCreationWithMultipleDataSameModelManualInstances method
+ *
+ * @access public
+ * @return void
+ */
+	function testCreationWithMultipleDataSameModelManualInstances() {
+		$this->loadFixtures('PrimaryModel');
+		$Primary =& new PrimaryModel();
+		$Secondary =& new PrimaryModel();
+
+		$result = $Primary->field('primary_name', array('id' => 1));
+		$this->assertEqual($result, 'Primary Name Existing');
+
+		$data = array(
+			'PrimaryModel' => array(
+				'primary_name' => 'Primary Name New'
+			),
+			'SecondaryModel' => array(
+				'id' => array(1)
+		));
+
+		$Primary->create();
+		$result = $Primary->save($data);
+		$this->assertTrue($result);
+
+		$result = $Primary->field('primary_name', array('id' => 1));
+		$this->assertEqual($result, 'Primary Name Existing');
+
+		$result = $Primary->getInsertID();
+		$this->assertTrue(!empty($result));
+
+		$result = $Primary->field('primary_name', array('id' => $result));
+		$this->assertEqual($result, 'Primary Name New');
+
+		$result = $Primary->find('count');
+		$this->assertEqual($result, 2);
+	}
+
+/**
+ * testRecordExists method
+ *
+ * @access public
+ * @return void
+ */
+	function testRecordExists() {
+		$this->loadFixtures('User');
+		$TestModel =& new User();
+
+		$this->assertFalse($TestModel->exists());
+		$TestModel->read(null, 1);
+		$this->assertTrue($TestModel->exists());
+		$TestModel->create();
+		$this->assertFalse($TestModel->exists());
+		$TestModel->id = 4;
+		$this->assertTrue($TestModel->exists());
+
+		$TestModel =& new TheVoid();
+		$this->assertFalse($TestModel->exists());
+
+		$TestModel->id = 5;
+		$this->expectError();
+		ob_start();
+		$this->assertFalse($TestModel->exists());
+		$output = ob_get_clean();
+	}
+
+/**
+ * testUpdateExisting method
+ *
+ * @access public
+ * @return void
+ */
+	function testUpdateExisting() {
+		$this->loadFixtures('User', 'Article', 'Comment');
+		$TestModel =& new User();
+		$TestModel->create();
+
+		$TestModel->save(array(
+			'User' => array(
+				'user' => 'some user',
+				'password' => 'some password'
+		)));
+		$this->assertTrue(is_int($TestModel->id) || (intval($TestModel->id) === 5));
+		$id = $TestModel->id;
+
+		$TestModel->save(array(
+			'User' => array(
+				'user' => 'updated user'
+		)));
+		$this->assertEqual($TestModel->id, $id);
+
+		$result = $TestModel->findById($id);
+		$this->assertEqual($result['User']['user'], 'updated user');
+		$this->assertEqual($result['User']['password'], 'some password');
+
+		$Article =& new Article();
+		$Comment =& new Comment();
+		$data = array(
+			'Comment' => array(
+				'id' => 1,
+				'comment' => 'First Comment for First Article'
+			),
+			'Article' => array(
+				'id' => 2,
+				'title' => 'Second Article'
+		));
+
+		$result = $Article->save($data);
+		$this->assertTrue($result);
+
+		$result = $Comment->save($data);
+		$this->assertTrue($result);
+	}
+
+/**
+ * test updating records and saving blank values.
+ *
+ * @return void
+ */
+	function testUpdateSavingBlankValues() {
+		$this->loadFixtures('Article');
+		$Article =& new Article();
+		$Article->validate = array();
+		$Article->create();
+		$result = $Article->save(array(
+			'id' => 1,
+			'title' => '',
+			'body' => ''
+		));
+		$this->assertTrue($result);
+		$result = $Article->find('first', array('conditions' => array('Article.id' => 1)));
+		$this->assertEqual('', $result['Article']['title'], 'Title is not blank');
+		$this->assertEqual('', $result['Article']['body'], 'Body is not blank');
+	}
+
+/**
+ * testUpdateMultiple method
+ *
+ * @access public
+ * @return void
+ */
+	function testUpdateMultiple() {
+		$this->loadFixtures('Comment', 'Article', 'User', 'CategoryThread');
+		$TestModel =& new Comment();
+		$result = Set::extract($TestModel->find('all'), '{n}.Comment.user_id');
+		$expected = array('2', '4', '1', '1', '1', '2');
+		$this->assertEqual($result, $expected);
+
+		$TestModel->updateAll(array('Comment.user_id' => 5), array('Comment.user_id' => 2));
+		$result = Set::combine($TestModel->find('all'), '{n}.Comment.id', '{n}.Comment.user_id');
+		$expected = array(1 => 5, 2 => 4, 3 => 1, 4 => 1, 5 => 1, 6 => 5);
+		$this->assertEqual($result, $expected);
+
+		$result = $TestModel->updateAll(
+			array('Comment.comment' => "'Updated today'"),
+			array('Comment.user_id' => 5)
+		);
+		$this->assertTrue($result);
+		$result = Set::extract(
+			$TestModel->find('all', array(
+				'conditions' => array(
+					'Comment.user_id' => 5
+			))),
+			'{n}.Comment.comment'
+		);
+		$expected = array_fill(0, 2, 'Updated today');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testHabtmUuidWithUuidId method
+ *
+ * @access public
+ * @return void
+ */
+	function testHabtmUuidWithUuidId() {
+		$this->loadFixtures('Uuidportfolio', 'Uuiditem', 'UuiditemsUuidportfolio');
+		$TestModel =& new Uuidportfolio();
+
+		$data = array('Uuidportfolio' => array('name' => 'Portfolio 3'));
+		$data['Uuiditem']['Uuiditem'] = array('483798c8-c7cc-430e-8cf9-4fcc40cf8569');
+		$TestModel->create($data);
+		$TestModel->save();
+		$id = $TestModel->id;
+		$result = $TestModel->read(null, $id);
+		$this->assertEqual(1, count($result['Uuiditem']));
+		$this->assertEqual(strlen($result['Uuiditem'][0]['UuiditemsUuidportfolio']['id']), 36);
+	}
+
+/**
+ * test HABTM saving when join table has no primary key and only 2 columns.
+ *
+ * @return void
+ */
+	function testHabtmSavingWithNoPrimaryKeyUuidJoinTable() {
+		$this->loadFixtures('UuidTag', 'Fruit', 'FruitsUuidTag');
+		$Fruit =& new Fruit();
+		$data = array(
+			'Fruit' => array(
+				'color' => 'Red',
+				'shape' => 'Heart-shaped',
+				'taste' => 'sweet',
+				'name' => 'Strawberry',
+			),
+			'UuidTag' => array(
+				'UuidTag' => array(
+					'481fc6d0-b920-43e0-e50f-6d1740cf8569'
+				)
+			)
+		);
+		$this->assertTrue($Fruit->save($data));
+	}
+
+/**
+ * test HABTM saving when join table has no primary key and only 2 columns, no with model is used.
+ *
+ * @return void
+ */
+	function testHabtmSavingWithNoPrimaryKeyUuidJoinTableNoWith() {
+		$this->loadFixtures('UuidTag', 'Fruit', 'FruitsUuidTag');
+		$Fruit =& new FruitNoWith();
+		$data = array(
+			'Fruit' => array(
+				'color' => 'Red',
+				'shape' => 'Heart-shaped',
+				'taste' => 'sweet',
+				'name' => 'Strawberry',
+			),
+			'UuidTag' => array(
+				'UuidTag' => array(
+					'481fc6d0-b920-43e0-e50f-6d1740cf8569'
+				)
+			)
+		);
+		$this->assertTrue($Fruit->save($data));
+	}
+
+/**
+ * testHabtmUuidWithNumericId method
+ *
+ * @access public
+ * @return void
+ */
+	function testHabtmUuidWithNumericId() {
+		$this->loadFixtures('Uuidportfolio', 'Uuiditem', 'UuiditemsUuidportfolioNumericid');
+		$TestModel =& new Uuiditem();
+
+		$data = array('Uuiditem' => array('name' => 'Item 7', 'published' => 0));
+		$data['Uuidportfolio']['Uuidportfolio'] = array('480af662-eb8c-47d3-886b-230540cf8569');
+		$TestModel->create($data);
+		$TestModel->save();
+		$id = $TestModel->id;
+		$result = $TestModel->read(null, $id);
+		$this->assertEqual(1, count($result['Uuidportfolio']));
+	}
+
+/**
+ * testSaveMultipleHabtm method
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveMultipleHabtm() {
+		$this->loadFixtures('JoinA', 'JoinB', 'JoinC', 'JoinAB', 'JoinAC');
+		$TestModel = new JoinA();
+		$result = $TestModel->findById(1);
+
+		$expected = array(
+			'JoinA' => array(
+				'id' => 1,
+				'name' => 'Join A 1',
+				'body' => 'Join A 1 Body',
+				'created' => '2008-01-03 10:54:23',
+				'updated' => '2008-01-03 10:54:23'
+			),
+			'JoinB' => array(
+				0 => array(
+					'id' => 2,
+					'name' => 'Join B 2',
+					'created' => '2008-01-03 10:55:02',
+					'updated' => '2008-01-03 10:55:02',
+					'JoinAsJoinB' => array(
+						'id' => 1,
+						'join_a_id' => 1,
+						'join_b_id' => 2,
+						'other' => 'Data for Join A 1 Join B 2',
+						'created' => '2008-01-03 10:56:33',
+						'updated' => '2008-01-03 10:56:33'
+			))),
+			'JoinC' => array(
+				0 => array(
+					'id' => 2,
+					'name' => 'Join C 2',
+					'created' => '2008-01-03 10:56:12',
+					'updated' => '2008-01-03 10:56:12',
+					'JoinAsJoinC' => array(
+						'id' => 1,
+						'join_a_id' => 1,
+						'join_c_id' => 2,
+						'other' => 'Data for Join A 1 Join C 2',
+						'created' => '2008-01-03 10:57:22',
+						'updated' => '2008-01-03 10:57:22'
+		))));
+
+		$this->assertEqual($result, $expected);
+
+		$ts = date('Y-m-d H:i:s');
+		$TestModel->id = 1;
+		$data = array(
+			'JoinA' => array(
+				'id' => '1',
+				'name' => 'New name for Join A 1',
+				'updated' => $ts
+			),
+			'JoinB' => array(
+				array(
+					'id' => 1,
+					'join_b_id' => 2,
+					'other' => 'New data for Join A 1 Join B 2',
+					'created' => $ts,
+					'updated' => $ts
+			)),
+			'JoinC' => array(
+				array(
+					'id' => 1,
+					'join_c_id' => 2,
+					'other' => 'New data for Join A 1 Join C 2',
+					'created' => $ts,
+					'updated' => $ts
+		)));
+
+		$TestModel->set($data);
+		$TestModel->save();
+
+		$result = $TestModel->findById(1);
+		$expected = array(
+			'JoinA' => array(
+				'id' => 1,
+				'name' => 'New name for Join A 1',
+				'body' => 'Join A 1 Body',
+				'created' => '2008-01-03 10:54:23',
+				'updated' => $ts
+			),
+			'JoinB' => array(
+				0 => array(
+					'id' => 2,
+					'name' => 'Join B 2',
+					'created' => '2008-01-03 10:55:02',
+					'updated' => '2008-01-03 10:55:02',
+					'JoinAsJoinB' => array(
+						'id' => 1,
+						'join_a_id' => 1,
+						'join_b_id' => 2,
+						'other' => 'New data for Join A 1 Join B 2',
+						'created' => $ts,
+						'updated' => $ts
+			))),
+			'JoinC' => array(
+				0 => array(
+					'id' => 2,
+					'name' => 'Join C 2',
+					'created' => '2008-01-03 10:56:12',
+					'updated' => '2008-01-03 10:56:12',
+					'JoinAsJoinC' => array(
+						'id' => 1,
+						'join_a_id' => 1,
+						'join_c_id' => 2,
+						'other' => 'New data for Join A 1 Join C 2',
+						'created' => $ts,
+						'updated' => $ts
+		))));
+
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testSaveAll method
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveAll() {
+		$this->loadFixtures('Post', 'Author', 'Comment', 'Attachment');
+		$TestModel =& new Post();
+
+		$result = $TestModel->find('all');
+		$this->assertEqual(count($result), 3);
+		$this->assertFalse(isset($result[3]));
+		$ts = date('Y-m-d H:i:s');
+
+		$TestModel->saveAll(array(
+			'Post' => array(
+				'title' => 'Post with Author',
+				'body' => 'This post will be saved with an author'
+			),
+			'Author' => array(
+				'user' => 'bob',
+				'password' => '5f4dcc3b5aa765d61d8327deb882cf90'
+		)));
+
+		$result = $TestModel->find('all');
+		$expected = array(
+			'Post' => array(
+				'id' => '4',
+				'author_id' => '5',
+				'title' => 'Post with Author',
+				'body' => 'This post will be saved with an author',
+				'published' => 'N',
+				'created' => $ts,
+				'updated' => $ts
+			),
+			'Author' => array(
+				'id' => '5',
+				'user' => 'bob',
+				'password' => '5f4dcc3b5aa765d61d8327deb882cf90',
+				'created' => $ts,
+				'updated' => $ts,
+				'test' => 'working'
+		));
+		$this->assertEqual($result[3], $expected);
+		$this->assertEqual(count($result), 4);
+
+		$TestModel->deleteAll(true);
+		$this->assertEqual($TestModel->find('all'), array());
+
+		// SQLite seems to reset the PK counter when that happens, so we need this to make the tests pass
+		$this->db->truncate($TestModel);
+
+		$ts = date('Y-m-d H:i:s');
+		$TestModel->saveAll(array(
+			array(
+				'title' => 'Multi-record post 1',
+				'body' => 'First multi-record post',
+				'author_id' => 2
+			),
+			array(
+				'title' => 'Multi-record post 2',
+				'body' => 'Second multi-record post',
+				'author_id' => 2
+		)));
+
+		$result = $TestModel->find('all', array(
+			'recursive' => -1,
+			'order' => 'Post.id ASC'
+		));
+		$expected = array(
+			array(
+				'Post' => array(
+					'id' => '1',
+					'author_id' => '2',
+					'title' => 'Multi-record post 1',
+					'body' => 'First multi-record post',
+					'published' => 'N',
+					'created' => $ts,
+					'updated' => $ts
+			)),
+			array(
+				'Post' => array(
+					'id' => '2',
+					'author_id' => '2',
+					'title' => 'Multi-record post 2',
+					'body' => 'Second multi-record post',
+					'published' => 'N',
+					'created' => $ts,
+					'updated' => $ts
+		)));
+		$this->assertEqual($result, $expected);
+
+		$TestModel =& new Comment();
+		$ts = date('Y-m-d H:i:s');
+		$result = $TestModel->saveAll(array(
+			'Comment' => array(
+				'article_id' => 2,
+				'user_id' => 2,
+				'comment' => 'New comment with attachment',
+				'published' => 'Y'
+			),
+			'Attachment' => array(
+				'attachment' => 'some_file.tgz'
+			)));
+		$this->assertTrue($result);
+
+		$result = $TestModel->find('all');
+		$expected = array(
+			'id' => '7',
+			'article_id' => '2',
+			'user_id' => '2',
+			'comment' => 'New comment with attachment',
+			'published' => 'Y',
+			'created' => $ts,
+			'updated' => $ts
+		);
+		$this->assertEqual($result[6]['Comment'], $expected);
+
+		$expected = array(
+			'id' => '7',
+			'article_id' => '2',
+			'user_id' => '2',
+			'comment' => 'New comment with attachment',
+			'published' => 'Y',
+			'created' => $ts,
+			'updated' => $ts
+		);
+		$this->assertEqual($result[6]['Comment'], $expected);
+
+		$expected = array(
+			'id' => '2',
+			'comment_id' => '7',
+			'attachment' => 'some_file.tgz',
+			'created' => $ts,
+			'updated' => $ts
+		);
+		$this->assertEqual($result[6]['Attachment'], $expected);
+	}
+
+/**
+ * Test SaveAll with Habtm relations
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveAllHabtm() {
+		$this->loadFixtures('Article', 'Tag', 'Comment', 'User');
+		$data = array(
+			'Article' => array(
+				'user_id' => 1,
+				'title' => 'Article Has and belongs to Many Tags'
+			),
+			'Tag' => array(
+				'Tag' => array(1, 2)
+			),
+			'Comment' => array(
+				array(
+					'comment' => 'Article comment',
+					'user_id' => 1
+		)));
+		$Article =& new Article();
+		$result = $Article->saveAll($data);
+		$this->assertTrue($result);
+
+		$result = $Article->read();
+		$this->assertEqual(count($result['Tag']), 2);
+		$this->assertEqual($result['Tag'][0]['tag'], 'tag1');
+		$this->assertEqual(count($result['Comment']), 1);
+		$this->assertEqual(count($result['Comment'][0]['comment']), 1);
+	}
+
+/**
+ * Test SaveAll with Habtm relations and extra join table fields
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveAllHabtmWithExtraJoinTableFields() {
+		$this->loadFixtures('Something', 'SomethingElse', 'JoinThing');
+
+		$data = array(
+			'Something' => array(
+				'id' => 4,
+				'title' => 'Extra Fields',
+				'body' => 'Extra Fields Body',
+				'published' => '1'
+			),
+			'SomethingElse' => array(
+				array('something_else_id' => 1, 'doomed' => '1'),
+				array('something_else_id' => 2, 'doomed' => '0'),
+				array('something_else_id' => 3, 'doomed' => '1')
+			)
+		);
+
+		$Something =& new Something();
+		$result = $Something->saveAll($data);
+		$this->assertTrue($result);
+		$result = $Something->read();
+
+		$this->assertEqual(count($result['SomethingElse']), 3);
+		$this->assertTrue(Set::matches('/Something[id=4]', $result));
+
+		$this->assertTrue(Set::matches('/SomethingElse[id=1]', $result));
+		$this->assertTrue(Set::matches('/SomethingElse[id=1]/JoinThing[something_else_id=1]', $result));
+		$this->assertTrue(Set::matches('/SomethingElse[id=1]/JoinThing[doomed=1]', $result));
+
+		$this->assertTrue(Set::matches('/SomethingElse[id=2]', $result));
+		$this->assertTrue(Set::matches('/SomethingElse[id=2]/JoinThing[something_else_id=2]', $result));
+		$this->assertTrue(Set::matches('/SomethingElse[id=2]/JoinThing[doomed=0]', $result));
+
+		$this->assertTrue(Set::matches('/SomethingElse[id=3]', $result));
+		$this->assertTrue(Set::matches('/SomethingElse[id=3]/JoinThing[something_else_id=3]', $result));
+		$this->assertTrue(Set::matches('/SomethingElse[id=3]/JoinThing[doomed=1]', $result));
+	}
+
+/**
+ * testSaveAllHasOne method
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveAllHasOne() {
+		$model = new Comment();
+		$model->deleteAll(true);
+		$this->assertEqual($model->find('all'), array());
+
+		$model->Attachment->deleteAll(true);
+		$this->assertEqual($model->Attachment->find('all'), array());
+
+		$this->assertTrue($model->saveAll(array(
+			'Comment' => array(
+				'comment' => 'Comment with attachment',
+				'article_id' => 1,
+				'user_id' => 1
+			),
+			'Attachment' => array(
+				'attachment' => 'some_file.zip'
+		))));
+		$result = $model->find('all', array('fields' => array(
+			'Comment.id', 'Comment.comment', 'Attachment.id',
+			'Attachment.comment_id', 'Attachment.attachment'
+		)));
+		$expected = array(array(
+			'Comment' => array(
+				'id' => '1',
+				'comment' => 'Comment with attachment'
+			),
+			'Attachment' => array(
+				'id' => '1',
+				'comment_id' => '1',
+				'attachment' => 'some_file.zip'
+		)));
+		$this->assertEqual($result, $expected);
+
+
+		$model->Attachment->bindModel(array('belongsTo' => array('Comment')), false);
+		$data = array(
+			'Comment' => array(
+				'comment' => 'Comment with attachment',
+				'article_id' => 1,
+				'user_id' => 1
+			),
+			'Attachment' => array(
+				'attachment' => 'some_file.zip'
+		));
+		$this->assertTrue($model->saveAll($data, array('validate' => 'first')));
+	}
+
+/**
+ * testSaveAllBelongsTo method
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveAllBelongsTo() {
+		$model = new Comment();
+		$model->deleteAll(true);
+		$this->assertEqual($model->find('all'), array());
+
+		$model->Article->deleteAll(true);
+		$this->assertEqual($model->Article->find('all'), array());
+
+		$this->assertTrue($model->saveAll(array(
+			'Comment' => array(
+				'comment' => 'Article comment',
+				'article_id' => 1,
+				'user_id' => 1
+			),
+			'Article' => array(
+				'title' => 'Model Associations 101',
+				'user_id' => 1
+		))));
+		$result = $model->find('all', array('fields' => array(
+			'Comment.id', 'Comment.comment', 'Comment.article_id', 'Article.id', 'Article.title'
+		)));
+		$expected = array(array(
+			'Comment' => array(
+				'id' => '1',
+				'article_id' => '1',
+				'comment' => 'Article comment'
+			),
+			'Article' => array(
+				'id' => '1',
+				'title' => 'Model Associations 101'
+		)));
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testSaveAllHasOneValidation method
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveAllHasOneValidation() {
+		$model = new Comment();
+		$model->deleteAll(true);
+		$this->assertEqual($model->find('all'), array());
+
+		$model->Attachment->deleteAll(true);
+		$this->assertEqual($model->Attachment->find('all'), array());
+
+		$model->validate = array('comment' => 'notEmpty');
+		$model->Attachment->validate = array('attachment' => 'notEmpty');
+		$model->Attachment->bindModel(array('belongsTo' => array('Comment')));
+
+		$this->assertFalse($model->saveAll(
+			array(
+				'Comment' => array(
+					'comment' => '',
+					'article_id' => 1,
+					'user_id' => 1
+				),
+				'Attachment' => array('attachment' => '')
+			),
+			array('validate' => 'first')
+		));
+		$expected = array(
+			'Comment' => array('comment' => 'This field cannot be left blank'),
+			'Attachment' => array('attachment' => 'This field cannot be left blank')
+		);
+		$this->assertEqual($model->validationErrors, $expected['Comment']);
+		$this->assertEqual($model->Attachment->validationErrors, $expected['Attachment']);
+
+		$this->assertFalse($model->saveAll(
+			array(
+				'Comment' => array('comment' => '', 'article_id' => 1, 'user_id' => 1),
+				'Attachment' => array('attachment' => '')
+			),
+			array('validate' => 'only')
+		));
+		$this->assertEqual($model->validationErrors, $expected['Comment']);
+		$this->assertEqual($model->Attachment->validationErrors, $expected['Attachment']);
+	}
+
+/**
+ * testSaveAllAtomic method
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveAllAtomic() {
+		$this->loadFixtures('Article', 'User');
+		$TestModel =& new Article();
+
+		$result = $TestModel->saveAll(array(
+			'Article' => array(
+				'title' => 'Post with Author',
+				'body' => 'This post will be saved with an author',
+				'user_id' => 2
+			),
+			'Comment' => array(
+				array('comment' => 'First new comment', 'user_id' => 2))
+		), array('atomic' => false));
+
+		$this->assertIdentical($result, array('Article' => true, 'Comment' => array(true)));
+
+		$result = $TestModel->saveAll(array(
+			array(
+				'id' => '1',
+				'title' => 'Baleeted First Post',
+				'body' => 'Baleeted!',
+				'published' => 'N'
+			),
+			array(
+				'id' => '2',
+				'title' => 'Just update the title'
+			),
+			array(
+				'title' => 'Creating a fourth post',
+				'body' => 'Fourth post body',
+				'user_id' => 2
+			)
+		), array('atomic' => false));
+		$this->assertIdentical($result, array(true, true, true));
+
+		$TestModel->validate = array('title' => 'notEmpty', 'author_id' => 'numeric');
+		$result = $TestModel->saveAll(array(
+			array(
+				'id' => '1',
+				'title' => 'Un-Baleeted First Post',
+				'body' => 'Not Baleeted!',
+				'published' => 'Y'
+			),
+			array(
+				'id' => '2',
+				'title' => '',
+				'body' => 'Trying to get away with an empty title'
+			)
+		), array('validate' => true, 'atomic' => false));
+
+		$this->assertIdentical($result, array(true, false));
+
+		$result = $TestModel->saveAll(array(
+			'Article' => array('id' => 2),
+			'Comment' => array(
+				array(
+					'comment' => 'First new comment',
+					'published' => 'Y',
+					'user_id' => 1
+				),
+				array(
+					'comment' => 'Second new comment',
+					'published' => 'Y',
+					'user_id' => 2
+			))
+		), array('validate' => true, 'atomic' => false));
+		$this->assertIdentical($result, array('Article' => true, 'Comment' => array(true, true)));
+	}
+
+/**
+ * testSaveAllHasMany method
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveAllHasMany() {
+		$this->loadFixtures('Article', 'Comment');
+		$TestModel =& new Article();
+		$TestModel->belongsTo = $TestModel->hasAndBelongsToMany = array();
+
+		$result = $TestModel->saveAll(array(
+			'Article' => array('id' => 2),
+			'Comment' => array(
+				array('comment' => 'First new comment', 'published' => 'Y', 'user_id' => 1),
+				array('comment' => 'Second new comment', 'published' => 'Y', 'user_id' => 2)
+			)
+		));
+		$this->assertTrue($result);
+
+		$result = $TestModel->findById(2);
+		$expected = array(
+			'First Comment for Second Article',
+			'Second Comment for Second Article',
+			'First new comment',
+			'Second new comment'
+		);
+		$this->assertEqual(Set::extract($result['Comment'], '{n}.comment'), $expected);
+
+		$result = $TestModel->saveAll(
+			array(
+				'Article' => array('id' => 2),
+				'Comment' => array(
+					array(
+						'comment' => 'Third new comment',
+						'published' => 'Y',
+						'user_id' => 1
+			))),
+			array('atomic' => false)
+		);
+		$this->assertTrue($result);
+
+		$result = $TestModel->findById(2);
+		$expected = array(
+			'First Comment for Second Article',
+			'Second Comment for Second Article',
+			'First new comment',
+			'Second new comment',
+			'Third new comment'
+		);
+		$this->assertEqual(Set::extract($result['Comment'], '{n}.comment'), $expected);
+
+		$TestModel->beforeSaveReturn = false;
+		$result = $TestModel->saveAll(
+			array(
+				'Article' => array('id' => 2),
+				'Comment' => array(
+					array(
+						'comment' => 'Fourth new comment',
+						'published' => 'Y',
+						'user_id' => 1
+			))),
+			array('atomic' => false)
+		);
+		$this->assertEqual($result, array('Article' => false));
+
+		$result = $TestModel->findById(2);
+		$expected = array(
+			'First Comment for Second Article',
+			'Second Comment for Second Article',
+			'First new comment',
+			'Second new comment',
+			'Third new comment'
+		);
+		$this->assertEqual(Set::extract($result['Comment'], '{n}.comment'), $expected);
+	}
+
+/**
+ * testSaveAllHasManyValidation method
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveAllHasManyValidation() {
+		$this->loadFixtures('Article', 'Comment');
+		$TestModel =& new Article();
+		$TestModel->belongsTo = $TestModel->hasAndBelongsToMany = array();
+		$TestModel->Comment->validate = array('comment' => 'notEmpty');
+
+		$result = $TestModel->saveAll(array(
+			'Article' => array('id' => 2),
+			'Comment' => array(
+				array('comment' => '', 'published' => 'Y', 'user_id' => 1),
+			)
+		), array('validate' => true));
+		$expected = array('Comment' => array(false));
+		$this->assertEqual($result, $expected);
+
+		$expected = array('Comment' => array(
+			array('comment' => 'This field cannot be left blank')
+		));
+		$this->assertEqual($TestModel->validationErrors, $expected);
+		$expected = array(
+			array('comment' => 'This field cannot be left blank')
+		);
+		$this->assertEqual($TestModel->Comment->validationErrors, $expected);
+
+		$result = $TestModel->saveAll(array(
+			'Article' => array('id' => 2),
+			'Comment' => array(
+				array(
+					'comment' => '',
+					'published' => 'Y',
+					'user_id' => 1
+			))
+		), array('validate' => 'first'));
+		$this->assertFalse($result);
+	}
+
+/**
+ * test saveAll with transactions and ensure there is no missing rollback.
+ *
+ * @return void
+ */
+	function testSaveAllManyRowsTransactionNoRollback() {
+		$this->loadFixtures('Post');
+
+		Mock::generate('DboSource', 'MockTransactionDboSource');
+		$db = ConnectionManager::create('mock_transaction', array(
+			'datasource' => 'MockTransactionDbo',
+		));
+		$db->expectOnce('rollback');
+
+		$Post =& new Post();
+		$Post->useDbConfig = 'mock_transaction';
+
+		$Post->validate = array(
+			'title' => array('rule' => array('notEmpty'))
+		);
+
+		$data = array(
+			array('author_id' => 1, 'title' => 'New Fourth Post'),
+			array('author_id' => 1, 'title' => '')
+		);
+		$Post->saveAll($data, array('atomic' => true));
+	}
+
+/**
+ * test saveAll with transactions and ensure there is no missing rollback.
+ *
+ * @return void
+ */
+	function testSaveAllAssociatedTransactionNoRollback() {
+		$testDb = ConnectionManager::getDataSource('test_suite');
+
+		Mock::generate('DboSource', 'MockTransactionAssociatedDboSource');
+		$db = ConnectionManager::create('mock_transaction_assoc', array(
+			'datasource' => 'MockTransactionAssociatedDbo',
+		));
+		$db->columns = $testDb->columns;
+
+		$db->expectOnce('rollback');
+
+		$Post =& new Post();
+		$Post->useDbConfig = 'mock_transaction_assoc';
+		$Post->Author->useDbConfig = 'mock_transaction_assoc';
+
+		$Post->Author->validate = array(
+			'user' => array('rule' => array('notEmpty'))
+		);
+
+		$data = array(
+			'Post' => array(
+				'title' => 'New post',
+				'body' => 'Content',
+				'published' => 'Y'
+			),
+			'Author' => array(
+				'user' => '',
+				'password' => "sekret"
+			)
+		);
+		$Post->saveAll($data);
+	}
+
+/**
+ * test saveAll with nested saveAll call.
+ *
+ * @return void
+ */
+	function testSaveAllNestedSaveAll() {
+		$this->loadFixtures('Sample');
+		$TransactionTestModel =& new TransactionTestModel();
+
+		$data = array(
+			array('apple_id' => 1, 'name' => 'sample5'),
+		);
+
+		$this->assertTrue($TransactionTestModel->saveAll($data, array('atomic' => true)));
+	}
+
+/**
+ * testSaveAllTransaction method
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveAllTransaction() {
+		$this->loadFixtures('Post', 'Author', 'Comment', 'Attachment');
+		$TestModel =& new Post();
+
+		$TestModel->validate = array('title' => 'notEmpty');
+		$data = array(
+			array('author_id' => 1, 'title' => 'New Fourth Post'),
+			array('author_id' => 1, 'title' => 'New Fifth Post'),
+			array('author_id' => 1, 'title' => '')
+		);
+		$ts = date('Y-m-d H:i:s');
+		$this->assertFalse($TestModel->saveAll($data));
+
+		$result = $TestModel->find('all', array('recursive' => -1));
+		$expected = array(
+			array('Post' => array(
+				'id' => '1',
+				'author_id' => 1,
+				'title' => 'First Post',
+				'body' => 'First Post Body',
+				'published' => 'Y',
+				'created' => '2007-03-18 10:39:23',
+				'updated' => '2007-03-18 10:41:31'
+			)),
+			array('Post' => array(
+				'id' => '2',
+				'author_id' => 3,
+				'title' => 'Second Post',
+				'body' => 'Second Post Body',
+				'published' => 'Y',
+				'created' => '2007-03-18 10:41:23',
+				'updated' => '2007-03-18 10:43:31'
+			)),
+			array('Post' => array(
+				'id' => '3',
+				'author_id' => 1,
+				'title' => 'Third Post',
+				'body' => 'Third Post Body',
+				'published' => 'Y',
+				'created' => '2007-03-18 10:43:23',
+				'updated' => '2007-03-18 10:45:31'
+		)));
+
+		if (count($result) != 3) {
+			// Database doesn't support transactions
+			$expected[] = array(
+				'Post' => array(
+					'id' => '4',
+					'author_id' => 1,
+					'title' => 'New Fourth Post',
+					'body' => null,
+					'published' => 'N',
+					'created' => $ts,
+					'updated' => $ts
+			));
+
+			$expected[] = array(
+				'Post' => array(
+					'id' => '5',
+					'author_id' => 1,
+					'title' => 'New Fifth Post',
+					'body' => null,
+					'published' => 'N',
+					'created' => $ts,
+					'updated' => $ts
+			));
+
+			$this->assertEqual($result, $expected);
+			// Skip the rest of the transactional tests
+			return;
+		}
+
+		$this->assertEqual($result, $expected);
+
+		$data = array(
+			array('author_id' => 1, 'title' => 'New Fourth Post'),
+			array('author_id' => 1, 'title' => ''),
+			array('author_id' => 1, 'title' => 'New Sixth Post')
+		);
+		$ts = date('Y-m-d H:i:s');
+		$this->assertFalse($TestModel->saveAll($data));
+
+		$result = $TestModel->find('all', array('recursive' => -1));
+		$expected = array(
+			array('Post' => array(
+				'id' => '1',
+				'author_id' => 1,
+				'title' => 'First Post',
+				'body' => 'First Post Body',
+				'published' => 'Y',
+				'created' => '2007-03-18 10:39:23',
+				'updated' => '2007-03-18 10:41:31'
+			)),
+			array('Post' => array(
+				'id' => '2',
+				'author_id' => 3,
+				'title' => 'Second Post',
+				'body' => 'Second Post Body',
+				'published' => 'Y',
+				'created' => '2007-03-18 10:41:23',
+				'updated' => '2007-03-18 10:43:31'
+			)),
+			array('Post' => array(
+				'id' => '3',
+				'author_id' => 1,
+				'title' => 'Third Post',
+				'body' => 'Third Post Body',
+				'published' => 'Y',
+				'created' => '2007-03-18 10:43:23',
+				'updated' => '2007-03-18 10:45:31'
+		)));
+
+		if (count($result) != 3) {
+			// Database doesn't support transactions
+			$expected[] = array(
+				'Post' => array(
+					'id' => '4',
+					'author_id' => 1,
+					'title' => 'New Fourth Post',
+					'body' => 'Third Post Body',
+					'published' => 'N',
+					'created' => $ts,
+					'updated' => $ts
+			));
+
+			$expected[] = array(
+				'Post' => array(
+					'id' => '5',
+					'author_id' => 1,
+					'title' => 'Third Post',
+					'body' => 'Third Post Body',
+					'published' => 'N',
+					'created' => $ts,
+					'updated' => $ts
+			));
+		}
+		$this->assertEqual($result, $expected);
+
+		$TestModel->validate = array('title' => 'notEmpty');
+		$data = array(
+			array('author_id' => 1, 'title' => 'New Fourth Post'),
+			array('author_id' => 1, 'title' => 'New Fifth Post'),
+			array('author_id' => 1, 'title' => 'New Sixth Post')
+		);
+		$this->assertTrue($TestModel->saveAll($data));
+
+		$result = $TestModel->find('all', array(
+			'recursive' => -1,
+			'fields' => array('author_id', 'title','body','published')
+		));
+
+		$expected = array(
+			array('Post' => array(
+				'author_id' => 1,
+				'title' => 'First Post',
+				'body' => 'First Post Body',
+				'published' => 'Y'
+			)),
+			array('Post' => array(
+				'author_id' => 3,
+				'title' => 'Second Post',
+				'body' => 'Second Post Body',
+				'published' => 'Y'
+			)),
+			array('Post' => array(
+				'author_id' => 1,
+				'title' => 'Third Post',
+				'body' => 'Third Post Body',
+				'published' => 'Y'
+			)),
+			array('Post' => array(
+				'author_id' => 1,
+				'title' => 'New Fourth Post',
+				'body' => '',
+				'published' => 'N'
+			)),
+			array('Post' => array(
+				'author_id' => 1,
+				'title' => 'New Fifth Post',
+				'body' => '',
+				'published' => 'N'
+			)),
+			array('Post' => array(
+				'author_id' => 1,
+				'title' => 'New Sixth Post',
+				'body' => '',
+				'published' => 'N'
+		)));
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testSaveAllValidation method
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveAllValidation() {
+		$this->loadFixtures('Post', 'Author', 'Comment', 'Attachment');
+		$TestModel =& new Post();
+
+		$data = array(
+			array(
+				'id' => '1',
+				'title' => 'Baleeted First Post',
+				'body' => 'Baleeted!',
+				'published' => 'N'
+			),
+			array(
+				'id' => '2',
+				'title' => 'Just update the title'
+			),
+			array(
+				'title' => 'Creating a fourth post',
+				'body' => 'Fourth post body',
+				'author_id' => 2
+		));
+
+		$this->assertTrue($TestModel->saveAll($data));
+
+		$result = $TestModel->find('all', array('recursive' => -1, 'order' => 'Post.id ASC'));
+		$ts = date('Y-m-d H:i:s');
+		$expected = array(
+			array(
+				'Post' => array(
+					'id' => '1',
+					'author_id' => '1',
+					'title' => 'Baleeted First Post',
+					'body' => 'Baleeted!',
+					'published' => 'N',
+					'created' => '2007-03-18 10:39:23',
+					'updated' => $ts
+			)),
+			array(
+				'Post' => array(
+					'id' => '2',
+					'author_id' => '3',
+					'title' => 'Just update the title',
+					'body' => 'Second Post Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:41:23', 'updated' => $ts
+			)),
+			array(
+				'Post' => array(
+					'id' => '3',
+					'author_id' => '1',
+					'title' => 'Third Post',
+					'body' => 'Third Post Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:43:23',
+					'updated' => '2007-03-18 10:45:31'
+			)),
+			array(
+				'Post' => array(
+					'id' => '4',
+					'author_id' => '2',
+					'title' => 'Creating a fourth post',
+					'body' => 'Fourth post body',
+					'published' => 'N',
+					'created' => $ts,
+					'updated' => $ts
+		)));
+		$this->assertEqual($result, $expected);
+
+		$TestModel->validate = array('title' => 'notEmpty', 'author_id' => 'numeric');
+		$data = array(
+			array(
+				'id' => '1',
+				'title' => 'Un-Baleeted First Post',
+				'body' => 'Not Baleeted!',
+				'published' => 'Y'
+			),
+			array(
+				'id' => '2',
+				'title' => '',
+				'body' => 'Trying to get away with an empty title'
+		));
+		$result = $TestModel->saveAll($data);
+		$this->assertEqual($result, false);
+
+		$result = $TestModel->find('all', array('recursive' => -1, 'order' => 'Post.id ASC'));
+		$errors = array(1 => array('title' => 'This field cannot be left blank'));
+		$transactionWorked = Set::matches('/Post[1][title=Baleeted First Post]', $result);
+		if (!$transactionWorked) {
+			$this->assertTrue(Set::matches('/Post[1][title=Un-Baleeted First Post]', $result));
+			$this->assertTrue(Set::matches('/Post[2][title=Just update the title]', $result));
+		}
+
+		$this->assertEqual($TestModel->validationErrors, $errors);
+
+		$TestModel->validate = array('title' => 'notEmpty', 'author_id' => 'numeric');
+		$data = array(
+			array(
+				'id' => '1',
+				'title' => 'Un-Baleeted First Post',
+				'body' => 'Not Baleeted!',
+				'published' => 'Y'
+			),
+			array(
+				'id' => '2',
+				'title' => '',
+				'body' => 'Trying to get away with an empty title'
+		));
+		$result = $TestModel->saveAll($data, array('validate' => true, 'atomic' => false));
+		$this->assertEqual($result, array(true, false));
+		$result = $TestModel->find('all', array('recursive' => -1, 'order' => 'Post.id ASC'));
+		$errors = array(1 => array('title' => 'This field cannot be left blank'));
+		$newTs = date('Y-m-d H:i:s');
+		$expected = array(
+			array(
+				'Post' => array(
+					'id' => '1',
+					'author_id' => '1',
+					'title' => 'Un-Baleeted First Post',
+					'body' => 'Not Baleeted!',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:39:23',
+					'updated' => $newTs
+			)),
+			array(
+				'Post' => array(
+					'id' => '2',
+					'author_id' => '3',
+					'title' => 'Just update the title',
+					'body' => 'Second Post Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:41:23',
+					'updated' => $ts
+			)),
+			array(
+				'Post' => array(
+					'id' => '3',
+					'author_id' => '1',
+					'title' => 'Third Post',
+					'body' => 'Third Post Body',
+					'published' => 'Y',
+					'created' => '2007-03-18 10:43:23',
+					'updated' => '2007-03-18 10:45:31'
+			)),
+			array(
+				'Post' => array(
+					'id' => '4',
+					'author_id' => '2',
+					'title' => 'Creating a fourth post',
+					'body' => 'Fourth post body',
+					'published' => 'N',
+					'created' => $ts,
+					'updated' => $ts
+		)));
+		$this->assertEqual($result, $expected);
+		$this->assertEqual($TestModel->validationErrors, $errors);
+
+		$data = array(
+			array(
+				'id' => '1',
+				'title' => 'Re-Baleeted First Post',
+				'body' => 'Baleeted!',
+				'published' => 'N'
+			),
+			array(
+				'id' => '2',
+				'title' => '',
+				'body' => 'Trying to get away with an empty title'
+		));
+		$this->assertFalse($TestModel->saveAll($data, array('validate' => 'first')));
+
+		$result = $TestModel->find('all', array('recursive' => -1, 'order' => 'Post.id ASC'));
+		$this->assertEqual($result, $expected);
+		$this->assertEqual($TestModel->validationErrors, $errors);
+
+		$data = array(
+			array(
+				'title' => 'First new post',
+				'body' => 'Woohoo!',
+				'published' => 'Y'
+			),
+			array(
+				'title' => 'Empty body',
+				'body' => ''
+		));
+
+		$TestModel->validate['body'] = 'notEmpty';
+	}
+
+/**
+ * testSaveAllValidationOnly method
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveAllValidationOnly() {
+		$TestModel =& new Comment();
+		$TestModel->Attachment->validate = array('attachment' => 'notEmpty');
+
+		$data = array(
+			'Comment' => array(
+				'comment' => 'This is the comment'
+			),
+			'Attachment' => array(
+				'attachment' => ''
+			)
+		);
+
+		$result = $TestModel->saveAll($data, array('validate' => 'only'));
+		$this->assertFalse($result);
+
+		$TestModel =& new Article();
+		$TestModel->validate = array('title' => 'notEmpty');
+		$result = $TestModel->saveAll(
+			array(
+				0 => array('title' => ''),
+				1 => array('title' => 'title 1'),
+				2 => array('title' => 'title 2'),
+			),
+			array('validate'=>'only')
+		);
+		$this->assertFalse($result);
+		$expected = array(
+			0 => array('title' => 'This field cannot be left blank'),
+		);
+		$this->assertEqual($TestModel->validationErrors, $expected);
+
+		$result = $TestModel->saveAll(
+			array(
+				0 => array('title' => 'title 0'),
+				1 => array('title' => ''),
+				2 => array('title' => 'title 2'),
+			),
+			array('validate'=>'only')
+		);
+		$this->assertFalse($result);
+		$expected = array(
+			1 => array('title' => 'This field cannot be left blank'),
+		);
+		$this->assertEqual($TestModel->validationErrors, $expected);
+	}
+
+/**
+ * testSaveAllValidateFirst method
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveAllValidateFirst() {
+		$model =& new Article();
+		$model->deleteAll(true);
+
+		$model->Comment->validate = array('comment' => 'notEmpty');
+		$result = $model->saveAll(array(
+			'Article' => array(
+				'title' => 'Post with Author',
+				'body' => 'This post will be saved  author'
+			),
+			'Comment' => array(
+				array('comment' => 'First new comment'),
+				array('comment' => '')
+			)
+		), array('validate' => 'first'));
+
+		$this->assertFalse($result);
+
+		$result = $model->find('all');
+		$this->assertEqual($result, array());
+		$expected = array('Comment' => array(
+			1 => array('comment' => 'This field cannot be left blank')
+		));
+
+		$this->assertEqual($model->Comment->validationErrors, $expected['Comment']);
+
+		$this->assertIdentical($model->Comment->find('count'), 0);
+
+		$result = $model->saveAll(
+			array(
+				'Article' => array(
+					'title' => 'Post with Author',
+					'body' => 'This post will be saved with an author',
+					'user_id' => 2
+				),
+				'Comment' => array(
+					array(
+						'comment' => 'Only new comment',
+						'user_id' => 2
+			))),
+			array('validate' => 'first')
+		);
+
+		$this->assertIdentical($result, true);
+
+		$result = $model->Comment->find('all');
+		$this->assertIdentical(count($result), 1);
+		$result = Set::extract('/Comment/article_id', $result);
+		$this->assertTrue($result[0] === 1 || $result[0] === '1');
+
+
+		$model->deleteAll(true);
+		$data = array(
+			'Article' => array(
+				'title' => 'Post with Author saveAlled from comment',
+				'body' => 'This post will be saved with an author',
+				'user_id' => 2
+			),
+			'Comment' => array(
+				'comment' => 'Only new comment', 'user_id' => 2
+		));
+
+		$result = $model->Comment->saveAll($data, array('validate' => 'first'));
+		$this->assertTrue($result);
+
+		$result = $model->find('all');
+		$this->assertEqual(
+			$result[0]['Article']['title'],
+			'Post with Author saveAlled from comment'
+		);
+		$this->assertEqual($result[0]['Comment'][0]['comment'], 'Only new comment');
+	}
+
+/**
+ * test saveAll()'s return is correct when using atomic = false and validate = first.
+ *
+ * @return void
+ */
+	function testSaveAllValidateFirstAtomicFalse() {
+		$Something =& new Something();
+		$invalidData = array(
+			array(
+				'title' => 'foo',
+				'body' => 'bar',
+				'published' => 'baz',
+			),
+			array(
+				'body' => 3,
+				'published' =>'sd',
+			),
+		);
+		$Something->create();
+		$Something->validate = array(
+			'title' => array(
+				'rule' => 'alphaNumeric',
+				'required' => true,
+			),
+			'body' => array(
+				'rule' => 'alphaNumeric',
+				'required' => true,
+				'allowEmpty' => true,
+			),
+		);
+		$result = $Something->saveAll($invalidData, array(
+			'atomic' => false,
+			'validate' => 'first',
+		));
+		$expected = array(true, false);
+		$this->assertEqual($result, $expected);
+
+		$Something =& new Something();
+		$validData = array(
+			array(
+				'title' => 'title value',
+				'body' => 'body value',
+				'published' => 'baz',
+			),
+			array(
+				'title' => 'valid',
+				'body' => 'this body',
+				'published' =>'sd',
+			),
+		);
+		$Something->create();
+		$result = $Something->saveAll($validData, array(
+			'atomic' => false,
+			'validate' => 'first',
+		));
+		$expected = array(true, true);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testUpdateWithCalculation method
+ *
+ * @access public
+ * @return void
+ */
+	function testUpdateWithCalculation() {
+		$this->loadFixtures('DataTest');
+		$model =& new DataTest();
+		$model->deleteAll(true);
+		$result = $model->saveAll(array(
+			array('count' => 5, 'float' => 1.1),
+			array('count' => 3, 'float' => 1.2),
+			array('count' => 4, 'float' => 1.3),
+			array('count' => 1, 'float' => 2.0),
+		));
+		$this->assertTrue($result);
+
+		$result = Set::extract('/DataTest/count', $model->find('all', array('fields' => 'count')));
+		$this->assertEqual($result, array(5, 3, 4, 1));
+
+		$this->assertTrue($model->updateAll(array('count' => 'count + 2')));
+		$result = Set::extract('/DataTest/count', $model->find('all', array('fields' => 'count')));
+		$this->assertEqual($result, array(7, 5, 6, 3));
+
+		$this->assertTrue($model->updateAll(array('DataTest.count' => 'DataTest.count - 1')));
+		$result = Set::extract('/DataTest/count', $model->find('all', array('fields' => 'count')));
+		$this->assertEqual($result, array(6, 4, 5, 2));
+	}
+
+/**
+ * testSaveAllHasManyValidationOnly method
+ *
+ * @access public
+ * @return void
+ */
+	function testSaveAllHasManyValidationOnly() {
+		$this->loadFixtures('Article', 'Comment');
+		$TestModel =& new Article();
+		$TestModel->belongsTo = $TestModel->hasAndBelongsToMany = array();
+		$TestModel->Comment->validate = array('comment' => 'notEmpty');
+
+		$result = $TestModel->saveAll(
+			array(
+				'Article' => array('id' => 2),
+				'Comment' => array(
+					array(
+						'id' => 1,
+						'comment' => '',
+						'published' => 'Y',
+						'user_id' => 1),
+					array(
+						'id' => 2,
+						'comment' =>
+						'comment',
+						'published' => 'Y',
+						'user_id' => 1
+			))),
+			array('validate' => 'only')
+		);
+		$this->assertFalse($result);
+
+		$result = $TestModel->saveAll(
+			array(
+				'Article' => array('id' => 2),
+				'Comment' => array(
+					array(
+						'id' => 1,
+						'comment' => '',
+						'published' => 'Y',
+						'user_id' => 1
+					),
+					array(
+						'id' => 2,
+						'comment' => 'comment',
+						'published' => 'Y',
+						'user_id' => 1
+					),
+					array(
+						'id' => 3,
+						'comment' => '',
+						'published' => 'Y',
+						'user_id' => 1
+			))),
+			array(
+				'validate' => 'only',
+				'atomic' => false
+		));
+		$expected = array(
+			'Article' => true,
+			'Comment' => array(false, true, false)
+		);
+		$this->assertIdentical($result, $expected);
+
+		$expected = array('Comment' => array(
+			0 => array('comment' => 'This field cannot be left blank'),
+			2 => array('comment' => 'This field cannot be left blank')
+		));
+		$this->assertEqual($TestModel->validationErrors, $expected);
+
+		$expected = array(
+			0 => array('comment' => 'This field cannot be left blank'),
+			2 => array('comment' => 'This field cannot be left blank')
+		);
+		$this->assertEqual($TestModel->Comment->validationErrors, $expected);
+	}
+
+/**
+ * TestFindAllWithoutForeignKey
+ *
+ * @link http://code.cakephp.org/tickets/view/69
+ * @access public
+ * @return void
+ */
+	function testFindAllForeignKey() {
+		$this->loadFixtures('ProductUpdateAll', 'GroupUpdateAll');
+		$ProductUpdateAll =& new ProductUpdateAll();
+
+		$conditions = array('Group.name' => 'group one');
+
+		$ProductUpdateAll->bindModel(array(
+			'belongsTo' => array(
+				'Group' => array('className' => 'GroupUpdateAll')
+			)
+		));
+
+		$ProductUpdateAll->belongsTo = array(
+			'Group' => array('className' => 'GroupUpdateAll', 'foreignKey' => 'group_id')
+		);
+
+		$results = $ProductUpdateAll->find('all', compact('conditions'));
+		$this->assertTrue(!empty($results));
+
+		$ProductUpdateAll->bindModel(array('belongsTo'=>array('Group')));
+		$ProductUpdateAll->belongsTo = array(
+			'Group' => array(
+				'className' => 'GroupUpdateAll',
+				'foreignKey' => false,
+				'conditions' => 'ProductUpdateAll.groupcode = Group.code'
+			));
+
+		$resultsFkFalse = $ProductUpdateAll->find('all', compact('conditions'));
+		$this->assertTrue(!empty($resultsFkFalse));
+		$expected = array(
+			'0' => array(
+				'ProductUpdateAll' => array(
+					'id'  => 1,
+					'name'	=> 'product one',
+					'groupcode'	 => 120,
+					'group_id'	=> 1),
+				'Group' => array(
+					'id' => 1,
+					'name' => 'group one',
+					'code' => 120)
+				),
+			'1' => array(
+				'ProductUpdateAll' => array(
+					'id'  => 2,
+					'name'	=> 'product two',
+					'groupcode'	 => 120,
+					'group_id'	=> 1),
+				'Group' => array(
+					'id' => 1,
+					'name' => 'group one',
+					'code' => 120)
+				)
+
+			);
+		$this->assertEqual($results, $expected);
+		$this->assertEqual($resultsFkFalse, $expected);
+	}
+
+/**
+ * test updateAll with empty values.
+ *
+ * @return void
+ */
+	function testUpdateAllEmptyValues() {
+		$this->loadFixtures('Author', 'Post');
+		$model = new Author();
+		$result = $model->updateAll(array('user' => '""'));
+		$this->assertTrue($result);
+	}
+
+/**
+ * testUpdateAllWithJoins
+ *
+ * @link http://code.cakephp.org/tickets/view/69
+ * @access public
+ * @return void
+ */
+	function testUpdateAllWithJoins() {
+		$this->skipIf(
+			$this->db->config['driver'] == 'postgres',
+			'%s Currently, there is no way of doing joins in an update statement in postgresql'
+		);
+		$this->loadFixtures('ProductUpdateAll', 'GroupUpdateAll');
+		$ProductUpdateAll =& new ProductUpdateAll();
+
+		$conditions = array('Group.name' => 'group one');
+
+		$ProductUpdateAll->bindModel(array('belongsTo' => array(
+			'Group' => array('className' => 'GroupUpdateAll')))
+		);
+
+		$ProductUpdateAll->updateAll(array('name' => "'new product'"), $conditions);
+		$results = $ProductUpdateAll->find('all', array(
+			'conditions' => array('ProductUpdateAll.name' => 'new product')
+		));
+		$expected = array(
+			'0' => array(
+				'ProductUpdateAll' => array(
+					'id'  => 1,
+					'name'	=> 'new product',
+					'groupcode'	 => 120,
+					'group_id'	=> 1),
+				'Group' => array(
+					'id' => 1,
+					'name' => 'group one',
+					'code' => 120)
+				),
+			'1' => array(
+				'ProductUpdateAll' => array(
+					'id'  => 2,
+					'name'	=> 'new product',
+					'groupcode'	 => 120,
+					'group_id'	=> 1),
+				'Group' => array(
+					'id' => 1,
+					'name' => 'group one',
+					'code' => 120)));
+
+		$this->assertEqual($results, $expected);
+	}
+
+/**
+ * testUpdateAllWithoutForeignKey
+ *
+ * @link http://code.cakephp.org/tickets/view/69
+ * @access public
+ * @return void
+ */
+    function testUpdateAllWithoutForeignKey() {
+		$this->skipIf(
+			$this->db->config['driver'] == 'postgres',
+			'%s Currently, there is no way of doing joins in an update statement in postgresql'
+		);
+		$this->loadFixtures('ProductUpdateAll', 'GroupUpdateAll');
+		$ProductUpdateAll =& new ProductUpdateAll();
+
+		$conditions = array('Group.name' => 'group one');
+
+        $ProductUpdateAll->bindModel(array('belongsTo' => array(
+			'Group' => array('className' => 'GroupUpdateAll')
+		)));
+
+        $ProductUpdateAll->belongsTo = array(
+            'Group' => array(
+				'className' => 'GroupUpdateAll',
+				'foreignKey' => false,
+				'conditions' => 'ProductUpdateAll.groupcode = Group.code'
+			)
+		);
+
+		$ProductUpdateAll->updateAll(array('name' => "'new product'"), $conditions);
+		$resultsFkFalse = $ProductUpdateAll->find('all', array('conditions' => array('ProductUpdateAll.name'=>'new product')));
+		$expected = array(
+			'0' => array(
+				'ProductUpdateAll' => array(
+					'id'  => 1,
+					'name'	=> 'new product',
+					'groupcode'	 => 120,
+					'group_id'	=> 1),
+				'Group' => array(
+					'id' => 1,
+					'name' => 'group one',
+					'code' => 120)
+				),
+			'1' => array(
+				'ProductUpdateAll' => array(
+					'id'  => 2,
+					'name'	=> 'new product',
+					'groupcode'	 => 120,
+					'group_id'	=> 1),
+				'Group' => array(
+					'id' => 1,
+					'name' => 'group one',
+					'code' => 120)));
+		$this->assertEqual($resultsFkFalse, $expected);
+	}
+
+/**
+ * test that saveAll behaves like plain save() when suplied empty data
+ *
+ * @link http://cakephp.lighthouseapp.com/projects/42648/tickets/277-test-saveall-with-validation-returns-incorrect-boolean-when-saving-empty-data
+ * @access public
+ * @return void
+ */
+	function testSaveAllEmptyData() {
+		$this->loadFixtures('Article', 'ProductUpdateAll');
+		$model =& new Article();
+		$result = $model->saveAll(array(), array('validate' => 'first'));
+		$this->assertTrue($result);
+
+		$model =& new ProductUpdateAll();
+		$result = $model->saveAll(array());
+		$this->assertFalse($result);
+	}
+
+/**
+ * test writing floats in german locale.
+ *
+ * @return void
+ */
+	function testWriteFloatAsGerman() {
+		$restore = setlocale(LC_ALL, null);
+		setlocale(LC_ALL, 'de_DE');
+
+		$model = new DataTest();
+		$result = $model->save(array(
+			'count' => 1,
+			'float' => 3.14593
+		));
+		$this->assertTrue($result);
+		setlocale(LC_ALL, $restore);
+	}
+
+}

Added: trunk/src/Web/cake/tests/cases/libs/model/models.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/model/models.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/model/models.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,3675 @@
+<?php
+/**
+ * Mock models file
+ *
+ * Mock classes for use in Model and related test cases
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ * @since         CakePHP(tm) v 1.2.0.6464
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) {
+	define('CAKEPHP_UNIT_TEST_EXECUTION', 1);
+}
+
+/**
+ * Test class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Test extends CakeTestModel {
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * name property
+ *
+ * @var string 'Test'
+ * @access public
+ */
+	var $name = 'Test';
+
+/**
+ * schema property
+ *
+ * @var array
+ * @access protected
+ */
+	var $_schema = array(
+		'id'=> array('type' => 'integer', 'null' => '', 'default' => '1', 'length' => '8', 'key'=>'primary'),
+		'name'=> array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+		'email'=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+		'notes'=> array('type' => 'text', 'null' => '1', 'default' => 'write some notes here', 'length' => ''),
+		'created'=> array('type' => 'date', 'null' => '1', 'default' => '', 'length' => ''),
+		'updated'=> array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => null)
+	);
+}
+
+/**
+ * TestAlias class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class TestAlias extends CakeTestModel {
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * name property
+ *
+ * @var string 'TestAlias'
+ * @access public
+ */
+	var $name = 'TestAlias';
+
+/**
+ * alias property
+ *
+ * @var string 'TestAlias'
+ * @access public
+ */
+	var $alias = 'TestAlias';
+
+/**
+ * schema property
+ *
+ * @var array
+ * @access protected
+ */
+	var $_schema = array(
+		'id'=> array('type' => 'integer', 'null' => '', 'default' => '1', 'length' => '8', 'key'=>'primary'),
+		'name'=> array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+		'email'=> array('type' => 'string', 'null' => '1', 'default' => '', 'length' => '155'),
+		'notes'=> array('type' => 'text', 'null' => '1', 'default' => 'write some notes here', 'length' => ''),
+		'created'=> array('type' => 'date', 'null' => '1', 'default' => '', 'length' => ''),
+		'updated'=> array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => null)
+	);
+}
+
+/**
+ * TestValidate class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class TestValidate extends CakeTestModel {
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * name property
+ *
+ * @var string 'TestValidate'
+ * @access public
+ */
+	var $name = 'TestValidate';
+
+/**
+ * schema property
+ *
+ * @var array
+ * @access protected
+ */
+	var $_schema = array(
+		'id' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8'),
+		'title' => array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+		'body' => array('type' => 'string', 'null' => '1', 'default' => '', 'length' => ''),
+		'number' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8'),
+		'created' => array('type' => 'date', 'null' => '1', 'default' => '', 'length' => ''),
+		'modified' => array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => null)
+	);
+
+/**
+ * validateNumber method
+ *
+ * @param mixed $value
+ * @param mixed $options
+ * @access public
+ * @return void
+ */
+	function validateNumber($value, $options) {
+		$options = array_merge(array('min' => 0, 'max' => 100), $options);
+		$valid = ($value['number'] >= $options['min'] && $value['number'] <= $options['max']);
+		return $valid;
+	}
+
+/**
+ * validateTitle method
+ *
+ * @param mixed $value
+ * @access public
+ * @return void
+ */
+	function validateTitle($value) {
+		return (!empty($value) && strpos(low($value['title']), 'title-') === 0);
+	}
+}
+
+/**
+ * User class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class User extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'User'
+ * @access public
+ */
+	var $name = 'User';
+
+/**
+ * validate property
+ *
+ * @var array
+ * @access public
+ */
+	var $validate = array('user' => 'notEmpty', 'password' => 'notEmpty');
+}
+
+/**
+ * Article class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Article extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Article'
+ * @access public
+ */
+	var $name = 'Article';
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('User');
+
+/**
+ * hasMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasMany = array('Comment' => array('dependent' => true));
+
+/**
+ * hasAndBelongsToMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('Tag');
+
+/**
+ * validate property
+ *
+ * @var array
+ * @access public
+ */
+	var $validate = array('user_id' => 'numeric', 'title' => array('allowEmpty' => false, 'rule' => 'notEmpty'), 'body' => 'notEmpty');
+
+/**
+ * beforeSaveReturn property
+ *
+ * @var bool true
+ * @access public
+ */
+	var $beforeSaveReturn = true;
+
+/**
+ * beforeSave method
+ *
+ * @access public
+ * @return void
+ */
+	function beforeSave() {
+		return $this->beforeSaveReturn;
+	}
+
+/**
+ * titleDuplicate method
+ *
+ * @param mixed $title
+ * @access public
+ * @return void
+ */
+	function titleDuplicate ($title) {
+		if ($title === 'My Article Title') {
+			return false;
+		}
+		return true;
+	}
+}
+
+/**
+ * Model stub for beforeDelete testing
+ *
+ * @see #250
+ * @package cake.tests
+ */
+class BeforeDeleteComment extends CakeTestModel {
+	var $name = 'BeforeDeleteComment';
+
+	var $useTable = 'comments';
+
+	function beforeDelete($cascade = true) {
+		$db =& $this->getDataSource();
+		$db->delete($this, array($this->alias . '.' . $this->primaryKey => array(1, 3)));
+		return true;
+	}
+}
+
+/**
+ * NumericArticle class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class NumericArticle extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'NumericArticle'
+ * @access public
+ */
+	var $name = 'NumericArticle';
+
+/**
+ * useTable property
+ *
+ * @var string 'numeric_articles'
+ * @access public
+ */
+	var $useTable = 'numeric_articles';
+}
+
+/**
+ * Article10 class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Article10 extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Article10'
+ * @access public
+ */
+	var $name = 'Article10';
+
+/**
+ * useTable property
+ *
+ * @var string 'articles'
+ * @access public
+ */
+	var $useTable = 'articles';
+
+/**
+ * hasMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasMany = array('Comment' => array('dependent' => true, 'exclusive' => true));
+}
+
+/**
+ * ArticleFeatured class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class ArticleFeatured extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'ArticleFeatured'
+ * @access public
+ */
+	var $name = 'ArticleFeatured';
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('User', 'Category');
+
+/**
+ * hasOne property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasOne = array('Featured');
+
+/**
+ * hasMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasMany = array('Comment' => array('className' => 'Comment', 'dependent' => true));
+
+/**
+ * hasAndBelongsToMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('Tag');
+
+/**
+ * validate property
+ *
+ * @var array
+ * @access public
+ */
+	var $validate = array('user_id' => 'numeric', 'title' => 'notEmpty', 'body' => 'notEmpty');
+}
+
+/**
+ * Featured class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Featured extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Featured'
+ * @access public
+ */
+	var $name = 'Featured';
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('ArticleFeatured', 'Category');
+}
+
+/**
+ * Tag class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Tag extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Tag'
+ * @access public
+ */
+	var $name = 'Tag';
+}
+
+/**
+ * ArticlesTag class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class ArticlesTag extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'ArticlesTag'
+ * @access public
+ */
+	var $name = 'ArticlesTag';
+}
+
+/**
+ * ArticleFeaturedsTag class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class ArticleFeaturedsTag extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'ArticleFeaturedsTag'
+ * @access public
+ */
+	var $name = 'ArticleFeaturedsTag';
+}
+
+/**
+ * Comment class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Comment extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Comment'
+ * @access public
+ */
+	var $name = 'Comment';
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('Article', 'User');
+
+/**
+ * hasOne property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasOne = array('Attachment' => array('dependent' => true));
+}
+
+/**
+ * Modified Comment Class has afterFind Callback
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class ModifiedComment extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Comment'
+ * @access public
+ */
+	var $name = 'Comment';
+
+/**
+ * useTable property
+ *
+ * @var string 'comments'
+ * @access public
+ */
+	var $useTable = 'comments';
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('Article');
+
+/**
+ * afterFind callback
+ *
+ * @return void
+ */
+	function afterFind($results) {
+		if (isset($results[0])) {
+			$results[0]['Comment']['callback'] = 'Fire';
+		}
+		return $results;
+	}
+}
+
+/**
+ * Modified Comment Class has afterFind Callback
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class AgainModifiedComment extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Comment'
+ * @access public
+ */
+	var $name = 'Comment';
+
+/**
+ * useTable property
+ *
+ * @var string 'comments'
+ * @access public
+ */
+	var $useTable = 'comments';
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('Article');
+
+/**
+ * afterFind callback
+ *
+ * @return void
+ */
+	function afterFind($results) {
+		if (isset($results[0])) {
+			$results[0]['Comment']['querytype'] = $this->findQueryType;
+		}
+		return $results;
+	}
+}
+
+/**
+ * MergeVarPluginAppModel class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class MergeVarPluginAppModel extends AppModel {
+
+/**
+ * actsAs parameter
+ *
+ * @var array
+ */
+	var $actsAs = array(
+		'Containable'
+	);
+}
+
+/**
+ * MergeVarPluginPost class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class MergeVarPluginPost extends MergeVarPluginAppModel {
+
+/**
+ * actsAs parameter
+ *
+ * @var array
+ */
+	var $actsAs = array(
+		'Tree'
+	);
+
+/**
+ * useTable parameter
+ *
+ * @var string
+ */
+	var $useTable = 'posts';
+}
+
+/**
+ * MergeVarPluginComment class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class MergeVarPluginComment extends MergeVarPluginAppModel {
+
+/**
+ * actsAs parameter
+ *
+ * @var array
+ */
+	var $actsAs = array(
+		'Containable' => array('some_settings')
+	);
+
+/**
+ * useTable parameter
+ *
+ * @var string
+ */
+	var $useTable = 'comments';
+}
+
+
+/**
+ * Attachment class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Attachment extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Attachment'
+ * @access public
+ */
+	var $name = 'Attachment';
+}
+
+/**
+ * Category class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Category extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Category'
+ * @access public
+ */
+	var $name = 'Category';
+}
+
+/**
+ * CategoryThread class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class CategoryThread extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'CategoryThread'
+ * @access public
+ */
+	var $name = 'CategoryThread';
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('ParentCategory' => array('className' => 'CategoryThread', 'foreignKey' => 'parent_id'));
+}
+
+/**
+ * Apple class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Apple extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Apple'
+ * @access public
+ */
+	var $name = 'Apple';
+
+/**
+ * validate property
+ *
+ * @var array
+ * @access public
+ */
+	var $validate = array('name' => 'notEmpty');
+
+/**
+ * hasOne property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasOne = array('Sample');
+
+/**
+ * hasMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasMany = array('Child' => array('className' => 'Apple', 'dependent' => true));
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('Parent' => array('className' => 'Apple', 'foreignKey' => 'apple_id'));
+}
+
+/**
+ * Sample class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Sample extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Sample'
+ * @access public
+ */
+	var $name = 'Sample';
+
+/**
+ * belongsTo property
+ *
+ * @var string 'Apple'
+ * @access public
+ */
+	var $belongsTo = 'Apple';
+}
+
+/**
+ * AnotherArticle class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class AnotherArticle extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'AnotherArticle'
+ * @access public
+ */
+	var $name = 'AnotherArticle';
+
+/**
+ * hasMany property
+ *
+ * @var string 'Home'
+ * @access public
+ */
+	var $hasMany = 'Home';
+}
+
+/**
+ * Advertisement class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Advertisement extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Advertisement'
+ * @access public
+ */
+	var $name = 'Advertisement';
+
+/**
+ * hasMany property
+ *
+ * @var string 'Home'
+ * @access public
+ */
+	var $hasMany = 'Home';
+}
+
+/**
+ * Home class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Home extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Home'
+ * @access public
+ */
+	var $name = 'Home';
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('AnotherArticle', 'Advertisement');
+}
+
+/**
+ * Post class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Post extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Post'
+ * @access public
+ */
+	var $name = 'Post';
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('Author');
+
+	function beforeFind($queryData) {
+		if (isset($queryData['connection'])) {
+			$this->useDbConfig = $queryData['connection'];
+		}
+		return true;
+	}
+
+	function afterFind($results) {
+		$this->useDbConfig = 'test_suite';
+		return $results;
+	}
+}
+
+/**
+ * Author class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Author extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Author'
+ * @access public
+ */
+	var $name = 'Author';
+
+/**
+ * hasMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasMany = array('Post');
+
+/**
+ * afterFind method
+ *
+ * @param mixed $results
+ * @access public
+ * @return void
+ */
+	function afterFind($results) {
+		$results[0]['Author']['test'] = 'working';
+		return $results;
+	}
+}
+
+/**
+ * ModifiedAuthor class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class ModifiedAuthor extends Author {
+
+/**
+ * name property
+ *
+ * @var string 'Author'
+ * @access public
+ */
+	var $name = 'Author';
+
+/**
+ * afterFind method
+ *
+ * @param mixed $results
+ * @access public
+ * @return void
+ */
+	function afterFind($results) {
+		foreach($results as $index => $result) {
+			$results[$index]['Author']['user'] .= ' (CakePHP)';
+		}
+		return $results;
+	}
+}
+
+/**
+ * Project class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Project extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Project'
+ * @access public
+ */
+	var $name = 'Project';
+
+/**
+ * hasMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasMany = array('Thread');
+}
+
+/**
+ * Thread class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Thread extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Thread'
+ * @access public
+ */
+	var $name = 'Thread';
+
+/**
+ * hasMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('Project');
+
+/**
+ * hasMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasMany = array('Message');
+}
+
+/**
+ * Message class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Message extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Message'
+ * @access public
+ */
+	var $name = 'Message';
+
+/**
+ * hasOne property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasOne = array('Bid');
+}
+
+/**
+ * Bid class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Bid extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Bid'
+ * @access public
+ */
+	var $name = 'Bid';
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('Message');
+}
+
+/**
+ * BiddingMessage class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class BiddingMessage extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'BiddingMessage'
+ * @access public
+ */
+	var $name = 'BiddingMessage';
+
+/**
+ * primaryKey property
+ *
+ * @var string 'bidding'
+ * @access public
+ */
+	var $primaryKey = 'bidding';
+
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array(
+		'Bidding' => array(
+			'foreignKey' => false,
+			'conditions' => array('BiddingMessage.bidding = Bidding.bid')
+		)
+	);
+}
+
+/**
+ * Bidding class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Bidding extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Bidding'
+ * @access public
+ */
+	var $name = 'Bidding';
+
+/**
+ * hasOne property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasOne = array(
+		'BiddingMessage' => array(
+			'foreignKey' => false,
+			'conditions' => array('BiddingMessage.bidding = Bidding.bid'),
+			'dependent' => true
+		)
+	);
+}
+
+/**
+ * NodeAfterFind class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class NodeAfterFind extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'NodeAfterFind'
+ * @access public
+ */
+	var $name = 'NodeAfterFind';
+
+/**
+ * validate property
+ *
+ * @var array
+ * @access public
+ */
+	var $validate = array('name' => 'notEmpty');
+
+/**
+ * useTable property
+ *
+ * @var string 'apples'
+ * @access public
+ */
+	var $useTable = 'apples';
+
+/**
+ * hasOne property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasOne = array('Sample' => array('className' => 'NodeAfterFindSample'));
+
+/**
+ * hasMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasMany = array('Child' => array('className' => 'NodeAfterFind', 'dependent' => true));
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('Parent' => array('className' => 'NodeAfterFind', 'foreignKey' => 'apple_id'));
+
+/**
+ * afterFind method
+ *
+ * @param mixed $results
+ * @access public
+ * @return void
+ */
+	function afterFind($results) {
+		return $results;
+	}
+}
+
+/**
+ * NodeAfterFindSample class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class NodeAfterFindSample extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'NodeAfterFindSample'
+ * @access public
+ */
+	var $name = 'NodeAfterFindSample';
+
+/**
+ * useTable property
+ *
+ * @var string 'samples'
+ * @access public
+ */
+	var $useTable = 'samples';
+
+/**
+ * belongsTo property
+ *
+ * @var string 'NodeAfterFind'
+ * @access public
+ */
+	var $belongsTo = 'NodeAfterFind';
+}
+
+/**
+ * NodeNoAfterFind class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class NodeNoAfterFind extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'NodeAfterFind'
+ * @access public
+ */
+	var $name = 'NodeAfterFind';
+
+/**
+ * validate property
+ *
+ * @var array
+ * @access public
+ */
+	var $validate = array('name' => 'notEmpty');
+
+/**
+ * useTable property
+ *
+ * @var string 'apples'
+ * @access public
+ */
+	var $useTable = 'apples';
+
+/**
+ * hasOne property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasOne = array('Sample' => array('className' => 'NodeAfterFindSample'));
+
+/**
+ * hasMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasMany = array('Child' => array('className' => 'NodeAfterFind', 'dependent' => true));
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('Parent' => array('className' => 'NodeAfterFind', 'foreignKey' => 'apple_id'));
+}
+
+/**
+ * Node class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Node extends CakeTestModel{
+
+/**
+ * name property
+ *
+ * @var string 'Node'
+ * @access public
+ */
+	var $name = 'Node';
+
+/**
+ * hasAndBelongsToMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array(
+		'ParentNode' => array(
+			'className' => 'Node',
+			'joinTable' => 'dependency',
+			'with' => 'Dependency',
+			'foreignKey' => 'child_id',
+			'associationForeignKey' => 'parent_id',
+		)
+	);
+}
+
+/**
+ * Dependency class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Dependency extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Dependency'
+ * @access public
+ */
+	var $name = 'Dependency';
+}
+
+/**
+ * ModelA class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class ModelA extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'ModelA'
+ * @access public
+ */
+	var $name = 'ModelA';
+
+/**
+ * useTable property
+ *
+ * @var string 'apples'
+ * @access public
+ */
+	var $useTable = 'apples';
+
+/**
+ * hasMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasMany = array('ModelB', 'ModelC');
+}
+
+/**
+ * ModelB class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class ModelB extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'ModelB'
+ * @access public
+ */
+	var $name = 'ModelB';
+
+/**
+ * useTable property
+ *
+ * @var string 'messages'
+ * @access public
+ */
+	var $useTable = 'messages';
+
+/**
+ * hasMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasMany = array('ModelD');
+}
+
+/**
+ * ModelC class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class ModelC extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'ModelC'
+ * @access public
+ */
+	var $name = 'ModelC';
+
+/**
+ * useTable property
+ *
+ * @var string 'bids'
+ * @access public
+ */
+	var $useTable = 'bids';
+
+/**
+ * hasMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasMany = array('ModelD');
+}
+
+/**
+ * ModelD class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class ModelD extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'ModelD'
+ * @access public
+ */
+	var $name = 'ModelD';
+
+/**
+ * useTable property
+ *
+ * @var string 'threads'
+ * @access public
+ */
+	var $useTable = 'threads';
+}
+
+/**
+ * Something class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Something extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Something'
+ * @access public
+ */
+	var $name = 'Something';
+
+/**
+ * hasAndBelongsToMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('SomethingElse' => array('with' => array('JoinThing' => array('doomed'))));
+}
+
+/**
+ * SomethingElse class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class SomethingElse extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'SomethingElse'
+ * @access public
+ */
+	var $name = 'SomethingElse';
+
+/**
+ * hasAndBelongsToMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('Something' => array('with' => 'JoinThing'));
+}
+
+/**
+ * JoinThing class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class JoinThing extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'JoinThing'
+ * @access public
+ */
+	var $name = 'JoinThing';
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('Something', 'SomethingElse');
+}
+
+/**
+ * Portfolio class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Portfolio extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Portfolio'
+ * @access public
+ */
+	var $name = 'Portfolio';
+
+/**
+ * hasAndBelongsToMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('Item');
+}
+
+/**
+ * Item class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Item extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Item'
+ * @access public
+ */
+	var $name = 'Item';
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('Syfile' => array('counterCache' => true));
+
+/**
+ * hasAndBelongsToMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('Portfolio' => array('unique' => false));
+}
+
+/**
+ * ItemsPortfolio class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class ItemsPortfolio extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'ItemsPortfolio'
+ * @access public
+ */
+	var $name = 'ItemsPortfolio';
+}
+
+/**
+ * Syfile class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Syfile extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Syfile'
+ * @access public
+ */
+	var $name = 'Syfile';
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('Image');
+}
+
+/**
+ * Image class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Image extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Image'
+ * @access public
+ */
+	var $name = 'Image';
+}
+
+/**
+ * DeviceType class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class DeviceType extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'DeviceType'
+ * @access public
+ */
+	var $name = 'DeviceType';
+
+/**
+ * order property
+ *
+ * @var array
+ * @access public
+ */
+	var $order = array('DeviceType.order' => 'ASC');
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array(
+		'DeviceTypeCategory', 'FeatureSet', 'ExteriorTypeCategory',
+		'Image' => array('className' => 'Document'),
+		'Extra1' => array('className' => 'Document'),
+		'Extra2' => array('className' => 'Document'));
+
+/**
+ * hasMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasMany = array('Device' => array('order' => array('Device.id' => 'ASC')));
+}
+
+/**
+ * DeviceTypeCategory class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class DeviceTypeCategory extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'DeviceTypeCategory'
+ * @access public
+ */
+	var $name = 'DeviceTypeCategory';
+}
+
+/**
+ * FeatureSet class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class FeatureSet extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'FeatureSet'
+ * @access public
+ */
+	var $name = 'FeatureSet';
+}
+
+/**
+ * ExteriorTypeCategory class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class ExteriorTypeCategory extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'ExteriorTypeCategory'
+ * @access public
+ */
+	var $name = 'ExteriorTypeCategory';
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('Image' => array('className' => 'Device'));
+}
+
+/**
+ * Document class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Document extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Document'
+ * @access public
+ */
+	var $name = 'Document';
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('DocumentDirectory');
+}
+
+/**
+ * Device class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Device extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Device'
+ * @access public
+ */
+	var $name = 'Device';
+}
+
+/**
+ * DocumentDirectory class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class DocumentDirectory extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'DocumentDirectory'
+ * @access public
+ */
+	var $name = 'DocumentDirectory';
+}
+
+/**
+ * PrimaryModel class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class PrimaryModel extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'PrimaryModel'
+ * @access public
+ */
+	var $name = 'PrimaryModel';
+}
+
+/**
+ * SecondaryModel class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class SecondaryModel extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'SecondaryModel'
+ * @access public
+ */
+	var $name = 'SecondaryModel';
+}
+
+/**
+ * JoinA class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class JoinA extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'JoinA'
+ * @access public
+ */
+	var $name = 'JoinA';
+
+/**
+ * hasAndBelongsToMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('JoinB', 'JoinC');
+}
+
+/**
+ * JoinB class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class JoinB extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'JoinB'
+ * @access public
+ */
+	var $name = 'JoinB';
+
+/**
+ * hasAndBelongsToMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('JoinA');
+}
+
+/**
+ * JoinC class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class JoinC extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'JoinC'
+ * @access public
+ */
+	var $name = 'JoinC';
+
+/**
+ * hasAndBelongsToMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('JoinA');
+}
+
+/**
+ * ThePaper class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class ThePaper extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'ThePaper'
+ * @access public
+ */
+	var $name = 'ThePaper';
+
+/**
+ * useTable property
+ *
+ * @var string 'apples'
+ * @access public
+ */
+	var $useTable = 'apples';
+
+/**
+ * hasOne property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasOne = array('Itself' => array('className' => 'ThePaper', 'foreignKey' => 'apple_id'));
+
+/**
+ * hasAndBelongsToMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('Monkey' => array('joinTable' => 'the_paper_monkies', 'order' => 'id'));
+}
+
+/**
+ * Monkey class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Monkey extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Monkey'
+ * @access public
+ */
+	var $name = 'Monkey';
+
+/**
+ * useTable property
+ *
+ * @var string 'devices'
+ * @access public
+ */
+	var $useTable = 'devices';
+}
+
+/**
+ * AssociationTest1 class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class AssociationTest1 extends CakeTestModel {
+
+/**
+ * useTable property
+ *
+ * @var string 'join_as'
+ * @access public
+ */
+	var $useTable = 'join_as';
+
+/**
+ * name property
+ *
+ * @var string 'AssociationTest1'
+ * @access public
+ */
+	var $name = 'AssociationTest1';
+
+/**
+ * hasAndBelongsToMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('AssociationTest2' => array(
+		'unique' => false, 'joinTable' => 'join_as_join_bs', 'foreignKey' => false
+	));
+}
+
+/**
+ * AssociationTest2 class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class AssociationTest2 extends CakeTestModel {
+
+/**
+ * useTable property
+ *
+ * @var string 'join_bs'
+ * @access public
+ */
+	var $useTable = 'join_bs';
+
+/**
+ * name property
+ *
+ * @var string 'AssociationTest2'
+ * @access public
+ */
+	var $name = 'AssociationTest2';
+
+/**
+ * hasAndBelongsToMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('AssociationTest1' => array(
+		'unique' => false, 'joinTable' => 'join_as_join_bs'
+	));
+}
+
+/**
+ * Callback class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Callback extends CakeTestModel {
+
+}
+/**
+ * CallbackPostTestModel class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class CallbackPostTestModel extends CakeTestModel {
+	var $useTable = 'posts';
+/**
+ * variable to control return of beforeValidate
+ *
+ * @var string
+ */
+	var $beforeValidateReturn = true;
+/**
+ * variable to control return of beforeSave
+ *
+ * @var string
+ */
+	var $beforeSaveReturn = true;
+/**
+ * variable to control return of beforeDelete
+ *
+ * @var string
+ */
+	var $beforeDeleteReturn = true;
+/**
+ * beforeSave callback
+ *
+ * @return void
+ */
+	function beforeSave($options) {
+		return $this->beforeSaveReturn;
+	}
+/**
+ * beforeValidate callback
+ *
+ * @return void
+ */
+	function beforeValidate($options) {
+		return $this->beforeValidateReturn;
+	}
+/**
+ * beforeDelete callback
+ *
+ * @return void
+ */
+	function beforeDelete($cascade = true) {
+		return $this->beforeDeleteReturn;
+	}
+}
+
+/**
+ * Uuid class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Uuid extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Uuid'
+ * @access public
+ */
+	var $name = 'Uuid';
+}
+
+/**
+ * DataTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class DataTest extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'DataTest'
+ * @access public
+ */
+	var $name = 'DataTest';
+}
+
+/**
+ * TheVoid class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class TheVoid extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'TheVoid'
+ * @access public
+ */
+	var $name = 'TheVoid';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+}
+
+/**
+ * ValidationTest1 class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class ValidationTest1 extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'ValidationTest'
+ * @access public
+ */
+	var $name = 'ValidationTest1';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * schema property
+ *
+ * @var array
+ * @access protected
+ */
+	var $_schema = array();
+
+/**
+ * validate property
+ *
+ * @var array
+ * @access public
+ */
+	var $validate = array(
+		'title' => 'notEmpty',
+		'published' => 'customValidationMethod',
+		'body' => array(
+			'notEmpty',
+			'/^.{5,}$/s' => 'no matchy',
+			'/^[0-9A-Za-z \\.]{1,}$/s'
+		)
+	);
+
+/**
+ * customValidationMethod method
+ *
+ * @param mixed $data
+ * @access public
+ * @return void
+ */
+	function customValidationMethod($data) {
+		return $data === 1;
+	}
+
+/**
+ * Custom validator with parameters + default values
+ *
+ * @access public
+ * @return array
+ */
+	function customValidatorWithParams($data, $validator, $or = true, $ignore_on_same = 'id') {
+		$this->validatorParams = get_defined_vars();
+		unset($this->validatorParams['this']);
+		return true;
+	}
+
+/**
+ * Custom validator with messaage
+ *
+ * @access public
+ * @return array
+ */
+	function customValidatorWithMessage($data) {
+		return 'This field will *never* validate! Muhahaha!';
+	}
+/**
+ * Test validation with many parameters
+ *
+ * @return void
+ */
+	function customValidatorWithSixParams($data, $one = 1, $two = 2, $three = 3, $four = 4, $five = 5, $six = 6) {
+		$this->validatorParams = get_defined_vars();
+		unset($this->validatorParams['this']);
+		return true;
+	}
+}
+
+/**
+ * ValidationTest2 class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class ValidationTest2 extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'ValidationTest2'
+ * @access public
+ */
+	var $name = 'ValidationTest2';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * validate property
+ *
+ * @var array
+ * @access public
+ */
+	var $validate = array(
+		'title' => 'notEmpty',
+		'published' => 'customValidationMethod',
+		'body' => array(
+			'notEmpty',
+			'/^.{5,}$/s' => 'no matchy',
+			'/^[0-9A-Za-z \\.]{1,}$/s'
+		)
+	);
+
+/**
+ * customValidationMethod method
+ *
+ * @param mixed $data
+ * @access public
+ * @return void
+ */
+	function customValidationMethod($data) {
+		return $data === 1;
+	}
+
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function schema() {
+		return array();
+	}
+}
+
+/**
+ * Person class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Person extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Person'
+ * @access public
+ */
+	var $name = 'Person';
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array(
+			'Mother' => array(
+				'className' => 'Person',
+				'foreignKey' => 'mother_id'),
+			'Father' => array(
+				'className' => 'Person',
+				'foreignKey' => 'father_id'));
+}
+
+/**
+ * UnderscoreField class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class UnderscoreField extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'UnderscoreField'
+ * @access public
+ */
+	var $name = 'UnderscoreField';
+}
+
+/**
+ * Product class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Product extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Product'
+ * @access public
+ */
+	var $name = 'Product';
+}
+
+/**
+ * Story class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Story extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Story'
+ * @access public
+ */
+	var $name = 'Story';
+
+/**
+ * primaryKey property
+ *
+ * @var string 'story'
+ * @access public
+ */
+	var $primaryKey = 'story';
+
+/**
+ * hasAndBelongsToMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('Tag' => array('foreignKey' => 'story'));
+
+/**
+ * validate property
+ *
+ * @var array
+ * @access public
+ */
+	var $validate = array('title' => 'notEmpty');
+}
+
+/**
+ * Cd class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Cd extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Cd'
+ * @access public
+ */
+	var $name = 'Cd';
+
+/**
+ * hasOne property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasOne = array('OverallFavorite' => array('foreignKey' => 'model_id', 'dependent' => true, 'conditions' => array('model_type' => 'Cd')));
+}
+
+/**
+ * Book class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Book extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Book'
+ * @access public
+ */
+	var $name = 'Book';
+
+/**
+ * hasOne property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasOne = array('OverallFavorite' => array('foreignKey' => 'model_id', 'dependent' => true, 'conditions' => 'OverallFavorite.model_type = \'Book\''));
+}
+
+/**
+ * OverallFavorite class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class OverallFavorite extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'OverallFavorite'
+ * @access public
+ */
+	var $name = 'OverallFavorite';
+}
+
+/**
+ * MyUser class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class MyUser extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'MyUser'
+ * @access public
+ */
+	var $name = 'MyUser';
+
+/**
+ * undocumented variable
+ *
+ * @var string
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('MyCategory');
+}
+
+/**
+ * MyCategory class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class MyCategory extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'MyCategory'
+ * @access public
+ */
+	var $name = 'MyCategory';
+
+/**
+ * undocumented variable
+ *
+ * @var string
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('MyProduct', 'MyUser');
+}
+
+/**
+ * MyProduct class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class MyProduct extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'MyProduct'
+ * @access public
+ */
+	var $name = 'MyProduct';
+
+/**
+ * undocumented variable
+ *
+ * @var string
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('MyCategory');
+}
+
+/**
+ * MyCategoriesMyUser class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class MyCategoriesMyUser extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'MyCategoriesMyUser'
+ * @access public
+ */
+	var $name = 'MyCategoriesMyUser';
+}
+
+/**
+ * MyCategoriesMyProduct class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class MyCategoriesMyProduct extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'MyCategoriesMyProduct'
+ * @access public
+ */
+	var $name = 'MyCategoriesMyProduct';
+}
+
+/**
+ * I18nModel class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class I18nModel extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'I18nModel'
+ * @access public
+ */
+	var $name = 'I18nModel';
+
+/**
+ * useTable property
+ *
+ * @var string 'i18n'
+ * @access public
+ */
+	var $useTable = 'i18n';
+
+/**
+ * displayField property
+ *
+ * @var string 'field'
+ * @access public
+ */
+	var $displayField = 'field';
+}
+
+/**
+ * NumberTree class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class NumberTree extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'NumberTree'
+ * @access public
+ */
+	var $name = 'NumberTree';
+
+/**
+ * actsAs property
+ *
+ * @var array
+ * @access public
+ */
+	var $actsAs = array('Tree');
+
+/**
+ * initialize method
+ *
+ * @param int $levelLimit
+ * @param int $childLimit
+ * @param mixed $currentLevel
+ * @param mixed $parent_id
+ * @param string $prefix
+ * @param bool $hierachial
+ * @access public
+ * @return void
+ */
+	function initialize($levelLimit = 3, $childLimit = 3, $currentLevel = null, $parent_id = null, $prefix = '1', $hierachial = true) {
+		if (!$parent_id) {
+			$db =& ConnectionManager::getDataSource($this->useDbConfig);
+			$db->truncate($this->table);
+			$this->save(array($this->name => array('name' => '1. Root')));
+			$this->initialize($levelLimit, $childLimit, 1, $this->id, '1', $hierachial);
+			$this->create(array());
+		}
+
+		if (!$currentLevel || $currentLevel > $levelLimit) {
+			return;
+		}
+
+		for ($i = 1; $i <= $childLimit; $i++) {
+			$name = $prefix . '.' . $i;
+			$data = array($this->name => array('name' => $name));
+			$this->create($data);
+
+			if ($hierachial) {
+				if ($this->name == 'UnconventionalTree') {
+					$data[$this->name]['join'] = $parent_id;
+				} else {
+					$data[$this->name]['parent_id'] = $parent_id;
+				}
+			}
+			$this->save($data);
+			$this->initialize($levelLimit, $childLimit, $currentLevel + 1, $this->id, $name, $hierachial);
+		}
+	}
+}
+
+/**
+ * NumberTreeTwo class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class NumberTreeTwo extends NumberTree {
+
+/**
+ * name property
+ *
+ * @var string 'NumberTree'
+ * @access public
+ */
+	var $name = 'NumberTreeTwo';
+
+/**
+ * actsAs property
+ *
+ * @var array
+ * @access public
+ */
+	var $actsAs = array();
+}
+
+/**
+ * FlagTree class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class FlagTree extends NumberTree {
+
+/**
+ * name property
+ *
+ * @var string 'FlagTree'
+ * @access public
+ */
+	var $name = 'FlagTree';
+}
+
+/**
+ * UnconventionalTree class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class UnconventionalTree extends NumberTree {
+
+/**
+ * name property
+ *
+ * @var string 'FlagTree'
+ * @access public
+ */
+	var $name = 'UnconventionalTree';
+	var $actsAs = array(
+		'Tree' => array(
+			'parent' => 'join',
+			'left'  => 'left',
+			'right' => 'right'
+		)
+	);
+}
+
+/**
+ * UuidTree class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class UuidTree extends NumberTree {
+
+/**
+ * name property
+ *
+ * @var string 'FlagTree'
+ * @access public
+ */
+	var $name = 'UuidTree';
+}
+
+/**
+ * Campaign class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Campaign extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Campaign'
+ * @access public
+ */
+	var $name = 'Campaign';
+
+/**
+ * hasMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasMany = array('Ad' => array('fields' => array('id','campaign_id','name')));
+}
+
+/**
+ * Ad class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Ad extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Ad'
+ * @access public
+ */
+	var $name = 'Ad';
+
+/**
+ * actsAs property
+ *
+ * @var array
+ * @access public
+ */
+	var $actsAs = array('Tree');
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('Campaign');
+}
+
+/**
+ * AfterTree class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class AfterTree extends NumberTree {
+
+/**
+ * name property
+ *
+ * @var string 'AfterTree'
+ * @access public
+ */
+	var $name = 'AfterTree';
+
+/**
+ * actsAs property
+ *
+ * @var array
+ * @access public
+ */
+	var $actsAs = array('Tree');
+
+	function afterSave($created) {
+		if ($created && isset($this->data['AfterTree'])) {
+			$this->data['AfterTree']['name'] = 'Six and One Half Changed in AfterTree::afterSave() but not in database';
+		}
+	}
+}
+
+/**
+ * Nonconformant Content class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Content extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Content'
+ * @access public
+ */
+	var $name = 'Content';
+
+/**
+ * useTable property
+ *
+ * @var string 'Content'
+ * @access public
+ */
+	var $useTable = 'Content';
+
+/**
+ * primaryKey property
+ *
+ * @var string 'iContentId'
+ * @access public
+ */
+	var $primaryKey = 'iContentId';
+
+/**
+ * hasAndBelongsToMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('Account' => array('className' => 'Account', 'with' => 'ContentAccount', 'joinTable' => 'ContentAccounts', 'foreignKey' => 'iContentId', 'associationForeignKey', 'iAccountId'));
+}
+
+/**
+ * Nonconformant Account class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Account extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Account'
+ * @access public
+ */
+	var $name = 'Account';
+
+/**
+ * useTable property
+ *
+ * @var string 'Account'
+ * @access public
+ */
+	var $useTable = 'Accounts';
+
+/**
+ * primaryKey property
+ *
+ * @var string 'iAccountId'
+ * @access public
+ */
+	var $primaryKey = 'iAccountId';
+}
+
+/**
+ * Nonconformant ContentAccount class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class ContentAccount extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Account'
+ * @access public
+ */
+	var $name = 'ContentAccount';
+
+/**
+ * useTable property
+ *
+ * @var string 'Account'
+ * @access public
+ */
+	var $useTable = 'ContentAccounts';
+
+/**
+ * primaryKey property
+ *
+ * @var string 'iAccountId'
+ * @access public
+ */
+	var $primaryKey = 'iContentAccountsId';
+}
+
+/**
+ * FilmFile class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class FilmFile extends CakeTestModel {
+	var $name = 'FilmFile';
+}
+
+/**
+ * Basket test model
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Basket extends CakeTestModel {
+	var $name = 'Basket';
+
+	var $belongsTo = array(
+		'FilmFile' => array(
+			'className' => 'FilmFile',
+			'foreignKey' => 'object_id',
+			'conditions' => "Basket.type = 'file'",
+			'fields' => '',
+			'order' => ''
+		)
+	);
+}
+
+/**
+ * TestPluginArticle class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class TestPluginArticle extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'TestPluginArticle'
+ * @access public
+ */
+	var $name = 'TestPluginArticle';
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('User');
+
+/**
+ * hasMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasMany = array(
+		'TestPluginComment' => array(
+			'className' => 'TestPlugin.TestPluginComment',
+			'foreignKey' => 'article_id',
+			'dependent' => true
+		)
+	);
+}
+
+/**
+ * TestPluginComment class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class TestPluginComment extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'TestPluginComment'
+ * @access public
+ */
+	var $name = 'TestPluginComment';
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array(
+		'TestPluginArticle' => array(
+			'className' => 'TestPlugin.TestPluginArticle',
+			'foreignKey' => 'article_id',
+		),
+		'User'
+	);
+}
+
+/**
+ * Uuidportfolio class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Uuidportfolio extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Uuidportfolio'
+ * @access public
+ */
+	var $name = 'Uuidportfolio';
+
+/**
+ * hasAndBelongsToMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('Uuiditem');
+}
+
+/**
+ * Uuiditem class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class Uuiditem extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Item'
+ * @access public
+ */
+	var $name = 'Uuiditem';
+
+/**
+ * hasAndBelongsToMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('Uuidportfolio' => array('with' => 'UuiditemsUuidportfolioNumericid'));
+
+}
+
+/**
+ * UuiditemsPortfolio class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class UuiditemsUuidportfolio extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'ItemsPortfolio'
+ * @access public
+ */
+	var $name = 'UuiditemsUuidportfolio';
+}
+
+/**
+ * UuiditemsPortfolioNumericid class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class UuiditemsUuidportfolioNumericid extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string
+ * @access public
+ */
+	var $name = 'UuiditemsUuidportfolioNumericid';
+}
+
+/**
+ * TranslateTestModel class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class TranslateTestModel extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'TranslateTestModel'
+ * @access public
+ */
+	var $name = 'TranslateTestModel';
+
+/**
+ * useTable property
+ *
+ * @var string 'i18n'
+ * @access public
+ */
+	var $useTable = 'i18n';
+
+/**
+ * displayField property
+ *
+ * @var string 'field'
+ * @access public
+ */
+	var $displayField = 'field';
+}
+
+/**
+ * TranslateTestModel class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class TranslateWithPrefix extends CakeTestModel {
+/**
+ * name property
+ *
+ * @var string 'TranslateTestModel'
+ * @access public
+ */
+	var $name = 'TranslateWithPrefix';
+/**
+ * tablePrefix property
+ *
+ * @var string 'i18n'
+ * @access public
+ */
+	var $tablePrefix = 'i18n_';
+/**
+ * displayField property
+ *
+ * @var string 'field'
+ * @access public
+ */
+	var $displayField = 'field';
+}
+/**
+ * TranslatedItem class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class TranslatedItem extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'TranslatedItem'
+ * @access public
+ */
+	var $name = 'TranslatedItem';
+
+/**
+ * cacheQueries property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $cacheQueries = false;
+
+/**
+ * actsAs property
+ *
+ * @var array
+ * @access public
+ */
+	var $actsAs = array('Translate' => array('content', 'title'));
+
+/**
+ * translateModel property
+ *
+ * @var string 'TranslateTestModel'
+ * @access public
+ */
+	var $translateModel = 'TranslateTestModel';
+}
+
+/**
+ * TranslatedItem class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class TranslatedItem2 extends CakeTestModel {
+/**
+ * name property
+ *
+ * @var string 'TranslatedItem'
+ * @access public
+ */
+	var $name = 'TranslatedItem';
+/**
+ * cacheQueries property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $cacheQueries = false;
+/**
+ * actsAs property
+ *
+ * @var array
+ * @access public
+ */
+	var $actsAs = array('Translate' => array('content', 'title'));
+/**
+ * translateModel property
+ *
+ * @var string 'TranslateTestModel'
+ * @access public
+ */
+	var $translateModel = 'TranslateWithPrefix';
+}
+/**
+ * TranslatedItemWithTable class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class TranslatedItemWithTable extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'TranslatedItemWithTable'
+ * @access public
+ */
+	var $name = 'TranslatedItemWithTable';
+
+/**
+ * useTable property
+ *
+ * @var string 'translated_items'
+ * @access public
+ */
+	var $useTable = 'translated_items';
+
+/**
+ * cacheQueries property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $cacheQueries = false;
+
+/**
+ * actsAs property
+ *
+ * @var array
+ * @access public
+ */
+	var $actsAs = array('Translate' => array('content', 'title'));
+
+/**
+ * translateModel property
+ *
+ * @var string 'TranslateTestModel'
+ * @access public
+ */
+	var $translateModel = 'TranslateTestModel';
+
+/**
+ * translateTable property
+ *
+ * @var string 'another_i18n'
+ * @access public
+ */
+	var $translateTable = 'another_i18n';
+}
+
+/**
+ * TranslateArticleModel class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class TranslateArticleModel extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'TranslateArticleModel'
+ * @access public
+ */
+	var $name = 'TranslateArticleModel';
+
+/**
+ * useTable property
+ *
+ * @var string 'article_i18n'
+ * @access public
+ */
+	var $useTable = 'article_i18n';
+
+/**
+ * displayField property
+ *
+ * @var string 'field'
+ * @access public
+ */
+	var $displayField = 'field';
+}
+
+/**
+ * TranslatedArticle class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.model
+ */
+class TranslatedArticle extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'TranslatedArticle'
+ * @access public
+ */
+	var $name = 'TranslatedArticle';
+
+/**
+ * cacheQueries property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $cacheQueries = false;
+
+/**
+ * actsAs property
+ *
+ * @var array
+ * @access public
+ */
+	var $actsAs = array('Translate' => array('title', 'body'));
+
+/**
+ * translateModel property
+ *
+ * @var string 'TranslateArticleModel'
+ * @access public
+ */
+	var $translateModel = 'TranslateArticleModel';
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('User');
+}
+
+class CounterCacheUser extends CakeTestModel {
+	var $name = 'CounterCacheUser';
+	var $alias = 'User';
+
+	var $hasMany = array('Post' => array(
+		'className' => 'CounterCachePost',
+		'foreignKey' => 'user_id'
+	));
+}
+
+class CounterCachePost extends CakeTestModel {
+	var $name = 'CounterCachePost';
+	var $alias = 'Post';
+
+	var $belongsTo = array('User' => array(
+		'className' => 'CounterCacheUser',
+		'foreignKey' => 'user_id',
+		'counterCache' => true
+	));
+}
+
+class CounterCacheUserNonstandardPrimaryKey extends CakeTestModel {
+	var $name = 'CounterCacheUserNonstandardPrimaryKey';
+	var $alias = 'User';
+    var $primaryKey = 'uid';
+
+	var $hasMany = array('Post' => array(
+		'className' => 'CounterCachePostNonstandardPrimaryKey',
+		'foreignKey' => 'uid'
+	));
+}
+
+class CounterCachePostNonstandardPrimaryKey extends CakeTestModel {
+	var $name = 'CounterCachePostNonstandardPrimaryKey';
+	var $alias = 'Post';
+    var $primaryKey = 'pid';
+
+	var $belongsTo = array('User' => array(
+		'className' => 'CounterCacheUserNonstandardPrimaryKey',
+		'foreignKey' => 'uid',
+		'counterCache' => true
+	));
+}
+
+class ArticleB extends CakeTestModel {
+	var $name = 'ArticleB';
+	var $useTable = 'articles';
+	var $hasAndBelongsToMany = array(
+		'TagB' => array(
+			'className' => 'TagB',
+			'joinTable' => 'articles_tags',
+			'foreignKey' => 'article_id',
+			'associationForeignKey' => 'tag_id'
+		)
+	);
+}
+
+class TagB extends CakeTestModel {
+	var $name = 'TagB';
+	var $useTable = 'tags';
+	var $hasAndBelongsToMany = array(
+		'ArticleB' => array(
+			'className' => 'ArticleB',
+			'joinTable' => 'articles_tags',
+			'foreignKey' => 'tag_id',
+			'associationForeignKey' => 'article_id'
+		)
+	);
+}
+
+class Fruit extends CakeTestModel {
+	var $name = 'Fruit';
+	var $hasAndBelongsToMany = array(
+		'UuidTag' => array(
+			'className' => 'UuidTag',
+			'joinTable' => 'fruits_uuid_tags',
+			'foreignKey' => 'fruit_id',
+			'associationForeignKey' => 'uuid_tag_id',
+			'with' => 'FruitsUuidTag'
+		)
+	);
+}
+
+class FruitsUuidTag extends CakeTestModel {
+	var $name = 'FruitsUuidTag';
+	var $primaryKey = false;
+	var $belongsTo = array(
+		'UuidTag' => array(
+			'className' => 'UuidTag',
+			'foreignKey' => 'uuid_tag_id',
+		),
+		'Fruit' => array(
+			'className' => 'Fruit',
+			'foreignKey' => 'fruit_id',
+		)
+	);
+}
+
+class UuidTag extends CakeTestModel {
+	var $name = 'UuidTag';
+	var $hasAndBelongsToMany = array(
+		'Fruit' => array(
+			'className' => 'Fruit',
+			'joinTable' => 'fruits_uuid_tags',
+			'foreign_key' => 'uuid_tag_id',
+			'associationForeignKey' => 'fruit_id',
+			'with' => 'FruitsUuidTag'
+		)
+	);
+}
+
+class FruitNoWith extends CakeTestModel {
+	var $name = 'Fruit';
+	var $useTable = 'fruits';
+	var $hasAndBelongsToMany = array(
+		'UuidTag' => array(
+			'className' => 'UuidTagNoWith',
+			'joinTable' => 'fruits_uuid_tags',
+			'foreignKey' => 'fruit_id',
+			'associationForeignKey' => 'uuid_tag_id',
+		)
+	);
+}
+
+class UuidTagNoWith extends CakeTestModel {
+	var $name = 'UuidTag';
+	var $useTable = 'uuid_tags';
+	var $hasAndBelongsToMany = array(
+		'Fruit' => array(
+			'className' => 'FruitNoWith',
+			'joinTable' => 'fruits_uuid_tags',
+			'foreign_key' => 'uuid_tag_id',
+			'associationForeignKey' => 'fruit_id',
+		)
+	);
+}
+
+class ProductUpdateAll extends CakeTestModel {
+	var $name = 'ProductUpdateAll';
+	var $useTable = 'product_update_all';
+}
+
+class GroupUpdateAll extends CakeTestModel {
+	var $name = 'GroupUpdateAll';
+	var $useTable = 'group_update_all';
+}
+
+class TransactionTestModel extends CakeTestModel {
+	var $name = 'TransactionTestModel';
+	var $useTable = 'samples';
+
+	function afterSave($created) {
+		$data = array(
+			array('apple_id' => 1, 'name' => 'sample6'),
+		);
+		$this->saveAll($data, array('atomic' => true, 'callbacks' => false));
+	}
+}
+
+/**
+ * Test model for datasource prefixes
+ *
+ */
+class PrefixTestModel extends CakeTestModel {
+}
+class PrefixTestUseTableModel extends CakeTestModel {
+	var $name = 'PrefixTest';
+	var $useTable = 'prefix_tests';
+}

Added: trunk/src/Web/cake/tests/cases/libs/multibyte.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/multibyte.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/multibyte.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,9343 @@
+<?php
+/**
+ * MultibyteTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.6833
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', 'Multibyte');
+
+/**
+ * MultibyteTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class MultibyteTest extends CakeTestCase {
+
+/**
+ * testUtf8 method
+ *
+ * @access public
+ * @return void
+ */
+	function testUtf8() {
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$result = Multibyte::utf8($string);
+		$expected = array(33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+								58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
+								83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
+								106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126);
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$result = Multibyte::utf8($string);
+		$expected = array(161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181,
+								182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200);
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$result = Multibyte::utf8($string);
+		$expected = array(201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221,
+								222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242,
+								243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263,
+								264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
+								285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300);
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$result = Multibyte::utf8($string);
+		$expected = array(301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321,
+								322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342,
+								343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363,
+								364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384,
+								385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400);
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$result = Multibyte::utf8($string);
+		$expected = array(401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421,
+								422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442,
+								443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463,
+								464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484,
+								485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500);
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$result = Multibyte::utf8($string);
+		$expected = array(601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621,
+								622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642,
+								643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663,
+								664, 665, 666, 667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 683, 684,
+								685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, 698, 699, 700);
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$result = Multibyte::utf8($string);
+		$expected = array(1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041,
+								1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1051);
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$result = Multibyte::utf8($string);
+		$expected = array(1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069,
+								1070, 1071, 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, 1086, 1087,
+								1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, 1100);
+		$this->assertEqual($result, $expected);
+
+		$string = 'չպջռսվտ';
+		$result = Multibyte::utf8($string);
+		$expected = array(1401, 1402, 1403, 1404, 1405, 1406, 1407);
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$result = Multibyte::utf8($string);
+		$expected = array(1601, 1602, 1603, 1604, 1605, 1606, 1607, 1608, 1609, 1610, 1611, 1612, 1613, 1614, 1615);
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$result = Multibyte::utf8($string);
+		$expected = array(10032, 10033, 10034, 10035, 10036, 10037, 10038, 10039, 10040, 10041, 10042, 10043, 10044,
+								10045, 10046, 10047, 10048, 10049, 10050, 10051, 10052, 10053, 10054, 10055, 10056, 10057,
+								10058, 10059, 10060, 10061, 10062, 10063, 10064, 10065, 10066, 10067, 10068, 10069, 10070,
+								10071, 10072, 10073, 10074, 10075, 10076, 10077, 10078);
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$result = Multibyte::utf8($string);
+		$expected = array(11904, 11905, 11906, 11907, 11908, 11909, 11910, 11911, 11912, 11913, 11914, 11915, 11916, 11917, 11918, 11919,
+								11920, 11921, 11922, 11923, 11924, 11925, 11926, 11927, 11928, 11929, 11931, 11932, 11933, 11934, 11935, 11936,
+								11937, 11938, 11939, 11940, 11941, 11942, 11943, 11944, 11945, 11946, 11947, 11948, 11949, 11950, 11951, 11952,
+								11953, 11954, 11955, 11956, 11957, 11958, 11959, 11960, 11961, 11962, 11963, 11964, 11965, 11966, 11967, 11968,
+								11969, 11970, 11971, 11972, 11973, 11974, 11975, 11976, 11977, 11978, 11979, 11980, 11981, 11982, 11983, 11984,
+								11985, 11986, 11987, 11988, 11989, 11990, 11991, 11992, 11993, 11994, 11995, 11996, 11997, 11998, 11999, 12000);
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$result = Multibyte::utf8($string);
+		$expected = array(12101, 12102, 12103, 12104, 12105, 12106, 12107, 12108, 12109, 12110, 12111, 12112, 12113, 12114, 12115, 12116,
+								12117, 12118, 12119, 12120, 12121, 12122, 12123, 12124, 12125, 12126, 12127, 12128, 12129, 12130, 12131, 12132,
+								12133, 12134, 12135, 12136, 12137, 12138, 12139, 12140, 12141, 12142, 12143, 12144, 12145, 12146, 12147, 12148,
+								12149, 12150, 12151, 12152, 12153, 12154, 12155, 12156, 12157, 12158, 12159);
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$result = Multibyte::utf8($string);
+		$expected = array(45601, 45602, 45603, 45604, 45605, 45606, 45607, 45608, 45609, 45610, 45611, 45612, 45613, 45614, 45615, 45616,
+								45617, 45618, 45619, 45620, 45621, 45622, 45623, 45624, 45625, 45626, 45627, 45628, 45629, 45630, 45631, 45632,
+								45633, 45634, 45635, 45636, 45637, 45638, 45639, 45640, 45641, 45642, 45643, 45644, 45645, 45646, 45647, 45648,
+								45649, 45650, 45651, 45652, 45653, 45654, 45655, 45656, 45657, 45658, 45659, 45660, 45661, 45662, 45663, 45664,
+								45665, 45666, 45667, 45668, 45669, 45670, 45671, 45672, 45673, 45674, 45675, 45676, 45677, 45678, 45679, 45680,
+								45681, 45682, 45683, 45684, 45685, 45686, 45687, 45688, 45689, 45690, 45691, 45692, 45693, 45694, 45695, 45696,
+								45697, 45698, 45699, 45700);
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$result = Multibyte::utf8($string);
+		$expected = array(65136, 65137, 65138, 65139, 65140, 65141, 65142, 65143, 65144, 65145, 65146, 65147, 65148, 65149, 65150, 65151,
+								65152, 65153, 65154, 65155, 65156, 65157, 65158, 65159, 65160, 65161, 65162, 65163, 65164, 65165, 65166, 65167,
+								65168, 65169, 65170, 65171, 65172, 65173, 65174, 65175, 65176, 65177, 65178, 65179, 65180, 65181, 65182, 65183,
+								65184, 65185, 65186, 65187, 65188, 65189, 65190, 65191, 65192, 65193, 65194, 65195, 65196, 65197, 65198, 65199,
+								65200);
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$result = Multibyte::utf8($string);
+		$expected = array(65201, 65202, 65203, 65204, 65205, 65206, 65207, 65208, 65209, 65210, 65211, 65212, 65213, 65214, 65215, 65216,
+								65217, 65218, 65219, 65220, 65221, 65222, 65223, 65224, 65225, 65226, 65227, 65228, 65229, 65230, 65231, 65232,
+								65233, 65234, 65235, 65236, 65237, 65238, 65239, 65240, 65241, 65242, 65243, 65244, 65245, 65246, 65247, 65248,
+								65249, 65250, 65251, 65252, 65253, 65254, 65255, 65256, 65257, 65258, 65259, 65260, 65261, 65262, 65263, 65264,
+								65265, 65266, 65267, 65268, 65269, 65270, 65271, 65272, 65273, 65274, 65275, 65276);
+		$this->assertEqual($result, $expected);
+
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$result = Multibyte::utf8($string);
+		$expected = array(65345, 65346, 65347, 65348, 65349, 65350, 65351, 65352, 65353, 65354, 65355, 65356, 65357, 65358, 65359, 65360,
+								65361, 65362, 65363, 65364, 65365, 65366, 65367, 65368, 65369, 65370);
+		$this->assertEqual($result, $expected);
+
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$result = Multibyte::utf8($string);
+		$expected = array(65377, 65378, 65379, 65380, 65381, 65382, 65383, 65384, 65385, 65386, 65387, 65388, 65389, 65390, 65391, 65392,
+								65393, 65394, 65395, 65396, 65397, 65398, 65399, 65400);
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$result = Multibyte::utf8($string);
+		$expected = array(65401, 65402, 65403, 65404, 65405, 65406, 65407, 65408, 65409, 65410, 65411, 65412, 65413, 65414, 65415, 65416,
+								65417, 65418, 65419, 65420, 65421, 65422, 65423, 65424, 65425, 65426, 65427, 65428, 65429, 65430, 65431, 65432,
+								65433, 65434, 65435, 65436, 65437, 65438);
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$result = Multibyte::utf8($string);
+		$expected = array(292, 275, 314, 316, 335, 44, 32, 372, 337, 345, 316, 271, 33);
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$result = Multibyte::utf8($string);
+		$expected = array(72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33);
+		$this->assertEqual($result, $expected);
+
+		$string = '¨';
+		$result = Multibyte::utf8($string);
+		$expected = array(168);
+		$this->assertEqual($result, $expected);
+
+		$string = '¿';
+		$result = Multibyte::utf8($string);
+		$expected = array(191);
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$result = Multibyte::utf8($string);
+		$expected = array(269, 105, 110, 105);
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$result = Multibyte::utf8($string);
+		$expected = array(109, 111, 263, 105);
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$result = Multibyte::utf8($string);
+		$expected = array(100, 114, 382, 97, 118, 110, 105);
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$result = Multibyte::utf8($string);
+		$expected = array(25226, 30334, 24230, 35774, 20026, 39318, 39029);
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$result = Multibyte::utf8($string);
+		$expected = array(19968, 20108, 19977, 21608, 27704, 40845);
+		$this->assertEqual($result, $expected);
+
+		$string = 'ԀԂԄԆԈԊԌԎԐԒ';
+		$result = Multibyte::utf8($string);
+		$expected = array(1280, 1282, 1284, 1286, 1288, 1290, 1292, 1294, 1296, 1298);
+		$this->assertEqual($result, $expected);
+
+		$string = 'ԁԃԅԇԉԋԍԏԐԒ';
+		$result = Multibyte::utf8($string);
+		$expected = array(1281, 1283, 1285, 1287, 1289, 1291, 1293, 1295, 1296, 1298);
+		$this->assertEqual($result, $expected);
+
+		$string = 'ԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉՊՋՌՍՎՏՐՑՒՓՔՕՖև';
+		$result = Multibyte::utf8($string);
+		$expected = array(1329, 1330, 1331, 1332, 1333, 1334, 1335, 1336, 1337, 1338, 1339, 1340, 1341, 1342, 1343, 1344, 1345, 1346,
+								1347, 1348, 1349, 1350, 1351, 1352, 1353, 1354, 1355, 1356, 1357, 1358, 1359, 1360, 1361, 1362, 1363, 1364,
+								1365, 1366, 1415);
+		$this->assertEqual($result, $expected);
+
+		$string = 'աբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆև';
+		$result = Multibyte::utf8($string);
+		$expected = array(1377, 1378, 1379, 1380, 1381, 1382, 1383, 1384, 1385, 1386, 1387, 1388, 1389, 1390, 1391, 1392, 1393, 1394,
+								1395, 1396, 1397, 1398, 1399, 1400, 1401, 1402, 1403, 1404, 1405, 1406, 1407, 1408, 1409, 1410, 1411, 1412,
+								1413, 1414, 1415);
+		$this->assertEqual($result, $expected);
+
+		$string = 'ႠႡႢႣႤႥႦႧႨႩႪႫႬႭႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅ';
+		$result = Multibyte::utf8($string);
+		$expected = array(4256, 4257, 4258, 4259, 4260, 4261, 4262, 4263, 4264, 4265, 4266, 4267, 4268, 4269, 4270, 4271, 4272, 4273,
+								4274, 4275, 4276, 4277, 4278, 4279, 4280, 4281, 4282, 4283, 4284, 4285, 4286, 4287, 4288, 4289, 4290, 4291,
+								4292, 4293);
+		$this->assertEqual($result, $expected);
+
+		$string = 'ḀḂḄḆḈḊḌḎḐḒḔḖḘḚḜḞḠḢḤḦḨḪḬḮḰḲḴḶḸḺḼḾṀṂṄṆṈṊṌṎṐṒṔṖṘṚṜṞṠṢṤṦṨṪṬṮṰṲṴṶṸṺṼṾẀẂẄẆẈẊẌẎẐẒẔẖẗẘẙẚẠẢẤẦẨẪẬẮẰẲẴẶẸẺẼẾỀỂỄỆỈỊỌỎỐỒỔỖỘỚỜỞỠỢỤỦỨỪỬỮỰỲỴỶỸ';
+		$result = Multibyte::utf8($string);
+		$expected = array(7680, 7682, 7684, 7686, 7688, 7690, 7692, 7694, 7696, 7698, 7700, 7702, 7704, 7706, 7708, 7710, 7712, 7714,
+								7716, 7718, 7720, 7722, 7724, 7726, 7728, 7730, 7732, 7734, 7736, 7738, 7740, 7742, 7744, 7746, 7748, 7750,
+								7752, 7754, 7756, 7758, 7760, 7762, 7764, 7766, 7768, 7770, 7772, 7774, 7776, 7778, 7780, 7782, 7784, 7786,
+								7788, 7790, 7792, 7794, 7796, 7798, 7800, 7802, 7804, 7806, 7808, 7810, 7812, 7814, 7816, 7818, 7820, 7822,
+								7824, 7826, 7828, 7830, 7831, 7832, 7833, 7834,       7840, 7842, 7844, 7846, 7848, 7850, 7852, 7854, 7856,
+								7858, 7860, 7862, 7864, 7866, 7868, 7870, 7872, 7874, 7876, 7878, 7880, 7882, 7884, 7886, 7888, 7890, 7892,
+								7894, 7896, 7898, 7900, 7902, 7904, 7906, 7908, 7910, 7912, 7914, 7916, 7918, 7920, 7922, 7924, 7926,  7928);
+		$this->assertEqual($result, $expected);
+
+		$string = 'ḁḃḅḇḉḋḍḏḑḓḕḗḙḛḝḟḡḣḥḧḩḫḭḯḱḳḵḷḹḻḽḿṁṃṅṇṉṋṍṏṑṓṕṗṙṛṝṟṡṣṥṧṩṫṭṯṱṳṵṷṹṻṽṿẁẃẅẇẉẋẍẏẑẓẕẖẗẘẙẚạảấầẩẫậắằẳẵặẹẻẽếềểễệỉịọỏốồổỗộớờởỡợụủứừửữựỳỵỷỹ';
+		$result = Multibyte::utf8($string);
+		$expected = array(7681, 7683, 7685, 7687, 7689, 7691, 7693, 7695, 7697, 7699, 7701, 7703, 7705, 7707, 7709, 7711, 7713, 7715,
+									7717, 7719, 7721, 7723, 7725, 7727, 7729, 7731, 7733, 7735, 7737, 7739, 7741, 7743, 7745, 7747, 7749, 7751,
+									7753, 7755, 7757, 7759, 7761, 7763, 7765, 7767, 7769, 7771, 7773, 7775, 7777, 7779, 7781, 7783, 7785, 7787,
+									7789, 7791, 7793, 7795, 7797, 7799, 7801, 7803, 7805, 7807, 7809, 7811, 7813, 7815, 7817, 7819, 7821, 7823,
+									7825, 7827, 7829, 7830, 7831, 7832, 7833, 7834, 7841, 7843, 7845, 7847, 7849, 7851, 7853, 7855, 7857, 7859,
+									7861, 7863, 7865, 7867, 7869, 7871, 7873, 7875, 7877, 7879, 7881, 7883, 7885, 7887, 7889, 7891, 7893, 7895,
+									7897, 7899, 7901, 7903, 7905, 7907, 7909, 7911, 7913, 7915, 7917, 7919, 7921, 7923, 7925, 7927, 7929);
+		$this->assertEqual($result, $expected);
+
+		$string = 'ΩKÅℲ';
+		$result = Multibyte::utf8($string);
+		$expected = array(8486, 8490, 8491, 8498);
+		$this->assertEqual($result, $expected);
+
+		$string = 'ωkåⅎ';
+		$result = Multibyte::utf8($string);
+		$expected = array(969, 107, 229, 8526);
+		$this->assertEqual($result, $expected);
+
+		$string = 'ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯↃ';
+		$result = Multibyte::utf8($string);
+		$expected = array(8544, 8545, 8546, 8547, 8548, 8549, 8550, 8551, 8552, 8553, 8554, 8555, 8556, 8557, 8558, 8559, 8579);
+		$this->assertEqual($result, $expected);
+
+		$string = 'ⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿↄ';
+		$result = Multibyte::utf8($string);
+		$expected = array(8560, 8561, 8562, 8563, 8564, 8565, 8566, 8567, 8568, 8569, 8570, 8571, 8572, 8573, 8574, 8575, 8580);
+		$this->assertEqual($result, $expected);
+
+		$string = 'ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏ';
+		$result = Multibyte::utf8($string);
+		$expected = array(9398, 9399, 9400, 9401, 9402, 9403, 9404, 9405, 9406, 9407, 9408, 9409, 9410, 9411, 9412, 9413, 9414,
+								9415, 9416, 9417, 9418, 9419, 9420, 9421, 9422, 9423);
+		$this->assertEqual($result, $expected);
+
+		$string = 'ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ';
+		$result = Multibyte::utf8($string);
+		$expected = array(9424, 9425, 9426, 9427, 9428, 9429, 9430, 9431, 9432, 9433, 9434, 9435, 9436, 9437, 9438, 9439, 9440, 9441,
+								9442, 9443, 9444, 9445, 9446, 9447, 9448, 9449);
+		$this->assertEqual($result, $expected);
+
+		$string = 'ⰀⰁⰂⰃⰄⰅⰆⰇⰈⰉⰊⰋⰌⰍⰎⰏⰐⰑⰒⰓⰔⰕⰖⰗⰘⰙⰚⰛⰜⰝⰞⰟⰠⰡⰢⰣⰤⰥⰦⰧⰨⰩⰪⰫⰬⰭⰮ';
+		$result = Multibyte::utf8($string);
+		$expected = array(11264, 11265, 11266, 11267, 11268, 11269, 11270, 11271, 11272, 11273, 11274, 11275, 11276, 11277, 11278,
+								11279, 11280, 11281, 11282, 11283, 11284, 11285, 11286, 11287, 11288, 11289, 11290, 11291, 11292, 11293,
+								11294, 11295, 11296, 11297, 11298, 11299, 11300, 11301, 11302, 11303, 11304, 11305, 11306, 11307, 11308,
+								11309, 11310);
+		$this->assertEqual($result, $expected);
+
+		$string = 'ⰰⰱⰲⰳⰴⰵⰶⰷⰸⰹⰺⰻⰼⰽⰾⰿⱀⱁⱂⱃⱄⱅⱆⱇⱈⱉⱊⱋⱌⱍⱎⱏⱐⱑⱒⱓⱔⱕⱖⱗⱘⱙⱚⱛⱜⱝⱞ';
+		$result = Multibyte::utf8($string);
+		$expected = array(11312, 11313, 11314, 11315, 11316, 11317, 11318, 11319, 11320, 11321, 11322, 11323, 11324, 11325, 11326, 11327,
+								11328, 11329, 11330, 11331, 11332, 11333, 11334, 11335, 11336, 11337, 11338, 11339, 11340, 11341, 11342, 11343,
+								11344, 11345, 11346, 11347, 11348, 11349, 11350, 11351, 11352, 11353, 11354, 11355, 11356, 11357, 11358);
+		$this->assertEqual($result, $expected);
+
+		$string = 'ⲀⲂⲄⲆⲈⲊⲌⲎⲐⲒⲔⲖⲘⲚⲜⲞⲠⲢⲤⲦⲨⲪⲬⲮⲰⲲⲴⲶⲸⲺⲼⲾⳀⳂⳄⳆⳈⳊⳌⳎⳐⳒⳔⳖⳘⳚⳜⳞⳠⳢ';
+		$result = Multibyte::utf8($string);
+		$expected = array(11392, 11394, 11396, 11398, 11400, 11402, 11404, 11406, 11408, 11410, 11412, 11414, 11416, 11418, 11420,
+									11422, 11424, 11426, 11428, 11430, 11432, 11434, 11436, 11438, 11440, 11442, 11444, 11446, 11448, 11450,
+									11452, 11454, 11456, 11458, 11460, 11462, 11464, 11466, 11468, 11470, 11472, 11474, 11476, 11478, 11480,
+									11482, 11484, 11486, 11488, 11490);
+		$this->assertEqual($result, $expected);
+
+		$string = 'ⲁⲃⲅⲇⲉⲋⲍⲏⲑⲓⲕⲗⲙⲛⲝⲟⲡⲣⲥⲧⲩⲫⲭⲯⲱⲳⲵⲷⲹⲻⲽⲿⳁⳃⳅⳇⳉⳋⳍⳏⳑⳓⳕⳗⳙⳛⳝⳟⳡⳣ';
+		$result = Multibyte::utf8($string);
+		$expected = array(11393, 11395, 11397, 11399, 11401, 11403, 11405, 11407, 11409, 11411, 11413, 11415, 11417, 11419, 11421, 11423,
+								11425, 11427, 11429, 11431, 11433, 11435, 11437, 11439, 11441, 11443, 11445, 11447, 11449, 11451, 11453, 11455,
+								11457, 11459, 11461, 11463, 11465, 11467, 11469, 11471, 11473, 11475, 11477, 11479, 11481, 11483, 11485, 11487,
+								11489, 11491);
+		$this->assertEqual($result, $expected);
+
+
+		$string = 'fffiflffifflſtstﬓﬔﬕﬖﬗ';
+		$result = Multibyte::utf8($string);
+		$expected = array(64256, 64257, 64258, 64259, 64260, 64261, 64262, 64275, 64276, 64277, 64278, 64279);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testAscii method
+ *
+ * @access public
+ * @return void
+ */
+	function testAscii() {
+		$utf8 = array(33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+							58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
+							83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
+							106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126);
+		$result = Multibyte::ascii($utf8);
+
+		$expected = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181,
+								182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200);
+		$result = Multibyte::ascii($utf8);
+
+		$expected = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221,
+								222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242,
+								243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263,
+								264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
+								285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300);
+		$result = Multibyte::ascii($utf8);
+		$expected = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321,
+								322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342,
+								343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363,
+								364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384,
+								385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400);
+		$expected = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$result = Multibyte::ascii($utf8);
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421,
+								422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442,
+								443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463,
+								464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484,
+								485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500);
+		$expected = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$result = Multibyte::ascii($utf8);
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621,
+								622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642,
+								643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663,
+								664, 665, 666, 667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 683, 684,
+								685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, 698, 699, 700);
+		$expected = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$result = Multibyte::ascii($utf8);
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041,
+								1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1051);
+		$expected = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$result = Multibyte::ascii($utf8);
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069,
+								1070, 1071, 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, 1086, 1087,
+								1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, 1100);
+		$expected = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$result = Multibyte::ascii($utf8);
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(1401, 1402, 1403, 1404, 1405, 1406, 1407);
+		$expected = 'չպջռսվտ';
+		$result = Multibyte::ascii($utf8);
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(1601, 1602, 1603, 1604, 1605, 1606, 1607, 1608, 1609, 1610, 1611, 1612, 1613, 1614, 1615);
+		$expected = 'فقكلمنهوىيًٌٍَُ';
+		$result = Multibyte::ascii($utf8);
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(10032, 10033, 10034, 10035, 10036, 10037, 10038, 10039, 10040, 10041, 10042, 10043, 10044,
+								10045, 10046, 10047, 10048, 10049, 10050, 10051, 10052, 10053, 10054, 10055, 10056, 10057,
+								10058, 10059, 10060, 10061, 10062, 10063, 10064, 10065, 10066, 10067, 10068, 10069, 10070,
+								10071, 10072, 10073, 10074, 10075, 10076, 10077, 10078);
+		$expected = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$result = Multibyte::ascii($utf8);
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(11904, 11905, 11906, 11907, 11908, 11909, 11910, 11911, 11912, 11913, 11914, 11915, 11916, 11917, 11918, 11919,
+								11920, 11921, 11922, 11923, 11924, 11925, 11926, 11927, 11928, 11929, 11931, 11932, 11933, 11934, 11935, 11936,
+								11937, 11938, 11939, 11940, 11941, 11942, 11943, 11944, 11945, 11946, 11947, 11948, 11949, 11950, 11951, 11952,
+								11953, 11954, 11955, 11956, 11957, 11958, 11959, 11960, 11961, 11962, 11963, 11964, 11965, 11966, 11967, 11968,
+								11969, 11970, 11971, 11972, 11973, 11974, 11975, 11976, 11977, 11978, 11979, 11980, 11981, 11982, 11983, 11984,
+								11985, 11986, 11987, 11988, 11989, 11990, 11991, 11992, 11993, 11994, 11995, 11996, 11997, 11998, 11999, 12000);
+		$expected = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$result = Multibyte::ascii($utf8);
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(12101, 12102, 12103, 12104, 12105, 12106, 12107, 12108, 12109, 12110, 12111, 12112, 12113, 12114, 12115, 12116,
+								12117, 12118, 12119, 12120, 12121, 12122, 12123, 12124, 12125, 12126, 12127, 12128, 12129, 12130, 12131, 12132,
+								12133, 12134, 12135, 12136, 12137, 12138, 12139, 12140, 12141, 12142, 12143, 12144, 12145, 12146, 12147, 12148,
+								12149, 12150, 12151, 12152, 12153, 12154, 12155, 12156, 12157, 12158, 12159);
+		$expected = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$result = Multibyte::ascii($utf8);
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(45601, 45602, 45603, 45604, 45605, 45606, 45607, 45608, 45609, 45610, 45611, 45612, 45613, 45614, 45615, 45616,
+								45617, 45618, 45619, 45620, 45621, 45622, 45623, 45624, 45625, 45626, 45627, 45628, 45629, 45630, 45631, 45632,
+								45633, 45634, 45635, 45636, 45637, 45638, 45639, 45640, 45641, 45642, 45643, 45644, 45645, 45646, 45647, 45648,
+								45649, 45650, 45651, 45652, 45653, 45654, 45655, 45656, 45657, 45658, 45659, 45660, 45661, 45662, 45663, 45664,
+								45665, 45666, 45667, 45668, 45669, 45670, 45671, 45672, 45673, 45674, 45675, 45676, 45677, 45678, 45679, 45680,
+								45681, 45682, 45683, 45684, 45685, 45686, 45687, 45688, 45689, 45690, 45691, 45692, 45693, 45694, 45695, 45696,
+								45697, 45698, 45699, 45700);
+		$expected = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$result = Multibyte::ascii($utf8);
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(65136, 65137, 65138, 65139, 65140, 65141, 65142, 65143, 65144, 65145, 65146, 65147, 65148, 65149, 65150, 65151,
+								65152, 65153, 65154, 65155, 65156, 65157, 65158, 65159, 65160, 65161, 65162, 65163, 65164, 65165, 65166, 65167,
+								65168, 65169, 65170, 65171, 65172, 65173, 65174, 65175, 65176, 65177, 65178, 65179, 65180, 65181, 65182, 65183,
+								65184, 65185, 65186, 65187, 65188, 65189, 65190, 65191, 65192, 65193, 65194, 65195, 65196, 65197, 65198, 65199,
+								65200);
+		$expected = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$result = Multibyte::ascii($utf8);
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(65201, 65202, 65203, 65204, 65205, 65206, 65207, 65208, 65209, 65210, 65211, 65212, 65213, 65214, 65215, 65216,
+								65217, 65218, 65219, 65220, 65221, 65222, 65223, 65224, 65225, 65226, 65227, 65228, 65229, 65230, 65231, 65232,
+								65233, 65234, 65235, 65236, 65237, 65238, 65239, 65240, 65241, 65242, 65243, 65244, 65245, 65246, 65247, 65248,
+								65249, 65250, 65251, 65252, 65253, 65254, 65255, 65256, 65257, 65258, 65259, 65260, 65261, 65262, 65263, 65264,
+								65265, 65266, 65267, 65268, 65269, 65270, 65271, 65272, 65273, 65274, 65275, 65276);
+		$expected = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$result = Multibyte::ascii($utf8);
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(65345, 65346, 65347, 65348, 65349, 65350, 65351, 65352, 65353, 65354, 65355, 65356, 65357, 65358, 65359, 65360,
+								65361, 65362, 65363, 65364, 65365, 65366, 65367, 65368, 65369, 65370);
+		$expected = 'abcdefghijklmnopqrstuvwxyz';
+		$result = Multibyte::ascii($utf8);
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(65377, 65378, 65379, 65380, 65381, 65382, 65383, 65384, 65385, 65386, 65387, 65388, 65389, 65390, 65391, 65392,
+								65393, 65394, 65395, 65396, 65397, 65398, 65399, 65400);
+		$expected = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$result = Multibyte::ascii($utf8);
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(65401, 65402, 65403, 65404, 65405, 65406, 65407, 65408, 65409, 65410, 65411, 65412, 65413, 65414, 65415, 65416,
+								65417, 65418, 65419, 65420, 65421, 65422, 65423, 65424, 65425, 65426, 65427, 65428, 65429, 65430, 65431, 65432,
+								65433, 65434, 65435, 65436, 65437, 65438);
+		$expected = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$result = Multibyte::ascii($utf8);
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(292, 275, 314, 316, 335, 44, 32, 372, 337, 345, 316, 271, 33);
+		$expected = 'Ĥēĺļŏ, Ŵőřļď!';
+		$result = Multibyte::ascii($utf8);
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33);
+		$expected = 'Hello, World!';
+		$result = Multibyte::ascii($utf8);
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(168);
+		$expected = '¨';
+		$result = Multibyte::ascii($utf8);
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(191);
+		$expected = '¿';
+		$result = Multibyte::ascii($utf8);
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(269, 105, 110, 105);
+		$expected = 'čini';
+		$result = Multibyte::ascii($utf8);
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(109, 111, 263, 105);
+		$expected = 'moći';
+		$result = Multibyte::ascii($utf8);
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(100, 114, 382, 97, 118, 110, 105);
+		$expected = 'državni';
+		$result = Multibyte::ascii($utf8);
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(25226, 30334, 24230, 35774, 20026, 39318, 39029);
+		$expected = '把百度设为首页';
+		$result = Multibyte::ascii($utf8);
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(19968, 20108, 19977, 21608, 27704, 40845);
+		$expected = '一二三周永龍';
+		$result = Multibyte::ascii($utf8);
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(1280, 1282, 1284, 1286, 1288, 1290, 1292, 1294, 1296, 1298);
+		$expected = 'ԀԂԄԆԈԊԌԎԐԒ';
+		$result = Multibyte::ascii($utf8);
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(1281, 1283, 1285, 1287, 1289, 1291, 1293, 1295, 1296, 1298);
+		$expected = 'ԁԃԅԇԉԋԍԏԐԒ';
+		$result = Multibyte::ascii($utf8);
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(1329, 1330, 1331, 1332, 1333, 1334, 1335, 1336, 1337, 1338, 1339, 1340, 1341, 1342, 1343, 1344, 1345, 1346, 1347,
+							1348, 1349, 1350, 1351, 1352, 1353, 1354, 1355, 1356, 1357, 1358, 1359, 1360, 1361, 1362, 1363, 1364, 1365,
+							1366, 1415);
+		$result = Multibyte::ascii($utf8);
+		$expected = 'ԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉՊՋՌՍՎՏՐՑՒՓՔՕՖև';
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(1377, 1378, 1379, 1380, 1381, 1382, 1383, 1384, 1385, 1386, 1387, 1388, 1389, 1390, 1391, 1392, 1393, 1394,
+								1395, 1396, 1397, 1398, 1399, 1400, 1401, 1402, 1403, 1404, 1405, 1406, 1407, 1408, 1409, 1410, 1411, 1412,
+								1413, 1414, 1415);
+		$result = Multibyte::ascii($utf8);
+		$expected = 'աբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆև';
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(4256, 4257, 4258, 4259, 4260, 4261, 4262, 4263, 4264, 4265, 4266, 4267, 4268, 4269, 4270, 4271, 4272, 4273, 4274,
+							4275, 4276, 4277, 4278, 4279, 4280, 4281, 4282, 4283, 4284, 4285, 4286, 4287, 4288, 4289, 4290, 4291, 4292, 4293);
+		$result = Multibyte::ascii($utf8);
+		$expected = 'ႠႡႢႣႤႥႦႧႨႩႪႫႬႭႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅ';
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(7680, 7682, 7684, 7686, 7688, 7690, 7692, 7694, 7696, 7698, 7700, 7702, 7704, 7706, 7708, 7710, 7712, 7714,
+								7716, 7718, 7720, 7722, 7724, 7726, 7728, 7730, 7732, 7734, 7736, 7738, 7740, 7742, 7744, 7746, 7748, 7750,
+								7752, 7754, 7756, 7758, 7760, 7762, 7764, 7766, 7768, 7770, 7772, 7774, 7776, 7778, 7780, 7782, 7784, 7786,
+								7788, 7790, 7792, 7794, 7796, 7798, 7800, 7802, 7804, 7806, 7808, 7810, 7812, 7814, 7816, 7818, 7820, 7822,
+								7824, 7826, 7828, 7830, 7831, 7832, 7833, 7834,       7840, 7842, 7844, 7846, 7848, 7850, 7852, 7854, 7856,
+								7858, 7860, 7862, 7864, 7866, 7868, 7870, 7872, 7874, 7876, 7878, 7880, 7882, 7884, 7886, 7888, 7890, 7892,
+								7894, 7896, 7898, 7900, 7902, 7904, 7906, 7908, 7910, 7912, 7914, 7916, 7918, 7920, 7922, 7924, 7926,  7928);
+		$result = Multibyte::ascii($utf8);
+		$expected = 'ḀḂḄḆḈḊḌḎḐḒḔḖḘḚḜḞḠḢḤḦḨḪḬḮḰḲḴḶḸḺḼḾṀṂṄṆṈṊṌṎṐṒṔṖṘṚṜṞṠṢṤṦṨṪṬṮṰṲṴṶṸṺṼṾẀẂẄẆẈẊẌẎẐẒẔẖẗẘẙẚẠẢẤẦẨẪẬẮẰẲẴẶẸẺẼẾỀỂỄỆỈỊỌỎỐỒỔỖỘỚỜỞỠỢỤỦỨỪỬỮỰỲỴỶỸ';
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(7681, 7683, 7685, 7687, 7689, 7691, 7693, 7695, 7697, 7699, 7701, 7703, 7705, 7707, 7709, 7711, 7713, 7715,
+							7717, 7719, 7721, 7723, 7725, 7727, 7729, 7731, 7733, 7735, 7737, 7739, 7741, 7743, 7745, 7747, 7749, 7751,
+							7753, 7755, 7757, 7759, 7761, 7763, 7765, 7767, 7769, 7771, 7773, 7775, 7777, 7779, 7781, 7783, 7785, 7787,
+							7789, 7791, 7793, 7795, 7797, 7799, 7801, 7803, 7805, 7807, 7809, 7811, 7813, 7815, 7817, 7819, 7821, 7823,
+							7825, 7827, 7829, 7830, 7831, 7832, 7833, 7834, 7841, 7843, 7845, 7847, 7849, 7851, 7853, 7855, 7857, 7859,
+							7861, 7863, 7865, 7867, 7869, 7871, 7873, 7875, 7877, 7879, 7881, 7883, 7885, 7887, 7889, 7891, 7893, 7895,
+							7897, 7899, 7901, 7903, 7905, 7907, 7909, 7911, 7913, 7915, 7917, 7919, 7921, 7923, 7925, 7927, 7929);
+		$result = Multibyte::ascii($utf8);
+		$expected = 'ḁḃḅḇḉḋḍḏḑḓḕḗḙḛḝḟḡḣḥḧḩḫḭḯḱḳḵḷḹḻḽḿṁṃṅṇṉṋṍṏṑṓṕṗṙṛṝṟṡṣṥṧṩṫṭṯṱṳṵṷṹṻṽṿẁẃẅẇẉẋẍẏẑẓẕẖẗẘẙẚạảấầẩẫậắằẳẵặẹẻẽếềểễệỉịọỏốồổỗộớờởỡợụủứừửữựỳỵỷỹ';
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(8486, 8490, 8491, 8498);
+		$result = Multibyte::ascii($utf8);
+		$expected = 'ΩKÅℲ';
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(969, 107, 229, 8526);
+		$result = Multibyte::ascii($utf8);
+		$expected = 'ωkåⅎ';
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(8544, 8545, 8546, 8547, 8548, 8549, 8550, 8551, 8552, 8553, 8554, 8555, 8556, 8557, 8558, 8559, 8579);
+		$result = Multibyte::ascii($utf8);
+		$expected = 'ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯↃ';
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(8560, 8561, 8562, 8563, 8564, 8565, 8566, 8567, 8568, 8569, 8570, 8571, 8572, 8573, 8574, 8575, 8580);
+		$result = Multibyte::ascii($utf8);
+		$expected = 'ⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿↄ';
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(9398, 9399, 9400, 9401, 9402, 9403, 9404, 9405, 9406, 9407, 9408, 9409, 9410, 9411, 9412, 9413, 9414,
+							9415, 9416, 9417, 9418, 9419, 9420, 9421, 9422, 9423);
+		$result = Multibyte::ascii($utf8);
+		$expected = 'ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏ';
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(9424, 9425, 9426, 9427, 9428, 9429, 9430, 9431, 9432, 9433, 9434, 9435, 9436, 9437, 9438, 9439, 9440, 9441,
+							9442, 9443, 9444, 9445, 9446, 9447, 9448, 9449);
+		$result = Multibyte::ascii($utf8);
+		$expected = 'ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ';
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(11264, 11265, 11266, 11267, 11268, 11269, 11270, 11271, 11272, 11273, 11274, 11275, 11276, 11277, 11278, 11279,
+							11280, 11281, 11282, 11283, 11284, 11285, 11286, 11287, 11288, 11289, 11290, 11291, 11292, 11293, 11294, 11295,
+							11296, 11297, 11298, 11299, 11300, 11301, 11302, 11303, 11304, 11305, 11306, 11307, 11308, 11309, 11310);
+		$result = Multibyte::ascii($utf8);
+		$expected = 'ⰀⰁⰂⰃⰄⰅⰆⰇⰈⰉⰊⰋⰌⰍⰎⰏⰐⰑⰒⰓⰔⰕⰖⰗⰘⰙⰚⰛⰜⰝⰞⰟⰠⰡⰢⰣⰤⰥⰦⰧⰨⰩⰪⰫⰬⰭⰮ';
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(11312, 11313, 11314, 11315, 11316, 11317, 11318, 11319, 11320, 11321, 11322, 11323, 11324, 11325, 11326, 11327,
+							11328, 11329, 11330, 11331, 11332, 11333, 11334, 11335, 11336, 11337, 11338, 11339, 11340, 11341, 11342, 11343,
+							11344, 11345, 11346, 11347, 11348, 11349, 11350, 11351, 11352, 11353, 11354, 11355, 11356, 11357, 11358);
+		$result = Multibyte::ascii($utf8);
+		$expected = 'ⰰⰱⰲⰳⰴⰵⰶⰷⰸⰹⰺⰻⰼⰽⰾⰿⱀⱁⱂⱃⱄⱅⱆⱇⱈⱉⱊⱋⱌⱍⱎⱏⱐⱑⱒⱓⱔⱕⱖⱗⱘⱙⱚⱛⱜⱝⱞ';
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(11392, 11394, 11396, 11398, 11400, 11402, 11404, 11406, 11408, 11410, 11412, 11414, 11416, 11418, 11420,
+									11422, 11424, 11426, 11428, 11430, 11432, 11434, 11436, 11438, 11440, 11442, 11444, 11446, 11448, 11450,
+									11452, 11454, 11456, 11458, 11460, 11462, 11464, 11466, 11468, 11470, 11472, 11474, 11476, 11478, 11480,
+									11482, 11484, 11486, 11488, 11490);
+		$result = Multibyte::ascii($utf8);
+		$expected = 'ⲀⲂⲄⲆⲈⲊⲌⲎⲐⲒⲔⲖⲘⲚⲜⲞⲠⲢⲤⲦⲨⲪⲬⲮⲰⲲⲴⲶⲸⲺⲼⲾⳀⳂⳄⳆⳈⳊⳌⳎⳐⳒⳔⳖⳘⳚⳜⳞⳠⳢ';
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(11393, 11395, 11397, 11399, 11401, 11403, 11405, 11407, 11409, 11411, 11413, 11415, 11417, 11419, 11421, 11423,
+							11425, 11427, 11429, 11431, 11433, 11435, 11437, 11439, 11441, 11443, 11445, 11447, 11449, 11451, 11453, 11455,
+							11457, 11459, 11461, 11463, 11465, 11467, 11469, 11471, 11473, 11475, 11477, 11479, 11481, 11483, 11485, 11487,
+							11489, 11491);
+		$result = Multibyte::ascii($utf8);
+		$expected = 'ⲁⲃⲅⲇⲉⲋⲍⲏⲑⲓⲕⲗⲙⲛⲝⲟⲡⲣⲥⲧⲩⲫⲭⲯⲱⲳⲵⲷⲹⲻⲽⲿⳁⳃⳅⳇⳉⳋⳍⳏⳑⳓⳕⳗⳙⳛⳝⳟⳡⳣ';
+		$this->assertEqual($result, $expected);
+
+		$utf8 = array(64256, 64257, 64258, 64259, 64260, 64261, 64262, 64275, 64276, 64277, 64278, 64279);
+		$result = Multibyte::ascii($utf8);
+		$expected = 'fffiflffifflſtstﬓﬔﬕﬖﬗ';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testUsingMbStripos method
+ *
+ * @access public
+ * @return void
+ */
+	function testUsingMbStripos() {
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$find = 'f';
+		$result = mb_stripos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ABCDEFGHIJKLMNOPQFRSTUVWXYZ0123456789';
+		$find = 'f';
+		$result = mb_stripos($string, $find, 6);
+		$expected = 17;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$find = 'å';
+		$result = mb_stripos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÅÙÚÛÜÝÞ';
+		$find = 'å';
+		$result = mb_stripos($string, $find, 6);
+		$expected = 24;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'ċ';
+		$result = mb_stripos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁĊŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'ċ';
+		$result = mb_stripos($string, $find, 6);
+		$expected = 32;
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$find = 'f';
+		$result = mb_stripos($string, $find);
+		$expected = 37;
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$find = 'Μ';
+		$result = mb_stripos($string, $find);
+		$expected = 20;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$find = 'É';
+		$result = mb_stripos($string, $find, 6);
+		$expected = 32;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$find = 'Ņ';
+		$result = mb_stripos($string, $find);
+		$expected = 24;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$find = 'Ƹ';
+		$result = mb_stripos($string, $find);
+		$expected = 39;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$find = 'Ƹ';
+		$result = mb_stripos($string, $find, 40);
+		$expected = 40;
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$find = 'Ʀ';
+		$result = mb_stripos($string, $find);
+		$expected = 39;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$find = 'ї';
+		$result = mb_stripos($string, $find);
+		$expected = 7;
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'Р';
+		$result = mb_stripos($string, $find);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'Р';
+		$result = mb_stripos($string, $find, 5);
+		$expected = 36;
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$find = 'ن';
+		$result = mb_stripos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$find = '✿';
+		$result = mb_stripos($string, $find);
+		$expected = 15;
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$find = '⺐';
+		$result = mb_stripos($string, $find);
+		$expected = 16;
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$find = '⽤';
+		$result = mb_stripos($string, $find);
+		$expected = 31;
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$find = '눻';
+		$result = mb_stripos($string, $find);
+		$expected = 26;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$find = 'ﺞ';
+		$result = mb_stripos($string, $find);
+		$expected = 46;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$find = 'ﻞ';
+		$result = mb_stripos($string, $find);
+		$expected = 45;
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'k';
+		$result = mb_stripos($string, $find);
+		$expected = 10;
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'K';
+		$result = mb_stripos($string, $find);
+		$expected = 10;
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$find = 'ア';
+		$result = mb_stripos($string, $find);
+		$expected = 16;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$find = 'ハ';
+		$result = mb_stripos($string, $find);
+		$expected = 17;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ő';
+		$result = mb_stripos($string, $find);
+		$expected = 8;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'Ő';
+		$result = mb_stripos($string, $find);
+		$expected = 8;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'O';
+		$result = mb_stripos($string, $find);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'o';
+		$result = mb_stripos($string, $find);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'n';
+		$result = mb_stripos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'N';
+		$result = mb_stripos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'ć';
+		$result = mb_stripos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'Ć';
+		$result = mb_stripos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'ž';
+		$result = mb_stripos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'Ž';
+		$result = mb_stripos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$find = '设';
+		$result = mb_stripos($string, $find);
+		$expected = 3;
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '周';
+		$result = mb_stripos($string, $find);
+		$expected = 3;
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'DŽ';
+		$result = mb_stripos($string, $find);
+		$expected = false;
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testMultibyteStripos method
+ *
+ * @access public
+ * @return void
+ */
+	function testMultibyteStripos() {
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$find = 'f';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ABCDEFGHIJKLMNOPQFRSTUVWXYZ0123456789';
+		$find = 'f';
+		$result = Multibyte::stripos($string, $find, 6);
+		$expected = 17;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$find = 'å';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÅÙÚÛÜÝÞ';
+		$find = 'å';
+		$result = Multibyte::stripos($string, $find, 6);
+		$expected = 24;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'ċ';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁĊŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'ċ';
+		$result = Multibyte::stripos($string, $find, 6);
+		$expected = 32;
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$find = 'f';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 37;
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$find = 'Μ';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 20;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$find = 'É';
+		$result = Multibyte::stripos($string, $find, 6);
+		$expected = 32;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$find = 'Ņ';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 24;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$find = 'Ƹ';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 39;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$find = 'Ƹ';
+		$result = Multibyte::stripos($string, $find, 40);
+		$expected = 40;
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$find = 'Ʀ';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 39;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$find = 'ї';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 7;
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'Р';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'Р';
+		$result = Multibyte::stripos($string, $find, 5);
+		$expected = 36;
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$find = 'ن';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$find = '✿';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 15;
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$find = '⺐';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 16;
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$find = '⽤';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 31;
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$find = '눻';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 26;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$find = 'ﺞ';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 46;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$find = 'ﻞ';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 45;
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'k';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 10;
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'K';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 10;
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$find = 'ア';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 16;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$find = 'ハ';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 17;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ő';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 8;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'Ő';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 8;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'O';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'o';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'n';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'N';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'ć';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'Ć';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'ž';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'Ž';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$find = '设';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 3;
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '周';
+		$result = Multibyte::stripos($string, $find);
+		$expected = 3;
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'DŽ';
+		$result = Multibyte::stripos($string, $find);
+		$expected = false;
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testUsingMbStristr method
+ *
+ * @access public
+ * @return void
+ */
+	function testUsingMbStristr() {
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$find = 'f';
+		$result = mb_stristr($string, $find);
+		$expected = 'FGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$find = 'f';
+		$result = mb_stristr($string, $find, true);
+		$expected = 'ABCDE';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$find = 'å';
+		$result = mb_stristr($string, $find);
+		$expected = 'ÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$find = 'å';
+		$result = mb_stristr($string, $find, true);
+		$expected = 'ÀÁÂÃÄ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'ċ';
+		$result = mb_stristr($string, $find);
+		$expected = 'ĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'ċ';
+		$result = mb_stristr($string, $find, true);
+		$expected = 'ĀĂĄĆĈ';
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$find = 'f';
+		$result = mb_stristr($string, $find);
+		$expected = 'FGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$find = 'f';
+		$result = mb_stristr($string, $find, true);
+		$expected = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDE';
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$find = 'Μ';
+		$result = mb_stristr($string, $find);
+		$expected = 'µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$find = 'Μ';
+		$result = mb_stristr($string, $find, true);
+		$expected = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$find = 'þ';
+		$result = mb_stristr($string, $find);
+		$expected = 'Þßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$find = 'þ';
+		$result = mb_stristr($string, $find, true);
+		$expected = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$find = 'Ņ';
+		$result = mb_stristr($string, $find);
+		$expected = 'ŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$find = 'Ņ';
+		$result = mb_stristr($string, $find, true);
+		$expected = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃń';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$find = 'Ƹ';
+		$result = mb_stristr($string, $find);
+		$expected = 'ƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$this->assertEqual($result, $expected);
+
+		$find = 'Ƹ';
+		$result = mb_stristr($string, $find, true);
+		$expected = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$find = 'Ʀ';
+		$result = mb_stristr($string, $find);
+		$expected = 'ʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$find = 'Ʀ';
+		$result = mb_stristr($string, $find, true);
+		$expected = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$find = 'ї';
+		$result = mb_stristr($string, $find);
+		$expected = 'ЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$find = 'ї';
+		$result = mb_stristr($string, $find, true);
+		$expected = 'ЀЁЂЃЄЅІ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'Р';
+		$result = mb_stristr($string, $find);
+		$expected = 'РСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'Р';
+		$result = mb_stristr($string, $find, true);
+		$expected = 'МНОП';
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$find = 'ن';
+		$result = mb_stristr($string, $find);
+		$expected = 'نهوىيًٌٍَُ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$find = 'ن';
+		$result = mb_stristr($string, $find, true);
+		$expected = 'فقكلم';
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$find = '✿';
+		$result = mb_stristr($string, $find);
+		$expected = '✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$find = '✿';
+		$result = mb_stristr($string, $find, true);
+		$expected = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾';
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$find = '⺐';
+		$result = mb_stristr($string, $find);
+		$expected = '⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$find = '⺐';
+		$result = mb_stristr($string, $find, true);
+		$expected = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏';
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$find = '⽤';
+		$result = mb_stristr($string, $find);
+		$expected = '⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$find = '⽤';
+		$result = mb_stristr($string, $find, true);
+		$expected = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣';
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$find = '눻';
+		$result = mb_stristr($string, $find);
+		$expected = '눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$find = '눻';
+		$result = mb_stristr($string, $find, true);
+		$expected = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$find = 'ﺞ';
+		$result = mb_stristr($string, $find);
+		$expected = 'ﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$find = 'ﺞ';
+		$result = mb_stristr($string, $find, true);
+		$expected = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$find = 'ﻞ';
+		$result = mb_stristr($string, $find);
+		$expected = 'ﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$find = 'ﻞ';
+		$result = mb_stristr($string, $find, true);
+		$expected = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'K';
+		$result = mb_stristr($string, $find);
+		$expected = 'klmnopqrstuvwxyz';
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'K';
+		$result = mb_stristr($string, $find, true);
+		$expected = 'abcdefghij';
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$find = 'ア';
+		$result = mb_stristr($string, $find);
+		$expected = 'アイウエオカキク';
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$find = 'ア';
+		$result = mb_stristr($string, $find, true);
+		$expected = '。「」、・ヲァィゥェォャュョッー';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$find = 'ハ';
+		$result = mb_stristr($string, $find);
+		$expected = 'ハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$find = 'ハ';
+		$result = mb_stristr($string, $find, true);
+		$expected = 'ケコサシスセソタチツテトナニヌネノ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'Ő';
+		$result = mb_stristr($string, $find);
+		$expected = 'őřļď!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'Ő';
+		$result = mb_stristr($string, $find, true);
+		$expected = 'Ĥēĺļŏ, Ŵ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ĺļ';
+		$result = mb_stristr($string, $find, true);
+		$expected = 'Ĥē';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'O';
+		$result = mb_stristr($string, $find);
+		$expected = 'o, World!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'O';
+		$result = mb_stristr($string, $find, true);
+		$expected = 'Hell';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'Wo';
+		$result = mb_stristr($string, $find);
+		$expected = 'World!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'Wo';
+		$result = mb_stristr($string, $find, true);
+		$expected = 'Hello, ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'll';
+		$result = mb_stristr($string, $find);
+		$expected = 'llo, World!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'll';
+		$result = mb_stristr($string, $find, true);
+		$expected = 'He';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'rld';
+		$result = mb_stristr($string, $find);
+		$expected = 'rld!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'rld';
+		$result = mb_stristr($string, $find, true);
+		$expected = 'Hello, Wo';
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'N';
+		$result = mb_stristr($string, $find);
+		$expected = 'ni';
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'N';
+		$result = mb_stristr($string, $find, true);
+		$expected = 'či';
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'Ć';
+		$result = mb_stristr($string, $find);
+		$expected = 'ći';
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'Ć';
+		$result = mb_stristr($string, $find, true);
+		$expected = 'mo';
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'Ž';
+		$result = mb_stristr($string, $find);
+		$expected = 'žavni';
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'Ž';
+		$result = mb_stristr($string, $find, true);
+		$expected = 'dr';
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$find = '设';
+		$result = mb_stristr($string, $find);
+		$expected = '设为首页';
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$find = '设';
+		$result = mb_stristr($string, $find, true);
+		$expected = '把百度';
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '周';
+		$result = mb_stristr($string, $find);
+		$expected = '周永龍';
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '周';
+		$result = mb_stristr($string, $find, true);
+		$expected = '一二三';
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '二周';
+		$result = mb_stristr($string, $find);
+		$expected = false;
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testMultibyteStristr method
+ *
+ * @access public
+ * @return void
+ */
+	function testMultibyteStristr() {
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$find = 'f';
+		$result = Multibyte::stristr($string, $find);
+		$expected = 'FGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$find = 'f';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = 'ABCDE';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$find = 'å';
+		$result = Multibyte::stristr($string, $find);
+		$expected = 'ÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$find = 'å';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = 'ÀÁÂÃÄ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'ċ';
+		$result = Multibyte::stristr($string, $find);
+		$expected = 'ĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'ċ';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = 'ĀĂĄĆĈ';
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$find = 'f';
+		$result = Multibyte::stristr($string, $find);
+		$expected = 'FGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$find = 'f';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDE';
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$find = 'Μ';
+		$result = Multibyte::stristr($string, $find);
+		$expected = 'µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$find = 'Μ';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$find = 'þ';
+		$result = Multibyte::stristr($string, $find);
+		$expected = 'Þßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$find = 'þ';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$find = 'Ņ';
+		$result = Multibyte::stristr($string, $find);
+		$expected = 'ŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$find = 'Ņ';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃń';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$find = 'Ƹ';
+		$result = Multibyte::stristr($string, $find);
+		$expected = 'ƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$this->assertEqual($result, $expected);
+
+		$find = 'Ƹ';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$find = 'Ʀ';
+		$result = Multibyte::stristr($string, $find);
+		$expected = 'ʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$find = 'Ʀ';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$find = 'ї';
+		$result = Multibyte::stristr($string, $find);
+		$expected = 'ЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$find = 'ї';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = 'ЀЁЂЃЄЅІ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'Р';
+		$result = Multibyte::stristr($string, $find);
+		$expected = 'РСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'Р';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = 'МНОП';
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$find = 'ن';
+		$result = Multibyte::stristr($string, $find);
+		$expected = 'نهوىيًٌٍَُ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$find = 'ن';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = 'فقكلم';
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$find = '✿';
+		$result = Multibyte::stristr($string, $find);
+		$expected = '✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$find = '✿';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾';
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$find = '⺐';
+		$result = Multibyte::stristr($string, $find);
+		$expected = '⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$find = '⺐';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏';
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$find = '⽤';
+		$result = Multibyte::stristr($string, $find);
+		$expected = '⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$find = '⽤';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣';
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$find = '눻';
+		$result = Multibyte::stristr($string, $find);
+		$expected = '눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$find = '눻';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$find = 'ﺞ';
+		$result = Multibyte::stristr($string, $find);
+		$expected = 'ﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$find = 'ﺞ';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$find = 'ﻞ';
+		$result = Multibyte::stristr($string, $find);
+		$expected = 'ﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$find = 'ﻞ';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'K';
+		$result = Multibyte::stristr($string, $find);
+		$expected = 'klmnopqrstuvwxyz';
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'K';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = 'abcdefghij';
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$find = 'ア';
+		$result = Multibyte::stristr($string, $find);
+		$expected = 'アイウエオカキク';
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$find = 'ア';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = '。「」、・ヲァィゥェォャュョッー';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$find = 'ハ';
+		$result = Multibyte::stristr($string, $find);
+		$expected = 'ハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$find = 'ハ';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = 'ケコサシスセソタチツテトナニヌネノ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'Ő';
+		$result = Multibyte::stristr($string, $find);
+		$expected = 'őřļď!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'Ő';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = 'Ĥēĺļŏ, Ŵ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ĺļ';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = 'Ĥē';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'O';
+		$result = Multibyte::stristr($string, $find);
+		$expected = 'o, World!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'O';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = 'Hell';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'Wo';
+		$result = Multibyte::stristr($string, $find);
+		$expected = 'World!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'Wo';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = 'Hello, ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'll';
+		$result = Multibyte::stristr($string, $find);
+		$expected = 'llo, World!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'll';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = 'He';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'rld';
+		$result = Multibyte::stristr($string, $find);
+		$expected = 'rld!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'rld';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = 'Hello, Wo';
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'N';
+		$result = Multibyte::stristr($string, $find);
+		$expected = 'ni';
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'N';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = 'či';
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'Ć';
+		$result = Multibyte::stristr($string, $find);
+		$expected = 'ći';
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'Ć';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = 'mo';
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'Ž';
+		$result = Multibyte::stristr($string, $find);
+		$expected = 'žavni';
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'Ž';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = 'dr';
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$find = '设';
+		$result = Multibyte::stristr($string, $find);
+		$expected = '设为首页';
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$find = '设';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = '把百度';
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '周';
+		$result = Multibyte::stristr($string, $find);
+		$expected = '周永龍';
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '周';
+		$result = Multibyte::stristr($string, $find, true);
+		$expected = '一二三';
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '二周';
+		$result = Multibyte::stristr($string, $find);
+		$expected = false;
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testUsingMbStrlen method
+ *
+ * @access public
+ * @return void
+ */
+	function testUsingMbStrlen() {
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$result = mb_strlen($string);
+		$expected = 36;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$result = mb_strlen($string);
+		$expected = 30;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$result = mb_strlen($string);
+		$expected = 61;
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$result = mb_strlen($string);
+		$expected = 94;
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$result = mb_strlen($string);
+		$expected = 40;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$result = mb_strlen($string);
+		$expected = 100;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$result = mb_strlen($string);
+		$expected = 100;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$result = mb_strlen($string);
+		$expected = 100;
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$result = mb_strlen($string);
+		$expected = 100;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$result = mb_strlen($string);
+		$expected = 28;
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$result = mb_strlen($string);
+		$expected = 49;
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$result = mb_strlen($string);
+		$expected = 15;
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$result = mb_strlen($string);
+		$expected = 47;
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$result = mb_strlen($string);
+		$expected = 96;
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$result = mb_strlen($string);
+		$expected = 59;
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$result = mb_strlen($string);
+		$expected = 100;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$result = mb_strlen($string);
+		$expected = 65;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$result = mb_strlen($string);
+		$expected = 76;
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$result = mb_strlen($string);
+		$expected = 26;
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$result = mb_strlen($string);
+		$expected = 24;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$result = mb_strlen($string);
+		$expected = 38;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$result = mb_strlen($string);
+		$expected = 13;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$result = mb_strlen($string);
+		$expected = 13;
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$result = mb_strlen($string);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$result = mb_strlen($string);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$result = mb_strlen($string);
+		$expected = 7;
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$result = mb_strlen($string);
+		$expected = 7;
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$result = mb_strlen($string);
+		$expected = 6;
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testMultibyteStrlen method
+ *
+ * @access public
+ * @return void
+ */
+	function testMultibyteStrlen() {
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$result = Multibyte::strlen($string);
+		$expected = 36;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$result = Multibyte::strlen($string);
+		$expected = 30;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$result = Multibyte::strlen($string);
+		$expected = 61;
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$result = Multibyte::strlen($string);
+		$expected = 94;
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$result = Multibyte::strlen($string);
+		$expected = 40;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$result = Multibyte::strlen($string);
+		$expected = 100;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$result = Multibyte::strlen($string);
+		$expected = 100;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$result = Multibyte::strlen($string);
+		$expected = 100;
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$result = Multibyte::strlen($string);
+		$expected = 100;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$result = Multibyte::strlen($string);
+		$expected = 28;
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$result = Multibyte::strlen($string);
+		$expected = 49;
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$result = Multibyte::strlen($string);
+		$expected = 15;
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$result = Multibyte::strlen($string);
+		$expected = 47;
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$result = Multibyte::strlen($string);
+		$expected = 96;
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$result = Multibyte::strlen($string);
+		$expected = 59;
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$result = Multibyte::strlen($string);
+		$expected = 100;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$result = Multibyte::strlen($string);
+		$expected = 65;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$result = Multibyte::strlen($string);
+		$expected = 76;
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$result = Multibyte::strlen($string);
+		$expected = 26;
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$result = Multibyte::strlen($string);
+		$expected = 24;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$result = Multibyte::strlen($string);
+		$expected = 38;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$result = Multibyte::strlen($string);
+		$expected = 13;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$result = Multibyte::strlen($string);
+		$expected = 13;
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$result = Multibyte::strlen($string);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$result = Multibyte::strlen($string);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$result = Multibyte::strlen($string);
+		$expected = 7;
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$result = Multibyte::strlen($string);
+		$expected = 7;
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$result = Multibyte::strlen($string);
+		$expected = 6;
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testUsingMbStrpos method
+ *
+ * @access public
+ * @return void
+ */
+	function testUsingMbStrpos() {
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$find = 'F';
+		$result = mb_strpos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ABCDEFGHIJKLMNOPQFRSTUVWXYZ0123456789';
+		$find = 'F';
+		$result = mb_strpos($string, $find, 6);
+		$expected = 17;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$find = 'Å';
+		$result = mb_strpos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÅÙÚÛÜÝÞ';
+		$find = 'Å';
+		$result = mb_strpos($string, $find, 6);
+		$expected = 24;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = mb_strpos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁĊŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = mb_strpos($string, $find, 6);
+		$expected = 32;
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$find = 'F';
+		$result = mb_strpos($string, $find);
+		$expected = 37;
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$find = 'µ';
+		$result = mb_strpos($string, $find);
+		$expected = 20;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$find = 'é';
+		$result = mb_strpos($string, $find);
+		$expected = 32;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$find = 'Ņ';
+		$result = mb_strpos($string, $find);
+		$expected = 24;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$find = 'Ƹ';
+		$result = mb_strpos($string, $find);
+		$expected = 39;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$find = 'ƹ';
+		$result = mb_strpos($string, $find);
+		$expected = 40;
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$find = 'ʀ';
+		$result = mb_strpos($string, $find);
+		$expected = 39;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$find = 'Ї';
+		$result = mb_strpos($string, $find);
+		$expected = 7;
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'Р';
+		$result = mb_strpos($string, $find);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'р';
+		$result = mb_strpos($string, $find, 5);
+		$expected = 36;
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$find = 'ن';
+		$result = mb_strpos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$find = '✿';
+		$result = mb_strpos($string, $find);
+		$expected = 15;
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$find = '⺐';
+		$result = mb_strpos($string, $find);
+		$expected = 16;
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$find = '⽤';
+		$result = mb_strpos($string, $find);
+		$expected = 31;
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$find = '눻';
+		$result = mb_strpos($string, $find);
+		$expected = 26;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$find = 'ﺞ';
+		$result = mb_strpos($string, $find);
+		$expected = 46;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$find = 'ﻞ';
+		$result = mb_strpos($string, $find);
+		$expected = 45;
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'k';
+		$result = mb_strpos($string, $find);
+		$expected = 10;
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'k';
+		$result = mb_strpos($string, $find);
+		$expected = 10;
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$find = 'ア';
+		$result = mb_strpos($string, $find);
+		$expected = 16;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$find = 'ハ';
+		$result = mb_strpos($string, $find);
+		$expected = 17;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ő';
+		$result = mb_strpos($string, $find);
+		$expected = 8;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ő';
+		$result = mb_strpos($string, $find);
+		$expected = 8;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'őř';
+		$result = mb_strpos($string, $find);
+		$expected = 8;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'o';
+		$result = mb_strpos($string, $find);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'o';
+		$result = mb_strpos($string, $find, 5);
+		$expected = 8;
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'n';
+		$result = mb_strpos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'n';
+		$result = mb_strpos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'ć';
+		$result = mb_strpos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'ć';
+		$result = mb_strpos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'ž';
+		$result = mb_strpos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$find = '设';
+		$result = mb_strpos($string, $find);
+		$expected = 3;
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '周';
+		$result = mb_strpos($string, $find);
+		$expected = 3;
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '一周';
+		$result = mb_strpos($string, $find);
+		$expected = false;
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testMultibyteStrpos method
+ *
+ * @access public
+ * @return void
+ */
+	function testMultibyteStrpos() {
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$find = 'F';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ABCDEFGHIJKLMNOPQFRSTUVWXYZ0123456789';
+		$find = 'F';
+		$result = Multibyte::strpos($string, $find, 6);
+		$expected = 17;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$find = 'Å';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÅÙÚÛÜÝÞ';
+		$find = 'Å';
+		$result = Multibyte::strpos($string, $find, 6);
+		$expected = 24;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁĊŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = Multibyte::strpos($string, $find, 6);
+		$expected = 32;
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$find = 'F';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 37;
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$find = 'µ';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 20;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$find = 'é';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 32;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$find = 'Ņ';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 24;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$find = 'Ƹ';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 39;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$find = 'ƹ';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 40;
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$find = 'ʀ';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 39;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$find = 'Ї';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 7;
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'Р';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'р';
+		$result = Multibyte::strpos($string, $find, 5);
+		$expected = 36;
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$find = 'ن';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$find = '✿';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 15;
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$find = '⺐';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 16;
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$find = '⽤';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 31;
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$find = '눻';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 26;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$find = 'ﺞ';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 46;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$find = 'ﻞ';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 45;
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'k';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 10;
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'k';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 10;
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$find = 'ア';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 16;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$find = 'ハ';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 17;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ő';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 8;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ő';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 8;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'őř';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 8;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'o';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'o';
+		$result = Multibyte::strpos($string, $find, 5);
+		$expected = 8;
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'n';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'n';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'ć';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'ć';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'ž';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$find = '设';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 3;
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '周';
+		$result = Multibyte::strpos($string, $find);
+		$expected = 3;
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '一周';
+		$result = Multibyte::strpos($string, $find);
+		$expected = false;
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testUsingMbStrrchr method
+ *
+ * @access public
+ * @return void
+ */
+	function testUsingMbStrrchr() {
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$find = 'F';
+		$result = mb_strrchr($string, $find);
+		$expected = 'FGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$find = 'F';
+		$result = mb_strrchr($string, $find, true);
+		$expected = 'ABCDE';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$find = 'Å';
+		$result = mb_strrchr($string, $find);
+		$expected = 'ÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$find = 'Å';
+		$result = mb_strrchr($string, $find, true);
+		$expected = 'ÀÁÂÃÄ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = mb_strrchr($string, $find);
+		$expected = 'ĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = mb_strrchr($string, $find, true);
+		$expected = 'ĀĂĄĆĈ';
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$find = 'F';
+		$result = mb_strrchr($string, $find);
+		$expected = 'FGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$find = 'F';
+		$result = mb_strrchr($string, $find, true);
+		$expected = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDE';
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$find = 'µ';
+		$result = mb_strrchr($string, $find);
+		$expected = 'µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$find = 'µ';
+		$result = mb_strrchr($string, $find, true);
+		$expected = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$find = 'Þ';
+		$result = mb_strrchr($string, $find);
+		$expected = 'Þßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$find = 'Þ';
+		$result = mb_strrchr($string, $find, true);
+		$expected = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$find = 'Ņ';
+		$result = mb_strrchr($string, $find);
+		$expected = 'ŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$find = 'Ņ';
+		$result = mb_strrchr($string, $find, true);
+		$expected = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃń';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$find = 'Ƹ';
+		$result = mb_strrchr($string, $find);
+		$expected = 'ƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$this->assertEqual($result, $expected);
+
+		$find = 'Ƹ';
+		$result = mb_strrchr($string, $find, true);
+		$expected = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$find = 'ʀ';
+		$result = mb_strrchr($string, $find);
+		$expected = 'ʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$find = 'ʀ';
+		$result = mb_strrchr($string, $find, true);
+		$expected = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$find = 'Ї';
+		$result = mb_strrchr($string, $find);
+		$expected = 'ЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$find = 'Ї';
+		$result = mb_strrchr($string, $find, true);
+		$expected = 'ЀЁЂЃЄЅІ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'Р';
+		$result = mb_strrchr($string, $find);
+		$expected = 'РСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'Р';
+		$result = mb_strrchr($string, $find, true);
+		$expected = 'МНОП';
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$find = 'ن';
+		$result = mb_strrchr($string, $find);
+		$expected = 'نهوىيًٌٍَُ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$find = 'ن';
+		$result = mb_strrchr($string, $find, true);
+		$expected = 'فقكلم';
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$find = '✿';
+		$result = mb_strrchr($string, $find);
+		$expected = '✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$find = '✿';
+		$result = mb_strrchr($string, $find, true);
+		$expected = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾';
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$find = '⺐';
+		$result = mb_strrchr($string, $find);
+		$expected = '⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$find = '⺐';
+		$result = mb_strrchr($string, $find, true);
+		$expected = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏';
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$find = '⽤';
+		$result = mb_strrchr($string, $find);
+		$expected = '⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$find = '⽤';
+		$result = mb_strrchr($string, $find, true);
+		$expected = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣';
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$find = '눻';
+		$result = mb_strrchr($string, $find);
+		$expected = '눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$find = '눻';
+		$result = mb_strrchr($string, $find, true);
+		$expected = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$find = 'ﺞ';
+		$result = mb_strrchr($string, $find);
+		$expected = 'ﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$find = 'ﺞ';
+		$result = mb_strrchr($string, $find, true);
+		$expected = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$find = 'ﻞ';
+		$result = mb_strrchr($string, $find);
+		$expected = 'ﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$find = 'ﻞ';
+		$result = mb_strrchr($string, $find, true);
+		$expected = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'k';
+		$result = mb_strrchr($string, $find);
+		$expected = 'klmnopqrstuvwxyz';
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'k';
+		$result = mb_strrchr($string, $find, true);
+		$expected = 'abcdefghij';
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$find = 'ア';
+		$result = mb_strrchr($string, $find);
+		$expected = 'アイウエオカキク';
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$find = 'ア';
+		$result = mb_strrchr($string, $find, true);
+		$expected = '。「」、・ヲァィゥェォャュョッー';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$find = 'ハ';
+		$result = mb_strrchr($string, $find);
+		$expected = 'ハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$find = 'ハ';
+		$result = mb_strrchr($string, $find, true);
+		$expected = 'ケコサシスセソタチツテトナニヌネノ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ő';
+		$result = mb_strrchr($string, $find);
+		$expected = 'őřļď!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ő';
+		$result = mb_strrchr($string, $find, true);
+		$expected = 'Ĥēĺļŏ, Ŵ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'o';
+		$result = mb_strrchr($string, $find);
+		$expected = 'orld!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'o';
+		$result = mb_strrchr($string, $find, true);
+		$expected = 'Hello, W';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'Wo';
+		$result = mb_strrchr($string, $find);
+		$expected = 'World!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'Wo';
+		$result = mb_strrchr($string, $find, true);
+		$expected = 'Hello, ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'll';
+		$result = mb_strrchr($string, $find);
+		$expected = 'llo, World!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'll';
+		$result = mb_strrchr($string, $find, true);
+		$expected = 'He';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'rld';
+		$result = mb_strrchr($string, $find);
+		$expected = 'rld!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'rld';
+		$result = mb_strrchr($string, $find, true);
+		$expected = 'Hello, Wo';
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'n';
+		$result = mb_strrchr($string, $find);
+		$expected = 'ni';
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'n';
+		$result = mb_strrchr($string, $find, true);
+		$expected = 'či';
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'ć';
+		$result = mb_strrchr($string, $find);
+		$expected = 'ći';
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'ć';
+		$result = mb_strrchr($string, $find, true);
+		$expected = 'mo';
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'ž';
+		$result = mb_strrchr($string, $find);
+		$expected = 'žavni';
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'ž';
+		$result = mb_strrchr($string, $find, true);
+		$expected = 'dr';
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$find = '设';
+		$result = mb_strrchr($string, $find);
+		$expected = '设为首页';
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$find = '设';
+		$result = mb_strrchr($string, $find, true);
+		$expected = '把百度';
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '周';
+		$result = mb_strrchr($string, $find);
+		$expected = '周永龍';
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '周';
+		$result = mb_strrchr($string, $find, true);
+		$expected = '一二三';
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '周龍';
+		$result = mb_strrchr($string, $find, true);
+		$expected = false;
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testMultibyteStrrchr method
+ *
+ * @access public
+ * @return void
+ */
+	function testMultibyteStrrchr() {
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$find = 'F';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = 'FGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$find = 'F';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = 'ABCDE';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$find = 'Å';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = 'ÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$find = 'Å';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = 'ÀÁÂÃÄ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = 'ĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = 'ĀĂĄĆĈ';
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$find = 'F';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = 'FGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$find = 'F';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDE';
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$find = 'µ';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = 'µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$find = 'µ';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$find = 'Þ';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = 'Þßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$find = 'Þ';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$find = 'Ņ';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = 'ŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$find = 'Ņ';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃń';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$find = 'Ƹ';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = 'ƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$this->assertEqual($result, $expected);
+
+		$find = 'Ƹ';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$find = 'ʀ';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = 'ʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$find = 'ʀ';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$find = 'Ї';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = 'ЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$find = 'Ї';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = 'ЀЁЂЃЄЅІ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'Р';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = 'РСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'Р';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = 'МНОП';
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$find = 'ن';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = 'نهوىيًٌٍَُ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$find = 'ن';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = 'فقكلم';
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$find = '✿';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = '✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$find = '✿';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾';
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$find = '⺐';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = '⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$find = '⺐';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏';
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$find = '⽤';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = '⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$find = '⽤';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣';
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$find = '눻';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = '눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$find = '눻';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$find = 'ﺞ';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = 'ﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$find = 'ﺞ';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$find = 'ﻞ';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = 'ﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$find = 'ﻞ';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'k';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = 'klmnopqrstuvwxyz';
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'k';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = 'abcdefghij';
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$find = 'ア';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = 'アイウエオカキク';
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$find = 'ア';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = '。「」、・ヲァィゥェォャュョッー';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$find = 'ハ';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = 'ハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$find = 'ハ';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = 'ケコサシスセソタチツテトナニヌネノ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ő';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = 'őřļď!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ő';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = 'Ĥēĺļŏ, Ŵ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'o';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = 'orld!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'o';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = 'Hello, W';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'Wo';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = 'World!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'Wo';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = 'Hello, ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'll';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = 'llo, World!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'll';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = 'He';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'rld';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = 'rld!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'rld';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = 'Hello, Wo';
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'n';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = 'ni';
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'n';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = 'či';
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'ć';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = 'ći';
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'ć';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = 'mo';
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'ž';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = 'žavni';
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'ž';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = 'dr';
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$find = '设';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = '设为首页';
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$find = '设';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = '把百度';
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '周';
+		$result = Multibyte::strrchr($string, $find);
+		$expected = '周永龍';
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '周';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = '一二三';
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '周龍';
+		$result = Multibyte::strrchr($string, $find, true);
+		$expected = false;
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testUsingMbStrrichr method
+ *
+ * @access public
+ * @return void
+ */
+	function testUsingMbStrrichr() {
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$find = 'F';
+		$result = mb_strrichr($string, $find);
+		$expected = 'FGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$find = 'F';
+		$result = mb_strrichr($string, $find, true);
+		$expected = 'ABCDE';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$find = 'Å';
+		$result = mb_strrichr($string, $find);
+		$expected = 'ÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$find = 'Å';
+		$result = mb_strrichr($string, $find, true);
+		$expected = 'ÀÁÂÃÄ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = mb_strrichr($string, $find);
+		$expected = 'ĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = mb_strrichr($string, $find, true);
+		$expected = 'ĀĂĄĆĈ';
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$find = 'F';
+		$result = mb_strrichr($string, $find);
+		$expected = 'fghijklmnopqrstuvwxyz{|}~';
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$find = 'F';
+		$result = mb_strrichr($string, $find, true);
+		$expected = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcde';
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$find = 'µ';
+		$result = mb_strrichr($string, $find);
+		$expected = 'µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$find = 'µ';
+		$result = mb_strrichr($string, $find, true);
+		$expected = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$find = 'Þ';
+		$result = mb_strrichr($string, $find);
+		$expected = 'þÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$find = 'Þ';
+		$result = mb_strrichr($string, $find, true);
+		$expected = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüý';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$find = 'Ņ';
+		$result = mb_strrichr($string, $find);
+		$expected = 'ņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$find = 'Ņ';
+		$result = mb_strrichr($string, $find, true);
+		$expected = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$find = 'Ƹ';
+		$result = mb_strrichr($string, $find);
+		$expected = 'ƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$this->assertEqual($result, $expected);
+
+		$find = 'Ƹ';
+		$result = mb_strrichr($string, $find, true);
+		$expected = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$find = 'ʀ';
+		$result = mb_strrichr($string, $find);
+		$expected = 'ʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$find = 'ʀ';
+		$result = mb_strrichr($string, $find, true);
+		$expected = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$find = 'Ї';
+		$result = mb_strrichr($string, $find);
+		$expected = 'ЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$find = 'Ї';
+		$result = mb_strrichr($string, $find, true);
+		$expected = 'ЀЁЂЃЄЅІ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'Р';
+		$result = mb_strrichr($string, $find);
+		$expected = 'рстуфхцчшщъыь';
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмноп';
+		$find = 'Р';
+		$result = mb_strrichr($string, $find, true);
+		$expected = 'МНОП';
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$find = 'ن';
+		$result = mb_strrichr($string, $find);
+		$expected = 'نهوىيًٌٍَُ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$find = 'ن';
+		$result = mb_strrichr($string, $find, true);
+		$expected = 'فقكلم';
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$find = '✿';
+		$result = mb_strrichr($string, $find);
+		$expected = '✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$find = '✿';
+		$result = mb_strrichr($string, $find, true);
+		$expected = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾';
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$find = '⺐';
+		$result = mb_strrichr($string, $find);
+		$expected = '⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$find = '⺐';
+		$result = mb_strrichr($string, $find, true);
+		$expected = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏';
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$find = '⽤';
+		$result = mb_strrichr($string, $find);
+		$expected = '⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$find = '⽤';
+		$result = mb_strrichr($string, $find, true);
+		$expected = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣';
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$find = '눻';
+		$result = mb_strrichr($string, $find);
+		$expected = '눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$find = '눻';
+		$result = mb_strrichr($string, $find, true);
+		$expected = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$find = 'ﺞ';
+		$result = mb_strrichr($string, $find);
+		$expected = 'ﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$find = 'ﺞ';
+		$result = mb_strrichr($string, $find, true);
+		$expected = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$find = 'ﻞ';
+		$result = mb_strrichr($string, $find);
+		$expected = 'ﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$find = 'ﻞ';
+		$result = mb_strrichr($string, $find, true);
+		$expected = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'k';
+		$result = mb_strrichr($string, $find);
+		$expected = 'klmnopqrstuvwxyz';
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'k';
+		$result = mb_strrichr($string, $find, true);
+		$expected = 'abcdefghij';
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$find = 'ア';
+		$result = mb_strrichr($string, $find);
+		$expected = 'アイウエオカキク';
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$find = 'ア';
+		$result = mb_strrichr($string, $find, true);
+		$expected = '。「」、・ヲァィゥェォャュョッー';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$find = 'ハ';
+		$result = mb_strrichr($string, $find);
+		$expected = 'ハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$find = 'ハ';
+		$result = mb_strrichr($string, $find, true);
+		$expected = 'ケコサシスセソタチツテトナニヌネノ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ő';
+		$result = mb_strrichr($string, $find);
+		$expected = 'őřļď!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ő';
+		$result = mb_strrichr($string, $find, true);
+		$expected = 'Ĥēĺļŏ, Ŵ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'o';
+		$result = mb_strrichr($string, $find);
+		$expected = 'orld!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'o';
+		$result = mb_strrichr($string, $find, true);
+		$expected = 'Hello, W';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'Wo';
+		$result = mb_strrichr($string, $find);
+		$expected = 'World!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'Wo';
+		$result = mb_strrichr($string, $find, true);
+		$expected = 'Hello, ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'll';
+		$result = mb_strrichr($string, $find);
+		$expected = 'llo, World!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'll';
+		$result = mb_strrichr($string, $find, true);
+		$expected = 'He';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'rld';
+		$result = mb_strrichr($string, $find);
+		$expected = 'rld!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'rld';
+		$result = mb_strrichr($string, $find, true);
+		$expected = 'Hello, Wo';
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'n';
+		$result = mb_strrichr($string, $find);
+		$expected = 'ni';
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'n';
+		$result = mb_strrichr($string, $find, true);
+		$expected = 'či';
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'ć';
+		$result = mb_strrichr($string, $find);
+		$expected = 'ći';
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'ć';
+		$result = mb_strrichr($string, $find, true);
+		$expected = 'mo';
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'ž';
+		$result = mb_strrichr($string, $find);
+		$expected = 'žavni';
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'ž';
+		$result = mb_strrichr($string, $find, true);
+		$expected = 'dr';
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$find = '设';
+		$result = mb_strrichr($string, $find);
+		$expected = '设为首页';
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$find = '设';
+		$result = mb_strrichr($string, $find, true);
+		$expected = '把百度';
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '周';
+		$result = mb_strrichr($string, $find);
+		$expected = '周永龍';
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '周';
+		$result = mb_strrichr($string, $find, true);
+		$expected = '一二三';
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$find = '百设';
+		$result = mb_strrichr($string, $find, true);
+		$expected = false;
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testMultibyteStrrichr method
+ *
+ * @access public
+ * @return void
+ */
+	function testMultibyteStrrichr() {
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$find = 'F';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = 'FGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$find = 'F';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = 'ABCDE';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$find = 'Å';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = 'ÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$find = 'Å';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = 'ÀÁÂÃÄ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = 'ĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = 'ĀĂĄĆĈ';
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$find = 'F';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = 'fghijklmnopqrstuvwxyz{|}~';
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$find = 'F';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcde';
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$find = 'µ';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = 'µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$find = 'µ';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$find = 'Þ';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = 'þÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$find = 'Þ';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüý';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$find = 'Ņ';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = 'ņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$find = 'Ņ';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$find = 'Ƹ';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = 'ƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$this->assertEqual($result, $expected);
+
+		$find = 'Ƹ';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$find = 'ʀ';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = 'ʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$find = 'ʀ';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$find = 'Ї';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = 'ЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$find = 'Ї';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = 'ЀЁЂЃЄЅІ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'Р';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = 'рстуфхцчшщъыь';
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмноп';
+		$find = 'Р';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = 'МНОП';
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$find = 'ن';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = 'نهوىيًٌٍَُ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$find = 'ن';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = 'فقكلم';
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$find = '✿';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = '✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$find = '✿';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾';
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$find = '⺐';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = '⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$find = '⺐';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏';
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$find = '⽤';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = '⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$find = '⽤';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣';
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$find = '눻';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = '눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$find = '눻';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$find = 'ﺞ';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = 'ﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$find = 'ﺞ';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$find = 'ﻞ';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = 'ﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$find = 'ﻞ';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'k';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = 'klmnopqrstuvwxyz';
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'k';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = 'abcdefghij';
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$find = 'ア';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = 'アイウエオカキク';
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$find = 'ア';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = '。「」、・ヲァィゥェォャュョッー';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$find = 'ハ';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = 'ハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$find = 'ハ';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = 'ケコサシスセソタチツテトナニヌネノ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ő';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = 'őřļď!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ő';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = 'Ĥēĺļŏ, Ŵ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'o';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = 'orld!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'o';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = 'Hello, W';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'Wo';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = 'World!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'Wo';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = 'Hello, ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'll';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = 'llo, World!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'll';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = 'He';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'rld';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = 'rld!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'rld';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = 'Hello, Wo';
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'n';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = 'ni';
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'n';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = 'či';
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'ć';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = 'ći';
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'ć';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = 'mo';
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'ž';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = 'žavni';
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'ž';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = 'dr';
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$find = '设';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = '设为首页';
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$find = '设';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = '把百度';
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '周';
+		$result = Multibyte::strrichr($string, $find);
+		$expected = '周永龍';
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '周';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = '一二三';
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$find = '百设';
+		$result = Multibyte::strrichr($string, $find, true);
+		$expected = false;
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testUsingMbStrripos method
+ *
+ * @access public
+ * @return void
+ */
+	function testUsingMbStrripos() {
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$find = 'F';
+		$result = mb_strripos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ABCDEFGHIJKLMNOPQFRSTUVWXYZ0123456789';
+		$find = 'F';
+		$result = mb_strripos($string, $find, 6);
+		$expected = 17;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$find = 'Å';
+		$result = mb_strripos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÅÙÚÛÜÝÞ';
+		$find = 'Å';
+		$result = mb_strripos($string, $find, 6);
+		$expected = 24;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÅÙÚÛÜÝÞ';
+		$find = 'ÓÔ';
+		$result = mb_strripos($string, $find);
+		$expected = 19;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = mb_strripos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁĊŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = mb_strripos($string, $find, 6);
+		$expected = 32;
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$find = 'F';
+		$result = mb_strripos($string, $find);
+		$expected = 69;
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$find = 'µ';
+		$result = mb_strripos($string, $find);
+		$expected = 20;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$find = 'é';
+		$result = mb_strripos($string, $find);
+		$expected = 32;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$find = 'Ņ';
+		$result = mb_strripos($string, $find);
+		$expected = 25;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$find = 'Ƹ';
+		$result = mb_strripos($string, $find);
+		$expected = 40;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$find = 'ƹ';
+		$result = mb_strripos($string, $find);
+		$expected = 40;
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$find = 'ʀ';
+		$result = mb_strripos($string, $find);
+		$expected = 39;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$find = 'Ї';
+		$result = mb_strripos($string, $find);
+		$expected = 7;
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'Р';
+		$result = mb_strripos($string, $find);
+		$expected = 36;
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'р';
+		$result = mb_strripos($string, $find, 5);
+		$expected = 36;
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$find = 'ن';
+		$result = mb_strripos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$find = '✿';
+		$result = mb_strripos($string, $find);
+		$expected = 15;
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$find = '⺐';
+		$result = mb_strripos($string, $find);
+		$expected = 16;
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$find = '⽤';
+		$result = mb_strripos($string, $find);
+		$expected = 31;
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$find = '눻';
+		$result = mb_strripos($string, $find);
+		$expected = 26;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$find = 'ﺞ';
+		$result = mb_strripos($string, $find);
+		$expected = 46;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$find = 'ﻞ';
+		$result = mb_strripos($string, $find);
+		$expected = 45;
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'k';
+		$result = mb_strripos($string, $find);
+		$expected = 10;
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'k';
+		$result = mb_strripos($string, $find);
+		$expected = 10;
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnoppqrstuvwxyz';
+		$find = 'pp';
+		$result = mb_strripos($string, $find);
+		$expected = 15;
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$find = 'ア';
+		$result = mb_strripos($string, $find);
+		$expected = 16;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$find = 'ハ';
+		$result = mb_strripos($string, $find);
+		$expected = 17;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ő';
+		$result = mb_strripos($string, $find);
+		$expected = 8;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ő';
+		$result = mb_strripos($string, $find);
+		$expected = 8;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'o';
+		$result = mb_strripos($string, $find);
+		$expected = 8;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'o';
+		$result = mb_strripos($string, $find, 5);
+		$expected = 8;
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'n';
+		$result = mb_strripos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'n';
+		$result = mb_strripos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'ć';
+		$result = mb_strripos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'ć';
+		$result = mb_strripos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'ž';
+		$result = mb_strripos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$find = '设';
+		$result = mb_strripos($string, $find);
+		$expected = 3;
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '周';
+		$result = mb_strripos($string, $find);
+		$expected = 3;
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'dž';
+		$result = mb_strripos($string, $find);
+		$expected = false;
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testMultibyteStrripos method
+ *
+ * @access public
+ * @return void
+ */
+	function testMultibyteStrripos() {
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$find = 'F';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ABCDEFGHIJKLMNOPQFRSTUVWXYZ0123456789';
+		$find = 'F';
+		$result = Multibyte::strripos($string, $find, 6);
+		$expected = 17;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$find = 'Å';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÅÙÚÛÜÝÞ';
+		$find = 'Å';
+		$result = Multibyte::strripos($string, $find, 6);
+		$expected = 24;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÅÙÚÛÜÝÞ';
+		$find = 'ÓÔ';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 19;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁĊŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = Multibyte::strripos($string, $find, 6);
+		$expected = 32;
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$find = 'F';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 69;
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$find = 'µ';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 20;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$find = 'é';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 32;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$find = 'Ņ';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 25;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$find = 'Ƹ';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 40;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$find = 'ƹ';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 40;
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$find = 'ʀ';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 39;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$find = 'Ї';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 7;
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'Р';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 36;
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'р';
+		$result = Multibyte::strripos($string, $find, 5);
+		$expected = 36;
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$find = 'ن';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$find = '✿';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 15;
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$find = '⺐';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 16;
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$find = '⽤';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 31;
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$find = '눻';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 26;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$find = 'ﺞ';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 46;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$find = 'ﻞ';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 45;
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'k';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 10;
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'k';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 10;
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnoppqrstuvwxyz';
+		$find = 'pp';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 15;
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$find = 'ア';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 16;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$find = 'ハ';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 17;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ő';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 8;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ő';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 8;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'o';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 8;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'o';
+		$result = Multibyte::strripos($string, $find, 5);
+		$expected = 8;
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'n';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'n';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'ć';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'ć';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'ž';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$find = '设';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 3;
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '周';
+		$result = Multibyte::strripos($string, $find);
+		$expected = 3;
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'dž';
+		$result = Multibyte::strripos($string, $find);
+		$expected = false;
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testUsingMbStrrpos method
+ *
+ * @access public
+ * @return void
+ */
+	function testUsingMbStrrpos() {
+		$skip = extension_loaded('mbstring') && version_compare(PHP_VERSION, '5.2.0', '<');
+		if ($this->skipIf($skip, '%s PHP version does not support $offset parameter in mb_strrpos().')) {
+			return;
+		}
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$find = 'F';
+		$result = mb_strrpos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ABCDEFGHIJKLMNOPQFRSTUVWXYZ0123456789';
+		$find = 'F';
+		$result = mb_strrpos($string, $find, 6);
+		$expected = 17;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$find = 'Å';
+		$result = mb_strrpos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÅÙÚÛÜÝÞ';
+		$find = 'ÙÚ';
+		$result = mb_strrpos($string, $find);
+		$expected = 25;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÅÙÚÛÜÝÞ';
+		$find = 'Å';
+		$result = mb_strrpos($string, $find, 6);
+		$expected = 24;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = mb_strrpos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁĊŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = mb_strrpos($string, $find, 6);
+		$expected = 32;
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$find = 'F';
+		$result = mb_strrpos($string, $find);
+		$expected = 37;
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$find = 'µ';
+		$result = mb_strrpos($string, $find);
+		$expected = 20;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$find = 'é';
+		$result = mb_strrpos($string, $find);
+		$expected = 32;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$find = 'Ņ';
+		$result = mb_strrpos($string, $find);
+		$expected = 24;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$find = 'Ƹ';
+		$result = mb_strrpos($string, $find);
+		$expected = 39;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$find = 'ƹ';
+		$result = mb_strrpos($string, $find);
+		$expected = 40;
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$find = 'ʀ';
+		$result = mb_strrpos($string, $find);
+		$expected = 39;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$find = 'Ї';
+		$result = mb_strrpos($string, $find);
+		$expected = 7;
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'Р';
+		$result = mb_strrpos($string, $find);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'р';
+		$result = mb_strrpos($string, $find, 5);
+		$expected = 36;
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$find = 'ن';
+		$result = mb_strrpos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$find = '✿';
+		$result = mb_strrpos($string, $find);
+		$expected = 15;
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$find = '⺐';
+		$result = mb_strrpos($string, $find);
+		$expected = 16;
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$find = '⽤';
+		$result = mb_strrpos($string, $find);
+		$expected = 31;
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$find = '눻';
+		$result = mb_strrpos($string, $find);
+		$expected = 26;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$find = 'ﺞ';
+		$result = mb_strrpos($string, $find);
+		$expected = 46;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$find = 'ﻞ';
+		$result = mb_strrpos($string, $find);
+		$expected = 45;
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'k';
+		$result = mb_strrpos($string, $find);
+		$expected = 10;
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'k';
+		$result = mb_strrpos($string, $find);
+		$expected = 10;
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnoppqrstuvwxyz';
+		$find = 'pp';
+		$result = mb_strrpos($string, $find);
+		$expected = 15;
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$find = 'ア';
+		$result = mb_strrpos($string, $find);
+		$expected = 16;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$find = 'ハ';
+		$result = mb_strrpos($string, $find);
+		$expected = 17;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ő';
+		$result = mb_strrpos($string, $find);
+		$expected = 8;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ő';
+		$result = mb_strrpos($string, $find);
+		$expected = 8;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'o';
+		$result = mb_strrpos($string, $find);
+		$expected = 8;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'o';
+		$result = mb_strrpos($string, $find, 5);
+		$expected = 8;
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'n';
+		$result = mb_strrpos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'n';
+		$result = mb_strrpos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'ć';
+		$result = mb_strrpos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'ć';
+		$result = mb_strrpos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'ž';
+		$result = mb_strrpos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$find = '设';
+		$result = mb_strrpos($string, $find);
+		$expected = 3;
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '周';
+		$result = mb_strrpos($string, $find);
+		$expected = 3;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'H';
+		$result = mb_strrpos($string, $find);
+		$expected = false;
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testMultibyteStrrpos method
+ *
+ * @access public
+ * @return void
+ */
+	function testMultibyteStrrpos() {
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$find = 'F';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ABCDEFGHIJKLMNOPQFRSTUVWXYZ0123456789';
+		$find = 'F';
+		$result = Multibyte::strrpos($string, $find, 6);
+		$expected = 17;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$find = 'Å';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÅÙÚÛÜÝÞ';
+		$find = 'Å';
+		$result = Multibyte::strrpos($string, $find, 6);
+		$expected = 24;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÅÙÚÛÜÝÞ';
+		$find = 'ÙÚ';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 25;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁĊŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = Multibyte::strrpos($string, $find, 6);
+		$expected = 32;
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$find = 'F';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 37;
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$find = 'µ';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 20;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$find = 'é';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 32;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$find = 'Ņ';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 24;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$find = 'Ƹ';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 39;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$find = 'ƹ';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 40;
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$find = 'ʀ';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 39;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$find = 'Ї';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 7;
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'Р';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'р';
+		$result = Multibyte::strrpos($string, $find, 5);
+		$expected = 36;
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$find = 'ن';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$find = '✿';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 15;
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$find = '⺐';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 16;
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$find = '⽤';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 31;
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$find = '눻';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 26;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$find = 'ﺞ';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 46;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$find = 'ﻞ';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 45;
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'k';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 10;
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'k';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 10;
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnoppqrstuvwxyz';
+		$find = 'pp';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 15;
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$find = 'ア';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 16;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$find = 'ハ';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 17;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ő';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 8;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ő';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 8;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'o';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 8;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'o';
+		$result = Multibyte::strrpos($string, $find, 5);
+		$expected = 8;
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'n';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'n';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'ć';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'ć';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'ž';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$find = '设';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 3;
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '周';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = 3;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'H';
+		$result = Multibyte::strrpos($string, $find);
+		$expected = false;
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testUsingMbStrstr method
+ *
+ * @access public
+ * @return void
+ */
+	function testUsingMbStrstr() {
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$find = 'F';
+		$result = mb_strstr($string, $find);
+		$expected = 'FGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$find = 'F';
+		$result = mb_strstr($string, $find, true);
+		$expected = 'ABCDE';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$find = 'Å';
+		$result = mb_strstr($string, $find);
+		$expected = 'ÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$find = 'Å';
+		$result = mb_strstr($string, $find, true);
+		$expected = 'ÀÁÂÃÄ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = mb_strstr($string, $find);
+		$expected = 'ĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = mb_strstr($string, $find, true);
+		$expected = 'ĀĂĄĆĈ';
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$find = 'F';
+		$result = mb_strstr($string, $find);
+		$expected = 'FGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$find = 'F';
+		$result = mb_strstr($string, $find, true);
+		$expected = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDE';
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$find = 'µ';
+		$result = mb_strstr($string, $find);
+		$expected = 'µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$find = 'µ';
+		$result = mb_strstr($string, $find, true);
+		$expected = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$find = 'Þ';
+		$result = mb_strstr($string, $find);
+		$expected = 'Þßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$find = 'Þ';
+		$result = mb_strstr($string, $find, true);
+		$expected = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$find = 'Ņ';
+		$result = mb_strstr($string, $find);
+		$expected = 'ŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$find = 'Ņ';
+		$result = mb_strstr($string, $find, true);
+		$expected = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃń';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$find = 'Ƹ';
+		$result = mb_strstr($string, $find);
+		$expected = 'ƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$this->assertEqual($result, $expected);
+
+		$find = 'Ƹ';
+		$result = mb_strstr($string, $find, true);
+		$expected = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$find = 'ʀ';
+		$result = mb_strstr($string, $find);
+		$expected = 'ʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$find = 'ʀ';
+		$result = mb_strstr($string, $find, true);
+		$expected = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$find = 'Ї';
+		$result = mb_strstr($string, $find);
+		$expected = 'ЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$find = 'Ї';
+		$result = mb_strstr($string, $find, true);
+		$expected = 'ЀЁЂЃЄЅІ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'Р';
+		$result = mb_strstr($string, $find);
+		$expected = 'РСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'Р';
+		$result = mb_strstr($string, $find, true);
+		$expected = 'МНОП';
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$find = 'ن';
+		$result = mb_strstr($string, $find);
+		$expected = 'نهوىيًٌٍَُ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$find = 'ن';
+		$result = mb_strstr($string, $find, true);
+		$expected = 'فقكلم';
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$find = '✿';
+		$result = mb_strstr($string, $find);
+		$expected = '✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$find = '✿';
+		$result = mb_strstr($string, $find, true);
+		$expected = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾';
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$find = '⺐';
+		$result = mb_strstr($string, $find);
+		$expected = '⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$find = '⺐';
+		$result = mb_strstr($string, $find, true);
+		$expected = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏';
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$find = '⽤';
+		$result = mb_strstr($string, $find);
+		$expected = '⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$find = '⽤';
+		$result = mb_strstr($string, $find, true);
+		$expected = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣';
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$find = '눻';
+		$result = mb_strstr($string, $find);
+		$expected = '눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$find = '눻';
+		$result = mb_strstr($string, $find, true);
+		$expected = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$find = 'ﺞ';
+		$result = mb_strstr($string, $find);
+		$expected = 'ﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$find = 'ﺞ';
+		$result = mb_strstr($string, $find, true);
+		$expected = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$find = 'ﻞ';
+		$result = mb_strstr($string, $find);
+		$expected = 'ﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$find = 'ﻞ';
+		$result = mb_strstr($string, $find, true);
+		$expected = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'k';
+		$result = mb_strstr($string, $find);
+		$expected = 'klmnopqrstuvwxyz';
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'k';
+		$result = mb_strstr($string, $find, true);
+		$expected = 'abcdefghij';
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'K';
+		$result = mb_strstr($string, $find);
+		$expected = false;
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$find = 'ア';
+		$result = mb_strstr($string, $find);
+		$expected = 'アイウエオカキク';
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$find = 'ア';
+		$result = mb_strstr($string, $find, true);
+		$expected = '。「」、・ヲァィゥェォャュョッー';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$find = 'ハ';
+		$result = mb_strstr($string, $find);
+		$expected = 'ハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$find = 'ハ';
+		$result = mb_strstr($string, $find, true);
+		$expected = 'ケコサシスセソタチツテトナニヌネノ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ő';
+		$result = mb_strstr($string, $find);
+		$expected = 'őřļď!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ő';
+		$result = mb_strstr($string, $find, true);
+		$expected = 'Ĥēĺļŏ, Ŵ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ĺļ';
+		$result = mb_strstr($string, $find, true);
+		$expected = 'Ĥē';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'o';
+		$result = mb_strstr($string, $find);
+		$expected = 'o, World!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'o';
+		$result = mb_strstr($string, $find, true);
+		$expected = 'Hell';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'Wo';
+		$result = mb_strstr($string, $find);
+		$expected = 'World!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'Wo';
+		$result = mb_strstr($string, $find, true);
+		$expected = 'Hello, ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'll';
+		$result = mb_strstr($string, $find);
+		$expected = 'llo, World!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'll';
+		$result = mb_strstr($string, $find, true);
+		$expected = 'He';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'rld';
+		$result = mb_strstr($string, $find);
+		$expected = 'rld!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'rld';
+		$result = mb_strstr($string, $find, true);
+		$expected = 'Hello, Wo';
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'n';
+		$result = mb_strstr($string, $find);
+		$expected = 'ni';
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'n';
+		$result = mb_strstr($string, $find, true);
+		$expected = 'či';
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'ć';
+		$result = mb_strstr($string, $find);
+		$expected = 'ći';
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'ć';
+		$result = mb_strstr($string, $find, true);
+		$expected = 'mo';
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'ž';
+		$result = mb_strstr($string, $find);
+		$expected = 'žavni';
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'ž';
+		$result = mb_strstr($string, $find, true);
+		$expected = 'dr';
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$find = '设';
+		$result = mb_strstr($string, $find);
+		$expected = '设为首页';
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$find = '设';
+		$result = mb_strstr($string, $find, true);
+		$expected = '把百度';
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '周';
+		$result = mb_strstr($string, $find);
+		$expected = '周永龍';
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '周';
+		$result = mb_strstr($string, $find, true);
+		$expected = '一二三';
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '二周';
+		$result = mb_strstr($string, $find);
+		$expected = false;
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testMultibyteStrstr method
+ *
+ * @access public
+ * @return void
+ */
+	function testMultibyteStrstr() {
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$find = 'F';
+		$result = Multibyte::strstr($string, $find);
+		$expected = 'FGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$find = 'F';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = 'ABCDE';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$find = 'Å';
+		$result = Multibyte::strstr($string, $find);
+		$expected = 'ÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$find = 'Å';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = 'ÀÁÂÃÄ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = Multibyte::strstr($string, $find);
+		$expected = 'ĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = 'ĀĂĄĆĈ';
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$find = 'F';
+		$result = Multibyte::strstr($string, $find);
+		$expected = 'FGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$find = 'F';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDE';
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$find = 'µ';
+		$result = Multibyte::strstr($string, $find);
+		$expected = 'µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$find = 'µ';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$find = 'Þ';
+		$result = Multibyte::strstr($string, $find);
+		$expected = 'Þßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$find = 'Þ';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$find = 'Ņ';
+		$result = Multibyte::strstr($string, $find);
+		$expected = 'ŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$find = 'Ņ';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃń';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$find = 'Ƹ';
+		$result = Multibyte::strstr($string, $find);
+		$expected = 'ƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$this->assertEqual($result, $expected);
+
+		$find = 'Ƹ';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$find = 'ʀ';
+		$result = Multibyte::strstr($string, $find);
+		$expected = 'ʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$find = 'ʀ';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$find = 'Ї';
+		$result = Multibyte::strstr($string, $find);
+		$expected = 'ЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$find = 'Ї';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = 'ЀЁЂЃЄЅІ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'Р';
+		$result = Multibyte::strstr($string, $find);
+		$expected = 'РСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$find = 'Р';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = 'МНОП';
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$find = 'ن';
+		$result = Multibyte::strstr($string, $find);
+		$expected = 'نهوىيًٌٍَُ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$find = 'ن';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = 'فقكلم';
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$find = '✿';
+		$result = Multibyte::strstr($string, $find);
+		$expected = '✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$find = '✿';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾';
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$find = '⺐';
+		$result = Multibyte::strstr($string, $find);
+		$expected = '⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$find = '⺐';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏';
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$find = '⽤';
+		$result = Multibyte::strstr($string, $find);
+		$expected = '⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$find = '⽤';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣';
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$find = '눻';
+		$result = Multibyte::strstr($string, $find);
+		$expected = '눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$find = '눻';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$find = 'ﺞ';
+		$result = Multibyte::strstr($string, $find);
+		$expected = 'ﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$find = 'ﺞ';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$find = 'ﻞ';
+		$result = Multibyte::strstr($string, $find);
+		$expected = 'ﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$find = 'ﻞ';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'k';
+		$result = Multibyte::strstr($string, $find);
+		$expected = 'klmnopqrstuvwxyz';
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'k';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = 'abcdefghij';
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$find = 'K';
+		$result = Multibyte::strstr($string, $find);
+		$expected = false;
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$find = 'ア';
+		$result = Multibyte::strstr($string, $find);
+		$expected = 'アイウエオカキク';
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$find = 'ア';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = '。「」、・ヲァィゥェォャュョッー';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$find = 'ハ';
+		$result = Multibyte::strstr($string, $find);
+		$expected = 'ハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$find = 'ハ';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = 'ケコサシスセソタチツテトナニヌネノ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ő';
+		$result = Multibyte::strstr($string, $find);
+		$expected = 'őřļď!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ő';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = 'Ĥēĺļŏ, Ŵ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ĺļ';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = 'Ĥē';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'o';
+		$result = Multibyte::strstr($string, $find);
+		$expected = 'o, World!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'o';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = 'Hell';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'Wo';
+		$result = Multibyte::strstr($string, $find);
+		$expected = 'World!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'Wo';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = 'Hello, ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'll';
+		$result = Multibyte::strstr($string, $find);
+		$expected = 'llo, World!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'll';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = 'He';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'rld';
+		$result = Multibyte::strstr($string, $find);
+		$expected = 'rld!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'rld';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = 'Hello, Wo';
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'n';
+		$result = Multibyte::strstr($string, $find);
+		$expected = 'ni';
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'n';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = 'či';
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'ć';
+		$result = Multibyte::strstr($string, $find);
+		$expected = 'ći';
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'ć';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = 'mo';
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'ž';
+		$result = Multibyte::strstr($string, $find);
+		$expected = 'žavni';
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'ž';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = 'dr';
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$find = '设';
+		$result = Multibyte::strstr($string, $find);
+		$expected = '设为首页';
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$find = '设';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = '把百度';
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '周';
+		$result = Multibyte::strstr($string, $find);
+		$expected = '周永龍';
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '周';
+		$result = Multibyte::strstr($string, $find, true);
+		$expected = '一二三';
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '二周';
+		$result = Multibyte::strstr($string, $find);
+		$expected = false;
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testUsingMbStrtolower method
+ *
+ * @access public
+ * @return void
+ */
+	function testUsingMbStrtolower() {
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~';
+		$result = mb_strtolower($string);
+		$expected = '!"#$%&\'()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@';
+		$result = mb_strtolower($string);
+		$expected = '!"#$%&\'()*+,-./0123456789:;<=>?@';
+		$this->assertEqual($result, $expected);
+
+		$string = 'À';
+		$result = mb_strtolower($string);
+		$expected = 'à';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Á';
+		$result = mb_strtolower($string);
+		$expected = 'á';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Â';
+		$result = mb_strtolower($string);
+		$expected = 'â';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ã';
+		$result = mb_strtolower($string);
+		$expected = 'ã';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ä';
+		$result = mb_strtolower($string);
+		$expected = 'ä';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Å';
+		$result = mb_strtolower($string);
+		$expected = 'å';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Æ';
+		$result = mb_strtolower($string);
+		$expected = 'æ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ç';
+		$result = mb_strtolower($string);
+		$expected = 'ç';
+		$this->assertEqual($result, $expected);
+
+		$string = 'È';
+		$result = mb_strtolower($string);
+		$expected = 'è';
+		$this->assertEqual($result, $expected);
+
+		$string = 'É';
+		$result = mb_strtolower($string);
+		$expected = 'é';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ê';
+		$result = mb_strtolower($string);
+		$expected = 'ê';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ë';
+		$result = mb_strtolower($string);
+		$expected = 'ë';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ì';
+		$result = mb_strtolower($string);
+		$expected = 'ì';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Í';
+		$result = mb_strtolower($string);
+		$expected = 'í';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Î';
+		$result = mb_strtolower($string);
+		$expected = 'î';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ï';
+		$result = mb_strtolower($string);
+		$expected = 'ï';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ð';
+		$result = mb_strtolower($string);
+		$expected = 'ð';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ñ';
+		$result = mb_strtolower($string);
+		$expected = 'ñ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ò';
+		$result = mb_strtolower($string);
+		$expected = 'ò';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ó';
+		$result = mb_strtolower($string);
+		$expected = 'ó';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ô';
+		$result = mb_strtolower($string);
+		$expected = 'ô';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Õ';
+		$result = mb_strtolower($string);
+		$expected = 'õ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ö';
+		$result = mb_strtolower($string);
+		$expected = 'ö';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ø';
+		$result = mb_strtolower($string);
+		$expected = 'ø';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ù';
+		$result = mb_strtolower($string);
+		$expected = 'ù';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ú';
+		$result = mb_strtolower($string);
+		$expected = 'ú';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Û';
+		$result = mb_strtolower($string);
+		$expected = 'û';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ü';
+		$result = mb_strtolower($string);
+		$expected = 'ü';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ý';
+		$result = mb_strtolower($string);
+		$expected = 'ý';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Þ';
+		$result = mb_strtolower($string);
+		$expected = 'þ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$result = mb_strtolower($string);
+		$expected = 'àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ā';
+		$result = mb_strtolower($string);
+		$expected = 'ā';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ă';
+		$result = mb_strtolower($string);
+		$expected = 'ă';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ą';
+		$result = mb_strtolower($string);
+		$expected = 'ą';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ć';
+		$result = mb_strtolower($string);
+		$expected = 'ć';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĉ';
+		$result = mb_strtolower($string);
+		$expected = 'ĉ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ċ';
+		$result = mb_strtolower($string);
+		$expected = 'ċ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Č';
+		$result = mb_strtolower($string);
+		$expected = 'č';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ď';
+		$result = mb_strtolower($string);
+		$expected = 'ď';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Đ';
+		$result = mb_strtolower($string);
+		$expected = 'đ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ē';
+		$result = mb_strtolower($string);
+		$expected = 'ē';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĕ';
+		$result = mb_strtolower($string);
+		$expected = 'ĕ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ė';
+		$result = mb_strtolower($string);
+		$expected = 'ė';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ę';
+		$result = mb_strtolower($string);
+		$expected = 'ę';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ě';
+		$result = mb_strtolower($string);
+		$expected = 'ě';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĝ';
+		$result = mb_strtolower($string);
+		$expected = 'ĝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ğ';
+		$result = mb_strtolower($string);
+		$expected = 'ğ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ġ';
+		$result = mb_strtolower($string);
+		$expected = 'ġ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ģ';
+		$result = mb_strtolower($string);
+		$expected = 'ģ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥ';
+		$result = mb_strtolower($string);
+		$expected = 'ĥ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ħ';
+		$result = mb_strtolower($string);
+		$expected = 'ħ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĩ';
+		$result = mb_strtolower($string);
+		$expected = 'ĩ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ī';
+		$result = mb_strtolower($string);
+		$expected = 'ī';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĭ';
+		$result = mb_strtolower($string);
+		$expected = 'ĭ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Į';
+		$result = mb_strtolower($string);
+		$expected = 'į';
+		$this->assertEqual($result, $expected);
+
+		$string = 'IJ';
+		$result = mb_strtolower($string);
+		$expected = 'ij';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĵ';
+		$result = mb_strtolower($string);
+		$expected = 'ĵ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ķ';
+		$result = mb_strtolower($string);
+		$expected = 'ķ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĺ';
+		$result = mb_strtolower($string);
+		$expected = 'ĺ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ļ';
+		$result = mb_strtolower($string);
+		$expected = 'ļ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ľ';
+		$result = mb_strtolower($string);
+		$expected = 'ľ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ŀ';
+		$result = mb_strtolower($string);
+		$expected = 'ŀ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ł';
+		$result = mb_strtolower($string);
+		$expected = 'ł';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ń';
+		$result = mb_strtolower($string);
+		$expected = 'ń';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ņ';
+		$result = mb_strtolower($string);
+		$expected = 'ņ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ň';
+		$result = mb_strtolower($string);
+		$expected = 'ň';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ŋ';
+		$result = mb_strtolower($string);
+		$expected = 'ŋ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ō';
+		$result = mb_strtolower($string);
+		$expected = 'ō';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ŏ';
+		$result = mb_strtolower($string);
+		$expected = 'ŏ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ő';
+		$result = mb_strtolower($string);
+		$expected = 'ő';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Œ';
+		$result = mb_strtolower($string);
+		$expected = 'œ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ŕ';
+		$result = mb_strtolower($string);
+		$expected = 'ŕ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ŗ';
+		$result = mb_strtolower($string);
+		$expected = 'ŗ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ř';
+		$result = mb_strtolower($string);
+		$expected = 'ř';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ś';
+		$result = mb_strtolower($string);
+		$expected = 'ś';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ŝ';
+		$result = mb_strtolower($string);
+		$expected = 'ŝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ş';
+		$result = mb_strtolower($string);
+		$expected = 'ş';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Š';
+		$result = mb_strtolower($string);
+		$expected = 'š';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ţ';
+		$result = mb_strtolower($string);
+		$expected = 'ţ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ť';
+		$result = mb_strtolower($string);
+		$expected = 'ť';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ŧ';
+		$result = mb_strtolower($string);
+		$expected = 'ŧ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ũ';
+		$result = mb_strtolower($string);
+		$expected = 'ũ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ū';
+		$result = mb_strtolower($string);
+		$expected = 'ū';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ŭ';
+		$result = mb_strtolower($string);
+		$expected = 'ŭ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ů';
+		$result = mb_strtolower($string);
+		$expected = 'ů';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ű';
+		$result = mb_strtolower($string);
+		$expected = 'ű';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ų';
+		$result = mb_strtolower($string);
+		$expected = 'ų';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ŵ';
+		$result = mb_strtolower($string);
+		$expected = 'ŵ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ŷ';
+		$result = mb_strtolower($string);
+		$expected = 'ŷ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ź';
+		$result = mb_strtolower($string);
+		$expected = 'ź';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ż';
+		$result = mb_strtolower($string);
+		$expected = 'ż';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ž';
+		$result = mb_strtolower($string);
+		$expected = 'ž';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$result = mb_strtolower($string);
+		$expected = 'āăąćĉċčďđēĕėęěĝğġģĥħĩīĭįijĵķĺļľŀłńņňŋōŏőœŕŗřśŝşšţťŧũūŭůűųŵŷźżž';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĤĒĹĻŎ, ŴŐŘĻĎ!';
+		$result = mb_strtolower($string);
+		$expected = 'ĥēĺļŏ, ŵőřļď!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĥēĺļŏ, ŵőřļď!';
+		$result = mb_strtolower($string);
+		$expected = 'ĥēĺļŏ, ŵőřļď!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ἈΙ';
+		$result = mb_strtolower($string);
+		$expected = 'ἀι';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ԀԂԄԆԈԊԌԎԐԒ';
+		$result = mb_strtolower($string);
+		$expected = 'ԁԃԅԇԉԋԍԏԐԒ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉՊՋՌՍՎՏՐՑՒՓՔՕՖև';
+		$result = mb_strtolower($string);
+		$expected = 'աբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆև';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ႠႡႢႣႤႥႦႧႨႩႪႫႬႭႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅ';
+		$result = mb_strtolower($string);
+		$expected = 'ႠႡႢႣႤႥႦႧႨႩႪႫႬႭႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ḀḂḄḆḈḊḌḎḐḒḔḖḘḚḜḞḠḢḤḦḨḪḬḮḰḲḴḶḸḺḼḾṀṂṄṆṈṊṌṎṐṒṔṖṘṚṜṞṠṢṤṦṨṪṬṮṰṲṴṶṸṺṼṾẀẂẄẆẈẊẌẎẐẒẔẖẗẘẙẚẠẢẤẦẨẪẬẮẰẲẴẶẸẺẼẾỀỂỄỆỈỊỌỎỐỒỔỖỘỚỜỞỠỢỤỦỨỪỬỮỰỲỴỶỸ';
+		$result = mb_strtolower($string);
+		$expected = 'ḁḃḅḇḉḋḍḏḑḓḕḗḙḛḝḟḡḣḥḧḩḫḭḯḱḳḵḷḹḻḽḿṁṃṅṇṉṋṍṏṑṓṕṗṙṛṝṟṡṣṥṧṩṫṭṯṱṳṵṷṹṻṽṿẁẃẅẇẉẋẍẏẑẓẕẖẗẘẙẚạảấầẩẫậắằẳẵặẹẻẽếềểễệỉịọỏốồổỗộớờởỡợụủứừửữựỳỵỷỹ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ΩKÅ';
+		$result = mb_strtolower($string);
+		$expected = 'ωkå';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ΩKÅ';
+		$result = mb_strtolower($string);
+		$expected = 'ωkå';
+		$this->assertEqual($result, $expected);
+
+/*
+mb_strtolower does not work for these strings.
+
+		$string = 'ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯↃ';
+		$result = mb_strtolower($string);
+		$expected = 'ⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿↄ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏ';
+		$result = mb_strtolower($string);
+		$expected = 'ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ⰀⰁⰂⰃⰄⰅⰆⰇⰈⰉⰊⰋⰌⰍⰎⰏⰐⰑⰒⰓⰔⰕⰖⰗⰘⰙⰚⰛⰜⰝⰞⰟⰠⰡⰢⰣⰤⰥⰦⰧⰨⰩⰪⰫⰬⰭⰮ';
+		$result = mb_strtolower($string);
+		$expected = 'ⰰⰱⰲⰳⰴⰵⰶⰷⰸⰹⰺⰻⰼⰽⰾⰿⱀⱁⱂⱃⱄⱅⱆⱇⱈⱉⱊⱋⱌⱍⱎⱏⱐⱑⱒⱓⱔⱕⱖⱗⱘⱙⱚⱛⱜⱝⱞ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ⲀⲂⲄⲆⲈⲊⲌⲎⲐⲒⲔⲖⲘⲚⲜⲞⲠⲢⲤⲦⲨⲪⲬⲮⲰⲲⲴⲶⲸⲺⲼⲾⳀⳂⳄⳆⳈⳊⳌⳎⳐⳒⳔⳖⳘⳚⳜⳞⳠⳢ';
+		$result = mb_strtolower($string);
+		$expected = 'ⲁⲃⲅⲇⲉⲋⲍⲏⲑⲓⲕⲗⲙⲛⲝⲟⲡⲣⲥⲧⲩⲫⲭⲯⲱⲳⲵⲷⲹⲻⲽⲿⳁⳃⳅⳇⳉⳋⳍⳏⳑⳓⳕⳗⳙⳛⳝⳟⳡⳣ';
+		$this->assertEqual($result, $expected);
+*/
+		$string = 'fffiflffifflſtstﬓﬔﬕﬖﬗ';
+		$result = mb_strtolower($string);
+		$expected = 'fffiflffifflſtstﬓﬔﬕﬖﬗ';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testMultibyteStrtolower method
+ *
+ * @access public
+ * @return void
+ */
+	function testMultibyteStrtolower() {
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~';
+		$result = Multibyte::strtolower($string);
+		$expected = '!"#$%&\'()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@';
+		$result = Multibyte::strtolower($string);
+		$expected = '!"#$%&\'()*+,-./0123456789:;<=>?@';
+		$this->assertEqual($result, $expected);
+
+		$string = 'À';
+		$result = Multibyte::strtolower($string);
+		$expected = 'à';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Á';
+		$result = Multibyte::strtolower($string);
+		$expected = 'á';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Â';
+		$result = Multibyte::strtolower($string);
+		$expected = 'â';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ã';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ã';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ä';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ä';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Å';
+		$result = Multibyte::strtolower($string);
+		$expected = 'å';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Æ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'æ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ç';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ç';
+		$this->assertEqual($result, $expected);
+
+		$string = 'È';
+		$result = Multibyte::strtolower($string);
+		$expected = 'è';
+		$this->assertEqual($result, $expected);
+
+		$string = 'É';
+		$result = Multibyte::strtolower($string);
+		$expected = 'é';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ê';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ê';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ë';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ë';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ì';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ì';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Í';
+		$result = Multibyte::strtolower($string);
+		$expected = 'í';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Î';
+		$result = Multibyte::strtolower($string);
+		$expected = 'î';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ï';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ï';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ð';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ð';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ñ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ñ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ò';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ò';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ó';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ó';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ô';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ô';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Õ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'õ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ö';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ö';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ø';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ø';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ù';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ù';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ú';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ú';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Û';
+		$result = Multibyte::strtolower($string);
+		$expected = 'û';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ü';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ü';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ý';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ý';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Þ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'þ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ā';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ā';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ă';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ă';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ą';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ą';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ć';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ć';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĉ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ĉ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ċ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ċ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Č';
+		$result = Multibyte::strtolower($string);
+		$expected = 'č';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ď';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ď';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Đ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'đ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ē';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ē';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĕ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ĕ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ė';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ė';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ę';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ę';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ě';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ě';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĝ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ĝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ğ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ğ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ġ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ġ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ģ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ģ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ĥ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ħ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ħ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĩ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ĩ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ī';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ī';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĭ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ĭ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Į';
+		$result = Multibyte::strtolower($string);
+		$expected = 'į';
+		$this->assertEqual($result, $expected);
+
+		$string = 'IJ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ij';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĵ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ĵ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ķ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ķ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĺ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ĺ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ļ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ļ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ľ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ľ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ŀ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ŀ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ł';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ł';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ń';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ń';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ņ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ņ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ň';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ň';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ŋ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ŋ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ō';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ō';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ŏ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ŏ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ő';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ő';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Œ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'œ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ŕ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ŕ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ŗ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ŗ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ř';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ř';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ś';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ś';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ŝ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ŝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ş';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ş';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Š';
+		$result = Multibyte::strtolower($string);
+		$expected = 'š';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ţ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ţ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ť';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ť';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ŧ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ŧ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ũ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ũ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ū';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ū';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ŭ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ŭ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ů';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ů';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ű';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ű';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ų';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ų';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ŵ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ŵ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ŷ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ŷ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ź';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ź';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ż';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ż';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ž';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ž';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'āăąćĉċčďđēĕėęěĝğġģĥħĩīĭįijĵķĺļľŀłńņňŋōŏőœŕŗřśŝşšţťŧũūŭůűųŵŷźżž';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĤĒĹĻŎ, ŴŐŘĻĎ!';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ĥēĺļŏ, ŵőřļď!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĥēĺļŏ, ŵőřļď!';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ĥēĺļŏ, ŵőřļď!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ἈΙ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ἀι';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ԀԂԄԆԈԊԌԎԐԒ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ԁԃԅԇԉԋԍԏԐԒ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉՊՋՌՍՎՏՐՑՒՓՔՕՖև';
+		$result = Multibyte::strtolower($string);
+		$expected = 'աբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆև';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ႠႡႢႣႤႥႦႧႨႩႪႫႬႭႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ႠႡႢႣႤႥႦႧႨႩႪႫႬႭႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ḀḂḄḆḈḊḌḎḐḒḔḖḘḚḜḞḠḢḤḦḨḪḬḮḰḲḴḶḸḺḼḾṀṂṄṆṈṊṌṎṐṒṔṖṘṚṜṞṠṢṤṦṨṪṬṮṰṲṴṶṸṺṼṾẀẂẄẆẈẊẌẎẐẒẔẖẗẘẙẚẠẢẤẦẨẪẬẮẰẲẴẶẸẺẼẾỀỂỄỆỈỊỌỎỐỒỔỖỘỚỜỞỠỢỤỦỨỪỬỮỰỲỴỶỸ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ḁḃḅḇḉḋḍḏḑḓḕḗḙḛḝḟḡḣḥḧḩḫḭḯḱḳḵḷḹḻḽḿṁṃṅṇṉṋṍṏṑṓṕṗṙṛṝṟṡṣṥṧṩṫṭṯṱṳṵṷṹṻṽṿẁẃẅẇẉẋẍẏẑẓẕẖẗẘẙẚạảấầẩẫậắằẳẵặẹẻẽếềểễệỉịọỏốồổỗộớờởỡợụủứừửữựỳỵỷỹ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ΩKÅℲ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ωkåⅎ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ΩKÅ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ωkå';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ΩKÅ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ωkå';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯↃ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿↄ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ⰀⰁⰂⰃⰄⰅⰆⰇⰈⰉⰊⰋⰌⰍⰎⰏⰐⰑⰒⰓⰔⰕⰖⰗⰘⰙⰚⰛⰜⰝⰞⰟⰠⰡⰢⰣⰤⰥⰦⰧⰨⰩⰪⰫⰬⰭⰮ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ⰰⰱⰲⰳⰴⰵⰶⰷⰸⰹⰺⰻⰼⰽⰾⰿⱀⱁⱂⱃⱄⱅⱆⱇⱈⱉⱊⱋⱌⱍⱎⱏⱐⱑⱒⱓⱔⱕⱖⱗⱘⱙⱚⱛⱜⱝⱞ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ⲀⲂⲄⲆⲈⲊⲌⲎⲐⲒⲔⲖⲘⲚⲜⲞⲠⲢⲤⲦⲨⲪⲬⲮⲰⲲⲴⲶⲸⲺⲼⲾⳀⳂⳄⳆⳈⳊⳌⳎⳐⳒⳔⳖⳘⳚⳜⳞⳠⳢ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'ⲁⲃⲅⲇⲉⲋⲍⲏⲑⲓⲕⲗⲙⲛⲝⲟⲡⲣⲥⲧⲩⲫⲭⲯⲱⲳⲵⲷⲹⲻⲽⲿⳁⳃⳅⳇⳉⳋⳍⳏⳑⳓⳕⳗⳙⳛⳝⳟⳡⳣ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'fffiflffifflſtstﬓﬔﬕﬖﬗ';
+		$result = Multibyte::strtolower($string);
+		$expected = 'fffiflffifflſtstﬓﬔﬕﬖﬗ';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testUsingMbStrtoupper method
+ *
+ * @access public
+ * @return void
+ */
+	function testUsingMbStrtoupper() {
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$result = mb_strtoupper($string);
+		$expected = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~';
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@';
+		$result = mb_strtoupper($string);
+		$expected = '!"#$%&\'()*+,-./0123456789:;<=>?@';
+		$this->assertEqual($result, $expected);
+
+		$string = 'à';
+		$result = mb_strtoupper($string);
+		$expected = 'À';
+		$this->assertEqual($result, $expected);
+
+		$string = 'á';
+		$result = mb_strtoupper($string);
+		$expected = 'Á';
+		$this->assertEqual($result, $expected);
+
+		$string = 'â';
+		$result = mb_strtoupper($string);
+		$expected = 'Â';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ã';
+		$result = mb_strtoupper($string);
+		$expected = 'Ã';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ä';
+		$result = mb_strtoupper($string);
+		$expected = 'Ä';
+		$this->assertEqual($result, $expected);
+
+		$string = 'å';
+		$result = mb_strtoupper($string);
+		$expected = 'Å';
+		$this->assertEqual($result, $expected);
+
+		$string = 'æ';
+		$result = mb_strtoupper($string);
+		$expected = 'Æ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ç';
+		$result = mb_strtoupper($string);
+		$expected = 'Ç';
+		$this->assertEqual($result, $expected);
+
+		$string = 'è';
+		$result = mb_strtoupper($string);
+		$expected = 'È';
+		$this->assertEqual($result, $expected);
+
+		$string = 'é';
+		$result = mb_strtoupper($string);
+		$expected = 'É';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ê';
+		$result = mb_strtoupper($string);
+		$expected = 'Ê';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ë';
+		$result = mb_strtoupper($string);
+		$expected = 'Ë';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ì';
+		$result = mb_strtoupper($string);
+		$expected = 'Ì';
+		$this->assertEqual($result, $expected);
+
+		$string = 'í';
+		$result = mb_strtoupper($string);
+		$expected = 'Í';
+		$this->assertEqual($result, $expected);
+
+		$string = 'î';
+		$result = mb_strtoupper($string);
+		$expected = 'Î';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ï';
+		$result = mb_strtoupper($string);
+		$expected = 'Ï';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ð';
+		$result = mb_strtoupper($string);
+		$expected = 'Ð';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ñ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ñ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ò';
+		$result = mb_strtoupper($string);
+		$expected = 'Ò';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ó';
+		$result = mb_strtoupper($string);
+		$expected = 'Ó';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ô';
+		$result = mb_strtoupper($string);
+		$expected = 'Ô';
+		$this->assertEqual($result, $expected);
+
+		$string = 'õ';
+		$result = mb_strtoupper($string);
+		$expected = 'Õ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ö';
+		$result = mb_strtoupper($string);
+		$expected = 'Ö';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ø';
+		$result = mb_strtoupper($string);
+		$expected = 'Ø';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ù';
+		$result = mb_strtoupper($string);
+		$expected = 'Ù';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ú';
+		$result = mb_strtoupper($string);
+		$expected = 'Ú';
+		$this->assertEqual($result, $expected);
+
+		$string = 'û';
+		$result = mb_strtoupper($string);
+		$expected = 'Û';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ü';
+		$result = mb_strtoupper($string);
+		$expected = 'Ü';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ý';
+		$result = mb_strtoupper($string);
+		$expected = 'Ý';
+		$this->assertEqual($result, $expected);
+
+		$string = 'þ';
+		$result = mb_strtoupper($string);
+		$expected = 'Þ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþ';
+		$result = mb_strtoupper($string);
+		$expected = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ā';
+		$result = mb_strtoupper($string);
+		$expected = 'Ā';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ă';
+		$result = mb_strtoupper($string);
+		$expected = 'Ă';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ą';
+		$result = mb_strtoupper($string);
+		$expected = 'Ą';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ć';
+		$result = mb_strtoupper($string);
+		$expected = 'Ć';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĉ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ĉ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ċ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ċ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'č';
+		$result = mb_strtoupper($string);
+		$expected = 'Č';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ď';
+		$result = mb_strtoupper($string);
+		$expected = 'Ď';
+		$this->assertEqual($result, $expected);
+
+		$string = 'đ';
+		$result = mb_strtoupper($string);
+		$expected = 'Đ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ē';
+		$result = mb_strtoupper($string);
+		$expected = 'Ē';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĕ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ĕ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ė';
+		$result = mb_strtoupper($string);
+		$expected = 'Ė';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ę';
+		$result = mb_strtoupper($string);
+		$expected = 'Ę';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ě';
+		$result = mb_strtoupper($string);
+		$expected = 'Ě';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĝ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ĝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ğ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ğ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ġ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ġ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ģ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ģ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĥ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ĥ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ħ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ħ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĩ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ĩ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ī';
+		$result = mb_strtoupper($string);
+		$expected = 'Ī';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ĭ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'į';
+		$result = mb_strtoupper($string);
+		$expected = 'Į';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ij';
+		$result = mb_strtoupper($string);
+		$expected = 'IJ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĵ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ĵ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ķ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ķ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĺ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ĺ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ļ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ļ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ľ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ľ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ŀ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ŀ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ł';
+		$result = mb_strtoupper($string);
+		$expected = 'Ł';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ń';
+		$result = mb_strtoupper($string);
+		$expected = 'Ń';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ņ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ņ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ň';
+		$result = mb_strtoupper($string);
+		$expected = 'Ň';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ŋ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ŋ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ō';
+		$result = mb_strtoupper($string);
+		$expected = 'Ō';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ŏ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ŏ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ő';
+		$result = mb_strtoupper($string);
+		$expected = 'Ő';
+		$this->assertEqual($result, $expected);
+
+		$string = 'œ';
+		$result = mb_strtoupper($string);
+		$expected = 'Œ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ŕ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ŕ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ŗ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ŗ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ř';
+		$result = mb_strtoupper($string);
+		$expected = 'Ř';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ś';
+		$result = mb_strtoupper($string);
+		$expected = 'Ś';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ŝ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ŝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ş';
+		$result = mb_strtoupper($string);
+		$expected = 'Ş';
+		$this->assertEqual($result, $expected);
+
+		$string = 'š';
+		$result = mb_strtoupper($string);
+		$expected = 'Š';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ţ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ţ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ť';
+		$result = mb_strtoupper($string);
+		$expected = 'Ť';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ŧ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ŧ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ũ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ũ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ū';
+		$result = mb_strtoupper($string);
+		$expected = 'Ū';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ŭ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ŭ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ů';
+		$result = mb_strtoupper($string);
+		$expected = 'Ů';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ű';
+		$result = mb_strtoupper($string);
+		$expected = 'Ű';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ų';
+		$result = mb_strtoupper($string);
+		$expected = 'Ų';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ŵ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ŵ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ŷ';
+		$result = mb_strtoupper($string);
+		$expected = 'Ŷ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ź';
+		$result = mb_strtoupper($string);
+		$expected = 'Ź';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ż';
+		$result = mb_strtoupper($string);
+		$expected = 'Ż';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ž';
+		$result = mb_strtoupper($string);
+		$expected = 'Ž';
+		$this->assertEqual($result, $expected);
+
+		$string = 'āăąćĉċčďđēĕėęěĝğġģĥħĩīĭįijĵķĺļľŀłńņňŋōŏőœŕŗřśŝşšţťŧũūŭůűųŵŷźżž';
+		$result = mb_strtoupper($string);
+		$expected = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$result = mb_strtoupper($string);
+		$expected = 'ĤĒĹĻŎ, ŴŐŘĻĎ!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ἀι';
+		$result = mb_strtoupper($string);
+		$expected = 'ἈΙ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ԁԃԅԇԉԋԍԏԐԒ';
+		$result = mb_strtoupper($string);
+		$expected = 'ԀԂԄԆԈԊԌԎԐԒ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'աբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆև';
+		$result = mb_strtoupper($string);
+		$expected = 'ԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉՊՋՌՍՎՏՐՑՒՓՔՕՖև';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ႠႡႢႣႤႥႦႧႨႩႪႫႬႭႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅ';
+		$result = mb_strtoupper($string);
+		$expected = 'ႠႡႢႣႤႥႦႧႨႩႪႫႬႭႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ḁḃḅḇḉḋḍḏḑḓḕḗḙḛḝḟḡḣḥḧḩḫḭḯḱḳḵḷḹḻḽḿṁṃṅṇṉṋṍṏṑṓṕṗṙṛṝṟṡṣṥṧṩṫṭṯṱṳṵṷṹṻṽṿẁẃẅẇẉẋẍẏẑẓẕẖẗẘẙẚạảấầẩẫậắằẳẵặẹẻẽếềểễệỉịọỏốồổỗộớờởỡợụủứừửữựỳỵỷỹ';
+		$result = mb_strtoupper($string);
+		$expected = 'ḀḂḄḆḈḊḌḎḐḒḔḖḘḚḜḞḠḢḤḦḨḪḬḮḰḲḴḶḸḺḼḾṀṂṄṆṈṊṌṎṐṒṔṖṘṚṜṞṠṢṤṦṨṪṬṮṰṲṴṶṸṺṼṾẀẂẄẆẈẊẌẎẐẒẔẖẗẘẙẚẠẢẤẦẨẪẬẮẰẲẴẶẸẺẼẾỀỂỄỆỈỊỌỎỐỒỔỖỘỚỜỞỠỢỤỦỨỪỬỮỰỲỴỶỸ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ωkå';
+		$result = mb_strtoupper($string);
+		$expected = 'ΩKÅ';
+		$this->assertEqual($result, $expected);
+
+/*
+mb_strtoupper does not work for these strings.
+
+		$string = 'ⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿↄ';
+		$result = mb_strtoupper($string);
+		$expected = 'ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯↃ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ';
+		$result = mb_strtoupper($string);
+		$expected = 'ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ⰰⰱⰲⰳⰴⰵⰶⰷⰸⰹⰺⰻⰼⰽⰾⰿⱀⱁⱂⱃⱄⱅⱆⱇⱈⱉⱊⱋⱌⱍⱎⱏⱐⱑⱒⱓⱔⱕⱖⱗⱘⱙⱚⱛⱜⱝⱞ';
+		$result = mb_strtoupper($string);
+		$expected = 'ⰀⰁⰂⰃⰄⰅⰆⰇⰈⰉⰊⰋⰌⰍⰎⰏⰐⰑⰒⰓⰔⰕⰖⰗⰘⰙⰚⰛⰜⰝⰞⰟⰠⰡⰢⰣⰤⰥⰦⰧⰨⰩⰪⰫⰬⰭⰮ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ⲁⲃⲅⲇⲉⲋⲍⲏⲑⲓⲕⲗⲙⲛⲝⲟⲡⲣⲥⲧⲩⲫⲭⲯⲱⲳⲵⲷⲹⲻⲽⲿⳁⳃⳅⳇⳉⳋⳍⳏⳑⳓⳕⳗⳙⳛⳝⳟⳡⳣ';
+		$result = mb_strtoupper($string);
+		$expected = 'ⲀⲂⲄⲆⲈⲊⲌⲎⲐⲒⲔⲖⲘⲚⲜⲞⲠⲢⲤⲦⲨⲪⲬⲮⲰⲲⲴⲶⲸⲺⲼⲾⳀⳂⳄⳆⳈⳊⳌⳎⳐⳒⳔⳖⳘⳚⳜⳞⳠⳢ';
+		$this->assertEqual($result, $expected);
+*/
+		$string = 'fffiflffifflſtstﬓﬔﬕﬖﬗ';
+		$result = mb_strtoupper($string);
+		$expected = 'fffiflffifflſtstﬓﬔﬕﬖﬗ';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testMultibyteStrtoupper method
+ *
+ * @access public
+ * @return void
+ */
+	function testMultibyteStrtoupper() {
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$result = Multibyte::strtoupper($string);
+		$expected = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~';
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@';
+		$result = Multibyte::strtoupper($string);
+		$expected = '!"#$%&\'()*+,-./0123456789:;<=>?@';
+		$this->assertEqual($result, $expected);
+
+		$string = 'à';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'À';
+		$this->assertEqual($result, $expected);
+
+		$string = 'á';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Á';
+		$this->assertEqual($result, $expected);
+
+		$string = 'â';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Â';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ã';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ã';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ä';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ä';
+		$this->assertEqual($result, $expected);
+
+		$string = 'å';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Å';
+		$this->assertEqual($result, $expected);
+
+		$string = 'æ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Æ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ç';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ç';
+		$this->assertEqual($result, $expected);
+
+		$string = 'è';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'È';
+		$this->assertEqual($result, $expected);
+
+		$string = 'é';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'É';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ê';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ê';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ë';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ë';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ì';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ì';
+		$this->assertEqual($result, $expected);
+
+		$string = 'í';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Í';
+		$this->assertEqual($result, $expected);
+
+		$string = 'î';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Î';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ï';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ï';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ð';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ð';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ñ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ñ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ò';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ò';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ó';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ó';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ô';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ô';
+		$this->assertEqual($result, $expected);
+
+		$string = 'õ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Õ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ö';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ö';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ø';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ø';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ù';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ù';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ú';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ú';
+		$this->assertEqual($result, $expected);
+
+		$string = 'û';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Û';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ü';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ü';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ý';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ý';
+		$this->assertEqual($result, $expected);
+
+		$string = 'þ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Þ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ā';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ā';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ă';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ă';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ą';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ą';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ć';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ć';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĉ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ĉ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ċ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ċ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'č';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Č';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ď';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ď';
+		$this->assertEqual($result, $expected);
+
+		$string = 'đ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Đ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ē';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ē';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĕ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ĕ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ė';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ė';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ę';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ę';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ě';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ě';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĝ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ĝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ğ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ğ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ġ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ġ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ģ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ģ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĥ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ĥ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ħ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ħ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĩ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ĩ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ī';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ī';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ĭ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'į';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Į';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ij';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'IJ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĵ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ĵ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ķ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ķ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĺ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ĺ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ļ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ļ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ľ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ľ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ŀ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ŀ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ł';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ł';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ń';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ń';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ņ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ņ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ň';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ň';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ŋ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ŋ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ō';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ō';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ŏ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ŏ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ő';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ő';
+		$this->assertEqual($result, $expected);
+
+		$string = 'œ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Œ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ŕ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ŕ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ŗ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ŗ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ř';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ř';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ś';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ś';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ŝ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ŝ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ş';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ş';
+		$this->assertEqual($result, $expected);
+
+		$string = 'š';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Š';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ţ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ţ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ť';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ť';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ŧ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ŧ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ũ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ũ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ū';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ū';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ŭ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ŭ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ů';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ů';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ű';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ű';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ų';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ų';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ŵ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ŵ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ŷ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ŷ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ź';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ź';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ż';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ż';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ž';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'Ž';
+		$this->assertEqual($result, $expected);
+
+		$string = 'āăąćĉċčďđēĕėęěĝğġģĥħĩīĭįijĵķĺļľŀłńņňŋōŏőœŕŗřśŝşšţťŧũūŭůűųŵŷźżž';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'ĤĒĹĻŎ, ŴŐŘĻĎ!';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ἀι';
+		$result = mb_strtoupper($string);
+		$expected = 'ἈΙ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ἀι';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'ἈΙ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ԁԃԅԇԉԋԍԏԐԒ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'ԀԂԄԆԈԊԌԎԐԒ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'աբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆև';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'ԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉՊՋՌՍՎՏՐՑՒՓՔՕՖև';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ႠႡႢႣႤႥႦႧႨႩႪႫႬႭႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'ႠႡႢႣႤႥႦႧႨႩႪႫႬႭႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ḁḃḅḇḉḋḍḏḑḓḕḗḙḛḝḟḡḣḥḧḩḫḭḯḱḳḵḷḹḻḽḿṁṃṅṇṉṋṍṏṑṓṕṗṙṛṝṟṡṣṥṧṩṫṭṯṱṳṵṷṹṻṽṿẁẃẅẇẉẋẍẏẑẓẕẖẗẘẙẚạảấầẩẫậắằẳẵặẹẻẽếềểễệỉịọỏốồổỗộớờởỡợụủứừửữựỳỵỷỹ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'ḀḂḄḆḈḊḌḎḐḒḔḖḘḚḜḞḠḢḤḦḨḪḬḮḰḲḴḶḸḺḼḾṀṂṄṆṈṊṌṎṐṒṔṖṘṚṜṞṠṢṤṦṨṪṬṮṰṲṴṶṸṺṼṾẀẂẄẆẈẊẌẎẐẒẔẖẗẘẙẚẠẢẤẦẨẪẬẮẰẲẴẶẸẺẼẾỀỂỄỆỈỊỌỎỐỒỔỖỘỚỜỞỠỢỤỦỨỪỬỮỰỲỴỶỸ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ωkåⅎ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'ΩKÅℲ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ωkå';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'ΩKÅ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿↄ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯↃ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ⰰⰱⰲⰳⰴⰵⰶⰷⰸⰹⰺⰻⰼⰽⰾⰿⱀⱁⱂⱃⱄⱅⱆⱇⱈⱉⱊⱋⱌⱍⱎⱏⱐⱑⱒⱓⱔⱕⱖⱗⱘⱙⱚⱛⱜⱝⱞ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'ⰀⰁⰂⰃⰄⰅⰆⰇⰈⰉⰊⰋⰌⰍⰎⰏⰐⰑⰒⰓⰔⰕⰖⰗⰘⰙⰚⰛⰜⰝⰞⰟⰠⰡⰢⰣⰤⰥⰦⰧⰨⰩⰪⰫⰬⰭⰮ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ⲁⲃⲅⲇⲉⲋⲍⲏⲑⲓⲕⲗⲙⲛⲝⲟⲡⲣⲥⲧⲩⲫⲭⲯⲱⲳⲵⲷⲹⲻⲽⲿⳁⳃⳅⳇⳉⳋⳍⳏⳑⳓⳕⳗⳙⳛⳝⳟⳡⳣ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'ⲀⲂⲄⲆⲈⲊⲌⲎⲐⲒⲔⲖⲘⲚⲜⲞⲠⲢⲤⲦⲨⲪⲬⲮⲰⲲⲴⲶⲸⲺⲼⲾⳀⳂⳄⳆⳈⳊⳌⳎⳐⳒⳔⳖⳘⳚⳜⳞⳠⳢ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'fffiflffifflſtstﬓﬔﬕﬖﬗ';
+		$result = Multibyte::strtoupper($string);
+		$expected = 'fffiflffifflſtstﬓﬔﬕﬖﬗ';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testUsingMbSubstrCount method
+ *
+ * @access public
+ * @return void
+ */
+	function testUsingMbSubstrCount() {
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$find = 'F';
+		$result = mb_substr_count($string, $find);
+		$expected = 1;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ABCDEFGHIJKLMNOPQFRSFTUVWXYZ0F12345F6789';
+		$find = 'F';
+		$result = mb_substr_count($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÅÊËÌÍÎÏÐÑÒÓÔÅÕÖØÅÙÚÛÅÜÝÞ';
+		$find = 'Å';
+		$result = mb_substr_count($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÙÚÂÃÄÅÆÇÈÙÚÉÊËÌÍÎÏÐÑÒÓÔÕÖØÅÙÚÛÜÝÞÙÚ';
+		$find = 'ÙÚ';
+		$result = mb_substr_count($string, $find);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊÅËÌÍÎÏÐÑÒÓÔÕÅÖØÅÙÚÅÛÜÅÝÞÅ';
+		$find = 'Å';
+		$result = mb_substr_count($string, $find);
+		$expected = 7;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĊĀĂĄĆĈĊČĎĐĒĔĖĊĘĚĜĞĠĢĤĦĨĪĬĮĊIJĴĶĹĻĽĿŁŃŅŇŊŌĊŎŐŒŔŖŘŚŜŞŠŢĊŤŦŨŪŬŮŰĊŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = mb_substr_count($string, $find);
+		$expected = 7;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĊĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁĊŃŅĊŇŊŌŎŐŒŔŖĊŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = mb_substr_count($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./012F34567F89:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghiFjklmnopqFrstuvwFxyz{|}~';
+		$find = 'F';
+		$result = mb_substr_count($string, $find);
+		$expected = 6;
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥µ¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁµÂõÄÅÆÇµÈ';
+		$find = 'µ';
+		$result = mb_substr_count($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôÕÖõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉÕÖĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝÕÖĞğĠġĢģĤĥĦÕÖħĨĩĪīĬ';
+		$find = 'ÕÖ';
+		$result = mb_substr_count($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōĵĶķĸĹŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšĵĶķĸĹŢţŤťŦŧŨũŪūŬŭŮůŰűŲųĵĶķĸĹŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$find = 'ĵĶķĸĹ';
+		$result = mb_substr_count($string, $find);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƸƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJƸNjnjǍǎǏǐǑǒǓƸǔǕǖǗǘǙǚƸǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$find = 'Ƹ';
+		$result = mb_substr_count($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƹƠơƢƣƤƥƦƧƨƩƹƪƫƬƭƮƯưƱƲƳƴƹƵƶƷƸƹƺƻƼƽƾƿǀǁǂƹǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$find = 'ƹ';
+		$result = mb_substr_count($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞʀɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʀʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʀʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʀʻʼ';
+		$find = 'ʀ';
+		$result = mb_substr_count($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЇЎЏАБВГДЕЖЗИЙКЛ';
+		$find = 'Ї';
+		$result = mb_substr_count($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСРТУФХЦЧШЩЪЫЬРЭЮЯабРвгдежзийклРмнопрстуфхцчшщъыь';
+		$find = 'Р';
+		$result = mb_substr_count($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСрТУФХЦЧШЩЪЫрЬЭЮЯабвгдежзийклмнопррстуфхцчшщъыь';
+		$find = 'р';
+		$result = mb_substr_count($string, $find);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = 'فنقكلنمنهونىينًٌٍَُ';
+		$find = 'ن';
+		$result = mb_substr_count($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✿✴✵✶✷✸✿✹✺✻✼✽✾✿❀❁❂❃❄❅❆✿❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$find = '✿';
+		$result = mb_substr_count($string, $find);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺐⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺐⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⺐⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$find = '⺐';
+		$result = mb_substr_count($string, $find);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽤⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽤⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$find = '⽤';
+		$result = mb_substr_count($string, $find);
+		$expected = 3;
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눺눻눼눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕눺눻눼뉖뉗뉘뉙뉚뉛뉜뉝눺눻눼뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$find = '눺눻눼';
+		$result = mb_substr_count($string, $find);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺞﺟﺠﺡﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺞﺟﺠﺡﺆﺇﺞﺟﺠﺡﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$find = 'ﺞﺟﺠﺡ';
+		$result = mb_substr_count($string, $find);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﻞﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻞﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻞﻸﻹﻺﻞﻻﻼ';
+		$find = 'ﻞ';
+		$result = mb_substr_count($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdkefghijklmnopqrstuvwxkyz';
+		$find = 'k';
+		$result = mb_substr_count($string, $find);
+		$expected = 3;
+		$this->assertEqual($result, $expected);
+
+		$string = 'abklmcdefghijklmnopqrstuvklmwxyz';
+		$find = 'klm';
+		$result = mb_substr_count($string, $find);
+		$expected = 3;
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdppefghijklmnoppqrstuvwxyz';
+		$find = 'ppe';
+		$result = mb_substr_count($string, $find);
+		$expected = 1;
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$find = 'ア';
+		$result = mb_substr_count($string, $find);
+		$expected = 1;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$find = 'ハ';
+		$result = mb_substr_count($string, $find);
+		$expected = 1;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ő';
+		$result = mb_substr_count($string, $find);
+		$expected = 1;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ĺļ';
+		$result = mb_substr_count($string, $find);
+		$expected = 1;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'o';
+		$result = mb_substr_count($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'rl';
+		$result = mb_substr_count($string, $find);
+		$expected = 1;
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'n';
+		$result = mb_substr_count($string, $find);
+		$expected = 1;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ničiničiini';
+		$find = 'n';
+		$result = mb_substr_count($string, $find);
+		$expected = 3;
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'ć';
+		$result = mb_substr_count($string, $find);
+		$expected = 1;
+		$this->assertEqual($result, $expected);
+
+		$string = 'moćimoćimoćmćioći';
+		$find = 'ći';
+		$result = mb_substr_count($string, $find);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'ž';
+		$result = mb_substr_count($string, $find);
+		$expected = 1;
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$find = '设';
+		$result = mb_substr_count($string, $find);
+		$expected = 1;
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '周';
+		$result = mb_substr_count($string, $find);
+		$expected = 1;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'H';
+		$result = mb_substr_count($string, $find);
+		$expected = false;
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testMultibyteSubstrCount method
+ *
+ * @access public
+ * @return void
+ */
+	function testMultibyteSubstrCount() {
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$find = 'F';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 1;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ABCDEFGHIJKLMNOPQFRSFTUVWXYZ0F12345F6789';
+		$find = 'F';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÅÊËÌÍÎÏÐÑÒÓÔÅÕÖØÅÙÚÛÅÜÝÞ';
+		$find = 'Å';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÙÚÂÃÄÅÆÇÈÙÚÉÊËÌÍÎÏÐÑÒÓÔÕÖØÅÙÚÛÜÝÞÙÚ';
+		$find = 'ÙÚ';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊÅËÌÍÎÏÐÑÒÓÔÕÅÖØÅÙÚÅÛÜÅÝÞÅ';
+		$find = 'Å';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 7;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĊĀĂĄĆĈĊČĎĐĒĔĖĊĘĚĜĞĠĢĤĦĨĪĬĮĊIJĴĶĹĻĽĿŁŃŅŇŊŌĊŎŐŒŔŖŘŚŜŞŠŢĊŤŦŨŪŬŮŰĊŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 7;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĊĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁĊŃŅĊŇŊŌŎŐŒŔŖĊŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./012F34567F89:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghiFjklmnopqFrstuvwFxyz{|}~';
+		$find = 'F';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 6;
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥µ¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁµÂõÄÅÆÇµÈ';
+		$find = 'µ';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôÕÖõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉÕÖĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝÕÖĞğĠġĢģĤĥĦÕÖħĨĩĪīĬ';
+		$find = 'ÕÖ';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōĵĶķĸĹŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšĵĶķĸĹŢţŤťŦŧŨũŪūŬŭŮůŰűŲųĵĶķĸĹŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$find = 'ĵĶķĸĹ';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƸƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJƸNjnjǍǎǏǐǑǒǓƸǔǕǖǗǘǙǚƸǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$find = 'Ƹ';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƹƠơƢƣƤƥƦƧƨƩƹƪƫƬƭƮƯưƱƲƳƴƹƵƶƷƸƹƺƻƼƽƾƿǀǁǂƹǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$find = 'ƹ';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞʀɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʀʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʀʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʀʻʼ';
+		$find = 'ʀ';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЇЎЏАБВГДЕЖЗИЙКЛ';
+		$find = 'Ї';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСРТУФХЦЧШЩЪЫЬРЭЮЯабРвгдежзийклРмнопрстуфхцчшщъыь';
+		$find = 'Р';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСрТУФХЦЧШЩЪЫрЬЭЮЯабвгдежзийклмнопррстуфхцчшщъыь';
+		$find = 'р';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = 'فنقكلنمنهونىينًٌٍَُ';
+		$find = 'ن';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✿✴✵✶✷✸✿✹✺✻✼✽✾✿❀❁❂❃❄❅❆✿❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$find = '✿';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺐⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺐⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⺐⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$find = '⺐';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽤⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽤⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$find = '⽤';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 3;
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눺눻눼눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕눺눻눼뉖뉗뉘뉙뉚뉛뉜뉝눺눻눼뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$find = '눺눻눼';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺞﺟﺠﺡﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺞﺟﺠﺡﺆﺇﺞﺟﺠﺡﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$find = 'ﺞﺟﺠﺡ';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﻞﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻞﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻞﻸﻹﻺﻞﻻﻼ';
+		$find = 'ﻞ';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdkefghijklmnopqrstuvwxkyz';
+		$find = 'k';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 3;
+		$this->assertEqual($result, $expected);
+
+		$string = 'abklmcdefghijklmnopqrstuvklmwxyz';
+		$find = 'klm';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 3;
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdppefghijklmnoppqrstuvwxyz';
+		$find = 'ppe';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 1;
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$find = 'ア';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 1;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$find = 'ハ';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 1;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ő';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 1;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'ĺļ';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 1;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'o';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 2;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$find = 'rl';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 1;
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$find = 'n';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 1;
+		$this->assertEqual($result, $expected);
+
+		$string = 'ničiničiini';
+		$find = 'n';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 3;
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$find = 'ć';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 1;
+		$this->assertEqual($result, $expected);
+
+		$string = 'moćimoćimoćmćioći';
+		$find = 'ći';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$find = 'ž';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 1;
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$find = '设';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 1;
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$find = '周';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = 1;
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$find = 'H';
+		$result = Multibyte::substrCount($string, $find);
+		$expected = false;
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testUsingMbSubstr method
+ *
+ * @access public
+ * @return void
+ */
+	function testUsingMbSubstr() {
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$result = mb_substr($string, 4, 7);
+		$expected = 'EFGHIJK';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$result = mb_substr($string, 4, 7);
+		$expected = 'ÄÅÆÇÈÉÊ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = mb_substr($string, 4, 7);
+		$expected = 'ĈĊČĎĐĒĔ';
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$result = mb_substr($string, 4, 7);
+		$expected = '%&\'()*+';
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$result = mb_substr($string, 4);
+		$expected = '¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$result = mb_substr($string, 4, 7);
+		$expected = 'ÍÎÏÐÑÒÓ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$result = mb_substr($string, 4, 7);
+		$expected = 'ıIJijĴĵĶķ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$result = mb_substr($string, 25);
+		$expected = 'ƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$result = mb_substr($string, 3);
+		$expected = 'ɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$result = mb_substr($string, 3);
+		$expected = 'ЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$result = mb_substr($string, 3, 16);
+		$expected = 'ПРСТУФХЦЧШЩЪЫЬЭЮ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$result = mb_substr($string, 3, 6);
+		$expected = 'لمنهوى';
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$result = mb_substr($string, 6, 14);
+		$expected = '✶✷✸✹✺✻✼✽✾✿❀❁❂❃';
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$result = mb_substr($string, 8, 13);
+		$expected = '⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔';
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$result = mb_substr($string, 12, 24);
+		$expected = '⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨';
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$result = mb_substr($string, 12, 24);
+		$expected = '눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$result = mb_substr($string, 12);
+		$expected = 'ﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$result = mb_substr($string, 24, 12);
+		$expected = 'ﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$result = mb_substr($string, 11, 2);
+		$expected = 'lm';
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$result = mb_substr($string, 7, 11);
+		$expected = 'ィゥェォャュョッーアイ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$result = mb_substr($string, 13, 13);
+		$expected = 'ニヌネノハヒフヘホマミムメ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$result = mb_substr($string, 3, 4);
+		$expected = 'ļŏ, ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$result = mb_substr($string, 3, 4);
+		$expected = 'lo, ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$result = mb_substr($string, 3);
+		$expected = 'i';
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$result = mb_substr($string, 1);
+		$expected = 'oći';
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$result = mb_substr($string, 0, 2);
+		$expected = 'dr';
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$result = mb_substr($string, 3, 3);
+		$expected = '设为首';
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$result = mb_substr($string, 0, 1);
+		$expected = '一';
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$result = mb_substr($string, 6);
+		$expected = false;
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$result = mb_substr($string, 0);
+		$expected = '一二三周永龍';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testMultibyteSubstr method
+ *
+ * @access public
+ * @return void
+ */
+	function testMultibyteSubstr() {
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$result = Multibyte::substr($string, 4, 7);
+		$expected = 'EFGHIJK';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$result = Multibyte::substr($string, 4, 7);
+		$expected = 'ÄÅÆÇÈÉÊ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$find = 'Ċ';
+		$result = Multibyte::substr($string, 4, 7);
+		$expected = 'ĈĊČĎĐĒĔ';
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$result = Multibyte::substr($string, 4, 7);
+		$expected = '%&\'()*+';
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$result = Multibyte::substr($string, 4);
+		$expected = '¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$result = Multibyte::substr($string, 4, 7);
+		$expected = 'ÍÎÏÐÑÒÓ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$result = Multibyte::substr($string, 4, 7);
+		$expected = 'ıIJijĴĵĶķ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$result = Multibyte::substr($string, 25);
+		$expected = 'ƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$result = Multibyte::substr($string, 3);
+		$expected = 'ɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$result = Multibyte::substr($string, 3);
+		$expected = 'ЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$result = Multibyte::substr($string, 3, 16);
+		$expected = 'ПРСТУФХЦЧШЩЪЫЬЭЮ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$result = Multibyte::substr($string, 3, 6);
+		$expected = 'لمنهوى';
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$result = Multibyte::substr($string, 6, 14);
+		$expected = '✶✷✸✹✺✻✼✽✾✿❀❁❂❃';
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$result = Multibyte::substr($string, 8, 13);
+		$expected = '⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔';
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$result = Multibyte::substr($string, 12, 24);
+		$expected = '⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨';
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$result = Multibyte::substr($string, 12, 24);
+		$expected = '눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$result = Multibyte::substr($string, 12);
+		$expected = 'ﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$result = Multibyte::substr($string, 24, 12);
+		$expected = 'ﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$result = Multibyte::substr($string, 11, 2);
+		$expected = 'lm';
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$result = Multibyte::substr($string, 7, 11);
+		$expected = 'ィゥェォャュョッーアイ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$result = Multibyte::substr($string, 13, 13);
+		$expected = 'ニヌネノハヒフヘホマミムメ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$result = Multibyte::substr($string, 3, 4);
+		$expected = 'ļŏ, ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$result = Multibyte::substr($string, 3, 4);
+		$expected = 'lo, ';
+		$this->assertEqual($result, $expected);
+
+		$string = 'čini';
+		$result = Multibyte::substr($string, 3);
+		$expected = 'i';
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$result = Multibyte::substr($string, 1);
+		$expected = 'oći';
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$result = Multibyte::substr($string, 0, 2);
+		$expected = 'dr';
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$result = Multibyte::substr($string, 3, 3);
+		$expected = '设为首';
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$result = Multibyte::substr($string, 0, 1);
+		$expected = '一';
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$result = Multibyte::substr($string, 6);
+		$expected = false;
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$result = Multibyte::substr($string, 0);
+		$expected = '一二三周永龍';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testMultibyteSubstr method
+ *
+ * @access public
+ * @return void
+ */
+	function testMultibyteMimeEncode() {
+		$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+		$result = Multibyte::mimeEncode($string);
+		$this->assertEqual($result, $string);
+
+		$string = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ';
+		$result = Multibyte::mimeEncode($string);
+		$expected = '=?UTF-8?B?w4DDgcOCw4PDhMOFw4bDh8OIw4nDisOLw4zDjcOOw4/DkMORw5LDk8OUw5U=?=' . "\r\n" .
+					' =?UTF-8?B?w5bDmMOZw5rDm8Ocw53Dng==?=';
+		$this->assertEqual($result, $expected);
+		$result = Multibyte::mimeEncode($string, null, "\n");
+		$expected = '=?UTF-8?B?w4DDgcOCw4PDhMOFw4bDh8OIw4nDisOLw4zDjcOOw4/DkMORw5LDk8OUw5U=?=' . "\n" .
+					' =?UTF-8?B?w5bDmMOZw5rDm8Ocw53Dng==?=';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮIJĴĶĹĻĽĿŁŃŅŇŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŹŻŽ';
+		$result = Multibyte::mimeEncode($string);
+		$expected = '=?UTF-8?B?xIDEgsSExIbEiMSKxIzEjsSQxJLElMSWxJjEmsScxJ7EoMSixKTEpsSoxKo=?=' . "\r\n" .
+					' =?UTF-8?B?xKzErsSyxLTEtsS5xLvEvcS/xYHFg8WFxYfFisWMxY7FkMWSxZTFlsWYxZo=?=' . "\r\n" .
+					' =?UTF-8?B?xZzFnsWgxaLFpMWmxajFqsWsxa7FsMWyxbTFtsW5xbvFvQ==?=';
+		$this->assertEqual($result, $expected);
+
+		$string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
+		$result = Multibyte::mimeEncode($string);
+		$expected = '=?UTF-8?B?ISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xN?=' . "\r\n" .
+					' =?UTF-8?B?Tk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6?=' . "\r\n" .
+					' =?UTF-8?B?e3x9fg==?=';
+		$this->assertEqual($result, $expected);
+
+		$string = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈ';
+		$result = Multibyte::mimeEncode($string);
+		$expected = '=?UTF-8?B?wqHCosKjwqTCpcKmwqfCqMKpwqrCq8Kswq3CrsKvwrDCscKywrPCtMK1wrY=?=' . "\r\n" .
+					' =?UTF-8?B?wrfCuMK5wrrCu8K8wr3CvsK/w4DDgcOCw4PDhMOFw4bDh8OI?=';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬ';
+		$result = Multibyte::mimeEncode($string);
+		$expected = '=?UTF-8?B?w4nDisOLw4zDjcOOw4/DkMORw5LDk8OUw5XDlsOXw5jDmcOaw5vDnMOdw54=?=' . "\r\n" .
+					' =?UTF-8?B?w5/DoMOhw6LDo8Okw6XDpsOnw6jDqcOqw6vDrMOtw67Dr8Oww7HDssOzw7Q=?=' . "\r\n" .
+					' =?UTF-8?B?w7XDtsO3w7jDucO6w7vDvMO9w77Dv8SAxIHEgsSDxITEhcSGxIfEiMSJxIo=?=' . "\r\n" .
+					' =?UTF-8?B?xIvEjMSNxI7Ej8SQxJHEksSTxJTElcSWxJfEmMSZxJrEm8ScxJ3EnsSfxKA=?=' . "\r\n" .
+					' =?UTF-8?B?xKHEosSjxKTEpcSmxKfEqMSpxKrEq8Ss?=';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐ';
+		$result = Multibyte::mimeEncode($string);
+		$expected = '=?UTF-8?B?xK3ErsSvxLDEscSyxLPEtMS1xLbEt8S4xLnEusS7xLzEvcS+xL/FgMWBxYI=?=' . "\r\n" .
+					' =?UTF-8?B?xYPFhMWFxYbFh8WIxYnFisWLxYzFjcWOxY/FkMWRxZLFk8WUxZXFlsWXxZg=?=' . "\r\n" .
+					' =?UTF-8?B?xZnFmsWbxZzFncWexZ/FoMWhxaLFo8WkxaXFpsWnxajFqcWqxavFrMWtxa4=?=' . "\r\n" .
+					' =?UTF-8?B?xa/FsMWxxbLFs8W0xbXFtsW3xbjFucW6xbvFvMW9xb7Fv8aAxoHGgsaDxoQ=?=' . "\r\n" .
+					' =?UTF-8?B?xoXGhsaHxojGicaKxovGjMaNxo7Gj8aQ?=';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴ';
+		$result = Multibyte::mimeEncode($string);
+		$expected = '=?UTF-8?B?xpHGksaTxpTGlcaWxpfGmMaZxprGm8acxp3GnsafxqDGocaixqPGpMalxqY=?=' . "\r\n" .
+					' =?UTF-8?B?xqfGqMapxqrGq8asxq3GrsavxrDGscayxrPGtMa1xrbGt8a4xrnGusa7xrw=?=' . "\r\n" .
+					' =?UTF-8?B?xr3Gvsa/x4DHgceCx4PHhMeFx4bHh8eIx4nHiseLx4zHjceOx4/HkMeRx5I=?=' . "\r\n" .
+					' =?UTF-8?B?x5PHlMeVx5bHl8eYx5nHmsebx5zHnceex5/HoMehx6LHo8ekx6XHpsenx6g=?=' . "\r\n" .
+					' =?UTF-8?B?x6nHqserx6zHrceux6/HsMexx7LHs8e0?=';
+		$this->assertEqual($result, $expected);
+
+		$string = 'əɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼ';
+		$result = Multibyte::mimeEncode($string);
+		$expected = '=?UTF-8?B?yZnJmsmbyZzJncmeyZ/JoMmhyaLJo8mkyaXJpsmnyajJqcmqyavJrMmtya4=?=' . "\r\n" .
+					' =?UTF-8?B?ya/JsMmxybLJs8m0ybXJtsm3ybjJucm6ybvJvMm9yb7Jv8qAyoHKgsqDyoQ=?=' . "\r\n" .
+					' =?UTF-8?B?yoXKhsqHyojKicqKyovKjMqNyo7Kj8qQypHKksqTypTKlcqWypfKmMqZypo=?=' . "\r\n" .
+					' =?UTF-8?B?ypvKnMqdyp7Kn8qgyqHKosqjyqTKpcqmyqfKqMqpyqrKq8qsyq3KrsqvyrA=?=' . "\r\n" .
+					' =?UTF-8?B?yrHKssqzyrTKtcq2yrfKuMq5yrrKu8q8?=';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛ';
+		$result = Multibyte::mimeEncode($string);
+		$expected = '=?UTF-8?B?0IDQgdCC0IPQhNCF0IbQh9CI0InQitCL0IzQjdCO0I/QkNCR0JLQk9CU0JU=?=' . "\r\n" .
+					' =?UTF-8?B?0JbQl9CY0JnQmtCb?=';
+		$this->assertEqual($result, $expected);
+
+		$string = 'МНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+		$result = Multibyte::mimeEncode($string);
+		$expected = '=?UTF-8?B?0JzQndCe0J/QoNCh0KLQo9Ck0KXQptCn0KjQqdCq0KvQrNCt0K7Qr9Cw0LE=?=' . "\r\n" .
+					' =?UTF-8?B?0LLQs9C00LXQttC30LjQudC60LvQvNC90L7Qv9GA0YHRgtGD0YTRhdGG0Yc=?=' . "\r\n" .
+					' =?UTF-8?B?0YjRidGK0YvRjA==?=';
+		$this->assertEqual($result, $expected);
+
+		$string = 'فقكلمنهوىيًٌٍَُ';
+		$result = Multibyte::mimeEncode($string);
+		$expected = '=?UTF-8?B?2YHZgtmD2YTZhdmG2YfZiNmJ2YrZi9mM2Y3ZjtmP?=';
+		$this->assertEqual($result, $expected);
+
+		$string = '✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞';
+		$result = Multibyte::mimeEncode($string);
+		$expected = '=?UTF-8?B?4pyw4pyx4pyy4pyz4py04py14py24py34py44py54py64py74py84py94py+?=' . "\r\n" .
+					' =?UTF-8?B?4py/4p2A4p2B4p2C4p2D4p2E4p2F4p2G4p2H4p2I4p2J4p2K4p2L4p2M4p2N?=' . "\r\n" .
+					' =?UTF-8?B?4p2O4p2P4p2Q4p2R4p2S4p2T4p2U4p2V4p2W4p2X4p2Y4p2Z4p2a4p2b4p2c?=' . "\r\n" .
+					' =?UTF-8?B?4p2d4p2e?=';
+		$this->assertEqual($result, $expected);
+
+		$string = '⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠';
+		$result = Multibyte::mimeEncode($string);
+		$expected = '=?UTF-8?B?4rqA4rqB4rqC4rqD4rqE4rqF4rqG4rqH4rqI4rqJ4rqK4rqL4rqM4rqN4rqO?=' . "\r\n" .
+					' =?UTF-8?B?4rqP4rqQ4rqR4rqS4rqT4rqU4rqV4rqW4rqX4rqY4rqZ4rqb4rqc4rqd4rqe?=' . "\r\n" .
+					' =?UTF-8?B?4rqf4rqg4rqh4rqi4rqj4rqk4rql4rqm4rqn4rqo4rqp4rqq4rqr4rqs4rqt?=' . "\r\n" .
+					' =?UTF-8?B?4rqu4rqv4rqw4rqx4rqy4rqz4rq04rq14rq24rq34rq44rq54rq64rq74rq8?=' . "\r\n" .
+					' =?UTF-8?B?4rq94rq+4rq/4ruA4ruB4ruC4ruD4ruE4ruF4ruG4ruH4ruI4ruJ4ruK4ruL?=' . "\r\n" .
+					' =?UTF-8?B?4ruM4ruN4ruO4ruP4ruQ4ruR4ruS4ruT4ruU4ruV4ruW4ruX4ruY4ruZ4rua?=' . "\r\n" .
+					' =?UTF-8?B?4rub4ruc4rud4rue4ruf4rug?=';
+		$this->assertEqual($result, $expected);
+
+		$string = '⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿';
+		$result = Multibyte::mimeEncode($string);
+		$expected = '=?UTF-8?B?4r2F4r2G4r2H4r2I4r2J4r2K4r2L4r2M4r2N4r2O4r2P4r2Q4r2R4r2S4r2T?=' . "\r\n" .
+					' =?UTF-8?B?4r2U4r2V4r2W4r2X4r2Y4r2Z4r2a4r2b4r2c4r2d4r2e4r2f4r2g4r2h4r2i?=' . "\r\n" .
+					' =?UTF-8?B?4r2j4r2k4r2l4r2m4r2n4r2o4r2p4r2q4r2r4r2s4r2t4r2u4r2v4r2w4r2x?=' . "\r\n" .
+					' =?UTF-8?B?4r2y4r2z4r204r214r224r234r244r254r264r274r284r294r2+4r2/?=';
+		$this->assertEqual($result, $expected);
+
+		$string = '눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄';
+		$result = Multibyte::mimeEncode($string);
+		$expected = '=?UTF-8?B?64ih64ii64ij64ik64il64im64in64io64ip64iq64ir64is64it64iu64iv?=' . "\r\n" .
+					' =?UTF-8?B?64iw64ix64iy64iz64i064i164i264i364i464i564i664i764i864i964i+?=' . "\r\n" .
+					' =?UTF-8?B?64i/64mA64mB64mC64mD64mE64mF64mG64mH64mI64mJ64mK64mL64mM64mN?=' . "\r\n" .
+					' =?UTF-8?B?64mO64mP64mQ64mR64mS64mT64mU64mV64mW64mX64mY64mZ64ma64mb64mc?=' . "\r\n" .
+					' =?UTF-8?B?64md64me64mf64mg64mh64mi64mj64mk64ml64mm64mn64mo64mp64mq64mr?=' . "\r\n" .
+					' =?UTF-8?B?64ms64mt64mu64mv64mw64mx64my64mz64m064m164m264m364m464m564m6?=' . "\r\n" .
+					' =?UTF-8?B?64m764m864m964m+64m/64qA64qB64qC64qD64qE?=';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰ';
+		$result = Multibyte::mimeEncode($string);
+		$expected = '=?UTF-8?B?77mw77mx77my77mz77m077m177m277m377m477m577m677m777m877m977m+?=' . "\r\n" .
+					' =?UTF-8?B?77m/77qA77qB77qC77qD77qE77qF77qG77qH77qI77qJ77qK77qL77qM77qN?=' . "\r\n" .
+					' =?UTF-8?B?77qO77qP77qQ77qR77qS77qT77qU77qV77qW77qX77qY77qZ77qa77qb77qc?=' . "\r\n" .
+					' =?UTF-8?B?77qd77qe77qf77qg77qh77qi77qj77qk77ql77qm77qn77qo77qp77qq77qr?=' . "\r\n" .
+					' =?UTF-8?B?77qs77qt77qu77qv77qw?=';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ';
+		$result = Multibyte::mimeEncode($string);
+		$expected = '=?UTF-8?B?77qx77qy77qz77q077q177q277q377q477q577q677q777q877q977q+77q/?=' . "\r\n" .
+					' =?UTF-8?B?77uA77uB77uC77uD77uE77uF77uG77uH77uI77uJ77uK77uL77uM77uN77uO?=' . "\r\n" .
+					' =?UTF-8?B?77uP77uQ77uR77uS77uT77uU77uV77uW77uX77uY77uZ77ua77ub77uc77ud?=' . "\r\n" .
+					' =?UTF-8?B?77ue77uf77ug77uh77ui77uj77uk77ul77um77un77uo77up77uq77ur77us?=' . "\r\n" .
+					' =?UTF-8?B?77ut77uu77uv77uw77ux77uy77uz77u077u177u277u377u477u577u677u7?=' . "\r\n" .
+					' =?UTF-8?B?77u8?=';
+		$this->assertEqual($result, $expected);
+
+		$string = 'abcdefghijklmnopqrstuvwxyz';
+		$result = Multibyte::mimeEncode($string);
+		$expected = '=?UTF-8?B?772B772C772D772E772F772G772H772I772J772K772L772M772N772O772P?=' . "\r\n" .
+					' =?UTF-8?B?772Q772R772S772T772U772V772W772X772Y772Z772a?=';
+		$this->assertEqual($result, $expected);
+
+		$string = '。「」、・ヲァィゥェォャュョッーアイウエオカキク';
+		$result = Multibyte::mimeEncode($string);
+		$expected = '=?UTF-8?B?772h772i772j772k772l772m772n772o772p772q772r772s772t772u772v?=' . "\r\n" .
+					' =?UTF-8?B?772w772x772y772z77207721772277237724?=';
+		$this->assertEqual($result, $expected);
+
+		$string = 'ケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙';
+		$result = Multibyte::mimeEncode($string);
+		$expected = '=?UTF-8?B?77257726772777287729772+772/776A776B776C776D776E776F776G776H?=' . "\r\n" .
+					' =?UTF-8?B?776I776J776K776L776M776N776O776P776Q776R776S776T776U776V776W?=' . "\r\n" .
+					' =?UTF-8?B?776X776Y776Z776a776b776c776d776e?=';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Ĥēĺļŏ, Ŵőřļď!';
+		$result = Multibyte::mimeEncode($string);
+		$expected = '=?UTF-8?B?xKTEk8S6xLzFjywgxbTFkcWZxLzEjyE=?=';
+		$this->assertEqual($result, $expected);
+
+		$string = 'Hello, World!';
+		$result = Multibyte::mimeEncode($string);
+		$this->assertEqual($result, $string);
+
+		$string = 'čini';
+		$result = Multibyte::mimeEncode($string);
+		$expected = '=?UTF-8?B?xI1pbmk=?=';
+		$this->assertEqual($result, $expected);
+
+		$string = 'moći';
+		$result = Multibyte::mimeEncode($string);
+		$expected = '=?UTF-8?B?bW/Eh2k=?=';
+		$this->assertEqual($result, $expected);
+
+		$string = 'državni';
+		$result = Multibyte::mimeEncode($string);
+		$expected = '=?UTF-8?B?ZHLFvmF2bmk=?=';
+		$this->assertEqual($result, $expected);
+
+		$string = '把百度设为首页';
+		$result = Multibyte::mimeEncode($string);
+		$expected = '=?UTF-8?B?5oqK55m+5bqm6K6+5Li66aaW6aG1?=';
+		$this->assertEqual($result, $expected);
+
+		$string = '一二三周永龍';
+		$result = Multibyte::mimeEncode($string);
+		$expected = '=?UTF-8?B?5LiA5LqM5LiJ5ZGo5rC46b6N?=';
+		$this->assertEqual($result, $expected);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/object.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/object.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/object.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,866 @@
+<?php
+/**
+ * ObjectTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.5432
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', array('Object', 'Controller', 'Model'));
+
+/**
+ * RequestActionPost class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.object
+ */
+class RequestActionPost extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'ControllerPost'
+ * @access public
+ */
+	var $name = 'RequestActionPost';
+
+/**
+ * useTable property
+ *
+ * @var string 'posts'
+ * @access public
+ */
+	var $useTable = 'posts';
+}
+
+/**
+ * RequestActionController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class RequestActionController extends Controller {
+
+/**
+* uses property
+*
+* @var array
+* @access public
+*/
+	var $uses = array('RequestActionPost');
+
+/**
+* test_request_action method
+*
+* @access public
+* @return void
+*/
+	function test_request_action() {
+		return 'This is a test';
+	}
+
+/**
+* another_ra_test method
+*
+* @param mixed $id
+* @param mixed $other
+* @access public
+* @return void
+*/
+	function another_ra_test($id, $other) {
+		return $id + $other;
+	}
+
+/**
+ * normal_request_action method
+ *
+ * @access public
+ * @return void
+ */
+	function normal_request_action() {
+		return 'Hello World';
+	}
+
+/**
+ * returns $this->here
+ *
+ * @return void
+ */
+	function return_here() {
+		return $this->here;
+	}
+
+/**
+ * paginate_request_action method
+ *
+ * @access public
+ * @return void
+ */
+	function paginate_request_action() {
+		$data = $this->paginate();
+		return true;
+	}
+
+/**
+ * post pass, testing post passing
+ *
+ * @return array
+ */
+	function post_pass() {
+		return $this->data;
+	}
+
+/**
+ * test param passing and parsing.
+ *
+ * @return array
+ */
+	function params_pass() {
+		return $this->params;
+	}
+}
+
+/**
+ * RequestActionPersistentController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class RequestActionPersistentController extends Controller {
+
+/**
+* uses property
+*
+* @var array
+* @access public
+*/
+	var $uses = array('PersisterOne');
+
+/**
+* persistModel property
+*
+* @var array
+* @access public
+*/
+	var $persistModel = true;
+
+/**
+ * post pass, testing post passing
+ *
+ * @return array
+ */
+	function index() {
+		return 'This is a test';
+	}
+}
+
+/**
+ * TestObject class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class TestObject extends Object {
+
+/**
+ * firstName property
+ *
+ * @var string 'Joel'
+ * @access public
+ */
+	var $firstName = 'Joel';
+
+/**
+ * lastName property
+ *
+ * @var string 'Moss'
+ * @access public
+ */
+	var $lastName = 'Moss';
+
+/**
+ * methodCalls property
+ *
+ * @var array
+ * @access public
+ */
+	var $methodCalls = array();
+
+/**
+ * emptyMethod method
+ *
+ * @access public
+ * @return void
+ */
+	function emptyMethod() {
+		$this->methodCalls[] = 'emptyMethod';
+	}
+
+/**
+ * oneParamMethod method
+ *
+ * @param mixed $param
+ * @access public
+ * @return void
+ */
+	function oneParamMethod($param) {
+		$this->methodCalls[] = array('oneParamMethod' => array($param));
+	}
+
+/**
+ * twoParamMethod method
+ *
+ * @param mixed $param
+ * @param mixed $param2
+ * @access public
+ * @return void
+ */
+	function twoParamMethod($param, $param2) {
+		$this->methodCalls[] = array('twoParamMethod' => array($param, $param2));
+	}
+
+/**
+ * threeParamMethod method
+ *
+ * @param mixed $param
+ * @param mixed $param2
+ * @param mixed $param3
+ * @access public
+ * @return void
+ */
+	function threeParamMethod($param, $param2, $param3) {
+		$this->methodCalls[] = array('threeParamMethod' => array($param, $param2, $param3));
+	}
+	/**
+ * fourParamMethod method
+ *
+ * @param mixed $param
+ * @param mixed $param2
+ * @param mixed $param3
+ * @param mixed $param4
+ * @access public
+ * @return void
+ */
+	function fourParamMethod($param, $param2, $param3, $param4) {
+		$this->methodCalls[] = array('fourParamMethod' => array($param, $param2, $param3, $param4));
+	}
+	/**
+ * fiveParamMethod method
+ *
+ * @param mixed $param
+ * @param mixed $param2
+ * @param mixed $param3
+ * @param mixed $param4
+ * @param mixed $param5
+ * @access public
+ * @return void
+ */
+	function fiveParamMethod($param, $param2, $param3, $param4, $param5) {
+		$this->methodCalls[] = array('fiveParamMethod' => array($param, $param2, $param3, $param4, $param5));
+	}
+
+/**
+ * crazyMethod method
+ *
+ * @param mixed $param
+ * @param mixed $param2
+ * @param mixed $param3
+ * @param mixed $param4
+ * @param mixed $param5
+ * @param mixed $param6
+ * @param mixed $param7
+ * @access public
+ * @return void
+ */
+	function crazyMethod($param, $param2, $param3, $param4, $param5, $param6, $param7 = null) {
+		$this->methodCalls[] = array('crazyMethod' => array($param, $param2, $param3, $param4, $param5, $param6, $param7));
+	}
+
+/**
+ * methodWithOptionalParam method
+ *
+ * @param mixed $param
+ * @access public
+ * @return void
+ */
+	function methodWithOptionalParam($param = null) {
+		$this->methodCalls[] = array('methodWithOptionalParam' => array($param));
+	}
+
+/**
+ * testPersist
+ *
+ * @return void
+ */
+	function testPersist($name, $return = null, &$object, $type = null) {
+		return $this->_persist($name, $return, $object, $type);
+	}
+}
+
+/**
+ * ObjectTestModel class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class ObjectTestModel extends CakeTestModel {
+	var $useTable = false;
+	var $name = 'ObjectTestModel';
+}
+
+/**
+ * Object Test class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class ObjectTest extends CakeTestCase {
+
+/**
+ * fixtures
+ *
+ * @var string
+ */
+	var $fixtures = array('core.post', 'core.test_plugin_comment', 'core.comment');
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		$this->object = new TestObject();
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		unset($this->object);
+	}
+
+/**
+ * endTest
+ *
+ * @access public
+ * @return void
+ */
+	function endTest() {
+		App::build();
+	}
+
+/**
+ * testLog method
+ *
+ * @access public
+ * @return void
+ */
+	function testLog() {
+		@unlink(LOGS . 'error.log');
+		$this->assertTrue($this->object->log('Test warning 1'));
+		$this->assertTrue($this->object->log(array('Test' => 'warning 2')));
+		$result = file(LOGS . 'error.log');
+		$this->assertPattern('/^2[0-9]{3}-[0-9]+-[0-9]+ [0-9]+:[0-9]+:[0-9]+ Error: Test warning 1$/', $result[0]);
+		$this->assertPattern('/^2[0-9]{3}-[0-9]+-[0-9]+ [0-9]+:[0-9]+:[0-9]+ Error: Array$/', $result[1]);
+		$this->assertPattern('/^\($/', $result[2]);
+		$this->assertPattern('/\[Test\] => warning 2$/', $result[3]);
+		$this->assertPattern('/^\)$/', $result[4]);
+		unlink(LOGS . 'error.log');
+
+		@unlink(LOGS . 'error.log');
+		$this->assertTrue($this->object->log('Test warning 1', LOG_WARNING));
+		$this->assertTrue($this->object->log(array('Test' => 'warning 2'), LOG_WARNING));
+		$result = file(LOGS . 'error.log');
+		$this->assertPattern('/^2[0-9]{3}-[0-9]+-[0-9]+ [0-9]+:[0-9]+:[0-9]+ Warning: Test warning 1$/', $result[0]);
+		$this->assertPattern('/^2[0-9]{3}-[0-9]+-[0-9]+ [0-9]+:[0-9]+:[0-9]+ Warning: Array$/', $result[1]);
+		$this->assertPattern('/^\($/', $result[2]);
+		$this->assertPattern('/\[Test\] => warning 2$/', $result[3]);
+		$this->assertPattern('/^\)$/', $result[4]);
+		unlink(LOGS . 'error.log');
+	}
+
+/**
+ * testSet method
+ *
+ * @access public
+ * @return void
+ */
+	function testSet() {
+		$this->object->_set('a string');
+		$this->assertEqual($this->object->firstName, 'Joel');
+
+		$this->object->_set(array('firstName'));
+		$this->assertEqual($this->object->firstName, 'Joel');
+
+		$this->object->_set(array('firstName' => 'Ashley'));
+		$this->assertEqual($this->object->firstName, 'Ashley');
+
+		$this->object->_set(array('firstName' => 'Joel', 'lastName' => 'Moose'));
+		$this->assertEqual($this->object->firstName, 'Joel');
+		$this->assertEqual($this->object->lastName, 'Moose');
+	}
+
+/**
+ * testPersist method
+ *
+ * @access public
+ * @return void
+ */
+	function testPersist() {
+		ClassRegistry::flush();
+
+		$cacheDisable = Configure::read('Cache.disable');
+		Configure::write('Cache.disable', false);
+		@unlink(CACHE . 'persistent' . DS . 'testmodel.php');
+		$test = new stdClass;
+		$this->assertFalse($this->object->testPersist('TestModel', null, $test));
+		$this->assertFalse($this->object->testPersist('TestModel', true, $test));
+		$this->assertTrue($this->object->testPersist('TestModel', null, $test));
+		$this->assertTrue(file_exists(CACHE . 'persistent' . DS . 'testmodel.php'));
+		$this->assertTrue($this->object->testPersist('TestModel', true, $test));
+		$this->assertEqual($this->object->TestModel, $test);
+
+		@unlink(CACHE . 'persistent' . DS . 'testmodel.php');
+
+		$model =& new ObjectTestModel();
+		$expected = ClassRegistry::keys();
+
+		ClassRegistry::flush();
+		$data = array('object_test_model' => $model);
+		$this->assertFalse($this->object->testPersist('ObjectTestModel', true, $data));
+		$this->assertTrue(file_exists(CACHE . 'persistent' . DS . 'objecttestmodel.php'));
+
+		$this->object->testPersist('ObjectTestModel', true, $model, 'registry');
+
+		$result = ClassRegistry::keys();
+		$this->assertEqual($result, $expected);
+
+		$newModel = ClassRegistry::getObject('object_test_model');
+		$this->assertEqual('ObjectTestModel', $newModel->name);
+
+		@unlink(CACHE . 'persistent' . DS . 'objecttestmodel.php');
+
+		Configure::write('Cache.disable', $cacheDisable);
+	}
+
+/**
+ * testPersistWithRequestAction method
+ *
+ * @access public
+ * @return void
+ */
+	function testPersistWithBehavior() {
+		ClassRegistry::flush();
+
+		$cacheDisable = Configure::read('Cache.disable');
+		Configure::write('Cache.disable', false);
+
+		App::build(array(
+			'models' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'models' . DS),
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins'. DS),
+			'behaviors' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'models'. DS . 'behaviors' . DS),
+		), true);
+
+		$this->assertFalse(class_exists('PersisterOneBehaviorBehavior'));
+		$this->assertFalse(class_exists('PersisterTwoBehaviorBehavior'));
+		$this->assertFalse(class_exists('TestPluginPersisterBehavior'));
+		$this->assertFalse(class_exists('TestPluginAuthors'));
+
+		$Controller = new RequestActionPersistentController();
+		$Controller->persistModel = true;
+		$Controller->constructClasses();
+
+		$this->assertTrue(file_exists(CACHE . 'persistent' . DS . 'persisterone.php'));
+		$this->assertTrue(file_exists(CACHE . 'persistent' . DS . 'persisteroneregistry.php'));
+
+		$contents = file_get_contents(CACHE . 'persistent' . DS . 'persisteroneregistry.php');
+		$contents = str_replace('"PersisterOne"', '"PersisterTwo"', $contents);
+		$contents = str_replace('persister_one', 'persister_two', $contents);
+		$contents = str_replace('test_plugin_comment', 'test_plugin_authors', $contents);
+		$result = file_put_contents(CACHE . 'persistent' . DS . 'persisteroneregistry.php', $contents);
+
+		$this->assertTrue(class_exists('PersisterOneBehaviorBehavior'));
+		$this->assertTrue(class_exists('TestPluginPersisterOneBehavior'));
+		$this->assertTrue(class_exists('TestPluginComment'));
+		$this->assertFalse(class_exists('PersisterTwoBehaviorBehavior'));
+		$this->assertFalse(class_exists('TestPluginPersisterTwoBehavior'));
+		$this->assertFalse(class_exists('TestPluginAuthors'));
+
+		$Controller = new RequestActionPersistentController();
+		$Controller->persistModel = true;
+		$Controller->constructClasses();
+
+		$this->assertTrue(class_exists('PersisterOneBehaviorBehavior'));
+		$this->assertTrue(class_exists('PersisterTwoBehaviorBehavior'));
+		$this->assertTrue(class_exists('TestPluginPersisterTwoBehavior'));
+		$this->assertTrue(class_exists('TestPluginAuthors'));
+
+		@unlink(CACHE . 'persistent' . DS . 'persisterone.php');
+		@unlink(CACHE . 'persistent' . DS . 'persisteroneregistry.php');
+	}
+
+/**
+ * testPersistWithBehaviorAndRequestAction method
+ *
+ * @see testPersistWithBehavior
+ * @access public
+ * @return void
+ */
+	function testPersistWithBehaviorAndRequestAction() {
+		ClassRegistry::flush();
+
+		$cacheDisable = Configure::read('Cache.disable');
+		Configure::write('Cache.disable', false);
+
+		$this->assertFalse(class_exists('ContainableBehavior'));
+
+		App::build(array(
+			'models' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'models' . DS),
+			'behaviors' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'models'. DS . 'behaviors' . DS),
+		), true);
+
+		$this->assertFalse(class_exists('PersistOneBehaviorBehavior'));
+		$this->assertFalse(class_exists('PersistTwoBehaviorBehavior'));
+
+		$Controller = new RequestActionPersistentController();
+		$Controller->persistModel = true;
+		$Controller->constructClasses();
+
+		$this->assertTrue(file_exists(CACHE . 'persistent' . DS . 'persisterone.php'));
+		$this->assertTrue(file_exists(CACHE . 'persistent' . DS . 'persisteroneregistry.php'));
+
+		$keys = ClassRegistry::keys();
+		$this->assertEqual($keys, array(
+			'persister_one',
+			'comment',
+			'test_plugin_comment',
+			'test_plugin.test_plugin_comment',
+			'persister_one_behavior_behavior',
+			'test_plugin_persister_one_behavior',
+			'test_plugin.test_plugin_persister_one_behavior'
+		));
+
+		ob_start();
+		$Controller->set('content_for_layout', 'cool');
+		$Controller->render('index', 'ajax', '/layouts/ajax');
+		$result = ob_get_clean();
+
+		$keys = ClassRegistry::keys();
+		$this->assertEqual($keys, array(
+			'persister_one',
+			'comment',
+			'test_plugin_comment',
+			'test_plugin.test_plugin_comment',
+			'persister_one_behavior_behavior',
+			'test_plugin_persister_one_behavior',
+			'test_plugin.test_plugin_persister_one_behavior',
+			'view'
+		));
+		$result = $this->object->requestAction('/request_action_persistent/index');
+		$expected = 'This is a test';
+		$this->assertEqual($result, $expected);
+
+		@unlink(CACHE . 'persistent' . DS . 'persisterone.php');
+		@unlink(CACHE . 'persistent' . DS . 'persisteroneregistry.php');
+
+		$Controller = new RequestActionPersistentController();
+		$Controller->persistModel = true;
+		$Controller->constructClasses();
+
+		@unlink(CACHE . 'persistent' . DS . 'persisterone.php');
+		@unlink(CACHE . 'persistent' . DS . 'persisteroneregistry.php');
+
+		Configure::write('Cache.disable', $cacheDisable);
+	}
+
+/**
+ * testToString method
+ *
+ * @access public
+ * @return void
+ */
+	function testToString() {
+		$result = strtolower($this->object->toString());
+		$this->assertEqual($result, 'testobject');
+	}
+
+/**
+ * testMethodDispatching method
+ *
+ * @access public
+ * @return void
+ */
+	function testMethodDispatching() {
+		$this->object->emptyMethod();
+		$expected = array('emptyMethod');
+		$this->assertIdentical($this->object->methodCalls, $expected);
+
+		$this->object->oneParamMethod('Hello');
+		$expected[] = array('oneParamMethod' => array('Hello'));
+		$this->assertIdentical($this->object->methodCalls, $expected);
+
+		$this->object->twoParamMethod(true, false);
+		$expected[] = array('twoParamMethod' => array(true, false));
+		$this->assertIdentical($this->object->methodCalls, $expected);
+
+		$this->object->threeParamMethod(true, false, null);
+		$expected[] = array('threeParamMethod' => array(true, false, null));
+		$this->assertIdentical($this->object->methodCalls, $expected);
+
+		$this->object->crazyMethod(1, 2, 3, 4, 5, 6, 7);
+		$expected[] = array('crazyMethod' => array(1, 2, 3, 4, 5, 6, 7));
+		$this->assertIdentical($this->object->methodCalls, $expected);
+
+		$this->object = new TestObject();
+		$this->assertIdentical($this->object->methodCalls, array());
+
+		$this->object->dispatchMethod('emptyMethod');
+		$expected = array('emptyMethod');
+		$this->assertIdentical($this->object->methodCalls, $expected);
+
+		$this->object->dispatchMethod('oneParamMethod', array('Hello'));
+		$expected[] = array('oneParamMethod' => array('Hello'));
+		$this->assertIdentical($this->object->methodCalls, $expected);
+
+		$this->object->dispatchMethod('twoParamMethod', array(true, false));
+		$expected[] = array('twoParamMethod' => array(true, false));
+		$this->assertIdentical($this->object->methodCalls, $expected);
+
+		$this->object->dispatchMethod('threeParamMethod', array(true, false, null));
+		$expected[] = array('threeParamMethod' => array(true, false, null));
+		$this->assertIdentical($this->object->methodCalls, $expected);
+
+		$this->object->dispatchMethod('fourParamMethod', array(1, 2, 3, 4));
+		$expected[] = array('fourParamMethod' => array(1, 2, 3, 4));
+		$this->assertIdentical($this->object->methodCalls, $expected);
+
+		$this->object->dispatchMethod('fiveParamMethod', array(1, 2, 3, 4, 5));
+		$expected[] = array('fiveParamMethod' => array(1, 2, 3, 4, 5));
+		$this->assertIdentical($this->object->methodCalls, $expected);
+
+		$this->object->dispatchMethod('crazyMethod', array(1, 2, 3, 4, 5, 6, 7));
+		$expected[] = array('crazyMethod' => array(1, 2, 3, 4, 5, 6, 7));
+		$this->assertIdentical($this->object->methodCalls, $expected);
+
+		$this->object->dispatchMethod('methodWithOptionalParam', array('Hello'));
+		$expected[] = array('methodWithOptionalParam' => array("Hello"));
+		$this->assertIdentical($this->object->methodCalls, $expected);
+
+		$this->object->dispatchMethod('methodWithOptionalParam');
+		$expected[] = array('methodWithOptionalParam' => array(null));
+		$this->assertIdentical($this->object->methodCalls, $expected);
+	}
+
+/**
+ * testRequestAction method
+ *
+ * @access public
+ * @return void
+ */
+	function testRequestAction() {
+		App::build(array(
+			'models' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'models' . DS),
+			'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS),
+			'controllers' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'controllers' . DS)
+		));
+		$result = $this->object->requestAction('');
+		$this->assertFalse($result);
+
+		$result = $this->object->requestAction('/request_action/test_request_action');
+		$expected = 'This is a test';
+		$this->assertEqual($result, $expected);;
+
+		$result = $this->object->requestAction('/request_action/another_ra_test/2/5');
+		$expected = 7;
+		$this->assertEqual($result, $expected);
+
+		$result = $this->object->requestAction('/tests_apps/index', array('return'));
+		$expected = 'This is the TestsAppsController index view';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->object->requestAction('/tests_apps/some_method');
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$result = $this->object->requestAction('/request_action/paginate_request_action');
+		$this->assertTrue($result);
+
+		$result = $this->object->requestAction('/request_action/normal_request_action');
+		$expected = 'Hello World';
+		$this->assertEqual($result, $expected);
+		
+		App::build();
+	}
+
+/**
+ * test requestAction() and plugins.
+ *
+ * @return void
+ */
+	function testRequestActionPlugins() {
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS),
+		));
+		App::objects('plugin', null, false);
+		Router::reload();
+		
+		$result = $this->object->requestAction('/test_plugin/tests/index', array('return'));
+		$expected = 'test plugin index';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->object->requestAction('/test_plugin/tests/index/some_param', array('return'));
+		$expected = 'test plugin index';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->object->requestAction(
+			array('controller' => 'tests', 'action' => 'index', 'plugin' => 'test_plugin'), array('return')
+		);
+		$expected = 'test plugin index';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->object->requestAction('/test_plugin/tests/some_method');
+		$expected = 25;
+		$this->assertEqual($result, $expected);
+
+		$result = $this->object->requestAction(
+			array('controller' => 'tests', 'action' => 'some_method', 'plugin' => 'test_plugin')
+		);
+		$expected = 25;
+		$this->assertEqual($result, $expected);
+		
+		App::build();
+		App::objects('plugin', null, false);
+	}
+
+/**
+ * test requestAction() with arrays.
+ *
+ * @return void
+ */
+	function testRequestActionArray() {
+		App::build(array(
+			'models' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'models' . DS),
+			'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS),
+			'controllers' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'controllers' . DS)
+		));
+	
+		$result = $this->object->requestAction(
+			array('controller' => 'request_action', 'action' => 'test_request_action')
+		);
+		$expected = 'This is a test';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->object->requestAction(
+			array('controller' => 'request_action', 'action' => 'another_ra_test'), 
+			array('pass' => array('5', '7'))
+		);
+		$expected = 12;
+		$this->assertEqual($result, $expected);
+
+		$result = $this->object->requestAction(
+			array('controller' => 'tests_apps', 'action' => 'index'), array('return')
+		);
+		$expected = 'This is the TestsAppsController index view';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->object->requestAction(array('controller' => 'tests_apps', 'action' => 'some_method'));
+		$expected = 5;
+		$this->assertEqual($result, $expected);
+
+		$result = $this->object->requestAction(
+			array('controller' => 'request_action', 'action' => 'normal_request_action')
+		);
+		$expected = 'Hello World';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->object->requestAction(
+			array('controller' => 'request_action', 'action' => 'paginate_request_action')
+		);
+		$this->assertTrue($result);
+
+		$result = $this->object->requestAction(
+			array('controller' => 'request_action', 'action' => 'paginate_request_action'),
+			array('pass' => array(5), 'named' => array('param' => 'value'))
+		);
+		$this->assertTrue($result);
+
+		App::build();
+	}
+
+/**
+ * Test that requestAction() is populating $this->params properly
+ *
+ * @access public
+ * @return void
+ */
+	function testRequestActionParamParseAndPass() {
+		$result = $this->object->requestAction('/request_action/params_pass');
+		$this->assertTrue(isset($result['url']['url']));
+		$this->assertEqual($result['url']['url'], '/request_action/params_pass');
+		$this->assertEqual($result['controller'], 'request_action');
+		$this->assertEqual($result['action'], 'params_pass');
+		$this->assertEqual($result['form'], array());
+		$this->assertEqual($result['plugin'], null);
+
+		$result = $this->object->requestAction('/request_action/params_pass/sort:desc/limit:5');
+		$expected = array('sort' => 'desc', 'limit' => 5,);
+		$this->assertEqual($result['named'], $expected);
+
+		$result = $this->object->requestAction(
+			array('controller' => 'request_action', 'action' => 'params_pass'), 
+			array('named' => array('sort' => 'desc', 'limit' => 5))
+		);
+		$this->assertEqual($result['named'], $expected);
+	}
+
+/**
+ * test requestAction and POST parameter passing, and not passing when url is an array.
+ *
+ * @access public
+ * @return void
+ */
+	function testRequestActionPostPassing() {
+		$_tmp = $_POST;
+
+		$_POST = array('data' => array(
+			'item' => 'value'
+		));
+		$result = $this->object->requestAction(array('controller' => 'request_action', 'action' => 'post_pass'));
+		$expected = array();
+		$this->assertEqual($expected, $result);
+
+		$result = $this->object->requestAction(array('controller' => 'request_action', 'action' => 'post_pass'), array('data' => $_POST['data']));
+		$expected = $_POST['data'];
+		$this->assertEqual($expected, $result);
+
+		$result = $this->object->requestAction('/request_action/post_pass');
+		$expected = $_POST['data'];
+		$this->assertEqual($expected, $result);
+
+		$_POST = $_tmp;
+	}
+
+/**
+ * testCakeError
+ *
+ * @return void
+ */
+	function testCakeError() {
+
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/overloadable.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/overloadable.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/overloadable.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,39 @@
+<?php
+/**
+ * OverloadableTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.5432
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', 'Overloadable');
+
+/**
+ * OverloadableTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class OverloadableTest extends CakeTestCase {
+
+/**
+ * skip method
+ *
+ * @access public
+ * @return void
+ */
+	function skip() {
+		$this->skipIf(true, ' %s OverloadableTest not implemented');
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/router.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/router.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/router.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,2685 @@
+<?php
+/**
+ * RouterTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *	Licensed under The Open Group Test Suite License
+ *	Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', array('Router'));
+
+if (!defined('FULL_BASE_URL')) {
+	define('FULL_BASE_URL', 'http://cakephp.org');
+}
+
+/**
+ * RouterTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class RouterTest extends CakeTestCase {
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		$this->_routing = Configure::read('Routing');
+		Configure::write('Routing', array('admin' => null, 'prefixes' => array()));
+		Router::reload();
+		$this->router =& Router::getInstance();
+	}
+
+/**
+ * end the test and reset the environment
+ *
+ * @return void
+ */
+	function endTest() {
+		Configure::write('Routing', $this->_routing);
+	}
+
+/**
+ * testReturnedInstanceReference method
+ *
+ * @access public
+ * @return void
+ */
+	function testReturnedInstanceReference() {
+		$this->router->testVar = 'test';
+		$this->assertIdentical($this->router, Router::getInstance());
+		unset($this->router->testVar);
+	}
+
+/**
+ * testFullBaseURL method
+ *
+ * @access public
+ * @return void
+ */
+	function testFullBaseURL() {
+		$this->assertPattern('/^http(s)?:\/\//', Router::url('/', true));
+		$this->assertPattern('/^http(s)?:\/\//', Router::url(null, true));
+		$this->assertPattern('/^http(s)?:\/\//', Router::url(array('full_base' => true)));
+		$this->assertIdentical(FULL_BASE_URL . '/', Router::url(array('full_base' => true)));
+	}
+
+/**
+ * testRouteDefaultParams method
+ *
+ * @access public
+ * @return void
+ */
+	function testRouteDefaultParams() {
+		Router::connect('/:controller', array('controller' => 'posts'));
+		$this->assertEqual(Router::url(array('action' => 'index')), '/');
+	}
+
+/**
+ * testRouterIdentity method
+ *
+ * @access public
+ * @return void
+ */
+	function testRouterIdentity() {
+		$router2 = new Router();
+		$this->assertEqual(get_object_vars($this->router), get_object_vars($router2));
+	}
+
+/**
+ * testResourceRoutes method
+ *
+ * @access public
+ * @return void
+ */
+	function testResourceRoutes() {
+		Router::mapResources('Posts');
+
+		$_SERVER['REQUEST_METHOD'] = 'GET';
+		$result = Router::parse('/posts');
+		$this->assertEqual($result, array('pass' => array(), 'named' => array(), 'plugin' => '', 'controller' => 'posts', 'action' => 'index', '[method]' => 'GET'));
+		$this->assertEqual($this->router->__resourceMapped, array('posts'));
+
+		$_SERVER['REQUEST_METHOD'] = 'GET';
+		$result = Router::parse('/posts/13');
+		$this->assertEqual($result, array('pass' => array('13'), 'named' => array(), 'plugin' => '', 'controller' => 'posts', 'action' => 'view', 'id' => '13', '[method]' => 'GET'));
+		$this->assertEqual($this->router->__resourceMapped, array('posts'));
+
+		$_SERVER['REQUEST_METHOD'] = 'POST';
+		$result = Router::parse('/posts');
+		$this->assertEqual($result, array('pass' => array(), 'named' => array(), 'plugin' => '', 'controller' => 'posts', 'action' => 'add', '[method]' => 'POST'));
+		$this->assertEqual($this->router->__resourceMapped, array('posts'));
+
+		$_SERVER['REQUEST_METHOD'] = 'PUT';
+		$result = Router::parse('/posts/13');
+		$this->assertEqual($result, array('pass' => array('13'), 'named' => array(), 'plugin' => '', 'controller' => 'posts', 'action' => 'edit', 'id' => '13', '[method]' => 'PUT'));
+		$this->assertEqual($this->router->__resourceMapped, array('posts'));
+
+		$result = Router::parse('/posts/475acc39-a328-44d3-95fb-015000000000');
+		$this->assertEqual($result, array('pass' => array('475acc39-a328-44d3-95fb-015000000000'), 'named' => array(), 'plugin' => '', 'controller' => 'posts', 'action' => 'edit', 'id' => '475acc39-a328-44d3-95fb-015000000000', '[method]' => 'PUT'));
+		$this->assertEqual($this->router->__resourceMapped, array('posts'));
+
+		$_SERVER['REQUEST_METHOD'] = 'DELETE';
+		$result = Router::parse('/posts/13');
+		$this->assertEqual($result, array('pass' => array('13'), 'named' => array(), 'plugin' => '', 'controller' => 'posts', 'action' => 'delete', 'id' => '13', '[method]' => 'DELETE'));
+		$this->assertEqual($this->router->__resourceMapped, array('posts'));
+
+		$_SERVER['REQUEST_METHOD'] = 'GET';
+		$result = Router::parse('/posts/add');
+		$this->assertEqual($result, array('pass' => array(), 'named' => array(), 'plugin' => '', 'controller' => 'posts', 'action' => 'add'));
+		$this->assertEqual($this->router->__resourceMapped, array('posts'));
+
+		Router::reload();
+		Router::mapResources('Posts', array('id' => '[a-z0-9_]+'));
+
+		$_SERVER['REQUEST_METHOD'] = 'GET';
+		$result = Router::parse('/posts/add');
+		$this->assertEqual($result, array('pass' => array('add'), 'named' => array(), 'plugin' => '', 'controller' => 'posts', 'action' => 'view', 'id' => 'add', '[method]' => 'GET'));
+		$this->assertEqual($this->router->__resourceMapped, array('posts'));
+
+		$_SERVER['REQUEST_METHOD'] = 'PUT';
+		$result = Router::parse('/posts/name');
+		$this->assertEqual($result, array('pass' => array('name'), 'named' => array(), 'plugin' => '', 'controller' => 'posts', 'action' => 'edit', 'id' => 'name', '[method]' => 'PUT'));
+		$this->assertEqual($this->router->__resourceMapped, array('posts'));
+	}
+
+/**
+ * testMultipleResourceRoute method
+ *
+ * @access public
+ * @return void
+ */
+	function testMultipleResourceRoute() {
+		Router::connect('/:controller', array('action' => 'index', '[method]' => array('GET', 'POST')));
+
+		$_SERVER['REQUEST_METHOD'] = 'GET';
+		$result = Router::parse('/posts');
+		$this->assertEqual($result, array('pass' => array(), 'named' => array(), 'plugin' => '', 'controller' => 'posts', 'action' => 'index', '[method]' => array('GET', 'POST')));
+
+		$_SERVER['REQUEST_METHOD'] = 'POST';
+		$result = Router::parse('/posts');
+		$this->assertEqual($result, array('pass' => array(), 'named' => array(), 'plugin' => '', 'controller' => 'posts', 'action' => 'index', '[method]' => array('GET', 'POST')));
+	}
+
+/**
+ * testGenerateUrlResourceRoute method
+ *
+ * @access public
+ * @return void
+ */
+	function testGenerateUrlResourceRoute() {
+		Router::mapResources('Posts');
+
+		$result = Router::url(array('controller' => 'posts', 'action' => 'index', '[method]' => 'GET'));
+		$expected = '/posts';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('controller' => 'posts', 'action' => 'view', '[method]' => 'GET', 'id' => 10));
+		$expected = '/posts/10';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('controller' => 'posts', 'action' => 'add', '[method]' => 'POST'));
+		$expected = '/posts';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('controller' => 'posts', 'action' => 'edit', '[method]' => 'PUT', 'id' => 10));
+		$expected = '/posts/10';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('controller' => 'posts', 'action' => 'delete', '[method]' => 'DELETE', 'id' => 10));
+		$expected = '/posts/10';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('controller' => 'posts', 'action' => 'edit', '[method]' => 'POST', 'id' => 10));
+		$expected = '/posts/10';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testUrlNormalization method
+ *
+ * @access public
+ * @return void
+ */
+	function testUrlNormalization() {
+		$expected = '/users/logout';
+
+		$result = Router::normalize('/users/logout/');
+		$this->assertEqual($result, $expected);
+
+		$result = Router::normalize('//users//logout//');
+		$this->assertEqual($result, $expected);
+
+		$result = Router::normalize('users/logout');
+		$this->assertEqual($result, $expected);
+
+		$result = Router::normalize(array('controller' => 'users', 'action' => 'logout'));
+		$this->assertEqual($result, $expected);
+
+		$result = Router::normalize('/');
+		$this->assertEqual($result, '/');
+
+		$result = Router::normalize('http://google.com/');
+		$this->assertEqual($result, 'http://google.com/');
+
+		$result = Router::normalize('http://google.com//');
+		$this->assertEqual($result, 'http://google.com//');
+
+		$result = Router::normalize('/users/login/scope://foo');
+		$this->assertEqual($result, '/users/login/scope:/foo');
+
+		$result = Router::normalize('/recipe/recipes/add');
+		$this->assertEqual($result, '/recipe/recipes/add');
+
+		Router::setRequestInfo(array(array(), array('base' => '/us')));
+		$result = Router::normalize('/us/users/logout/');
+		$this->assertEqual($result, '/users/logout');
+
+		Router::reload();
+
+		Router::setRequestInfo(array(array(), array('base' => '/cake_12')));
+		$result = Router::normalize('/cake_12/users/logout/');
+		$this->assertEqual($result, '/users/logout');
+
+		Router::reload();
+		$_back = Configure::read('App.baseUrl');
+		Configure::write('App.baseUrl', '/');
+
+		Router::setRequestInfo(array(array(), array('base' => '/')));
+		$result = Router::normalize('users/login');
+		$this->assertEqual($result, '/users/login');
+		Configure::write('App.baseUrl', $_back);
+
+		Router::reload();
+		Router::setRequestInfo(array(array(), array('base' => 'beer')));
+		$result = Router::normalize('beer/admin/beers_tags/add');
+		$this->assertEqual($result, '/admin/beers_tags/add');
+
+		$result = Router::normalize('/admin/beers_tags/add');
+		$this->assertEqual($result, '/admin/beers_tags/add');
+	}
+
+/**
+ * test generation of basic urls.
+ *
+ * @access public
+ * @return void
+ */
+	function testUrlGenerationBasic() {
+		extract(Router::getNamedExpressions());
+
+		Router::setRequestInfo(array(
+			array(
+				'pass' => array(), 'action' => 'index', 'plugin' => null, 'controller' => 'subscribe',
+				'admin' => true, 'url' => array('url' => '')
+			),
+			array(
+				'base' => '/magazine', 'here' => '/magazine',
+				'webroot' => '/magazine/', 'passedArgs' => array('page' => 2), 'namedArgs' => array('page' => 2),
+			)
+		));
+		$result = Router::url();
+		$this->assertEqual('/magazine', $result);
+
+		Router::reload();
+
+		Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home'));
+		$out = Router::url(array('controller' => 'pages', 'action' => 'display', 'home'));
+		$this->assertEqual($out, '/');
+
+		Router::connect('/pages/*', array('controller' => 'pages', 'action' => 'display'));
+		$result = Router::url(array('controller' => 'pages', 'action' => 'display', 'about'));
+		$expected = '/pages/about';
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/:plugin/:id/*', array('controller' => 'posts', 'action' => 'view'), array('id' => $ID));
+		Router::parse('/');
+
+		$result = Router::url(array('plugin' => 'cake_plugin', 'controller' => 'posts', 'action' => 'view', 'id' => '1'));
+		$expected = '/cake_plugin/1';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('plugin' => 'cake_plugin', 'controller' => 'posts', 'action' => 'view', 'id' => '1', '0'));
+		$expected = '/cake_plugin/1/0';
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/:controller/:action/:id', array(), array('id' => $ID));
+		Router::parse('/');
+
+		$result = Router::url(array('controller' => 'posts', 'action' => 'view', 'id' => '1'));
+		$expected = '/posts/view/1';
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/:controller/:id', array('action' => 'view'));
+		Router::parse('/');
+
+		$result = Router::url(array('controller' => 'posts', 'action' => 'view', 'id' => '1'));
+		$expected = '/posts/1';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('controller' => 'posts', 'action' => 'index', '0'));
+		$expected = '/posts/index/0';
+		$this->assertEqual($result, $expected);
+
+		Router::connect('/view/*', array('controller' => 'posts', 'action' => 'view'));
+		Router::promote();
+		$result = Router::url(array('controller' => 'posts', 'action' => 'view', '1'));
+		$expected = '/view/1';
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::setRequestInfo(array(
+			array('pass' => array(), 'action' => 'index', 'plugin' => null, 'controller' => 'real_controller_name', 'url' => array('url' => '')),
+			array(
+				'base' => '/', 'here' => '/',
+				'webroot' => '/', 'passedArgs' => array('page' => 2), 'namedArgs' => array('page' => 2),
+			)
+		));
+		Router::connect('short_controller_name/:action/*', array('controller' => 'real_controller_name'));
+		Router::parse('/');
+
+		$result = Router::url(array('controller' => 'real_controller_name', 'page' => '1'));
+		$expected = '/short_controller_name/index/page:1';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('action' => 'add'));
+		$expected = '/short_controller_name/add';
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::parse('/');
+		Router::setRequestInfo(array(
+			array('pass' => array(), 'action' => 'index', 'plugin' => null, 'controller' => 'users', 'url' => array('url' => 'users')),
+			array(
+				'base' => '/', 'here' => '/',
+				'webroot' => '/', 'passedArgs' => array(), 'argSeparator' => ':', 'namedArgs' => array(),
+			)
+		));
+
+		$result = Router::url(array('action' => 'login'));
+		$expected = '/users/login';
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/page/*', array('plugin' => null, 'controller' => 'pages', 'action' => 'view'));
+		Router::parse('/');
+
+		$result = Router::url(array('plugin' => 'my_plugin', 'controller' => 'pages', 'action' => 'view', 'my-page'));
+		$expected = '/my_plugin/pages/view/my-page';
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/contact/:action', array('plugin' => 'contact', 'controller' => 'contact'));
+		Router::parse('/');
+
+		$result = Router::url(array('plugin' => 'contact', 'controller' => 'contact', 'action' => 'me'));
+
+		$expected = '/contact/me';
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::setRequestInfo(array(
+			array(
+				'pass' => array(), 'action' => 'index', 'plugin' => 'myplugin', 'controller' => 'mycontroller',
+				'admin' => false, 'url' => array('url' => array())
+			),
+			array(
+				'base' => '/', 'here' => '/',
+				'webroot' => '/', 'passedArgs' => array(), 'namedArgs' => array(),
+			)
+		));
+
+		$result = Router::url(array('plugin' => null, 'controller' => 'myothercontroller'));
+		$expected = '/myothercontroller';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * Test generation of routes with query string parameters.
+ *
+ * @return void
+ **/
+	function testUrlGenerationWithQueryStrings() {
+		$result = Router::url(array('controller' => 'posts', 'action'=>'index', '0', '?' => 'var=test&var2=test2'));
+		$expected = '/posts/index/0?var=test&var2=test2';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('controller' => 'posts', '0', '?' => 'var=test&var2=test2'));
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('controller' => 'posts', '0', '?' => array('var' => 'test', 'var2' => 'test2')));
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('controller' => 'posts', '0', '?' => array('var' => null)));
+		$this->assertEqual($result, '/posts/index/0');
+
+		$result = Router::url(array('controller' => 'posts', '0', '?' => 'var=test&var2=test2', '#' => 'unencoded string %'));
+		$expected = '/posts/index/0?var=test&var2=test2#unencoded+string+%25';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that regex validation of keyed route params is working.
+ *
+ * @return void
+ **/
+	function testUrlGenerationWithRegexQualifiedParams() {
+		Router::connect(
+			':language/galleries',
+			array('controller' => 'galleries', 'action' => 'index'),
+			array('language' => '[a-z]{3}')
+		);
+
+		Router::connect(
+			'/:language/:admin/:controller/:action/*',
+			array('admin' => 'admin'),
+			array('language' => '[a-z]{3}', 'admin' => 'admin')
+		);
+
+		Router::connect('/:language/:controller/:action/*',
+			array(),
+			array('language' => '[a-z]{3}')
+		);
+
+		$result = Router::url(array('admin' => false, 'language' => 'dan', 'action' => 'index', 'controller' => 'galleries'));
+		$expected = '/dan/galleries';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('admin' => false, 'language' => 'eng', 'action' => 'index', 'controller' => 'galleries'));
+		$expected = '/eng/galleries';
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/:language/pages',
+			array('controller' => 'pages', 'action' => 'index'),
+			array('language' => '[a-z]{3}')
+		);
+		Router::connect('/:language/:controller/:action/*', array(), array('language' => '[a-z]{3}'));
+
+		$result = Router::url(array('language' => 'eng', 'action' => 'index', 'controller' => 'pages'));
+		$expected = '/eng/pages';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('language' => 'eng', 'controller' => 'pages'));
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('language' => 'eng', 'controller' => 'pages', 'action' => 'add'));
+		$expected = '/eng/pages/add';
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/forestillinger/:month/:year/*',
+			array('plugin' => 'shows', 'controller' => 'shows', 'action' => 'calendar'),
+			array('month' => '0[1-9]|1[012]', 'year' => '[12][0-9]{3}')
+		);
+		Router::parse('/');
+
+		$result = Router::url(array('plugin' => 'shows', 'controller' => 'shows', 'action' => 'calendar', 'month' => 10, 'year' => 2007, 'min-forestilling'));
+		$expected = '/forestillinger/10/2007/min-forestilling';
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/kalender/:month/:year/*',
+			array('plugin' => 'shows', 'controller' => 'shows', 'action' => 'calendar'),
+			array('month' => '0[1-9]|1[012]', 'year' => '[12][0-9]{3}')
+		);
+		Router::connect('/kalender/*', array('plugin' => 'shows', 'controller' => 'shows', 'action' => 'calendar'));
+		Router::parse('/');
+
+		$result = Router::url(array('plugin' => 'shows', 'controller' => 'shows', 'action' => 'calendar', 'min-forestilling'));
+		$expected = '/kalender/min-forestilling';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('plugin' => 'shows', 'controller' => 'shows', 'action' => 'calendar', 'year' => 2007, 'month' => 10, 'min-forestilling'));
+		$expected = '/kalender/10/2007/min-forestilling';
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/:controller/:action/*', array(), array(
+			'controller' => 'source|wiki|commits|tickets|comments|view',
+			'action' => 'branches|history|branch|logs|view|start|add|edit|modify'
+		));
+		Router::defaults(false);
+		$result = Router::parse('/foo/bar');
+		$expected = array('pass' => array(), 'named' => array());
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * Test url generation with an admin prefix
+ *
+ * @access public
+ * @return void
+ */
+	function testUrlGenerationWithAdminPrefix() {
+		Configure::write('Routing.admin', 'admin');
+		Router::reload();
+
+		Router::connectNamed(array('event', 'lang'));
+		Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home'));
+		Router::connect('/pages/contact_us', array('controller' => 'pages', 'action' => 'contact_us'));
+		Router::connect('/pages/*', array('controller' => 'pages', 'action' => 'display'));
+		Router::connect('/reset/*', array('admin' => true, 'controller' => 'users', 'action' => 'reset'));
+		Router::connect('/tests', array('controller' => 'tests', 'action' => 'index'));
+		Router::parseExtensions('rss');
+
+		Router::setRequestInfo(array(
+			array('pass' => array(), 'named' => array(), 'controller' => 'registrations', 'action' => 'admin_index', 'plugin' => '', 'prefix' => 'admin', 'admin' => true, 'url' => array('ext' => 'html', 'url' => 'admin/registrations/index'), 'form' => array()),
+			array('base' => '', 'here' => '/admin/registrations/index', 'webroot' => '/')
+		));
+
+		$result = Router::url(array('page' => 2));
+		$expected = '/admin/registrations/index/page:2';
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::setRequestInfo(array(
+			array(
+				'pass' => array(), 'action' => 'admin_index', 'plugin' => null, 'controller' => 'subscriptions',
+				'admin' => true, 'url' => array('url' => 'admin/subscriptions/index/page:2'),
+			),
+			array(
+				'base' => '/magazine', 'here' => '/magazine/admin/subscriptions/index/page:2',
+				'webroot' => '/magazine/', 'passedArgs' => array('page' => 2),
+			)
+		));
+		Router::parse('/');
+
+		$result = Router::url(array('page' => 3));
+		$expected = '/magazine/admin/subscriptions/index/page:3';
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/admin/subscriptions/:action/*', array('controller' => 'subscribe', 'admin' => true, 'prefix' => 'admin'));
+		Router::parse('/');
+		Router::setRequestInfo(array(
+			array(
+				'pass' => array(), 'action' => 'admin_index', 'plugin' => null, 'controller' => 'subscribe',
+				'admin' => true, 'url' => array('url' => 'admin/subscriptions/edit/1')
+			),
+			array(
+				'base' => '/magazine', 'here' => '/magazine/admin/subscriptions/edit/1',
+				'webroot' => '/magazine/', 'passedArgs' => array('page' => 2), 'namedArgs' => array('page' => 2),
+			)
+		));
+
+		$result = Router::url(array('action' => 'edit', 1));
+		$expected = '/magazine/admin/subscriptions/edit/1';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('admin' => true, 'controller' => 'users', 'action' => 'login'));
+		$expected = '/magazine/admin/users/login';
+		$this->assertEqual($result, $expected);
+
+
+		Router::reload();
+		Router::setRequestInfo(array(
+			array('pass' => array(), 'admin' => true, 'action' => 'index', 'plugin' => null, 'controller' => 'users', 'url' => array('url' => 'users')),
+			array(
+				'base' => '/', 'here' => '/',
+				'webroot' => '/', 'passedArgs' => array(), 'argSeparator' => ':', 'namedArgs' => array(),
+			)
+		));
+		Router::connect('/page/*', array('controller' => 'pages', 'action' => 'view', 'admin' => true, 'prefix' => 'admin'));
+		Router::parse('/');
+
+		$result = Router::url(array('admin' => true, 'controller' => 'pages', 'action' => 'view', 'my-page'));
+		$expected = '/page/my-page';
+		$this->assertEqual($result, $expected);
+
+		Configure::write('Routing.admin', 'admin');
+		Router::reload();
+
+		Router::setRequestInfo(array(
+			array('plugin' => null, 'controller' => 'pages', 'action' => 'admin_add', 'pass' => array(), 'prefix' => 'admin', 'admin' => true, 'form' => array(), 'url' => array('url' => 'admin/pages/add')),
+			array('plugin' => null, 'controller' => null, 'action' => null, 'base' => '', 'here' => '/admin/pages/add', 'webroot' => '/')
+		));
+		Router::parse('/');
+
+		$result = Router::url(array('plugin' => null, 'controller' => 'pages', 'action' => 'add', 'id' => false));
+		$expected = '/admin/pages/add';
+		$this->assertEqual($result, $expected);
+
+
+		Router::reload();
+		Router::parse('/');
+		Router::setRequestInfo(array(
+			array('plugin' => null, 'controller' => 'pages', 'action' => 'admin_add', 'pass' => array(), 'prefix' => 'admin', 'admin' => true, 'form' => array(), 'url' => array('url' => 'admin/pages/add')),
+			array('plugin' => null, 'controller' => null, 'action' => null, 'base' => '', 'here' => '/admin/pages/add', 'webroot' => '/')
+		));
+
+		$result = Router::url(array('plugin' => null, 'controller' => 'pages', 'action' => 'add', 'id' => false));
+		$expected = '/admin/pages/add';
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/admin/:controller/:action/:id', array('admin' => true), array('id' => '[0-9]+'));
+		Router::parse('/');
+		Router::setRequestInfo(array(
+			array ('plugin' => null, 'controller' => 'pages', 'action' => 'admin_edit', 'pass' => array('284'), 'prefix' => 'admin', 'admin' => true, 'form' => array(), 'url' => array('url' => 'admin/pages/edit/284')),
+			array ('plugin' => null, 'controller' => null, 'action' => null, 'base' => '', 'here' => '/admin/pages/edit/284', 'webroot' => '/')
+		));
+
+		$result = Router::url(array('plugin' => null, 'controller' => 'pages', 'action' => 'edit', 'id' => '284'));
+		$expected = '/admin/pages/edit/284';
+		$this->assertEqual($result, $expected);
+
+
+		Router::reload();
+		Router::parse('/');
+		Router::setRequestInfo(array(
+			array ('plugin' => null, 'controller' => 'pages', 'action' => 'admin_add', 'pass' => array(), 'prefix' => 'admin', 'admin' => true, 'form' => array(), 'url' => array('url' => 'admin/pages/add')),
+			array ('plugin' => null, 'controller' => null, 'action' => null, 'base' => '', 'here' => '/admin/pages/add', 'webroot' => '/')
+		));
+
+		$result = Router::url(array('plugin' => null, 'controller' => 'pages', 'action' => 'add', 'id' => false));
+		$expected = '/admin/pages/add';
+		$this->assertEqual($result, $expected);
+
+
+		Router::reload();
+		Router::parse('/');
+		Router::setRequestInfo(array(
+			array('plugin' => null, 'controller' => 'pages', 'action' => 'admin_edit', 'pass' => array('284'), 'prefix' => 'admin', 'admin' => true, 'form' => array(), 'url' => array('url' => 'admin/pages/edit/284')),
+			array('plugin' => null, 'controller' => null, 'action' => null, 'base' => '', 'here' => '/admin/pages/edit/284', 'webroot' => '/')
+		));
+
+		$result = Router::url(array('plugin' => null, 'controller' => 'pages', 'action' => 'edit', 284));
+		$expected = '/admin/pages/edit/284';
+		$this->assertEqual($result, $expected);
+
+
+		Router::reload();
+		Router::connect('/admin/posts/*', array('controller' => 'posts', 'action' => 'index', 'admin' => true));
+		Router::parse('/');
+		Router::setRequestInfo(array(
+			array('pass' => array(), 'action' => 'admin_index', 'plugin' => null, 'controller' => 'posts', 'prefix' => 'admin', 'admin' => true, 'url' => array('url' => 'admin/posts')),
+			array('base' => '', 'here' => '/admin/posts', 'webroot' => '/')
+		));
+
+		$result = Router::url(array('all'));
+		$expected = '/admin/posts/all';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testUrlGenerationWithExtensions method
+ *
+ * @access public
+ * @return void
+ */
+	function testUrlGenerationWithExtensions() {
+		Router::parse('/');
+		$result = Router::url(array('plugin' => null, 'controller' => 'articles', 'action' => 'add', 'id' => null, 'ext' => 'json'));
+		$expected = '/articles/add.json';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('plugin' => null, 'controller' => 'articles', 'action' => 'add', 'ext' => 'json'));
+		$expected = '/articles/add.json';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('plugin' => null, 'controller' => 'articles', 'action' => 'index', 'id' => null, 'ext' => 'json'));
+		$expected = '/articles.json';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('plugin' => null, 'controller' => 'articles', 'action' => 'index', 'ext' => 'json'));
+		$expected = '/articles.json';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testPluginUrlGeneration method
+ *
+ * @access public
+ * @return void
+ */
+	function testUrlGenerationPlugins() {
+		Router::setRequestInfo(array(
+			array(
+				'controller' => 'controller', 'action' => 'index', 'form' => array(),
+				'url' => array(), 'plugin' => 'test'
+			),
+			array(
+				'base' => '/base', 'here' => '/clients/sage/portal/donations', 'webroot' => '/base/',
+				'passedArgs' => array(), 'argSeparator' => ':', 'namedArgs' => array()
+			)
+		));
+
+		$this->assertEqual(Router::url('read/1'), '/base/test/controller/read/1');
+
+		Router::reload();
+		Router::connect('/:lang/:plugin/:controller/*', array('action' => 'index'));
+
+		Router::setRequestInfo(array(
+			array(
+				'lang' => 'en',
+				'plugin' => 'shows', 'controller' => 'shows', 'action' => 'index', 'pass' =>
+					array(), 'form' => array(), 'url' =>
+					array('url' => 'en/shows/')),
+			array('plugin' => NULL, 'controller' => NULL, 'action' => NULL, 'base' => '',
+			'here' => '/en/shows/', 'webroot' => '/')
+		));
+
+		Router::parse('/en/shows/');
+
+		$result = Router::url(array(
+			'lang' => 'en',
+			'controller' => 'shows', 'action' => 'index', 'page' => '1',
+		));
+		$expected = '/en/shows/shows/page:1';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that you can leave active plugin routes with plugin = null
+ *
+ * @return void
+ */
+	function testCanLeavePlugin() {
+		Router::reload();
+		Router::connect(
+			'/admin/other/:controller/:action/*',
+			array(
+				'admin' => 1,
+				'plugin' => 'aliased',
+				'prefix' => 'admin'
+			)
+		);
+		Router::setRequestInfo(array(
+			array(
+				'pass' => array(),
+				'admin' => true,
+				'prefix' => 'admin',
+				'plugin' => 'this',
+				'action' => 'admin_index',
+				'controller' => 'interesting',
+				'url' => array('url' => 'admin/this/interesting/index'),
+			),
+			array(
+				'base' => '',
+				'here' => '/admin/this/interesting/index',
+				'webroot' => '/',
+				'passedArgs' => array(),
+			)
+		));
+		$result = Router::url(array('plugin' => null, 'controller' => 'posts', 'action' => 'index'));
+		$this->assertEqual($result, '/admin/posts');
+
+		$result = Router::url(array('controller' => 'posts', 'action' => 'index'));
+		$this->assertEqual($result, '/admin/this/posts');
+
+		$result = Router::url(array('plugin' => 'aliased', 'controller' => 'posts', 'action' => 'index'));
+		$this->assertEqual($result, '/admin/other/posts/index');
+	}
+
+/**
+ * testUrlParsing method
+ *
+ * @access public
+ * @return void
+ */
+	function testUrlParsing() {
+		extract(Router::getNamedExpressions());
+
+		Router::connect('/posts/:value/:somevalue/:othervalue/*', array('controller' => 'posts', 'action' => 'view'), array('value','somevalue', 'othervalue'));
+		$result = Router::parse('/posts/2007/08/01/title-of-post-here');
+		$expected = array('value' => '2007', 'somevalue' => '08', 'othervalue' => '01', 'controller' => 'posts', 'action' => 'view', 'plugin' =>'', 'pass' => array('0' => 'title-of-post-here'), 'named' => array());
+		$this->assertEqual($result, $expected);
+
+		$this->router->routes = array();
+		Router::connect('/posts/:year/:month/:day/*', array('controller' => 'posts', 'action' => 'view'), array('year' => $Year, 'month' => $Month, 'day' => $Day));
+		$result = Router::parse('/posts/2007/08/01/title-of-post-here');
+		$expected = array('year' => '2007', 'month' => '08', 'day' => '01', 'controller' => 'posts', 'action' => 'view', 'plugin' =>'', 'pass' => array('0' => 'title-of-post-here'), 'named' => array());
+		$this->assertEqual($result, $expected);
+
+		$this->router->routes = array();
+		Router::connect('/posts/:day/:year/:month/*', array('controller' => 'posts', 'action' => 'view'), array('year' => $Year, 'month' => $Month, 'day' => $Day));
+		$result = Router::parse('/posts/01/2007/08/title-of-post-here');
+		$expected = array('day' => '01', 'year' => '2007', 'month' => '08', 'controller' => 'posts', 'action' => 'view', 'plugin' =>'', 'pass' => array('0' => 'title-of-post-here'), 'named' => array());
+		$this->assertEqual($result, $expected);
+
+		$this->router->routes = array();
+		Router::connect('/posts/:month/:day/:year/*', array('controller' => 'posts', 'action' => 'view'), array('year' => $Year, 'month' => $Month, 'day' => $Day));
+		$result = Router::parse('/posts/08/01/2007/title-of-post-here');
+		$expected = array('month' => '08', 'day' => '01', 'year' => '2007', 'controller' => 'posts', 'action' => 'view', 'plugin' =>'', 'pass' => array('0' => 'title-of-post-here'), 'named' => array());
+		$this->assertEqual($result, $expected);
+
+		$this->router->routes = array();
+		Router::connect('/posts/:year/:month/:day/*', array('controller' => 'posts', 'action' => 'view'));
+		$result = Router::parse('/posts/2007/08/01/title-of-post-here');
+		$expected = array('year' => '2007', 'month' => '08', 'day' => '01', 'controller' => 'posts', 'action' => 'view', 'plugin' =>'', 'pass' => array('0' => 'title-of-post-here'), 'named' => array());
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		$result = Router::parse('/pages/display/home');
+		$expected = array('plugin' => null, 'pass' => array('home'), 'controller' => 'pages', 'action' => 'display', 'named' => array());
+		$this->assertEqual($result, $expected);
+
+		$result = Router::parse('pages/display/home/');
+		$this->assertEqual($result, $expected);
+
+		$result = Router::parse('pages/display/home');
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/page/*', array('controller' => 'test'));
+		$result = Router::parse('/page/my-page');
+		$expected = array('pass' => array('my-page'), 'plugin' => null, 'controller' => 'test', 'action' => 'index');
+
+		Router::reload();
+		Router::connect('/:language/contact', array('language' => 'eng', 'plugin' => 'contact', 'controller' => 'contact', 'action' => 'index'), array('language' => '[a-z]{3}'));
+		$result = Router::parse('/eng/contact');
+		$expected = array('pass' => array(), 'named' => array(), 'language' => 'eng', 'plugin' => 'contact', 'controller' => 'contact', 'action' => 'index');
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/forestillinger/:month/:year/*',
+			array('plugin' => 'shows', 'controller' => 'shows', 'action' => 'calendar'),
+			array('month' => '0[1-9]|1[012]', 'year' => '[12][0-9]{3}')
+		);
+
+		$result = Router::parse('/forestillinger/10/2007/min-forestilling');
+		$expected = array('pass' => array('min-forestilling'), 'plugin' => 'shows', 'controller' => 'shows', 'action' => 'calendar', 'year' => 2007, 'month' => 10, 'named' => array());
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/:controller/:action/*');
+		Router::connect('/', array('plugin' => 'pages', 'controller' => 'pages', 'action' => 'display'));
+		$result = Router::parse('/');
+		$expected = array('pass' => array(), 'named' => array(), 'controller' => 'pages', 'action' => 'display', 'plugin' => 'pages');
+		$this->assertEqual($result, $expected);
+
+		$result = Router::parse('/posts/edit/0');
+		$expected = array('pass' => array(0), 'named' => array(), 'controller' => 'posts', 'action' => 'edit', 'plugin' => null);
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/posts/:id::url_title', array('controller' => 'posts', 'action' => 'view'), array('pass' => array('id', 'url_title'), 'id' => '[\d]+'));
+		$result = Router::parse('/posts/5:sample-post-title');
+		$expected = array('pass' => array('5', 'sample-post-title'), 'named' => array(), 'id' => 5, 'url_title' => 'sample-post-title', 'plugin' => null, 'controller' => 'posts', 'action' => 'view');
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/posts/:id::url_title/*', array('controller' => 'posts', 'action' => 'view'), array('pass' => array('id', 'url_title'), 'id' => '[\d]+'));
+		$result = Router::parse('/posts/5:sample-post-title/other/params/4');
+		$expected = array('pass' => array('5', 'sample-post-title', 'other', 'params', '4'), 'named' => array(), 'id' => 5, 'url_title' => 'sample-post-title', 'plugin' => null, 'controller' => 'posts', 'action' => 'view');
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/posts/:url_title-(uuid::id)', array('controller' => 'posts', 'action' => 'view'), array('pass' => array('id', 'url_title'), 'id' => $UUID));
+		$result = Router::parse('/posts/sample-post-title-(uuid:47fc97a9-019c-41d1-a058-1fa3cbdd56cb)');
+		$expected = array('pass' => array('47fc97a9-019c-41d1-a058-1fa3cbdd56cb', 'sample-post-title'), 'named' => array(), 'id' => '47fc97a9-019c-41d1-a058-1fa3cbdd56cb', 'url_title' => 'sample-post-title', 'plugin' => null, 'controller' => 'posts', 'action' => 'view');
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/posts/view/*', array('controller' => 'posts', 'action' => 'view'), array('named' => false));
+		$result = Router::parse('/posts/view/foo:bar/routing:fun');
+		$expected = array('pass' => array('foo:bar', 'routing:fun'), 'named' => array(), 'plugin' => null, 'controller' => 'posts', 'action' => 'view');
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/posts/view/*', array('controller' => 'posts', 'action' => 'view'), array('named' => array('foo', 'answer')));
+		$result = Router::parse('/posts/view/foo:bar/routing:fun/answer:42');
+		$expected = array('pass' => array('routing:fun'), 'named' => array('foo' => 'bar', 'answer' => '42'), 'plugin' => null, 'controller' => 'posts', 'action' => 'view');
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/posts/view/*', array('controller' => 'posts', 'action' => 'view'), array('named' => array('foo', 'answer'), 'greedy' => true));
+		$result = Router::parse('/posts/view/foo:bar/routing:fun/answer:42');
+		$expected = array('pass' => array(), 'named' => array('foo' => 'bar', 'routing' => 'fun', 'answer' => '42'), 'plugin' => null, 'controller' => 'posts', 'action' => 'view');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that the persist key works.
+ *
+ * @return void
+ */
+	function testPersistentParameters() {
+		Router::reload();
+		Router::connect(
+			'/:lang/:color/posts/view/*',
+			array('controller' => 'posts', 'action' => 'view'),
+			array('persist' => array('lang', 'color')
+		));
+		Router::connect(
+			'/:lang/:color/posts/index',
+			array('controller' => 'posts', 'action' => 'index'),
+			array('persist' => array('lang')
+		));
+		Router::connect('/:lang/:color/posts/edit/*', array('controller' => 'posts', 'action' => 'edit'));
+		Router::connect('/about', array('controller' => 'pages', 'action' => 'view', 'about'));
+		Router::parse('/en/red/posts/view/5');
+
+		Router::setRequestInfo(array(
+			array('controller' => 'posts', 'action' => 'view', 'lang' => 'en', 'color' => 'red', 'form' => array(), 'url' => array(), 'plugin' => null),
+			array('base' => '/', 'here' => '/en/red/posts/view/5', 'webroot' => '/', 'passedArgs' => array(), 'argSeparator' => ':', 'namedArgs' => array())
+		));
+		$expected = '/en/red/posts/view/6';
+		$result = Router::url(array('controller' => 'posts', 'action' => 'view', 6));
+		$this->assertEqual($result, $expected);
+
+		$expected = '/en/blue/posts/index';
+		$result = Router::url(array('controller' => 'posts', 'action' => 'index', 'color' => 'blue'));
+		$this->assertEqual($result, $expected);
+
+		$expected = '/posts/edit/6';
+		$result = Router::url(array('controller' => 'posts', 'action' => 'edit', 6, 'color' => null, 'lang' => null));
+		$this->assertEqual($result, $expected);
+
+		$expected = '/posts';
+		$result = Router::url(array('controller' => 'posts', 'action' => 'index'));
+		$this->assertEqual($result, $expected);
+
+		$expected = '/posts/edit/7';
+		$result = Router::url(array('controller' => 'posts', 'action' => 'edit', 7));
+		$this->assertEqual($result, $expected);
+
+		$expected = '/about';
+		$result = Router::url(array('controller' => 'pages', 'action' => 'view', 'about'));
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testUuidRoutes method
+ *
+ * @access public
+ * @return void
+ */
+	function testUuidRoutes() {
+		Router::connect(
+			'/subjects/add/:category_id',
+			array('controller' => 'subjects', 'action' => 'add'),
+			array('category_id' => '\w{8}-\w{4}-\w{4}-\w{4}-\w{12}')
+		);
+		$result = Router::parse('/subjects/add/4795d601-19c8-49a6-930e-06a8b01d17b7');
+		$expected = array('pass' => array(), 'named' => array(), 'category_id' => '4795d601-19c8-49a6-930e-06a8b01d17b7', 'plugin' => null, 'controller' => 'subjects', 'action' => 'add');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testRouteSymmetry method
+ *
+ * @access public
+ * @return void
+ */
+	function testRouteSymmetry() {
+		Router::connect(
+			"/:extra/page/:slug/*",
+			array('controller' => 'pages', 'action' => 'view', 'extra' => null),
+			array("extra" => '[a-z1-9_]*', "slug" => '[a-z1-9_]+', "action" => 'view')
+		);
+
+		$result = Router::parse('/some_extra/page/this_is_the_slug');
+		$expected = array('pass' => array(), 'named' => array(), 'plugin' => null, 'controller' => 'pages', 'action' => 'view', 'slug' => 'this_is_the_slug', 'extra' => 'some_extra');
+		$this->assertEqual($result, $expected);
+
+		$result = Router::parse('/page/this_is_the_slug');
+		$expected = array('pass' => array(), 'named' => array(), 'plugin' => null, 'controller' => 'pages', 'action' => 'view', 'slug' => 'this_is_the_slug', 'extra' => null);
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect(
+			"/:extra/page/:slug/*",
+			array('controller' => 'pages', 'action' => 'view', 'extra' => null),
+			array("extra" => '[a-z1-9_]*', "slug" => '[a-z1-9_]+')
+		);
+		Router::parse('/');
+
+		$result = Router::url(array('admin' => null, 'plugin' => null, 'controller' => 'pages', 'action' => 'view', 'slug' => 'this_is_the_slug', 'extra' => null));
+		$expected = '/page/this_is_the_slug';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('admin' => null, 'plugin' => null, 'controller' => 'pages', 'action' => 'view', 'slug' => 'this_is_the_slug', 'extra' => 'some_extra'));
+		$expected = '/some_extra/page/this_is_the_slug';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * Test that Routing.prefixes and Routing.admin are used when a Router instance is created
+ * or reset
+ *
+ * @return void
+ */
+	function testRoutingPrefixesSetting() {
+		$restore = Configure::read('Routing');
+
+		Configure::write('Routing.admin', 'admin');
+		Configure::write('Routing.prefixes', array('member', 'super_user'));
+		Router::reload();
+		$result = Router::prefixes();
+		$expected = array('admin', 'member', 'super_user');
+		$this->assertEqual($result, $expected);
+
+		Configure::write('Routing.prefixes', 'member');
+		Router::reload();
+		$result = Router::prefixes();
+		$expected = array('admin', 'member');
+		$this->assertEqual($result, $expected);
+
+		Configure::write('Routing', $restore);
+	}
+
+/**
+ * test compatibility with old Routing.admin config setting.
+ *
+ * @access public
+ * @return void
+ * @todo Once Routing.admin is removed update these tests.
+ */
+	function testAdminRoutingCompatibility() {
+		Configure::write('Routing.admin', 'admin');
+
+		Router::reload();
+		Router::connect('/admin', array('admin' => true, 'controller' => 'users'));
+		$result = Router::parse('/admin');
+
+		$expected = array('pass' => array(), 'named' => array(), 'plugin' => '', 'controller' => 'users', 'action' => 'index', 'admin' => true, 'prefix' => 'admin');
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('admin' => true, 'controller' => 'posts', 'action' => 'index', '0', '?' => 'var=test&var2=test2'));
+		$expected = '/admin/posts/index/0?var=test&var2=test2';
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::parse('/');
+		$result = Router::url(array('admin' => false, 'controller' => 'posts', 'action' => 'index', '0', '?' => 'var=test&var2=test2'));
+		$expected = '/posts/index/0?var=test&var2=test2';
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::setRequestInfo(array(
+			array('admin' => true, 'controller' => 'controller', 'action' => 'index', 'form' => array(), 'url' => array(), 'plugin' => null),
+			array('base' => '/', 'here' => '/', 'webroot' => '/base/', 'passedArgs' => array(), 'argSeparator' => ':', 'namedArgs' => array())
+		));
+
+		Router::parse('/');
+		$result = Router::url(array('admin' => false, 'controller' => 'posts', 'action' => 'index', '0', '?' => 'var=test&var2=test2'));
+		$expected = '/posts/index/0?var=test&var2=test2';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('controller' => 'posts', 'action' => 'index', '0', '?' => 'var=test&var2=test2'));
+		$expected = '/admin/posts/index/0?var=test&var2=test2';
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		$result = Router::parse('admin/users/view/');
+		$expected = array('pass' => array(), 'named' => array(), 'controller' => 'users', 'action' => 'view', 'plugin' => null, 'prefix' => 'admin', 'admin' => true);
+		$this->assertEqual($result, $expected);
+
+		Configure::write('Routing.admin', 'beheer');
+
+		Router::reload();
+		Router::setRequestInfo(array(
+			array('beheer' => true, 'controller' => 'posts', 'action' => 'index', 'form' => array(), 'url' => array(), 'plugin' => null),
+			array('base' => '/', 'here' => '/beheer/posts/index', 'webroot' => '/', 'passedArgs' => array(), 'argSeparator' => ':', 'namedArgs' => array())
+		));
+
+		$result = Router::parse('beheer/users/view/');
+		$expected = array('pass' => array(), 'named' => array(), 'controller' => 'users', 'action' => 'view', 'plugin' => null, 'prefix' => 'beheer', 'beheer' => true);
+		$this->assertEqual($result, $expected);
+
+
+		$result = Router::url(array('controller' => 'posts', 'action' => 'index', '0', '?' => 'var=test&var2=test2'));
+		$expected = '/beheer/posts/index/0?var=test&var2=test2';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * Test prefix routing and plugin combinations
+ *
+ * @return void
+ */
+	function testPrefixRoutingAndPlugins() {
+		Configure::write('Routing.prefixes', array('admin'));
+		$paths = App::path('plugins');
+		App::build(array(
+			'plugins' =>  array(
+				TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS
+			)
+		), true);
+		App::objects('plugin', null, false);
+
+		Router::reload();
+		Router::setRequestInfo(array(
+			array('admin' => true, 'controller' => 'controller', 'action' => 'action',
+				'form' => array(), 'url' => array(), 'plugin' => null, 'prefix' => 'admin'),
+			array('base' => '/', 'here' => '/', 'webroot' => '/base/', 'passedArgs' => array(),
+				'argSeparator' => ':', 'namedArgs' => array())
+		));
+		Router::parse('/');
+
+		$result = Router::url(array('plugin' => 'test_plugin', 'controller' => 'test_plugin', 'action' => 'index'));
+		$expected = '/admin/test_plugin';
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::parse('/');
+		Router::setRequestInfo(array(
+			array(
+				'plugin' => 'test_plugin', 'controller' => 'show_tickets', 'action' => 'admin_edit',
+				'pass' => array('6'), 'prefix' => 'admin', 'admin' => true, 'form' => array(),
+				'url' => array('url' => 'admin/shows/show_tickets/edit/6')
+			),
+			array(
+				'plugin' => null, 'controller' => null, 'action' => null, 'base' => '',
+				'here' => '/admin/shows/show_tickets/edit/6', 'webroot' => '/'
+			)
+		));
+
+		$result = Router::url(array(
+			'plugin' => 'test_plugin', 'controller' => 'show_tickets', 'action' => 'edit', 6,
+			'admin' => true, 'prefix' => 'admin'
+		));
+		$expected = '/admin/test_plugin/show_tickets/edit/6';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array(
+			'plugin' => 'test_plugin', 'controller' => 'show_tickets', 'action' => 'index', 'admin' => true
+		));
+		$expected = '/admin/test_plugin/show_tickets';
+		$this->assertEqual($result, $expected);
+
+		App::build(array('plugins' => $paths));
+	}
+
+/**
+ * testExtensionParsingSetting method
+ *
+ * @access public
+ * @return void
+ */
+	function testExtensionParsingSetting() {
+		$router =& Router::getInstance();
+		$this->assertFalse($this->router->__parseExtensions);
+
+		$router->parseExtensions();
+		$this->assertTrue($this->router->__parseExtensions);
+	}
+
+/**
+ * testExtensionParsing method
+ *
+ * @access public
+ * @return void
+ */
+	function testExtensionParsing() {
+		Router::parseExtensions();
+
+		$result = Router::parse('/posts.rss');
+		$expected = array('plugin' => null, 'controller' => 'posts', 'action' => 'index', 'url' => array('ext' => 'rss'), 'pass'=> array(), 'named' => array());
+		$this->assertEqual($result, $expected);
+
+		$result = Router::parse('/posts/view/1.rss');
+		$expected = array('plugin' => null, 'controller' => 'posts', 'action' => 'view', 'pass' => array('1'), 'named' => array(), 'url' => array('ext' => 'rss'), 'named' => array());
+		$this->assertEqual($result, $expected);
+
+		$result = Router::parse('/posts/view/1.rss?query=test');
+		$this->assertEqual($result, $expected);
+
+		$result = Router::parse('/posts/view/1.atom');
+		$expected['url'] = array('ext' => 'atom');
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::parseExtensions('rss', 'xml');
+
+		$result = Router::parse('/posts.xml');
+		$expected = array('plugin' => null, 'controller' => 'posts', 'action' => 'index', 'url' => array('ext' => 'xml'), 'pass'=> array(), 'named' => array());
+		$this->assertEqual($result, $expected);
+
+		$result = Router::parse('/posts.atom?hello=goodbye');
+		$expected = array('plugin' => null, 'controller' => 'posts.atom', 'action' => 'index', 'pass' => array(), 'named' => array(), 'url' => array('ext' => 'html'));
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::parseExtensions();
+		$result = $this->router->__parseExtension('/posts.atom');
+		$expected = array('ext' => 'atom', 'url' => '/posts');
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/controller/action', array('controller' => 'controller', 'action' => 'action', 'url' => array('ext' => 'rss')));
+		$result = Router::parse('/controller/action');
+		$expected = array('controller' => 'controller', 'action' => 'action', 'plugin' => null, 'url' => array('ext' => 'rss'), 'named' => array(), 'pass' => array());
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::parseExtensions('rss');
+		Router::connect('/controller/action', array('controller' => 'controller', 'action' => 'action', 'url' => array('ext' => 'rss')));
+		$result = Router::parse('/controller/action');
+		$expected = array('controller' => 'controller', 'action' => 'action', 'plugin' => null, 'url' => array('ext' => 'rss'), 'named' => array(), 'pass' => array());
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testQuerystringGeneration method
+ *
+ * @access public
+ * @return void
+ */
+	function testQuerystringGeneration() {
+		$result = Router::url(array('controller' => 'posts', 'action'=>'index', '0', '?' => 'var=test&var2=test2'));
+		$expected = '/posts/index/0?var=test&var2=test2';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('controller' => 'posts', 'action'=>'index', '0', '?' => array('var' => 'test', 'var2' => 'test2')));
+		$this->assertEqual($result, $expected);
+
+		$expected .= '&more=test+data';
+		$result = Router::url(array('controller' => 'posts', 'action'=>'index', '0', '?' => array('var' => 'test', 'var2' => 'test2', 'more' => 'test data')));
+		$this->assertEqual($result, $expected);
+
+// Test bug #4614
+		$restore = ini_get('arg_separator.output');
+		ini_set('arg_separator.output', '&amp;');
+		$result = Router::url(array('controller' => 'posts', 'action'=>'index', '0', '?' => array('var' => 'test', 'var2' => 'test2', 'more' => 'test data')));
+		$this->assertEqual($result, $expected);
+		ini_set('arg_separator.output', $restore);
+
+		$result = Router::url(array('controller' => 'posts', 'action'=>'index', '0', '?' => array('var' => 'test', 'var2' => 'test2')), array('escape' => true));
+		$expected = '/posts/index/0?var=test&amp;var2=test2';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testConnectNamed method
+ *
+ * @access public
+ * @return void
+ */
+	function testConnectNamed() {
+		$named = Router::connectNamed(false, array('default' => true));
+		$this->assertFalse($named['greedy']);
+		$this->assertEqual(array_keys($named['rules']), $named['default']);
+
+		Router::reload();
+		Router::connect('/foo/*', array('controller' => 'bar', 'action' => 'fubar'));
+		Router::connectNamed(array(), array('argSeparator' => '='));
+		$result = Router::parse('/foo/param1=value1/param2=value2');
+		$expected = array('pass' => array(), 'named' => array('param1' => 'value1', 'param2' => 'value2'), 'controller' => 'bar', 'action' => 'fubar', 'plugin' => null);
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/controller/action/*', array('controller' => 'controller', 'action' => 'action'), array('named' => array('param1' => 'value[\d]')));
+		Router::connectNamed(array(), array('greedy' => false, 'argSeparator' => '='));
+		$result = Router::parse('/controller/action/param1=value1/param2=value2');
+		$expected = array('pass' => array('param2=value2'), 'named' => array('param1' => 'value1'), 'controller' => 'controller', 'action' => 'action', 'plugin' => null);
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/:controller/:action/*');
+		Router::connectNamed(array('page'), array('default' => false, 'greedy' => false));
+		$result = Router::parse('/categories/index?limit=5');
+		$this->assertTrue(empty($result['named']));
+	}
+
+/**
+ * testNamedArgsUrlGeneration method
+ *
+ * @access public
+ * @return void
+ */
+	function testNamedArgsUrlGeneration() {
+		$result = Router::url(array('controller' => 'posts', 'action' => 'index', 'published' => 1, 'deleted' => 1));
+		$expected = '/posts/index/published:1/deleted:1';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('controller' => 'posts', 'action' => 'index', 'published' => 0, 'deleted' => 0));
+		$expected = '/posts/index/published:0/deleted:0';
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		extract(Router::getNamedExpressions());
+		Router::connectNamed(array('file'=> '[\w\.\-]+\.(html|png)'));
+		Router::connect('/', array('controller' => 'graphs', 'action' => 'index'));
+		Router::connect('/:id/*', array('controller' => 'graphs', 'action' => 'view'), array('id' => $ID));
+
+		$result = Router::url(array('controller' => 'graphs', 'action' => 'view', 'id' => 12, 'file' => 'asdf.png'));
+		$expected = '/12/file:asdf.png';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('controller' => 'graphs', 'action' => 'view', 12, 'file' => 'asdf.foo'));
+		$expected = '/graphs/view/12/file:asdf.foo';
+		$this->assertEqual($result, $expected);
+
+		Configure::write('Routing.admin', 'admin');
+
+		Router::reload();
+		Router::setRequestInfo(array(
+			array('admin' => true, 'controller' => 'controller', 'action' => 'index', 'form' => array(), 'url' => array(), 'plugin' => null),
+			array('base' => '/', 'here' => '/', 'webroot' => '/base/', 'passedArgs' => array(), 'argSeparator' => ':', 'namedArgs' => array())
+		));
+		Router::parse('/');
+
+		$result = Router::url(array('page' => 1, 0 => null, 'sort' => 'controller', 'direction' => 'asc', 'order' => null));
+		$expected = "/admin/controller/index/page:1/sort:controller/direction:asc";
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::setRequestInfo(array(
+			array('admin' => true, 'controller' => 'controller', 'action' => 'index', 'form' => array(), 'url' => array(), 'plugin' => null),
+			array('base' => '/', 'here' => '/', 'webroot' => '/base/', 'passedArgs' => array('type'=> 'whatever'), 'argSeparator' => ':', 'namedArgs' => array('type'=> 'whatever'))
+		));
+
+		$result = Router::parse('/admin/controller/index/type:whatever');
+		$result = Router::url(array('type'=> 'new'));
+		$expected = "/admin/controller/index/type:new";
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testNamedArgsUrlParsing method
+ *
+ * @access public
+ * @return void
+ */
+	function testNamedArgsUrlParsing() {
+		$Router =& Router::getInstance();
+		Router::reload();
+		$result = Router::parse('/controller/action/param1:value1:1/param2:value2:3/param:value');
+		$expected = array('pass' => array(), 'named' => array('param1' => 'value1:1', 'param2' => 'value2:3', 'param' => 'value'), 'controller' => 'controller', 'action' => 'action', 'plugin' => null);
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		$result = Router::connectNamed(false);
+		$this->assertEqual(array_keys($result['rules']), array());
+		$this->assertFalse($result['greedy']);
+		$result = Router::parse('/controller/action/param1:value1:1/param2:value2:3/param:value');
+		$expected = array('pass' => array('param1:value1:1', 'param2:value2:3', 'param:value'), 'named' => array(), 'controller' => 'controller', 'action' => 'action', 'plugin' => null);
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		$result = Router::connectNamed(true);
+		$this->assertEqual(array_keys($result['rules']), $Router->named['default']);
+		$this->assertTrue($result['greedy']);
+		Router::reload();
+		Router::connectNamed(array('param1' => 'not-matching'));
+		$result = Router::parse('/controller/action/param1:value1:1/param2:value2:3/param:value');
+		$expected = array('pass' => array('param1:value1:1'), 'named' => array('param2' => 'value2:3', 'param' => 'value'), 'controller' => 'controller', 'action' => 'action', 'plugin' => null);
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/foo/:action/*', array('controller' => 'bar'), array('named' => array('param1' => array('action' => 'index')), 'greedy' => true));
+		$result = Router::parse('/foo/index/param1:value1:1/param2:value2:3/param:value');
+		$expected = array('pass' => array(), 'named' => array('param1' => 'value1:1', 'param2' => 'value2:3', 'param' => 'value'), 'controller' => 'bar', 'action' => 'index', 'plugin' => null);
+		$this->assertEqual($result, $expected);
+
+		$result = Router::parse('/foo/view/param1:value1:1/param2:value2:3/param:value');
+		$expected = array('pass' => array('param1:value1:1'), 'named' => array('param2' => 'value2:3', 'param' => 'value'), 'controller' => 'bar', 'action' => 'view', 'plugin' => null);
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connectNamed(array('param1' => '[\d]', 'param2' => '[a-z]', 'param3' => '[\d]'));
+		$result = Router::parse('/controller/action/param1:1/param2:2/param3:3');
+		$expected = array('pass' => array('param2:2'), 'named' => array('param1' => '1', 'param3' => '3'), 'controller' => 'controller', 'action' => 'action', 'plugin' => null);
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connectNamed(array('param1' => '[\d]', 'param2' => true, 'param3' => '[\d]'));
+		$result = Router::parse('/controller/action/param1:1/param2:2/param3:3');
+		$expected = array('pass' => array(), 'named' => array('param1' => '1', 'param2' => '2', 'param3' => '3'), 'controller' => 'controller', 'action' => 'action', 'plugin' => null);
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connectNamed(array('param1' => 'value[\d]+:[\d]+'), array('greedy' => false));
+		$result = Router::parse('/controller/action/param1:value1:1/param2:value2:3/param3:value');
+		$expected = array('pass' => array('param2:value2:3', 'param3:value'), 'named' => array('param1' => 'value1:1'), 'controller' => 'controller', 'action' => 'action', 'plugin' => null);
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/foo/*', array('controller' => 'bar', 'action' => 'fubar'), array('named' => array('param1' => 'value[\d]:[\d]')));
+		Router::connectNamed(array(), array('greedy' => false));
+		$result = Router::parse('/foo/param1:value1:1/param2:value2:3/param3:value');
+		$expected = array('pass' => array('param2:value2:3', 'param3:value'), 'named' => array('param1' => 'value1:1'), 'controller' => 'bar', 'action' => 'fubar', 'plugin' => null);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test url generation with legacy (1.2) style prefix routes.
+ *
+ * @access public
+ * @return void
+ * @todo Remove tests related to legacy style routes.
+ * @see testUrlGenerationWithAutoPrefixes
+ */
+	function testUrlGenerationWithLegacyPrefixes() {
+		Router::reload();
+		Router::connect('/protected/:controller/:action/*', array(
+			'prefix' => 'protected',
+			'protected' => true
+		));
+		Router::parse('/');
+
+		Router::setRequestInfo(array(
+			array('plugin' => null, 'controller' => 'images', 'action' => 'index', 'pass' => array(), 'prefix' => null, 'admin' => false, 'form' => array(), 'url' => array('url' => 'images/index')),
+			array('plugin' => null, 'controller' => null, 'action' => null, 'base' => '', 'here' => '/images/index', 'webroot' => '/')
+		));
+
+		$result = Router::url(array('protected' => true));
+		$expected = '/protected/images/index';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('controller' => 'images', 'action' => 'add'));
+		$expected = '/images/add';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('controller' => 'images', 'action' => 'add', 'protected' => true));
+		$expected = '/protected/images/add';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('action' => 'edit', 1));
+		$expected = '/images/edit/1';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('action' => 'edit', 1, 'protected' => true));
+		$expected = '/protected/images/edit/1';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('action' => 'protected_edit', 1, 'protected' => true));
+		$expected = '/protected/images/edit/1';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('action' => 'edit', 1, 'protected' => true));
+		$expected = '/protected/images/edit/1';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('controller' => 'others', 'action' => 'edit', 1));
+		$expected = '/others/edit/1';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('controller' => 'others', 'action' => 'edit', 1, 'protected' => true));
+		$expected = '/protected/others/edit/1';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('controller' => 'others', 'action' => 'edit', 1, 'protected' => true, 'page' => 1));
+		$expected = '/protected/others/edit/1/page:1';
+		$this->assertEqual($result, $expected);
+
+		Router::connectNamed(array('random'));
+		$result = Router::url(array('controller' => 'others', 'action' => 'edit', 1, 'protected' => true, 'random' => 'my-value'));
+		$expected = '/protected/others/edit/1/random:my-value';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test newer style automatically generated prefix routes.
+ *
+ * @return void
+ */
+	function testUrlGenerationWithAutoPrefixes() {
+		Configure::write('Routing.prefixes', array('protected'));
+		Router::reload();
+		Router::parse('/');
+
+		Router::setRequestInfo(array(
+			array('plugin' => null, 'controller' => 'images', 'action' => 'index', 'pass' => array(), 'prefix' => null, 'protected' => false, 'form' => array(), 'url' => array('url' => 'images/index')),
+			array('base' => '', 'here' => '/images/index', 'webroot' => '/')
+		));
+
+		$result = Router::url(array('controller' => 'images', 'action' => 'add'));
+		$expected = '/images/add';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('controller' => 'images', 'action' => 'add', 'protected' => true));
+		$expected = '/protected/images/add';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('action' => 'edit', 1));
+		$expected = '/images/edit/1';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('action' => 'edit', 1, 'protected' => true));
+		$expected = '/protected/images/edit/1';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('action' => 'protected_edit', 1, 'protected' => true));
+		$expected = '/protected/images/edit/1';
+		$this->assertEqual($result, $expected);
+		
+		$result = Router::url(array('action' => 'protectededit', 1, 'protected' => true));
+		$expected = '/protected/images/protectededit/1';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('action' => 'edit', 1, 'protected' => true));
+		$expected = '/protected/images/edit/1';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('controller' => 'others', 'action' => 'edit', 1));
+		$expected = '/others/edit/1';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('controller' => 'others', 'action' => 'edit', 1, 'protected' => true));
+		$expected = '/protected/others/edit/1';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('controller' => 'others', 'action' => 'edit', 1, 'protected' => true, 'page' => 1));
+		$expected = '/protected/others/edit/1/page:1';
+		$this->assertEqual($result, $expected);
+
+		Router::connectNamed(array('random'));
+		$result = Router::url(array('controller' => 'others', 'action' => 'edit', 1, 'protected' => true, 'random' => 'my-value'));
+		$expected = '/protected/others/edit/1/random:my-value';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that auto-generated prefix routes persist
+ *
+ * @return void
+ */
+	function testAutoPrefixRoutePersistence() {
+		Configure::write('Routing.prefixes', array('protected'));
+		Router::reload();
+		Router::parse('/');
+
+		Router::setRequestInfo(array(
+			array('plugin' => null, 'controller' => 'images', 'action' => 'index', 'pass' => array(), 'prefix' => 'protected', 'protected' => true, 'form' => array(), 'url' => array('url' => 'protected/images/index')),
+			array('base' => '', 'here' => '/protected/images/index', 'webroot' => '/')
+		));
+
+		$result = Router::url(array('controller' => 'images', 'action' => 'add'));
+		$expected = '/protected/images/add';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('controller' => 'images', 'action' => 'add', 'protected' => false));
+		$expected = '/images/add';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that setting a prefix override the current one
+ *
+ * @return void
+ */
+	function testPrefixOverride() {
+		Configure::write('Routing.prefixes', array('protected', 'admin'));
+		Router::reload();
+		Router::parse('/');
+
+		Router::setRequestInfo(array(
+			array('plugin' => null, 'controller' => 'images', 'action' => 'index', 'pass' => array(), 'prefix' => 'protected', 'protected' => true, 'form' => array(), 'url' => array('url' => 'protected/images/index')),
+			array('base' => '', 'here' => '/protected/images/index', 'webroot' => '/')
+		));
+
+		$result = Router::url(array('controller' => 'images', 'action' => 'add', 'admin' => true));
+		$expected = '/admin/images/add';
+		$this->assertEqual($result, $expected);
+
+		Router::setRequestInfo(array(
+			array('plugin' => null, 'controller' => 'images', 'action' => 'index', 'pass' => array(), 'prefix' => 'admin', 'admin' => true, 'form' => array(), 'url' => array('url' => 'admin/images/index')),
+			array('base' => '', 'here' => '/admin/images/index', 'webroot' => '/')
+		));
+		$result = Router::url(array('controller' => 'images', 'action' => 'add', 'protected' => true));
+		$expected = '/protected/images/add';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testRemoveBase method
+ *
+ * @access public
+ * @return void
+ */
+	function testRemoveBase() {
+		Router::setRequestInfo(array(
+			array('controller' => 'controller', 'action' => 'index', 'form' => array(), 'url' => array(), 'bare' => 0, 'plugin' => null),
+			array('base' => '/base', 'here' => '/', 'webroot' => '/base/', 'passedArgs' => array(), 'argSeparator' => ':', 'namedArgs' => array())
+		));
+
+		$result = Router::url(array('controller' => 'my_controller', 'action' => 'my_action'));
+		$expected = '/base/my_controller/my_action';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('controller' => 'my_controller', 'action' => 'my_action', 'base' => false));
+		$expected = '/my_controller/my_action';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('controller' => 'my_controller', 'action' => 'my_action', 'base' => true));
+		$expected = '/base/my_controller/my_action/base:1';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testPagesUrlParsing method
+ *
+ * @access public
+ * @return void
+ */
+	function testPagesUrlParsing() {
+		Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home'));
+		Router::connect('/pages/*', array('controller' => 'pages', 'action' => 'display'));
+
+		$result = Router::parse('/');
+		$expected = array('pass'=> array('home'), 'named' => array(), 'plugin' => null, 'controller' => 'pages', 'action' => 'display');
+		$this->assertEqual($result, $expected);
+
+		$result = Router::parse('/pages/home/');
+		$expected = array('pass' => array('home'), 'named' => array(), 'plugin' => null, 'controller' => 'pages', 'action' => 'display');
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home'));
+
+		$result = Router::parse('/pages/display/home/parameter:value');
+		$expected = array('pass' => array('home'), 'named' => array('parameter' => 'value'), 'plugin' => null, 'controller' => 'pages', 'action' => 'display');
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home'));
+
+		$result = Router::parse('/');
+		$expected = array('pass' => array('home'), 'named' => array(), 'plugin' => null, 'controller' => 'pages', 'action' => 'display');
+		$this->assertEqual($result, $expected);
+
+		$result = Router::parse('/pages/display/home/event:value');
+		$expected = array('pass' => array('home'), 'named' => array('event' => 'value'), 'plugin' => null, 'controller' => 'pages', 'action' => 'display');
+		$this->assertEqual($result, $expected);
+
+		$result = Router::parse('/pages/display/home/event:Val_u2');
+		$expected = array('pass' => array('home'), 'named' => array('event' => 'Val_u2'), 'plugin' => null, 'controller' => 'pages', 'action' => 'display');
+		$this->assertEqual($result, $expected);
+
+		$result = Router::parse('/pages/display/home/event:val-ue');
+		$expected = array('pass' => array('home'), 'named' => array('event' => 'val-ue'), 'plugin' => null, 'controller' => 'pages', 'action' => 'display');
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/', array('controller' => 'posts', 'action' => 'index'));
+		Router::connect('/pages/*', array('controller' => 'pages', 'action' => 'display'));
+		$result = Router::parse('/pages/contact/');
+
+		$expected = array('pass'=>array('contact'), 'named' => array(), 'plugin'=> null, 'controller'=>'pages', 'action'=>'display');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that requests with a trailing dot don't loose the do.
+ *
+ * @return void
+ */
+	function testParsingWithTrailingPeriod() {
+		Router::reload();
+		$result = Router::parse('/posts/view/something.');
+		$this->assertEqual($result['pass'][0], 'something.', 'Period was chopped off %s');
+
+		$result = Router::parse('/posts/view/something. . .');
+		$this->assertEqual($result['pass'][0], 'something. . .', 'Period was chopped off %s');
+	}
+
+/**
+ * test that requests with a trailing dot don't loose the do.
+ *
+ * @return void
+ */
+	function testParsingWithTrailingPeriodAndParseExtensions() {
+		Router::reload();
+		Router::parseExtensions('json');
+
+		$result = Router::parse('/posts/view/something.');
+		$this->assertEqual($result['pass'][0], 'something.', 'Period was chopped off %s');
+
+		$result = Router::parse('/posts/view/something. . .');
+		$this->assertEqual($result['pass'][0], 'something. . .', 'Period was chopped off %s');
+	}
+
+/**
+ * test that patterns work for :action
+ *
+ * @return void
+ */
+	function testParsingWithPatternOnAction() {
+		Router::reload();
+		Router::connect(
+			'/blog/:action/*',
+			array('controller' => 'blog_posts'),
+			array('action' => 'other|actions')
+		);
+		$result = Router::parse('/blog/other');
+		$expected = array(
+			'plugin' => null,
+			'controller' => 'blog_posts',
+			'action' => 'other',
+			'pass' => array(),
+			'named' => array()
+		);
+		$this->assertEqual($expected, $result);
+
+		$result = Router::parse('/blog/foobar');
+		$expected = array(
+			'plugin' => null,
+			'controller' => 'blog',
+			'action' => 'foobar',
+			'pass' => array(),
+			'named' => array()
+		);
+		$this->assertEqual($expected, $result);
+
+		$result = Router::url(array('controller' => 'blog_posts', 'action' => 'foo'));
+		$this->assertEqual('/blog_posts/foo', $result);
+
+		$result = Router::url(array('controller' => 'blog_posts', 'action' => 'actions'));
+		$this->assertEqual('/blog/actions', $result);
+	}
+
+/**
+ * testParsingWithPrefixes method
+ *
+ * @access public
+ * @return void
+ */
+	function testParsingWithPrefixes() {
+		$adminParams = array('prefix' => 'admin', 'admin' => true);
+		Router::connect('/admin/:controller', $adminParams);
+		Router::connect('/admin/:controller/:action', $adminParams);
+		Router::connect('/admin/:controller/:action/*', $adminParams);
+
+		Router::setRequestInfo(array(
+			array('controller' => 'controller', 'action' => 'index', 'form' => array(), 'url' => array(), 'plugin' => null),
+			array('base' => '/base', 'here' => '/', 'webroot' => '/base/', 'passedArgs' => array(), 'argSeparator' => ':', 'namedArgs' => array())
+		));
+
+		$result = Router::parse('/admin/posts/');
+		$expected = array('pass' => array(), 'named' => array(), 'prefix' => 'admin', 'plugin' => null, 'controller' => 'posts', 'action' => 'index', 'admin' => true);
+		$this->assertEqual($result, $expected);
+
+		$result = Router::parse('/admin/posts');
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('admin' => true, 'controller' => 'posts'));
+		$expected = '/base/admin/posts';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::prefixes();
+		$expected = array('admin');
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+
+		$prefixParams = array('prefix' => 'members', 'members' => true);
+		Router::connect('/members/:controller', $prefixParams);
+		Router::connect('/members/:controller/:action', $prefixParams);
+		Router::connect('/members/:controller/:action/*', $prefixParams);
+
+		Router::setRequestInfo(array(
+			array('controller' => 'controller', 'action' => 'index', 'form' => array(), 'url' => array(), 'plugin' => null),
+			array('base' => '/base', 'here' => '/', 'webroot' => '/', 'passedArgs' => array(), 'argSeparator' => ':', 'namedArgs' => array())
+		));
+
+		$result = Router::parse('/members/posts/index');
+		$expected = array('pass' => array(), 'named' => array(), 'prefix' => 'members', 'plugin' => null, 'controller' => 'posts', 'action' => 'index', 'members' => true);
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('members' => true, 'controller' => 'posts', 'action' =>'index', 'page' => 2));
+		$expected = '/base/members/posts/index/page:2';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('members' => true, 'controller' => 'users', 'action' => 'add'));
+		$expected = '/base/members/users/add';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::parse('/posts/index');
+		$expected = array('pass' => array(), 'named' => array(), 'plugin' => null, 'controller' => 'posts', 'action' => 'index');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * Tests URL generation with flags and prefixes in and out of context
+ *
+ * @access public
+ * @return void
+ */
+	function testUrlWritingWithPrefixes() {
+		Router::connect('/company/:controller/:action/*', array('prefix' => 'company', 'company' => true));
+		Router::connect('/login', array('controller' => 'users', 'action' => 'login'));
+
+		$result = Router::url(array('controller' => 'users', 'action' => 'login', 'company' => true));
+		$expected = '/company/users/login';
+		$this->assertEqual($result, $expected);
+
+		Router::setRequestInfo(array(
+			array('controller' => 'users', 'action' => 'login', 'company' => true, 'form' => array(), 'url' => array(), 'plugin' => null),
+			array('base' => '/', 'here' => '/', 'webroot' => '/base/')
+		));
+
+		$result = Router::url(array('controller' => 'users', 'action' => 'login', 'company' => false));
+		$expected = '/login';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test url generation with prefixes and custom routes
+ *
+ * @return void
+ */
+	function testUrlWritingWithPrefixesAndCustomRoutes() {
+		Router::connect(
+			'/admin/login',
+			array('controller' => 'users', 'action' => 'login', 'prefix' => 'admin', 'admin' => true)
+		);
+		Router::setRequestInfo(array(
+			array('controller' => 'posts', 'action' => 'index', 'admin' => true, 'prefix' => 'admin',
+				'form' => array(), 'url' => array(), 'plugin' => null
+			),
+			array('base' => '/', 'here' => '/', 'webroot' => '/')
+		));
+		$result = Router::url(array('controller' => 'users', 'action' => 'login', 'admin' => true));
+		$this->assertEqual($result, '/admin/login');
+
+		$result = Router::url(array('controller' => 'users', 'action' => 'login'));
+		$this->assertEqual($result, '/admin/login');
+
+		$result = Router::url(array('controller' => 'users', 'action' => 'admin_login'));
+		$this->assertEqual($result, '/admin/login');
+	}
+
+/**
+ * testPassedArgsOrder method
+ *
+ * @access public
+ * @return void
+ */
+	function testPassedArgsOrder() {
+		Router::connect('/test-passed/*', array('controller' => 'pages', 'action' => 'display', 'home'));
+		Router::connect('/test2/*', array('controller' => 'pages', 'action' => 'display', 2));
+		Router::connect('/test/*', array('controller' => 'pages', 'action' => 'display', 1));
+		Router::parse('/');
+
+		$result = Router::url(array('controller' => 'pages', 'action' => 'display', 1, 'whatever'));
+		$expected = '/test/whatever';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('controller' => 'pages', 'action' => 'display', 2, 'whatever'));
+		$expected = '/test2/whatever';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('controller' => 'pages', 'action' => 'display', 'home', 'whatever'));
+		$expected = '/test-passed/whatever';
+		$this->assertEqual($result, $expected);
+
+		Configure::write('Routing.prefixes', array('admin'));
+		Router::reload();
+
+		Router::setRequestInfo(array(
+			array('plugin' => null, 'controller' => 'images', 'action' => 'index', 'pass' => array(), 'named' => array(), 'prefix' => 'protected', 'protected' => true,  'form' => array(), 'url' => array ('url' => 'protected/images/index')),
+			array('plugin' => null, 'controller' => null, 'action' => null, 'base' => '', 'here' => '/protected/images/index', 'webroot' => '/')
+		));
+
+		Router::connect('/protected/:controller/:action/*', array(
+			'controller' => 'users',
+			'action' => 'index',
+			'prefix' => 'protected'
+		));
+
+		Router::parse('/');
+		$result = Router::url(array('controller' => 'images', 'action' => 'add'));
+		$expected = '/protected/images/add';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::prefixes();
+		$expected = array('admin', 'protected');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testRegexRouteMatching method
+ *
+ * @access public
+ * @return void
+ */
+	function testRegexRouteMatching() {
+		Router::connect('/:locale/:controller/:action/*', array(), array('locale' => 'dan|eng'));
+
+		$result = Router::parse('/test/test_action');
+		$expected = array('pass' => array(), 'named' => array(), 'controller' => 'test', 'action' => 'test_action', 'plugin' => null);
+		$this->assertEqual($result, $expected);
+
+		$result = Router::parse('/eng/test/test_action');
+		$expected = array('pass' => array(), 'named' => array(), 'locale' => 'eng', 'controller' => 'test', 'action' => 'test_action', 'plugin' => null);
+		$this->assertEqual($result, $expected);
+
+		$result = Router::parse('/badness/test/test_action');
+		$expected = array('pass' => array('test_action'), 'named' => array(), 'controller' => 'badness', 'action' => 'test', 'plugin' => null);
+		$this->assertEqual($result, $expected);
+
+		Router::reload();
+		Router::connect('/:locale/:controller/:action/*', array(), array('locale' => 'dan|eng'));
+
+		Router::setRequestInfo(array(
+			array('plugin' => null, 'controller' => 'test', 'action' => 'index', 'pass' => array(), 'form' => array(), 'url' => array ('url' => 'test/test_action')),
+			array('plugin' => null, 'controller' => null, 'action' => null, 'base' => '', 'here' => '/test/test_action', 'webroot' => '/')
+		));
+
+		$result = Router::url(array('action' => 'test_another_action'));
+		$expected = '/test/test_another_action';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('action' => 'test_another_action', 'locale' => 'eng'));
+		$expected = '/eng/test/test_another_action';
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('action' => 'test_another_action', 'locale' => 'badness'));
+		$expected = '/test/test_another_action/locale:badness';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testStripPlugin
+ *
+ * @return void
+ * @access public
+ */
+	function testStripPlugin() {
+		$pluginName = 'forums';
+		$url = 'example.com/' . $pluginName . '/';
+		$expected = 'example.com';
+
+		$this->assertEqual(Router::stripPlugin($url, $pluginName), $expected);
+		$this->assertEqual(Router::stripPlugin($url), $url);
+		$this->assertEqual(Router::stripPlugin($url, null), $url);
+	}
+/**
+ * testCurentRoute
+ *
+ * This test needs some improvement and actual requestAction() usage
+ *
+ * @return void
+ * @access public
+ */
+	function testCurrentRoute() {
+		$url = array('controller' => 'pages', 'action' => 'display', 'government');
+		Router::connect('/government', $url);
+		Router::parse('/government');
+		$route =& Router::currentRoute();
+		$this->assertEqual(array_merge($url, array('plugin' => null)), $route->defaults);
+	}
+/**
+ * testRequestRoute
+ *
+ * @return void
+ * @access public
+ */
+	function testRequestRoute() {
+		$url = array('controller' => 'products', 'action' => 'display', 5);
+		Router::connect('/government', $url);
+		Router::parse('/government');
+		$route =& Router::requestRoute();
+		$this->assertEqual(array_merge($url, array('plugin' => null)), $route->defaults);
+
+		// test that the first route is matched
+		$newUrl = array('controller' => 'products', 'action' => 'display', 6);
+		Router::connect('/government', $url);
+		Router::parse('/government');
+		$route =& Router::requestRoute();
+		$this->assertEqual(array_merge($url, array('plugin' => null)), $route->defaults);
+
+		// test that an unmatched route does not change the current route
+		$newUrl = array('controller' => 'products', 'action' => 'display', 6);
+		Router::connect('/actor', $url);
+		Router::parse('/government');
+		$route =& Router::requestRoute();
+		$this->assertEqual(array_merge($url, array('plugin' => null)), $route->defaults);
+	}
+/**
+ * testGetParams
+ *
+ * @return void
+ * @access public
+ */
+	function testGetParams() {
+		$paths = array('base' => '/', 'here' => '/products/display/5', 'webroot' => '/webroot');
+		$params = array('param1' => '1', 'param2' => '2');
+		Router::setRequestInfo(array($params, $paths));
+		$expected = array(
+			'plugin' => null, 'controller' => false, 'action' => false,
+			'param1' => '1', 'param2' => '2'
+		);
+		$this->assertEqual(Router::getparams(), $expected);
+		$this->assertEqual(Router::getparam('controller'), false);
+		$this->assertEqual(Router::getparam('param1'), '1');
+		$this->assertEqual(Router::getparam('param2'), '2');
+
+		Router::reload();
+
+		$params = array('controller' => 'pages', 'action' => 'display');
+		Router::setRequestInfo(array($params, $paths));
+		$expected = array('plugin' => null, 'controller' => 'pages', 'action' => 'display');
+		$this->assertEqual(Router::getparams(), $expected);
+		$this->assertEqual(Router::getparams(true), $expected);
+	}
+
+/**
+ * test that connectDefaults() can disable default route connection
+ *
+ * @return void
+ */
+	function testDefaultsMethod() {
+		Router::defaults(false);
+		Router::connect('/test/*', array('controller' => 'pages', 'action' => 'display', 2));
+		$result = Router::parse('/posts/edit/5');
+		$this->assertFalse(isset($result['controller']));
+		$this->assertFalse(isset($result['action']));
+	}
+
+/**
+ * test that the required default routes are connected.
+ *
+ * @return void
+ */
+	function testConnectDefaultRoutes() {
+		App::build(array(
+			'plugins' =>  array(
+				TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS
+			)
+		), true);
+		App::objects('plugin', null, false);
+		Router::reload();
+
+		$result = Router::url(array('plugin' => 'plugin_js', 'controller' => 'js_file', 'action' => 'index'));
+		$this->assertEqual($result, '/plugin_js/js_file');
+
+		$result = Router::parse('/plugin_js/js_file');
+		$expected = array(
+			'plugin' => 'plugin_js', 'controller' => 'js_file', 'action' => 'index',
+			'named' => array(), 'pass' => array()
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = Router::url(array('plugin' => 'test_plugin', 'controller' => 'test_plugin', 'action' => 'index'));
+		$this->assertEqual($result, '/test_plugin');
+
+		$result = Router::parse('/test_plugin');
+		$expected = array(
+			'plugin' => 'test_plugin', 'controller' => 'test_plugin', 'action' => 'index',
+			'named' => array(), 'pass' => array()
+		);
+
+		$this->assertEqual($result, $expected, 'Plugin shortcut route broken. %s');
+	}
+
+/**
+ * test using a custom route class for route connection
+ *
+ * @return void
+ */
+	function testUsingCustomRouteClass() {
+		Mock::generate('CakeRoute', 'MockConnectedRoute');
+		$routes = Router::connect(
+			'/:slug',
+			array('controller' => 'posts', 'action' => 'view'),
+			array('routeClass' => 'MockConnectedRoute', 'slug' => '[a-z_-]+')
+		);
+		$this->assertTrue(is_a($routes[0], 'MockConnectedRoute'), 'Incorrect class used. %s');
+		$expected = array('controller' => 'posts', 'action' => 'view', 'slug' => 'test');
+		$routes[0]->setReturnValue('parse', $expected);
+		$result = Router::parse('/test');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test reversing parameter arrays back into strings.
+ *
+ * @return void
+ */
+	function testRouterReverse() {
+		$params = array(
+			'controller' => 'posts',
+			'action' => 'view',
+			'pass' => array(1),
+			'named' => array(),
+			'url' => array(),
+			'autoRender' => 1,
+			'bare' => 1,
+			'return' => 1,
+			'requested' => 1
+		);
+		$result = Router::reverse($params);
+		$this->assertEqual($result, '/posts/view/1');
+
+		$params = array(
+			'controller' => 'posts',
+			'action' => 'index',
+			'pass' => array(1),
+			'named' => array('page' => 1, 'sort' => 'Article.title', 'direction' => 'desc'),
+			'url' => array()
+		);
+		$result = Router::reverse($params);
+		$this->assertEqual($result, '/posts/index/1/page:1/sort:Article.title/direction:desc');
+
+		Router::connect('/:lang/:controller/:action/*', array(), array('lang' => '[a-z]{3}'));
+		$params = array(
+			'lang' => 'eng',
+			'controller' => 'posts',
+			'action' => 'view',
+			'pass' => array(1),
+			'named' => array(),
+			'url' => array('url' => 'eng/posts/view/1')
+		);
+		$result = Router::reverse($params);
+		$this->assertEqual($result, '/eng/posts/view/1');
+
+		$params = array(
+			'lang' => 'eng',
+			'controller' => 'posts',
+			'action' => 'view',
+			'pass' => array(1),
+			'named' => array(),
+			'url' => array('url' => 'eng/posts/view/1', 'foo' => 'bar', 'baz' => 'quu'),
+			'paging' => array(),
+			'models' => array()
+		);
+		$result = Router::reverse($params);
+		$this->assertEqual($result, '/eng/posts/view/1?foo=bar&baz=quu');
+	}
+}
+
+/**
+ * Test case for CakeRoute
+ *
+ * @package cake.tests.cases.libs.
+ **/
+class CakeRouteTestCase extends CakeTestCase {
+/**
+ * startTest method
+ *
+ * @access public
+ * @return void
+ */
+	function startTest() {
+		$this->_routing = Configure::read('Routing');
+		Configure::write('Routing', array('admin' => null, 'prefixes' => array()));
+		Router::reload();
+	}
+
+/**
+ * end the test and reset the environment
+ *
+ * @return void
+ **/
+	function endTest() {
+		Configure::write('Routing', $this->_routing);
+	}
+
+/**
+ * Test the construction of a CakeRoute
+ *
+ * @return void
+ **/
+	function testConstruction() {
+		$route =& new CakeRoute('/:controller/:action/:id', array(), array('id' => '[0-9]+'));
+
+		$this->assertEqual($route->template, '/:controller/:action/:id');
+		$this->assertEqual($route->defaults, array());
+		$this->assertEqual($route->options, array('id' => '[0-9]+'));
+		$this->assertFalse($route->compiled());
+	}
+
+/**
+ * test Route compiling.
+ *
+ * @return void
+ **/
+	function testBasicRouteCompiling() {
+		$route =& new CakeRoute('/', array('controller' => 'pages', 'action' => 'display', 'home'));
+		$result = $route->compile();
+		$expected = '#^/*$#';
+		$this->assertEqual($result, $expected);
+		$this->assertEqual($route->keys, array());
+
+		$route =& new CakeRoute('/:controller/:action', array('controller' => 'posts'));
+		$result = $route->compile();
+
+		$this->assertPattern($result, '/posts/edit');
+		$this->assertPattern($result, '/posts/super_delete');
+		$this->assertNoPattern($result, '/posts');
+		$this->assertNoPattern($result, '/posts/super_delete/1');
+
+		$route =& new CakeRoute('/posts/foo:id', array('controller' => 'posts', 'action' => 'view'));
+		$result = $route->compile();
+
+		$this->assertPattern($result, '/posts/foo:1');
+		$this->assertPattern($result, '/posts/foo:param');
+		$this->assertNoPattern($result, '/posts');
+		$this->assertNoPattern($result, '/posts/');
+
+		$this->assertEqual($route->keys, array('id'));
+
+		$route =& new CakeRoute('/:plugin/:controller/:action/*', array('plugin' => 'test_plugin', 'action' => 'index'));
+		$result = $route->compile();
+		$this->assertPattern($result, '/test_plugin/posts/index');
+		$this->assertPattern($result, '/test_plugin/posts/edit/5');
+		$this->assertPattern($result, '/test_plugin/posts/edit/5/name:value/nick:name');
+	}
+
+/**
+ * test route names with - in them.
+ *
+ * @return void
+ */
+	function testHyphenNames() {
+		$route =& new CakeRoute('/articles/:date-from/:date-to', array(
+			'controller' => 'articles', 'action' => 'index'
+		));
+		$expected = array(
+			'controller' => 'articles',
+			'action' => 'index',
+			'date-from' => '2009-07-31',
+			'date-to' => '2010-07-31',
+			'named' => array(),
+			'pass' => array()
+		);
+		$result = $route->parse('/articles/2009-07-31/2010-07-31');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that route parameters that overlap don't cause errors.
+ *
+ * @return void
+ */
+	function testRouteParameterOverlap() {
+		$route =& new CakeRoute('/invoices/add/:idd/:id', array('controller' => 'invoices', 'action' => 'add'));
+		$result = $route->compile();
+		$this->assertPattern($result, '/invoices/add/1/3');
+
+		$route =& new CakeRoute('/invoices/add/:id/:idd', array('controller' => 'invoices', 'action' => 'add'));
+		$result = $route->compile();
+		$this->assertPattern($result, '/invoices/add/1/3');
+	}
+
+/**
+ * test compiling routes with keys that have patterns
+ *
+ * @return void
+ **/
+	function testRouteCompilingWithParamPatterns() {
+		extract(Router::getNamedExpressions());
+
+		$route = new CakeRoute(
+			'/:controller/:action/:id',
+			array(),
+			array('id' => $ID)
+		);
+		$result = $route->compile();
+		$this->assertPattern($result, '/posts/edit/1');
+		$this->assertPattern($result, '/posts/view/518098');
+		$this->assertNoPattern($result, '/posts/edit/name-of-post');
+		$this->assertNoPattern($result, '/posts/edit/4/other:param');
+		$this->assertEqual($route->keys, array('controller', 'action', 'id'));
+
+		$route =& new CakeRoute(
+			'/:lang/:controller/:action/:id',
+			array('controller' => 'testing4'),
+			array('id' => $ID, 'lang' => '[a-z]{3}')
+		);
+		$result = $route->compile();
+		$this->assertPattern($result, '/eng/posts/edit/1');
+		$this->assertPattern($result, '/cze/articles/view/1');
+		$this->assertNoPattern($result, '/language/articles/view/2');
+		$this->assertNoPattern($result, '/eng/articles/view/name-of-article');
+		$this->assertEqual($route->keys, array('lang', 'controller', 'action', 'id'));
+
+		foreach (array(':', '@', ';', '$', '-') as $delim) {
+			$route =& new CakeRoute('/posts/:id' . $delim . ':title');
+			$result = $route->compile();
+
+			$this->assertPattern($result, '/posts/1' . $delim . 'name-of-article');
+			$this->assertPattern($result, '/posts/13244' . $delim . 'name-of_Article[]');
+			$this->assertNoPattern($result, '/posts/11!nameofarticle');
+			$this->assertNoPattern($result, '/posts/11');
+
+			$this->assertEqual($route->keys, array('id', 'title'));
+		}
+
+		$route =& new CakeRoute(
+			'/posts/:id::title/:year',
+			array('controller' => 'posts', 'action' => 'view'),
+			array('id' => $ID, 'year' => $Year, 'title' => '[a-z-_]+')
+		);
+		$result = $route->compile();
+		$this->assertPattern($result, '/posts/1:name-of-article/2009/');
+		$this->assertPattern($result, '/posts/13244:name-of-article/1999');
+		$this->assertNoPattern($result, '/posts/hey_now:nameofarticle');
+		$this->assertNoPattern($result, '/posts/:nameofarticle/2009');
+		$this->assertNoPattern($result, '/posts/:nameofarticle/01');
+		$this->assertEqual($route->keys, array('id', 'title', 'year'));
+
+		$route =& new CakeRoute(
+			'/posts/:url_title-(uuid::id)',
+			array('controller' => 'posts', 'action' => 'view'),
+			array('pass' => array('id', 'url_title'), 'id' => $ID)
+		);
+		$result = $route->compile();
+		$this->assertPattern($result, '/posts/some_title_for_article-(uuid:12534)/');
+		$this->assertPattern($result, '/posts/some_title_for_article-(uuid:12534)');
+		$this->assertNoPattern($result, '/posts/');
+		$this->assertNoPattern($result, '/posts/nameofarticle');
+		$this->assertNoPattern($result, '/posts/nameofarticle-12347');
+		$this->assertEqual($route->keys, array('url_title', 'id'));
+	}
+
+/**
+ * test more complex route compiling & parsing with mid route greedy stars
+ * and optional routing parameters
+ *
+ * @return void
+ */
+	function testComplexRouteCompilingAndParsing() {
+		extract(Router::getNamedExpressions());
+
+		$route =& new CakeRoute(
+			'/posts/:month/:day/:year/*',
+			array('controller' => 'posts', 'action' => 'view'),
+			array('year' => $Year, 'month' => $Month, 'day' => $Day)
+		);
+		$result = $route->compile();
+		$this->assertPattern($result, '/posts/08/01/2007/title-of-post');
+		$result = $route->parse('/posts/08/01/2007/title-of-post');
+
+		$this->assertEqual(count($result), 8);
+		$this->assertEqual($result['controller'], 'posts');
+		$this->assertEqual($result['action'], 'view');
+		$this->assertEqual($result['year'], '2007');
+		$this->assertEqual($result['month'], '08');
+		$this->assertEqual($result['day'], '01');
+
+		$route =& new CakeRoute(
+			"/:extra/page/:slug/*",
+			array('controller' => 'pages', 'action' => 'view', 'extra' => null),
+			array("extra" => '[a-z1-9_]*', "slug" => '[a-z1-9_]+', "action" => 'view')
+		);
+		$result = $route->compile();
+
+		$this->assertPattern($result, '/some_extra/page/this_is_the_slug');
+		$this->assertPattern($result, '/page/this_is_the_slug');
+		$this->assertEqual($route->keys, array('extra', 'slug'));
+		$this->assertEqual($route->options, array('extra' => '[a-z1-9_]*', 'slug' => '[a-z1-9_]+', 'action' => 'view'));
+		$expected = array(
+			'controller' => 'pages',
+			'action' => 'view',
+			'extra' => null,
+		);
+		$this->assertEqual($route->defaults, $expected);
+
+		$route =& new CakeRoute(
+			'/:controller/:action/*',
+			array('project' => false),
+			array(
+				'controller' => 'source|wiki|commits|tickets|comments|view',
+				'action' => 'branches|history|branch|logs|view|start|add|edit|modify'
+			)
+		);
+		$this->assertFalse($route->parse('/chaw_test/wiki'));
+
+		$result = $route->compile();
+		$this->assertNoPattern($result, '/some_project/source');
+		$this->assertPattern($result, '/source/view');
+		$this->assertPattern($result, '/source/view/other/params');
+		$this->assertNoPattern($result, '/chaw_test/wiki');
+		$this->assertNoPattern($result, '/source/wierd_action');
+	}
+
+/**
+ * test that routes match their pattern.
+ *
+ * @return void
+ **/
+	function testMatchBasic() {
+		$route = new CakeRoute('/:controller/:action/:id', array('plugin' => null));
+		$result = $route->match(array('controller' => 'posts', 'action' => 'view', 'plugin' => null));
+		$this->assertFalse($result);
+
+		$result = $route->match(array('plugin' => null, 'controller' => 'posts', 'action' => 'view', 0));
+		$this->assertFalse($result);
+
+		$result = $route->match(array('plugin' => null, 'controller' => 'posts', 'action' => 'view', 'id' => 1));
+		$this->assertEqual($result, '/posts/view/1');
+
+		$route =& new CakeRoute('/', array('controller' => 'pages', 'action' => 'display', 'home'));
+		$result = $route->match(array('controller' => 'pages', 'action' => 'display', 'home'));
+		$this->assertEqual($result, '/');
+
+		$result = $route->match(array('controller' => 'pages', 'action' => 'display', 'about'));
+		$this->assertFalse($result);
+
+
+		$route =& new CakeRoute('/pages/*', array('controller' => 'pages', 'action' => 'display'));
+		$result = $route->match(array('controller' => 'pages', 'action' => 'display', 'home'));
+		$this->assertEqual($result, '/pages/home');
+
+		$result = $route->match(array('controller' => 'pages', 'action' => 'display', 'about'));
+		$this->assertEqual($result, '/pages/about');
+
+
+		$route =& new CakeRoute('/blog/:action', array('controller' => 'posts'));
+		$result = $route->match(array('controller' => 'posts', 'action' => 'view'));
+		$this->assertEqual($result, '/blog/view');
+
+		$result = $route->match(array('controller' => 'nodes', 'action' => 'view'));
+		$this->assertFalse($result);
+
+		$result = $route->match(array('controller' => 'posts', 'action' => 'view', 1));
+		$this->assertFalse($result);
+
+		$result = $route->match(array('controller' => 'posts', 'action' => 'view', 'id' => 2));
+		$this->assertFalse($result);
+
+
+		$route =& new CakeRoute('/foo/:controller/:action', array('action' => 'index'));
+		$result = $route->match(array('controller' => 'posts', 'action' => 'view'));
+		$this->assertEqual($result, '/foo/posts/view');
+
+
+		$route =& new CakeRoute('/:plugin/:id/*', array('controller' => 'posts', 'action' => 'view'));
+		$result = $route->match(array('plugin' => 'test', 'controller' => 'posts', 'action' => 'view', 'id' => '1'));
+		$this->assertEqual($result, '/test/1/');
+
+		$result = $route->match(array('plugin' => 'fo', 'controller' => 'posts', 'action' => 'view', 'id' => '1', '0'));
+		$this->assertEqual($result, '/fo/1/0');
+
+		$result = $route->match(array('plugin' => 'fo', 'controller' => 'nodes', 'action' => 'view', 'id' => 1));
+		$this->assertFalse($result);
+
+		$result = $route->match(array('plugin' => 'fo', 'controller' => 'posts', 'action' => 'edit', 'id' => 1));
+		$this->assertFalse($result);
+
+		$route =& new CakeRoute('/admin/subscriptions/:action/*', array(
+			'controller' => 'subscribe', 'admin' => true, 'prefix' => 'admin'
+		));
+
+		$url = array('controller' => 'subscribe', 'admin' => true, 'action' => 'edit', 1);
+		$result = $route->match($url);
+		$expected = '/admin/subscriptions/edit/1';
+		$this->assertEqual($result, $expected);
+
+		$route =& new CakeRoute('/articles/:date-from/:date-to', array(
+			'controller' => 'articles', 'action' => 'index'
+		));
+		$url = array(
+			'controller' => 'articles',
+			'action' => 'index',
+			'date-from' => '2009-07-31',
+			'date-to' => '2010-07-31'
+		);
+
+		$result = $route->match($url);
+		$expected = '/articles/2009-07-31/2010-07-31';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test match() with greedy routes, named parameters and passed args.
+ *
+ * @return void
+ */
+	function testMatchWithNamedParametersAndPassedArgs() {
+		Router::connectNamed(true);
+
+		$route = new CakeRoute('/:controller/:action/*', array('plugin' => null));
+		$result = $route->match(array('controller' => 'posts', 'action' => 'index', 'plugin' => null, 'page' => 1));
+		$this->assertEqual($result, '/posts/index/page:1');
+
+		$result = $route->match(array('controller' => 'posts', 'action' => 'view', 'plugin' => null, 5));
+		$this->assertEqual($result, '/posts/view/5');
+
+		$result = $route->match(array('controller' => 'posts', 'action' => 'view', 'plugin' => null, 5, 'page' => 1, 'limit' => 20, 'order' => 'title'));
+		$this->assertEqual($result, '/posts/view/5/page:1/limit:20/order:title');
+
+
+		$route =& new CakeRoute('/test2/*', array('controller' => 'pages', 'action' => 'display', 2));
+		$result = $route->match(array('controller' => 'pages', 'action' => 'display', 1));
+		$this->assertFalse($result);
+
+		$result = $route->match(array('controller' => 'pages', 'action' => 'display', 2, 'something'));
+		$this->assertEqual($result, '/test2/something');
+
+		$result = $route->match(array('controller' => 'pages', 'action' => 'display', 5, 'something'));
+		$this->assertFalse($result);
+	}
+
+/**
+ * test that match with patterns works.
+ *
+ * @return void
+ */
+	function testMatchWithPatterns() {
+		$route =& new CakeRoute('/:controller/:action/:id', array('plugin' => null), array('id' => '[0-9]+'));
+		$result = $route->match(array('controller' => 'posts', 'action' => 'view', 'id' => 'foo'));
+		$this->assertFalse($result);
+
+		$result = $route->match(array('plugin' => null, 'controller' => 'posts', 'action' => 'view', 'id' => '9'));
+		$this->assertEqual($result, '/posts/view/9');
+
+		$result = $route->match(array('plugin' => null, 'controller' => 'posts', 'action' => 'view', 'id' => '922'));
+		$this->assertEqual($result, '/posts/view/922');
+
+		$result = $route->match(array('plugin' => null, 'controller' => 'posts', 'action' => 'view', 'id' => 'a99'));
+		$this->assertFalse($result);
+	}
+
+/**
+ * Test protocol relative urls.
+ *
+ * @return void
+ */
+	function testProtocolUrls() {
+		$url = 'svn+ssh://example.com';
+		$this->assertEqual($url, Router::url($url));
+
+		$url = '://example.com';
+		$this->assertEqual($url, Router::url($url));
+	}
+
+/**
+ * test that patterns work for :action
+ *
+ * @return void
+ */
+	function testPatternOnAction() {
+		$route =& new CakeRoute(
+			'/blog/:action/*',
+			array('controller' => 'blog_posts'),
+			array('action' => 'other|actions')
+		);
+		$result = $route->match(array('controller' => 'blog_posts', 'action' => 'foo'));
+		$this->assertFalse($result);
+
+		$result = $route->match(array('controller' => 'blog_posts', 'action' => 'actions'));
+		$this->assertTrue($result);
+
+		$result = $route->parse('/blog/other');
+		$expected = array('controller' => 'blog_posts', 'action' => 'other', 'pass' => array(), 'named' => array());
+		$this->assertEqual($expected, $result);
+
+		$result = $route->parse('/blog/foobar');
+		$this->assertFalse($result);
+	}
+
+/**
+ * test persistParams ability to persist parameters from $params and remove params.
+ *
+ * @return void
+ */
+	function testPersistParams() {
+		$route =& new CakeRoute(
+			'/:lang/:color/blog/:action',
+			array('controller' => 'posts'),
+			array('persist' => array('lang', 'color'))
+		);
+		$url = array('controller' => 'posts', 'action' => 'index');
+		$params = array('lang' => 'en', 'color' => 'blue');
+		$result = $route->persistParams($url, $params);
+		$this->assertEqual($result['lang'], 'en');
+		$this->assertEqual($result['color'], 'blue');
+
+		$url = array('controller' => 'posts', 'action' => 'index', 'color' => 'red');
+		$params = array('lang' => 'en', 'color' => 'blue');
+		$result = $route->persistParams($url, $params);
+		$this->assertEqual($result['lang'], 'en');
+		$this->assertEqual($result['color'], 'red');
+	}
+
+/**
+ * test the parse method of CakeRoute.
+ *
+ * @return void
+ */
+	function testParse() {
+		extract(Router::getNamedExpressions());
+		$route =& new CakeRoute('/:controller/:action/:id', array('controller' => 'testing4', 'id' => null), array('id' => $ID));
+		$route->compile();
+		$result = $route->parse('/posts/view/1');
+		$this->assertEqual($result['controller'], 'posts');
+		$this->assertEqual($result['action'], 'view');
+		$this->assertEqual($result['id'], '1');
+
+		$route =& new Cakeroute(
+			'/admin/:controller',
+			array('prefix' => 'admin', 'admin' => 1, 'action' => 'index')
+		);
+		$route->compile();
+		$result = $route->parse('/admin/');
+		$this->assertFalse($result);
+
+		$result = $route->parse('/admin/posts');
+		$this->assertEqual($result['controller'], 'posts');
+		$this->assertEqual($result['action'], 'index');
+	}
+}
+
+/**
+ * test case for PluginShortRoute
+ *
+ * @package cake.tests.libs
+ */
+class PluginShortRouteTestCase extends  CakeTestCase {
+/**
+ * startTest method
+ *
+ * @access public
+ * @return void
+ */
+	function startTest() {
+		$this->_routing = Configure::read('Routing');
+		Configure::write('Routing', array('admin' => null, 'prefixes' => array()));
+		Router::reload();
+	}
+
+/**
+ * end the test and reset the environment
+ *
+ * @return void
+ **/
+	function endTest() {
+		Configure::write('Routing', $this->_routing);
+	}
+
+/**
+ * test the parsing of routes.
+ *
+ * @return void
+ */
+	function testParsing() {
+		$route =& new PluginShortRoute('/:plugin', array('action' => 'index'), array('plugin' => 'foo|bar'));
+
+		$result = $route->parse('/foo');
+		$this->assertEqual($result['plugin'], 'foo');
+		$this->assertEqual($result['controller'], 'foo');
+		$this->assertEqual($result['action'], 'index');
+
+		$result = $route->parse('/wrong');
+		$this->assertFalse($result, 'Wrong plugin name matched %s');
+	}
+
+/**
+ * test the reverse routing of the plugin shortcut urls.
+ *
+ * @return void
+ */
+	function testMatch() {
+		$route =& new PluginShortRoute('/:plugin', array('action' => 'index'), array('plugin' => 'foo|bar'));
+
+		$result = $route->match(array('plugin' => 'foo', 'controller' => 'posts', 'action' => 'index'));
+		$this->assertFalse($result, 'plugin controller mismatch was converted. %s');
+
+		$result = $route->match(array('plugin' => 'foo', 'controller' => 'foo', 'action' => 'index'));
+		$this->assertEqual($result, '/foo');
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/sanitize.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/sanitize.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/sanitize.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,528 @@
+<?php
+/**
+ * SanitizeTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.5428
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', 'Sanitize');
+
+/**
+ * DataTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class SanitizeDataTest extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'SanitizeDataTest'
+ * @access public
+ */
+	var $name = 'SanitizeDataTest';
+
+/**
+ * useTable property
+ *
+ * @var string 'data_tests'
+ * @access public
+ */
+	var $useTable = 'data_tests';
+}
+
+/**
+ * Article class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class SanitizeArticle extends CakeTestModel {
+
+/**
+ * name property
+ *
+ * @var string 'Article'
+ * @access public
+ */
+	var $name = 'SanitizeArticle';
+
+/**
+ * useTable property
+ *
+ * @var string 'articles'
+ * @access public
+ */
+	var $useTable = 'articles';
+}
+
+/**
+ * SanitizeTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class SanitizeTest extends CakeTestCase {
+
+/**
+ * autoFixtures property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $autoFixtures = false;
+
+/**
+ * fixtures property
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array('core.data_test', 'core.article');
+
+/**
+ * startTest method
+ *
+ * @param mixed $method
+ * @access public
+ * @return void
+ */
+	function startTest($method) {
+		parent::startTest($method);
+		$this->_initDb();
+	}
+
+/**
+ * testEscapeAlphaNumeric method
+ *
+ * @access public
+ * @return void
+ */
+	function testEscapeAlphaNumeric() {
+		$resultAlpha = Sanitize::escape('abc', 'test_suite');
+		$this->assertEqual($resultAlpha, 'abc');
+
+		$resultNumeric = Sanitize::escape('123', 'test_suite');
+		$this->assertEqual($resultNumeric, '123');
+
+		$resultNumeric = Sanitize::escape(1234, 'test_suite');
+		$this->assertEqual($resultNumeric, 1234);
+
+		$resultNumeric = Sanitize::escape(1234.23, 'test_suite');
+		$this->assertEqual($resultNumeric, 1234.23);
+
+		$resultNumeric = Sanitize::escape('#1234.23', 'test_suite');
+		$this->assertEqual($resultNumeric, '#1234.23');
+
+		$resultNull = Sanitize::escape(null, 'test_suite');
+		$this->assertEqual($resultNull, null);
+
+		$resultNull = Sanitize::escape(false, 'test_suite');
+		$this->assertEqual($resultNull, false);
+
+		$resultNull = Sanitize::escape(true, 'test_suite');
+		$this->assertEqual($resultNull, true);
+	}
+
+/**
+ * testClean method
+ *
+ * @access public
+ * @return void
+ */
+	function testClean() {
+		$string = 'test & "quote" \'other\' ;.$ symbol.' . "\r" . 'another line';
+		$expected = 'test &amp; &quot;quote&quot; &#039;other&#039; ;.$ symbol.another line';
+		$result = Sanitize::clean($string, array('connection' => 'test_suite'));
+		$this->assertEqual($result, $expected);
+
+		$string = 'test & "quote" \'other\' ;.$ symbol.' . "\r" . 'another line';
+		$expected = 'test & ' . Sanitize::escape('"quote"', 'test_suite') . ' ' . Sanitize::escape('\'other\'', 'test_suite') . ' ;.$ symbol.another line';
+		$result = Sanitize::clean($string, array('encode' => false, 'connection' => 'test_suite'));
+		$this->assertEqual($result, $expected);
+
+		$string = 'test & "quote" \'other\' ;.$ \\$ symbol.' . "\r" . 'another line';
+		$expected = 'test & "quote" \'other\' ;.$ $ symbol.another line';
+		$result = Sanitize::clean($string, array('encode' => false, 'escape' => false, 'connection' => 'test_suite'));
+		$this->assertEqual($result, $expected);
+
+		$string = 'test & "quote" \'other\' ;.$ \\$ symbol.' . "\r" . 'another line';
+		$expected = 'test & "quote" \'other\' ;.$ \\$ symbol.another line';
+		$result = Sanitize::clean($string, array('encode' => false, 'escape' => false, 'dollar' => false, 'connection' => 'test_suite'));
+		$this->assertEqual($result, $expected);
+
+		$string = 'test & "quote" \'other\' ;.$ symbol.' . "\r" . 'another line';
+		$expected = 'test & "quote" \'other\' ;.$ symbol.' . "\r" . 'another line';
+		$result = Sanitize::clean($string, array('encode' => false, 'escape' => false, 'carriage' => false, 'connection' => 'test_suite'));
+		$this->assertEqual($result, $expected);
+
+		$array = array(array('test & "quote" \'other\' ;.$ symbol.' . "\r" . 'another line'));
+		$expected = array(array('test &amp; &quot;quote&quot; &#039;other&#039; ;.$ symbol.another line'));
+		$result = Sanitize::clean($array, array('connection' => 'test_suite'));
+		$this->assertEqual($result, $expected);
+
+		$array = array(array('test & "quote" \'other\' ;.$ \\$ symbol.' . "\r" . 'another line'));
+		$expected = array(array('test & "quote" \'other\' ;.$ $ symbol.another line'));
+		$result = Sanitize::clean($array, array('encode' => false, 'escape' => false, 'connection' => 'test_suite'));
+		$this->assertEqual($result, $expected);
+
+		$array = array(array('test odd Ä spacesé'));
+		$expected = array(array('test odd &Auml; spaces&eacute;'));
+		$result = Sanitize::clean($array, array('odd_spaces' => false, 'escape' => false, 'connection' => 'test_suite'));
+		$this->assertEqual($result, $expected);
+
+		$array = array(array('\\$', array('key' => 'test & "quote" \'other\' ;.$ \\$ symbol.' . "\r" . 'another line')));
+		$expected = array(array('$', array('key' => 'test & "quote" \'other\' ;.$ $ symbol.another line')));
+		$result = Sanitize::clean($array, array('encode' => false, 'escape' => false));
+		$this->assertEqual($result, $expected);
+
+		$string = '';
+		$expected = '';
+		$result = Sanitize::clean($string);
+		$this->assertEqual($string, $expected);
+
+		$data = array(
+			'Grant' => array(
+				'title' => '2 o clock grant',
+				'grant_peer_review_id' => 3,
+				'institution_id' => 5,
+				'created_by' => 1,
+				'modified_by' => 1,
+				'created' => '2010-07-15 14:11:00',
+				'modified' => '2010-07-19 10:45:41'
+			),
+			'GrantsMember' => array(
+				0 => array(
+					'id' => 68,
+					'grant_id' => 120,
+					'member_id' => 16,
+					'program_id' => 29,
+					'pi_percent_commitment' => 1
+				)
+			)
+		);
+		$result = Sanitize::clean($data);
+		$this->assertEqual($result, $data);
+	}
+
+/**
+ * testHtml method
+ *
+ * @access public
+ * @return void
+ */
+	function testHtml() {
+		$string = '<p>This is a <em>test string</em> & so is this</p>';
+		$expected = 'This is a test string &amp; so is this';
+		$result = Sanitize::html($string, array('remove' => true));
+		$this->assertEqual($result, $expected);
+
+		$string = 'The "lazy" dog \'jumped\' & flew over the moon. If (1+1) = 2 <em>is</em> true, (2-1) = 1 is also true';
+		$expected = 'The &quot;lazy&quot; dog &#039;jumped&#039; &amp; flew over the moon. If (1+1) = 2 &lt;em&gt;is&lt;/em&gt; true, (2-1) = 1 is also true';
+		$result = Sanitize::html($string);
+		$this->assertEqual($result, $expected);
+		
+		$string = 'The "lazy" dog \'jumped\'';
+		$expected = 'The &quot;lazy&quot; dog \'jumped\'';
+		$result = Sanitize::html($string, array('quotes' => ENT_COMPAT));
+		$this->assertEqual($result, $expected);
+		
+		$string = 'The "lazy" dog \'jumped\'';
+		$result = Sanitize::html($string, array('quotes' => ENT_NOQUOTES));
+		$this->assertEqual($result, $string);
+		
+		$string = 'The "lazy" dog \'jumped\' & flew over the moon. If (1+1) = 2 <em>is</em> true, (2-1) = 1 is also true';
+		$expected = 'The &quot;lazy&quot; dog &#039;jumped&#039; &amp; flew over the moon. If (1+1) = 2 &lt;em&gt;is&lt;/em&gt; true, (2-1) = 1 is also true';
+		$result = Sanitize::html($string);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testStripWhitespace method
+ *
+ * @access public
+ * @return void
+ */
+	function testStripWhitespace() {
+		$string = "This     sentence \t\t\t has lots of \n\n white\nspace \rthat \r\n needs to be    \t    \n trimmed.";
+		$expected = "This sentence has lots of whitespace that needs to be trimmed.";
+		$result = Sanitize::stripWhitespace($string);
+		$this->assertEqual($result, $expected);
+
+		$text = 'I    love  ßá†ö√    letters.';
+		$result = Sanitize::stripWhitespace($text);
+		$expected = 'I love ßá†ö√ letters.';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testParanoid method
+ *
+ * @access public
+ * @return void
+ */
+	function testParanoid() {
+		$string = 'I would like to !%@#% & dance & sing ^$&*()-+';
+		$expected = 'Iwouldliketodancesing';
+		$result = Sanitize::paranoid($string);
+		$this->assertEqual($result, $expected);
+
+		$string = array('This |s th% s0ng that never ends it g*es',
+						'on and on my friends, b^ca#use it is the',
+						'so&g th===t never ends.');
+		$expected = array('This s th% s0ng that never ends it g*es',
+						'on and on my friends bcause it is the',
+						'sog tht never ends.');
+		$result = Sanitize::paranoid($string, array('%', '*', '.', ' '));
+		$this->assertEqual($result, $expected);
+
+		$string = "anything' OR 1 = 1";
+		$expected = 'anythingOR11';
+		$result = Sanitize::paranoid($string);
+		$this->assertEqual($result, $expected);
+
+		$string = "x' AND email IS NULL; --";
+		$expected = 'xANDemailISNULL';
+		$result = Sanitize::paranoid($string);
+		$this->assertEqual($result, $expected);
+
+		$string = "x' AND 1=(SELECT COUNT(*) FROM users); --";
+		$expected = "xAND1SELECTCOUNTFROMusers";
+		$result = Sanitize::paranoid($string);
+		$this->assertEqual($result, $expected);
+
+		$string = "x'; DROP TABLE members; --";
+		$expected = "xDROPTABLEmembers";
+		$result = Sanitize::paranoid($string);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testStripImages method
+ *
+ * @access public
+ * @return void
+ */
+	function testStripImages() {
+		$string = '<img src="/img/test.jpg" alt="my image" />';
+		$expected = 'my image<br />';
+		$result = Sanitize::stripImages($string);
+		$this->assertEqual($result, $expected);
+
+		$string = '<img src="javascript:alert(\'XSS\');" />';
+		$expected = '';
+		$result = Sanitize::stripImages($string);
+		$this->assertEqual($result, $expected);
+
+		$string = '<a href="http://www.badsite.com/phising"><img src="/img/test.jpg" alt="test image alt" title="test image title" id="myImage" class="image-left"/></a>';
+		$expected = '<a href="http://www.badsite.com/phising">test image alt</a><br />';
+		$result = Sanitize::stripImages($string);
+		$this->assertEqual($result, $expected);
+
+		$string = '<a onclick="medium()" href="http://example.com"><img src="foobar.png" onclick="evilFunction(); return false;"/></a>';
+		$expected = '<a onclick="medium()" href="http://example.com"></a>';
+		$result = Sanitize::stripImages($string);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testStripScripts method
+ *
+ * @access public
+ * @return void
+ */
+	function testStripScripts() {
+		$string = '<link href="/css/styles.css" media="screen" rel="stylesheet" />';
+		$expected = '';
+		$result = Sanitize::stripScripts($string);
+		$this->assertEqual($result, $expected);
+
+		$string = '<link href="/css/styles.css" media="screen" rel="stylesheet" />' . "\n" . '<link rel="icon" href="/favicon.ico" type="image/x-icon" />' . "\n" . '<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />' . "\n" . '<link rel="alternate" href="/feed.xml" title="RSS Feed" type="application/rss+xml" />';
+		$expected = "\n" . '<link rel="icon" href="/favicon.ico" type="image/x-icon" />' . "\n" . '<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />'."\n".'<link rel="alternate" href="/feed.xml" title="RSS Feed" type="application/rss+xml" />';
+		$result = Sanitize::stripScripts($string);
+		$this->assertEqual($result, $expected);
+
+		$string = '<script type="text/javascript"> alert("hacked!");</script>';
+		$expected = '';
+		$result = Sanitize::stripScripts($string);
+		$this->assertEqual($result, $expected);
+
+		$string = '<script> alert("hacked!");</script>';
+		$expected = '';
+		$result = Sanitize::stripScripts($string);
+		$this->assertEqual($result, $expected);
+
+		$string = '<style>#content { display:none; }</style>';
+		$expected = '';
+		$result = Sanitize::stripScripts($string);
+		$this->assertEqual($result, $expected);
+
+		$string = '<style type="text/css"><!-- #content { display:none; } --></style>';
+		$expected = '';
+		$result = Sanitize::stripScripts($string);
+		$this->assertEqual($result, $expected);
+
+		$string = <<<HTML
+text
+<style type="text/css">
+<!-- 
+#content { display:none; } 
+-->
+</style>
+text
+HTML;
+		$expected = "text\n\ntext";
+		$result = Sanitize::stripScripts($string);
+		$this->assertEqual($result, $expected);
+
+		$string = <<<HTML
+text
+<script type="text/javascript">
+<!-- 
+alert('wooo');
+-->
+</script>
+text
+HTML;
+		$expected = "text\n\ntext";
+		$result = Sanitize::stripScripts($string);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testStripAll method
+ *
+ * @access public
+ * @return void
+ */
+	function testStripAll() {
+		$string = '<img """><script>alert("xss")</script>"/>';
+		$expected ='"/>';
+		$result = Sanitize::stripAll($string);
+		$this->assertEqual($result, $expected);
+
+		$string = '<IMG SRC=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>';
+		$expected = '';
+		$result = Sanitize::stripAll($string);
+		$this->assertEqual($result, $expected);
+
+		$string = '<<script>alert("XSS");//<</script>';
+		$expected = '<';
+		$result = Sanitize::stripAll($string);
+		$this->assertEqual($result, $expected);
+
+		$string = '<img src="http://google.com/images/logo.gif" onload="window.location=\'http://sam.com/\'" />'."\n".
+					"<p>This is ok      \t\n   text</p>\n".
+					'<link rel="stylesheet" href="/css/master.css" type="text/css" media="screen" title="my sheet" charset="utf-8">'."\n".
+					'<script src="xss.js" type="text/javascript" charset="utf-8"></script>';
+		$expected = '<p>This is ok text</p>';
+		$result = Sanitize::stripAll($string);
+		$this->assertEqual($result, $expected);
+
+	}
+
+/**
+ * testStripTags method
+ *
+ * @access public
+ * @return void
+ */
+	function testStripTags() {
+		$string = '<h2>Headline</h2><p><a href="http://example.com">My Link</a> could go to a bad site</p>';
+		$expected = 'Headline<p>My Link could go to a bad site</p>';
+		$result = Sanitize::stripTags($string, 'h2', 'a');
+		$this->assertEqual($result, $expected);
+
+		$string = '<script type="text/javascript" src="http://evildomain.com"> </script>';
+		$expected = ' ';
+		$result = Sanitize::stripTags($string, 'script');
+		$this->assertEqual($result, $expected);
+
+		$string = '<h2>Important</h2><p>Additional information here <a href="/about"><img src="/img/test.png" /></a>. Read even more here</p>';
+		$expected = 'Important<p>Additional information here <img src="/img/test.png" />. Read even more here</p>';
+		$result = Sanitize::stripTags($string, 'h2', 'a');
+		$this->assertEqual($result, $expected);
+
+		$string = '<h2>Important</h2><p>Additional information here <a href="/about"><img src="/img/test.png" /></a>. Read even more here</p>';
+		$expected = 'Important<p>Additional information here . Read even more here</p>';
+		$result = Sanitize::stripTags($string, 'h2', 'a', 'img');
+		$this->assertEqual($result, $expected);
+
+		$string = '<b>Important message!</b><br>This message will self destruct!';
+		$expected = 'Important message!<br>This message will self destruct!';
+		$result = Sanitize::stripTags($string, 'b');
+		$this->assertEqual($result, $expected);
+
+		$string = '<b>Important message!</b><br />This message will self destruct!';
+		$expected = 'Important message!<br />This message will self destruct!';
+		$result = Sanitize::stripTags($string, 'b');
+		$this->assertEqual($result, $expected);
+
+		$string = '<h2 onclick="alert(\'evil\'); onmouseover="badness()">Important</h2><p>Additional information here <a href="/about"><img src="/img/test.png" /></a>. Read even more here</p>';
+		$expected = 'Important<p>Additional information here . Read even more here</p>';
+		$result = Sanitize::stripTags($string, 'h2', 'a', 'img');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testFormatColumns method
+ *
+ * @access public
+ * @return void
+ */
+	function testFormatColumns() {
+		$this->loadFixtures('DataTest', 'Article');
+
+		$this->DataTest =& new SanitizeDataTest(array('alias' => 'DataTest'));
+		$data = array('DataTest' => array(
+						'id' => 'z',
+						'count' => '12a',
+						'float' => '2.31456',
+						'updated' => '2008-01-01'
+						)
+					);
+		$this->DataTest->set($data);
+		$expected = array('DataTest' => array(
+			'id' => '0',
+			'count' => '12',
+			'float' => 2.31456,
+			'updated' => '2008-01-01 00:00:00',
+		));
+		Sanitize::formatColumns($this->DataTest);
+		$result = $this->DataTest->data;
+		$this->assertEqual($result, $expected);
+
+		$this->Article =& new SanitizeArticle(array('alias' => 'Article'));
+		$data = array('Article' => array(
+			'id' => 'ZB',
+			'user_id' => '12',
+			'title' => 'title of article',
+			'body' => 'body text',
+			'published' => 'QQQQQQQ',
+		));
+		$this->Article->set($data);
+		$expected = array('Article' => array(
+			'id' => '0',
+			'user_id' => '12',
+			'title' => 'title of article',
+			'body' => 'body text',
+			'published' => 'QQQQQQQ',
+		));
+		Sanitize::formatColumns($this->Article);
+		$result = $this->Article->data;
+		$this->assertEqual($result, $expected);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/security.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/security.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/security.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,173 @@
+<?php
+/**
+ * SecurityTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.5432
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', 'Security');
+
+/**
+ * SecurityTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class SecurityTest extends CakeTestCase {
+
+/**
+ * sut property
+ *
+ * @var mixed null
+ * @access public
+ */
+	var $sut = null;
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		$this->sut =& Security::getInstance();
+	}
+
+/**
+ * testInactiveMins method
+ *
+ * @access public
+ * @return void
+ */
+	function testInactiveMins() {
+		Configure::write('Security.level', 'high');
+		$this->assertEqual(10, Security::inactiveMins());
+
+		Configure::write('Security.level', 'medium');
+		$this->assertEqual(100, Security::inactiveMins());
+
+		Configure::write('Security.level', 'low');
+		$this->assertEqual(300, Security::inactiveMins());
+	}
+
+/**
+ * testGenerateAuthkey method
+ *
+ * @access public
+ * @return void
+ */
+	function testGenerateAuthkey() {
+		$this->assertEqual(strlen(Security::generateAuthKey()), 40);
+	}
+
+/**
+ * testValidateAuthKey method
+ *
+ * @access public
+ * @return void
+ */
+	function testValidateAuthKey() {
+		$authKey = Security::generateAuthKey();
+		$this->assertTrue(Security::validateAuthKey($authKey));
+	}
+
+/**
+ * testHash method
+ *
+ * @access public
+ * @return void
+ */
+	function testHash() {
+		$Security =& Security::getInstance();
+		$_hashType =  $Security->hashType;
+
+		$key = 'someKey';
+		$hash = 'someHash';
+
+		$this->assertIdentical(strlen(Security::hash($key, null, false)), 40);
+		$this->assertIdentical(strlen(Security::hash($key, 'sha1', false)), 40);
+		$this->assertIdentical(strlen(Security::hash($key, null, true)), 40);
+		$this->assertIdentical(strlen(Security::hash($key, 'sha1', true)), 40);
+
+		$result = Security::hash($key, null, $hash);
+		$this->assertIdentical($result, 'e38fcb877dccb6a94729a81523851c931a46efb1');
+
+		$result = Security::hash($key, 'sha1', $hash);
+		$this->assertIdentical($result, 'e38fcb877dccb6a94729a81523851c931a46efb1');
+
+		$hashType = 'sha1';
+		Security::setHash($hashType);
+		$this->assertIdentical($this->sut->hashType, $hashType);
+		$this->assertIdentical(strlen(Security::hash($key, null, true)), 40);
+		$this->assertIdentical(strlen(Security::hash($key, null, false)), 40);
+
+		$this->assertIdentical(strlen(Security::hash($key, 'md5', false)), 32);
+		$this->assertIdentical(strlen(Security::hash($key, 'md5', true)), 32);
+
+		$hashType = 'md5';
+		Security::setHash($hashType);
+		$this->assertIdentical($this->sut->hashType, $hashType);
+		$this->assertIdentical(strlen(Security::hash($key, null, false)), 32);
+		$this->assertIdentical(strlen(Security::hash($key, null, true)), 32);
+
+		if (!function_exists('hash') && !function_exists('mhash')) {
+			$this->assertIdentical(strlen(Security::hash($key, 'sha256', false)), 32);
+			$this->assertIdentical(strlen(Security::hash($key, 'sha256', true)), 32);
+		} else {
+			$this->assertIdentical(strlen(Security::hash($key, 'sha256', false)), 64);
+			$this->assertIdentical(strlen(Security::hash($key, 'sha256', true)), 64);
+		}
+
+		Security::setHash($_hashType);
+	}
+
+/**
+ * testCipher method
+ *
+ * @access public
+ * @return void
+ */
+	function testCipher() {
+		$length = 10;
+		$txt = '';
+		for ($i = 0; $i < $length; $i++) {
+			$txt .= mt_rand(0, 255);
+		}
+		$key = 'my_key';
+		$result = Security::cipher($txt, $key);
+		$this->assertEqual(Security::cipher($result, $key), $txt);
+
+		$txt = '';
+		$key = 'my_key';
+		$result = Security::cipher($txt, $key);
+		$this->assertEqual(Security::cipher($result, $key), $txt);
+
+		$txt = 'some_text';
+		$key = '';
+		$result = Security::cipher($txt, $key);
+		$this->assertError();
+		$this->assertIdentical($result, '');
+
+		$txt = 123456;
+		$key = 'my_key';
+		$result = Security::cipher($txt, $key);
+		$this->assertEqual(Security::cipher($result, $key), $txt);
+
+		$txt = '123456';
+		$key = 'my_key';
+		$result = Security::cipher($txt, $key);
+		$this->assertEqual(Security::cipher($result, $key), $txt);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/set.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/set.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/set.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,3144 @@
+<?php
+/**
+ * SetTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', 'Set');
+
+/**
+ * SetTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class SetTest extends CakeTestCase {
+
+/**
+ * testNumericKeyExtraction method
+ *
+ * @access public
+ * @return void
+ */
+	function testNumericKeyExtraction() {
+		$data = array('plugin' => null, 'controller' => '', 'action' => '', 1, 'whatever');
+		$this->assertIdentical(Set::extract($data, '{n}'), array(1, 'whatever'));
+		$this->assertIdentical(Set::diff($data, Set::extract($data, '{n}')), array('plugin' => null, 'controller' => '', 'action' => ''));
+	}
+
+/**
+ * testEnum method
+ *
+ * @access public
+ * @return void
+ */
+	function testEnum() {
+		$result = Set::enum(1, 'one, two');
+		$this->assertIdentical($result, 'two');
+		$result = Set::enum(2, 'one, two');
+		$this->assertNull($result);
+
+		$set = array('one', 'two');
+		$result = Set::enum(0, $set);
+		$this->assertIdentical($result, 'one');
+		$result = Set::enum(1, $set);
+		$this->assertIdentical($result, 'two');
+
+		$result = Set::enum(1, array('one', 'two'));
+		$this->assertIdentical($result, 'two');
+		$result = Set::enum(2, array('one', 'two'));
+		$this->assertNull($result);
+
+		$result = Set::enum('first', array('first' => 'one', 'second' => 'two'));
+		$this->assertIdentical($result, 'one');
+		$result = Set::enum('third', array('first' => 'one', 'second' => 'two'));
+		$this->assertNull($result);
+
+		$result = Set::enum('no', array('no' => 0, 'yes' => 1));
+		$this->assertIdentical($result, 0);
+		$result = Set::enum('not sure', array('no' => 0, 'yes' => 1));
+		$this->assertNull($result);
+
+		$result = Set::enum(0);
+		$this->assertIdentical($result, 'no');
+		$result = Set::enum(1);
+		$this->assertIdentical($result, 'yes');
+		$result = Set::enum(2);
+		$this->assertNull($result);
+	}
+
+/**
+ * testFilter method
+ *
+ * @access public
+ * @return void
+ */
+	function testFilter() {
+		$result = Set::filter(array('0', false, true, 0, array('one thing', 'I can tell you', 'is you got to be', false)));
+		$expected = array('0', 2 => true, 3 => 0, 4 => array('one thing', 'I can tell you', 'is you got to be', false));
+		$this->assertIdentical($result, $expected);
+	}
+
+/**
+ * testNumericArrayCheck method
+ *
+ * @access public
+ * @return void
+ */
+	function testNumericArrayCheck() {
+		$data = array('one');
+		$this->assertTrue(Set::numeric(array_keys($data)));
+
+		$data = array(1 => 'one');
+		$this->assertFalse(Set::numeric($data));
+
+		$data = array('one');
+		$this->assertFalse(Set::numeric($data));
+
+		$data = array('one' => 'two');
+		$this->assertFalse(Set::numeric($data));
+
+		$data = array('one' => 1);
+		$this->assertTrue(Set::numeric($data));
+
+		$data = array(0);
+		$this->assertTrue(Set::numeric($data));
+
+		$data = array('one', 'two', 'three', 'four', 'five');
+		$this->assertTrue(Set::numeric(array_keys($data)));
+
+		$data = array(1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four', 5 => 'five');
+		$this->assertTrue(Set::numeric(array_keys($data)));
+
+		$data = array('1' => 'one', 2 => 'two', 3 => 'three', 4 => 'four', 5 => 'five');
+		$this->assertTrue(Set::numeric(array_keys($data)));
+
+		$data = array('one', 2 => 'two', 3 => 'three', 4 => 'four', 'a' => 'five');
+		$this->assertFalse(Set::numeric(array_keys($data)));
+	}
+
+/**
+ * testKeyCheck method
+ *
+ * @access public
+ * @return void
+ */
+	function testKeyCheck() {
+		$data = array('Multi' => array('dimensonal' => array('array')));
+		$this->assertTrue(Set::check($data, 'Multi.dimensonal'));
+		$this->assertFalse(Set::check($data, 'Multi.dimensonal.array'));
+
+		$data = array(
+			array(
+				'Article' => array('id' => '1', 'user_id' => '1', 'title' => 'First Article', 'body' => 'First Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'),
+				'User' => array('id' => '1', 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'),
+				'Comment' => array(
+					array('id' => '1', 'article_id' => '1', 'user_id' => '2', 'comment' => 'First Comment for First Article', 'published' => 'Y', 'created' => '2007-03-18 10:45:23', 'updated' => '2007-03-18 10:47:31'),
+					array('id' => '2', 'article_id' => '1', 'user_id' => '4', 'comment' => 'Second Comment for First Article', 'published' => 'Y', 'created' => '2007-03-18 10:47:23', 'updated' => '2007-03-18 10:49:31'),
+				),
+				'Tag' => array(
+					array('id' => '1', 'tag' => 'tag1', 'created' => '2007-03-18 12:22:23', 'updated' => '2007-03-18 12:24:31'),
+					array('id' => '2', 'tag' => 'tag2', 'created' => '2007-03-18 12:24:23', 'updated' => '2007-03-18 12:26:31')
+				)
+			),
+			array(
+				'Article' => array('id' => '3', 'user_id' => '1', 'title' => 'Third Article', 'body' => 'Third Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'),
+				'User' => array('id' => '1', 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'),
+				'Comment' => array(),
+				'Tag' => array()
+			)
+		);
+		$this->assertTrue(Set::check($data, '0.Article.user_id'));
+		$this->assertTrue(Set::check($data, '0.Comment.0.id'));
+		$this->assertFalse(Set::check($data, '0.Comment.0.id.0'));
+		$this->assertTrue(Set::check($data, '0.Article.user_id'));
+		$this->assertFalse(Set::check($data, '0.Article.user_id.a'));
+	}
+
+/**
+ * testMerge method
+ *
+ * @access public
+ * @return void
+ */
+	function testMerge() {
+		$r = Set::merge(array('foo'));
+		$this->assertIdentical($r, array('foo'));
+
+		$r = Set::merge('foo');
+		$this->assertIdentical($r, array('foo'));
+
+		$r = Set::merge('foo', 'bar');
+		$this->assertIdentical($r, array('foo', 'bar'));
+
+		if (substr(PHP_VERSION, 0, 1) >= 5) {
+			$r = eval('class StaticSetCaller{static function merge($a, $b){return Set::merge($a, $b);}} return StaticSetCaller::merge("foo", "bar");');
+			$this->assertIdentical($r, array('foo', 'bar'));
+		}
+
+		$r = Set::merge('foo', array('user' => 'bob', 'no-bar'), 'bar');
+		$this->assertIdentical($r, array('foo', 'user' => 'bob', 'no-bar', 'bar'));
+
+		$a = array('foo', 'foo2');
+		$b = array('bar', 'bar2');
+		$this->assertIdentical(Set::merge($a, $b), array('foo', 'foo2', 'bar', 'bar2'));
+
+		$a = array('foo' => 'bar', 'bar' => 'foo');
+		$b = array('foo' => 'no-bar', 'bar' => 'no-foo');
+		$this->assertIdentical(Set::merge($a, $b), array('foo' => 'no-bar', 'bar' => 'no-foo'));
+
+		$a = array('users' => array('bob', 'jim'));
+		$b = array('users' => array('lisa', 'tina'));
+		$this->assertIdentical(Set::merge($a, $b), array('users' => array('bob', 'jim', 'lisa', 'tina')));
+
+		$a = array('users' => array('jim', 'bob'));
+		$b = array('users' => 'none');
+		$this->assertIdentical(Set::merge($a, $b), array('users' => 'none'));
+
+		$a = array('users' => array('lisa' => array('id' => 5, 'pw' => 'secret')), 'cakephp');
+		$b = array('users' => array('lisa' => array('pw' => 'new-pass', 'age' => 23)), 'ice-cream');
+		$this->assertIdentical(Set::merge($a, $b), array('users' => array('lisa' => array('id' => 5, 'pw' => 'new-pass', 'age' => 23)), 'cakephp', 'ice-cream'));
+
+		$c = array('users' => array('lisa' => array('pw' => 'you-will-never-guess', 'age' => 25, 'pet' => 'dog')), 'chocolate');
+		$expected = array('users' => array('lisa' => array('id' => 5, 'pw' => 'you-will-never-guess', 'age' => 25, 'pet' => 'dog')), 'cakephp', 'ice-cream', 'chocolate');
+		$this->assertIdentical(Set::merge($a, $b, $c), $expected);
+
+		$this->assertIdentical(Set::merge($a, $b, array(), $c), $expected);
+
+		$r = Set::merge($a, $b, $c);
+		$this->assertIdentical($r, $expected);
+
+		$a = array('Tree', 'CounterCache',
+				'Upload' => array('folder' => 'products',
+					'fields' => array('image_1_id', 'image_2_id', 'image_3_id', 'image_4_id', 'image_5_id')));
+		$b =  array('Cacheable' => array('enabled' => false),
+				'Limit',
+				'Bindable',
+				'Validator',
+				'Transactional');
+
+		$expected = array('Tree', 'CounterCache',
+				'Upload' => array('folder' => 'products',
+					'fields' => array('image_1_id', 'image_2_id', 'image_3_id', 'image_4_id', 'image_5_id')),
+				'Cacheable' => array('enabled' => false),
+				'Limit',
+				'Bindable',
+				'Validator',
+				'Transactional');
+
+		$this->assertIdentical(Set::merge($a, $b), $expected);
+
+		$expected = array('Tree' => null, 'CounterCache' => null,
+				'Upload' => array('folder' => 'products',
+					'fields' => array('image_1_id', 'image_2_id', 'image_3_id', 'image_4_id', 'image_5_id')),
+				'Cacheable' => array('enabled' => false),
+				'Limit' => null,
+				'Bindable' => null,
+				'Validator' => null,
+				'Transactional' => null);
+
+		$this->assertIdentical(Set::normalize(Set::merge($a, $b)), $expected);
+	}
+
+/**
+ * testSort method
+ *
+ * @access public
+ * @return void
+ */
+	function testSort() {
+		$a = array(
+			0 => array('Person' => array('name' => 'Jeff'), 'Friend' => array(array('name' => 'Nate'))),
+			1 => array('Person' => array('name' => 'Tracy'),'Friend' => array(array('name' => 'Lindsay')))
+		);
+		$b = array(
+			0 => array('Person' => array('name' => 'Tracy'),'Friend' => array(array('name' => 'Lindsay'))),
+			1 => array('Person' => array('name' => 'Jeff'), 'Friend' => array(array('name' => 'Nate')))
+
+		);
+		$a = Set::sort($a, '{n}.Friend.{n}.name', 'asc');
+		$this->assertIdentical($a, $b);
+
+		$b = array(
+			0 => array('Person' => array('name' => 'Jeff'), 'Friend' => array(array('name' => 'Nate'))),
+			1 => array('Person' => array('name' => 'Tracy'),'Friend' => array(array('name' => 'Lindsay')))
+		);
+		$a = array(
+			0 => array('Person' => array('name' => 'Tracy'),'Friend' => array(array('name' => 'Lindsay'))),
+			1 => array('Person' => array('name' => 'Jeff'), 'Friend' => array(array('name' => 'Nate')))
+
+		);
+		$a = Set::sort($a, '{n}.Friend.{n}.name', 'desc');
+		$this->assertIdentical($a, $b);
+
+		$a = array(
+			0 => array('Person' => array('name' => 'Jeff'), 'Friend' => array(array('name' => 'Nate'))),
+			1 => array('Person' => array('name' => 'Tracy'),'Friend' => array(array('name' => 'Lindsay'))),
+			2 => array('Person' => array('name' => 'Adam'),'Friend' => array(array('name' => 'Bob')))
+		);
+		$b = array(
+			0 => array('Person' => array('name' => 'Adam'),'Friend' => array(array('name' => 'Bob'))),
+			1 => array('Person' => array('name' => 'Jeff'), 'Friend' => array(array('name' => 'Nate'))),
+			2 => array('Person' => array('name' => 'Tracy'),'Friend' => array(array('name' => 'Lindsay')))
+		);
+		$a = Set::sort($a, '{n}.Person.name', 'asc');
+		$this->assertIdentical($a, $b);
+
+		$a = array(
+			array(7,6,4),
+			array(3,4,5),
+			array(3,2,1),
+		);
+
+		$b = array(
+			array(3,2,1),
+			array(3,4,5),
+			array(7,6,4),
+		);
+
+		$a = Set::sort($a, '{n}.{n}', 'asc');
+		$this->assertIdentical($a, $b);
+
+		$a = array(
+			array(7,6,4),
+			array(3,4,5),
+			array(3,2,array(1,1,1)),
+		);
+
+		$b = array(
+			array(3,2,array(1,1,1)),
+			array(3,4,5),
+			array(7,6,4),
+		);
+
+		$a = Set::sort($a, '{n}', 'asc');
+		$this->assertIdentical($a, $b);
+
+		$a = array(
+			0 => array('Person' => array('name' => 'Jeff')),
+			1 => array('Shirt' => array('color' => 'black'))
+		);
+		$b = array(
+			0 => array('Shirt' => array('color' => 'black')),
+			1 => array('Person' => array('name' => 'Jeff')),
+		);
+		$a = Set::sort($a, '{n}.Person.name', 'ASC');
+		$this->assertIdentical($a, $b);
+
+		$names = array(
+			array('employees' => array(array('name' => array('first' => 'John', 'last' => 'Doe')))),
+			array('employees' => array(array('name' => array('first' => 'Jane', 'last' => 'Doe')))),
+			array('employees' => array(array('name' => array()))),
+			array('employees' => array(array('name' => array())))
+		);
+		$result = Set::sort($names, '{n}.employees.0.name', 'asc', 1);
+		$expected = array(
+			array('employees' => array(array('name' => array('first' => 'John', 'last' => 'Doe')))),
+			array('employees' => array(array('name' => array('first' => 'Jane', 'last' => 'Doe')))),
+			array('employees' => array(array('name' => array()))),
+			array('employees' => array(array('name' => array())))
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test sorting with out of order keys.
+ *
+ * @return void
+ */
+	function testSortWithOutOfOrderKeys() {
+		$data = array(
+			9 => array('class' => 510, 'test2' => 2),
+			1 => array('class' => 500, 'test2' => 1),
+			2 => array('class' => 600, 'test2' => 2),
+			5 => array('class' => 625, 'test2' => 4),
+			0 => array('class' => 605, 'test2' => 3),
+		);
+		$expected = array(
+			array('class' => 500, 'test2' => 1),
+			array('class' => 510, 'test2' => 2),
+			array('class' => 600, 'test2' => 2),
+			array('class' => 605, 'test2' => 3),
+			array('class' => 625, 'test2' => 4),
+		);
+		$result = Set::sort($data, '{n}.class', 'asc');
+		$this->assertEqual($expected, $result);
+
+		$result = Set::sort($data, '{n}.test2', 'asc');
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * testExtract method
+ *
+ * @access public
+ * @return void
+ */
+	function testExtract() {
+		$a = array(
+			array(
+				'Article' => array('id' => '1', 'user_id' => '1', 'title' => 'First Article', 'body' => 'First Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'),
+				'User' => array('id' => '1', 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'),
+				'Comment' => array(
+					array('id' => '1', 'article_id' => '1', 'user_id' => '2', 'comment' => 'First Comment for First Article', 'published' => 'Y', 'created' => '2007-03-18 10:45:23', 'updated' => '2007-03-18 10:47:31'),
+					array('id' => '2', 'article_id' => '1', 'user_id' => '4', 'comment' => 'Second Comment for First Article', 'published' => 'Y', 'created' => '2007-03-18 10:47:23', 'updated' => '2007-03-18 10:49:31'),
+				),
+				'Tag' => array(
+					array('id' => '1', 'tag' => 'tag1', 'created' => '2007-03-18 12:22:23', 'updated' => '2007-03-18 12:24:31'),
+					array('id' => '2', 'tag' => 'tag2', 'created' => '2007-03-18 12:24:23', 'updated' => '2007-03-18 12:26:31')
+				),
+				'Deep' => array(
+					'Nesting' => array(
+						'test' => array(
+							1 => 'foo',
+							2 => array(
+								'and' => array('more' => 'stuff')
+							)
+						)
+					)
+				)
+			),
+			array(
+				'Article' => array('id' => '3', 'user_id' => '1', 'title' => 'Third Article', 'body' => 'Third Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'),
+				'User' => array('id' => '2', 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'),
+				'Comment' => array(),
+				'Tag' => array()
+			),
+			array(
+				'Article' => array('id' => '3', 'user_id' => '1', 'title' => 'Third Article', 'body' => 'Third Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'),
+				'User' => array('id' => '3', 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'),
+				'Comment' => array(),
+				'Tag' => array()
+			),
+			array(
+				'Article' => array('id' => '3', 'user_id' => '1', 'title' => 'Third Article', 'body' => 'Third Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'),
+				'User' => array('id' => '4', 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'),
+				'Comment' => array(),
+				'Tag' => array()
+			),
+			array(
+				'Article' => array('id' => '3', 'user_id' => '1', 'title' => 'Third Article', 'body' => 'Third Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31'),
+				'User' => array('id' => '5', 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'),
+				'Comment' => array(),
+				'Tag' => array()
+			)
+		);
+		$b = array('Deep' => $a[0]['Deep']);
+		$c = array(
+			array('a' => array('I' => array('a' => 1))),
+			array(
+				'a' => array(
+					2
+				)
+			),
+			array('a' => array('II' => array('a' => 3, 'III' => array('a' => array('foo' => 4))))),
+		);
+
+		$expected = array(array('a' => $c[2]['a']));
+		$r = Set::extract('/a/II[a=3]/..', $c);
+		$this->assertEqual($r, $expected);
+
+		$expected = array(1, 2, 3, 4, 5);
+		$this->assertEqual(Set::extract('/User/id', $a), $expected);
+
+		$expected = array(1, 2, 3, 4, 5);
+		$this->assertEqual(Set::extract('/User/id', $a), $expected);
+
+		$expected = array(
+			array('id' => 1), array('id' => 2), array('id' => 3), array('id' => 4), array('id' => 5)
+		);
+
+		$r = Set::extract('/User/id', $a, array('flatten' => false));
+		$this->assertEqual($r, $expected);
+
+		$expected = array(array('test' => $a[0]['Deep']['Nesting']['test']));
+		$this->assertEqual(Set::extract('/Deep/Nesting/test', $a), $expected);
+		$this->assertEqual(Set::extract('/Deep/Nesting/test', $b), $expected);
+
+		$expected = array(array('test' => $a[0]['Deep']['Nesting']['test']));
+		$r = Set::extract('/Deep/Nesting/test/1/..', $a);
+		$this->assertEqual($r, $expected);
+
+		$expected = array(array('test' => $a[0]['Deep']['Nesting']['test']));
+		$r = Set::extract('/Deep/Nesting/test/2/and/../..', $a);
+		$this->assertEqual($r, $expected);
+
+		$expected = array(array('test' => $a[0]['Deep']['Nesting']['test']));
+		$r = Set::extract('/Deep/Nesting/test/2/../../../Nesting/test/2/..', $a);
+		$this->assertEqual($r, $expected);
+
+		$expected = array(2);
+		$r = Set::extract('/User[2]/id', $a);
+		$this->assertEqual($r, $expected);
+
+		$expected = array(4, 5);
+		$r = Set::extract('/User[id>3]/id', $a);
+		$this->assertEqual($r, $expected);
+
+		$expected = array(2, 3);
+		$r = Set::extract('/User[id>1][id<=3]/id', $a);
+		$this->assertEqual($r, $expected);
+
+		$expected = array(array('I'), array('II'));
+		$r = Set::extract('/a/@*', $c);
+		$this->assertEqual($r, $expected);
+
+		$single = array(
+			'User' => array(
+				'id' => 4,
+				'name' => 'Neo',
+			)
+		);
+		$tricky = array(
+			0 => array(
+				'User' => array(
+					'id' => 1,
+					'name' => 'John',
+				)
+			),
+			1 => array(
+				'User' => array(
+					'id' => 2,
+					'name' => 'Bob',
+				)
+			),
+			2 => array(
+				'User' => array(
+					'id' => 3,
+					'name' => 'Tony',
+				)
+			),
+			'User' => array(
+				'id' => 4,
+				'name' => 'Neo',
+			)
+		);
+
+		$expected = array(1, 2, 3, 4);
+		$r = Set::extract('/User/id', $tricky);
+		$this->assertEqual($r, $expected);
+
+		$expected = array(4);
+		$r = Set::extract('/User/id', $single);
+		$this->assertEqual($r, $expected);
+
+		$expected = array(1, 3);
+		$r = Set::extract('/User[name=/n/]/id', $tricky);
+		$this->assertEqual($r, $expected);
+
+		$expected = array(4);
+		$r = Set::extract('/User[name=/N/]/id', $tricky);
+		$this->assertEqual($r, $expected);
+
+		$expected = array(1, 3, 4);
+		$r = Set::extract('/User[name=/N/i]/id', $tricky);
+		$this->assertEqual($r, $expected);
+
+		$expected = array(array('id', 'name'), array('id', 'name'), array('id', 'name'), array('id', 'name'));
+		$r = Set::extract('/User/@*', $tricky);
+		$this->assertEqual($r, $expected);
+
+		$common = array(
+			array(
+				'Article' => array(
+					'id' => 1,
+					'name' => 'Article 1',
+				),
+				'Comment' => array(
+					array(
+						'id' => 1,
+						'user_id' => 5,
+						'article_id' => 1,
+						'text' => 'Comment 1',
+					),
+					array(
+						'id' => 2,
+						'user_id' => 23,
+						'article_id' => 1,
+						'text' => 'Comment 2',
+					),
+					array(
+						'id' => 3,
+						'user_id' => 17,
+						'article_id' => 1,
+						'text' => 'Comment 3',
+					),
+				),
+			),
+			array(
+				'Article' => array(
+					'id' => 2,
+					'name' => 'Article 2',
+				),
+				'Comment' => array(
+					array(
+						'id' => 4,
+						'user_id' => 2,
+						'article_id' => 2,
+						'text' => 'Comment 4',
+						'addition' => '',
+					),
+					array(
+						'id' => 5,
+						'user_id' => 23,
+						'article_id' => 2,
+						'text' => 'Comment 5',
+						'addition' => 'foo',
+					),
+				),
+			),
+			array(
+				'Article' => array(
+					'id' => 3,
+					'name' => 'Article 3',
+				),
+				'Comment' => array(),
+			)
+		);
+
+		$r = Set::extract('/Comment/id', $common);
+		$expected = array(1, 2, 3, 4, 5);
+		$this->assertEqual($r, $expected);
+
+		$expected = array(1, 2, 4, 5);
+		$r = Set::extract('/Comment[id!=3]/id', $common);
+		$this->assertEqual($r, $expected);
+
+		$r = Set::extract('/', $common);
+		$this->assertEqual($r, $common);
+
+		$expected = array(1, 2, 4, 5);
+		$r = Set::extract($common, '/Comment[id!=3]/id');
+		$this->assertEqual($r, $expected);
+
+		$expected = array($common[0]['Comment'][2]);
+		$r = Set::extract($common, '/Comment/2');
+		$this->assertEqual($r, $expected);
+
+		$expected = array($common[0]['Comment'][0]);
+		$r = Set::extract($common, '/Comment[1]/.[id=1]');
+		$this->assertEqual($r, $expected);
+
+		$expected = array($common[1]['Comment'][1]);
+		$r = Set::extract($common, '/1/Comment/.[2]');
+		$this->assertEqual($r, $expected);
+
+		$expected = array();
+		$r = Set::extract('/User/id', array());
+		$this->assertEqual($r, $expected);
+
+		$expected = array(5);
+		$r = Set::extract('/Comment/id[:last]', $common);
+		$this->assertEqual($r, $expected);
+
+		$expected = array(1);
+		$r = Set::extract('/Comment/id[:first]', $common);
+		$this->assertEqual($r, $expected);
+
+		$expected = array(3);
+		$r = Set::extract('/Article[:last]/id', $common);
+		$this->assertEqual($r, $expected);
+
+		$expected = array(array('Comment' => $common[1]['Comment'][0]));
+		$r = Set::extract('/Comment[addition=]', $common);
+		$this->assertEqual($r, $expected);
+
+		$habtm = array(
+			array(
+				'Post' => array(
+					'id' => 1,
+					'title' => 'great post',
+				),
+				'Comment' => array(
+					array(
+						'id' => 1,
+						'text' => 'foo',
+						'User' => array(
+							'id' => 1,
+							'name' => 'bob'
+						),
+					),
+					array(
+						'id' => 2,
+						'text' => 'bar',
+						'User' => array(
+							'id' => 2,
+							'name' => 'tod'
+						),
+					),
+				),
+			),
+			array(
+				'Post' => array(
+					'id' => 2,
+					'title' => 'fun post',
+				),
+				'Comment' => array(
+					array(
+						'id' => 3,
+						'text' => '123',
+						'User' => array(
+							'id' => 3,
+							'name' => 'dan'
+						),
+					),
+					array(
+						'id' => 4,
+						'text' => '987',
+						'User' => array(
+							'id' => 4,
+							'name' => 'jim'
+						),
+					),
+				),
+			),
+		);
+
+		$r = Set::extract('/Comment/User[name=/bob|dan/]/..', $habtm);
+		$this->assertEqual($r[0]['Comment']['User']['name'], 'bob');
+		$this->assertEqual($r[1]['Comment']['User']['name'], 'dan');
+		$this->assertEqual(count($r), 2);
+
+		$r = Set::extract('/Comment/User[name=/bob|tod/]/..', $habtm);
+		$this->assertEqual($r[0]['Comment']['User']['name'], 'bob');
+
+		$this->assertEqual($r[1]['Comment']['User']['name'], 'tod');
+		$this->assertEqual(count($r), 2);
+
+		$tree = array(
+			array(
+				'Category' => array('name' => 'Category 1'),
+				'children' => array(array('Category' => array('name' => 'Category 1.1')))
+			),
+			array(
+				'Category' => array('name' => 'Category 2'),
+				'children' => array(
+					array('Category' => array('name' => 'Category 2.1')),
+					array('Category' => array('name' => 'Category 2.2'))
+				)
+			),
+			array(
+				'Category' => array('name' => 'Category 3'),
+				'children' => array(array('Category' => array('name' => 'Category 3.1')))
+			)
+		);
+
+		$expected = array(array('Category' => $tree[1]['Category']));
+		$r = Set::extract('/Category[name=Category 2]', $tree);
+		$this->assertEqual($r, $expected);
+
+		$expected = array(
+			array('Category' => $tree[1]['Category'], 'children' => $tree[1]['children'])
+		);
+		$r = Set::extract('/Category[name=Category 2]/..', $tree);
+		$this->assertEqual($r, $expected);
+
+		$expected = array(
+			array('children' => $tree[1]['children'][0]),
+			array('children' => $tree[1]['children'][1])
+		);
+		$r = Set::extract('/Category[name=Category 2]/../children', $tree);
+		$this->assertEqual($r, $expected);
+
+		$habtm = array(
+			array(
+				'Post' => array(
+					'id' => 1,
+					'title' => 'great post',
+				),
+				'Comment' => array(
+					array(
+						'id' => 1,
+						'text' => 'foo',
+						'User' => array(
+							'id' => 1,
+							'name' => 'bob'
+						),
+					),
+					array(
+						'id' => 2,
+						'text' => 'bar',
+						'User' => array(
+							'id' => 2,
+							'name' => 'tod'
+						),
+					),
+				),
+			),
+			array(
+				'Post' => array(
+					'id' => 2,
+					'title' => 'fun post',
+				),
+				'Comment' => array(
+					array(
+						'id' => 3,
+						'text' => '123',
+						'User' => array(
+							'id' => 3,
+							'name' => 'dan'
+						),
+					),
+					array(
+						'id' => 4,
+						'text' => '987',
+						'User' => array(
+							'id' => 4,
+							'name' => 'jim'
+						),
+					),
+				),
+			),
+		);
+
+		$r = Set::extract('/Comment/User[name=/\w+/]/..', $habtm);
+		$this->assertEqual($r[0]['Comment']['User']['name'], 'bob');
+		$this->assertEqual($r[1]['Comment']['User']['name'], 'tod');
+		$this->assertEqual($r[2]['Comment']['User']['name'], 'dan');
+		$this->assertEqual($r[3]['Comment']['User']['name'], 'dan');
+		$this->assertEqual(count($r), 4);
+
+		$r = Set::extract('/Comment/User[name=/[a-z]+/]/..', $habtm);
+		$this->assertEqual($r[0]['Comment']['User']['name'], 'bob');
+		$this->assertEqual($r[1]['Comment']['User']['name'], 'tod');
+		$this->assertEqual($r[2]['Comment']['User']['name'], 'dan');
+		$this->assertEqual($r[3]['Comment']['User']['name'], 'dan');
+		$this->assertEqual(count($r), 4);
+
+		$r = Set::extract('/Comment/User[name=/bob|dan/]/..', $habtm);
+		$this->assertEqual($r[0]['Comment']['User']['name'], 'bob');
+		$this->assertEqual($r[1]['Comment']['User']['name'], 'dan');
+		$this->assertEqual(count($r), 2);
+
+		$r = Set::extract('/Comment/User[name=/bob|tod/]/..', $habtm);
+		$this->assertEqual($r[0]['Comment']['User']['name'], 'bob');
+		$this->assertEqual($r[1]['Comment']['User']['name'], 'tod');
+		$this->assertEqual(count($r), 2);
+
+		$mixedKeys = array(
+			'User' => array(
+				0 => array(
+					'id' => 4,
+					'name' => 'Neo'
+				),
+				1 => array(
+					'id' => 5,
+					'name' => 'Morpheus'
+				),
+				'stringKey' => array()
+			)
+		);
+		$expected = array('Neo', 'Morpheus');
+		$r = Set::extract('/User/name', $mixedKeys);
+		$this->assertEqual($r, $expected);
+
+		$f = array(
+			array(
+				'file' => array(
+					'name' => 'zipfile.zip',
+					'type' => 'application/zip',
+					'tmp_name' => '/tmp/php178.tmp',
+					'error' => 0,
+					'size' => '564647'
+				)
+			),
+			array(
+				'file' => array(
+					'name' => 'zipfile2.zip',
+					'type' => 'application/x-zip-compressed',
+					'tmp_name' => '/tmp/php179.tmp',
+					'error' => 0,
+					'size' => '354784'
+				)
+			),
+			array(
+				'file' => array(
+					'name' => 'picture.jpg',
+					'type' => 'image/jpeg',
+					'tmp_name' => '/tmp/php180.tmp',
+					'error' => 0,
+					'size' => '21324'
+				)
+			)
+		);
+		$expected = array(array('name' => 'zipfile2.zip','type' => 'application/x-zip-compressed','tmp_name' => '/tmp/php179.tmp','error' => 0,'size' => '354784'));
+		$r = Set::extract('/file/.[type=application/x-zip-compressed]', $f);
+		$this->assertEqual($r, $expected);
+
+		$expected = array(array('name' => 'zipfile.zip','type' => 'application/zip','tmp_name' => '/tmp/php178.tmp','error' => 0,'size' => '564647'));
+		$r = Set::extract('/file/.[type=application/zip]', $f);
+		$this->assertEqual($r, $expected);
+
+		$f = array(
+			array(
+				'file' => array(
+					'name' => 'zipfile.zip',
+					'type' => 'application/zip',
+					'tmp_name' => '/tmp/php178.tmp',
+					'error' => 0,
+					'size' => '564647'
+				)
+			),
+			array(
+				'file' => array(
+					'name' => 'zipfile2.zip',
+					'type' => 'application/x zip compressed',
+					'tmp_name' => '/tmp/php179.tmp',
+					'error' => 0,
+					'size' => '354784'
+				)
+			),
+			array(
+				'file' => array(
+					'name' => 'picture.jpg',
+					'type' => 'image/jpeg',
+					'tmp_name' => '/tmp/php180.tmp',
+					'error' => 0,
+					'size' => '21324'
+				)
+			)
+		);
+		$expected = array(array('name' => 'zipfile2.zip','type' => 'application/x zip compressed','tmp_name' => '/tmp/php179.tmp','error' => 0,'size' => '354784'));
+		$r = Set::extract('/file/.[type=application/x zip compressed]', $f);
+		$this->assertEqual($r, $expected);
+
+		$expected = array(
+			array('name' => 'zipfile.zip','type' => 'application/zip','tmp_name' => '/tmp/php178.tmp','error' => 0,'size' => '564647'),
+			array('name' => 'zipfile2.zip','type' => 'application/x zip compressed','tmp_name' => '/tmp/php179.tmp','error' => 0,'size' => '354784')
+		);
+		$r = Set::extract('/file/.[tmp_name=/tmp\/php17/]', $f);
+		$this->assertEqual($r, $expected);
+
+		$hasMany = array(
+			'Node' => array(
+				'id' => 1,
+				'name' => 'First',
+				'state' => 50
+			),
+			'ParentNode' => array(
+				0 => array(
+					'id' => 2,
+					'name' => 'Second',
+					'state' => 60,
+				)
+			)
+		);
+		$result = Set::extract('/ParentNode/name', $hasMany);
+		$expected = array('Second');
+		$this->assertEqual($result, $expected);
+
+		$data = array(
+			array(
+				'Category' => array(
+					'id' => 1,
+					'name' => 'First'
+				),
+				0 => array(
+					'value' => 50
+				)
+			),
+			array(
+				'Category' => array(
+					'id' => 2,
+					'name' => 'Second'
+				),
+				0 => array(
+					'value' => 60
+				)
+			)
+		);
+		$expected = array(
+			array(
+				'Category' => array(
+					'id' => 1,
+					'name' => 'First'
+				),
+				0 => array(
+					'value' => 50
+				)
+			)
+		);
+		$result = Set::extract('/Category[id=1]/..', $data);
+		$this->assertEqual($result, $expected);
+
+		$data = array(
+			array(
+				'ChildNode' => array('id' => 1),
+				array('name' => 'Item 1')
+			),
+			array(
+				'ChildNode' => array('id' => 2),
+				array('name' => 'Item 2')
+			),
+		);
+
+		$expected = array(
+			'Item 1',
+			'Item 2'
+		);
+		$result = Set::extract('/0/name', $data);
+		$this->assertEqual($result, $expected);
+
+		$data = array(
+			array('A1', 'B1'),
+			array('A2', 'B2')
+		);
+		$expected = array('A1', 'A2');
+		$result =  Set::extract('/0', $data);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test parent selectors with extract
+ *
+ * @return void
+ */
+	function testExtractParentSelector() {
+		$tree = array(
+			array(
+				'Category' => array(
+					'name' => 'Category 1'
+				),
+				'children' => array(
+					array(
+						'Category' => array(
+							'name' => 'Category 1.1'
+						)
+					)
+				)
+			),
+			array(
+				'Category' => array(
+					'name' => 'Category 2'
+				),
+				'children' => array(
+					array(
+						'Category' => array(
+							'name' => 'Category 2.1'
+						)
+					),
+					array(
+						'Category' => array(
+							'name' => 'Category 2.2'
+						)
+					),
+				)
+			),
+			array(
+				'Category' => array(
+					'name' => 'Category 3'
+				),
+				'children' => array(
+					array(
+						'Category' => array(
+							'name' => 'Category 3.1'
+						)
+					)
+				)
+			)
+		);
+		$expected = array(array('Category' => $tree[1]['Category']));
+		$r = Set::extract('/Category[name=Category 2]', $tree);
+		$this->assertEqual($r, $expected);
+
+		$expected = array(array('Category' => $tree[1]['Category'], 'children' => $tree[1]['children']));
+		$r = Set::extract('/Category[name=Category 2]/..', $tree);
+		$this->assertEqual($r, $expected);
+
+		$expected = array(array('children' => $tree[1]['children'][0]), array('children' => $tree[1]['children'][1]));
+		$r = Set::extract('/Category[name=Category 2]/../children', $tree);
+		$this->assertEqual($r, $expected);
+
+		$single = array(
+			array(
+				'CallType' => array(
+					'name' => 'Internal Voice'
+				),
+				'x' => array(
+					'hour' => 7
+				)
+			)
+		);
+
+		$expected = array(7);
+		$r = Set::extract('/CallType[name=Internal Voice]/../x/hour', $single);
+		$this->assertEqual($r, $expected);
+
+		$multiple = array(
+			array(
+				'CallType' => array(
+					'name' => 'Internal Voice'
+				),
+				'x' => array(
+					'hour' => 7
+				)
+			),
+			array(
+				'CallType' => array(
+					'name' => 'Internal Voice'
+				),
+				'x' => array(
+					'hour' => 2
+				)
+			),
+			array(
+				'CallType' => array(
+					'name' => 'Internal Voice'
+				),
+				'x' => array(
+					'hour' => 1
+				)
+			)
+		);
+
+		$expected = array(7,2,1);
+		$r = Set::extract('/CallType[name=Internal Voice]/../x/hour', $multiple);
+		$this->assertEqual($r, $expected);
+
+		$a = array(
+			'Model' => array(
+				'0' => array(
+					'id' => 18,
+					'SubModelsModel' => array(
+						'id' => 1,
+						'submodel_id' => 66,
+						'model_id' => 18,
+						'type' => 1
+					),
+				),
+				'1' => array(
+					'id' => 0,
+					'SubModelsModel' => array(
+						'id' => 2,
+						'submodel_id' => 66,
+						'model_id' => 0,
+						'type' => 1
+					),
+				),
+				'2' => array(
+					'id' => 17,
+					'SubModelsModel' => array(
+						'id' => 3,
+						'submodel_id' => 66,
+						'model_id' => 17,
+						'type' => 2
+					),
+				),
+				'3' => array(
+					'id' => 0,
+					'SubModelsModel' => array(
+						'id' => 4,
+						'submodel_id' => 66,
+						'model_id' => 0,
+						'type' => 2
+					)
+				)
+			)
+		);
+
+		$expected = array(
+			array(
+				'Model' => array(
+					'id' => 17,
+					'SubModelsModel' => array(
+						'id' => 3,
+						'submodel_id' => 66,
+						'model_id' => 17,
+						'type' => 2
+					),
+				)
+			),
+			array(
+				'Model' => array(
+					'id' => 0,
+					'SubModelsModel' => array(
+						'id' => 4,
+						'submodel_id' => 66,
+						'model_id' => 0,
+						'type' => 2
+					)
+				)
+			)
+		);
+		$r = Set::extract('/Model/SubModelsModel[type=2]/..', $a);
+		$this->assertEqual($r, $expected);
+	}
+
+/**
+ * test that extract() still works when arrays don't contain a 0 index.
+ *
+ * @return void
+ */
+	function testExtractWithNonZeroArrays() {
+		$nonZero = array(
+			1 => array(
+				'User' => array(
+					'id' => 1,
+					'name' => 'John',
+				)
+			),
+			2 => array(
+				'User' => array(
+					'id' => 2,
+					'name' => 'Bob',
+				)
+			),
+			3 => array(
+				'User' => array(
+					'id' => 3,
+					'name' => 'Tony',
+				)
+			)
+		);
+		$expected = array(1, 2, 3);
+		$r = Set::extract('/User/id', $nonZero);
+		$this->assertEqual($r, $expected);
+
+		$expected = array(
+			array('User' => array('id' => 1, 'name' => 'John')),
+			array('User' => array('id' => 2, 'name' => 'Bob')),
+			array('User' => array('id' => 3, 'name' => 'Tony')),
+		);
+		$result = Set::extract('/User', $nonZero);
+		$this->assertEqual($result, $expected);
+
+		$nonSequential = array(
+			'User' => array(
+				0  => array('id' => 1),
+				2  => array('id' => 2),
+				6  => array('id' => 3),
+				9  => array('id' => 4),
+				3  => array('id' => 5),
+			),
+		);
+
+		$nonZero = array(
+			'User' => array(
+				2  => array('id' => 1),
+				4  => array('id' => 2),
+				6  => array('id' => 3),
+				9  => array('id' => 4),
+				3  => array('id' => 5),
+			),
+		);
+
+		$expected = array(1, 2, 3, 4, 5);
+		$this->assertEqual(Set::extract('/User/id', $nonSequential), $expected);
+
+		$result = Set::extract('/User/id', $nonZero);
+		$this->assertEqual($result, $expected, 'Failed non zero array key extract');
+
+		$expected = array(1, 2, 3, 4, 5);
+		$this->assertEqual(Set::extract('/User/id', $nonSequential), $expected);
+
+		$result = Set::extract('/User/id', $nonZero);
+		$this->assertEqual($result, $expected, 'Failed non zero array key extract');
+
+		$startingAtOne = array(
+			'Article' => array(
+				1 => array(
+					'id' => 1,
+					'approved' => 1,
+				),
+			)
+		);
+
+		$expected = array(0 => array('Article' => array('id' => 1, 'approved' => 1)));
+		$result = Set::extract('/Article[approved=1]', $startingAtOne);
+		$this->assertEqual($result, $expected);
+
+		$items = array(
+			240 => array(
+				'A' => array(
+					'field1' => 'a240',
+					'field2' => 'a240',
+				),
+				'B' => array(
+					'field1' => 'b240',
+					'field2' => 'b240'
+				),
+			)
+		);
+
+		$expected = array(
+			0 => 'b240'
+		);
+
+		$result = Set::extract('/B/field1', $items);
+		$this->assertIdentical($result, $expected);
+		$this->assertIdentical($result, Set::extract('{n}.B.field1', $items));
+	}
+/**
+ * testExtractWithArrays method
+ *
+ * @access public
+ * @return void
+ */
+	function testExtractWithArrays() {
+		$data = array(
+			'Level1' => array(
+				'Level2' => array('test1', 'test2'),
+				'Level2bis' => array('test3', 'test4')
+			)
+		);
+		$this->assertEqual(Set::extract('/Level1/Level2', $data), array(array('Level2' => array('test1', 'test2'))));
+		$this->assertEqual(Set::extract('/Level1/Level2bis', $data), array(array('Level2bis' => array('test3', 'test4'))));
+	}
+
+/**
+ * test extract() with elements that have non-array children.
+ *
+ * @return void
+ */
+	function testExtractWithNonArrayElements() {
+		$data = array(
+			'node' => array(
+				array('foo'),
+				'bar'
+			)
+		);
+		$result = Set::extract('/node', $data);
+		$expected = array(
+			array('node' => array('foo')),
+			'bar'
+		);
+		$this->assertEqual($result, $expected);
+
+		$data = array(
+			'node' => array(
+				'foo' => array('bar'),
+				'bar' => array('foo')
+			)
+		);
+		$result = Set::extract('/node', $data);
+		$expected = array(
+			array('foo' => array('bar')),
+			array('bar' => array('foo')),
+		);
+		$this->assertEqual($result, $expected);
+
+		$data = array(
+			'node' => array(
+				'foo' => array(
+					'bar'
+				),
+				'bar' => 'foo'
+			)
+		);
+		$result = Set::extract('/node', $data);
+		$expected = array(
+			array('foo' => array('bar')),
+			'foo'
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testMatches method
+ *
+ * @access public
+ * @return void
+ */
+	function testMatches() {
+		$a = array(
+			array('Article' => array('id' => 1, 'title' => 'Article 1')),
+			array('Article' => array('id' => 2, 'title' => 'Article 2')),
+			array('Article' => array('id' => 3, 'title' => 'Article 3'))
+		);
+
+		$this->assertTrue(Set::matches(array('id=2'), $a[1]['Article']));
+		$this->assertFalse(Set::matches(array('id>2'), $a[1]['Article']));
+		$this->assertTrue(Set::matches(array('id>=2'), $a[1]['Article']));
+		$this->assertFalse(Set::matches(array('id>=3'), $a[1]['Article']));
+		$this->assertTrue(Set::matches(array('id<=2'), $a[1]['Article']));
+		$this->assertFalse(Set::matches(array('id<2'), $a[1]['Article']));
+		$this->assertTrue(Set::matches(array('id>1'), $a[1]['Article']));
+		$this->assertTrue(Set::matches(array('id>1', 'id<3', 'id!=0'), $a[1]['Article']));
+
+		$this->assertTrue(Set::matches(array('3'), null, 3));
+		$this->assertTrue(Set::matches(array('5'), null, 5));
+
+		$this->assertTrue(Set::matches(array('id'), $a[1]['Article']));
+		$this->assertTrue(Set::matches(array('id', 'title'), $a[1]['Article']));
+		$this->assertFalse(Set::matches(array('non-existant'), $a[1]['Article']));
+
+		$this->assertTrue(Set::matches('/Article[id=2]', $a));
+		$this->assertFalse(Set::matches('/Article[id=4]', $a));
+		$this->assertTrue(Set::matches(array(), $a));
+
+		$r = array(
+			'Attachment' => array(
+				'keep' => array()
+			),
+			'Comment' => array(
+				'keep' => array(
+					'Attachment' =>  array(
+						'fields' => array(
+							0 => 'attachment',
+						),
+					),
+				)
+			),
+			'User' => array(
+				'keep' => array()
+			),
+			'Article' => array(
+				'keep' => array(
+					'Comment' =>  array(
+						'fields' => array(
+							0 => 'comment',
+							1 => 'published',
+						),
+					),
+					'User' => array(
+						'fields' => array(
+							0 => 'user',
+						),
+					),
+				)
+			)
+		);
+
+		$this->assertTrue(Set::matches('/Article/keep/Comment', $r));
+		$this->assertEqual(Set::extract('/Article/keep/Comment/fields', $r), array('comment', 'published'));
+		$this->assertEqual(Set::extract('/Article/keep/User/fields', $r), array('user'));
+
+
+	}
+
+/**
+ * testSetExtractReturnsEmptyArray method
+ *
+ * @access public
+ * @return void
+ */
+	function testSetExtractReturnsEmptyArray() {
+
+		$this->assertIdentical(Set::extract(array(), '/Post/id'), array());
+
+		$this->assertIdentical(Set::extract('/Post/id', array()), array());
+
+		$this->assertIdentical(Set::extract('/Post/id', array(
+			array('Post' => array('name' => 'bob')),
+			array('Post' => array('name' => 'jim'))
+		)), array());
+
+		$this->assertIdentical(Set::extract(array(), 'Message.flash'), null);
+
+	}
+
+/**
+ * testClassicExtract method
+ *
+ * @access public
+ * @return void
+ */
+	function testClassicExtract() {
+		$a = array(
+			array('Article' => array('id' => 1, 'title' => 'Article 1')),
+			array('Article' => array('id' => 2, 'title' => 'Article 2')),
+			array('Article' => array('id' => 3, 'title' => 'Article 3'))
+		);
+
+		$result = Set::extract($a, '{n}.Article.id');
+		$expected = array( 1, 2, 3 );
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::extract($a, '{n}.Article.title');
+		$expected = array( 'Article 1', 'Article 2', 'Article 3' );
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::extract($a, '1.Article.title');
+		$expected = 'Article 2';
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::extract($a, '3.Article.title');
+		$expected = null;
+		$this->assertIdentical($result, $expected);
+
+		$a = array(
+			array(
+				'Article' => array('id' => 1, 'title' => 'Article 1',
+				'User' => array('id' => 1, 'username' => 'mariano.iglesias'))
+			),
+			array(
+				'Article' => array('id' => 2, 'title' => 'Article 2',
+				'User' => array('id' => 1, 'username' => 'mariano.iglesias'))
+			),
+			array(
+				'Article' => array('id' => 3, 'title' => 'Article 3',
+				'User' => array('id' => 2, 'username' => 'phpnut'))
+			)
+		);
+
+		$result = Set::extract($a, '{n}.Article.User.username');
+		$expected = array( 'mariano.iglesias', 'mariano.iglesias', 'phpnut' );
+		$this->assertIdentical($result, $expected);
+
+		$a = array(
+			array('Article' => array('id' => 1, 'title' => 'Article 1',
+				'Comment' => array(
+					array('id' => 10, 'title' => 'Comment 10'),
+					array('id' => 11, 'title' => 'Comment 11'),
+					array('id' => 12, 'title' => 'Comment 12')))),
+			array('Article' => array('id' => 2, 'title' => 'Article 2',
+				'Comment' => array(
+					array('id' => 13, 'title' => 'Comment 13'),
+					array('id' => 14, 'title' => 'Comment 14')))),
+			array('Article' => array('id' => 3, 'title' => 'Article 3')));
+
+		$result = Set::extract($a, '{n}.Article.Comment.{n}.id');
+		$expected = array (array(10, 11, 12), array(13, 14), null);
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::extract($a, '{n}.Article.Comment.{n}.title');
+		$expected = array(
+			array('Comment 10', 'Comment 11', 'Comment 12'),
+			array('Comment 13', 'Comment 14'),
+			null
+		);
+		$this->assertIdentical($result, $expected);
+
+		$a = array(array('1day' => '20 sales'), array('1day' => '2 sales'));
+		$result = Set::extract($a, '{n}.1day');
+		$expected = array('20 sales', '2 sales');
+		$this->assertIdentical($result, $expected);
+
+		$a = array(
+			'pages'     => array('name' => 'page'),
+			'fruites'   => array('name' => 'fruit'),
+			0           => array('name' => 'zero')
+		);
+		$result = Set::extract($a, '{s}.name');
+		$expected = array('page','fruit');
+		$this->assertIdentical($result, $expected);
+
+		$a = array(
+			0 => array('pages' => array('name' => 'page')),
+			1 => array('fruites'=> array('name' => 'fruit')),
+			'test' => array(array('name' => 'jippi')),
+			'dot.test' => array(array('name' => 'jippi'))
+		);
+
+		$result = Set::extract($a, '{n}.{s}.name');
+		$expected = array(0 => array('page'), 1 => array('fruit'));
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::extract($a, '{s}.{n}.name');
+		$expected = array(array('jippi'), array('jippi'));
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::extract($a,'{\w+}.{\w+}.name');
+		$expected = array(
+			array('pages' => 'page'),
+			array('fruites' => 'fruit'),
+			'test' => array('jippi'),
+			'dot.test' => array('jippi')
+		);
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::extract($a,'{\d+}.{\w+}.name');
+		$expected = array(array('pages' => 'page'), array('fruites' => 'fruit'));
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::extract($a,'{n}.{\w+}.name');
+		$expected = array(array('pages' => 'page'), array('fruites' => 'fruit'));
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::extract($a,'{s}.{\d+}.name');
+		$expected = array(array('jippi'), array('jippi'));
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::extract($a,'{s}');
+		$expected = array(array(array('name' => 'jippi')), array(array('name' => 'jippi')));
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::extract($a,'{[a-z]}');
+		$expected = array(
+			'test' => array(array('name' => 'jippi')),
+			'dot.test' => array(array('name' => 'jippi'))
+		);
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::extract($a, '{dot\.test}.{n}');
+		$expected = array('dot.test' => array(array('name' => 'jippi')));
+		$this->assertIdentical($result, $expected);
+
+		$a = new stdClass();
+		$a->articles = array(
+			array('Article' => array('id' => 1, 'title' => 'Article 1')),
+			array('Article' => array('id' => 2, 'title' => 'Article 2')),
+			array('Article' => array('id' => 3, 'title' => 'Article 3')));
+
+		$result = Set::extract($a, 'articles.{n}.Article.id');
+		$expected = array( 1, 2, 3 );
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::extract($a, 'articles.{n}.Article.title');
+		$expected = array( 'Article 1', 'Article 2', 'Article 3' );
+		$this->assertIdentical($result, $expected);
+	}
+
+/**
+ * testInsert method
+ *
+ * @access public
+ * @return void
+ */
+	function testInsert() {
+		$a = array(
+			'pages' => array('name' => 'page')
+		);
+
+		$result = Set::insert($a, 'files', array('name' => 'files'));
+		$expected = array(
+			'pages'     => array('name' => 'page'),
+			'files'		=> array('name' => 'files')
+		);
+		$this->assertIdentical($result, $expected);
+
+		$a = array(
+			'pages' => array('name' => 'page')
+		);
+		$result = Set::insert($a, 'pages.name', array());
+		$expected = array(
+			'pages'     => array('name' => array()),
+		);
+		$this->assertIdentical($result, $expected);
+
+		$a = array(
+			'pages' => array(
+				0 => array('name' => 'main'),
+				1 => array('name' => 'about')
+			)
+		);
+
+		$result = Set::insert($a, 'pages.1.vars', array('title' => 'page title'));
+		$expected = array(
+			'pages' => array(
+				0 => array('name' => 'main'),
+				1 => array('name' => 'about', 'vars' => array('title' => 'page title'))
+			)
+		);
+		$this->assertIdentical($result, $expected);
+	}
+
+/**
+ * testRemove method
+ *
+ * @access public
+ * @return void
+ */
+	function testRemove() {
+		$a = array(
+			'pages'     => array('name' => 'page'),
+			'files'		=> array('name' => 'files')
+		);
+
+		$result = Set::remove($a, 'files');
+		$expected = array(
+			'pages'     => array('name' => 'page')
+		);
+		$this->assertIdentical($result, $expected);
+
+		$a = array(
+			'pages' => array(
+				0 => array('name' => 'main'),
+				1 => array('name' => 'about', 'vars' => array('title' => 'page title'))
+			)
+		);
+
+		$result = Set::remove($a, 'pages.1.vars');
+		$expected = array(
+			'pages' => array(
+				0 => array('name' => 'main'),
+				1 => array('name' => 'about')
+			)
+		);
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::remove($a, 'pages.2.vars');
+		$expected = $a;
+		$this->assertIdentical($result, $expected);
+	}
+
+/**
+ * testCheck method
+ *
+ * @access public
+ * @return void
+ */
+	function testCheck() {
+		$set = array(
+			'My Index 1' => array('First' => 'The first item')
+		);
+		$this->assertTrue(Set::check($set, 'My Index 1.First'));
+		$this->assertTrue(Set::check($set, 'My Index 1'));
+		$this->assertTrue(Set::check($set, array()));
+
+		$set = array(
+			'My Index 1' => array('First' => array('Second' => array('Third' => array('Fourth' => 'Heavy. Nesting.'))))
+		);
+		$this->assertTrue(Set::check($set, 'My Index 1.First.Second'));
+		$this->assertTrue(Set::check($set, 'My Index 1.First.Second.Third'));
+		$this->assertTrue(Set::check($set, 'My Index 1.First.Second.Third.Fourth'));
+		$this->assertFalse(Set::check($set, 'My Index 1.First.Seconds.Third.Fourth'));
+	}
+
+/**
+ * testWritingWithFunkyKeys method
+ *
+ * @access public
+ * @return void
+ */
+	function testWritingWithFunkyKeys() {
+		$set = Set::insert(array(), 'Session Test', "test");
+		$this->assertEqual(Set::extract($set, 'Session Test'), 'test');
+
+		$set = Set::remove($set, 'Session Test');
+		$this->assertFalse(Set::check($set, 'Session Test'));
+
+		$this->assertTrue($set = Set::insert(array(), 'Session Test.Test Case', "test"));
+		$this->assertTrue(Set::check($set, 'Session Test.Test Case'));
+	}
+
+/**
+ * testDiff method
+ *
+ * @access public
+ * @return void
+ */
+	function testDiff() {
+		$a = array(
+			0 => array('name' => 'main'),
+			1 => array('name' => 'about')
+		);
+		$b = array(
+			0 => array('name' => 'main'),
+			1 => array('name' => 'about'),
+			2 => array('name' => 'contact')
+		);
+
+		$result = Set::diff($a, $b);
+		$expected = array(
+			2 => array('name' => 'contact')
+		);
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::diff($a, array());
+		$expected = $a;
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::diff(array(), $b);
+		$expected = $b;
+		$this->assertIdentical($result, $expected);
+
+		$b = array(
+			0 => array('name' => 'me'),
+			1 => array('name' => 'about')
+		);
+
+		$result = Set::diff($a, $b);
+		$expected = array(
+			0 => array('name' => 'main')
+		);
+		$this->assertIdentical($result, $expected);
+
+		$a = array();
+		$b = array('name' => 'bob', 'address' => 'home');
+		$result = Set::diff($a, $b);
+		$this->assertIdentical($result, $b);
+
+
+		$a = array('name' => 'bob', 'address' => 'home');
+		$b = array();
+		$result = Set::diff($a, $b);
+		$this->assertIdentical($result, $a);
+
+		$a = array('key' => true, 'another' => false, 'name' => 'me');
+		$b = array('key' => 1, 'another' => 0);
+		$expected = array('name' => 'me');
+		$result = Set::diff($a, $b);
+		$this->assertIdentical($result, $expected);
+
+		$a = array('key' => 'value', 'another' => null, 'name' => 'me');
+		$b = array('key' => 'differentValue', 'another' => null);
+		$expected = array('key' => 'value', 'name' => 'me');
+		$result = Set::diff($a, $b);
+		$this->assertIdentical($result, $expected);
+
+		$a = array('key' => 'value', 'another' => null, 'name' => 'me');
+		$b = array('key' => 'differentValue', 'another' => 'value');
+		$expected = array('key' => 'value', 'another' => null, 'name' => 'me');
+		$result = Set::diff($a, $b);
+		$this->assertIdentical($result, $expected);
+
+		$a = array('key' => 'value', 'another' => null, 'name' => 'me');
+		$b = array('key' => 'differentValue', 'another' => 'value');
+		$expected = array('key' => 'differentValue', 'another' => 'value', 'name' => 'me');
+		$result = Set::diff($b, $a);
+		$this->assertIdentical($result, $expected);
+
+		$a = array('key' => 'value', 'another' => null, 'name' => 'me');
+		$b = array(0 => 'differentValue', 1 => 'value');
+		$expected = $a + $b;
+		$result = Set::diff($a, $b);
+		$this->assertIdentical($result, $expected);
+	}
+
+/**
+ * testContains method
+ *
+ * @access public
+ * @return void
+ */
+	function testContains() {
+		$a = array(
+			0 => array('name' => 'main'),
+			1 => array('name' => 'about')
+		);
+		$b = array(
+			0 => array('name' => 'main'),
+			1 => array('name' => 'about'),
+			2 => array('name' => 'contact'),
+			'a' => 'b'
+		);
+
+		$this->assertTrue(Set::contains($a, $a));
+		$this->assertFalse(Set::contains($a, $b));
+		$this->assertTrue(Set::contains($b, $a));
+	}
+
+/**
+ * testCombine method
+ *
+ * @access public
+ * @return void
+ */
+	function testCombine() {
+		$result = Set::combine(array(), '{n}.User.id', '{n}.User.Data');
+		$this->assertFalse($result);
+		$result = Set::combine('', '{n}.User.id', '{n}.User.Data');
+		$this->assertFalse($result);
+
+		$a = array(
+			array('User' => array('id' => 2, 'group_id' => 1,
+				'Data' => array('user' => 'mariano.iglesias','name' => 'Mariano Iglesias'))),
+			array('User' => array('id' => 14, 'group_id' => 2,
+				'Data' => array('user' => 'phpnut', 'name' => 'Larry E. Masters'))),
+			array('User' => array('id' => 25, 'group_id' => 1,
+				'Data' => array('user' => 'gwoo','name' => 'The Gwoo'))));
+		$result = Set::combine($a, '{n}.User.id');
+		$expected = array(2 => null, 14 => null, 25 => null);
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::combine($a, '{n}.User.id', '{n}.User.non-existant');
+		$expected = array(2 => null, 14 => null, 25 => null);
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::combine($a, '{n}.User.id', '{n}.User.Data');
+		$expected = array(
+			2 => array('user' => 'mariano.iglesias',	'name' => 'Mariano Iglesias'),
+			14 => array('user' => 'phpnut',	'name' => 'Larry E. Masters'),
+			25 => array('user' => 'gwoo',	'name' => 'The Gwoo'));
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::combine($a, '{n}.User.id', '{n}.User.Data.name');
+		$expected = array(
+			2 => 'Mariano Iglesias',
+			14 => 'Larry E. Masters',
+			25 => 'The Gwoo');
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::combine($a, '{n}.User.id', '{n}.User.Data', '{n}.User.group_id');
+		$expected = array(
+			1 => array(
+				2 => array('user' => 'mariano.iglesias', 'name' => 'Mariano Iglesias'),
+				25 => array('user' => 'gwoo', 'name' => 'The Gwoo')),
+			2 => array(
+				14 => array('user' => 'phpnut', 'name' => 'Larry E. Masters')));
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::combine($a, '{n}.User.id', '{n}.User.Data.name', '{n}.User.group_id');
+		$expected = array(
+			1 => array(
+				2 => 'Mariano Iglesias',
+				25 => 'The Gwoo'),
+			2 => array(
+				14 => 'Larry E. Masters'));
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::combine($a, '{n}.User.id');
+		$expected = array(2 => null, 14 => null, 25 => null);
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::combine($a, '{n}.User.id', '{n}.User.Data');
+		$expected = array(
+			2 => array('user' => 'mariano.iglesias', 'name' => 'Mariano Iglesias'),
+			14 => array('user' => 'phpnut', 'name' => 'Larry E. Masters'),
+			25 => array('user' => 'gwoo', 'name' => 'The Gwoo'));
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::combine($a, '{n}.User.id', '{n}.User.Data.name');
+		$expected = array(2 => 'Mariano Iglesias', 14 => 'Larry E. Masters', 25 => 'The Gwoo');
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::combine($a, '{n}.User.id', '{n}.User.Data', '{n}.User.group_id');
+		$expected = array(
+			1 => array(
+				2 => array('user' => 'mariano.iglesias', 'name' => 'Mariano Iglesias'),
+				25 => array('user' => 'gwoo', 'name' => 'The Gwoo')),
+			2 => array(
+				14 => array('user' => 'phpnut', 'name' => 'Larry E. Masters')));
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::combine($a, '{n}.User.id', '{n}.User.Data.name', '{n}.User.group_id');
+		$expected = array(
+			1 => array(
+				2 => 'Mariano Iglesias',
+				25 => 'The Gwoo'),
+			2 => array(
+				14 => 'Larry E. Masters'));
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::combine($a, '{n}.User.id', array('{0}: {1}', '{n}.User.Data.user', '{n}.User.Data.name'), '{n}.User.group_id');
+		$expected = array (
+			1 => array (
+				2 => 'mariano.iglesias: Mariano Iglesias',
+				25 => 'gwoo: The Gwoo'),
+			2 => array (14 => 'phpnut: Larry E. Masters'));
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::combine($a, array('{0}: {1}', '{n}.User.Data.user', '{n}.User.Data.name'), '{n}.User.id');
+		$expected = array('mariano.iglesias: Mariano Iglesias' => 2, 'phpnut: Larry E. Masters' => 14, 'gwoo: The Gwoo' => 25);
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::combine($a, array('{1}: {0}', '{n}.User.Data.user', '{n}.User.Data.name'), '{n}.User.id');
+		$expected = array('Mariano Iglesias: mariano.iglesias' => 2, 'Larry E. Masters: phpnut' => 14, 'The Gwoo: gwoo' => 25);
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::combine($a, array('%1$s: %2$d', '{n}.User.Data.user', '{n}.User.id'), '{n}.User.Data.name');
+		$expected = array('mariano.iglesias: 2' => 'Mariano Iglesias', 'phpnut: 14' => 'Larry E. Masters', 'gwoo: 25' => 'The Gwoo');
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::combine($a, array('%2$d: %1$s', '{n}.User.Data.user', '{n}.User.id'), '{n}.User.Data.name');
+		$expected = array('2: mariano.iglesias' => 'Mariano Iglesias', '14: phpnut' => 'Larry E. Masters', '25: gwoo' => 'The Gwoo');
+		$this->assertIdentical($result, $expected);
+
+		$b = new stdClass();
+		$b->users = array(
+			array('User' => array('id' => 2, 'group_id' => 1,
+				'Data' => array('user' => 'mariano.iglesias','name' => 'Mariano Iglesias'))),
+			array('User' => array('id' => 14, 'group_id' => 2,
+				'Data' => array('user' => 'phpnut', 'name' => 'Larry E. Masters'))),
+			array('User' => array('id' => 25, 'group_id' => 1,
+				'Data' => array('user' => 'gwoo','name' => 'The Gwoo'))));
+		$result = Set::combine($b, 'users.{n}.User.id');
+		$expected = array(2 => null, 14 => null, 25 => null);
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::combine($b, 'users.{n}.User.id', 'users.{n}.User.non-existant');
+		$expected = array(2 => null, 14 => null, 25 => null);
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::combine($a, 'fail', 'fail');
+		$this->assertEqual($result, array());
+	}
+
+/**
+ * testMapReverse method
+ *
+ * @access public
+ * @return void
+ */
+	function testMapReverse() {
+		$result = Set::reverse(null);
+		$this->assertEqual($result, null);
+
+		$result = Set::reverse(false);
+		$this->assertEqual($result, false);
+
+		$expected = array(
+		'Array1' => array(
+				'Array1Data1' => 'Array1Data1 value 1', 'Array1Data2' => 'Array1Data2 value 2'),
+		'Array2' => array(
+				0 => array('Array2Data1' => 1, 'Array2Data2' => 'Array2Data2 value 2', 'Array2Data3' => 'Array2Data3 value 2', 'Array2Data4' => 'Array2Data4 value 4'),
+				1 => array('Array2Data1' => 2, 'Array2Data2' => 'Array2Data2 value 2', 'Array2Data3' => 'Array2Data3 value 2', 'Array2Data4' => 'Array2Data4 value 4'),
+				2 => array('Array2Data1' => 3, 'Array2Data2' => 'Array2Data2 value 2', 'Array2Data3' => 'Array2Data3 value 2', 'Array2Data4' => 'Array2Data4 value 4'),
+				3 => array('Array2Data1' => 4, 'Array2Data2' => 'Array2Data2 value 2', 'Array2Data3' => 'Array2Data3 value 2', 'Array2Data4' => 'Array2Data4 value 4'),
+				4 => array('Array2Data1' => 5, 'Array2Data2' => 'Array2Data2 value 2', 'Array2Data3' => 'Array2Data3 value 2', 'Array2Data4' => 'Array2Data4 value 4')),
+		'Array3' => array(
+				0 => array('Array3Data1' => 1, 'Array3Data2' => 'Array3Data2 value 2', 'Array3Data3' => 'Array3Data3 value 2', 'Array3Data4' => 'Array3Data4 value 4'),
+				1 => array('Array3Data1' => 2, 'Array3Data2' => 'Array3Data2 value 2', 'Array3Data3' => 'Array3Data3 value 2', 'Array3Data4' => 'Array3Data4 value 4'),
+				2 => array('Array3Data1' => 3, 'Array3Data2' => 'Array3Data2 value 2', 'Array3Data3' => 'Array3Data3 value 2', 'Array3Data4' => 'Array3Data4 value 4'),
+				3 => array('Array3Data1' => 4, 'Array3Data2' => 'Array3Data2 value 2', 'Array3Data3' => 'Array3Data3 value 2', 'Array3Data4' => 'Array3Data4 value 4'),
+				4 => array('Array3Data1' => 5, 'Array3Data2' => 'Array3Data2 value 2', 'Array3Data3' => 'Array3Data3 value 2', 'Array3Data4' => 'Array3Data4 value 4')));
+		$map = Set::map($expected, true);
+		$this->assertEqual($map->Array1->Array1Data1, $expected['Array1']['Array1Data1']);
+		$this->assertEqual($map->Array2[0]->Array2Data1, $expected['Array2'][0]['Array2Data1']);
+
+		$result = Set::reverse($map);
+		$this->assertIdentical($result, $expected);
+
+		$expected = array(
+			'Post' => array('id'=> 1, 'title' => 'First Post'),
+			'Comment' => array(
+				array('id'=> 1, 'title' => 'First Comment'),
+				array('id'=> 2, 'title' => 'Second Comment')
+			),
+			'Tag' => array(
+				array('id'=> 1, 'title' => 'First Tag'),
+				array('id'=> 2, 'title' => 'Second Tag')
+			),
+		);
+		$map = Set::map($expected);
+		$this->assertIdentical($map->title, $expected['Post']['title']);
+		foreach ($map->Comment as $comment) {
+			$ids[] = $comment->id;
+		}
+		$this->assertIdentical($ids, array(1, 2));
+
+		$expected = array(
+		'Array1' => array(
+				'Array1Data1' => 'Array1Data1 value 1', 'Array1Data2' => 'Array1Data2 value 2', 'Array1Data3' => 'Array1Data3 value 3','Array1Data4' => 'Array1Data4 value 4',
+				'Array1Data5' => 'Array1Data5 value 5', 'Array1Data6' => 'Array1Data6 value 6', 'Array1Data7' => 'Array1Data7 value 7', 'Array1Data8' => 'Array1Data8 value 8'),
+		'string' => 1,
+		'another' => 'string',
+		'some' => 'thing else',
+		'Array2' => array(
+				0 => array('Array2Data1' => 1, 'Array2Data2' => 'Array2Data2 value 2', 'Array2Data3' => 'Array2Data3 value 2', 'Array2Data4' => 'Array2Data4 value 4'),
+				1 => array('Array2Data1' => 2, 'Array2Data2' => 'Array2Data2 value 2', 'Array2Data3' => 'Array2Data3 value 2', 'Array2Data4' => 'Array2Data4 value 4'),
+				2 => array('Array2Data1' => 3, 'Array2Data2' => 'Array2Data2 value 2', 'Array2Data3' => 'Array2Data3 value 2', 'Array2Data4' => 'Array2Data4 value 4'),
+				3 => array('Array2Data1' => 4, 'Array2Data2' => 'Array2Data2 value 2', 'Array2Data3' => 'Array2Data3 value 2', 'Array2Data4' => 'Array2Data4 value 4'),
+				4 => array('Array2Data1' => 5, 'Array2Data2' => 'Array2Data2 value 2', 'Array2Data3' => 'Array2Data3 value 2', 'Array2Data4' => 'Array2Data4 value 4')),
+		'Array3' => array(
+				0 => array('Array3Data1' => 1, 'Array3Data2' => 'Array3Data2 value 2', 'Array3Data3' => 'Array3Data3 value 2', 'Array3Data4' => 'Array3Data4 value 4'),
+				1 => array('Array3Data1' => 2, 'Array3Data2' => 'Array3Data2 value 2', 'Array3Data3' => 'Array3Data3 value 2', 'Array3Data4' => 'Array3Data4 value 4'),
+				2 => array('Array3Data1' => 3, 'Array3Data2' => 'Array3Data2 value 2', 'Array3Data3' => 'Array3Data3 value 2', 'Array3Data4' => 'Array3Data4 value 4'),
+				3 => array('Array3Data1' => 4, 'Array3Data2' => 'Array3Data2 value 2', 'Array3Data3' => 'Array3Data3 value 2', 'Array3Data4' => 'Array3Data4 value 4'),
+				4 => array('Array3Data1' => 5, 'Array3Data2' => 'Array3Data2 value 2', 'Array3Data3' => 'Array3Data3 value 2', 'Array3Data4' => 'Array3Data4 value 4')));
+		$map = Set::map($expected, true);
+		$result = Set::reverse($map);
+		$this->assertIdentical($result, $expected);
+
+		$expected = array(
+		'Array1' => array(
+				'Array1Data1' => 'Array1Data1 value 1', 'Array1Data2' => 'Array1Data2 value 2', 'Array1Data3' => 'Array1Data3 value 3','Array1Data4' => 'Array1Data4 value 4',
+				'Array1Data5' => 'Array1Data5 value 5', 'Array1Data6' => 'Array1Data6 value 6', 'Array1Data7' => 'Array1Data7 value 7', 'Array1Data8' => 'Array1Data8 value 8'),
+		'string' => 1,
+		'another' => 'string',
+		'some' => 'thing else',
+		'Array2' => array(
+				0 => array('Array2Data1' => 1, 'Array2Data2' => 'Array2Data2 value 2', 'Array2Data3' => 'Array2Data3 value 2', 'Array2Data4' => 'Array2Data4 value 4'),
+				1 => array('Array2Data1' => 2, 'Array2Data2' => 'Array2Data2 value 2', 'Array2Data3' => 'Array2Data3 value 2', 'Array2Data4' => 'Array2Data4 value 4'),
+				2 => array('Array2Data1' => 3, 'Array2Data2' => 'Array2Data2 value 2', 'Array2Data3' => 'Array2Data3 value 2', 'Array2Data4' => 'Array2Data4 value 4'),
+				3 => array('Array2Data1' => 4, 'Array2Data2' => 'Array2Data2 value 2', 'Array2Data3' => 'Array2Data3 value 2', 'Array2Data4' => 'Array2Data4 value 4'),
+				4 => array('Array2Data1' => 5, 'Array2Data2' => 'Array2Data2 value 2', 'Array2Data3' => 'Array2Data3 value 2', 'Array2Data4' => 'Array2Data4 value 4')),
+		'string2' => 1,
+		'another2' => 'string',
+		'some2' => 'thing else',
+		'Array3' => array(
+				0 => array('Array3Data1' => 1, 'Array3Data2' => 'Array3Data2 value 2', 'Array3Data3' => 'Array3Data3 value 2', 'Array3Data4' => 'Array3Data4 value 4'),
+				1 => array('Array3Data1' => 2, 'Array3Data2' => 'Array3Data2 value 2', 'Array3Data3' => 'Array3Data3 value 2', 'Array3Data4' => 'Array3Data4 value 4'),
+				2 => array('Array3Data1' => 3, 'Array3Data2' => 'Array3Data2 value 2', 'Array3Data3' => 'Array3Data3 value 2', 'Array3Data4' => 'Array3Data4 value 4'),
+				3 => array('Array3Data1' => 4, 'Array3Data2' => 'Array3Data2 value 2', 'Array3Data3' => 'Array3Data3 value 2', 'Array3Data4' => 'Array3Data4 value 4'),
+				4 => array('Array3Data1' => 5, 'Array3Data2' => 'Array3Data2 value 2', 'Array3Data3' => 'Array3Data3 value 2', 'Array3Data4' => 'Array3Data4 value 4')),
+		'string3' => 1,
+		'another3' => 'string',
+		'some3' => 'thing else');
+		$map = Set::map($expected, true);
+		$result = Set::reverse($map);
+		$this->assertIdentical($result, $expected);
+
+		$expected = array('User' => array('psword'=> 'whatever', 'Icon' => array('id'=> 851)));
+		$map = Set::map($expected);
+		$result = Set::reverse($map);
+		$this->assertIdentical($result, $expected);
+
+		$expected = array('User' => array('psword'=> 'whatever', 'Icon' => array('id'=> 851)));
+		$class = new stdClass;
+		$class->User = new stdClass;
+		$class->User->psword = 'whatever';
+		$class->User->Icon = new stdClass;
+		$class->User->Icon->id = 851;
+		$result = Set::reverse($class);
+		$this->assertIdentical($result, $expected);
+
+		$expected = array('User' => array('psword'=> 'whatever', 'Icon' => array('id'=> 851), 'Profile' => array('name' => 'Some Name', 'address' => 'Some Address')));
+		$class = new stdClass;
+		$class->User = new stdClass;
+		$class->User->psword = 'whatever';
+		$class->User->Icon = new stdClass;
+		$class->User->Icon->id = 851;
+		$class->User->Profile = new stdClass;
+		$class->User->Profile->name = 'Some Name';
+		$class->User->Profile->address = 'Some Address';
+
+		$result = Set::reverse($class);
+		$this->assertIdentical($result, $expected);
+
+		$expected = array('User' => array('psword'=> 'whatever',
+						'Icon' => array('id'=> 851),
+						'Profile' => array('name' => 'Some Name', 'address' => 'Some Address'),
+						'Comment' => array(
+								array('id' => 1, 'article_id' => 1, 'user_id' => 1, 'comment' => 'First Comment for First Article', 'published' => 'Y', 'created' => '2007-03-18 10:47:23', 'updated' => '2007-03-18 10:49:31'),
+								array('id' => 2, 'article_id' => 1, 'user_id' => 2, 'comment' => 'Second Comment for First Article', 'published' => 'Y', 'created' => '2007-03-18 10:47:23', 'updated' => '2007-03-18 10:49:31'))));
+
+		$class = new stdClass;
+		$class->User = new stdClass;
+		$class->User->psword = 'whatever';
+		$class->User->Icon = new stdClass;
+		$class->User->Icon->id = 851;
+		$class->User->Profile = new stdClass;
+		$class->User->Profile->name = 'Some Name';
+		$class->User->Profile->address = 'Some Address';
+		$class->User->Comment = new stdClass;
+		$class->User->Comment->{'0'} = new stdClass;
+		$class->User->Comment->{'0'}->id = 1;
+		$class->User->Comment->{'0'}->article_id = 1;
+		$class->User->Comment->{'0'}->user_id = 1;
+		$class->User->Comment->{'0'}->comment = 'First Comment for First Article';
+		$class->User->Comment->{'0'}->published = 'Y';
+		$class->User->Comment->{'0'}->created = '2007-03-18 10:47:23';
+		$class->User->Comment->{'0'}->updated = '2007-03-18 10:49:31';
+		$class->User->Comment->{'1'} = new stdClass;
+		$class->User->Comment->{'1'}->id = 2;
+		$class->User->Comment->{'1'}->article_id = 1;
+		$class->User->Comment->{'1'}->user_id = 2;
+		$class->User->Comment->{'1'}->comment = 'Second Comment for First Article';
+		$class->User->Comment->{'1'}->published = 'Y';
+		$class->User->Comment->{'1'}->created = '2007-03-18 10:47:23';
+		$class->User->Comment->{'1'}->updated = '2007-03-18 10:49:31';
+
+		$result = Set::reverse($class);
+		$this->assertIdentical($result, $expected);
+
+		$expected = array('User' => array('psword'=> 'whatever',
+						'Icon' => array('id'=> 851),
+						'Profile' => array('name' => 'Some Name', 'address' => 'Some Address'),
+						'Comment' => array(
+								array('id' => 1, 'article_id' => 1, 'user_id' => 1, 'comment' => 'First Comment for First Article', 'published' => 'Y', 'created' => '2007-03-18 10:47:23', 'updated' => '2007-03-18 10:49:31'),
+								array('id' => 2, 'article_id' => 1, 'user_id' => 2, 'comment' => 'Second Comment for First Article', 'published' => 'Y', 'created' => '2007-03-18 10:47:23', 'updated' => '2007-03-18 10:49:31'))));
+
+		$class = new stdClass;
+		$class->User = new stdClass;
+		$class->User->psword = 'whatever';
+		$class->User->Icon = new stdClass;
+		$class->User->Icon->id = 851;
+		$class->User->Profile = new stdClass;
+		$class->User->Profile->name = 'Some Name';
+		$class->User->Profile->address = 'Some Address';
+		$class->User->Comment = array();
+		$comment = new stdClass;
+		$comment->id = 1;
+		$comment->article_id = 1;
+		$comment->user_id = 1;
+		$comment->comment = 'First Comment for First Article';
+		$comment->published = 'Y';
+		$comment->created = '2007-03-18 10:47:23';
+		$comment->updated = '2007-03-18 10:49:31';
+		$comment2 = new stdClass;
+		$comment2->id = 2;
+		$comment2->article_id = 1;
+		$comment2->user_id = 2;
+		$comment2->comment = 'Second Comment for First Article';
+		$comment2->published = 'Y';
+		$comment2->created = '2007-03-18 10:47:23';
+		$comment2->updated = '2007-03-18 10:49:31';
+		$class->User->Comment =  array($comment, $comment2);
+		$result = Set::reverse($class);
+		$this->assertIdentical($result, $expected);
+
+		$model = new Model(array('id' => false, 'name' => 'Model', 'table' => false));
+		$expected = array(
+			'Behaviors' => array('modelName' => 'Model', '_attached' => array(), '_disabled' => array(), '__methods' => array(), '__mappedMethods' => array()),
+			'useDbConfig' => 'default', 'useTable' => false, 'displayField' => null, 'id' => false, 'data' => array(), 'table' => 'models', 'primaryKey' => 'id', '_schema' => null, 'validate' => array(),
+			'validationErrors' => array(), 'tablePrefix' => null, 'name' => 'Model', 'alias' => 'Model', 'tableToModel' => array(), 'logTransactions' => false, 'cacheQueries' => false,
+			'belongsTo' => array(), 'hasOne' =>  array(), 'hasMany' =>  array(), 'hasAndBelongsToMany' =>  array(), 'actsAs' => null, 'whitelist' =>  array(), 'cacheSources' => true,
+			'findQueryType' => null, 'recursive' => 1, 'order' => null, 'virtualFields' => array(),
+			'__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')),
+			'__associations' => array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany'), '__backAssociation' => array(), '__insertID' => null, '__numRows' => null, '__affectedRows' => null,
+				'_findMethods' => array('all' => true, 'first' => true, 'count' => true, 'neighbors' => true, 'list' => true, 'threaded' => true));
+		$result = Set::reverse($model);
+
+		ksort($result);
+		ksort($expected);
+
+		$this->assertIdentical($result, $expected);
+
+		$class = new stdClass;
+		$class->User = new stdClass;
+		$class->User->id = 100;
+		$class->someString = 'this is some string';
+		$class->Profile = new stdClass;
+		$class->Profile->name = 'Joe Mamma';
+
+		$result = Set::reverse($class);
+		$expected = array('User' => array('id' => '100'), 'someString'=> 'this is some string', 'Profile' => array('name' => 'Joe Mamma'));
+		$this->assertEqual($result, $expected);
+
+		$class = new stdClass;
+		$class->User = new stdClass;
+		$class->User->id = 100;
+		$class->User->_name_ = 'User';
+		$class->Profile = new stdClass;
+		$class->Profile->name = 'Joe Mamma';
+		$class->Profile->_name_ = 'Profile';
+
+		$result = Set::reverse($class);
+		$expected = array('User' => array('id' => '100'), 'Profile' => array('name' => 'Joe Mamma'));
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testFormatting method
+ *
+ * @access public
+ * @return void
+ */
+	function testFormatting() {
+		$data = array(
+			array('Person' => array('first_name' => 'Nate', 'last_name' => 'Abele', 'city' => 'Boston', 'state' => 'MA', 'something' => '42')),
+			array('Person' => array('first_name' => 'Larry', 'last_name' => 'Masters', 'city' => 'Boondock', 'state' => 'TN', 'something' => '{0}')),
+			array('Person' => array('first_name' => 'Garrett', 'last_name' => 'Woodworth', 'city' => 'Venice Beach', 'state' => 'CA', 'something' => '{1}')));
+
+		$result = Set::format($data, '{1}, {0}', array('{n}.Person.first_name', '{n}.Person.last_name'));
+		$expected = array('Abele, Nate', 'Masters, Larry', 'Woodworth, Garrett');
+		$this->assertEqual($result, $expected);
+
+		$result = Set::format($data, '{0}, {1}', array('{n}.Person.last_name', '{n}.Person.first_name'));
+		$this->assertEqual($result, $expected);
+
+		$result = Set::format($data, '{0}, {1}', array('{n}.Person.city', '{n}.Person.state'));
+		$expected = array('Boston, MA', 'Boondock, TN', 'Venice Beach, CA');
+		$this->assertEqual($result, $expected);
+
+		$result = Set::format($data, '{{0}, {1}}', array('{n}.Person.city', '{n}.Person.state'));
+		$expected = array('{Boston, MA}', '{Boondock, TN}', '{Venice Beach, CA}');
+		$this->assertEqual($result, $expected);
+
+		$result = Set::format($data, '{{0}, {1}}', array('{n}.Person.something', '{n}.Person.something'));
+		$expected = array('{42, 42}', '{{0}, {0}}', '{{1}, {1}}');
+		$this->assertEqual($result, $expected);
+
+		$result = Set::format($data, '{%2$d, %1$s}', array('{n}.Person.something', '{n}.Person.something'));
+		$expected = array('{42, 42}', '{0, {0}}', '{0, {1}}');
+		$this->assertEqual($result, $expected);
+
+		$result = Set::format($data, '{%1$s, %1$s}', array('{n}.Person.something', '{n}.Person.something'));
+		$expected = array('{42, 42}', '{{0}, {0}}', '{{1}, {1}}');
+		$this->assertEqual($result, $expected);
+
+		$result = Set::format($data, '%2$d, %1$s', array('{n}.Person.first_name', '{n}.Person.something'));
+		$expected = array('42, Nate', '0, Larry', '0, Garrett');
+		$this->assertEqual($result, $expected);
+
+		$result = Set::format($data, '%1$s, %2$d', array('{n}.Person.first_name', '{n}.Person.something'));
+		$expected = array('Nate, 42', 'Larry, 0', 'Garrett, 0');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testFormattingNullValues method
+ *
+ * @return void
+ */
+	public function testFormattingNullValues() {
+		$data = array(
+			array('Person' => array('first_name' => 'Nate', 'last_name' => 'Abele', 'city' => 'Boston', 'state' => 'MA', 'something' => '42')),
+			array('Person' => array('first_name' => 'Larry', 'last_name' => 'Masters', 'city' => 'Boondock', 'state' => 'TN', 'something' => null)),
+			array('Person' => array('first_name' => 'Garrett', 'last_name' => 'Woodworth', 'city' => 'Venice Beach', 'state' => 'CA', 'something' => null)));
+
+		$result = Set::format($data, '%s', array('{n}.Person.something'));
+		$expected = array('42', '', '');
+		$this->assertEqual($expected, $result);
+
+		$result = Set::format($data, '{0}, {1}', array('{n}.Person.city', '{n}.Person.something'));
+		$expected = array('Boston, 42', 'Boondock, ', 'Venice Beach, ');
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * testCountDim method
+ *
+ * @access public
+ * @return void
+ */
+	function testCountDim() {
+		$data = array('one', '2', 'three');
+		$result = Set::countDim($data);
+		$this->assertEqual($result, 1);
+
+		$data = array('1' => '1.1', '2', '3');
+		$result = Set::countDim($data);
+		$this->assertEqual($result, 1);
+
+		$data = array('1' => array('1.1' => '1.1.1'), '2', '3' => array('3.1' => '3.1.1'));
+		$result = Set::countDim($data);
+		$this->assertEqual($result, 2);
+
+		$data = array('1' => '1.1', '2', '3' => array('3.1' => '3.1.1'));
+		$result = Set::countDim($data);
+		$this->assertEqual($result, 1);
+
+		$data = array('1' => '1.1', '2', '3' => array('3.1' => '3.1.1'));
+		$result = Set::countDim($data, true);
+		$this->assertEqual($result, 2);
+
+		$data = array('1' => array('1.1' => '1.1.1'), '2', '3' => array('3.1' => array('3.1.1' => '3.1.1.1')));
+		$result = Set::countDim($data);
+		$this->assertEqual($result, 2);
+
+		$data = array('1' => array('1.1' => '1.1.1'), '2', '3' => array('3.1' => array('3.1.1' => '3.1.1.1')));
+		$result = Set::countDim($data, true);
+		$this->assertEqual($result, 3);
+
+		$data = array('1' => array('1.1' => '1.1.1'), array('2' => array('2.1' => array('2.1.1' => '2.1.1.1'))), '3' => array('3.1' => array('3.1.1' => '3.1.1.1')));
+		$result = Set::countDim($data, true);
+		$this->assertEqual($result, 4);
+
+		$data = array('1' => array('1.1' => '1.1.1'), array('2' => array('2.1' => array('2.1.1' => array('2.1.1.1')))), '3' => array('3.1' => array('3.1.1' => '3.1.1.1')));
+		$result = Set::countDim($data, true);
+		$this->assertEqual($result, 5);
+
+		$data = array('1' => array('1.1' => '1.1.1'), array('2' => array('2.1' => array('2.1.1' => array('2.1.1.1' => '2.1.1.1.1')))), '3' => array('3.1' => array('3.1.1' => '3.1.1.1')));
+		$result = Set::countDim($data, true);
+		$this->assertEqual($result, 5);
+
+		$set = array('1' => array('1.1' => '1.1.1'), array('2' => array('2.1' => array('2.1.1' => array('2.1.1.1' => '2.1.1.1.1')))), '3' => array('3.1' => array('3.1.1' => '3.1.1.1')));
+		$result = Set::countDim($set, false, 0);
+		$this->assertEqual($result, 2);
+
+		$result = Set::countDim($set, true);
+		$this->assertEqual($result, 5);
+	}
+
+/**
+ * testMapNesting method
+ *
+ * @access public
+ * @return void
+ */
+	function testMapNesting() {
+		$expected = array(
+			array(
+				"IndexedPage" => array(
+					"id" => 1,
+					"url" => 'http://blah.com/',
+					'hash' => '68a9f053b19526d08e36c6a9ad150737933816a5',
+					'headers' => array(
+							'Date' => "Wed, 14 Nov 2007 15:51:42 GMT",
+							'Server' => "Apache",
+							'Expires' => "Thu, 19 Nov 1981 08:52:00 GMT",
+							'Cache-Control' => "private",
+							'Pragma' => "no-cache",
+							'Content-Type' => "text/html; charset=UTF-8",
+							'X-Original-Transfer-Encoding' => "chunked",
+							'Content-Length' => "50210",
+					),
+					'meta' => array(
+							'keywords' => array('testing','tests'),
+							'description'=>'describe me',
+					),
+					'get_vars' => '',
+					'post_vars' => array(),
+					'cookies' => array('PHPSESSID' => "dde9896ad24595998161ffaf9e0dbe2d"),
+					'redirect' => '',
+					'created' => "1195055503",
+					'updated' => "1195055503",
+				)
+			),
+			array(
+				"IndexedPage" => array(
+					"id" => 2,
+					"url" => 'http://blah.com/',
+					'hash' => '68a9f053b19526d08e36c6a9ad150737933816a5',
+					'headers' => array(
+						'Date' => "Wed, 14 Nov 2007 15:51:42 GMT",
+						'Server' => "Apache",
+						'Expires' => "Thu, 19 Nov 1981 08:52:00 GMT",
+						'Cache-Control' => "private",
+						'Pragma' => "no-cache",
+						'Content-Type' => "text/html; charset=UTF-8",
+						'X-Original-Transfer-Encoding' => "chunked",
+						'Content-Length' => "50210",
+					),
+					'meta' => array(
+							'keywords' => array('testing','tests'),
+							'description'=>'describe me',
+					),
+					'get_vars' => '',
+					'post_vars' => array(),
+					'cookies' => array('PHPSESSID' => "dde9896ad24595998161ffaf9e0dbe2d"),
+					'redirect' => '',
+					'created' => "1195055503",
+					'updated' => "1195055503",
+				),
+			)
+		);
+
+		$mapped = Set::map($expected);
+		$ids = array();
+
+		foreach($mapped as $object)	 {
+			$ids[] = $object->id;
+		}
+		$this->assertEqual($ids, array(1, 2));
+		$this->assertEqual(get_object_vars($mapped[0]->headers), $expected[0]['IndexedPage']['headers']);
+
+		$result = Set::reverse($mapped);
+		$this->assertIdentical($result, $expected);
+
+		$data = array(
+			array(
+				"IndexedPage" => array(
+					"id" => 1,
+					"url" => 'http://blah.com/',
+					'hash' => '68a9f053b19526d08e36c6a9ad150737933816a5',
+					'get_vars' => '',
+					'redirect' => '',
+					'created' => "1195055503",
+					'updated' => "1195055503",
+				)
+			),
+			array(
+				"IndexedPage" => array(
+					"id" => 2,
+					"url" => 'http://blah.com/',
+					'hash' => '68a9f053b19526d08e36c6a9ad150737933816a5',
+					'get_vars' => '',
+					'redirect' => '',
+					'created' => "1195055503",
+					'updated' => "1195055503",
+				),
+			)
+		);
+		$mapped = Set::map($data);
+
+		$expected = new stdClass();
+		$expected->_name_ = 'IndexedPage';
+		$expected->id = 2;
+		$expected->url = 'http://blah.com/';
+		$expected->hash = '68a9f053b19526d08e36c6a9ad150737933816a5';
+		$expected->get_vars = '';
+		$expected->redirect = '';
+		$expected->created = "1195055503";
+		$expected->updated = "1195055503";
+		$this->assertIdentical($mapped[1], $expected);
+
+		$ids = array();
+
+		foreach($mapped as $object)	 {
+			$ids[] = $object->id;
+		}
+		$this->assertEqual($ids, array(1, 2));
+
+		$result = Set::map(null);
+		$expected = null;
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testNestedMappedData method
+ *
+ * @access public
+ * @return void
+ */
+	function testNestedMappedData() {
+		$result = Set::map(array(
+				array(
+					'Post' => array('id' => '1', 'author_id' => '1', 'title' => 'First Post', 'body' => 'First Post Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'),
+					'Author' => array('id' => '1', 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31', 'test' => 'working'),
+				)
+				, array(
+					'Post' => array('id' => '2', 'author_id' => '3', 'title' => 'Second Post', 'body' => 'Second Post Body', 'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'),
+					'Author' => array('id' => '3', 'user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31', 'test' => 'working'),
+				)
+			));
+
+		$expected = new stdClass;
+		$expected->_name_ = 'Post';
+		$expected->id = '1';
+		$expected->author_id = '1';
+		$expected->title = 'First Post';
+		$expected->body = 'First Post Body';
+		$expected->published = 'Y';
+		$expected->created = "2007-03-18 10:39:23";
+		$expected->updated = "2007-03-18 10:41:31";
+
+		$expected->Author = new stdClass;
+		$expected->Author->id = '1';
+		$expected->Author->user = 'mariano';
+		$expected->Author->password = '5f4dcc3b5aa765d61d8327deb882cf99';
+		$expected->Author->created = "2007-03-17 01:16:23";
+		$expected->Author->updated = "2007-03-17 01:18:31";
+		$expected->Author->test = "working";
+		$expected->Author->_name_ = 'Author';
+
+		$expected2 = new stdClass;
+		$expected2->_name_ = 'Post';
+		$expected2->id = '2';
+		$expected2->author_id = '3';
+		$expected2->title = 'Second Post';
+		$expected2->body = 'Second Post Body';
+		$expected2->published = 'Y';
+		$expected2->created = "2007-03-18 10:41:23";
+		$expected2->updated = "2007-03-18 10:43:31";
+
+		$expected2->Author = new stdClass;
+		$expected2->Author->id = '3';
+		$expected2->Author->user = 'larry';
+		$expected2->Author->password = '5f4dcc3b5aa765d61d8327deb882cf99';
+		$expected2->Author->created = "2007-03-17 01:20:23";
+		$expected2->Author->updated = "2007-03-17 01:22:31";
+		$expected2->Author->test = "working";
+		$expected2->Author->_name_ = 'Author';
+
+		$test = array();
+		$test[0] = $expected;
+		$test[1] = $expected2;
+
+		$this->assertIdentical($test, $result);
+
+		$result = Set::map(
+				array(
+					'Post' => array('id' => '1', 'author_id' => '1', 'title' => 'First Post', 'body' => 'First Post Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'),
+					'Author' => array('id' => '1', 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31', 'test' => 'working'),
+				)
+			);
+		$expected = new stdClass;
+		$expected->_name_ = 'Post';
+		$expected->id = '1';
+		$expected->author_id = '1';
+		$expected->title = 'First Post';
+		$expected->body = 'First Post Body';
+		$expected->published = 'Y';
+		$expected->created = "2007-03-18 10:39:23";
+		$expected->updated = "2007-03-18 10:41:31";
+
+		$expected->Author = new stdClass;
+		$expected->Author->id = '1';
+		$expected->Author->user = 'mariano';
+		$expected->Author->password = '5f4dcc3b5aa765d61d8327deb882cf99';
+		$expected->Author->created = "2007-03-17 01:16:23";
+		$expected->Author->updated = "2007-03-17 01:18:31";
+		$expected->Author->test = "working";
+		$expected->Author->_name_ = 'Author';
+		$this->assertIdentical($expected, $result);
+
+		//Case where extra HABTM fields come back in a result
+		$data = array(
+			'User' => array(
+				'id' => 1,
+				'email' => 'user****@examp*****',
+				'first_name' => 'John',
+				'last_name' => 'Smith',
+			),
+			'Piece' => array(
+				array(
+					'id' => 1,
+					'title' => 'Moonlight Sonata',
+					'composer' => 'Ludwig van Beethoven',
+					'PiecesUser' => array(
+						'id' => 1,
+						'created' => '2008-01-01 00:00:00',
+						'modified' => '2008-01-01 00:00:00',
+						'piece_id' => 1,
+						'user_id' => 2,
+					)
+				),
+				array(
+					'id' => 2,
+					'title' => 'Moonlight Sonata 2',
+					'composer' => 'Ludwig van Beethoven',
+					'PiecesUser' => array(
+						'id' => 2,
+						'created' => '2008-01-01 00:00:00',
+						'modified' => '2008-01-01 00:00:00',
+						'piece_id' => 2,
+						'user_id' => 2,
+					)
+				)
+			)
+		);
+
+		$result = Set::map($data);
+
+		$expected = new stdClass();
+		$expected->_name_ = 'User';
+		$expected->id = 1;
+		$expected->email = 'user****@examp*****';
+		$expected->first_name = 'John';
+		$expected->last_name = 'Smith';
+
+		$piece = new stdClass();
+		$piece->id = 1;
+		$piece->title = 'Moonlight Sonata';
+		$piece->composer = 'Ludwig van Beethoven';
+
+		$piece->PiecesUser = new stdClass();
+		$piece->PiecesUser->id = 1;
+		$piece->PiecesUser->created = '2008-01-01 00:00:00';
+		$piece->PiecesUser->modified = '2008-01-01 00:00:00';
+		$piece->PiecesUser->piece_id = 1;
+		$piece->PiecesUser->user_id = 2;
+		$piece->PiecesUser->_name_ = 'PiecesUser';
+
+		$piece->_name_ = 'Piece';
+
+
+		$piece2 = new stdClass();
+		$piece2->id = 2;
+		$piece2->title = 'Moonlight Sonata 2';
+		$piece2->composer = 'Ludwig van Beethoven';
+
+		$piece2->PiecesUser = new stdClass();
+		$piece2->PiecesUser->id = 2;
+		$piece2->PiecesUser->created = '2008-01-01 00:00:00';
+		$piece2->PiecesUser->modified = '2008-01-01 00:00:00';
+		$piece2->PiecesUser->piece_id = 2;
+		$piece2->PiecesUser->user_id = 2;
+		$piece2->PiecesUser->_name_ = 'PiecesUser';
+
+		$piece2->_name_ = 'Piece';
+
+		$expected->Piece = array($piece, $piece2);
+
+		$this->assertIdentical($expected, $result);
+
+		//Same data, but should work if _name_ has been manually defined:
+		$data = array(
+			'User' => array(
+				'id' => 1,
+				'email' => 'user****@examp*****',
+				'first_name' => 'John',
+				'last_name' => 'Smith',
+				'_name_' => 'FooUser',
+			),
+			'Piece' => array(
+				array(
+					'id' => 1,
+					'title' => 'Moonlight Sonata',
+					'composer' => 'Ludwig van Beethoven',
+					'_name_' => 'FooPiece',
+					'PiecesUser' => array(
+						'id' => 1,
+						'created' => '2008-01-01 00:00:00',
+						'modified' => '2008-01-01 00:00:00',
+						'piece_id' => 1,
+						'user_id' => 2,
+						'_name_' => 'FooPiecesUser',
+					)
+				),
+				array(
+					'id' => 2,
+					'title' => 'Moonlight Sonata 2',
+					'composer' => 'Ludwig van Beethoven',
+					'_name_' => 'FooPiece',
+					'PiecesUser' => array(
+						'id' => 2,
+						'created' => '2008-01-01 00:00:00',
+						'modified' => '2008-01-01 00:00:00',
+						'piece_id' => 2,
+						'user_id' => 2,
+						'_name_' => 'FooPiecesUser',
+					)
+				)
+			)
+		);
+
+		$result = Set::map($data);
+
+		$expected = new stdClass();
+		$expected->_name_ = 'FooUser';
+		$expected->id = 1;
+		$expected->email = 'user****@examp*****';
+		$expected->first_name = 'John';
+		$expected->last_name = 'Smith';
+
+		$piece = new stdClass();
+		$piece->id = 1;
+		$piece->title = 'Moonlight Sonata';
+		$piece->composer = 'Ludwig van Beethoven';
+		$piece->_name_ = 'FooPiece';
+		$piece->PiecesUser = new stdClass();
+		$piece->PiecesUser->id = 1;
+		$piece->PiecesUser->created = '2008-01-01 00:00:00';
+		$piece->PiecesUser->modified = '2008-01-01 00:00:00';
+		$piece->PiecesUser->piece_id = 1;
+		$piece->PiecesUser->user_id = 2;
+		$piece->PiecesUser->_name_ = 'FooPiecesUser';
+
+		$piece2 = new stdClass();
+		$piece2->id = 2;
+		$piece2->title = 'Moonlight Sonata 2';
+		$piece2->composer = 'Ludwig van Beethoven';
+		$piece2->_name_ = 'FooPiece';
+		$piece2->PiecesUser = new stdClass();
+		$piece2->PiecesUser->id = 2;
+		$piece2->PiecesUser->created = '2008-01-01 00:00:00';
+		$piece2->PiecesUser->modified = '2008-01-01 00:00:00';
+		$piece2->PiecesUser->piece_id = 2;
+		$piece2->PiecesUser->user_id = 2;
+		$piece2->PiecesUser->_name_ = 'FooPiecesUser';
+
+		$expected->Piece = array($piece, $piece2);
+
+		$this->assertIdentical($expected, $result);
+	}
+
+/**
+ * testPushDiff method
+ *
+ * @access public
+ * @return void
+ */
+	function testPushDiff() {
+		$array1 = array('ModelOne' => array('id'=>1001, 'field_one'=>'a1.m1.f1', 'field_two'=>'a1.m1.f2'));
+		$array2 = array('ModelTwo' => array('id'=>1002, 'field_one'=>'a2.m2.f1', 'field_two'=>'a2.m2.f2'));
+
+		$result = Set::pushDiff($array1, $array2);
+
+		$this->assertIdentical($result, $array1 + $array2);
+
+		$array3 = array('ModelOne' => array('id'=>1003, 'field_one'=>'a3.m1.f1', 'field_two'=>'a3.m1.f2', 'field_three'=>'a3.m1.f3'));
+		$result = Set::pushDiff($array1, $array3);
+
+		$expected = array('ModelOne' => array('id'=>1001, 'field_one'=>'a1.m1.f1', 'field_two'=>'a1.m1.f2', 'field_three'=>'a3.m1.f3'));
+		$this->assertIdentical($result, $expected);
+
+
+		$array1 = array(
+				0 => array('ModelOne' => array('id'=>1001, 'field_one'=>'s1.0.m1.f1', 'field_two'=>'s1.0.m1.f2')),
+				1 => array('ModelTwo' => array('id'=>1002, 'field_one'=>'s1.1.m2.f2', 'field_two'=>'s1.1.m2.f2')));
+		$array2 = array(
+				0 => array('ModelOne' => array('id'=>1001, 'field_one'=>'s2.0.m1.f1', 'field_two'=>'s2.0.m1.f2')),
+				1 => array('ModelTwo' => array('id'=>1002, 'field_one'=>'s2.1.m2.f2', 'field_two'=>'s2.1.m2.f2')));
+
+		$result = Set::pushDiff($array1, $array2);
+		$this->assertIdentical($result, $array1);
+
+		$array3 = array(0 => array('ModelThree' => array('id'=>1003, 'field_one'=>'s3.0.m3.f1', 'field_two'=>'s3.0.m3.f2')));
+
+		$result = Set::pushDiff($array1, $array3);
+		$expected = array(
+					0 => array('ModelOne' => array('id'=>1001, 'field_one'=>'s1.0.m1.f1', 'field_two'=>'s1.0.m1.f2'),
+						'ModelThree' => array('id'=>1003, 'field_one'=>'s3.0.m3.f1', 'field_two'=>'s3.0.m3.f2')),
+					1 => array('ModelTwo' => array('id'=>1002, 'field_one'=>'s1.1.m2.f2', 'field_two'=>'s1.1.m2.f2')));
+		$this->assertIdentical($result, $expected);
+
+		$result = Set::pushDiff($array1, null);
+		$this->assertIdentical($result, $array1);
+
+		$result = Set::pushDiff($array1, $array2);
+		$this->assertIdentical($result, $array1+$array2);
+	}
+
+/**
+ * testSetApply method
+ * @access public
+ * @return void
+ *
+ */
+	function testApply() {
+		$data = array(
+			array('Movie' => array('id' => 1, 'title' => 'movie 3', 'rating' => 5)),
+			array('Movie' => array('id' => 1, 'title' => 'movie 1', 'rating' => 1)),
+			array('Movie' => array('id' => 1, 'title' => 'movie 2', 'rating' => 3))
+		);
+
+		$result = Set::apply('/Movie/rating', $data, 'array_sum');
+		$expected = 9;
+		$this->assertEqual($result, $expected);
+
+		if (PHP5) {
+			$result = Set::apply('/Movie/rating', $data, 'array_product');
+			$expected = 15;
+			$this->assertEqual($result, $expected);
+		}
+
+		$result = Set::apply('/Movie/title', $data, 'ucfirst', array('type' => 'map'));
+		$expected = array('Movie 3', 'Movie 1', 'Movie 2');
+		$this->assertEqual($result, $expected);
+
+		$result = Set::apply('/Movie/title', $data, 'strtoupper', array('type' => 'map'));
+		$expected = array('MOVIE 3', 'MOVIE 1', 'MOVIE 2');
+		$this->assertEqual($result, $expected);
+
+		$result = Set::apply('/Movie/rating', $data, array('SetTest', '_method'), array('type' => 'reduce'));
+		$expected = 9;
+		$this->assertEqual($result, $expected);
+
+		$result = Set::apply('/Movie/rating', $data, 'strtoupper', array('type' => 'non existing type'));
+		$expected = null;
+		$this->assertEqual($result, $expected);
+
+	}
+
+/**
+ * Helper method to test Set::apply()
+ *
+ * @access protected
+ * @return void
+ */
+	function _method($val1, $val2) {
+		$val1 += $val2;
+		return $val1;
+	}
+
+/**
+ * testXmlSetReverse method
+ *
+ * @access public
+ * @return void
+ */
+	function testXmlSetReverse() {
+		App::import('Core', 'Xml');
+
+		$string = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+		<rss version="2.0">
+		  <channel>
+		  <title>Cake PHP Google Group</title>
+		  <link>http://groups.google.com/group/cake-php</link>
+		  <description>Search this group before posting anything. There are over 20,000 posts and it&amp;#39;s very likely your question was answered before. Visit the IRC channel #cakep****@irc***** for live chat with users and developers of Cake. If you post, tell us the version of Cake, PHP, and database.</description>
+		  <language>en</language>
+		  	<item>
+			  <title>constructng result array when using findall</title>
+			  <link>http://groups.google.com/group/cake-php/msg/49bc00f3bc651b4f</link>
+			  <description>i&#39;m using cakephp to construct a logical data model array that will be &lt;br&gt; passed to a flex app. I have the following model association: &lt;br&gt; ServiceDay-&amp;gt;(hasMany)ServiceTi me-&amp;gt;(hasMany)ServiceTimePrice. So what &lt;br&gt; the current output from my findall is something like this example: &lt;br&gt; &lt;p&gt;Array( &lt;br&gt; [0] =&amp;gt; Array(</description>
+			  <guid isPermaLink="true">http://groups.google.com/group/cake-php/msg/49bc00f3bc651b4f</guid>
+			  <author>bmil.****@gmail*****(bpscrugs)</author>
+			  <pubDate>Fri, 28 Dec 2007 00:44:14 UT</pubDate>
+			  </item>
+			  <item>
+			  <title>Re: share views between actions?</title>
+			  <link>http://groups.google.com/group/cake-php/msg/8b350d898707dad8</link>
+			  <description>Then perhaps you might do us all a favour and refrain from replying to &lt;br&gt; things you do not understand. That goes especially for asinine comments. &lt;br&gt; Indeed. &lt;br&gt; To sum up: &lt;br&gt; No comment. &lt;br&gt; In my day, a simple &amp;quot;RTFM&amp;quot; would suffice. I&#39;ll keep in mind to ignore any &lt;br&gt; further responses from you. &lt;br&gt; You (and I) were referring to the *online documentation*, not other</description>
+			  <guid isPermaLink="true">http://groups.google.com/group/cake-php/msg/8b350d898707dad8</guid>
+			  <author>subtr****@gmail*****(subtropolis zijn)</author>
+			  <pubDate>Fri, 28 Dec 2007 00:45:01 UT</pubDate>
+			 </item>
+		</channel>
+		</rss>';
+		$xml = new Xml($string);
+		$result = Set::reverse($xml);
+		$expected = array('Rss' => array(
+			'version' => '2.0',
+			'Channel' => array(
+				'title' => 'Cake PHP Google Group',
+				'link' => 'http://groups.google.com/group/cake-php',
+				'description' => 'Search this group before posting anything. There are over 20,000 posts and it&#39;s very likely your question was answered before. Visit the IRC channel #cakep****@irc***** for live chat with users and developers of Cake. If you post, tell us the version of Cake, PHP, and database.',
+				'language' => 'en',
+				'Item' => array(
+					array(
+						'title' => 'constructng result array when using findall',
+						'link' => 'http://groups.google.com/group/cake-php/msg/49bc00f3bc651b4f',
+						'description' => "i'm using cakephp to construct a logical data model array that will be <br> passed to a flex app. I have the following model association: <br> ServiceDay-&gt;(hasMany)ServiceTi me-&gt;(hasMany)ServiceTimePrice. So what <br> the current output from my findall is something like this example: <br><p>Array( <br> [0] =&gt; Array(",
+						'guid' => array('isPermaLink' => 'true', 'value' => 'http://groups.google.com/group/cake-php/msg/49bc00f3bc651b4f'),
+						'author' => 'bmil.****@gmail*****(bpscrugs)',
+						'pubDate' => 'Fri, 28 Dec 2007 00:44:14 UT',
+					),
+					array(
+						'title' => 'Re: share views between actions?',
+						'link' => 'http://groups.google.com/group/cake-php/msg/8b350d898707dad8',
+						'description' => 'Then perhaps you might do us all a favour and refrain from replying to <br> things you do not understand. That goes especially for asinine comments. <br> Indeed. <br> To sum up: <br> No comment. <br> In my day, a simple &quot;RTFM&quot; would suffice. I\'ll keep in mind to ignore any <br> further responses from you. <br> You (and I) were referring to the *online documentation*, not other',
+						'guid' => array('isPermaLink' => 'true', 'value' => 'http://groups.google.com/group/cake-php/msg/8b350d898707dad8'),
+						'author' => 'subtr****@gmail*****(subtropolis zijn)',
+						'pubDate' => 'Fri, 28 Dec 2007 00:45:01 UT'
+					)
+				)
+			)
+		));
+		$this->assertEqual($result, $expected);
+		$string ='<data><post title="Title of this post" description="cool"/></data>';
+
+		$xml = new Xml($string);
+		$result = Set::reverse($xml);
+		$expected = array('Data' => array('Post' => array('title' => 'Title of this post', 'description' => 'cool')));
+		$this->assertEqual($result, $expected);
+
+		$xml = new Xml('<example><item><title>An example of a correctly reversed XMLNode</title><desc/></item></example>');
+		$result = Set::reverse($xml);
+		$expected = array('Example' =>
+			array(
+				'Item' => array(
+					'title' => 'An example of a correctly reversed XMLNode',
+					'desc' => array(),
+				)
+			)
+		);
+		$this->assertIdentical($result, $expected);
+
+		$xml = new Xml('<example><item attr="123"><titles><title>title1</title><title>title2</title></titles></item></example>');
+		$result = Set::reverse($xml);
+		$expected =
+			array('Example' => array(
+				'Item' => array(
+					'attr' => '123',
+					'Titles' => array(
+						'Title' => array('title1', 'title2')
+					)
+				)
+			)
+		);
+		$this->assertIdentical($result, $expected);
+
+		$xml = new Xml('<example attr="ex_attr"><item attr="123"><titles>list</titles>textforitems</item></example>');
+		$result = Set::reverse($xml);
+		$expected =
+			array('Example' => array(
+				'attr' => 'ex_attr',
+				'Item' => array(
+					'attr' => '123',
+					'titles' => 'list',
+					'value'  => 'textforitems'
+				)
+			)
+		);
+		$this->assertIdentical($result, $expected);
+
+		$string = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+		<rss version="2.0">
+		  <channel>
+		  <title>Cake PHP Google Group</title>
+		  <link>http://groups.google.com/group/cake-php</link>
+		  <description>Search this group before posting anything. There are over 20,000 posts and it&amp;#39;s very likely your question was answered before. Visit the IRC channel #cakep****@irc***** for live chat with users and developers of Cake. If you post, tell us the version of Cake, PHP, and database.</description>
+		  <language>en</language>
+		  	<item>
+			  <title>constructng result array when using findall</title>
+			  <link>http://groups.google.com/group/cake-php/msg/49bc00f3bc651b4f</link>
+			  <description>i&#39;m using cakephp to construct a logical data model array that will be &lt;br&gt; passed to a flex app. I have the following model association: &lt;br&gt; ServiceDay-&amp;gt;(hasMany)ServiceTi me-&amp;gt;(hasMany)ServiceTimePrice. So what &lt;br&gt; the current output from my findall is something like this example: &lt;br&gt; &lt;p&gt;Array( &lt;br&gt; [0] =&amp;gt; Array(</description>
+			  	<dc:creator>cakephp</dc:creator>
+				<category><![CDATA[cakephp]]></category>
+				<category><![CDATA[model]]></category>
+			  <guid isPermaLink="true">http://groups.google.com/group/cake-php/msg/49bc00f3bc651b4f</guid>
+			  <author>bmil.****@gmail*****(bpscrugs)</author>
+			  <pubDate>Fri, 28 Dec 2007 00:44:14 UT</pubDate>
+			  </item>
+			  <item>
+			  <title>Re: share views between actions?</title>
+			  <link>http://groups.google.com/group/cake-php/msg/8b350d898707dad8</link>
+			  <description>Then perhaps you might do us all a favour and refrain from replying to &lt;br&gt; things you do not understand. That goes especially for asinine comments. &lt;br&gt; Indeed. &lt;br&gt; To sum up: &lt;br&gt; No comment. &lt;br&gt; In my day, a simple &amp;quot;RTFM&amp;quot; would suffice. I&#39;ll keep in mind to ignore any &lt;br&gt; further responses from you. &lt;br&gt; You (and I) were referring to the *online documentation*, not other</description>
+			  	<dc:creator>cakephp</dc:creator>
+				<category><![CDATA[cakephp]]></category>
+				<category><![CDATA[model]]></category>
+			  <guid isPermaLink="true">http://groups.google.com/group/cake-php/msg/8b350d898707dad8</guid>
+			  <author>subtr****@gmail*****(subtropolis zijn)</author>
+			  <pubDate>Fri, 28 Dec 2007 00:45:01 UT</pubDate>
+			 </item>
+		</channel>
+		</rss>';
+
+		$xml = new Xml($string);
+		$result = Set::reverse($xml);
+
+		$expected = array('Rss' => array(
+			'version' => '2.0',
+			'Channel' => array(
+				'title' => 'Cake PHP Google Group',
+				'link' => 'http://groups.google.com/group/cake-php',
+				'description' => 'Search this group before posting anything. There are over 20,000 posts and it&#39;s very likely your question was answered before. Visit the IRC channel #cakep****@irc***** for live chat with users and developers of Cake. If you post, tell us the version of Cake, PHP, and database.',
+				'language' => 'en',
+				'Item' => array(
+					array(
+						'title' => 'constructng result array when using findall',
+						'link' => 'http://groups.google.com/group/cake-php/msg/49bc00f3bc651b4f',
+						'description' => "i'm using cakephp to construct a logical data model array that will be <br> passed to a flex app. I have the following model association: <br> ServiceDay-&gt;(hasMany)ServiceTi me-&gt;(hasMany)ServiceTimePrice. So what <br> the current output from my findall is something like this example: <br><p>Array( <br> [0] =&gt; Array(",
+						'creator' => 'cakephp',
+						'Category' => array('cakephp', 'model'),
+						'guid' => array('isPermaLink' => 'true', 'value' => 'http://groups.google.com/group/cake-php/msg/49bc00f3bc651b4f'),
+						'author' => 'bmil.****@gmail*****(bpscrugs)',
+						'pubDate' => 'Fri, 28 Dec 2007 00:44:14 UT',
+					),
+					array(
+						'title' => 'Re: share views between actions?',
+						'link' => 'http://groups.google.com/group/cake-php/msg/8b350d898707dad8',
+						'description' => 'Then perhaps you might do us all a favour and refrain from replying to <br> things you do not understand. That goes especially for asinine comments. <br> Indeed. <br> To sum up: <br> No comment. <br> In my day, a simple &quot;RTFM&quot; would suffice. I\'ll keep in mind to ignore any <br> further responses from you. <br> You (and I) were referring to the *online documentation*, not other',
+						'creator' => 'cakephp',
+						'Category' => array('cakephp', 'model'),
+						'guid' => array('isPermaLink' => 'true', 'value' => 'http://groups.google.com/group/cake-php/msg/8b350d898707dad8'),
+						'author' => 'subtr****@gmail*****(subtropolis zijn)',
+						'pubDate' => 'Fri, 28 Dec 2007 00:45:01 UT'
+					)
+				)
+			)
+		));
+		$this->assertEqual($result, $expected);
+
+		$text = '<?xml version="1.0" encoding="UTF-8"?>
+		<XRDS xmlns="xri://$xrds">
+		<XRD xml:id="oauth" xmlns="xri://$XRD*($v*2.0)" version="2.0">
+			<Type>xri://$xrds*simple</Type>
+			<Expires>2008-04-13T07:34:58Z</Expires>
+			<Service>
+				<Type>http://oauth.net/core/1.0/endpoint/authorize</Type>
+				<Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
+				<Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
+				<URI priority="10">https://ma.gnolia.com/oauth/authorize</URI>
+				<URI priority="20">http://ma.gnolia.com/oauth/authorize</URI>
+			</Service>
+		</XRD>
+		<XRD xmlns="xri://$XRD*($v*2.0)" version="2.0">
+			<Type>xri://$xrds*simple</Type>
+				<Service priority="10">
+					<Type>http://oauth.net/discovery/1.0</Type>
+					<URI>#oauth</URI>
+				</Service>
+		</XRD>
+		</XRDS>';
+
+		$xml = new Xml($text);
+		$result = Set::reverse($xml);
+
+		$expected = array('XRDS' => array(
+			'xmlns' => 'xri://$xrds',
+			'XRD' => array(
+				array(
+					'xml:id' => 'oauth',
+					'xmlns' => 'xri://$XRD*($v*2.0)',
+					'version' => '2.0',
+					'Type' => 'xri://$xrds*simple',
+					'Expires' => '2008-04-13T07:34:58Z',
+					'Service' => array(
+						'Type' => array(
+							'http://oauth.net/core/1.0/endpoint/authorize',
+							'http://oauth.net/core/1.0/parameters/auth-header',
+							'http://oauth.net/core/1.0/parameters/uri-query'
+						),
+						'URI' => array(
+							array(
+								'value' => 'https://ma.gnolia.com/oauth/authorize',
+								'priority' => '10',
+							),
+							array(
+								'value' => 'http://ma.gnolia.com/oauth/authorize',
+								'priority' => '20'
+							)
+						)
+					)
+				),
+				array(
+					'xmlns' => 'xri://$XRD*($v*2.0)',
+					'version' => '2.0',
+					'Type' => 'xri://$xrds*simple',
+					'Service' => array(
+						'priority' => '10',
+						'Type' => 'http://oauth.net/discovery/1.0',
+						'URI' => '#oauth'
+					)
+				)
+			)
+		));
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testStrictKeyCheck method
+ *
+ * @access public
+ * @return void
+ */
+	function testStrictKeyCheck() {
+		$set = array('a' => 'hi');
+		$this->assertFalse(Set::check($set, 'a.b'));
+	}
+
+/**
+ * Tests Set::flatten
+ *
+ * @access public
+ * @return void
+ */
+	function testFlatten() {
+		$data = array('Larry', 'Curly', 'Moe');
+		$result = Set::flatten($data);
+		$this->assertEqual($result, $data);
+
+		$data[9] = 'Shemp';
+		$result = Set::flatten($data);
+		$this->assertEqual($result, $data);
+
+		$data = array(
+			array(
+				'Post' => array('id' => '1', 'author_id' => '1', 'title' => 'First Post'),
+				'Author' => array('id' => '1', 'user' => 'nate', 'password' => 'foo'),
+			),
+			array(
+				'Post' => array('id' => '2', 'author_id' => '3', 'title' => 'Second Post', 'body' => 'Second Post Body'),
+				'Author' => array('id' => '3', 'user' => 'larry', 'password' => null),
+			)
+		);
+
+		$result = Set::flatten($data);
+		$expected = array(
+			'0.Post.id' => '1', '0.Post.author_id' => '1', '0.Post.title' => 'First Post', '0.Author.id' => '1',
+			'0.Author.user' => 'nate', '0.Author.password' => 'foo', '1.Post.id' => '2', '1.Post.author_id' => '3',
+			'1.Post.title' => 'Second Post', '1.Post.body' => 'Second Post Body', '1.Author.id' => '3',
+			'1.Author.user' => 'larry', '1.Author.password' => null
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test normalization
+ *
+ * @return void
+ */
+	function testNormalizeStrings() {
+		$result = Set::normalize('one,two,three');
+		$expected = array('one' => null, 'two' => null, 'three' => null);
+		$this->assertEqual($expected, $result);
+
+		$result = Set::normalize('one two three', true, ' ');
+		$expected = array('one' => null, 'two' => null, 'three' => null);
+		$this->assertEqual($expected, $result);
+
+		$result = Set::normalize('one  ,  two   ,  three   ', true, ',', true);
+		$expected = array('one' => null, 'two' => null, 'three' => null);
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * test normalizing arrays
+ *
+ * @return void
+ */
+	function testNormalizeArrays() {
+		$result = Set::normalize(array('one', 'two', 'three'));
+		$expected = array('one' => null, 'two' => null, 'three' => null);
+		$this->assertEqual($expected, $result);
+
+		$result = Set::normalize(array('one', 'two', 'three'), false);
+		$expected = array('one', 'two', 'three');
+		$this->assertEqual($expected, $result);
+
+		$result = Set::normalize(array('one' => 1, 'two' => 2, 'three' => 3, 'four'), false);
+		$expected = array('one' => 1, 'two' => 2, 'three' => 3, 'four' => null);
+		$this->assertEqual($expected, $result);
+
+		$result = Set::normalize(array('one' => 1, 'two' => 2, 'three' => 3, 'four'));
+		$expected = array('one' => 1, 'two' => 2, 'three' => 3, 'four' => null);
+		$this->assertEqual($expected, $result);
+
+		$result = Set::normalize(array('one' => array('a', 'b', 'c' => 'cee'), 'two' => 2, 'three'));
+		$expected = array('one' => array('a', 'b', 'c' => 'cee'), 'two' => 2, 'three' => null);
+		$this->assertEqual($expected, $result);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/string.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/string.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/string.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,310 @@
+<?php
+/**
+ * StringTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.5432
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Core', 'String');
+
+/**
+ * StringTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class StringTest extends CakeTestCase {
+
+/**
+ * testUuidGeneration method
+ *
+ * @access public
+ * @return void
+ */
+	function testUuidGeneration() {
+		$result = String::uuid();
+		$pattern = "/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/";
+		$match = preg_match($pattern, $result);
+		$this->assertTrue($match);
+	}
+
+/**
+ * testMultipleUuidGeneration method
+ *
+ * @access public
+ * @return void
+ */
+	function testMultipleUuidGeneration() {
+		$check = array();
+		$count = mt_rand(10, 1000);
+		$pattern = "/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/";
+
+		for($i = 0; $i < $count; $i++) {
+			$result = String::uuid();
+			$match = preg_match($pattern, $result);
+			$this->assertTrue($match);
+			$this->assertFalse(in_array($result, $check));
+			$check[] = $result;
+		}
+	}
+
+/**
+ * testInsert method
+ *
+ * @access public
+ * @return void
+ */
+	function testInsert() {
+		$string = 'some string';
+		$expected = 'some string';
+		$result = String::insert($string, array());
+		$this->assertEqual($result, $expected);
+
+		$string = '2 + 2 = :sum. Cake is :adjective.';
+		$expected = '2 + 2 = 4. Cake is yummy.';
+		$result = String::insert($string, array('sum' => '4', 'adjective' => 'yummy'));
+		$this->assertEqual($result, $expected);
+
+		$string = '2 + 2 = %sum. Cake is %adjective.';
+		$result = String::insert($string, array('sum' => '4', 'adjective' => 'yummy'), array('before' => '%'));
+		$this->assertEqual($result, $expected);
+
+		$string = '2 + 2 = 2sum2. Cake is 9adjective9.';
+		$result = String::insert($string, array('sum' => '4', 'adjective' => 'yummy'), array('format' => '/([\d])%s\\1/'));
+		$this->assertEqual($result, $expected);
+
+		$string = '2 + 2 = 12sum21. Cake is 23adjective45.';
+		$expected = '2 + 2 = 4. Cake is 23adjective45.';
+		$result = String::insert($string, array('sum' => '4', 'adjective' => 'yummy'), array('format' => '/([\d])([\d])%s\\2\\1/'));
+		$this->assertEqual($result, $expected);
+
+		$string = ':web :web_site';
+		$expected = 'www http';
+		$result = String::insert($string, array('web' => 'www', 'web_site' => 'http'));
+		$this->assertEqual($result, $expected);
+
+		$string = '2 + 2 = <sum. Cake is <adjective>.';
+		$expected = '2 + 2 = <sum. Cake is yummy.';
+		$result = String::insert($string, array('sum' => '4', 'adjective' => 'yummy'), array('before' => '<', 'after' => '>'));
+		$this->assertEqual($result, $expected);
+
+		$string = '2 + 2 = \:sum. Cake is :adjective.';
+		$expected = '2 + 2 = :sum. Cake is yummy.';
+		$result = String::insert($string, array('sum' => '4', 'adjective' => 'yummy'));
+		$this->assertEqual($result, $expected);
+
+		$string = '2 + 2 = !:sum. Cake is :adjective.';
+		$result = String::insert($string, array('sum' => '4', 'adjective' => 'yummy'), array('escape' => '!'));
+		$this->assertEqual($result, $expected);
+
+		$string = '2 + 2 = \%sum. Cake is %adjective.';
+		$expected = '2 + 2 = %sum. Cake is yummy.';
+		$result = String::insert($string, array('sum' => '4', 'adjective' => 'yummy'), array('before' => '%'));
+		$this->assertEqual($result, $expected);
+
+		$string = ':a :b \:a :a';
+		$expected = '1 2 :a 1';
+		$result = String::insert($string, array('a' => 1, 'b' => 2));
+		$this->assertEqual($result, $expected);
+
+		$string = ':a :b :c';
+		$expected = '2 3';
+		$result = String::insert($string, array('b' => 2, 'c' => 3), array('clean' => true));
+		$this->assertEqual($result, $expected);
+
+		$string = ':a :b :c';
+		$expected = '1 3';
+		$result = String::insert($string, array('a' => 1, 'c' => 3), array('clean' => true));
+		$this->assertEqual($result, $expected);
+
+		$string = ':a :b :c';
+		$expected = '2 3';
+		$result = String::insert($string, array('b' => 2, 'c' => 3), array('clean' => true));
+		$this->assertEqual($result, $expected);
+
+		$string = ':a, :b and :c';
+		$expected = '2 and 3';
+		$result = String::insert($string, array('b' => 2, 'c' => 3), array('clean' => true));
+		$this->assertEqual($result, $expected);
+
+		$string = '":a, :b and :c"';
+		$expected = '"1, 2"';
+		$result = String::insert($string, array('a' => 1, 'b' => 2), array('clean' => true));
+		$this->assertEqual($result, $expected);
+
+		$string = '"${a}, ${b} and ${c}"';
+		$expected = '"1, 2"';
+		$result = String::insert($string, array('a' => 1, 'b' => 2), array('before' => '${', 'after' => '}', 'clean' => true));
+		$this->assertEqual($result, $expected);
+
+		$string = '<img src=":src" alt=":alt" class="foo :extra bar"/>';
+		$expected = '<img src="foo" class="foo bar"/>';
+		$result = String::insert($string, array('src' => 'foo'), array('clean' => 'html'));
+
+		$this->assertEqual($result, $expected);
+
+		$string = '<img src=":src" class=":no :extra"/>';
+		$expected = '<img src="foo"/>';
+		$result = String::insert($string, array('src' => 'foo'), array('clean' => 'html'));
+		$this->assertEqual($result, $expected);
+
+		$string = '<img src=":src" class=":no :extra"/>';
+		$expected = '<img src="foo" class="bar"/>';
+		$result = String::insert($string, array('src' => 'foo', 'extra' => 'bar'), array('clean' => 'html'));
+		$this->assertEqual($result, $expected);
+
+		$result = String::insert("this is a ? string", "test");
+		$expected = "this is a test string";
+		$this->assertEqual($result, $expected);
+
+		$result = String::insert("this is a ? string with a ? ? ?", array('long', 'few?', 'params', 'you know'));
+		$expected = "this is a long string with a few? params you know";
+		$this->assertEqual($result, $expected);
+
+		$result = String::insert('update saved_urls set url = :url where id = :id', array('url' => 'http://www.testurl.com/param1:url/param2:id','id' => 1));
+		$expected = "update saved_urls set url = http://www.testurl.com/param1:url/param2:id where id = 1";
+		$this->assertEqual($result, $expected);
+
+		$result = String::insert('update saved_urls set url = :url where id = :id', array('id' => 1, 'url' => 'http://www.testurl.com/param1:url/param2:id'));
+		$expected = "update saved_urls set url = http://www.testurl.com/param1:url/param2:id where id = 1";
+		$this->assertEqual($result, $expected);
+
+		$result = String::insert(':me cake. :subject :verb fantastic.', array('me' => 'I :verb', 'subject' => 'cake', 'verb' => 'is'));
+		$expected = "I :verb cake. cake is fantastic.";
+		$this->assertEqual($result, $expected);
+
+		$result = String::insert(':I.am: :not.yet: passing.', array('I.am' => 'We are'), array('before' => ':', 'after' => ':', 'clean' => array('replacement' => ' of course', 'method' => 'text')));
+		$expected = "We are of course passing.";
+		$this->assertEqual($result, $expected);
+
+		$result = String::insert(
+			':I.am: :not.yet: passing.',
+			array('I.am' => 'We are'),
+			array('before' => ':', 'after' => ':', 'clean' => true)
+		);
+		$expected = "We are passing.";
+		$this->assertEqual($result, $expected);
+
+		$result = String::insert('?-pended result', array('Pre'));
+		$expected = "Pre-pended result";
+		$this->assertEqual($result, $expected);
+
+		$string = 'switching :timeout / :timeout_count';
+		$expected = 'switching 5 / 10';
+		$result = String::insert($string, array('timeout' => 5, 'timeout_count' => 10));
+		$this->assertEqual($result, $expected);
+
+		$string = 'switching :timeout / :timeout_count';
+		$expected = 'switching 5 / 10';
+		$result = String::insert($string, array('timeout_count' => 10, 'timeout' => 5));
+		$this->assertEqual($result, $expected);
+
+		$string = 'switching :timeout_count by :timeout';
+		$expected = 'switching 10 by 5';
+		$result = String::insert($string, array('timeout' => 5, 'timeout_count' => 10));
+		$this->assertEqual($result, $expected);
+
+		$string = 'switching :timeout_count by :timeout';
+		$expected = 'switching 10 by 5';
+		$result = String::insert($string, array('timeout_count' => 10, 'timeout' => 5));
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test Clean Insert
+ *
+ * @return void
+ */
+	function testCleanInsert() {
+		$result = String::cleanInsert(':incomplete', array(
+			'clean' => true, 'before' => ':', 'after' => ''
+		));
+		$this->assertEqual($result, '');
+
+		$result = String::cleanInsert(':incomplete', array(
+			'clean' => array('method' => 'text', 'replacement' => 'complete'),
+			'before' => ':', 'after' => '')
+		);
+		$this->assertEqual($result, 'complete');
+
+		$result = String::cleanInsert(':in.complete', array(
+			'clean' => true, 'before' => ':', 'after' => ''
+		));
+		$this->assertEqual($result, '');
+
+		$result = String::cleanInsert(':in.complete and', array(
+			'clean' => true, 'before' => ':', 'after' => '')
+		);
+		$this->assertEqual($result, '');
+
+		$result = String::cleanInsert(':in.complete or stuff', array(
+			'clean' => true, 'before' => ':', 'after' => ''
+		));
+		$this->assertEqual($result, 'stuff');
+
+		$result = String::cleanInsert(
+			'<p class=":missing" id=":missing">Text here</p>',
+			array('clean' => 'html', 'before' => ':', 'after' => '')
+		);
+		$this->assertEqual($result, '<p>Text here</p>');
+	}
+
+/**
+ * Tests that non-insertable variables (i.e. arrays) are skipped when used as values in
+ * String::insert().
+ *
+ * @return void
+ */
+	function testAutoIgnoreBadInsertData() {
+		$data = array('foo' => 'alpha', 'bar' => 'beta', 'fale' => array());
+		$result = String::insert('(:foo > :bar || :fale!)', $data, array('clean' => 'text'));
+		$this->assertEqual($result, '(alpha > beta || !)');
+	}
+
+/**
+ * testTokenize method
+ *
+ * @access public
+ * @return void
+ */
+	function testTokenize() {
+		$result = String::tokenize('A,(short,boring test)');
+		$expected = array('A', '(short,boring test)');
+		$this->assertEqual($result, $expected);
+
+		$result = String::tokenize('A,(short,more interesting( test)');
+		$expected = array('A', '(short,more interesting( test)');
+		$this->assertEqual($result, $expected);
+
+		$result = String::tokenize('A,(short,very interesting( test))');
+		$expected = array('A', '(short,very interesting( test))');
+		$this->assertEqual($result, $expected);
+
+		$result = String::tokenize('"single tag"', ' ', '"', '"');
+		$expected = array('"single tag"');
+		$this->assertEqual($expected, $result);
+
+		$result = String::tokenize('tagA "single tag" tagB', ' ', '"', '"');
+		$expected = array('tagA', '"single tag"', 'tagB');
+		$this->assertEqual($expected, $result);
+	}
+	
+	function testReplaceWithQuestionMarkInString() {
+		$string = ':a, :b and :c?';
+		$expected = '2 and 3?';
+		$result = String::insert($string, array('b' => 2, 'c' => 3), array('clean' => true));
+		$this->assertEqual($expected, $result);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/test_manager.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/test_manager.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/test_manager.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,120 @@
+<?php
+/**
+ * TestManagerTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *								1785 E. Sahara Avenue, Suite 490-204
+ *								Las Vegas, Nevada 89104
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * TestManagerTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class TestManagerTest extends CakeTestCase {
+
+/**
+ * setUp method
+ *
+ * @return void
+ * @access public
+ */
+	function setUp() {
+		$this->TestManager =& new TestManager();
+		$this->Reporter =& new CakeHtmlReporter();
+	}
+
+/**
+ * testRunAllTests method
+ *
+ * @return void
+ * @access public
+ */
+	function testRunAllTests() {
+		$folder =& new Folder($this->TestManager->_getTestsPath());
+		$extension = str_replace('.', '\.', $this->TestManager->getExtension('test'));
+		$out = $folder->findRecursive('.*' . $extension);
+
+		$reporter =& new CakeHtmlReporter();
+		$list = $this->TestManager->runAllTests($reporter, true);
+
+		$this->assertEqual(count($out), count($list));
+	}
+
+/**
+ * testRunTestCase method
+ *
+ * @return void
+ * @access public
+ */
+	function testRunTestCase() {
+		$file = md5(time());
+		$result = $this->TestManager->runTestCase($file, $this->Reporter);
+		$this->assertError('Test case ' . $file . ' cannot be found');
+		$this->assertFalse($result);
+
+		$file = str_replace(CORE_TEST_CASES, '', __FILE__);
+		$result = $this->TestManager->runTestCase($file, $this->Reporter, true);
+		$this->assertTrue($result);
+	}
+
+/**
+ * testRunGroupTest method
+ *
+ * @return void
+ * @access public
+ */
+	function testRunGroupTest() {
+	}
+
+/**
+ * testAddTestCasesFromDirectory method
+ *
+ * @return void
+ * @access public
+ */
+	function testAddTestCasesFromDirectory() {
+	}
+
+/**
+ * testAddTestFile method
+ *
+ * @return void
+ * @access public
+ */
+	function testAddTestFile() {
+	}
+
+/**
+ * testGetTestCaseList method
+ *
+ * @return void
+ * @access public
+ */
+	function testGetTestCaseList() {
+	}
+
+/**
+ * testGetGroupTestList method
+ *
+ * @return void
+ * @access public
+ */
+	function testGetGroupTestList() {
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/validation.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/validation.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/validation.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,2212 @@
+<?php
+/**
+ * ValidationTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', 'Validation');
+
+/**
+ * CustomValidator class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class CustomValidator {
+
+/**
+ * Makes sure that a given $email address is valid and unique
+ *
+ * @param string $email
+ * @return boolean
+ * @access public
+ */
+	function customValidate($check) {
+		return preg_match('/^[0-9]{3}$/', $check);
+	}
+}
+
+/**
+ * TestNlValidation class
+ *
+ * Used to test pass through of Validation
+ *
+ * @package cake.tests.cases.libs
+ */
+class TestNlValidation {
+/**
+ * postal function, for testing postal pass through.
+ *
+ * @param string $check
+ * @return void
+ */
+	function postal($check) {
+		return true;
+	}
+/**
+ * ssn function for testing ssn pass through
+ *
+ * @return void
+ */
+	function ssn($check) {
+		return true;
+	}
+}
+
+/**
+ * TestDeValidation class
+ *
+ * Used to test pass through of Validation
+ *
+ * @package cake.tests.cases.libs
+ */
+class TestDeValidation {
+/**
+ * phone function, for testing phone pass through.
+ *
+ * @param string $check
+ * @return void
+ */
+	function phone($check) {
+		return true;
+	}
+}
+
+/**
+ * Test Case for Validation Class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class ValidationTest extends CakeTestCase {
+
+/**
+ * Validation property
+ *
+ * @var mixed null
+ * @access public
+ */
+	var $Validation = null;
+
+/**
+ * setup method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		$this->Validation =& Validation::getInstance();
+		$this->_appEncoding = Configure::read('App.encoding');
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		Configure::write('App.encoding', $this->_appEncoding);
+	}
+
+/**
+ * testNotEmpty method
+ *
+ * @access public
+ * @return void
+ */
+	function testNotEmpty() {
+		$this->assertTrue(Validation::notEmpty('abcdefg'));
+		$this->assertTrue(Validation::notEmpty('fasdf '));
+		$this->assertTrue(Validation::notEmpty('fooo'.chr(243).'blabla'));
+		$this->assertTrue(Validation::notEmpty('abçďĕʑʘπй'));
+		$this->assertTrue(Validation::notEmpty('José'));
+		$this->assertTrue(Validation::notEmpty('é'));
+		$this->assertTrue(Validation::notEmpty('π'));
+		$this->assertFalse(Validation::notEmpty("\t "));
+		$this->assertFalse(Validation::notEmpty(""));
+
+	}
+
+/**
+ * testNotEmptyISO88591Encoding method
+ *
+ * @return void
+ * @access public
+ */
+	function testNotEmptyISO88591AppEncoding() {
+		Configure::write('App.encoding', 'ISO-8859-1');
+		$this->assertTrue(Validation::notEmpty('abcdefg'));
+		$this->assertTrue(Validation::notEmpty('fasdf '));
+		$this->assertTrue(Validation::notEmpty('fooo'.chr(243).'blabla'));
+		$this->assertTrue(Validation::notEmpty('abçďĕʑʘπй'));
+		$this->assertTrue(Validation::notEmpty('José'));
+		$this->assertTrue(Validation::notEmpty(utf8_decode('José')));
+		$this->assertFalse(Validation::notEmpty("\t "));
+		$this->assertFalse(Validation::notEmpty(""));
+	}
+
+/**
+ * testAlphaNumeric method
+ *
+ * @access public
+ * @return void
+ */
+	function testAlphaNumeric() {
+		$this->assertTrue(Validation::alphaNumeric('frferrf'));
+		$this->assertTrue(Validation::alphaNumeric('12234'));
+		$this->assertTrue(Validation::alphaNumeric('1w2e2r3t4y'));
+		$this->assertTrue(Validation::alphaNumeric('0'));
+		$this->assertTrue(Validation::alphaNumeric('abçďĕʑʘπй'));
+		$this->assertTrue(Validation::alphaNumeric('ˇˆๆゞ'));
+		$this->assertTrue(Validation::alphaNumeric('אกあアꀀ豈'));
+		$this->assertTrue(Validation::alphaNumeric('Džᾈᾨ'));
+		$this->assertTrue(Validation::alphaNumeric('ÆΔΩЖÇ'));
+
+
+		$this->assertFalse(Validation::alphaNumeric('12 234'));
+		$this->assertFalse(Validation::alphaNumeric('dfd 234'));
+		$this->assertFalse(Validation::alphaNumeric("\n"));
+		$this->assertFalse(Validation::alphaNumeric("\t"));
+		$this->assertFalse(Validation::alphaNumeric("\r"));
+		$this->assertFalse(Validation::alphaNumeric(' '));
+		$this->assertFalse(Validation::alphaNumeric(''));
+	}
+
+/**
+ * testAlphaNumericPassedAsArray method
+ *
+ * @access public
+ * @return void
+ */
+	function testAlphaNumericPassedAsArray() {
+		$this->assertTrue(Validation::alphaNumeric(array('check' => 'frferrf')));
+		$this->assertTrue(Validation::alphaNumeric(array('check' => '12234')));
+		$this->assertTrue(Validation::alphaNumeric(array('check' => '1w2e2r3t4y')));
+		$this->assertTrue(Validation::alphaNumeric(array('check' => '0')));
+		$this->assertFalse(Validation::alphaNumeric(array('check' => '12 234')));
+		$this->assertFalse(Validation::alphaNumeric(array('check' => 'dfd 234')));
+		$this->assertFalse(Validation::alphaNumeric(array('check' => "\n")));
+		$this->assertFalse(Validation::alphaNumeric(array('check' => "\t")));
+		$this->assertFalse(Validation::alphaNumeric(array('check' => "\r")));
+		$this->assertFalse(Validation::alphaNumeric(array('check' =>  ' ')));
+		$this->assertFalse(Validation::alphaNumeric(array('check' =>  '')));
+	}
+
+/**
+ * testBetween method
+ *
+ * @access public
+ * @return void
+ */
+	function testBetween() {
+		$this->assertTrue(Validation::between('abcdefg', 1, 7));
+		$this->assertTrue(Validation::between('', 0, 7));
+		$this->assertTrue(Validation::between('אกあアꀀ豈', 1, 7));
+
+		$this->assertFalse(Validation::between('abcdefg', 1, 6));
+		$this->assertFalse(Validation::between('ÆΔΩЖÇ', 1, 3));
+	}
+
+/**
+ * testBlank method
+ *
+ * @access public
+ * @return void
+ */
+	function testBlank() {
+		$this->assertTrue(Validation::blank(''));
+		$this->assertTrue(Validation::blank(' '));
+		$this->assertTrue(Validation::blank("\n"));
+		$this->assertTrue(Validation::blank("\t"));
+		$this->assertTrue(Validation::blank("\r"));
+		$this->assertFalse(Validation::blank('    Blank'));
+		$this->assertFalse(Validation::blank('Blank'));
+	}
+
+/**
+ * testBlankAsArray method
+ *
+ * @access public
+ * @return void
+ */
+	function testBlankAsArray() {
+		$this->assertTrue(Validation::blank(array('check' => '')));
+		$this->assertTrue(Validation::blank(array('check' => ' ')));
+		$this->assertTrue(Validation::blank(array('check' => "\n")));
+		$this->assertTrue(Validation::blank(array('check' => "\t")));
+		$this->assertTrue(Validation::blank(array('check' => "\r")));
+		$this->assertFalse(Validation::blank(array('check' => '    Blank')));
+		$this->assertFalse(Validation::blank(array('check' => 'Blank')));
+	}
+
+/**
+ * testcc method
+ *
+ * @access public
+ * @return void
+ */
+	function testcc() {
+		//American Express
+		$this->assertTrue(Validation::cc('370482756063980', array('amex')));
+		$this->assertTrue(Validation::cc('349106433773483', array('amex')));
+		$this->assertTrue(Validation::cc('344671486204764', array('amex')));
+		$this->assertTrue(Validation::cc('344042544509943', array('amex')));
+		$this->assertTrue(Validation::cc('377147515754475', array('amex')));
+		$this->assertTrue(Validation::cc('375239372816422', array('amex')));
+		$this->assertTrue(Validation::cc('376294341957707', array('amex')));
+		$this->assertTrue(Validation::cc('341779292230411', array('amex')));
+		$this->assertTrue(Validation::cc('341646919853372', array('amex')));
+		$this->assertTrue(Validation::cc('348498616319346', array('amex')));
+		//BankCard
+		$this->assertTrue(Validation::cc('5610745867413420', array('bankcard')));
+		$this->assertTrue(Validation::cc('5610376649499352', array('bankcard')));
+		$this->assertTrue(Validation::cc('5610091936000694', array('bankcard')));
+		$this->assertTrue(Validation::cc('5602248780118788', array('bankcard')));
+		$this->assertTrue(Validation::cc('5610631567676765', array('bankcard')));
+		$this->assertTrue(Validation::cc('5602238211270795', array('bankcard')));
+		$this->assertTrue(Validation::cc('5610173951215470', array('bankcard')));
+		$this->assertTrue(Validation::cc('5610139705753702', array('bankcard')));
+		$this->assertTrue(Validation::cc('5602226032150551', array('bankcard')));
+		$this->assertTrue(Validation::cc('5602223993735777', array('bankcard')));
+		//Diners Club 14
+		$this->assertTrue(Validation::cc('30155483651028', array('diners')));
+		$this->assertTrue(Validation::cc('36371312803821', array('diners')));
+		$this->assertTrue(Validation::cc('38801277489875', array('diners')));
+		$this->assertTrue(Validation::cc('30348560464296', array('diners')));
+		$this->assertTrue(Validation::cc('30349040317708', array('diners')));
+		$this->assertTrue(Validation::cc('36567413559978', array('diners')));
+		$this->assertTrue(Validation::cc('36051554732702', array('diners')));
+		$this->assertTrue(Validation::cc('30391842198191', array('diners')));
+		$this->assertTrue(Validation::cc('30172682197745', array('diners')));
+		$this->assertTrue(Validation::cc('30162056566641', array('diners')));
+		$this->assertTrue(Validation::cc('30085066927745', array('diners')));
+		$this->assertTrue(Validation::cc('36519025221976', array('diners')));
+		$this->assertTrue(Validation::cc('30372679371044', array('diners')));
+		$this->assertTrue(Validation::cc('38913939150124', array('diners')));
+		$this->assertTrue(Validation::cc('36852899094637', array('diners')));
+		$this->assertTrue(Validation::cc('30138041971120', array('diners')));
+		$this->assertTrue(Validation::cc('36184047836838', array('diners')));
+		$this->assertTrue(Validation::cc('30057460264462', array('diners')));
+		$this->assertTrue(Validation::cc('38980165212050', array('diners')));
+		$this->assertTrue(Validation::cc('30356516881240', array('diners')));
+		$this->assertTrue(Validation::cc('38744810033182', array('diners')));
+		$this->assertTrue(Validation::cc('30173638706621', array('diners')));
+		$this->assertTrue(Validation::cc('30158334709185', array('diners')));
+		$this->assertTrue(Validation::cc('30195413721186', array('diners')));
+		$this->assertTrue(Validation::cc('38863347694793', array('diners')));
+		$this->assertTrue(Validation::cc('30275627009113', array('diners')));
+		$this->assertTrue(Validation::cc('30242860404971', array('diners')));
+		$this->assertTrue(Validation::cc('30081877595151', array('diners')));
+		$this->assertTrue(Validation::cc('38053196067461', array('diners')));
+		$this->assertTrue(Validation::cc('36520379984870', array('diners')));
+		//2004 MasterCard/Diners Club Alliance International 14
+		$this->assertTrue(Validation::cc('36747701998969', array('diners')));
+		$this->assertTrue(Validation::cc('36427861123159', array('diners')));
+		$this->assertTrue(Validation::cc('36150537602386', array('diners')));
+		$this->assertTrue(Validation::cc('36582388820610', array('diners')));
+		$this->assertTrue(Validation::cc('36729045250216', array('diners')));
+		//2004 MasterCard/Diners Club Alliance US & Canada 16
+		$this->assertTrue(Validation::cc('5597511346169950', array('diners')));
+		$this->assertTrue(Validation::cc('5526443162217562', array('diners')));
+		$this->assertTrue(Validation::cc('5577265786122391', array('diners')));
+		$this->assertTrue(Validation::cc('5534061404676989', array('diners')));
+		$this->assertTrue(Validation::cc('5545313588374502', array('diners')));
+		//Discover
+		$this->assertTrue(Validation::cc('6011802876467237', array('disc')));
+		$this->assertTrue(Validation::cc('6506432777720955', array('disc')));
+		$this->assertTrue(Validation::cc('6011126265283942', array('disc')));
+		$this->assertTrue(Validation::cc('6502187151579252', array('disc')));
+		$this->assertTrue(Validation::cc('6506600836002298', array('disc')));
+		$this->assertTrue(Validation::cc('6504376463615189', array('disc')));
+		$this->assertTrue(Validation::cc('6011440907005377', array('disc')));
+		$this->assertTrue(Validation::cc('6509735979634270', array('disc')));
+		$this->assertTrue(Validation::cc('6011422366775856', array('disc')));
+		$this->assertTrue(Validation::cc('6500976374623323', array('disc')));
+		//enRoute
+		$this->assertTrue(Validation::cc('201496944158937', array('enroute')));
+		$this->assertTrue(Validation::cc('214945833739665', array('enroute')));
+		$this->assertTrue(Validation::cc('214982692491187', array('enroute')));
+		$this->assertTrue(Validation::cc('214901395949424', array('enroute')));
+		$this->assertTrue(Validation::cc('201480676269187', array('enroute')));
+		$this->assertTrue(Validation::cc('214911922887807', array('enroute')));
+		$this->assertTrue(Validation::cc('201485025457250', array('enroute')));
+		$this->assertTrue(Validation::cc('201402662758866', array('enroute')));
+		$this->assertTrue(Validation::cc('214981579370225', array('enroute')));
+		$this->assertTrue(Validation::cc('201447595859877', array('enroute')));
+		//JCB 15 digit
+		$this->assertTrue(Validation::cc('210034762247893', array('jcb')));
+		$this->assertTrue(Validation::cc('180078671678892', array('jcb')));
+		$this->assertTrue(Validation::cc('180010559353736', array('jcb')));
+		$this->assertTrue(Validation::cc('210095474464258', array('jcb')));
+		$this->assertTrue(Validation::cc('210006675562188', array('jcb')));
+		$this->assertTrue(Validation::cc('210063299662662', array('jcb')));
+		$this->assertTrue(Validation::cc('180032506857825', array('jcb')));
+		$this->assertTrue(Validation::cc('210057919192738', array('jcb')));
+		$this->assertTrue(Validation::cc('180031358949367', array('jcb')));
+		$this->assertTrue(Validation::cc('180033802147846', array('jcb')));
+		//JCB 16 digit
+		$this->assertTrue(Validation::cc('3096806857839939', array('jcb')));
+		$this->assertTrue(Validation::cc('3158699503187091', array('jcb')));
+		$this->assertTrue(Validation::cc('3112549607186579', array('jcb')));
+		$this->assertTrue(Validation::cc('3112332922425604', array('jcb')));
+		$this->assertTrue(Validation::cc('3112001541159239', array('jcb')));
+		$this->assertTrue(Validation::cc('3112162495317841', array('jcb')));
+		$this->assertTrue(Validation::cc('3337562627732768', array('jcb')));
+		$this->assertTrue(Validation::cc('3337107161330775', array('jcb')));
+		$this->assertTrue(Validation::cc('3528053736003621', array('jcb')));
+		$this->assertTrue(Validation::cc('3528915255020360', array('jcb')));
+		$this->assertTrue(Validation::cc('3096786059660921', array('jcb')));
+		$this->assertTrue(Validation::cc('3528264799292320', array('jcb')));
+		$this->assertTrue(Validation::cc('3096469164130136', array('jcb')));
+		$this->assertTrue(Validation::cc('3112127443822853', array('jcb')));
+		$this->assertTrue(Validation::cc('3096849995802328', array('jcb')));
+		$this->assertTrue(Validation::cc('3528090735127407', array('jcb')));
+		$this->assertTrue(Validation::cc('3112101006819234', array('jcb')));
+		$this->assertTrue(Validation::cc('3337444428040784', array('jcb')));
+		$this->assertTrue(Validation::cc('3088043154151061', array('jcb')));
+		$this->assertTrue(Validation::cc('3088295969414866', array('jcb')));
+		$this->assertTrue(Validation::cc('3158748843158575', array('jcb')));
+		$this->assertTrue(Validation::cc('3158709206148538', array('jcb')));
+		$this->assertTrue(Validation::cc('3158365159575324', array('jcb')));
+		$this->assertTrue(Validation::cc('3158671691305165', array('jcb')));
+		$this->assertTrue(Validation::cc('3528523028771093', array('jcb')));
+		$this->assertTrue(Validation::cc('3096057126267870', array('jcb')));
+		$this->assertTrue(Validation::cc('3158514047166834', array('jcb')));
+		$this->assertTrue(Validation::cc('3528274546125962', array('jcb')));
+		$this->assertTrue(Validation::cc('3528890967705733', array('jcb')));
+		$this->assertTrue(Validation::cc('3337198811307545', array('jcb')));
+		//Maestro (debit card)
+		$this->assertTrue(Validation::cc('5020147409985219', array('maestro')));
+		$this->assertTrue(Validation::cc('5020931809905616', array('maestro')));
+		$this->assertTrue(Validation::cc('5020412965470224', array('maestro')));
+		$this->assertTrue(Validation::cc('5020129740944022', array('maestro')));
+		$this->assertTrue(Validation::cc('5020024696747943', array('maestro')));
+		$this->assertTrue(Validation::cc('5020581514636509', array('maestro')));
+		$this->assertTrue(Validation::cc('5020695008411987', array('maestro')));
+		$this->assertTrue(Validation::cc('5020565359718977', array('maestro')));
+		$this->assertTrue(Validation::cc('6339931536544062', array('maestro')));
+		$this->assertTrue(Validation::cc('6465028615704406', array('maestro')));
+		//Mastercard
+		$this->assertTrue(Validation::cc('5580424361774366', array('mc')));
+		$this->assertTrue(Validation::cc('5589563059318282', array('mc')));
+		$this->assertTrue(Validation::cc('5387558333690047', array('mc')));
+		$this->assertTrue(Validation::cc('5163919215247175', array('mc')));
+		$this->assertTrue(Validation::cc('5386742685055055', array('mc')));
+		$this->assertTrue(Validation::cc('5102303335960674', array('mc')));
+		$this->assertTrue(Validation::cc('5526543403964565', array('mc')));
+		$this->assertTrue(Validation::cc('5538725892618432', array('mc')));
+		$this->assertTrue(Validation::cc('5119543573129778', array('mc')));
+		$this->assertTrue(Validation::cc('5391174753915767', array('mc')));
+		$this->assertTrue(Validation::cc('5510994113980714', array('mc')));
+		$this->assertTrue(Validation::cc('5183720260418091', array('mc')));
+		$this->assertTrue(Validation::cc('5488082196086704', array('mc')));
+		$this->assertTrue(Validation::cc('5484645164161834', array('mc')));
+		$this->assertTrue(Validation::cc('5171254350337031', array('mc')));
+		$this->assertTrue(Validation::cc('5526987528136452', array('mc')));
+		$this->assertTrue(Validation::cc('5504148941409358', array('mc')));
+		$this->assertTrue(Validation::cc('5240793507243615', array('mc')));
+		$this->assertTrue(Validation::cc('5162114693017107', array('mc')));
+		$this->assertTrue(Validation::cc('5163104807404753', array('mc')));
+		$this->assertTrue(Validation::cc('5590136167248365', array('mc')));
+		$this->assertTrue(Validation::cc('5565816281038948', array('mc')));
+		$this->assertTrue(Validation::cc('5467639122779531', array('mc')));
+		$this->assertTrue(Validation::cc('5297350261550024', array('mc')));
+		$this->assertTrue(Validation::cc('5162739131368058', array('mc')));
+		//Solo 16
+		$this->assertTrue(Validation::cc('6767432107064987', array('solo')));
+		$this->assertTrue(Validation::cc('6334667758225411', array('solo')));
+		$this->assertTrue(Validation::cc('6767037421954068', array('solo')));
+		$this->assertTrue(Validation::cc('6767823306394854', array('solo')));
+		$this->assertTrue(Validation::cc('6334768185398134', array('solo')));
+		$this->assertTrue(Validation::cc('6767286729498589', array('solo')));
+		$this->assertTrue(Validation::cc('6334972104431261', array('solo')));
+		$this->assertTrue(Validation::cc('6334843427400616', array('solo')));
+		$this->assertTrue(Validation::cc('6767493947881311', array('solo')));
+		$this->assertTrue(Validation::cc('6767194235798817', array('solo')));
+		//Solo 18
+		$this->assertTrue(Validation::cc('676714834398858593', array('solo')));
+		$this->assertTrue(Validation::cc('676751666435130857', array('solo')));
+		$this->assertTrue(Validation::cc('676781908573924236', array('solo')));
+		$this->assertTrue(Validation::cc('633488724644003240', array('solo')));
+		$this->assertTrue(Validation::cc('676732252338067316', array('solo')));
+		$this->assertTrue(Validation::cc('676747520084495821', array('solo')));
+		$this->assertTrue(Validation::cc('633465488901381957', array('solo')));
+		$this->assertTrue(Validation::cc('633487484858610484', array('solo')));
+		$this->assertTrue(Validation::cc('633453764680740694', array('solo')));
+		$this->assertTrue(Validation::cc('676768613295414451', array('solo')));
+		//Solo 19
+		$this->assertTrue(Validation::cc('6767838565218340113', array('solo')));
+		$this->assertTrue(Validation::cc('6767760119829705181', array('solo')));
+		$this->assertTrue(Validation::cc('6767265917091593668', array('solo')));
+		$this->assertTrue(Validation::cc('6767938856947440111', array('solo')));
+		$this->assertTrue(Validation::cc('6767501945697390076', array('solo')));
+		$this->assertTrue(Validation::cc('6334902868716257379', array('solo')));
+		$this->assertTrue(Validation::cc('6334922127686425532', array('solo')));
+		$this->assertTrue(Validation::cc('6334933119080706440', array('solo')));
+		$this->assertTrue(Validation::cc('6334647959628261714', array('solo')));
+		$this->assertTrue(Validation::cc('6334527312384101382', array('solo')));
+		//Switch 16
+		$this->assertTrue(Validation::cc('5641829171515733', array('switch')));
+		$this->assertTrue(Validation::cc('5641824852820809', array('switch')));
+		$this->assertTrue(Validation::cc('6759129648956909', array('switch')));
+		$this->assertTrue(Validation::cc('6759626072268156', array('switch')));
+		$this->assertTrue(Validation::cc('5641822698388957', array('switch')));
+		$this->assertTrue(Validation::cc('5641827123105470', array('switch')));
+		$this->assertTrue(Validation::cc('5641823755819553', array('switch')));
+		$this->assertTrue(Validation::cc('5641821939587682', array('switch')));
+		$this->assertTrue(Validation::cc('4936097148079186', array('switch')));
+		$this->assertTrue(Validation::cc('5641829739125009', array('switch')));
+		$this->assertTrue(Validation::cc('5641822860725507', array('switch')));
+		$this->assertTrue(Validation::cc('4936717688865831', array('switch')));
+		$this->assertTrue(Validation::cc('6759487613615441', array('switch')));
+		$this->assertTrue(Validation::cc('5641821346840617', array('switch')));
+		$this->assertTrue(Validation::cc('5641825793417126', array('switch')));
+		$this->assertTrue(Validation::cc('5641821302759595', array('switch')));
+		$this->assertTrue(Validation::cc('6759784969918837', array('switch')));
+		$this->assertTrue(Validation::cc('5641824910667036', array('switch')));
+		$this->assertTrue(Validation::cc('6759139909636173', array('switch')));
+		$this->assertTrue(Validation::cc('6333425070638022', array('switch')));
+		$this->assertTrue(Validation::cc('5641823910382067', array('switch')));
+		$this->assertTrue(Validation::cc('4936295218139423', array('switch')));
+		$this->assertTrue(Validation::cc('6333031811316199', array('switch')));
+		$this->assertTrue(Validation::cc('4936912044763198', array('switch')));
+		$this->assertTrue(Validation::cc('4936387053303824', array('switch')));
+		$this->assertTrue(Validation::cc('6759535838760523', array('switch')));
+		$this->assertTrue(Validation::cc('6333427174594051', array('switch')));
+		$this->assertTrue(Validation::cc('5641829037102700', array('switch')));
+		$this->assertTrue(Validation::cc('5641826495463046', array('switch')));
+		$this->assertTrue(Validation::cc('6333480852979946', array('switch')));
+		$this->assertTrue(Validation::cc('5641827761302876', array('switch')));
+		$this->assertTrue(Validation::cc('5641825083505317', array('switch')));
+		$this->assertTrue(Validation::cc('6759298096003991', array('switch')));
+		$this->assertTrue(Validation::cc('4936119165483420', array('switch')));
+		$this->assertTrue(Validation::cc('4936190990500993', array('switch')));
+		$this->assertTrue(Validation::cc('4903356467384927', array('switch')));
+		$this->assertTrue(Validation::cc('6333372765092554', array('switch')));
+		$this->assertTrue(Validation::cc('5641821330950570', array('switch')));
+		$this->assertTrue(Validation::cc('6759841558826118', array('switch')));
+		$this->assertTrue(Validation::cc('4936164540922452', array('switch')));
+		//Switch 18
+		$this->assertTrue(Validation::cc('493622764224625174', array('switch')));
+		$this->assertTrue(Validation::cc('564182823396913535', array('switch')));
+		$this->assertTrue(Validation::cc('675917308304801234', array('switch')));
+		$this->assertTrue(Validation::cc('675919890024220298', array('switch')));
+		$this->assertTrue(Validation::cc('633308376862556751', array('switch')));
+		$this->assertTrue(Validation::cc('564182377633208779', array('switch')));
+		$this->assertTrue(Validation::cc('564182870014926787', array('switch')));
+		$this->assertTrue(Validation::cc('675979788553829819', array('switch')));
+		$this->assertTrue(Validation::cc('493668394358130935', array('switch')));
+		$this->assertTrue(Validation::cc('493637431790930965', array('switch')));
+		$this->assertTrue(Validation::cc('633321438601941513', array('switch')));
+		$this->assertTrue(Validation::cc('675913800898840986', array('switch')));
+		$this->assertTrue(Validation::cc('564182592016841547', array('switch')));
+		$this->assertTrue(Validation::cc('564182428380440899', array('switch')));
+		$this->assertTrue(Validation::cc('493696376827623463', array('switch')));
+		$this->assertTrue(Validation::cc('675977939286485757', array('switch')));
+		$this->assertTrue(Validation::cc('490302699502091579', array('switch')));
+		$this->assertTrue(Validation::cc('564182085013662230', array('switch')));
+		$this->assertTrue(Validation::cc('493693054263310167', array('switch')));
+		$this->assertTrue(Validation::cc('633321755966697525', array('switch')));
+		$this->assertTrue(Validation::cc('675996851719732811', array('switch')));
+		$this->assertTrue(Validation::cc('493699211208281028', array('switch')));
+		$this->assertTrue(Validation::cc('493697817378356614', array('switch')));
+		$this->assertTrue(Validation::cc('675968224161768150', array('switch')));
+		$this->assertTrue(Validation::cc('493669416873337627', array('switch')));
+		$this->assertTrue(Validation::cc('564182439172549714', array('switch')));
+		$this->assertTrue(Validation::cc('675926914467673598', array('switch')));
+		$this->assertTrue(Validation::cc('564182565231977809', array('switch')));
+		$this->assertTrue(Validation::cc('675966282607849002', array('switch')));
+		$this->assertTrue(Validation::cc('493691609704348548', array('switch')));
+		$this->assertTrue(Validation::cc('675933118546065120', array('switch')));
+		$this->assertTrue(Validation::cc('493631116677238592', array('switch')));
+		$this->assertTrue(Validation::cc('675921142812825938', array('switch')));
+		$this->assertTrue(Validation::cc('633338311815675113', array('switch')));
+		$this->assertTrue(Validation::cc('633323539867338621', array('switch')));
+		$this->assertTrue(Validation::cc('675964912740845663', array('switch')));
+		$this->assertTrue(Validation::cc('633334008833727504', array('switch')));
+		$this->assertTrue(Validation::cc('493631941273687169', array('switch')));
+		$this->assertTrue(Validation::cc('564182971729706785', array('switch')));
+		$this->assertTrue(Validation::cc('633303461188963496', array('switch')));
+		//Switch 19
+		$this->assertTrue(Validation::cc('6759603460617628716', array('switch')));
+		$this->assertTrue(Validation::cc('4936705825268647681', array('switch')));
+		$this->assertTrue(Validation::cc('5641829846600479183', array('switch')));
+		$this->assertTrue(Validation::cc('6759389846573792530', array('switch')));
+		$this->assertTrue(Validation::cc('4936189558712637603', array('switch')));
+		$this->assertTrue(Validation::cc('5641822217393868189', array('switch')));
+		$this->assertTrue(Validation::cc('4903075563780057152', array('switch')));
+		$this->assertTrue(Validation::cc('4936510653566569547', array('switch')));
+		$this->assertTrue(Validation::cc('4936503083627303364', array('switch')));
+		$this->assertTrue(Validation::cc('4936777334398116272', array('switch')));
+		$this->assertTrue(Validation::cc('5641823876900554860', array('switch')));
+		$this->assertTrue(Validation::cc('6759619236903407276', array('switch')));
+		$this->assertTrue(Validation::cc('6759011470269978117', array('switch')));
+		$this->assertTrue(Validation::cc('6333175833997062502', array('switch')));
+		$this->assertTrue(Validation::cc('6759498728789080439', array('switch')));
+		$this->assertTrue(Validation::cc('4903020404168157841', array('switch')));
+		$this->assertTrue(Validation::cc('6759354334874804313', array('switch')));
+		$this->assertTrue(Validation::cc('6759900856420875115', array('switch')));
+		$this->assertTrue(Validation::cc('5641827269346868860', array('switch')));
+		$this->assertTrue(Validation::cc('5641828995047453870', array('switch')));
+		$this->assertTrue(Validation::cc('6333321884754806543', array('switch')));
+		$this->assertTrue(Validation::cc('6333108246283715901', array('switch')));
+		$this->assertTrue(Validation::cc('6759572372800700102', array('switch')));
+		$this->assertTrue(Validation::cc('4903095096797974933', array('switch')));
+		$this->assertTrue(Validation::cc('6333354315797920215', array('switch')));
+		$this->assertTrue(Validation::cc('6759163746089433755', array('switch')));
+		$this->assertTrue(Validation::cc('6759871666634807647', array('switch')));
+		$this->assertTrue(Validation::cc('5641827883728575248', array('switch')));
+		$this->assertTrue(Validation::cc('4936527975051407847', array('switch')));
+		$this->assertTrue(Validation::cc('5641823318396882141', array('switch')));
+		$this->assertTrue(Validation::cc('6759123772311123708', array('switch')));
+		$this->assertTrue(Validation::cc('4903054736148271088', array('switch')));
+		$this->assertTrue(Validation::cc('4936477526808883952', array('switch')));
+		$this->assertTrue(Validation::cc('4936433964890967966', array('switch')));
+		$this->assertTrue(Validation::cc('6333245128906049344', array('switch')));
+		$this->assertTrue(Validation::cc('4936321036970553134', array('switch')));
+		$this->assertTrue(Validation::cc('4936111816358702773', array('switch')));
+		$this->assertTrue(Validation::cc('4936196077254804290', array('switch')));
+		$this->assertTrue(Validation::cc('6759558831206830183', array('switch')));
+		$this->assertTrue(Validation::cc('5641827998830403137', array('switch')));
+		//VISA 13 digit
+		$this->assertTrue(Validation::cc('4024007174754', array('visa')));
+		$this->assertTrue(Validation::cc('4104816460717', array('visa')));
+		$this->assertTrue(Validation::cc('4716229700437', array('visa')));
+		$this->assertTrue(Validation::cc('4539305400213', array('visa')));
+		$this->assertTrue(Validation::cc('4728260558665', array('visa')));
+		$this->assertTrue(Validation::cc('4929100131792', array('visa')));
+		$this->assertTrue(Validation::cc('4024007117308', array('visa')));
+		$this->assertTrue(Validation::cc('4539915491024', array('visa')));
+		$this->assertTrue(Validation::cc('4539790901139', array('visa')));
+		$this->assertTrue(Validation::cc('4485284914909', array('visa')));
+		$this->assertTrue(Validation::cc('4782793022350', array('visa')));
+		$this->assertTrue(Validation::cc('4556899290685', array('visa')));
+		$this->assertTrue(Validation::cc('4024007134774', array('visa')));
+		$this->assertTrue(Validation::cc('4333412341316', array('visa')));
+		$this->assertTrue(Validation::cc('4539534204543', array('visa')));
+		$this->assertTrue(Validation::cc('4485640373626', array('visa')));
+		$this->assertTrue(Validation::cc('4929911445746', array('visa')));
+		$this->assertTrue(Validation::cc('4539292550806', array('visa')));
+		$this->assertTrue(Validation::cc('4716523014030', array('visa')));
+		$this->assertTrue(Validation::cc('4024007125152', array('visa')));
+		$this->assertTrue(Validation::cc('4539758883311', array('visa')));
+		$this->assertTrue(Validation::cc('4024007103258', array('visa')));
+		$this->assertTrue(Validation::cc('4916933155767', array('visa')));
+		$this->assertTrue(Validation::cc('4024007159672', array('visa')));
+		$this->assertTrue(Validation::cc('4716935544871', array('visa')));
+		$this->assertTrue(Validation::cc('4929415177779', array('visa')));
+		$this->assertTrue(Validation::cc('4929748547896', array('visa')));
+		$this->assertTrue(Validation::cc('4929153468612', array('visa')));
+		$this->assertTrue(Validation::cc('4539397132104', array('visa')));
+		$this->assertTrue(Validation::cc('4485293435540', array('visa')));
+		$this->assertTrue(Validation::cc('4485799412720', array('visa')));
+		$this->assertTrue(Validation::cc('4916744757686', array('visa')));
+		$this->assertTrue(Validation::cc('4556475655426', array('visa')));
+		$this->assertTrue(Validation::cc('4539400441625', array('visa')));
+		$this->assertTrue(Validation::cc('4485437129173', array('visa')));
+		$this->assertTrue(Validation::cc('4716253605320', array('visa')));
+		$this->assertTrue(Validation::cc('4539366156589', array('visa')));
+		$this->assertTrue(Validation::cc('4916498061392', array('visa')));
+		$this->assertTrue(Validation::cc('4716127163779', array('visa')));
+		$this->assertTrue(Validation::cc('4024007183078', array('visa')));
+		$this->assertTrue(Validation::cc('4041553279654', array('visa')));
+		$this->assertTrue(Validation::cc('4532380121960', array('visa')));
+		$this->assertTrue(Validation::cc('4485906062491', array('visa')));
+		$this->assertTrue(Validation::cc('4539365115149', array('visa')));
+		$this->assertTrue(Validation::cc('4485146516702', array('visa')));
+		//VISA 16 digit
+		$this->assertTrue(Validation::cc('4916375389940009', array('visa')));
+		$this->assertTrue(Validation::cc('4929167481032610', array('visa')));
+		$this->assertTrue(Validation::cc('4485029969061519', array('visa')));
+		$this->assertTrue(Validation::cc('4485573845281759', array('visa')));
+		$this->assertTrue(Validation::cc('4485669810383529', array('visa')));
+		$this->assertTrue(Validation::cc('4929615806560327', array('visa')));
+		$this->assertTrue(Validation::cc('4556807505609535', array('visa')));
+		$this->assertTrue(Validation::cc('4532611336232890', array('visa')));
+		$this->assertTrue(Validation::cc('4532201952422387', array('visa')));
+		$this->assertTrue(Validation::cc('4485073797976290', array('visa')));
+		$this->assertTrue(Validation::cc('4024007157580969', array('visa')));
+		$this->assertTrue(Validation::cc('4053740470212274', array('visa')));
+		$this->assertTrue(Validation::cc('4716265831525676', array('visa')));
+		$this->assertTrue(Validation::cc('4024007100222966', array('visa')));
+		$this->assertTrue(Validation::cc('4539556148303244', array('visa')));
+		$this->assertTrue(Validation::cc('4532449879689709', array('visa')));
+		$this->assertTrue(Validation::cc('4916805467840986', array('visa')));
+		$this->assertTrue(Validation::cc('4532155644440233', array('visa')));
+		$this->assertTrue(Validation::cc('4467977802223781', array('visa')));
+		$this->assertTrue(Validation::cc('4539224637000686', array('visa')));
+		$this->assertTrue(Validation::cc('4556629187064965', array('visa')));
+		$this->assertTrue(Validation::cc('4532970205932943', array('visa')));
+		$this->assertTrue(Validation::cc('4821470132041850', array('visa')));
+		$this->assertTrue(Validation::cc('4916214267894485', array('visa')));
+		$this->assertTrue(Validation::cc('4024007169073284', array('visa')));
+		$this->assertTrue(Validation::cc('4716783351296122', array('visa')));
+		$this->assertTrue(Validation::cc('4556480171913795', array('visa')));
+		$this->assertTrue(Validation::cc('4929678411034997', array('visa')));
+		$this->assertTrue(Validation::cc('4682061913519392', array('visa')));
+		$this->assertTrue(Validation::cc('4916495481746474', array('visa')));
+		$this->assertTrue(Validation::cc('4929007108460499', array('visa')));
+		$this->assertTrue(Validation::cc('4539951357838586', array('visa')));
+		$this->assertTrue(Validation::cc('4716482691051558', array('visa')));
+		$this->assertTrue(Validation::cc('4916385069917516', array('visa')));
+		$this->assertTrue(Validation::cc('4929020289494641', array('visa')));
+		$this->assertTrue(Validation::cc('4532176245263774', array('visa')));
+		$this->assertTrue(Validation::cc('4556242273553949', array('visa')));
+		$this->assertTrue(Validation::cc('4481007485188614', array('visa')));
+		$this->assertTrue(Validation::cc('4716533372139623', array('visa')));
+		$this->assertTrue(Validation::cc('4929152038152632', array('visa')));
+		$this->assertTrue(Validation::cc('4539404037310550', array('visa')));
+		$this->assertTrue(Validation::cc('4532800925229140', array('visa')));
+		$this->assertTrue(Validation::cc('4916845885268360', array('visa')));
+		$this->assertTrue(Validation::cc('4394514669078434', array('visa')));
+		$this->assertTrue(Validation::cc('4485611378115042', array('visa')));
+		//Visa Electron
+		$this->assertTrue(Validation::cc('4175003346287100', array('electron')));
+		$this->assertTrue(Validation::cc('4913042516577228', array('electron')));
+		$this->assertTrue(Validation::cc('4917592325659381', array('electron')));
+		$this->assertTrue(Validation::cc('4917084924450511', array('electron')));
+		$this->assertTrue(Validation::cc('4917994610643999', array('electron')));
+		$this->assertTrue(Validation::cc('4175005933743585', array('electron')));
+		$this->assertTrue(Validation::cc('4175008373425044', array('electron')));
+		$this->assertTrue(Validation::cc('4913119763664154', array('electron')));
+		$this->assertTrue(Validation::cc('4913189017481812', array('electron')));
+		$this->assertTrue(Validation::cc('4913085104968622', array('electron')));
+		$this->assertTrue(Validation::cc('4175008803122021', array('electron')));
+		$this->assertTrue(Validation::cc('4913294453962489', array('electron')));
+		$this->assertTrue(Validation::cc('4175009797419290', array('electron')));
+		$this->assertTrue(Validation::cc('4175005028142917', array('electron')));
+		$this->assertTrue(Validation::cc('4913940802385364', array('electron')));
+		//Voyager
+		$this->assertTrue(Validation::cc('869940697287073', array('voyager')));
+		$this->assertTrue(Validation::cc('869934523596112', array('voyager')));
+		$this->assertTrue(Validation::cc('869958670174621', array('voyager')));
+		$this->assertTrue(Validation::cc('869921250068209', array('voyager')));
+		$this->assertTrue(Validation::cc('869972521242198', array('voyager')));
+	}
+
+/**
+ * testLuhn method
+ *
+ * @access public
+ * @return void
+ */
+	function testLuhn() {
+		$this->Validation->deep = true;
+
+		//American Express
+		$this->Validation->check = '370482756063980';
+		$this->assertTrue($this->Validation->_luhn());
+		//BankCard
+		$this->Validation->check = '5610745867413420';
+		$this->assertTrue($this->Validation->_luhn());
+		//Diners Club 14
+		$this->Validation->check = '30155483651028';
+		$this->assertTrue($this->Validation->_luhn());
+		//2004 MasterCard/Diners Club Alliance International 14
+		$this->Validation->check = '36747701998969';
+		$this->assertTrue($this->Validation->_luhn());
+		//2004 MasterCard/Diners Club Alliance US & Canada 16
+		$this->Validation->check = '5597511346169950';
+		$this->assertTrue($this->Validation->_luhn());
+		//Discover
+		$this->Validation->check = '6011802876467237';
+		$this->assertTrue($this->Validation->_luhn());
+		//enRoute
+		$this->Validation->check = '201496944158937';
+		$this->assertTrue($this->Validation->_luhn());
+		//JCB 15 digit
+		$this->Validation->check = '210034762247893';
+		$this->assertTrue($this->Validation->_luhn());
+		//JCB 16 digit
+		$this->Validation->check = '3096806857839939';
+		$this->assertTrue($this->Validation->_luhn());
+		//Maestro (debit card)
+		$this->Validation->check = '5020147409985219';
+		$this->assertTrue($this->Validation->_luhn());
+		//Mastercard
+		$this->Validation->check = '5580424361774366';
+		$this->assertTrue($this->Validation->_luhn());
+		//Solo 16
+		$this->Validation->check = '6767432107064987';
+		$this->assertTrue($this->Validation->_luhn());
+		//Solo 18
+		$this->Validation->check = '676714834398858593';
+		$this->assertTrue($this->Validation->_luhn());
+		//Solo 19
+		$this->Validation->check = '6767838565218340113';
+		$this->assertTrue($this->Validation->_luhn());
+		//Switch 16
+		$this->Validation->check = '5641829171515733';
+		$this->assertTrue($this->Validation->_luhn());
+		//Switch 18
+		$this->Validation->check = '493622764224625174';
+		$this->assertTrue($this->Validation->_luhn());
+		//Switch 19
+		$this->Validation->check = '6759603460617628716';
+		$this->assertTrue($this->Validation->_luhn());
+		//VISA 13 digit
+		$this->Validation->check = '4024007174754';
+		$this->assertTrue($this->Validation->_luhn());
+		//VISA 16 digit
+		$this->Validation->check = '4916375389940009';
+		$this->assertTrue($this->Validation->_luhn());
+		//Visa Electron
+		$this->Validation->check = '4175003346287100';
+		$this->assertTrue($this->Validation->_luhn());
+		//Voyager
+		$this->Validation->check = '869940697287073';
+		$this->assertTrue($this->Validation->_luhn());
+
+		$this->Validation->check = '0000000000000000';
+		$this->assertFalse($this->Validation->_luhn());
+
+		$this->Validation->check = '869940697287173';
+		$this->assertFalse($this->Validation->_luhn());
+	}
+
+/**
+ * testCustomRegexForCc method
+ *
+ * @access public
+ * @return void
+ */
+	function testCustomRegexForCc() {
+		$this->assertTrue(Validation::cc('12332105933743585', null, null, '/123321\\d{11}/'));
+		$this->assertFalse(Validation::cc('1233210593374358', null, null, '/123321\\d{11}/'));
+		$this->assertFalse(Validation::cc('12312305933743585', null, null, '/123321\\d{11}/'));
+	}
+
+/**
+ * testCustomRegexForCcWithLuhnCheck method
+ *
+ * @access public
+ * @return void
+ */
+	function testCustomRegexForCcWithLuhnCheck() {
+		$this->assertTrue(Validation::cc('12332110426226941', null, true, '/123321\\d{11}/'));
+		$this->assertFalse(Validation::cc('12332105933743585', null, true, '/123321\\d{11}/'));
+		$this->assertFalse(Validation::cc('12332105933743587', null, true, '/123321\\d{11}/'));
+		$this->assertFalse(Validation::cc('12312305933743585', null, true, '/123321\\d{11}/'));
+	}
+
+/**
+ * testFastCc method
+ *
+ * @access public
+ * @return void
+ */
+	function testFastCc() {
+		// too short
+		$this->assertFalse(Validation::cc('123456789012'));
+		//American Express
+		$this->assertTrue(Validation::cc('370482756063980'));
+		//Diners Club 14
+		$this->assertTrue(Validation::cc('30155483651028'));
+		//2004 MasterCard/Diners Club Alliance International 14
+		$this->assertTrue(Validation::cc('36747701998969'));
+		//2004 MasterCard/Diners Club Alliance US & Canada 16
+		$this->assertTrue(Validation::cc('5597511346169950'));
+		//Discover
+		$this->assertTrue(Validation::cc('6011802876467237'));
+		//Mastercard
+		$this->assertTrue(Validation::cc('5580424361774366'));
+		//VISA 13 digit
+		$this->assertTrue(Validation::cc('4024007174754'));
+		//VISA 16 digit
+		$this->assertTrue(Validation::cc('4916375389940009'));
+		//Visa Electron
+		$this->assertTrue(Validation::cc('4175003346287100'));
+	}
+
+/**
+ * testAllCc method
+ *
+ * @access public
+ * @return void
+ */
+	function testAllCc() {
+		//American Express
+		$this->assertTrue(Validation::cc('370482756063980', 'all'));
+		//BankCard
+		$this->assertTrue(Validation::cc('5610745867413420', 'all'));
+		//Diners Club 14
+		$this->assertTrue(Validation::cc('30155483651028', 'all'));
+		//2004 MasterCard/Diners Club Alliance International 14
+		$this->assertTrue(Validation::cc('36747701998969', 'all'));
+		//2004 MasterCard/Diners Club Alliance US & Canada 16
+		$this->assertTrue(Validation::cc('5597511346169950', 'all'));
+		//Discover
+		$this->assertTrue(Validation::cc('6011802876467237', 'all'));
+		//enRoute
+		$this->assertTrue(Validation::cc('201496944158937', 'all'));
+		//JCB 15 digit
+		$this->assertTrue(Validation::cc('210034762247893', 'all'));
+		//JCB 16 digit
+		$this->assertTrue(Validation::cc('3096806857839939', 'all'));
+		//Maestro (debit card)
+		$this->assertTrue(Validation::cc('5020147409985219', 'all'));
+		//Mastercard
+		$this->assertTrue(Validation::cc('5580424361774366', 'all'));
+		//Solo 16
+		$this->assertTrue(Validation::cc('6767432107064987', 'all'));
+		//Solo 18
+		$this->assertTrue(Validation::cc('676714834398858593', 'all'));
+		//Solo 19
+		$this->assertTrue(Validation::cc('6767838565218340113', 'all'));
+		//Switch 16
+		$this->assertTrue(Validation::cc('5641829171515733', 'all'));
+		//Switch 18
+		$this->assertTrue(Validation::cc('493622764224625174', 'all'));
+		//Switch 19
+		$this->assertTrue(Validation::cc('6759603460617628716', 'all'));
+		//VISA 13 digit
+		$this->assertTrue(Validation::cc('4024007174754', 'all'));
+		//VISA 16 digit
+		$this->assertTrue(Validation::cc('4916375389940009', 'all'));
+		//Visa Electron
+		$this->assertTrue(Validation::cc('4175003346287100', 'all'));
+		//Voyager
+		$this->assertTrue(Validation::cc('869940697287073', 'all'));
+	}
+
+/**
+ * testAllCcDeep method
+ *
+ * @access public
+ * @return void
+ */
+	function testAllCcDeep() {
+		//American Express
+		$this->assertTrue(Validation::cc('370482756063980', 'all', true));
+		//BankCard
+		$this->assertTrue(Validation::cc('5610745867413420', 'all', true));
+		//Diners Club 14
+		$this->assertTrue(Validation::cc('30155483651028', 'all', true));
+		//2004 MasterCard/Diners Club Alliance International 14
+		$this->assertTrue(Validation::cc('36747701998969', 'all', true));
+		//2004 MasterCard/Diners Club Alliance US & Canada 16
+		$this->assertTrue(Validation::cc('5597511346169950', 'all', true));
+		//Discover
+		$this->assertTrue(Validation::cc('6011802876467237', 'all', true));
+		//enRoute
+		$this->assertTrue(Validation::cc('201496944158937', 'all', true));
+		//JCB 15 digit
+		$this->assertTrue(Validation::cc('210034762247893', 'all', true));
+		//JCB 16 digit
+		$this->assertTrue(Validation::cc('3096806857839939', 'all', true));
+		//Maestro (debit card)
+		$this->assertTrue(Validation::cc('5020147409985219', 'all', true));
+		//Mastercard
+		$this->assertTrue(Validation::cc('5580424361774366', 'all', true));
+		//Solo 16
+		$this->assertTrue(Validation::cc('6767432107064987', 'all', true));
+		//Solo 18
+		$this->assertTrue(Validation::cc('676714834398858593', 'all', true));
+		//Solo 19
+		$this->assertTrue(Validation::cc('6767838565218340113', 'all', true));
+		//Switch 16
+		$this->assertTrue(Validation::cc('5641829171515733', 'all', true));
+		//Switch 18
+		$this->assertTrue(Validation::cc('493622764224625174', 'all', true));
+		//Switch 19
+		$this->assertTrue(Validation::cc('6759603460617628716', 'all', true));
+		//VISA 13 digit
+		$this->assertTrue(Validation::cc('4024007174754', 'all', true));
+		//VISA 16 digit
+		$this->assertTrue(Validation::cc('4916375389940009', 'all', true));
+		//Visa Electron
+		$this->assertTrue(Validation::cc('4175003346287100', 'all', true));
+		//Voyager
+		$this->assertTrue(Validation::cc('869940697287073', 'all', true));
+	}
+
+/**
+ * testComparison method
+ *
+ * @access public
+ * @return void
+ */
+	function testComparison() {
+		$this->assertFalse(Validation::comparison(7, null, 6));
+		$this->assertTrue(Validation::comparison(7, 'is greater', 6));
+		$this->assertTrue(Validation::comparison(7, '>', 6));
+		$this->assertTrue(Validation::comparison(6, 'is less', 7));
+		$this->assertTrue(Validation::comparison(6, '<', 7));
+		$this->assertTrue(Validation::comparison(7, 'greater or equal', 7));
+		$this->assertTrue(Validation::comparison(7, '>=', 7));
+		$this->assertTrue(Validation::comparison(7, 'greater or equal', 6));
+		$this->assertTrue(Validation::comparison(7, '>=', 6));
+		$this->assertTrue(Validation::comparison(6, 'less or equal', 7));
+		$this->assertTrue(Validation::comparison(6, '<=', 7));
+		$this->assertTrue(Validation::comparison(7, 'equal to', 7));
+		$this->assertTrue(Validation::comparison(7, '==', 7));
+		$this->assertTrue(Validation::comparison(7, 'not equal', 6));
+		$this->assertTrue(Validation::comparison(7, '!=', 6));
+		$this->assertFalse(Validation::comparison(6, 'is greater', 7));
+		$this->assertFalse(Validation::comparison(6, '>', 7));
+		$this->assertFalse(Validation::comparison(7, 'is less', 6));
+		$this->assertFalse(Validation::comparison(7, '<', 6));
+		$this->assertFalse(Validation::comparison(6, 'greater or equal', 7));
+		$this->assertFalse(Validation::comparison(6, '>=', 7));
+		$this->assertFalse(Validation::comparison(6, 'greater or equal', 7));
+		$this->assertFalse(Validation::comparison(6, '>=', 7));
+		$this->assertFalse(Validation::comparison(7, 'less or equal', 6));
+		$this->assertFalse(Validation::comparison(7, '<=', 6));
+		$this->assertFalse(Validation::comparison(7, 'equal to', 6));
+		$this->assertFalse(Validation::comparison(7, '==', 6));
+		$this->assertFalse(Validation::comparison(7, 'not equal', 7));
+		$this->assertFalse(Validation::comparison(7, '!=', 7));
+	}
+
+/**
+ * testComparisonAsArray method
+ *
+ * @access public
+ * @return void
+ */
+	function testComparisonAsArray() {
+		$this->assertTrue(Validation::comparison(array('check1' => 7, 'operator' => 'is greater', 'check2' => 6)));
+		$this->assertTrue(Validation::comparison(array('check1' => 7, 'operator' => '>', 'check2' => 6)));
+		$this->assertTrue(Validation::comparison(array('check1' => 6, 'operator' => 'is less', 'check2' => 7)));
+		$this->assertTrue(Validation::comparison(array('check1' => 6, 'operator' => '<', 'check2' => 7)));
+		$this->assertTrue(Validation::comparison(array('check1' => 7, 'operator' => 'greater or equal', 'check2' => 7)));
+		$this->assertTrue(Validation::comparison(array('check1' => 7, 'operator' => '>=', 'check2' => 7)));
+		$this->assertTrue(Validation::comparison(array('check1' => 7, 'operator' => 'greater or equal','check2' =>  6)));
+		$this->assertTrue(Validation::comparison(array('check1' => 7, 'operator' => '>=', 'check2' => 6)));
+		$this->assertTrue(Validation::comparison(array('check1' => 6, 'operator' => 'less or equal', 'check2' => 7)));
+		$this->assertTrue(Validation::comparison(array('check1' => 6, 'operator' => '<=', 'check2' => 7)));
+		$this->assertTrue(Validation::comparison(array('check1' => 7, 'operator' => 'equal to', 'check2' => 7)));
+		$this->assertTrue(Validation::comparison(array('check1' => 7, 'operator' => '==', 'check2' => 7)));
+		$this->assertTrue(Validation::comparison(array('check1' => 7, 'operator' => 'not equal', 'check2' => 6)));
+		$this->assertTrue(Validation::comparison(array('check1' => 7, 'operator' => '!=', 'check2' => 6)));
+		$this->assertFalse(Validation::comparison(array('check1' => 6, 'operator' => 'is greater', 'check2' => 7)));
+		$this->assertFalse(Validation::comparison(array('check1' => 6, 'operator' => '>', 'check2' => 7)));
+		$this->assertFalse(Validation::comparison(array('check1' => 7, 'operator' => 'is less', 'check2' => 6)));
+		$this->assertFalse(Validation::comparison(array('check1' => 7, 'operator' => '<', 'check2' => 6)));
+		$this->assertFalse(Validation::comparison(array('check1' => 6, 'operator' => 'greater or equal', 'check2' => 7)));
+		$this->assertFalse(Validation::comparison(array('check1' => 6, 'operator' => '>=', 'check2' => 7)));
+		$this->assertFalse(Validation::comparison(array('check1' => 6, 'operator' => 'greater or equal', 'check2' => 7)));
+		$this->assertFalse(Validation::comparison(array('check1' => 6, 'operator' => '>=', 'check2' => 7)));
+		$this->assertFalse(Validation::comparison(array('check1' => 7, 'operator' => 'less or equal', 'check2' => 6)));
+		$this->assertFalse(Validation::comparison(array('check1' => 7, 'operator' => '<=', 'check2' => 6)));
+		$this->assertFalse(Validation::comparison(array('check1' => 7, 'operator' => 'equal to', 'check2' => 6)));
+		$this->assertFalse(Validation::comparison(array('check1' => 7, 'operator' => '==','check2' =>  6)));
+		$this->assertFalse(Validation::comparison(array('check1' => 7, 'operator' => 'not equal', 'check2' => 7)));
+		$this->assertFalse(Validation::comparison(array('check1' => 7, 'operator' => '!=', 'check2' => 7)));
+	}
+
+/**
+ * testCustom method
+ *
+ * @access public
+ * @return void
+ */
+	function testCustom() {
+		$this->assertTrue(Validation::custom('12345', '/(?<!\\S)\\d++(?!\\S)/'));
+		$this->assertFalse(Validation::custom('Text', '/(?<!\\S)\\d++(?!\\S)/'));
+		$this->assertFalse(Validation::custom('123.45', '/(?<!\\S)\\d++(?!\\S)/'));
+		$this->assertFalse(Validation::custom('missing regex'));
+	}
+
+/**
+ * testCustomAsArray method
+ *
+ * @access public
+ * @return void
+ */
+	function testCustomAsArray() {
+		$this->assertTrue(Validation::custom(array('check' => '12345', 'regex' => '/(?<!\\S)\\d++(?!\\S)/')));
+		$this->assertFalse(Validation::custom(array('check' => 'Text', 'regex' => '/(?<!\\S)\\d++(?!\\S)/')));
+		$this->assertFalse(Validation::custom(array('check' => '123.45', 'regex' => '/(?<!\\S)\\d++(?!\\S)/')));
+	}
+
+/**
+ * testDateDdmmyyyy method
+ *
+ * @access public
+ * @return void
+ */
+	function testDateDdmmyyyy() {
+		$this->assertTrue(Validation::date('27-12-2006', array('dmy')));
+		$this->assertTrue(Validation::date('27.12.2006', array('dmy')));
+		$this->assertTrue(Validation::date('27/12/2006', array('dmy')));
+		$this->assertTrue(Validation::date('27 12 2006', array('dmy')));
+		$this->assertFalse(Validation::date('00-00-0000', array('dmy')));
+		$this->assertFalse(Validation::date('00.00.0000', array('dmy')));
+		$this->assertFalse(Validation::date('00/00/0000', array('dmy')));
+		$this->assertFalse(Validation::date('00 00 0000', array('dmy')));
+		$this->assertFalse(Validation::date('31-11-2006', array('dmy')));
+		$this->assertFalse(Validation::date('31.11.2006', array('dmy')));
+		$this->assertFalse(Validation::date('31/11/2006', array('dmy')));
+		$this->assertFalse(Validation::date('31 11 2006', array('dmy')));
+	}
+
+/**
+ * testDateDdmmyyyyLeapYear method
+ *
+ * @access public
+ * @return void
+ */
+	function testDateDdmmyyyyLeapYear() {
+		$this->assertTrue(Validation::date('29-02-2004', array('dmy')));
+		$this->assertTrue(Validation::date('29.02.2004', array('dmy')));
+		$this->assertTrue(Validation::date('29/02/2004', array('dmy')));
+		$this->assertTrue(Validation::date('29 02 2004', array('dmy')));
+		$this->assertFalse(Validation::date('29-02-2006', array('dmy')));
+		$this->assertFalse(Validation::date('29.02.2006', array('dmy')));
+		$this->assertFalse(Validation::date('29/02/2006', array('dmy')));
+		$this->assertFalse(Validation::date('29 02 2006', array('dmy')));
+	}
+
+/**
+ * testDateDdmmyy method
+ *
+ * @access public
+ * @return void
+ */
+	function testDateDdmmyy() {
+		$this->assertTrue(Validation::date('27-12-06', array('dmy')));
+		$this->assertTrue(Validation::date('27.12.06', array('dmy')));
+		$this->assertTrue(Validation::date('27/12/06', array('dmy')));
+		$this->assertTrue(Validation::date('27 12 06', array('dmy')));
+		$this->assertFalse(Validation::date('00-00-00', array('dmy')));
+		$this->assertFalse(Validation::date('00.00.00', array('dmy')));
+		$this->assertFalse(Validation::date('00/00/00', array('dmy')));
+		$this->assertFalse(Validation::date('00 00 00', array('dmy')));
+		$this->assertFalse(Validation::date('31-11-06', array('dmy')));
+		$this->assertFalse(Validation::date('31.11.06', array('dmy')));
+		$this->assertFalse(Validation::date('31/11/06', array('dmy')));
+		$this->assertFalse(Validation::date('31 11 06', array('dmy')));
+	}
+
+/**
+ * testDateDdmmyyLeapYear method
+ *
+ * @access public
+ * @return void
+ */
+	function testDateDdmmyyLeapYear() {
+		$this->assertTrue(Validation::date('29-02-04', array('dmy')));
+		$this->assertTrue(Validation::date('29.02.04', array('dmy')));
+		$this->assertTrue(Validation::date('29/02/04', array('dmy')));
+		$this->assertTrue(Validation::date('29 02 04', array('dmy')));
+		$this->assertFalse(Validation::date('29-02-06', array('dmy')));
+		$this->assertFalse(Validation::date('29.02.06', array('dmy')));
+		$this->assertFalse(Validation::date('29/02/06', array('dmy')));
+		$this->assertFalse(Validation::date('29 02 06', array('dmy')));
+	}
+
+/**
+ * testDateDmyy method
+ *
+ * @access public
+ * @return void
+ */
+	function testDateDmyy() {
+		$this->assertTrue(Validation::date('7-2-06', array('dmy')));
+		$this->assertTrue(Validation::date('7.2.06', array('dmy')));
+		$this->assertTrue(Validation::date('7/2/06', array('dmy')));
+		$this->assertTrue(Validation::date('7 2 06', array('dmy')));
+		$this->assertFalse(Validation::date('0-0-00', array('dmy')));
+		$this->assertFalse(Validation::date('0.0.00', array('dmy')));
+		$this->assertFalse(Validation::date('0/0/00', array('dmy')));
+		$this->assertFalse(Validation::date('0 0 00', array('dmy')));
+		$this->assertFalse(Validation::date('32-2-06', array('dmy')));
+		$this->assertFalse(Validation::date('32.2.06', array('dmy')));
+		$this->assertFalse(Validation::date('32/2/06', array('dmy')));
+		$this->assertFalse(Validation::date('32 2 06', array('dmy')));
+	}
+
+/**
+ * testDateDmyyLeapYear method
+ *
+ * @access public
+ * @return void
+ */
+	function testDateDmyyLeapYear() {
+		$this->assertTrue(Validation::date('29-2-04', array('dmy')));
+		$this->assertTrue(Validation::date('29.2.04', array('dmy')));
+		$this->assertTrue(Validation::date('29/2/04', array('dmy')));
+		$this->assertTrue(Validation::date('29 2 04', array('dmy')));
+		$this->assertFalse(Validation::date('29-2-06', array('dmy')));
+		$this->assertFalse(Validation::date('29.2.06', array('dmy')));
+		$this->assertFalse(Validation::date('29/2/06', array('dmy')));
+		$this->assertFalse(Validation::date('29 2 06', array('dmy')));
+	}
+
+/**
+ * testDateDmyyyy method
+ *
+ * @access public
+ * @return void
+ */
+	function testDateDmyyyy() {
+		$this->assertTrue(Validation::date('7-2-2006', array('dmy')));
+		$this->assertTrue(Validation::date('7.2.2006', array('dmy')));
+		$this->assertTrue(Validation::date('7/2/2006', array('dmy')));
+		$this->assertTrue(Validation::date('7 2 2006', array('dmy')));
+		$this->assertFalse(Validation::date('0-0-0000', array('dmy')));
+		$this->assertFalse(Validation::date('0.0.0000', array('dmy')));
+		$this->assertFalse(Validation::date('0/0/0000', array('dmy')));
+		$this->assertFalse(Validation::date('0 0 0000', array('dmy')));
+		$this->assertFalse(Validation::date('32-2-2006', array('dmy')));
+		$this->assertFalse(Validation::date('32.2.2006', array('dmy')));
+		$this->assertFalse(Validation::date('32/2/2006', array('dmy')));
+		$this->assertFalse(Validation::date('32 2 2006', array('dmy')));
+	}
+
+/**
+ * testDateDmyyyyLeapYear method
+ *
+ * @access public
+ * @return void
+ */
+	function testDateDmyyyyLeapYear() {
+		$this->assertTrue(Validation::date('29-2-2004', array('dmy')));
+		$this->assertTrue(Validation::date('29.2.2004', array('dmy')));
+		$this->assertTrue(Validation::date('29/2/2004', array('dmy')));
+		$this->assertTrue(Validation::date('29 2 2004', array('dmy')));
+		$this->assertFalse(Validation::date('29-2-2006', array('dmy')));
+		$this->assertFalse(Validation::date('29.2.2006', array('dmy')));
+		$this->assertFalse(Validation::date('29/2/2006', array('dmy')));
+		$this->assertFalse(Validation::date('29 2 2006', array('dmy')));
+	}
+
+/**
+ * testDateMmddyyyy method
+ *
+ * @access public
+ * @return void
+ */
+	function testDateMmddyyyy() {
+		$this->assertTrue(Validation::date('12-27-2006', array('mdy')));
+		$this->assertTrue(Validation::date('12.27.2006', array('mdy')));
+		$this->assertTrue(Validation::date('12/27/2006', array('mdy')));
+		$this->assertTrue(Validation::date('12 27 2006', array('mdy')));
+		$this->assertFalse(Validation::date('00-00-0000', array('mdy')));
+		$this->assertFalse(Validation::date('00.00.0000', array('mdy')));
+		$this->assertFalse(Validation::date('00/00/0000', array('mdy')));
+		$this->assertFalse(Validation::date('00 00 0000', array('mdy')));
+		$this->assertFalse(Validation::date('11-31-2006', array('mdy')));
+		$this->assertFalse(Validation::date('11.31.2006', array('mdy')));
+		$this->assertFalse(Validation::date('11/31/2006', array('mdy')));
+		$this->assertFalse(Validation::date('11 31 2006', array('mdy')));
+	}
+
+/**
+ * testDateMmddyyyyLeapYear method
+ *
+ * @access public
+ * @return void
+ */
+	function testDateMmddyyyyLeapYear() {
+		$this->assertTrue(Validation::date('02-29-2004', array('mdy')));
+		$this->assertTrue(Validation::date('02.29.2004', array('mdy')));
+		$this->assertTrue(Validation::date('02/29/2004', array('mdy')));
+		$this->assertTrue(Validation::date('02 29 2004', array('mdy')));
+		$this->assertFalse(Validation::date('02-29-2006', array('mdy')));
+		$this->assertFalse(Validation::date('02.29.2006', array('mdy')));
+		$this->assertFalse(Validation::date('02/29/2006', array('mdy')));
+		$this->assertFalse(Validation::date('02 29 2006', array('mdy')));
+	}
+
+/**
+ * testDateMmddyy method
+ *
+ * @access public
+ * @return void
+ */
+	function testDateMmddyy() {
+		$this->assertTrue(Validation::date('12-27-06', array('mdy')));
+		$this->assertTrue(Validation::date('12.27.06', array('mdy')));
+		$this->assertTrue(Validation::date('12/27/06', array('mdy')));
+		$this->assertTrue(Validation::date('12 27 06', array('mdy')));
+		$this->assertFalse(Validation::date('00-00-00', array('mdy')));
+		$this->assertFalse(Validation::date('00.00.00', array('mdy')));
+		$this->assertFalse(Validation::date('00/00/00', array('mdy')));
+		$this->assertFalse(Validation::date('00 00 00', array('mdy')));
+		$this->assertFalse(Validation::date('11-31-06', array('mdy')));
+		$this->assertFalse(Validation::date('11.31.06', array('mdy')));
+		$this->assertFalse(Validation::date('11/31/06', array('mdy')));
+		$this->assertFalse(Validation::date('11 31 06', array('mdy')));
+	}
+
+/**
+ * testDateMmddyyLeapYear method
+ *
+ * @access public
+ * @return void
+ */
+	function testDateMmddyyLeapYear() {
+		$this->assertTrue(Validation::date('02-29-04', array('mdy')));
+		$this->assertTrue(Validation::date('02.29.04', array('mdy')));
+		$this->assertTrue(Validation::date('02/29/04', array('mdy')));
+		$this->assertTrue(Validation::date('02 29 04', array('mdy')));
+		$this->assertFalse(Validation::date('02-29-06', array('mdy')));
+		$this->assertFalse(Validation::date('02.29.06', array('mdy')));
+		$this->assertFalse(Validation::date('02/29/06', array('mdy')));
+		$this->assertFalse(Validation::date('02 29 06', array('mdy')));
+	}
+
+/**
+ * testDateMdyy method
+ *
+ * @access public
+ * @return void
+ */
+	function testDateMdyy() {
+		$this->assertTrue(Validation::date('2-7-06', array('mdy')));
+		$this->assertTrue(Validation::date('2.7.06', array('mdy')));
+		$this->assertTrue(Validation::date('2/7/06', array('mdy')));
+		$this->assertTrue(Validation::date('2 7 06', array('mdy')));
+		$this->assertFalse(Validation::date('0-0-00', array('mdy')));
+		$this->assertFalse(Validation::date('0.0.00', array('mdy')));
+		$this->assertFalse(Validation::date('0/0/00', array('mdy')));
+		$this->assertFalse(Validation::date('0 0 00', array('mdy')));
+		$this->assertFalse(Validation::date('2-32-06', array('mdy')));
+		$this->assertFalse(Validation::date('2.32.06', array('mdy')));
+		$this->assertFalse(Validation::date('2/32/06', array('mdy')));
+		$this->assertFalse(Validation::date('2 32 06', array('mdy')));
+	}
+
+/**
+ * testDateMdyyLeapYear method
+ *
+ * @access public
+ * @return void
+ */
+	function testDateMdyyLeapYear() {
+		$this->assertTrue(Validation::date('2-29-04', array('mdy')));
+		$this->assertTrue(Validation::date('2.29.04', array('mdy')));
+		$this->assertTrue(Validation::date('2/29/04', array('mdy')));
+		$this->assertTrue(Validation::date('2 29 04', array('mdy')));
+		$this->assertFalse(Validation::date('2-29-06', array('mdy')));
+		$this->assertFalse(Validation::date('2.29.06', array('mdy')));
+		$this->assertFalse(Validation::date('2/29/06', array('mdy')));
+		$this->assertFalse(Validation::date('2 29 06', array('mdy')));
+	}
+
+/**
+ * testDateMdyyyy method
+ *
+ * @access public
+ * @return void
+ */
+	function testDateMdyyyy() {
+		$this->assertTrue(Validation::date('2-7-2006', array('mdy')));
+		$this->assertTrue(Validation::date('2.7.2006', array('mdy')));
+		$this->assertTrue(Validation::date('2/7/2006', array('mdy')));
+		$this->assertTrue(Validation::date('2 7 2006', array('mdy')));
+		$this->assertFalse(Validation::date('0-0-0000', array('mdy')));
+		$this->assertFalse(Validation::date('0.0.0000', array('mdy')));
+		$this->assertFalse(Validation::date('0/0/0000', array('mdy')));
+		$this->assertFalse(Validation::date('0 0 0000', array('mdy')));
+		$this->assertFalse(Validation::date('2-32-2006', array('mdy')));
+		$this->assertFalse(Validation::date('2.32.2006', array('mdy')));
+		$this->assertFalse(Validation::date('2/32/2006', array('mdy')));
+		$this->assertFalse(Validation::date('2 32 2006', array('mdy')));
+	}
+
+/**
+ * testDateMdyyyyLeapYear method
+ *
+ * @access public
+ * @return void
+ */
+	function testDateMdyyyyLeapYear() {
+		$this->assertTrue(Validation::date('2-29-2004', array('mdy')));
+		$this->assertTrue(Validation::date('2.29.2004', array('mdy')));
+		$this->assertTrue(Validation::date('2/29/2004', array('mdy')));
+		$this->assertTrue(Validation::date('2 29 2004', array('mdy')));
+		$this->assertFalse(Validation::date('2-29-2006', array('mdy')));
+		$this->assertFalse(Validation::date('2.29.2006', array('mdy')));
+		$this->assertFalse(Validation::date('2/29/2006', array('mdy')));
+		$this->assertFalse(Validation::date('2 29 2006', array('mdy')));
+	}
+
+/**
+ * testDateYyyymmdd method
+ *
+ * @access public
+ * @return void
+ */
+	function testDateYyyymmdd() {
+		$this->assertTrue(Validation::date('2006-12-27', array('ymd')));
+		$this->assertTrue(Validation::date('2006.12.27', array('ymd')));
+		$this->assertTrue(Validation::date('2006/12/27', array('ymd')));
+		$this->assertTrue(Validation::date('2006 12 27', array('ymd')));
+		$this->assertFalse(Validation::date('2006-11-31', array('ymd')));
+		$this->assertFalse(Validation::date('2006.11.31', array('ymd')));
+		$this->assertFalse(Validation::date('2006/11/31', array('ymd')));
+		$this->assertFalse(Validation::date('2006 11 31', array('ymd')));
+	}
+
+/**
+ * testDateYyyymmddLeapYear method
+ *
+ * @access public
+ * @return void
+ */
+	function testDateYyyymmddLeapYear() {
+		$this->assertTrue(Validation::date('2004-02-29', array('ymd')));
+		$this->assertTrue(Validation::date('2004.02.29', array('ymd')));
+		$this->assertTrue(Validation::date('2004/02/29', array('ymd')));
+		$this->assertTrue(Validation::date('2004 02 29', array('ymd')));
+		$this->assertFalse(Validation::date('2006-02-29', array('ymd')));
+		$this->assertFalse(Validation::date('2006.02.29', array('ymd')));
+		$this->assertFalse(Validation::date('2006/02/29', array('ymd')));
+		$this->assertFalse(Validation::date('2006 02 29', array('ymd')));
+	}
+
+/**
+ * testDateYymmdd method
+ *
+ * @access public
+ * @return void
+ */
+	function testDateYymmdd() {
+		$this->assertTrue(Validation::date('06-12-27', array('ymd')));
+		$this->assertTrue(Validation::date('06.12.27', array('ymd')));
+		$this->assertTrue(Validation::date('06/12/27', array('ymd')));
+		$this->assertTrue(Validation::date('06 12 27', array('ymd')));
+		$this->assertFalse(Validation::date('12/27/2600', array('ymd')));
+		$this->assertFalse(Validation::date('12.27.2600', array('ymd')));
+		$this->assertFalse(Validation::date('12/27/2600', array('ymd')));
+		$this->assertFalse(Validation::date('12 27 2600', array('ymd')));
+		$this->assertFalse(Validation::date('06-11-31', array('ymd')));
+		$this->assertFalse(Validation::date('06.11.31', array('ymd')));
+		$this->assertFalse(Validation::date('06/11/31', array('ymd')));
+		$this->assertFalse(Validation::date('06 11 31', array('ymd')));
+	}
+
+/**
+ * testDateYymmddLeapYear method
+ *
+ * @access public
+ * @return void
+ */
+	function testDateYymmddLeapYear() {
+		$this->assertTrue(Validation::date('2004-02-29', array('ymd')));
+		$this->assertTrue(Validation::date('2004.02.29', array('ymd')));
+		$this->assertTrue(Validation::date('2004/02/29', array('ymd')));
+		$this->assertTrue(Validation::date('2004 02 29', array('ymd')));
+		$this->assertFalse(Validation::date('2006-02-29', array('ymd')));
+		$this->assertFalse(Validation::date('2006.02.29', array('ymd')));
+		$this->assertFalse(Validation::date('2006/02/29', array('ymd')));
+		$this->assertFalse(Validation::date('2006 02 29', array('ymd')));
+	}
+
+/**
+ * testDateDdMMMMyyyy method
+ *
+ * @access public
+ * @return void
+ */
+	function testDateDdMMMMyyyy() {
+		$this->assertTrue(Validation::date('27 December 2006', array('dMy')));
+		$this->assertTrue(Validation::date('27 Dec 2006', array('dMy')));
+		$this->assertFalse(Validation::date('2006 Dec 27', array('dMy')));
+		$this->assertFalse(Validation::date('2006 December 27', array('dMy')));
+	}
+
+/**
+ * testDateDdMMMMyyyyLeapYear method
+ *
+ * @access public
+ * @return void
+ */
+	function testDateDdMMMMyyyyLeapYear() {
+		$this->assertTrue(Validation::date('29 February 2004', array('dMy')));
+		$this->assertFalse(Validation::date('29 February 2006', array('dMy')));
+	}
+
+/**
+ * testDateMmmmDdyyyy method
+ *
+ * @access public
+ * @return void
+ */
+	function testDateMmmmDdyyyy() {
+		$this->assertTrue(Validation::date('December 27, 2006', array('Mdy')));
+		$this->assertTrue(Validation::date('Dec 27, 2006', array('Mdy')));
+		$this->assertTrue(Validation::date('December 27 2006', array('Mdy')));
+		$this->assertTrue(Validation::date('Dec 27 2006', array('Mdy')));
+		$this->assertFalse(Validation::date('27 Dec 2006', array('Mdy')));
+		$this->assertFalse(Validation::date('2006 December 27', array('Mdy')));
+		$this->assertTrue(Validation::date('Sep 12, 2011', array('Mdy')));
+	}
+
+/**
+ * testDateMmmmDdyyyyLeapYear method
+ *
+ * @access public
+ * @return void
+ */
+	function testDateMmmmDdyyyyLeapYear() {
+		$this->assertTrue(Validation::date('February 29, 2004', array('Mdy')));
+		$this->assertTrue(Validation::date('Feb 29, 2004', array('Mdy')));
+		$this->assertTrue(Validation::date('February 29 2004', array('Mdy')));
+		$this->assertTrue(Validation::date('Feb 29 2004', array('Mdy')));
+		$this->assertFalse(Validation::date('February 29, 2006', array('Mdy')));
+	}
+
+/**
+ * testDateMy method
+ *
+ * @access public
+ * @return void
+ */
+	function testDateMy() {
+		$this->assertTrue(Validation::date('December 2006', array('My')));
+		$this->assertTrue(Validation::date('Dec 2006', array('My')));
+		$this->assertTrue(Validation::date('December/2006', array('My')));
+		$this->assertTrue(Validation::date('Dec/2006', array('My')));
+	}
+
+/**
+ * testDateMyNumeric method
+ *
+ * @access public
+ * @return void
+ */
+	function testDateMyNumeric() {
+		$this->assertTrue(Validation::date('12/2006', array('my')));
+		$this->assertTrue(Validation::date('12-2006', array('my')));
+		$this->assertTrue(Validation::date('12.2006', array('my')));
+		$this->assertTrue(Validation::date('12 2006', array('my')));
+		$this->assertFalse(Validation::date('12/06', array('my')));
+		$this->assertFalse(Validation::date('12-06', array('my')));
+		$this->assertFalse(Validation::date('12.06', array('my')));
+		$this->assertFalse(Validation::date('12 06', array('my')));
+	}
+
+/**
+ * testTime method
+ *
+ * @access public
+ * @return void
+ */
+	function testTime() {
+		$this->assertTrue(Validation::time('00:00'));
+		$this->assertTrue(Validation::time('23:59'));
+		$this->assertFalse(Validation::time('24:00'));
+		$this->assertTrue(Validation::time('12:00'));
+		$this->assertTrue(Validation::time('12:01'));
+		$this->assertTrue(Validation::time('12:01am'));
+		$this->assertTrue(Validation::time('12:01pm'));
+		$this->assertTrue(Validation::time('1pm'));
+		$this->assertTrue(Validation::time('1 pm'));
+		$this->assertTrue(Validation::time('1 PM'));
+		$this->assertTrue(Validation::time('01:00'));
+		$this->assertFalse(Validation::time('1:00'));
+		$this->assertTrue(Validation::time('1:00pm'));
+		$this->assertFalse(Validation::time('13:00pm'));
+		$this->assertFalse(Validation::time('9:00'));
+	}
+
+/**
+ * testBoolean method
+ *
+ * @access public
+ * @return void
+ */
+	function testBoolean() {
+		$this->assertTrue(Validation::boolean('0'));
+		$this->assertTrue(Validation::boolean('1'));
+		$this->assertTrue(Validation::boolean(0));
+		$this->assertTrue(Validation::boolean(1));
+		$this->assertTrue(Validation::boolean(true));
+		$this->assertTrue(Validation::boolean(false));
+		$this->assertFalse(Validation::boolean('true'));
+		$this->assertFalse(Validation::boolean('false'));
+		$this->assertFalse(Validation::boolean('-1'));
+		$this->assertFalse(Validation::boolean('2'));
+		$this->assertFalse(Validation::boolean('Boo!'));
+	}
+
+/**
+ * testDateCustomRegx method
+ *
+ * @access public
+ * @return void
+ */
+	function testDateCustomRegx() {
+		$this->assertTrue(Validation::date('2006-12-27', null, '%^(19|20)[0-9]{2}[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])$%'));
+		$this->assertFalse(Validation::date('12-27-2006', null, '%^(19|20)[0-9]{2}[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])$%'));
+	}
+
+/**
+ * testDecimal method
+ *
+ * @access public
+ * @return void
+ */
+	function testDecimal() {
+		$this->assertTrue(Validation::decimal('+1234.54321'));
+		$this->assertTrue(Validation::decimal('-1234.54321'));
+		$this->assertTrue(Validation::decimal('1234.54321'));
+		$this->assertTrue(Validation::decimal('+0123.45e6'));
+		$this->assertTrue(Validation::decimal('-0123.45e6'));
+		$this->assertTrue(Validation::decimal('0123.45e6'));
+		$this->assertFalse(Validation::decimal('string'));
+		$this->assertFalse(Validation::decimal('1234'));
+		$this->assertFalse(Validation::decimal('-1234'));
+		$this->assertFalse(Validation::decimal('+1234'));
+	}
+
+/**
+ * testDecimalWithPlaces method
+ *
+ * @access public
+ * @return void
+ */
+	function testDecimalWithPlaces() {
+		$this->assertTrue(Validation::decimal('.27', '2'));
+		$this->assertTrue(Validation::decimal(.27, 2));
+		$this->assertTrue(Validation::decimal(-.27, 2));
+		$this->assertTrue(Validation::decimal(+.27, 2));
+		$this->assertTrue(Validation::decimal('.277', '3'));
+		$this->assertTrue(Validation::decimal(.277, 3));
+		$this->assertTrue(Validation::decimal(-.277, 3));
+		$this->assertTrue(Validation::decimal(+.277, 3));
+		$this->assertTrue(Validation::decimal('1234.5678', '4'));
+		$this->assertTrue(Validation::decimal(1234.5678, 4));
+		$this->assertTrue(Validation::decimal(-1234.5678, 4));
+		$this->assertTrue(Validation::decimal(+1234.5678, 4));
+		$this->assertFalse(Validation::decimal('1234.5678', '3'));
+		$this->assertFalse(Validation::decimal(1234.5678, 3));
+		$this->assertFalse(Validation::decimal(-1234.5678, 3));
+		$this->assertFalse(Validation::decimal(+1234.5678, 3));
+	}
+
+/**
+ * testDecimalCustomRegex method
+ *
+ * @access public
+ * @return void
+ */
+	function testDecimalCustomRegex() {
+		$this->assertTrue(Validation::decimal('1.54321', null, '/^[-+]?[0-9]+(\\.[0-9]+)?$/s'));
+		$this->assertFalse(Validation::decimal('.54321', null, '/^[-+]?[0-9]+(\\.[0-9]+)?$/s'));
+	}
+
+/**
+ * testEmail method
+ *
+ * @access public
+ * @return void
+ */
+	function testEmail() {
+		$this->assertTrue(Validation::email('abc.e****@domai*****'));
+		$this->assertTrue(Validation::email('efg****@domai*****'));
+		$this->assertTrue(Validation::email('abc-e****@domai*****'));
+		$this->assertTrue(Validation::email('abc_efg****@domai*****'));
+		$this->assertTrue(Validation::email('raw****@test*****'));
+		$this->assertTrue(Validation::email('abc-e****@domai*****'));
+		$this->assertTrue(Validation::email("p.o'malle****@domai*****"));
+		$this->assertTrue(Validation::email('abc+e****@domai*****'));
+		$this->assertTrue(Validation::email('abc&efg****@domai*****'));
+		$this->assertTrue(Validation::email('abc.e****@12345*****'));
+		$this->assertTrue(Validation::email('abc.e****@12345*****'));
+		$this->assertTrue(Validation::email('abc****@g*****'));
+		$this->assertTrue(Validation::email('abc****@x*****'));
+		$this->assertTrue(Validation::email('henri****@sbcgl*****'));
+		$this->assertTrue(Validation::email('sani****@sbcgl*****'));
+
+		// all ICANN TLDs
+		$this->assertTrue(Validation::email('abc****@examp*****'));
+		$this->assertTrue(Validation::email('abc****@examp*****'));
+		$this->assertTrue(Validation::email('abc****@examp*****'));
+		$this->assertTrue(Validation::email('abc****@examp*****'));
+		$this->assertTrue(Validation::email('abc****@examp*****'));
+		$this->assertTrue(Validation::email('abc****@examp*****'));
+		$this->assertTrue(Validation::email('abc****@examp*****'));
+		$this->assertTrue(Validation::email('abc****@examp*****'));
+		$this->assertTrue(Validation::email('abc****@examp*****'));
+		$this->assertTrue(Validation::email('abc****@examp*****'));
+		$this->assertTrue(Validation::email('abc****@examp*****'));
+		$this->assertTrue(Validation::email('abc****@examp*****'));
+		$this->assertTrue(Validation::email('abc****@examp*****'));
+		$this->assertTrue(Validation::email('abc****@examp*****'));
+		$this->assertTrue(Validation::email('abc****@examp*****'));
+		$this->assertTrue(Validation::email('abc****@examp*****'));
+		$this->assertTrue(Validation::email('abc****@examp*****'));
+		$this->assertTrue(Validation::email('abc****@examp*****'));
+		$this->assertTrue(Validation::email('abc****@examp*****'));
+		$this->assertTrue(Validation::email('abc****@examp*****'));
+		$this->assertTrue(Validation::email('someo****@st*****'));
+
+		// strange, but technically valid email addresses
+		$this->assertTrue(Validation::email('S=postmaster/OU=rz/P=uni-frankfurt/A=d400/C=de****@gatew*****'));
+		$this->assertTrue(Validation::email('customer/department=shipp****@examp*****'));
+		$this->assertTrue(Validation::email('$A1234****@examp*****'));
+		$this->assertTrue(Validation::email('!def!xyz%abc****@examp*****'));
+		$this->assertTrue(Validation::email('_somen****@examp*****'));
+
+		// invalid addresses
+		$this->assertFalse(Validation::email('abc @ example'));
+		$this->assertFalse(Validation::email('abc****@examp*****'));
+		$this->assertFalse(Validation::email('abc****@examp*****.'));
+		$this->assertFalse(Validation::email('abc.****@examp*****'));
+		$this->assertFalse(Validation::email('abc @ example..com'));
+		$this->assertFalse(Validation::email('abc****@examp*****'));
+		$this->assertFalse(Validation::email('abc****@examp*****'));
+		$this->assertFalse(Validation::email('abc;@example.com'));
+		$this->assertFalse(Validation::email('abc****@examp*****;'));
+		$this->assertFalse(Validation::email('abc @ efg@example.com'));
+		$this->assertFalse(Validation::email('abc@@example.com'));
+		$this->assertFalse(Validation::email('abc efg****@examp*****'));
+		$this->assertFalse(Validation::email('abc,efg****@examp*****'));
+		$this->assertFalse(Validation::email('abc @ sub,example.com'));
+		$this->assertFalse(Validation::email("abc @ sub'example.com"));
+		$this->assertFalse(Validation::email('abc @ sub/example.com'));
+		$this->assertFalse(Validation::email('abc @ yahoo!.com'));
+		$this->assertFalse(Validation::email("Nyrée.sur****@examp*****"));
+		$this->assertFalse(Validation::email('abc @ example_underscored.com'));
+		$this->assertFalse(Validation::email('raw****@test*****....com'));
+	}
+
+/**
+ * testEmailDeep method
+ *
+ * @access public
+ * @return void
+ */
+	function testEmailDeep() {
+		$found = gethostbynamel('example.abcd');
+		if ($this->skipIf($found, 'Your DNS service responds for non-existant domains, skipping deep email checks. %s'))  {
+			return;
+		}
+		$this->assertTrue(Validation::email('abc.e****@cakep*****', true));
+		$this->assertFalse(Validation::email('abc.e****@caphp*****', true));
+		$this->assertFalse(Validation::email('abc****@examp*****', true));
+	}
+
+/**
+ * testEmailCustomRegex method
+ *
+ * @access public
+ * @return void
+ */
+	function testEmailCustomRegex() {
+		$this->assertTrue(Validation::email('abc.e****@cakep*****', null, '/^[A-Z0-9._%-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}$/i'));
+		$this->assertFalse(Validation::email('abc.e****@com*****', null, '/^[A-Z0-9._%-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}$/i'));
+	}
+
+/**
+ * testEqualTo method
+ *
+ * @access public
+ * @return void
+ */
+	function testEqualTo() {
+		$this->assertTrue(Validation::equalTo("1", "1"));
+		$this->assertFalse(Validation::equalTo(1, "1"));
+		$this->assertFalse(Validation::equalTo("", null));
+		$this->assertFalse(Validation::equalTo("", false));
+		$this->assertFalse(Validation::equalTo(0, false));
+		$this->assertFalse(Validation::equalTo(null, false));
+	}
+
+/**
+ * testIp method
+ *
+ * @access public
+ * @return void
+ */
+	function testIpv4() {
+		$this->assertTrue(Validation::ip('0.0.0.0', 'IPv4'));
+		$this->assertTrue(Validation::ip('192.168.1.156', 'IPv4'));
+		$this->assertTrue(Validation::ip('255.255.255.255', 'IPv4'));
+
+		$this->assertFalse(Validation::ip('127.0.0', 'IPv4'));
+		$this->assertFalse(Validation::ip('127.0.0.a', 'IPv4'));
+		$this->assertFalse(Validation::ip('127.0.0.256', 'IPv4'));
+
+		$this->assertFalse(Validation::ip('2001:0db8:85a3:0000:0000:8a2e:0370:7334', 'IPv4'));
+	}
+
+/**
+ * testIp v6
+ *
+ * @access public
+ * @return void
+ */
+	function testIpv6() {
+		$this->assertTrue(Validation::ip('2001:0db8:85a3:0000:0000:8a2e:0370:7334', 'IPv6'));
+		$this->assertTrue(Validation::ip('2001:db8:85a3:0:0:8a2e:370:7334', 'IPv6'));
+		$this->assertTrue(Validation::ip('2001:db8:85a3::8a2e:370:7334', 'IPv6'));
+		$this->assertTrue(Validation::ip('2001:0db8:0000:0000:0000:0000:1428:57ab', 'IPv6'));
+		$this->assertTrue(Validation::ip('2001:0db8:0000:0000:0000::1428:57ab', 'IPv6'));
+		$this->assertTrue(Validation::ip('2001:0db8:0:0:0:0:1428:57ab', 'IPv6'));
+		$this->assertTrue(Validation::ip('2001:0db8:0:0::1428:57ab', 'IPv6'));
+		$this->assertTrue(Validation::ip('2001:0db8::1428:57ab', 'IPv6'));
+		$this->assertTrue(Validation::ip('2001:db8::1428:57ab', 'IPv6'));
+		$this->assertTrue(Validation::ip('0000:0000:0000:0000:0000:0000:0000:0001', 'IPv6'));
+		$this->assertTrue(Validation::ip('::1', 'IPv6'));
+		$this->assertTrue(Validation::ip('::ffff:12.34.56.78', 'IPv6'));
+		$this->assertTrue(Validation::ip('::ffff:0c22:384e', 'IPv6'));
+		$this->assertTrue(Validation::ip('2001:0db8:1234:0000:0000:0000:0000:0000', 'IPv6'));
+		$this->assertTrue(Validation::ip('2001:0db8:1234:ffff:ffff:ffff:ffff:ffff', 'IPv6'));
+		$this->assertTrue(Validation::ip('2001:db8:a::123', 'IPv6'));
+		$this->assertTrue(Validation::ip('fe80::', 'IPv6'));
+		$this->assertTrue(Validation::ip('::ffff:192.0.2.128', 'IPv6'));
+		$this->assertTrue(Validation::ip('::ffff:c000:280', 'IPv6'));
+
+		$this->assertFalse(Validation::ip('123', 'IPv6'));
+		$this->assertFalse(Validation::ip('ldkfj', 'IPv6'));
+		$this->assertFalse(Validation::ip('2001::FFD3::57ab', 'IPv6'));
+		$this->assertFalse(Validation::ip('2001:db8:85a3::8a2e:37023:7334', 'IPv6'));
+		$this->assertFalse(Validation::ip('2001:db8:85a3::8a2e:370k:7334', 'IPv6'));
+		$this->assertFalse(Validation::ip('1:2:3:4:5:6:7:8:9', 'IPv6'));
+		$this->assertFalse(Validation::ip('1::2::3', 'IPv6'));
+		$this->assertFalse(Validation::ip('1:::3:4:5', 'IPv6'));
+		$this->assertFalse(Validation::ip('1:2:3::4:5:6:7:8:9', 'IPv6'));
+		$this->assertFalse(Validation::ip('::ffff:2.3.4', 'IPv6'));
+		$this->assertFalse(Validation::ip('::ffff:257.1.2.3', 'IPv6'));
+
+		$this->assertFalse(Validation::ip('0.0.0.0', 'IPv6'));
+	}
+
+/**
+ * testIpBoth method
+ *
+ * @return void
+ * @access public
+ */
+	function testIpBoth() {
+		$this->assertTrue(Validation::ip('0.0.0.0'));
+		$this->assertTrue(Validation::ip('192.168.1.156'));
+		$this->assertTrue(Validation::ip('255.255.255.255'));
+
+		$this->assertFalse(Validation::ip('127.0.0'));
+		$this->assertFalse(Validation::ip('127.0.0.a'));
+		$this->assertFalse(Validation::ip('127.0.0.256'));
+
+		$this->assertTrue(Validation::ip('2001:0db8:85a3:0000:0000:8a2e:0370:7334'));
+		$this->assertTrue(Validation::ip('2001:db8:85a3:0:0:8a2e:370:7334'));
+		$this->assertTrue(Validation::ip('2001:db8:85a3::8a2e:370:7334'));
+
+		$this->assertFalse(Validation::ip('2001:db8:85a3::8a2e:37023:7334'));
+		$this->assertFalse(Validation::ip('2001:db8:85a3::8a2e:370k:7334'));
+		$this->assertFalse(Validation::ip('1:2:3:4:5:6:7:8:9'));
+	}
+
+/**
+ * testMaxLength method
+ *
+ * @access public
+ * @return void
+ */
+	function testMaxLength() {
+		$this->assertTrue(Validation::maxLength('ab', 3));
+		$this->assertTrue(Validation::maxLength('abc', 3));
+		$this->assertTrue(Validation::maxLength('ÆΔΩЖÇ', 10));
+
+		$this->assertFalse(Validation::maxLength('abcd', 3));
+		$this->assertFalse(Validation::maxLength('ÆΔΩЖÇ', 3));
+	}
+
+/**
+ * testMinLength method
+ *
+ * @access public
+ * @return void
+ */
+	function testMinLength() {
+		$this->assertFalse(Validation::minLength('ab', 3));
+		$this->assertFalse(Validation::minLength('ÆΔΩЖÇ', 10));
+
+		$this->assertTrue(Validation::minLength('abc', 3));
+		$this->assertTrue(Validation::minLength('abcd', 3));
+		$this->assertTrue(Validation::minLength('ÆΔΩЖÇ', 2));
+	}
+
+/**
+ * testUrl method
+ *
+ * @access public
+ * @return void
+ */
+	function testUrl() {
+		$this->assertTrue(Validation::url('http://www.cakephp.org'));
+		$this->assertTrue(Validation::url('http://cakephp.org'));
+		$this->assertTrue(Validation::url('http://www.cakephp.org/somewhere#anchor'));
+		$this->assertTrue(Validation::url('http://192.168.0.1'));
+		$this->assertTrue(Validation::url('https://www.cakephp.org'));
+		$this->assertTrue(Validation::url('https://cakephp.org'));
+		$this->assertTrue(Validation::url('https://www.cakephp.org/somewhere#anchor'));
+		$this->assertTrue(Validation::url('https://192.168.0.1'));
+		$this->assertTrue(Validation::url('ftps://www.cakephp.org/pub/cake'));
+		$this->assertTrue(Validation::url('ftps://cakephp.org/pub/cake'));
+		$this->assertTrue(Validation::url('ftps://192.168.0.1/pub/cake'));
+		$this->assertTrue(Validation::url('ftp://www.cakephp.org/pub/cake'));
+		$this->assertTrue(Validation::url('ftp://cakephp.org/pub/cake'));
+		$this->assertTrue(Validation::url('ftp://192.168.0.1/pub/cake'));
+		$this->assertFalse(Validation::url('ftps://256.168.0.1/pub/cake'));
+		$this->assertFalse(Validation::url('ftp://256.168.0.1/pub/cake'));
+		$this->assertTrue(Validation::url('https://my.domain.com/gizmo/app?class=MySip;proc=start'));
+		$this->assertTrue(Validation::url('www.domain.tld'));
+		$this->assertFalse(Validation::url('http://w_w.domain.co_m'));
+		$this->assertFalse(Validation::url('http://www.domain.12com'));
+		$this->assertFalse(Validation::url('http://www.domain.longttldnotallowed'));
+		$this->assertFalse(Validation::url('http://www.-invaliddomain.tld'));
+		$this->assertFalse(Validation::url('http://www.domain.-invalidtld'));
+		$this->assertTrue(Validation::url('http://123456789112345678921234567893123456789412345678951234567896123.com'));
+		$this->assertFalse(Validation::url('http://this-domain-is-too-loooooong-by-icann-rules-maximum-length-is-63.com'));
+		$this->assertTrue(Validation::url('http://www.domain.com/blogs/index.php?blog=6&tempskin=_rss2'));
+		$this->assertTrue(Validation::url('http://www.domain.com/blogs/parenth()eses.php'));
+		$this->assertTrue(Validation::url('http://www.domain.com/index.php?get=params&amp;get2=params'));
+		$this->assertTrue(Validation::url('http://www.domain.com/ndex.php?get=params&amp;get2=params#anchor'));
+		$this->assertFalse(Validation::url('http://www.domain.com/fakeenco%ode'));
+		$this->assertTrue(Validation::url('http://www.domain.com/real%20url%20encodeing'));
+		$this->assertTrue(Validation::url('http://en.wikipedia.org/wiki/Architectural_pattern_(computer_science)'));
+		$this->assertFalse(Validation::url('http://en.(wikipedia).org/'));
+		$this->assertFalse(Validation::url('www.cakephp.org', true));
+		$this->assertTrue(Validation::url('http://www.cakephp.org', true));
+		$this->assertTrue(Validation::url('http://example.com/~userdir/'));
+		$this->assertTrue(Validation::url('http://example.com/~userdir/subdir/index.html'));
+		$this->assertTrue(Validation::url('http://www.zwischenraume.de'));
+		$this->assertTrue(Validation::url('http://www.zwischenraume.cz'));
+		$this->assertTrue(Validation::url('http://www.last.fm/music/浜崎あゆみ'), 'utf8 path failed');
+		$this->assertTrue(Validation::url('http://www.electrohome.ro/images/239537750-284232-215_300[1].jpg'));
+
+		$this->assertTrue(Validation::url('http://cakephp.org:80'));
+		$this->assertTrue(Validation::url('http://cakephp.org:443'));
+		$this->assertTrue(Validation::url('http://cakephp.org:2000'));
+		$this->assertTrue(Validation::url('http://cakephp.org:27000'));
+		$this->assertTrue(Validation::url('http://cakephp.org:65000'));
+
+		$this->assertTrue(Validation::url('[2001:0db8::1428:57ab]'));
+		$this->assertTrue(Validation::url('[::1]'));
+		$this->assertTrue(Validation::url('[2001:0db8::1428:57ab]:80'));
+		$this->assertTrue(Validation::url('[::1]:80'));
+		$this->assertTrue(Validation::url('http://[2001:0db8::1428:57ab]'));
+		$this->assertTrue(Validation::url('http://[::1]'));
+		$this->assertTrue(Validation::url('http://[2001:0db8::1428:57ab]:80'));
+		$this->assertTrue(Validation::url('http://[::1]:80'));
+
+		$this->assertFalse(Validation::url('[1::2::3]'));
+	}
+
+	function testUuid() {
+		$this->assertTrue(Validation::uuid('550e8400-e29b-11d4-a716-446655440000'));
+		$this->assertFalse(Validation::uuid('BRAP-e29b-11d4-a716-446655440000'));
+		$this->assertTrue(Validation::uuid('550E8400-e29b-11D4-A716-446655440000'));
+		$this->assertFalse(Validation::uuid('550e8400-e29b11d4-a716-446655440000'));
+		$this->assertFalse(Validation::uuid('550e8400-e29b-11d4-a716-4466440000'));
+		$this->assertFalse(Validation::uuid('550e8400-e29b-11d4-a71-446655440000'));
+		$this->assertFalse(Validation::uuid('550e8400-e29b-11d-a716-446655440000'));
+		$this->assertFalse(Validation::uuid('550e8400-e29-11d4-a716-446655440000'));
+	}
+
+/**
+ * testInList method
+ *
+ * @access public
+ * @return void
+ */
+	function testInList() {
+		$this->assertTrue(Validation::inList('one', array('one', 'two')));
+		$this->assertTrue(Validation::inList('two', array('one', 'two')));
+		$this->assertFalse(Validation::inList('three', array('one', 'two')));
+	}
+
+/**
+ * testRange method
+ *
+ * @access public
+ * @return void
+ */
+	function testRange() {
+		$this->assertFalse(Validation::range(20, 100, 1));
+		$this->assertTrue(Validation::range(20, 1, 100));
+		$this->assertFalse(Validation::range(.5, 1, 100));
+		$this->assertTrue(Validation::range(.5, 0, 100));
+		$this->assertTrue(Validation::range(5));
+		$this->assertTrue(Validation::range(-5, -10, 1));
+		$this->assertFalse(Validation::range('word'));
+	}
+
+/**
+ * testExtension method
+ *
+ * @access public
+ * @return void
+ */
+	function testExtension() {
+		$this->assertTrue(Validation::extension('extension.jpeg'));
+		$this->assertTrue(Validation::extension('extension.JPEG'));
+		$this->assertTrue(Validation::extension('extension.gif'));
+		$this->assertTrue(Validation::extension('extension.GIF'));
+		$this->assertTrue(Validation::extension('extension.png'));
+		$this->assertTrue(Validation::extension('extension.jpg'));
+		$this->assertTrue(Validation::extension('extension.JPG'));
+		$this->assertFalse(Validation::extension('noextension'));
+		$this->assertTrue(Validation::extension('extension.pdf', array('PDF')));
+		$this->assertFalse(Validation::extension('extension.jpg', array('GIF')));
+		$this->assertTrue(Validation::extension(array('extension.JPG', 'extension.gif', 'extension.png')));
+		$this->assertTrue(Validation::extension(array('file' => array('name' => 'file.jpg'))));
+		$this->assertTrue(Validation::extension(array('file1' => array('name' => 'file.jpg'),
+												'file2' => array('name' => 'file.jpg'),
+												'file3' => array('name' => 'file.jpg'))));
+		$this->assertFalse(Validation::extension(array('file1' => array('name' => 'file.jpg'),
+												'file2' => array('name' => 'file.jpg'),
+												'file3' => array('name' => 'file.jpg')), array('gif')));
+
+		$this->assertFalse(Validation::extension(array('noextension', 'extension.JPG', 'extension.gif', 'extension.png')));
+		$this->assertFalse(Validation::extension(array('extension.pdf', 'extension.JPG', 'extension.gif', 'extension.png')));
+	}
+
+/**
+ * testMoney method
+ *
+ * @access public
+ * @return void
+ */
+	function testMoney() {
+		$this->assertTrue(Validation::money('$100'));
+		$this->assertTrue(Validation::money('$100.11'));
+		$this->assertTrue(Validation::money('$100.112'));
+		$this->assertFalse(Validation::money('$100.1'));
+		$this->assertFalse(Validation::money('$100.1111'));
+		$this->assertFalse(Validation::money('text'));
+
+		$this->assertTrue(Validation::money('100', 'right'));
+		$this->assertTrue(Validation::money('100.11$', 'right'));
+		$this->assertTrue(Validation::money('100.112$', 'right'));
+		$this->assertFalse(Validation::money('100.1$', 'right'));
+		$this->assertFalse(Validation::money('100.1111$', 'right'));
+
+		$this->assertTrue(Validation::money('€100'));
+		$this->assertTrue(Validation::money('€100.11'));
+		$this->assertTrue(Validation::money('€100.112'));
+		$this->assertFalse(Validation::money('€100.1'));
+		$this->assertFalse(Validation::money('€100.1111'));
+
+		$this->assertTrue(Validation::money('100', 'right'));
+		$this->assertTrue(Validation::money('100.11€', 'right'));
+		$this->assertTrue(Validation::money('100.112€', 'right'));
+		$this->assertFalse(Validation::money('100.1€', 'right'));
+		$this->assertFalse(Validation::money('100.1111€', 'right'));
+	}
+
+/**
+ * Test Multiple Select Validation
+ *
+ * @access public
+ * @return void
+ */
+	function testMultiple() {
+		$this->assertTrue(Validation::multiple(array(0, 1, 2, 3)));
+		$this->assertTrue(Validation::multiple(array(50, 32, 22, 0)));
+		$this->assertTrue(Validation::multiple(array('str', 'var', 'enum', 0)));
+		$this->assertFalse(Validation::multiple(''));
+		$this->assertFalse(Validation::multiple(null));
+		$this->assertFalse(Validation::multiple(array()));
+		$this->assertFalse(Validation::multiple(array(0)));
+		$this->assertFalse(Validation::multiple(array('0')));
+
+		$this->assertTrue(Validation::multiple(array(0, 3, 4, 5), array('in' => range(0, 10))));
+		$this->assertFalse(Validation::multiple(array(0, 15, 20, 5), array('in' => range(0, 10))));
+		$this->assertFalse(Validation::multiple(array(0, 5, 10, 11), array('in' => range(0, 10))));
+		$this->assertFalse(Validation::multiple(array('boo', 'foo', 'bar'), array('in' => array('foo', 'bar', 'baz'))));
+
+		$this->assertTrue(Validation::multiple(array(0, 5, 10, 11), array('max' => 3)));
+		$this->assertFalse(Validation::multiple(array(0, 5, 10, 11, 55), array('max' => 3)));
+		$this->assertTrue(Validation::multiple(array('foo', 'bar', 'baz'), array('max' => 3)));
+		$this->assertFalse(Validation::multiple(array('foo', 'bar', 'baz', 'squirrel'), array('max' => 3)));
+
+		$this->assertTrue(Validation::multiple(array(0, 5, 10, 11), array('min' => 3)));
+		$this->assertTrue(Validation::multiple(array(0, 5, 10, 11, 55), array('min' => 3)));
+		$this->assertFalse(Validation::multiple(array('foo', 'bar', 'baz'), array('min' => 5)));
+		$this->assertFalse(Validation::multiple(array('foo', 'bar', 'baz', 'squirrel'), array('min' => 10)));
+
+		$this->assertTrue(Validation::multiple(array(0, 5, 9), array('in' => range(0, 10), 'max' => 5)));
+		$this->assertFalse(Validation::multiple(array(0, 5, 9, 8, 6, 2, 1), array('in' => range(0, 10), 'max' => 5)));
+		$this->assertFalse(Validation::multiple(array(0, 5, 9, 8, 11), array('in' => range(0, 10), 'max' => 5)));
+
+		$this->assertFalse(Validation::multiple(array(0, 5, 9), array('in' => range(0, 10), 'max' => 5, 'min' => 3)));
+		$this->assertFalse(Validation::multiple(array(0, 5, 9, 8, 6, 2, 1), array('in' => range(0, 10), 'max' => 5, 'min' => 2)));
+		$this->assertFalse(Validation::multiple(array(0, 5, 9, 8, 11), array('in' => range(0, 10), 'max' => 5, 'min' => 2)));
+	}
+
+/**
+ * testNumeric method
+ *
+ * @access public
+ * @return void
+ */
+	function testNumeric() {
+		$this->assertFalse(Validation::numeric('teststring'));
+		$this->assertFalse(Validation::numeric('1.1test'));
+		$this->assertFalse(Validation::numeric('2test'));
+
+		$this->assertTrue(Validation::numeric('2'));
+		$this->assertTrue(Validation::numeric(2));
+		$this->assertTrue(Validation::numeric(2.2));
+		$this->assertTrue(Validation::numeric('2.2'));
+	}
+
+/**
+ * testPhone method
+ *
+ * @access public
+ * @return void
+ */
+	function testPhone() {
+		$this->assertFalse(Validation::phone('teststring'));
+		$this->assertFalse(Validation::phone('1-(33)-(333)-(4444)'));
+		$this->assertFalse(Validation::phone('1-(33)-3333-4444'));
+		$this->assertFalse(Validation::phone('1-(33)-33-4444'));
+		$this->assertFalse(Validation::phone('1-(33)-3-44444'));
+		$this->assertFalse(Validation::phone('1-(33)-3-444'));
+		$this->assertFalse(Validation::phone('1-(33)-3-44'));
+
+		$this->assertFalse(Validation::phone('(055) 999-9999'));
+		$this->assertFalse(Validation::phone('(155) 999-9999'));
+		$this->assertFalse(Validation::phone('(595) 999-9999'));
+		$this->assertFalse(Validation::phone('(555) 099-9999'));
+		$this->assertFalse(Validation::phone('(555) 199-9999'));
+
+		$this->assertTrue(Validation::phone('1 (222) 333 4444'));
+		$this->assertTrue(Validation::phone('+1 (222) 333 4444'));
+		$this->assertTrue(Validation::phone('(222) 333 4444'));
+
+		$this->assertTrue(Validation::phone('1-(333)-333-4444'));
+		$this->assertTrue(Validation::phone('1.(333)-333-4444'));
+		$this->assertTrue(Validation::phone('1.(333).333-4444'));
+		$this->assertTrue(Validation::phone('1.(333).333.4444'));
+		$this->assertTrue(Validation::phone('1-333-333-4444'));
+	}
+
+/**
+ * testPostal method
+ *
+ * @access public
+ * @return void
+ */
+	function testPostal() {
+		$this->assertFalse(Validation::postal('111', null, 'de'));
+		$this->assertFalse(Validation::postal('1111', null, 'de'));
+		$this->assertTrue(Validation::postal('13089', null, 'de'));
+
+		$this->assertFalse(Validation::postal('111', null, 'be'));
+		$this->assertFalse(Validation::postal('0123', null, 'be'));
+		$this->assertTrue(Validation::postal('1204', null, 'be'));
+
+		$this->assertFalse(Validation::postal('111', null, 'it'));
+		$this->assertFalse(Validation::postal('1111', null, 'it'));
+		$this->assertTrue(Validation::postal('13089', null, 'it'));
+
+		$this->assertFalse(Validation::postal('111', null, 'uk'));
+		$this->assertFalse(Validation::postal('1111', null, 'uk'));
+		$this->assertFalse(Validation::postal('AZA 0AB', null, 'uk'));
+		$this->assertFalse(Validation::postal('X0A 0ABC', null, 'uk'));
+		$this->assertTrue(Validation::postal('X0A 0AB', null, 'uk'));
+		$this->assertTrue(Validation::postal('AZ0A 0AA', null, 'uk'));
+		$this->assertTrue(Validation::postal('A89 2DD', null, 'uk'));
+
+		$this->assertFalse(Validation::postal('111', null, 'ca'));
+		$this->assertFalse(Validation::postal('1111', null, 'ca'));
+		$this->assertFalse(Validation::postal('D2A 0A0', null, 'ca'));
+		$this->assertFalse(Validation::postal('BAA 0ABC', null, 'ca'));
+		$this->assertFalse(Validation::postal('B2A AABC', null, 'ca'));
+		$this->assertFalse(Validation::postal('B2A 2AB', null, 'ca'));
+		$this->assertTrue(Validation::postal('X0A 0A2', null, 'ca'));
+		$this->assertTrue(Validation::postal('G4V 4C3', null, 'ca'));
+		$this->assertTrue(Validation::postal('L4J8D6', null, 'ca'));
+
+		$this->assertFalse(Validation::postal('111', null, 'us'));
+		$this->assertFalse(Validation::postal('1111', null, 'us'));
+		$this->assertFalse(Validation::postal('130896', null, 'us'));
+		$this->assertFalse(Validation::postal('13089-33333', null, 'us'));
+		$this->assertFalse(Validation::postal('13089-333', null, 'us'));
+		$this->assertFalse(Validation::postal('13A89-4333', null, 'us'));
+		$this->assertTrue(Validation::postal('13089-3333', null, 'us'));
+
+		$this->assertFalse(Validation::postal('111'));
+		$this->assertFalse(Validation::postal('1111'));
+		$this->assertFalse(Validation::postal('130896'));
+		$this->assertFalse(Validation::postal('13089-33333'));
+		$this->assertFalse(Validation::postal('13089-333'));
+		$this->assertFalse(Validation::postal('13A89-4333'));
+		$this->assertTrue(Validation::postal('13089-3333'));
+	}
+
+/**
+ * test that phone and postal pass to other classes.
+ *
+ * @return void
+ */
+	function testPhonePostalSsnPass() {
+		$this->assertTrue(Validation::postal('text', null, 'testNl'));
+		$this->assertTrue(Validation::phone('text', null, 'testDe'));
+		$this->assertTrue(Validation::ssn('text', null, 'testNl'));
+	}
+
+/**
+ * test the pass through calling of an alternate locale with postal()
+ *
+ * @return void
+ **/
+	function testPassThroughMethod() {
+		$this->assertTrue(Validation::postal('text', null, 'testNl'));
+
+		$this->expectError('Could not find AUTOFAILValidation class, unable to complete validation.');
+		Validation::postal('text', null, 'AUTOFAIL');
+
+		$this->expectError('Method phone does not exist on TestNlValidation unable to complete validation.');
+		Validation::phone('text', null, 'testNl');
+	}
+
+/**
+ * testSsn method
+ *
+ * @access public
+ * @return void
+ */
+	function testSsn() {
+		$this->assertFalse(Validation::ssn('111-333', null, 'dk'));
+		$this->assertFalse(Validation::ssn('111111-333', null, 'dk'));
+		$this->assertTrue(Validation::ssn('111111-3334', null, 'dk'));
+
+		$this->assertFalse(Validation::ssn('1118333', null, 'nl'));
+		$this->assertFalse(Validation::ssn('1234567890', null, 'nl'));
+		$this->assertFalse(Validation::ssn('12345A789', null, 'nl'));
+		$this->assertTrue(Validation::ssn('123456789', null, 'nl'));
+
+		$this->assertFalse(Validation::ssn('11-33-4333', null, 'us'));
+		$this->assertFalse(Validation::ssn('113-3-4333', null, 'us'));
+		$this->assertFalse(Validation::ssn('111-33-333', null, 'us'));
+		$this->assertTrue(Validation::ssn('111-33-4333', null, 'us'));
+	}
+
+/**
+ * testUserDefined method
+ *
+ * @access public
+ * @return void
+ */
+	function testUserDefined() {
+		$validator = new CustomValidator;
+		$this->assertFalse(Validation::userDefined('33', $validator, 'customValidate'));
+		$this->assertFalse(Validation::userDefined('3333', $validator, 'customValidate'));
+		$this->assertTrue(Validation::userDefined('333', $validator, 'customValidate'));
+	}
+
+}

Added: trunk/src/Web/cake/tests/cases/libs/view/helper.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/view/helper.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/view/helper.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,804 @@
+<?php
+/**
+ * HelperTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', array('View', 'Helper'));
+
+/**
+ * HelperTestPost class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view
+ */
+class HelperTestPost extends Model {
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function schema() {
+		$this->_schema = array(
+			'id' => array('type' => 'integer', 'null' => false, 'default' => '', 'length' => '8'),
+			'title' => array('type' => 'string', 'null' => false, 'default' => '', 'length' => '255'),
+			'body' => array('type' => 'string', 'null' => true, 'default' => '', 'length' => ''),
+			'number' => array('type' => 'integer', 'null' => false, 'default' => '', 'length' => '8'),
+			'date' => array('type' => 'date', 'null' => true, 'default' => '', 'length' => ''),
+			'created' => array('type' => 'date', 'null' => true, 'default' => '', 'length' => ''),
+			'modified' => array('type' => 'datetime', 'null' => true, 'default' => '', 'length' => null)
+		);
+		return $this->_schema;
+	}
+
+/**
+ * hasAndBelongsToMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('HelperTestTag'=> array('with' => 'HelperTestPostsTag'));
+}
+
+/**
+ * HelperTestComment class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view
+ */
+class HelperTestComment extends Model {
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function schema() {
+		$this->_schema = array(
+			'id' => array('type' => 'integer', 'null' => false, 'default' => '', 'length' => '8'),
+			'author_id' => array('type' => 'integer', 'null' => false, 'default' => '', 'length' => '8'),
+			'title' => array('type' => 'string', 'null' => false, 'default' => '', 'length' => '255'),
+			'body' => array('type' => 'string', 'null' => true, 'default' => '', 'length' => ''),
+			'created' => array('type' => 'date', 'null' => true, 'default' => '', 'length' => ''),
+			'modified' => array('type' => 'datetime', 'null' => true, 'default' => '', 'length' => null)
+		);
+		return $this->_schema;
+	}
+}
+
+/**
+ * HelperTestTag class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view
+ */
+class HelperTestTag extends Model {
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function schema() {
+		$this->_schema = array(
+			'id' => array('type' => 'integer', 'null' => false, 'default' => '', 'length' => '8'),
+			'name' => array('type' => 'string', 'null' => false, 'default' => '', 'length' => '255'),
+			'created' => array('type' => 'date', 'null' => true, 'default' => '', 'length' => ''),
+			'modified' => array('type' => 'datetime', 'null' => true, 'default' => '', 'length' => null)
+		);
+		return $this->_schema;
+	}
+}
+
+/**
+ * HelperTestPostsTag class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view
+ */
+class HelperTestPostsTag extends Model {
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function schema() {
+		$this->_schema = array(
+			'helper_test_post_id' => array('type' => 'integer', 'null' => false, 'default' => '', 'length' => '8'),
+			'helper_test_tag_id' => array('type' => 'integer', 'null' => false, 'default' => '', 'length' => '8'),
+		);
+		return $this->_schema;
+	}
+}
+
+class TestHelper extends Helper {
+/**
+ * expose a method as public
+ *
+ * @param string $options 
+ * @param string $exclude 
+ * @param string $insertBefore 
+ * @param string $insertAfter 
+ * @return void
+ */
+	function parseAttributes($options, $exclude = null, $insertBefore = ' ', $insertAfter = null) {
+		return $this->_parseAttributes($options, $exclude, $insertBefore, $insertAfter);
+	}
+}
+
+/**
+ * HelperTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class HelperTest extends CakeTestCase {
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		ClassRegistry::flush();
+		Router::reload();
+		$null = null;
+		$this->View = new View($null);
+		$this->Helper = new Helper();
+		ClassRegistry::addObject('HelperTestPost', new HelperTestPost());
+		ClassRegistry::addObject('HelperTestComment', new HelperTestComment());
+		ClassRegistry::addObject('HelperTestTag', new HelperTestTag());
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		unset($this->Helper, $this->View);
+		ClassRegistry::flush();
+	}
+
+/**
+ * testFormFieldNameParsing method
+ *
+ * @access public
+ * @return void
+ */
+	function testSetEntity() {
+		// PHP4 reference hack
+		ClassRegistry::removeObject('view');
+		ClassRegistry::addObject('view', $this->View);
+
+		$this->Helper->setEntity('HelperTestPost.id');
+		$this->assertFalse($this->View->modelScope);
+		$this->assertEqual($this->View->model, 'HelperTestPost');
+		$this->assertEqual($this->View->field, 'id');
+		$this->assertEqual($this->View->modelId, null);
+		$this->assertEqual($this->View->association, null);
+
+		$this->Helper->setEntity('HelperTestComment.body');
+		$this->assertFalse($this->View->modelScope);
+		$this->assertEqual($this->View->model, 'HelperTestComment');
+		$this->assertEqual($this->View->field, 'body');
+		$this->assertEqual($this->View->modelId, null);
+		$this->assertEqual($this->View->association, null);
+		$this->assertEqual($this->View->fieldSuffix, null);
+
+		$this->Helper->setEntity('HelperTestPost', true);
+		$this->assertTrue($this->View->modelScope);
+		$this->assertEqual($this->View->model, 'HelperTestPost');
+		$this->assertEqual($this->View->field, null);
+		$this->assertEqual($this->View->modelId, null);
+		$this->assertEqual($this->View->association, null);
+		$this->assertEqual($this->View->fieldSuffix, null);
+
+		$this->Helper->setEntity('_Token.fields');
+		$this->assertTrue($this->View->modelScope);
+		$this->assertEqual($this->View->model, 'HelperTestPost');
+		$this->assertEqual($this->View->field, 'fields');
+		$this->assertEqual($this->View->modelId, null);
+		$this->assertEqual($this->View->association, '_Token');
+		$this->assertEqual($this->View->fieldSuffix, null);
+
+
+		$this->Helper->setEntity('id');
+		$this->assertTrue($this->View->modelScope);
+		$this->assertEqual($this->View->model, 'HelperTestPost');
+		$this->assertEqual($this->View->field, 'id');
+		$this->assertEqual($this->View->modelId, null);
+		$this->assertEqual($this->View->association, null);
+		$this->assertEqual($this->View->fieldSuffix, null);
+
+		$this->Helper->setEntity('HelperTestComment.body');
+		$this->assertTrue($this->View->modelScope);
+		$this->assertEqual($this->View->model, 'HelperTestPost');
+		$this->assertEqual($this->View->field, 'body');
+		$this->assertEqual($this->View->modelId, null);
+		$this->assertEqual($this->View->association, 'HelperTestComment');
+		$this->assertEqual($this->View->fieldSuffix, null);
+
+		$this->Helper->setEntity('body');
+		$this->assertTrue($this->View->modelScope);
+		$this->assertEqual($this->View->model, 'HelperTestPost');
+		$this->assertEqual($this->View->field, 'body');
+		$this->assertEqual($this->View->modelId, null);
+		$this->assertEqual($this->View->association, null);
+		$this->assertEqual($this->View->fieldSuffix, null);
+
+		$this->Helper->setEntity('Something.else');
+		$this->assertTrue($this->View->modelScope);
+		$this->assertEqual($this->View->model, 'HelperTestPost');
+		$this->assertEqual($this->View->field, 'else');
+		$this->assertEqual($this->View->modelId, false);
+		$this->assertEqual($this->View->association, 'Something');
+		$this->assertEqual($this->View->fieldSuffix, '');
+
+		$this->Helper->setEntity('5.id');
+		$this->assertTrue($this->View->modelScope);
+		$this->assertEqual($this->View->model, 'HelperTestPost');
+		$this->assertEqual($this->View->field, 'id');
+		$this->assertEqual($this->View->modelId, '5');
+		$this->assertEqual($this->View->association, null);
+		$this->assertEqual($this->View->fieldSuffix, null);
+
+		$this->assertEqual($this->View->entity(), array('HelperTestPost', 5, 'id'));
+
+		$this->Helper->setEntity('0.id');
+		$this->assertTrue($this->View->modelScope);
+		$this->assertEqual($this->View->model, 'HelperTestPost');
+		$this->assertEqual($this->View->field, 'id');
+		$this->assertEqual($this->View->modelId, '0');
+		$this->assertEqual($this->View->association, null);
+		$this->assertEqual($this->View->fieldSuffix, null);
+
+		$this->assertEqual($this->View->entity(), array('HelperTestPost', 0, 'id'));
+
+		$this->Helper->setEntity('5.created.month');
+		$this->assertTrue($this->View->modelScope);
+		$this->assertEqual($this->View->model, 'HelperTestPost');
+		$this->assertEqual($this->View->field, 'created');
+		$this->assertEqual($this->View->modelId, '5');
+		$this->assertEqual($this->View->association, null);
+		$this->assertEqual($this->View->fieldSuffix, 'month');
+
+		$this->Helper->setEntity('HelperTestComment.5.id');
+		$this->assertTrue($this->View->modelScope);
+		$this->assertEqual($this->View->model, 'HelperTestPost');
+		$this->assertEqual($this->View->field, 'id');
+		$this->assertEqual($this->View->modelId, '5');
+		$this->assertEqual($this->View->association, 'HelperTestComment');
+		$this->assertEqual($this->View->fieldSuffix, null);
+
+		$this->Helper->setEntity('HelperTestComment.id.time');
+		$this->assertTrue($this->View->modelScope);
+		$this->assertEqual($this->View->model, 'HelperTestPost');
+		$this->assertEqual($this->View->field, 'id');
+		$this->assertEqual($this->View->modelId, null);
+		$this->assertEqual($this->View->association, 'HelperTestComment');
+		$this->assertEqual($this->View->fieldSuffix, 'time');
+
+		$this->Helper->setEntity('HelperTestTag');
+		$this->assertTrue($this->View->modelScope);
+		$this->assertEqual($this->View->model, 'HelperTestPost');
+		$this->assertEqual($this->View->field, 'HelperTestTag');
+		$this->assertEqual($this->View->modelId, '');
+		$this->assertEqual($this->View->association, 'HelperTestTag');
+		$this->assertEqual($this->View->fieldSuffix, '');
+
+		$this->Helper->setEntity(null);
+		$this->Helper->setEntity('ModelThatDoesntExist.field_that_doesnt_exist');
+		$this->assertFalse($this->View->modelScope);
+		$this->assertEqual($this->View->model, 'ModelThatDoesntExist');
+		$this->assertEqual($this->View->field, 'field_that_doesnt_exist');
+		$this->assertEqual($this->View->modelId, null);
+		$this->assertEqual($this->View->association, null);
+		$this->assertEqual($this->View->fieldSuffix, null);
+	}
+
+/**
+ * test that 'view' doesn't break things.
+ *
+ * @return void
+ */
+	function testSetEntityWithView() {
+		$this->assertNull($this->Helper->setEntity('Allow.view.group_id'));
+		$this->assertNull($this->Helper->setEntity('Allow.view'));
+		$this->assertNull($this->Helper->setEntity('View.view'));
+	}
+
+/**
+ * test getting values from Helper
+ *
+ * @return void
+ */
+	function testValue() {
+		$this->Helper->data = array('fullname' => 'This is me');
+		$this->Helper->setEntity('fullname');
+		$result = $this->Helper->value('fullname');
+		$this->assertEqual($result, 'This is me');
+
+		$this->Helper->data = array('Post' => array('name' => 'First Post'));
+		$this->Helper->setEntity('Post.name');
+		$result = $this->Helper->value('Post.name');
+		$this->assertEqual($result, 'First Post');
+
+		$this->Helper->data = array('Post' => array(2 => array('name' => 'First Post')));
+		$this->Helper->setEntity('Post.2.name');
+		$result = $this->Helper->value('Post.2.name');
+		$this->assertEqual($result, 'First Post');
+
+		$this->Helper->data = array('Post' => array(2 => array('created' => array('year' => '2008'))));
+		$this->Helper->setEntity('Post.2.created');
+		$result = $this->Helper->value('Post.2.created');
+		$this->assertEqual($result, array('year' => '2008'));
+
+		$this->Helper->data = array('Post' => array(2 => array('created' => array('year' => '2008'))));
+		$this->Helper->setEntity('Post.2.created.year');
+		$result = $this->Helper->value('Post.2.created.year');
+		$this->assertEqual($result, '2008');
+
+		$this->Helper->data = array('HelperTestTag' => array('HelperTestTag' => ''));
+		$this->Helper->setEntity('HelperTestTag.HelperTestTag');
+		$result = $this->Helper->value('HelperTestTag.HelperTestTag');
+		$this->assertEqual($result, '');
+
+		$this->Helper->data = array('HelperTestTag' => array('HelperTestTag' => array(2, 3, 4)));
+		$this->Helper->setEntity('HelperTestTag.HelperTestTag');
+		$result = $this->Helper->value('HelperTestTag.HelperTestTag');
+		$this->assertEqual($result, array(2, 3, 4));
+
+		$this->Helper->data = array(
+			'HelperTestTag' => array(
+				array('id' => 3),
+				array('id' => 5)
+			)
+		);
+		$this->Helper->setEntity('HelperTestTag.HelperTestTag');
+		$result = $this->Helper->value('HelperTestTag.HelperTestTag');
+		$this->assertEqual($result, array(3 => 3, 5 => 5));
+
+		$this->Helper->data = array('zero' => 0);
+		$this->Helper->setEntity('zero');
+		$result = $this->Helper->value(array('default' => 'something'), 'zero');
+		$this->assertEqual($result, array('value' => 0));
+
+		$this->Helper->data = array('zero' => '0');
+		$result = $this->Helper->value(array('default' => 'something'), 'zero');
+		$this->assertEqual($result, array('value' => '0'));
+
+		$this->Helper->setEntity('inexistent');
+		$result = $this->Helper->value(array('default' => 'something'), 'inexistent');
+		$this->assertEqual($result, array('value' => 'something'));
+	}
+
+/**
+ * Ensure HTML escaping of url params.  So link addresses are valid and not exploited
+ *
+ * @return void
+ */
+	function testUrlConversion() {
+		$result = $this->Helper->url('/controller/action/1');
+		$this->assertEqual($result, '/controller/action/1');
+
+		$result = $this->Helper->url('/controller/action/1?one=1&two=2');
+		$this->assertEqual($result, '/controller/action/1?one=1&amp;two=2');
+
+		$result = $this->Helper->url(array('controller' => 'posts', 'action' => 'index', 'page' => '1" onclick="alert(\'XSS\');"'));
+		$this->assertEqual($result, "/posts/index/page:1&quot; onclick=&quot;alert(&#039;XSS&#039;);&quot;");
+
+		$result = $this->Helper->url('/controller/action/1/param:this+one+more');
+		$this->assertEqual($result, '/controller/action/1/param:this+one+more');
+
+		$result = $this->Helper->url('/controller/action/1/param:this%20one%20more');
+		$this->assertEqual($result, '/controller/action/1/param:this%20one%20more');
+
+		$result = $this->Helper->url('/controller/action/1/param:%7Baround%20here%7D%5Bthings%5D%5Bare%5D%24%24');
+		$this->assertEqual($result, '/controller/action/1/param:%7Baround%20here%7D%5Bthings%5D%5Bare%5D%24%24');
+
+		$result = $this->Helper->url(array(
+			'controller' => 'posts', 'action' => 'index', 'param' => '%7Baround%20here%7D%5Bthings%5D%5Bare%5D%24%24'
+		));
+		$this->assertEqual($result, "/posts/index/param:%7Baround%20here%7D%5Bthings%5D%5Bare%5D%24%24");
+
+		$result = $this->Helper->url(array(
+			'controller' => 'posts', 'action' => 'index', 'page' => '1',
+			'?' => array('one' => 'value', 'two' => 'value', 'three' => 'purple')
+		));
+		$this->assertEqual($result, "/posts/index/page:1?one=value&amp;two=value&amp;three=purple");
+	}
+
+/**
+ * test assetTimestamp application
+ *
+ * @return void
+ */
+	function testAssetTimestamp() {
+		$_timestamp = Configure::read('Asset.timestamp');
+		$_debug = Configure::read('debug');
+
+		Configure::write('Asset.timestamp', false);
+		$result = $this->Helper->assetTimestamp(CSS_URL . 'cake.generic.css');
+		$this->assertEqual($result, CSS_URL . 'cake.generic.css');
+
+		Configure::write('Asset.timestamp', true);
+		Configure::write('debug', 0);
+		$result = $this->Helper->assetTimestamp(CSS_URL . 'cake.generic.css');
+		$this->assertEqual($result, CSS_URL . 'cake.generic.css');
+
+		Configure::write('Asset.timestamp', true);
+		Configure::write('debug', 2);
+		$result = $this->Helper->assetTimestamp(CSS_URL . 'cake.generic.css');
+		$this->assertPattern('/' . preg_quote(CSS_URL . 'cake.generic.css?', '/') . '[0-9]+/', $result);
+
+		Configure::write('Asset.timestamp', 'force');
+		Configure::write('debug', 0);
+		$result = $this->Helper->assetTimestamp(CSS_URL . 'cake.generic.css');
+		$this->assertPattern('/' . preg_quote(CSS_URL . 'cake.generic.css?', '/') . '[0-9]+/', $result);
+
+		$result = $this->Helper->assetTimestamp(CSS_URL . 'cake.generic.css?someparam');
+		$this->assertEqual($result, CSS_URL . 'cake.generic.css?someparam');
+
+		$this->Helper->webroot = '/some/dir/';
+		$result = $this->Helper->assetTimestamp('/some/dir/' . CSS_URL . 'cake.generic.css');
+		$this->assertPattern('/' . preg_quote(CSS_URL . 'cake.generic.css?', '/') . '[0-9]+/', $result);
+
+		Configure::write('debug', $_debug);
+		Configure::write('Asset.timestamp', $_timestamp);
+	}
+
+/**
+ * test assetTimestamp with plugins and themes
+ *
+ * @return void
+ */
+	function testAssetTimestampPluginsAndThemes() {
+		$_timestamp = Configure::read('Asset.timestamp');
+		Configure::write('Asset.timestamp', 'force');
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS),
+			'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS),
+		));
+
+		$result = $this->Helper->assetTimestamp('/test_plugin/css/test_plugin_asset.css');
+		$this->assertPattern('#/test_plugin/css/test_plugin_asset.css\?[0-9]+$#', $result, 'Missing timestamp plugin');
+
+		$result = $this->Helper->assetTimestamp('/test_plugin/css/i_dont_exist.css');
+		$this->assertPattern('#/test_plugin/css/i_dont_exist.css\?$#', $result, 'No error on missing file');
+
+		$result = $this->Helper->assetTimestamp('/theme/test_theme/js/theme.js');
+		$this->assertPattern('#/theme/test_theme/js/theme.js\?[0-9]+$#', $result, 'Missing timestamp theme');
+
+		$result = $this->Helper->assetTimestamp('/theme/test_theme/js/non_existant.js');
+		$this->assertPattern('#/theme/test_theme/js/non_existant.js\?$#', $result, 'No error on missing file');
+
+		App::build();
+		Configure::write('Asset.timestamp', $_timestamp);
+	}
+
+/**
+ * testFieldsWithSameName method
+ *
+ * @access public
+ * @return void
+ */
+	function testFieldsWithSameName() {
+		// PHP4 reference hack
+		ClassRegistry::removeObject('view');
+		ClassRegistry::addObject('view', $this->View);
+
+		$this->Helper->setEntity('HelperTestTag', true);
+
+		$this->Helper->setEntity('HelperTestTag.id');
+		$this->assertEqual($this->View->model, 'HelperTestTag');
+		$this->assertEqual($this->View->field, 'id');
+		$this->assertEqual($this->View->modelId, null);
+		$this->assertEqual($this->View->association, null);
+		$this->assertEqual($this->View->fieldSuffix, null);
+
+		$this->Helper->setEntity('My.id');
+		$this->assertEqual($this->View->model, 'HelperTestTag');
+		$this->assertEqual($this->View->field, 'id');
+		$this->assertEqual($this->View->modelId, null);
+		$this->assertEqual($this->View->association, 'My');
+		$this->assertEqual($this->View->fieldSuffix, null);
+
+		$this->Helper->setEntity('MyOther.id');
+		$this->assertEqual($this->View->model, 'HelperTestTag');
+		$this->assertEqual($this->View->field, 'id');
+		$this->assertEqual($this->View->modelId, null);
+		$this->assertEqual($this->View->association, 'MyOther');
+		$this->assertEqual($this->View->fieldSuffix, null);
+
+	}
+
+/**
+ * testFieldSameAsModel method
+ *
+ * @access public
+ * @return void
+ */
+	function testFieldSameAsModel() {
+		// PHP4 reference hack
+		ClassRegistry::removeObject('view');
+		ClassRegistry::addObject('view', $this->View);
+
+		$this->Helper->setEntity('HelperTestTag', true);
+
+		$this->Helper->setEntity('helper_test_post');
+		$this->assertEqual($this->View->model, 'HelperTestTag');
+		$this->assertEqual($this->View->field, 'helper_test_post');
+		$this->assertEqual($this->View->modelId, null);
+		$this->assertEqual($this->View->association, null);
+		$this->assertEqual($this->View->fieldSuffix, null);
+
+		$this->Helper->setEntity('HelperTestTag');
+		$this->assertEqual($this->View->model, 'HelperTestTag');
+		$this->assertEqual($this->View->field, 'HelperTestTag');
+		$this->assertEqual($this->View->modelId, null);
+		$this->assertEqual($this->View->association, null);
+		$this->assertEqual($this->View->fieldSuffix, null);
+		$this->assertEqual($this->View->entityPath, 'HelperTestTag');
+	}
+
+/**
+ * testFieldSuffixForDate method
+ *
+ * @access public
+ * @return void
+ */
+	function testFieldSuffixForDate() {
+		// PHP4 reference hack
+		ClassRegistry::removeObject('view');
+		ClassRegistry::addObject('view', $this->View);
+
+		$this->Helper->setEntity('HelperTestPost', true);
+		$this->assertEqual($this->View->model, 'HelperTestPost');
+		$this->assertEqual($this->View->field, null);
+		$this->assertEqual($this->View->modelId, null);
+		$this->assertEqual($this->View->association, null);
+		$this->assertEqual($this->View->fieldSuffix, null);
+
+		$this->Helper->setEntity('date.month');
+		$this->assertEqual($this->View->model, 'HelperTestPost');
+		$this->assertEqual($this->View->field, 'date');
+		$this->assertEqual($this->View->modelId, null);
+		$this->assertEqual($this->View->association, null);
+		$this->assertEqual($this->View->fieldSuffix, 'month');
+	}
+
+/**
+ * testMulitDimensionValue method
+ *
+ * @access public
+ * @return void
+ */
+	function testMulitDimensionValue() {
+		$this->Helper->data = array();
+		for ($i = 0; $i < 2; $i++) {
+			$this->Helper->data['Model'][$i] = 'what';
+			$result[] = $this->Helper->value("Model.{$i}");
+			$this->Helper->data['Model'][$i] = array();
+			for ($j = 0; $j < 2; $j++) {
+				$this->Helper->data['Model'][$i][$j] = 'how';
+				$result[] = $this->Helper->value("Model.{$i}.{$j}");
+			}
+		}
+		$expected = array('what', 'how', 'how', 'what', 'how', 'how');
+		$this->assertEqual($result, $expected);
+
+		$this->Helper->data['HelperTestComment']['5']['id'] = 'ok';
+		$result = $this->Helper->value('HelperTestComment.5.id');
+		$this->assertEqual($result, 'ok');
+
+		$this->Helper->setEntity('HelperTestPost', true);
+		$this->Helper->data['HelperTestPost']['5']['created']['month'] = '10';
+		$result = $this->Helper->value('5.created.month');
+		$this->assertEqual($result, 10);
+
+		$this->Helper->data['HelperTestPost']['0']['id'] = 100;
+		$result = $this->Helper->value('0.id');
+		$this->assertEqual($result, 100);
+	}
+
+/**
+ * testClean method
+ *
+ * @access public
+ * @return void
+ */
+	function testClean() {
+		$result = $this->Helper->clean(array());
+		$this->assertEqual($result, null);
+
+		$result = $this->Helper->clean(array('<script>with something</script>', '<applet>something else</applet>'));
+		$this->assertEqual($result, array('with something', 'something else'));
+
+		$result = $this->Helper->clean('<script>with something</script>');
+		$this->assertEqual($result, 'with something');
+
+		$result = $this->Helper->clean('<script type="text/javascript">alert("ruined");</script>');
+		$this->assertNoPattern('#</*script#', $result);
+
+		$result = $this->Helper->clean("<script \ntype=\"text/javascript\">\n\talert('ruined');\n\n\t\t</script>");
+		$this->assertNoPattern('#</*script#', $result);
+
+		$result = $this->Helper->clean('<body/onload=do(/something/)>');
+		$this->assertEqual($result, '<body/>');
+
+		$result = $this->Helper->clean('&lt;script&gt;alert(document.cookie)&lt;/script&gt;');
+		$this->assertEqual($result, '&amp;lt;script&amp;gt;alert(document.cookie)&amp;lt;/script&amp;gt;');
+	}
+
+/**
+ * testMultiDimensionalField method
+ *
+ * @access public
+ * @return void
+ */
+	function testMultiDimensionalField() {
+		// PHP4 reference hack
+		ClassRegistry::removeObject('view');
+		ClassRegistry::addObject('view', $this->View);
+
+		$this->Helper->setEntity('HelperTestPost', true);
+
+		$this->Helper->setEntity('HelperTestPost.2.HelperTestComment.1.title');
+		$this->assertEqual($this->View->model, 'HelperTestPost');
+		$this->assertEqual($this->View->association, 'HelperTestComment');
+		$this->assertEqual($this->View->modelId,2);
+		$this->assertEqual($this->View->field, 'title');
+
+		$this->Helper->setEntity('HelperTestPost.1.HelperTestComment.1.HelperTestTag.1.created');
+		$this->assertEqual($this->View->field,'created');
+		$this->assertEqual($this->View->association,'HelperTestTag');
+		$this->assertEqual($this->View->modelId,1);
+
+		$this->Helper->setEntity('HelperTestPost.0.HelperTestComment.1.HelperTestTag.1.fake');
+		$this->assertEqual($this->View->model,'HelperTestPost');
+		$this->assertEqual($this->View->association,'HelperTestTag');
+		$this->assertEqual($this->View->field,null);
+
+		$this->Helper->setEntity('1.HelperTestComment.1.HelperTestTag.created.year');
+		$this->assertEqual($this->View->model,'HelperTestPost');
+		$this->assertEqual($this->View->association,'HelperTestTag');
+		$this->assertEqual($this->View->field,'created');
+		$this->assertEqual($this->View->modelId,1);
+		$this->assertEqual($this->View->fieldSuffix,'year');
+
+		$this->Helper->data['HelperTestPost'][2]['HelperTestComment'][1]['title'] = 'My Title';
+		$result = $this->Helper->value('HelperTestPost.2.HelperTestComment.1.title');
+		$this->assertEqual($result,'My Title');
+
+		$this->Helper->data['HelperTestPost'][2]['HelperTestComment'][1]['created']['year'] = 2008;
+		$result = $this->Helper->value('HelperTestPost.2.HelperTestComment.1.created.year');
+		$this->assertEqual($result,2008);
+
+		$this->Helper->data[2]['HelperTestComment'][1]['created']['year'] = 2008;
+		$result = $this->Helper->value('HelperTestPost.2.HelperTestComment.1.created.year');
+		$this->assertEqual($result,2008);
+
+		$this->Helper->data['HelperTestPost']['title'] = 'My Title';
+		$result = $this->Helper->value('title');
+		$this->assertEqual($result,'My Title');
+
+		$this->Helper->data['My']['title'] = 'My Title';
+		$result = $this->Helper->value('My.title');
+		$this->assertEqual($result,'My Title');
+	}
+
+	function testWebrootPaths() {
+		$this->Helper->webroot = '/';
+		$result = $this->Helper->webroot('/img/cake.power.gif');
+		$expected = '/img/cake.power.gif';
+		$this->assertEqual($result, $expected);
+
+		$this->Helper->theme = 'test_theme';
+
+		App::build(array(
+			'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS)
+		));
+
+		$result = $this->Helper->webroot('/img/cake.power.gif');
+		$expected = '/theme/test_theme/img/cake.power.gif';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Helper->webroot('/img/test.jpg');
+		$expected = '/theme/test_theme/img/test.jpg';
+		$this->assertEqual($result, $expected);
+
+		$webRoot = Configure::read('App.www_root');
+		Configure::write('App.www_root', TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'webroot' . DS);
+
+		$result = $this->Helper->webroot('/img/cake.power.gif');
+		$expected = '/theme/test_theme/img/cake.power.gif';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Helper->webroot('/img/test.jpg');
+		$expected = '/theme/test_theme/img/test.jpg';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Helper->webroot('/img/cake.icon.gif');
+		$expected = '/img/cake.icon.gif';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Helper->webroot('/img/cake.icon.gif?some=param');
+		$expected = '/img/cake.icon.gif?some=param';
+		$this->assertEqual($result, $expected);
+
+		Configure::write('App.www_root', $webRoot);
+	}
+
+/**
+ * test parsing attributes.
+ *
+ * @return void
+ */
+	function testParseAttributeCompact() {
+		$helper =& new TestHelper();
+		$compact = array('compact', 'checked', 'declare', 'readonly', 'disabled',
+			'selected', 'defer', 'ismap', 'nohref', 'noshade', 'nowrap', 'multiple', 'noresize');
+		
+		foreach ($compact as $attribute) {
+			foreach (array('true', true, 1, '1', $attribute) as $value) {
+				$attrs = array($attribute => $value);
+				$expected = ' ' . $attribute . '="' . $attribute . '"';
+				$this->assertEqual($helper->parseAttributes($attrs), $expected, '%s Failed on ' . $value);
+			}
+		}
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/view/helpers/ajax.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/view/helpers/ajax.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/view/helpers/ajax.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,910 @@
+<?php
+/**
+ * AjaxHelperTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) {
+	define('CAKEPHP_UNIT_TEST_EXECUTION', 1);
+}
+App::import('Helper', array('Html', 'Form', 'Javascript', 'Ajax'));
+/**
+ * AjaxTestController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class AjaxTestController extends Controller {
+/**
+ * name property
+ *
+ * @var string 'AjaxTest'
+ * @access public
+ */
+	var $name = 'AjaxTest';
+/**
+ * uses property
+ *
+ * @var mixed null
+ * @access public
+ */
+	var $uses = null;
+}
+/**
+ * PostAjaxTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class PostAjaxTest extends Model {
+/**
+ * primaryKey property
+ *
+ * @var string 'id'
+ * @access public
+ */
+	var $primaryKey = 'id';
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function schema() {
+		return array(
+			'id' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8'),
+			'name' => array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+			'created' => array('type' => 'date', 'null' => '1', 'default' => '', 'length' => ''),
+			'updated' => array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => null)
+		);
+	}
+}
+/**
+ * TestAjaxHelper class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class TestAjaxHelper extends AjaxHelper {
+/**
+ * stop method
+ *
+ * @access public
+ * @return void
+ */
+	function _stop() {
+	}
+}
+/**
+ * TestJavascriptHelper class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class TestJavascriptHelper extends JavascriptHelper {
+/**
+ * codeBlocks property
+ *
+ * @var mixed
+ * @access public
+ */
+	var $codeBlocks;
+/**
+ * codeBlock method
+ *
+ * @param mixed $parameter
+ * @access public
+ * @return void
+ */
+	function codeBlock($parameter) {
+		if (empty($this->codeBlocks)) {
+			$this->codeBlocks = array();
+		}
+		$this->codeBlocks[] = $parameter;
+	}
+}
+/**
+ * AjaxTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class AjaxHelperTest extends CakeTestCase {
+/**
+ * Regexp for CDATA start block
+ *
+ * @var string
+ */
+	var $cDataStart = 'preg:/^\/\/<!\[CDATA\[[\n\r]*/';
+/**
+ * Regexp for CDATA end block
+ *
+ * @var string
+ */
+	var $cDataEnd = 'preg:/[^\]]*\]\]\>[\s\r\n]*/';
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		Router::reload();
+		$this->Ajax =& new TestAjaxHelper();
+		$this->Ajax->Html =& new HtmlHelper();
+		$this->Ajax->Form =& new FormHelper();
+		$this->Ajax->Javascript =& new JavascriptHelper();
+		$this->Ajax->Form->Html =& $this->Ajax->Html;
+		$view =& new View(new AjaxTestController());
+		ClassRegistry::addObject('view', $view);
+		ClassRegistry::addObject('PostAjaxTest', new PostAjaxTest());
+
+		$this->Ajax->Form->params = array(
+			'plugin' => null,
+			'action' => 'view',
+			'controller' => 'users'
+		);
+	}
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		unset($this->Ajax);
+		ClassRegistry::flush();
+	}
+/**
+ * testEvalScripts method
+ *
+ * @access public
+ * @return void
+ */
+	function testEvalScripts() {
+		$result = $this->Ajax->link('Test Link', 'http://www.cakephp.org', array('id' => 'link1', 'update' => 'content', 'evalScripts' => false));
+		$expected = array(
+			'a' => array('id' => 'link1', 'onclick' => ' event.returnValue = false; return false;', 'href' => 'http://www.cakephp.org'),
+			'Test Link',
+			'/a',
+			array('script' => array('type' => 'text/javascript')),
+			$this->cDataStart,
+			"Event.observe('link1', 'click', function(event) { new Ajax.Updater('content','http://www.cakephp.org', {asynchronous:true, evalScripts:false, requestHeaders:['X-Update', 'content']}) }, false);",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Ajax->link('Test Link', 'http://www.cakephp.org', array('id' => 'link1', 'update' => 'content'));
+		$expected = array(
+			'a' => array('id' => 'link1', 'onclick' => ' event.returnValue = false; return false;', 'href' => 'http://www.cakephp.org'),
+			'Test Link',
+			'/a',
+			array('script' => array('type' => 'text/javascript')),
+			$this->cDataStart,
+			"Event.observe('link1', 'click', function(event) { new Ajax.Updater('content','http://www.cakephp.org', {asynchronous:true, evalScripts:true, requestHeaders:['X-Update', 'content']}) }, false);",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+	}
+/**
+ * testAutoComplete method
+ *
+ * @access public
+ * @return void
+ */
+	function testAutoComplete() {
+		$result = $this->Ajax->autoComplete('PostAjaxTest.title' , '/posts', array('minChars' => 2));
+		$this->assertPattern('/^<input[^<>]+name="data\[PostAjaxTest\]\[title\]"[^<>]+autocomplete="off"[^<>]+\/>/', $result);
+		$this->assertPattern('/<div[^<>]+id="PostAjaxTestTitle_autoComplete"[^<>]*><\/div>/', $result);
+		$this->assertPattern('/<div[^<>]+class="auto_complete"[^<>]*><\/div>/', $result);
+		$this->assertPattern('/<\/div>\s+<script type="text\/javascript">\s*' . str_replace('/', '\\/', preg_quote('//<![CDATA[')) . '\s*' . str_replace('/', '\\/', preg_quote('new Ajax.Autocompleter(\'PostAjaxTestTitle\', \'PostAjaxTestTitle_autoComplete\', \'/posts\',')) . '/', $result);
+		$this->assertPattern('/' . str_replace('/', '\\/', preg_quote('new Ajax.Autocompleter(\'PostAjaxTestTitle\', \'PostAjaxTestTitle_autoComplete\', \'/posts\', {minChars:2});')) . '/', $result);
+		$this->assertPattern('/<\/script>$/', $result);
+
+		$result = $this->Ajax->autoComplete('PostAjaxTest.title' , '/posts', array('paramName' => 'parameter'));
+		$this->assertPattern('/^<input[^<>]+name="data\[PostAjaxTest\]\[title\]"[^<>]+autocomplete="off"[^<>]+\/>/', $result);
+		$this->assertPattern('/<div[^<>]+id="PostAjaxTestTitle_autoComplete"[^<>]*><\/div>/', $result);
+		$this->assertPattern('/<div[^<>]+class="auto_complete"[^<>]*><\/div>/', $result);
+		$this->assertPattern('/<\/div>\s+<script type="text\/javascript">\s*' . str_replace('/', '\\/', preg_quote('//<![CDATA[')) . '\s*' . str_replace('/', '\\/', preg_quote('new Ajax.Autocompleter(\'PostAjaxTestTitle\', \'PostAjaxTestTitle_autoComplete\', \'/posts\',')) . '/', $result);
+		$this->assertPattern('/' . str_replace('/', '\\/', preg_quote('new Ajax.Autocompleter(\'PostAjaxTestTitle\', \'PostAjaxTestTitle_autoComplete\', \'/posts\', {paramName:\'parameter\'});')) . '/', $result);
+		$this->assertPattern('/<\/script>$/', $result);
+
+		$result = $this->Ajax->autoComplete('PostAjaxTest.title' , '/posts', array('paramName' => 'parameter', 'updateElement' => 'elementUpdated', 'afterUpdateElement' => 'function (input, element) { alert("updated"); }'));
+		$this->assertPattern('/^<input[^<>]+name="data\[PostAjaxTest\]\[title\]"[^<>]+autocomplete="off"[^<>]+\/>/', $result);
+		$this->assertPattern('/<div[^<>]+id="PostAjaxTestTitle_autoComplete"[^<>]*><\/div>/', $result);
+		$this->assertPattern('/<div[^<>]+class="auto_complete"[^<>]*><\/div>/', $result);
+		$this->assertPattern('/<\/div>\s+<script type="text\/javascript">\s*' . str_replace('/', '\\/', preg_quote('//<![CDATA[')) . '\s*' . str_replace('/', '\\/', preg_quote('new Ajax.Autocompleter(\'PostAjaxTestTitle\', \'PostAjaxTestTitle_autoComplete\', \'/posts\',')) . '/', $result);
+		$this->assertPattern('/' . str_replace('/', '\\/', preg_quote('new Ajax.Autocompleter(\'PostAjaxTestTitle\', \'PostAjaxTestTitle_autoComplete\', \'/posts\', {paramName:\'parameter\', updateElement:elementUpdated, afterUpdateElement:function (input, element) { alert("updated"); }});')) . '/', $result);
+		$this->assertPattern('/<\/script>$/', $result);
+
+		$result = $this->Ajax->autoComplete('PostAjaxTest.title' , '/posts', array('callback' => 'function (input, queryString) { alert("requesting"); }'));
+		$this->assertPattern('/^<input[^<>]+name="data\[PostAjaxTest\]\[title\]"[^<>]+autocomplete="off"[^<>]+\/>/', $result);
+		$this->assertPattern('/<div[^<>]+id="PostAjaxTestTitle_autoComplete"[^<>]*><\/div>/', $result);
+		$this->assertPattern('/<div[^<>]+class="auto_complete"[^<>]*><\/div>/', $result);
+		$this->assertPattern('/<\/div>\s+<script type="text\/javascript">\s*' . str_replace('/', '\\/', preg_quote('//<![CDATA[')) . '\s*' . str_replace('/', '\\/', preg_quote('new Ajax.Autocompleter(\'PostAjaxTestTitle\', \'PostAjaxTestTitle_autoComplete\', \'/posts\',')) . '/', $result);
+		$this->assertPattern('/' . str_replace('/', '\\/', preg_quote('new Ajax.Autocompleter(\'PostAjaxTestTitle\', \'PostAjaxTestTitle_autoComplete\', \'/posts\', {callback:function (input, queryString) { alert("requesting"); }});')) . '/', $result);
+		$this->assertPattern('/<\/script>$/', $result);
+
+		$result = $this->Ajax->autoComplete("PostAjaxText.title", "/post", array("parameters" => "'key=value&key2=value2'"));
+		$this->assertPattern('/{parameters:\'key=value&key2=value2\'}/', $result);
+
+		$result = $this->Ajax->autoComplete("PostAjaxText.title", "/post", array("with" => "'key=value&key2=value2'"));
+		$this->assertPattern('/{parameters:\'key=value&key2=value2\'}/', $result);
+
+	}
+/**
+ * testAsynchronous method
+ *
+ * @access public
+ * @return void
+ */
+	function testAsynchronous() {
+		$result = $this->Ajax->link('Test Link', '/', array('id' => 'link1', 'update' => 'content', 'type' => 'synchronous'));
+		$expected = array(
+			'a' => array('id' => 'link1', 'onclick' => ' event.returnValue = false; return false;', 'href' => '/'),
+			'Test Link',
+			'/a',
+			array('script' => array('type' => 'text/javascript')),
+			$this->cDataStart,
+			"Event.observe('link1', 'click', function(event) { new Ajax.Updater('content','/', {asynchronous:false, evalScripts:true, requestHeaders:['X-Update', 'content']}) }, false);",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+	}
+/**
+ * testDraggable method
+ *
+ * @access public
+ * @return void
+ */
+	function testDraggable() {
+		$result = $this->Ajax->drag('id', array('handle' => 'other_id'));
+		$expected = array(
+			array('script' => array('type' => 'text/javascript')),
+			$this->cDataStart,
+			"new Draggable('id', {handle:'other_id'});",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Ajax->drag('id', array('onDrag' => 'doDrag', 'onEnd' => 'doEnd'));
+		$this->assertPattern('/onDrag:doDrag/', $result);
+		$this->assertPattern('/onEnd:doEnd/', $result);
+	}
+/**
+ * testDroppable method
+ *
+ * @access public
+ * @return void
+ */
+	function testDroppable() {
+		$result = $this->Ajax->drop('droppable', array('accept' => 'crap'));
+		$expected = array(
+			array('script' => array('type' => 'text/javascript')),
+			$this->cDataStart,
+			"Droppables.add('droppable', {accept:'crap'});",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Ajax->dropRemote('droppable', array('accept' => 'crap'), array('url' => '/posts'));
+		$expected = array(
+			array('script' => array('type' => 'text/javascript')),
+			$this->cDataStart,
+			"Droppables.add('droppable', {accept:'crap', onDrop:function(element, droppable, event) {new Ajax.Request('/posts', {asynchronous:true, evalScripts:true})}});",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Ajax->dropRemote('droppable', array('accept' => array('crap1', 'crap2')), array('url' => '/posts'));
+		$expected = array(
+			array('script' => array('type' => 'text/javascript')),
+			$this->cDataStart,
+			"Droppables.add('droppable', {accept:[\"crap1\",\"crap2\"], onDrop:function(element, droppable, event) {new Ajax.Request('/posts', {asynchronous:true, evalScripts:true})}});",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Ajax->dropRemote('droppable', array('accept' => 'crap'), array('url' => '/posts', 'with' => '{drag_id:element.id,drop_id:dropon.id,event:event.whatever_you_want}'));
+		$expected = array(
+			array('script' => array('type' => 'text/javascript')),
+			$this->cDataStart,
+			"Droppables.add('droppable', {accept:'crap', onDrop:function(element, droppable, event) {new Ajax.Request('/posts', {asynchronous:true, evalScripts:true, parameters:{drag_id:element.id,drop_id:dropon.id,event:event.whatever_you_want}})}});",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+	}
+/**
+ * testForm method
+ *
+ * @access public
+ * @return void
+ */
+	function testForm() {
+		$result = $this->Ajax->form('showForm', 'post', array('model' => 'Form', 'url' => array('action' => 'showForm', 'controller' => 'forms'), 'update' => 'form_box'));
+		$this->assertNoPattern('/model=/', $result);
+
+		$result = $this->Ajax->form('showForm', 'post', array('name'=> 'SomeFormName', 'id' => 'MyFormID', 'url' => array('action' => 'showForm', 'controller' => 'forms'), 'update' => 'form_box'));
+		$this->assertPattern('/id="MyFormID"/', $result);
+		$this->assertPattern('/name="SomeFormName"/', $result);
+	}
+/**
+ * testSortable method
+ *
+ * @access public
+ * @return void
+ */
+	function testSortable() {
+		$result = $this->Ajax->sortable('ull', array('constraint' => false, 'ghosting' => true));
+		$expected = array(
+			array('script' => array('type' => 'text/javascript')),
+			$this->cDataStart,
+			"Sortable.create('ull', {constraint:false, ghosting:true});",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Ajax->sortable('ull', array('constraint' => 'false', 'ghosting' => 'true'));
+		$expected = array(
+			array('script' => array('type' => 'text/javascript')),
+			$this->cDataStart,
+			"Sortable.create('ull', {constraint:false, ghosting:true});",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Ajax->sortable('ull', array('constraint'=>'false', 'ghosting'=>'true', 'update' => 'myId'));
+		$expected = array(
+			array('script' => array('type' => 'text/javascript')),
+			$this->cDataStart,
+			"Sortable.create('ull', {constraint:false, ghosting:true, update:'myId'});",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Ajax->sortable('faqs', array('url'=>'http://www.cakephp.org',
+			'update' => 'faqs',
+			'tag' => 'tbody',
+			'handle' => 'grip',
+			'before' => "Element.hide('message')",
+			'complete' => "Element.show('message');"
+		));
+		$expected = 'Sortable.create(\'faqs\', {update:\'faqs\', tag:\'tbody\', handle:\'grip\', onUpdate:function(sortable) {Element.hide(\'message\'); new Ajax.Updater(\'faqs\',\'http://www.cakephp.org\', {asynchronous:true, evalScripts:true, onComplete:function(request, json) {Element.show(\'message\');}, parameters:Sortable.serialize(\'faqs\'), requestHeaders:[\'X-Update\', \'faqs\']})}});';
+		$this->assertPattern('/^<script[^<>]+>\s*' . str_replace('/', '\\/', preg_quote('//<![CDATA[')) . '\s*' . str_replace('/', '\\/', preg_quote($expected)) . '\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+
+		$result = $this->Ajax->sortable('div', array('overlap' => 'foo'));
+		$expected = array(
+			array('script' => array('type' => 'text/javascript')),
+			$this->cDataStart,
+			"Sortable.create('div', {overlap:'foo'});",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Ajax->sortable('div', array('block' => false));
+		$expected = "Sortable.create('div', {});";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Ajax->sortable('div', array('block' => false, 'scroll' => 'someID'));
+		$expected = "Sortable.create('div', {scroll:'someID'});";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Ajax->sortable('div', array('block' => false, 'scroll' => 'window'));
+		$expected = "Sortable.create('div', {scroll:window});";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Ajax->sortable('div', array('block' => false, 'scroll' => "$('someElement')"));
+		$expected = "Sortable.create('div', {scroll:$('someElement')});";
+		$this->assertEqual($result, $expected);
+	}
+/**
+ * testSubmitWithIndicator method
+ *
+ * @access public
+ * @return void
+ */
+	function testSubmitWithIndicator() {
+		$result = $this->Ajax->submit('Add', array('div' => false, 'url' => "http://www.cakephp.org", 'indicator' => 'loading', 'loading' => "doSomething()", 'complete' => 'doSomethingElse() '));
+		$this->assertPattern('/onLoading:function\(request\) {doSomething\(\);\s+Element.show\(\'loading\'\);}/', $result);
+		$this->assertPattern('/onComplete:function\(request, json\) {doSomethingElse\(\) ;\s+Element.hide\(\'loading\'\);}/', $result);
+	}
+/**
+ * testLink method
+ *
+ * @access public
+ * @return void
+ */
+	function testLink() {
+		$result = $this->Ajax->link('Ajax Link', 'http://www.cakephp.org/downloads');
+		$this->assertPattern('/^<a[^<>]+>Ajax Link<\/a><script [^<>]+>\s*' . str_replace('/', '\\/', preg_quote('//<![CDATA[')) . '\s*[^<>]+\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/^<a[^<>]+href="http:\/\/www.cakephp.org\/downloads"[^<>]*>/', $result);
+		$this->assertPattern('/^<a[^<>]+id="link\d+"[^<>]*>/', $result);
+		$this->assertPattern('/^<a[^<>]+onclick="\s*event.returnValue = false;\s*return false;"[^<>]*>/', $result);
+		$this->assertPattern('/<script[^<>]+type="text\/javascript"[^<>]*>/', $result);
+		$this->assertNoPattern('/^<a\s+[^<>]*url="[^"]*"[^<>]*>/', $result);
+		$this->assertNoPattern('/<script[^<>]+[^type]=[^<>]*>/', $result);
+		$this->assertPattern('/Event.observe\(\'link\d+\',\s*\'click\',\s*function\(event\)\s*{.+},\s*false\);\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/function\(event\)\s*{\s*new Ajax\.Request\(\'http:\/\/www.cakephp.org\/downloads\',\s*{asynchronous:true, evalScripts:true}\)\s*},\s*false\);/', $result);
+
+		$result = $this->Ajax->link('Ajax Link', 'http://www.cakephp.org/downloads', array('confirm' => 'Are you sure & positive?'));
+		$this->assertPattern('/^<a[^<>]+>Ajax Link<\/a><script [^<>]+>\s*' . str_replace('/', '\\/', preg_quote('//<![CDATA[')) . '\s*[^<>]+\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/^<a[^<>]+href="http:\/\/www.cakephp.org\/downloads"[^<>]*>/', $result);
+		$this->assertPattern('/^<a[^<>]+id="link\d+"[^<>]*>/', $result);
+		$this->assertPattern('/^<a[^<>]+onclick="\s*event.returnValue = false;\s*return false;"[^<>]*>/', $result);
+		$this->assertPattern('/<script[^<>]+type="text\/javascript"[^<>]*>/', $result);
+		$this->assertNoPattern('/^<a\s+[^<>]*url="[^"]*"[^<>]*>/', $result);
+		$this->assertNoPattern('/<script[^<>]+[^type]=[^<>]*>/', $result);
+		$this->assertPattern('/Event.observe\(\'link\d+\',\s*\'click\',\s*function\(event\)\s*{.+},\s*false\);\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/function\(event\)\s*{\s*if \(confirm\(\'Are you sure & positive\?\'\)\) {\s*new Ajax\.Request\(\'http:\/\/www.cakephp.org\/downloads\',\s*{asynchronous:true, evalScripts:true}\);\s*}\s*else\s*{\s*event.returnValue = false;\s*return false;\s*}\s*},\s*false\);/', $result);
+
+		$result = $this->Ajax->link('Ajax Link', 'http://www.cakephp.org/downloads', array('update' => 'myDiv'));
+		$this->assertPattern('/^<a[^<>]+>Ajax Link<\/a><script [^<>]+>\s*' . str_replace('/', '\\/', preg_quote('//<![CDATA[')) . '\s*[^<>]+\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/^<a[^<>]+href="http:\/\/www.cakephp.org\/downloads"[^<>]*>/', $result);
+		$this->assertPattern('/^<a[^<>]+id="link\d+"[^<>]*>/', $result);
+		$this->assertPattern('/^<a[^<>]+onclick="\s*event.returnValue = false;\s*return false;"[^<>]*>/', $result);
+		$this->assertPattern('/<script[^<>]+type="text\/javascript"[^<>]*>/', $result);
+		$this->assertNoPattern('/^<a\s+[^<>]*url="[^"]*"[^<>]*>/', $result);
+		$this->assertNoPattern('/<script[^<>]+[^type]=[^<>]*>/', $result);
+		$this->assertPattern('/Event.observe\(\'link\d+\',\s*\'click\',\s*function\(event\)\s*{.+},\s*false\);\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/function\(event\)\s*{\s*new Ajax\.Updater\(\'myDiv\',\s*\'http:\/\/www.cakephp.org\/downloads\',\s*{asynchronous:true, evalScripts:true, requestHeaders:\[\'X-Update\', \'myDiv\'\]}\)\s*},\s*false\);/', $result);
+
+		$result = $this->Ajax->link('Ajax Link', 'http://www.cakephp.org/downloads', array('update' => 'myDiv', 'id' => 'myLink'));
+		$this->assertPattern('/^<a[^<>]+>Ajax Link<\/a><script [^<>]+>\s*' . str_replace('/', '\\/', preg_quote('//<![CDATA[')) . '\s*[^<>]+\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/^<a[^<>]+href="http:\/\/www.cakephp.org\/downloads"[^<>]*>/', $result);
+		$this->assertPattern('/^<a[^<>]+id="myLink"[^<>]*>/', $result);
+		$this->assertPattern('/^<a[^<>]+onclick="\s*event.returnValue = false;\s*return false;"[^<>]*>/', $result);
+		$this->assertPattern('/<script[^<>]+type="text\/javascript"[^<>]*>/', $result);
+		$this->assertNoPattern('/^<a\s+[^<>]*url="[^"]*"[^<>]*>/', $result);
+		$this->assertNoPattern('/<script[^<>]+[^type]=[^<>]*>/', $result);
+		$this->assertPattern('/Event.observe\(\'myLink\',\s*\'click\',\s*function\(event\)\s*{.+},\s*false\);\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/function\(event\)\s*{\s*new Ajax\.Updater\(\'myDiv\',\s*\'http:\/\/www.cakephp.org\/downloads\',\s*{asynchronous:true, evalScripts:true, requestHeaders:\[\'X-Update\', \'myDiv\'\]}\)\s*},\s*false\);/', $result);
+
+		$result = $this->Ajax->link('Ajax Link', 'http://www.cakephp.org/downloads', array('update' => 'myDiv', 'id' => 'myLink', 'complete' => 'myComplete();'));
+		$this->assertPattern('/^<a[^<>]+>Ajax Link<\/a><script [^<>]+>\s*' . str_replace('/', '\\/', preg_quote('//<![CDATA[')) . '\s*[^<>]+\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/^<a[^<>]+href="http:\/\/www.cakephp.org\/downloads"[^<>]*>/', $result);
+		$this->assertPattern('/^<a[^<>]+id="myLink"[^<>]*>/', $result);
+		$this->assertPattern('/^<a[^<>]+onclick="\s*event.returnValue = false;\s*return false;"[^<>]*>/', $result);
+		$this->assertPattern('/<script[^<>]+type="text\/javascript"[^<>]*>/', $result);
+		$this->assertNoPattern('/^<a\s+[^<>]*url="[^"]*"[^<>]*>/', $result);
+		$this->assertNoPattern('/<script[^<>]+[^type]=[^<>]*>/', $result);
+		$this->assertPattern('/Event.observe\(\'myLink\',\s*\'click\',\s*function\(event\)\s*{.+},\s*false\);\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/function\(event\)\s*{\s*new Ajax\.Updater\(\'myDiv\',\s*\'http:\/\/www.cakephp.org\/downloads\',\s*{asynchronous:true, evalScripts:true, onComplete:function\(request, json\) {myComplete\(\);}, requestHeaders:\[\'X-Update\', \'myDiv\'\]}\)\s*},\s*false\);/', $result);
+
+		$result = $this->Ajax->link('Ajax Link', 'http://www.cakephp.org/downloads', array('update' => 'myDiv', 'id' => 'myLink', 'loading' => 'myLoading();', 'complete' => 'myComplete();'));
+		$this->assertPattern('/^<a[^<>]+>Ajax Link<\/a><script [^<>]+>\s*' . str_replace('/', '\\/', preg_quote('//<![CDATA[')) . '\s*[^<>]+\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/^<a[^<>]+href="http:\/\/www.cakephp.org\/downloads"[^<>]*>/', $result);
+		$this->assertPattern('/^<a[^<>]+id="myLink"[^<>]*>/', $result);
+		$this->assertPattern('/^<a[^<>]+onclick="\s*event.returnValue = false;\s*return false;"[^<>]*>/', $result);
+		$this->assertPattern('/<script[^<>]+type="text\/javascript"[^<>]*>/', $result);
+		$this->assertNoPattern('/^<a\s+[^<>]*url="[^"]*"[^<>]*>/', $result);
+		$this->assertNoPattern('/<script[^<>]+[^type]=[^<>]*>/', $result);
+		$this->assertPattern('/Event.observe\(\'myLink\',\s*\'click\',\s*function\(event\)\s*{.+},\s*false\);\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/function\(event\)\s*{\s*new Ajax\.Updater\(\'myDiv\',\s*\'http:\/\/www.cakephp.org\/downloads\',\s*{asynchronous:true, evalScripts:true, onComplete:function\(request, json\) {myComplete\(\);}, onLoading:function\(request\) {myLoading\(\);}, requestHeaders:\[\'X-Update\', \'myDiv\'\]}\)\s*},\s*false\);/', $result);
+
+		$result = $this->Ajax->link('Ajax Link', 'http://www.cakephp.org/downloads', array('update' => 'myDiv', 'encoding' => 'utf-8'));
+		$this->assertPattern('/^<a[^<>]+>Ajax Link<\/a><script [^<>]+>\s*' . str_replace('/', '\\/', preg_quote('//<![CDATA[')) . '\s*[^<>]+\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/^<a[^<>]+href="http:\/\/www.cakephp.org\/downloads"[^<>]*>/', $result);
+		$this->assertPattern('/^<a[^<>]+onclick="\s*event.returnValue = false;\s*return false;"[^<>]*>/', $result);
+		$this->assertPattern('/<script[^<>]+type="text\/javascript"[^<>]*>/', $result);
+		$this->assertNoPattern('/^<a\s+[^<>]*url="[^"]*"[^<>]*>/', $result);
+		$this->assertNoPattern('/<script[^<>]+[^type]=[^<>]*>/', $result);
+		$this->assertPattern('/Event.observe\(\'\w+\',\s*\'click\',\s*function\(event\)\s*{.+},\s*false\);\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/function\(event\)\s*{\s*new Ajax\.Updater\(\'myDiv\',\s*\'http:\/\/www.cakephp.org\/downloads\',\s*{asynchronous:true, evalScripts:true, encoding:\'utf-8\', requestHeaders:\[\'X-Update\', \'myDiv\'\]}\)\s*},\s*false\);/', $result);
+
+		$result = $this->Ajax->link('Ajax Link', 'http://www.cakephp.org/downloads', array('update' => 'myDiv', 'success' => 'success();'));
+		$this->assertPattern('/^<a[^<>]+>Ajax Link<\/a><script [^<>]+>\s*' . str_replace('/', '\\/', preg_quote('//<![CDATA[')) . '\s*[^<>]+\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/^<a[^<>]+href="http:\/\/www.cakephp.org\/downloads"[^<>]*>/', $result);
+		$this->assertPattern('/^<a[^<>]+onclick="\s*event.returnValue = false;\s*return false;"[^<>]*>/', $result);
+		$this->assertPattern('/<script[^<>]+type="text\/javascript"[^<>]*>/', $result);
+		$this->assertNoPattern('/^<a\s+[^<>]*url="[^"]*"[^<>]*>/', $result);
+		$this->assertNoPattern('/<script[^<>]+[^type]=[^<>]*>/', $result);
+		$this->assertPattern('/Event.observe\(\'\w+\',\s*\'click\',\s*function\(event\)\s*{.+},\s*false\);\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/function\(event\)\s*{\s*new Ajax\.Updater\(\'myDiv\',\s*\'http:\/\/www.cakephp.org\/downloads\',\s*{asynchronous:true, evalScripts:true, onSuccess:function\(request\) {success\(\);}, requestHeaders:\[\'X-Update\', \'myDiv\'\]}\)\s*},\s*false\);/', $result);
+
+		$result = $this->Ajax->link('Ajax Link', 'http://www.cakephp.org/downloads', array('update' => 'myDiv', 'failure' => 'failure();'));
+		$this->assertPattern('/^<a[^<>]+>Ajax Link<\/a><script [^<>]+>\s*' . str_replace('/', '\\/', preg_quote('//<![CDATA[')) . '\s*[^<>]+\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/^<a[^<>]+href="http:\/\/www.cakephp.org\/downloads"[^<>]*>/', $result);
+		$this->assertPattern('/^<a[^<>]+onclick="\s*event.returnValue = false;\s*return false;"[^<>]*>/', $result);
+		$this->assertPattern('/<script[^<>]+type="text\/javascript"[^<>]*>/', $result);
+		$this->assertNoPattern('/^<a\s+[^<>]*url="[^"]*"[^<>]*>/', $result);
+		$this->assertNoPattern('/<script[^<>]+[^type]=[^<>]*>/', $result);
+		$this->assertPattern('/Event.observe\(\'\w+\',\s*\'click\',\s*function\(event\)\s*{.+},\s*false\);\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/function\(event\)\s*{\s*new Ajax\.Updater\(\'myDiv\',\s*\'http:\/\/www.cakephp.org\/downloads\',\s*{asynchronous:true, evalScripts:true, onFailure:function\(request\) {failure\(\);}, requestHeaders:\[\'X-Update\', \'myDiv\'\]}\)\s*},\s*false\);/', $result);
+
+		$result = $this->Ajax->link('Ajax Link', '/test', array('complete' => 'test'));
+		$this->assertPattern('/^<a[^<>]+>Ajax Link<\/a><script [^<>]+>\s*' . str_replace('/', '\\/', preg_quote('//<![CDATA[')) . '\s*[^<>]+\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern("/Event.observe\('link[0-9]+', [\w\d,'\(\)\s{}]+Ajax\.Request\([\w\d\s,'\(\){}:\/]+onComplete:function\(request, json\) {test}/", $result);
+		$this->assertNoPattern('/^<a[^<>]+complete="test"[^<>]*>Ajax Link<\/a>/', $result);
+		$this->assertNoPattern('/^<a\s+[^<>]*url="[^"]*"[^<>]*>/', $result);
+
+		$result = $this->Ajax->link(
+			'Ajax Link',
+			array('controller' => 'posts', 'action' => 'index', '?' => array('one' => '1', 'two' => '2')),
+			array('update' => 'myDiv', 'id' => 'myLink')
+		);
+		$this->assertPattern('#/posts\?one\=1\&two\=2#', $result);
+	}
+/**
+ * testRemoteTimer method
+ *
+ * @access public
+ * @return void
+ */
+	function testRemoteTimer() {
+		$result = $this->Ajax->remoteTimer(array('url' => 'http://www.cakephp.org'));
+		$this->assertPattern('/^<script[^<>]+type="text\/javascript"[^<>]*>.+<\/script>$/s', $result);
+		$this->assertNoPattern('/<script[^<>]+[^type]=[^<>]*>/', $result);
+		$this->assertPattern('/^<script[^<>]+>\s*' . str_replace('/', '\\/', preg_quote('//<![CDATA[')) . '\s*new PeriodicalExecuter\(function\(pe\) {.+}, 10\)\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/' . str_replace('/', '\\/', preg_quote('new Ajax.Request(\'http://www.cakephp.org\', {asynchronous:true, evalScripts:true})')) . '/', $result);
+
+		$result = $this->Ajax->remoteTimer(array('url' => 'http://www.cakephp.org', 'frequency' => 25));
+		$this->assertPattern('/^<script[^<>]+type="text\/javascript"[^<>]*>.+<\/script>$/s', $result);
+		$this->assertNoPattern('/<script[^<>]+[^type]=[^<>]*>/', $result);
+		$this->assertPattern('/^<script[^<>]+>\s*' . str_replace('/', '\\/', preg_quote('//<![CDATA[')) . '\s*new PeriodicalExecuter\(function\(pe\) {.+}, 25\)\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/' . str_replace('/', '\\/', preg_quote('new Ajax.Request(\'http://www.cakephp.org\', {asynchronous:true, evalScripts:true})')) . '/', $result);
+
+		$result = $this->Ajax->remoteTimer(array('url' => 'http://www.cakephp.org', 'complete' => 'complete();'));
+		$this->assertPattern('/^<script[^<>]+type="text\/javascript"[^<>]*>.+<\/script>$/s', $result);
+		$this->assertNoPattern('/<script[^<>]+[^type]=[^<>]*>/', $result);
+		$this->assertPattern('/^<script[^<>]+>\s*' . str_replace('/', '\\/', preg_quote('//<![CDATA[')) . '\s*new PeriodicalExecuter\(function\(pe\) {.+}, 10\)\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/' . str_replace('/', '\\/', preg_quote('new Ajax.Request(\'http://www.cakephp.org\', {asynchronous:true, evalScripts:true, onComplete:function(request, json) {complete();}})')) . '/', $result);
+
+		$result = $this->Ajax->remoteTimer(array('url' => 'http://www.cakephp.org', 'complete' => 'complete();', 'create' => 'create();'));
+		$this->assertPattern('/^<script[^<>]+type="text\/javascript"[^<>]*>.+<\/script>$/s', $result);
+		$this->assertNoPattern('/<script[^<>]+[^type]=[^<>]*>/', $result);
+		$this->assertPattern('/^<script[^<>]+>\s*' . str_replace('/', '\\/', preg_quote('//<![CDATA[')) . '\s*new PeriodicalExecuter\(function\(pe\) {.+}, 10\)\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/' . str_replace('/', '\\/', preg_quote('new Ajax.Request(\'http://www.cakephp.org\', {asynchronous:true, evalScripts:true, onComplete:function(request, json) {complete();}, onCreate:function(request, xhr) {create();}})')) . '/', $result);
+
+		$result = $this->Ajax->remoteTimer(array('url' => 'http://www.cakephp.org', 'exception' => 'alert(exception);'));
+		$this->assertPattern('/^<script[^<>]+type="text\/javascript"[^<>]*>.+<\/script>$/s', $result);
+		$this->assertNoPattern('/<script[^<>]+[^type]=[^<>]*>/', $result);
+		$this->assertPattern('/^<script[^<>]+>\s*' . str_replace('/', '\\/', preg_quote('//<![CDATA[')) . '\s*new PeriodicalExecuter\(function\(pe\) {.+}, 10\)\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/' . str_replace('/', '\\/', preg_quote('new Ajax.Request(\'http://www.cakephp.org\', {asynchronous:true, evalScripts:true, onException:function(request, exception) {alert(exception);}})')) . '/', $result);
+
+		$result = $this->Ajax->remoteTimer(array('url' => 'http://www.cakephp.org', 'contentType' => 'application/x-www-form-urlencoded'));
+		$this->assertPattern('/^<script[^<>]+type="text\/javascript"[^<>]*>.+<\/script>$/s', $result);
+		$this->assertNoPattern('/<script[^<>]+[^type]=[^<>]*>/', $result);
+		$this->assertPattern('/^<script[^<>]+>\s*' . str_replace('/', '\\/', preg_quote('//<![CDATA[')) . '\s*new PeriodicalExecuter\(function\(pe\) {.+}, 10\)\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/' . str_replace('/', '\\/', preg_quote('new Ajax.Request(\'http://www.cakephp.org\', {asynchronous:true, evalScripts:true, contentType:\'application/x-www-form-urlencoded\'})')) . '/', $result);
+
+		$result = $this->Ajax->remoteTimer(array('url' => 'http://www.cakephp.org', 'method' => 'get', 'encoding' => 'utf-8'));
+		$this->assertPattern('/^<script[^<>]+type="text\/javascript"[^<>]*>.+<\/script>$/s', $result);
+		$this->assertNoPattern('/<script[^<>]+[^type]=[^<>]*>/', $result);
+		$this->assertPattern('/^<script[^<>]+>\s*' . str_replace('/', '\\/', preg_quote('//<![CDATA[')) . '\s*new PeriodicalExecuter\(function\(pe\) {.+}, 10\)\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/' . str_replace('/', '\\/', preg_quote('new Ajax.Request(\'http://www.cakephp.org\', {asynchronous:true, evalScripts:true, method:\'get\', encoding:\'utf-8\'})')) . '/', $result);
+
+		$result = $this->Ajax->remoteTimer(array('url' => 'http://www.cakephp.org', 'postBody' => 'var1=value1'));
+		$this->assertPattern('/^<script[^<>]+type="text\/javascript"[^<>]*>.+<\/script>$/s', $result);
+		$this->assertNoPattern('/<script[^<>]+[^type]=[^<>]*>/', $result);
+		$this->assertPattern('/^<script[^<>]+>\s*' . str_replace('/', '\\/', preg_quote('//<![CDATA[')) . '\s*new PeriodicalExecuter\(function\(pe\) {.+}, 10\)\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/' . str_replace('/', '\\/', preg_quote('new Ajax.Request(\'http://www.cakephp.org\', {asynchronous:true, evalScripts:true, postBody:\'var1=value1\'})')) . '/', $result);
+	}
+/**
+ * testObserveField method
+ *
+ * @access public
+ * @return void
+ */
+	function testObserveField() {
+		$result = $this->Ajax->observeField('field', array('url' => 'http://www.cakephp.org'));
+		$this->assertPattern('/^<script[^<>]+type="text\/javascript"[^<>]*>.+<\/script>$/s', $result);
+		$this->assertNoPattern('/<script[^<>]+[^type]=[^<>]*>/', $result);
+		$this->assertPattern('/^<script[^<>]+>\s*' . str_replace('/', '\\/', preg_quote('//<![CDATA[')) . '\s*new Form.Element.EventObserver\(\'field\', function\(element, value\) {.+}\)\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/' . str_replace('/', '\\/', preg_quote('new Ajax.Request(\'http://www.cakephp.org\', {asynchronous:true, evalScripts:true, parameters:Form.Element.serialize(\'field\')})')) . '/', $result);
+
+		$result = $this->Ajax->observeField('field', array('url' => 'http://www.cakephp.org', 'frequency' => 15));
+		$this->assertPattern('/^<script[^<>]+type="text\/javascript"[^<>]*>.+<\/script>$/s', $result);
+		$this->assertNoPattern('/<script[^<>]+[^type]=[^<>]*>/', $result);
+		$this->assertPattern('/^<script[^<>]+>\s*' . str_replace('/', '\\/', preg_quote('//<![CDATA[')) . '\s*new Form.Element.Observer\(\'field\', 15, function\(element, value\) {.+}\)\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/' . str_replace('/', '\\/', preg_quote('new Ajax.Request(\'http://www.cakephp.org\', {asynchronous:true, evalScripts:true, parameters:Form.Element.serialize(\'field\')})')) . '/', $result);
+
+		$result = $this->Ajax->observeField('field', array('url' => 'http://www.cakephp.org', 'update' => 'divId'));
+		$this->assertPattern('/^<script[^<>]+type="text\/javascript"[^<>]*>.+<\/script>$/s', $result);
+		$this->assertNoPattern('/<script[^<>]+[^type]=[^<>]*>/', $result);
+		$this->assertPattern('/^<script[^<>]+>\s*' . str_replace('/', '\\/', preg_quote('//<![CDATA[')) . '\s*new Form.Element.EventObserver\(\'field\', function\(element, value\) {.+}\)\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/' . str_replace('/', '\\/', preg_quote('new Ajax.Updater(\'divId\',\'http://www.cakephp.org\', {asynchronous:true, evalScripts:true, parameters:Form.Element.serialize(\'field\'), requestHeaders:[\'X-Update\', \'divId\']})')) . '/', $result);
+
+		$result = $this->Ajax->observeField('field', array('url' => 'http://www.cakephp.org', 'update' => 'divId', 'with' => 'Form.Element.serialize(\'otherField\')'));
+		$this->assertPattern('/^<script[^<>]+type="text\/javascript"[^<>]*>.+<\/script>$/s', $result);
+		$this->assertNoPattern('/<script[^<>]+[^type]=[^<>]*>/', $result);
+		$this->assertPattern('/^<script[^<>]+>\s*' . str_replace('/', '\\/', preg_quote('//<![CDATA[')) . '\s*new Form.Element.EventObserver\(\'field\', function\(element, value\) {.+}\)\s*' . str_replace('/', '\\/', preg_quote('//]]>')) . '\s*<\/script>$/', $result);
+		$this->assertPattern('/' . str_replace('/', '\\/', preg_quote('new Ajax.Updater(\'divId\',\'http://www.cakephp.org\', {asynchronous:true, evalScripts:true, parameters:Form.Element.serialize(\'otherField\'), requestHeaders:[\'X-Update\', \'divId\']})')) . '/', $result);
+	}
+/**
+ * testObserveForm method
+ *
+ * @access public
+ * @return void
+ */
+	function testObserveForm() {
+		$result = $this->Ajax->observeForm('form', array('url' => 'http://www.cakephp.org'));
+		$expected = array(
+			array('script' => array('type' => 'text/javascript')),
+			$this->cDataStart,
+			"new Form.EventObserver('form', function(element, value) {new Ajax.Request('http://www.cakephp.org', {asynchronous:true, evalScripts:true, parameters:Form.serialize('form')})})",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Ajax->observeForm('form', array('url' => 'http://www.cakephp.org', 'frequency' => 15));
+		$expected = array(
+			array('script' => array('type' => 'text/javascript')),
+			$this->cDataStart,
+			"new Form.Observer('form', 15, function(element, value) {new Ajax.Request('http://www.cakephp.org', {asynchronous:true, evalScripts:true, parameters:Form.serialize('form')})})",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Ajax->observeForm('form', array('url' => 'http://www.cakephp.org', 'update' => 'divId'));
+		$expected = array(
+			array('script' => array('type' => 'text/javascript')),
+			$this->cDataStart,
+			"new Form.EventObserver('form', function(element, value) {new Ajax.Updater('divId','http://www.cakephp.org', {asynchronous:true, evalScripts:true, parameters:Form.serialize('form'), requestHeaders:['X-Update', 'divId']})}",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Ajax->observeForm('form', array('url' => 'http://www.cakephp.org', 'update' => 'divId', 'with' => "Form.serialize('otherForm')"));
+		$expected = array(
+			array('script' => array('type' => 'text/javascript')),
+			$this->cDataStart,
+			"new Form.EventObserver('form', function(element, value) {new Ajax.Updater('divId','http://www.cakephp.org', {asynchronous:true, evalScripts:true, parameters:Form.serialize('otherForm'), requestHeaders:['X-Update', 'divId']})}",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+	}
+/**
+ * testSlider method
+ *
+ * @access public
+ * @return void
+ */
+	function testSlider() {
+		$result = $this->Ajax->slider('sliderId', 'trackId');
+		$expected = array(
+			array('script' => array('type' => 'text/javascript')),
+			$this->cDataStart,
+			"var sliderId = new Control.Slider('sliderId', 'trackId', {});",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Ajax->slider('sliderId', 'trackId', array('axis' => 'vertical'));
+		$expected = array(
+			array('script' => array('type' => 'text/javascript')),
+			$this->cDataStart,
+			"var sliderId = new Control.Slider('sliderId', 'trackId', {axis:'vertical'});",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Ajax->slider('sliderId', 'trackId', array('axis' => 'vertical', 'minimum' => 60, 'maximum' => 288, 'alignX' => -28, 'alignY' => -5, 'disabled' => true));
+		$expected = array(
+			array('script' => array('type' => 'text/javascript')),
+			$this->cDataStart,
+			"var sliderId = new Control.Slider('sliderId', 'trackId', {axis:'vertical', minimum:60, maximum:288, alignX:-28, alignY:-5, disabled:true});",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Ajax->slider('sliderId', 'trackId', array('change' => "alert('changed');"));
+		$expected = array(
+			array('script' => array('type' => 'text/javascript')),
+			$this->cDataStart,
+			"var sliderId = new Control.Slider('sliderId', 'trackId', {onChange:function(value) {alert('changed');}});",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Ajax->slider('sliderId', 'trackId', array('change' => "alert('changed');", 'slide' => "alert('sliding');"));
+		$expected = array(
+			array('script' => array('type' => 'text/javascript')),
+			$this->cDataStart,
+			"var sliderId = new Control.Slider('sliderId', 'trackId', {onChange:function(value) {alert('changed');}, onSlide:function(value) {alert('sliding');}});",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Ajax->slider('sliderId', 'trackId', array('values' => array(10, 20, 30)));
+		$expected = array(
+			array('script' => array('type' => 'text/javascript')),
+			$this->cDataStart,
+			"var sliderId = new Control.Slider('sliderId', 'trackId', {values:[10,20,30]});",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Ajax->slider('sliderId', 'trackId', array('range' => '$R(10, 30)'));
+		$expected = array(
+			array('script' => array('type' => 'text/javascript')),
+			$this->cDataStart,
+			"var sliderId = new Control.Slider('sliderId', 'trackId', {range:\$R(10, 30)});",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+	}
+/**
+ * testRemoteFunction method
+ *
+ * @access public
+ * @return void
+ */
+	function testRemoteFunction() {
+		$result = $this->Ajax->remoteFunction(array('complete' => 'testComplete();'));
+		$expected = "new Ajax.Request('/', {asynchronous:true, evalScripts:true, onComplete:function(request, json) {testComplete();}})";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Ajax->remoteFunction(array('update' => 'myDiv'));
+		$expected = "new Ajax.Updater('myDiv','/', {asynchronous:true, evalScripts:true, requestHeaders:['X-Update', 'myDiv']})";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Ajax->remoteFunction(array('update' => array('div1', 'div2')));
+		$expected = "new Ajax.Updater(document.createElement('div'),'/', {asynchronous:true, evalScripts:true, requestHeaders:['X-Update', 'div1 div2']})";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Ajax->remoteFunction(array('update' => 'myDiv', 'confirm' => 'Are you sure?'));
+		$expected = "if (confirm('Are you sure?')) { new Ajax.Updater('myDiv','/', {asynchronous:true, evalScripts:true, requestHeaders:['X-Update', 'myDiv']}); } else { event.returnValue = false; return false; }";
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testDiv method
+ *
+ * @access public
+ * @return void
+ */
+	function testDiv() {
+		ob_start();
+		$oldXUpdate = env('HTTP_X_UPDATE');
+
+		$result = $this->Ajax->div('myDiv');
+		$this->assertTags($result, array('div' => array('id' => 'myDiv')));
+
+		$_SERVER['HTTP_X_UPDATE'] = null;
+		$result = $this->Ajax->divEnd('myDiv');
+		$this->assertTags($result, '/div');
+
+		$_SERVER['HTTP_X_UPDATE'] = 'secondDiv';
+		$result = $this->Ajax->div('myDiv');
+		$this->assertTags($result, array('div' => array('id' => 'myDiv')));
+		$result = $this->Ajax->divEnd('myDiv');
+		$this->assertTags($result, '/div');
+
+		$_SERVER['HTTP_X_UPDATE'] = 'secondDiv myDiv anotherDiv';
+		$result = $this->Ajax->div('myDiv');
+		$this->assertTrue(empty($result));
+
+		$result = $this->Ajax->divEnd('myDiv');
+		$this->assertTrue(empty($result));
+
+		$_SERVER['HTTP_X_UPDATE'] = $oldXUpdate;
+	}
+/**
+ * testAfterRender method
+ *
+ * @access public
+ * @return void
+ */
+	function testAfterRender() {
+		ob_start();
+		$oldXUpdate = env('HTTP_X_UPDATE');
+		$this->Ajax->Javascript =& new TestJavascriptHelper();
+
+		$_SERVER['HTTP_X_UPDATE'] = 'secondDiv myDiv anotherDiv';
+		$result = $this->Ajax->div('myDiv');
+		$this->assertTrue(empty($result));
+
+		echo 'Contents of myDiv';
+
+		$result = $this->Ajax->divEnd('myDiv');
+		$this->assertTrue(empty($result));
+
+		ob_start();
+		$this->Ajax->afterRender();
+
+		$result = array_shift($this->Ajax->Javascript->codeBlocks);
+		$this->assertPattern('/^\s*' . str_replace('/', '\\/', preg_quote('var __ajaxUpdater__ = {myDiv:"Contents%20of%20myDiv"};')) . '\s*' . str_replace('/', '\\/', preg_quote('for (n in __ajaxUpdater__) { if (typeof __ajaxUpdater__[n] == "string" && $(n)) Element.update($(n), unescape(decodeURIComponent(__ajaxUpdater__[n]))); }')) . '\s*$/s', $result);
+
+		$_SERVER['HTTP_X_UPDATE'] = $oldXUpdate;
+	}
+/**
+ * testEditor method
+ *
+ * @access public
+ * @return void
+ */
+	function testEditor() {
+		$result = $this->Ajax->editor('myDiv', '/');
+		$expected = array(
+			array('script' => array('type' => 'text/javascript')),
+			$this->cDataStart,
+			"new Ajax.InPlaceEditor('myDiv', '/', {ajaxOptions:{asynchronous:true, evalScripts:true}});",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Ajax->editor('myDiv', '/', array('complete' => 'testComplete();'));
+		$expected = array(
+			array('script' => array('type' => 'text/javascript')),
+			$this->cDataStart,
+			"new Ajax.InPlaceEditor('myDiv', '/', {ajaxOptions:{asynchronous:true, evalScripts:true, onComplete:function(request, json) {testComplete();}}});",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Ajax->editor('myDiv', '/', array('callback' => 'callback();'));
+		$expected = array(
+			array('script' => array('type' => 'text/javascript')),
+			$this->cDataStart,
+			"new Ajax.InPlaceEditor('myDiv', '/', {callback:function(form, value) {callback();}, ajaxOptions:{asynchronous:true, evalScripts:true}});",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Ajax->editor('myDiv', '/', array('collection' => array(1 => 'first', 2 => 'second')));
+		$expected = array(
+			array('script' => array('type' => 'text/javascript')),
+			$this->cDataStart,
+			"new Ajax.InPlaceCollectionEditor('myDiv', '/', {collection:{\"1\":\"first\",\"2\":\"second\"}, ajaxOptions:{asynchronous:true, evalScripts:true}});",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Ajax->editor('myDiv', '/', array('var' => 'myVar'));
+		$expected = array(
+			array('script' => array('type' => 'text/javascript')),
+			$this->cDataStart,
+			"var myVar = new Ajax.InPlaceEditor('myDiv', '/', {ajaxOptions:{asynchronous:true, evalScripts:true}});",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/view/helpers/cache.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/view/helpers/cache.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/view/helpers/cache.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,608 @@
+<?php
+/**
+ * CacheHelperTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', array('Controller', 'Model', 'View'));
+App::import('Helper', 'Cache');
+
+/**
+ * CacheTestController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class CacheTestController extends Controller {
+
+/**
+ * helpers property
+ *
+ * @var array
+ * @access public
+ */
+	var $helpers = array('Html', 'Cache');
+
+/**
+ * cache_parsing method
+ *
+ * @access public
+ * @return void
+ */
+	function cache_parsing() {
+		$this->viewPath = 'posts';
+		$this->layout = 'cache_layout';
+		$this->set('variable', 'variableValue');
+		$this->set('superman', 'clark kent');
+		$this->set('batman', 'bruce wayne');
+		$this->set('spiderman', 'peter parker');
+	}
+}
+
+/**
+ * CacheHelperTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class CacheHelperTest extends CakeTestCase {
+
+/**
+ * Checks if TMP/views is writable, and skips the case if it is not.
+ *
+ * @return void
+ */
+	function skip() {
+		$this->skipUnless(is_writable(TMP . 'cache' . DS . 'views' . DS), 'TMP/views is not writable %s');
+	}
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		$this->Controller = new CacheTestController();
+		$this->Cache = new CacheHelper();
+		$this->_cacheSettings = Configure::read('Cache');
+		Configure::write('Cache.check', true);
+		Configure::write('Cache.disable', false);
+	}
+
+/**
+ * Start Case - switch view paths
+ *
+ * @access public
+ * @return void
+ */
+	function startCase() {
+		App::build(array(
+			'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS)
+		), true);
+	}
+
+/**
+ * End Case - restore view Paths
+ *
+ * @access public
+ * @return void
+ */
+	function endCase() {
+		App::build();
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		clearCache();
+		unset($this->Cache);
+		Configure::write('Cache', $this->_cacheSettings);
+	}
+
+/**
+ * test cache parsing with no cake:nocache tags in view file.
+ *
+ * @access public
+ * @return void
+ */
+	function testLayoutCacheParsingNoTagsInView() {
+		$this->Controller->cache_parsing();
+		$this->Controller->params = array(
+			'controller' => 'cache_test',
+			'action' => 'cache_parsing',
+			'url' => array(),
+			'pass' => array(),
+			'named' => array()
+		);
+		$this->Controller->cacheAction = 21600;
+		$this->Controller->here = '/cacheTest/cache_parsing';
+		$this->Controller->action = 'cache_parsing';
+
+		$View = new View($this->Controller);
+		$result = $View->render('index');
+		$this->assertNoPattern('/cake:nocache/', $result);
+		$this->assertNoPattern('/php echo/', $result);
+
+		$filename = CACHE . 'views' . DS . 'cachetest_cache_parsing.php';
+		$this->assertTrue(file_exists($filename));
+
+		$contents = file_get_contents($filename);
+		$this->assertPattern('/php echo \$variable/', $contents);
+		$this->assertPattern('/php echo microtime()/', $contents);
+		$this->assertPattern('/clark kent/', $result);
+
+		@unlink($filename);
+	}
+
+/**
+ * test cache parsing with non-latin characters in current route
+ *
+ * @access public
+ * @return void
+ */
+	function testCacheNonLatinCharactersInRoute() {
+		$this->Controller->cache_parsing();
+		$this->Controller->params = array(
+			'controller' => 'cache_test',
+			'action' => 'cache_parsing',
+			'url' => array(),
+			'pass' => array('風街ろまん'),
+			'named' => array()
+		);
+		$this->Controller->cacheAction = 21600;
+		$this->Controller->here = '/posts/view/風街ろまん';
+		$this->Controller->action = 'view';
+
+		$View = new View($this->Controller);
+		$result = $View->render('index');
+
+		$filename = CACHE . 'views' . DS . 'posts_view_風街ろまん.php';
+		$this->assertTrue(file_exists($filename));
+
+		@unlink($filename);
+	}
+/**
+ * Test cache parsing with cake:nocache tags in view file.
+ *
+ * @access public
+ * @return void
+ */
+	function testLayoutCacheParsingWithTagsInView() {
+		$this->Controller->cache_parsing();
+		$this->Controller->params = array(
+			'controller' => 'cache_test',
+			'action' => 'cache_parsing',
+			'url' => array(),
+			'pass' => array(),
+			'named' => array()
+		);
+		$this->Controller->cacheAction = 21600;
+		$this->Controller->here = '/cacheTest/cache_parsing';
+		$this->Controller->action = 'cache_parsing';
+
+		$View = new View($this->Controller);
+		$result = $View->render('test_nocache_tags');
+		$this->assertNoPattern('/cake:nocache/', $result);
+		$this->assertNoPattern('/php echo/', $result);
+
+		$filename = CACHE . 'views' . DS . 'cachetest_cache_parsing.php';
+		$this->assertTrue(file_exists($filename));
+
+		$contents = file_get_contents($filename);
+		$this->assertPattern('/if \(is_writable\(TMP\)\)\:/', $contents);
+		$this->assertPattern('/php echo \$variable/', $contents);
+		$this->assertPattern('/php echo microtime()/', $contents);
+		$this->assertNoPattern('/cake:nocache/', $contents);
+
+		@unlink($filename);
+	}
+
+/**
+ * test that multiple <cake:nocache> tags function with multiple nocache tags in the layout.
+ *
+ * @return void
+ */
+	function testMultipleNoCacheTagsInViewfile() {
+		$this->Controller->cache_parsing();
+		$this->Controller->params = array(
+			'controller' => 'cache_test',
+			'action' => 'cache_parsing',
+			'url' => array(),
+			'pass' => array(),
+			'named' => array()
+		);
+		$this->Controller->cacheAction = 21600;
+		$this->Controller->here = '/cacheTest/cache_parsing';
+		$this->Controller->action = 'cache_parsing';
+
+		$View = new View($this->Controller);
+		$result = $View->render('multiple_nocache');
+
+		$this->assertNoPattern('/cake:nocache/', $result);
+		$this->assertNoPattern('/php echo/', $result);
+
+		$filename = CACHE . 'views' . DS . 'cachetest_cache_parsing.php';
+		$this->assertTrue(file_exists($filename));
+
+		$contents = file_get_contents($filename);
+		$this->assertNoPattern('/cake:nocache/', $contents);
+		@unlink($filename);
+	}
+
+/**
+ * testComplexNoCache method
+ *
+ * @return void
+ * @access public
+ */
+	function testComplexNoCache () {
+		$this->Controller->cache_parsing();
+		$this->Controller->params = array(
+			'controller' => 'cache_test',
+			'action' => 'cache_complex',
+			'url' => array(),
+			'pass' => array(),
+			'named' => array()
+		);
+		$this->Controller->cacheAction = array('cache_complex' => 21600);
+		$this->Controller->here = '/cacheTest/cache_complex';
+		$this->Controller->action = 'cache_complex';
+		$this->Controller->layout = 'multi_cache';
+		$this->Controller->viewPath = 'posts';
+
+		$View = new View($this->Controller);
+		$result = $View->render('sequencial_nocache');
+
+		$this->assertNoPattern('/cake:nocache/', $result);
+		$this->assertNoPattern('/php echo/', $result);
+		$this->assertPattern('/A\. Layout Before Content/', $result);
+		$this->assertPattern('/B\. In Plain Element/', $result);
+		$this->assertPattern('/C\. Layout After Test Element/', $result);
+		$this->assertPattern('/D\. In View File/', $result);
+		$this->assertPattern('/E\. Layout After Content/', $result);
+		//$this->assertPattern('/F\. In Element With No Cache Tags/', $result);
+		$this->assertPattern('/G\. Layout After Content And After Element With No Cache Tags/', $result);
+		$this->assertNoPattern('/1\. layout before content/', $result);
+		$this->assertNoPattern('/2\. in plain element/', $result);
+		$this->assertNoPattern('/3\. layout after test element/', $result);
+		$this->assertNoPattern('/4\. in view file/', $result);
+		$this->assertNoPattern('/5\. layout after content/', $result);
+		//$this->assertNoPattern('/6\. in element with no cache tags/', $result);
+		$this->assertNoPattern('/7\. layout after content and after element with no cache tags/', $result);
+
+		$filename = CACHE . 'views' . DS . 'cachetest_cache_complex.php';
+		$this->assertTrue(file_exists($filename));
+		$contents = file_get_contents($filename);
+		@unlink($filename);
+
+		$this->assertPattern('/A\. Layout Before Content/', $contents);
+		$this->assertNoPattern('/B\. In Plain Element/', $contents);
+		$this->assertPattern('/C\. Layout After Test Element/', $contents);
+		$this->assertPattern('/D\. In View File/', $contents);
+		$this->assertPattern('/E\. Layout After Content/', $contents);
+		//$this->assertPattern('/F\. In Element With No Cache Tags/', $contents);
+		$this->assertPattern('/G\. Layout After Content And After Element With No Cache Tags/', $contents);
+		$this->assertPattern('/1\. layout before content/', $contents);
+		$this->assertNoPattern('/2\. in plain element/', $contents);
+		$this->assertPattern('/3\. layout after test element/', $contents);
+		$this->assertPattern('/4\. in view file/', $contents);
+		$this->assertPattern('/5\. layout after content/', $contents);
+		//$this->assertPattern('/6\. in element with no cache tags/', $contents);
+		$this->assertPattern('/7\. layout after content and after element with no cache tags/', $contents);
+	}
+
+/**
+ * test cache of view vars
+ *
+ * @access public
+ * @return void
+ */
+	function testCacheViewVars() {
+		$this->Controller->cache_parsing();
+		$this->Controller->params = array(
+			'controller' => 'cache_test',
+			'action' => 'cache_parsing',
+			'url' => array(),
+			'pass' => array(),
+			'named' => array()
+		);
+		$this->Controller->cacheAction = 21600;
+		$this->Controller->here = '/cacheTest/cache_parsing';
+		$this->Controller->action = 'cache_parsing';
+
+		$View = new View($this->Controller);
+		$result = $View->render('index');
+		$this->assertNoPattern('/cake:nocache/', $result);
+		$this->assertNoPattern('/php echo/', $result);
+
+		$filename = CACHE . 'views' . DS . 'cachetest_cache_parsing.php';
+		$this->assertTrue(file_exists($filename));
+
+		$contents = file_get_contents($filename);
+		$this->assertPattern('/\$this\-\>viewVars/', $contents);
+		$this->assertPattern('/extract\(\$this\-\>viewVars, EXTR_SKIP\);/', $contents);
+		$this->assertPattern('/php echo \$variable/', $contents);
+
+		@unlink($filename);
+	}
+	
+	function testCacheCallbacks() {
+		$this->Controller->cache_parsing();
+		$this->Controller->params = array(
+			'controller' => 'cache_test',
+			'action' => 'cache_parsing',
+			'url' => array(),
+			'pass' => array(),
+			'named' => array()
+		);
+		$this->Controller->cacheAction = array(
+			'cache_parsing' => array(
+				'duration' => 21600,
+				'callbacks' => true
+			)
+		);
+		$this->Controller->here = '/cacheTest/cache_parsing';
+		$this->Controller->action = 'cache_parsing';
+
+		$View = new View($this->Controller);
+		$result = $View->render('index');
+
+		$filename = CACHE . 'views' . DS . 'cachetest_cache_parsing.php';
+		$this->assertTrue(file_exists($filename));
+
+		$contents = file_get_contents($filename);
+
+		$this->assertPattern('/\$controller->beforeFilter\(\);/', $contents);
+		$this->assertPattern('/\$this->params = \$controller->params;/', $contents);
+
+		@unlink($filename);
+	}
+
+/**
+ * test cacheAction set to a boolean
+ *
+ * @return void
+ */
+	function testCacheActionArray() {
+		$this->Controller->cache_parsing();
+		$this->Controller->params = array(
+			'controller' => 'cache_test',
+			'action' => 'cache_parsing',
+			'url' => array(),
+			'pass' => array(),
+			'named' => array()
+		);
+		$this->Controller->cacheAction = array(
+			'cache_parsing' => 21600
+		);
+		$this->Controller->here = '/cache_test/cache_parsing';
+		$this->Controller->action = 'cache_parsing';
+
+		$View = new View($this->Controller);
+		$result = $View->render('index');
+
+		$this->assertNoPattern('/cake:nocache/', $result);
+		$this->assertNoPattern('/php echo/', $result);
+
+		$filename = CACHE . 'views' . DS . 'cache_test_cache_parsing.php';
+		$this->assertTrue(file_exists($filename));
+		@unlink($filename);
+
+
+		$this->Controller->cache_parsing();
+		$this->Controller->cacheAction = array(
+			'cache_parsing' => 21600
+		);
+		$this->Controller->here = '/cacheTest/cache_parsing';
+		$this->Controller->action = 'cache_parsing';
+
+		$View = new View($this->Controller);
+		$result = $View->render('index');
+
+		$this->assertNoPattern('/cake:nocache/', $result);
+		$this->assertNoPattern('/php echo/', $result);
+
+		$filename = CACHE . 'views' . DS . 'cachetest_cache_parsing.php';
+		$this->assertTrue(file_exists($filename));
+		@unlink($filename);
+
+
+		$this->Controller->cache_parsing();
+		$this->Controller->params = array(
+			'controller' => 'cache_test',
+			'action' => 'cache_parsing',
+			'url' => array(),
+			'pass' => array(),
+			'named' => array()
+		);
+		$this->Controller->cacheAction = array(
+			'some_other_action' => 21600
+		);
+		$this->Controller->here = '/cacheTest/cache_parsing';
+		$this->Controller->action = 'cache_parsing';
+
+		$View = new View($this->Controller);
+		$result = $View->render('index');
+
+		$this->assertNoPattern('/cake:nocache/', $result);
+		$this->assertNoPattern('/php echo/', $result);
+
+		$filename = CACHE . 'views' . DS . 'cachetest_cache_parsing.php';
+		$this->assertFalse(file_exists($filename));
+	}
+
+/**
+ * test with named and pass args.
+ *
+ * @return void
+ */
+	function testCacheWithNamedAndPassedArgs() {
+		Router::reload();
+
+		$this->Controller->cache_parsing();
+		$this->Controller->params = array(
+			'controller' => 'cache_test',
+			'action' => 'cache_parsing',
+			'url' => array(),
+			'pass' => array(1, 2),
+			'named' => array(
+				'name' => 'mark',
+				'ice' => 'cream'
+			)
+		);
+		$this->Controller->cacheAction = array(
+			'cache_parsing' => 21600
+		);
+		$this->Controller->here = '/cache_test/cache_parsing/1/2/name:mark/ice:cream';
+		$this->Controller->action = 'cache_parsing';
+		
+		$View = new View($this->Controller);
+		$result = $View->render('index');
+
+		$this->assertNoPattern('/cake:nocache/', $result);
+		$this->assertNoPattern('/php echo/', $result);
+
+		$filename = CACHE . 'views' . DS . 'cache_test_cache_parsing_1_2_name_mark_ice_cream.php';
+		$this->assertTrue(file_exists($filename));
+		@unlink($filename);
+	}
+
+/**
+ * test that custom routes are respected when generating cache files.
+ *
+ * @return void
+ */
+	function testCacheWithCustomRoutes() {
+		Router::reload();
+		Router::connect('/:lang/:controller/:action/*', array(), array('lang' => '[a-z]{3}'));
+		
+		$this->Controller->cache_parsing();
+		$this->Controller->params = array(
+			'lang' => 'en',
+			'controller' => 'cache_test',
+			'action' => 'cache_parsing',
+			'url' => array(),
+			'pass' => array(),
+			'named' => array()
+		);
+		$this->Controller->cacheAction = array(
+			'cache_parsing' => 21600
+		);
+		$this->Controller->here = '/en/cache_test/cache_parsing';
+		$this->Controller->action = 'cache_parsing';
+
+		$View = new View($this->Controller);
+		$result = $View->render('index');
+
+		$this->assertNoPattern('/cake:nocache/', $result);
+		$this->assertNoPattern('/php echo/', $result);
+
+		$filename = CACHE . 'views' . DS . 'en_cache_test_cache_parsing.php';
+		$this->assertTrue(file_exists($filename));
+		@unlink($filename);
+	}
+
+/**
+ * test ControllerName contains AppName
+ *
+ * This test verifys view cache is created correctly when the app name is contained in part of the controller name.
+ * (webapp Name) base name is 'cache' controller is 'cacheTest' action is 'cache_name'
+ * apps url would look somehing like http://localhost/cache/cacheTest/cache_name
+ *
+ * @return void
+ **/
+	function testCacheBaseNameControllerName() {
+		$this->Controller->cache_parsing();
+		$this->Controller->cacheAction = array(
+			'cache_name' => 21600
+		);
+		$this->Controller->params = array(
+			'controller' => 'cacheTest',
+			'action' => 'cache_name',
+			'url' => array(),
+			'pass' => array(),
+			'named' => array()
+		);
+		$this->Controller->here = '/cache/cacheTest/cache_name';
+		$this->Controller->action = 'cache_name';
+		$this->Controller->base = '/cache';
+
+		$View = new View($this->Controller);
+		$result = $View->render('index');
+
+		$this->assertNoPattern('/cake:nocache/', $result);
+		$this->assertNoPattern('/php echo/', $result);
+
+		$filename = CACHE . 'views' . DS . 'cache_cachetest_cache_name.php';
+		$this->assertTrue(file_exists($filename));
+		@unlink($filename);
+    }
+
+/**
+ * testCacheEmptySections method
+ *
+ * This test must be uncommented/fixed in next release (1.2+)
+ *
+ * @return void
+ */
+	function testCacheEmptySections() {
+		$this->Controller->cache_parsing();
+		$this->Controller->params = array(
+			'controller' => 'cacheTest',
+			'action' => 'cache_empty_sections',
+			'url' => array(),
+			'pass' => array(),
+			'named' => array()
+		);
+		$this->Controller->cacheAction = array('cache_empty_sections' => 21600);
+		$this->Controller->here = '/cacheTest/cache_empty_sections';
+		$this->Controller->action = 'cache_empty_sections';
+		$this->Controller->layout = 'cache_empty_sections';
+		$this->Controller->viewPath = 'posts';
+
+		$View = new View($this->Controller);
+		$result = $View->render('cache_empty_sections');
+		$this->assertNoPattern('/cake:nocache/', $result);
+		$this->assertNoPattern('/php echo/', $result);
+		$this->assertPattern(
+			'@</title>\s*</head>\s*' .
+			'<body>\s*' .
+			'View Content\s*' .
+			'cached count is: 3\s*' .
+			'</body>@', $result);
+
+		$filename = CACHE . 'views' . DS . 'cachetest_cache_empty_sections.php';
+		$this->assertTrue(file_exists($filename));
+		$contents = file_get_contents($filename);
+		$this->assertNoPattern('/cake:nocache/', $contents);
+		$this->assertPattern(
+			'@<head>\s*<title>Posts</title>\s*' .
+			'<\?php \$x \= 1; \?>\s*' .
+			'</head>\s*' .
+			'<body>\s*' .
+			'<\?php \$x\+\+; \?>\s*' .
+			'<\?php \$x\+\+; \?>\s*' .
+			'View Content\s*' .
+			'<\?php \$y = 1; \?>\s*' .
+			'<\?php echo \'cached count is: \' . \$x; \?>\s*' .
+			'@', $contents);
+		@unlink($filename);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/view/helpers/form.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/view/helpers/form.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/view/helpers/form.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,7039 @@
+<?php
+/**
+ * FormHelperTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', array('ClassRegistry', 'Controller', 'View', 'Model', 'Security'));
+App::import('Helper', 'Html');
+App::import('Helper', 'Form');
+
+/**
+ * ContactTestController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class ContactTestController extends Controller {
+
+/**
+ * name property
+ *
+ * @var string 'ContactTest'
+ * @access public
+ */
+	var $name = 'ContactTest';
+
+/**
+ * uses property
+ *
+ * @var mixed null
+ * @access public
+ */
+	var $uses = null;
+}
+
+/**
+ * Contact class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class Contact extends CakeTestModel {
+
+/**
+ * primaryKey property
+ *
+ * @var string 'id'
+ * @access public
+ */
+	var $primaryKey = 'id';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * name property
+ *
+ * @var string 'Contact'
+ * @access public
+ */
+	var $name = 'Contact';
+
+/**
+ * Default schema
+ *
+ * @var array
+ * @access public
+ */
+	var $_schema = array(
+		'id' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8'),
+		'name' => array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+		'email' => array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+		'phone' => array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+		'password' => array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+		'published' => array('type' => 'date', 'null' => true, 'default' => null, 'length' => null),
+		'created' => array('type' => 'date', 'null' => '1', 'default' => '', 'length' => ''),
+		'updated' => array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => null)
+	);
+
+/**
+ * validate property
+ *
+ * @var array
+ * @access public
+ */
+	var $validate = array(
+		'non_existing' => array(),
+		'idontexist' => array(),
+		'imrequired' => array('rule' => array('between', 5, 30), 'allowEmpty' => false),
+		'imalsorequired' => array('rule' => 'alphaNumeric', 'allowEmpty' => false),
+		'imrequiredtoo' => array('rule' => 'notEmpty'),
+		'required_one' => array('required' => array('rule' => array('notEmpty'))),
+		'imnotrequired' => array('required' => false, 'rule' => 'alphaNumeric', 'allowEmpty' => true),
+		'imalsonotrequired' => array(
+			'alpha' => array('rule' => 'alphaNumeric','allowEmpty' => true),
+			'between' => array('rule' => array('between', 5, 30)),
+		),
+		'imnotrequiredeither' => array('required' => true, 'rule' => array('between', 5, 30), 'allowEmpty' => true),
+	);
+
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function setSchema($schema) {
+		$this->_schema = $schema;
+	}
+
+/**
+ * hasAndBelongsToMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasAndBelongsToMany = array('ContactTag' => array('with' => 'ContactTagsContact'));
+
+/**
+ * hasAndBelongsToMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('User' => array('className' => 'UserForm'));
+}
+
+/**
+ * ContactTagsContact class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class ContactTagsContact extends CakeTestModel {
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * name property
+ *
+ * @var string 'Contact'
+ * @access public
+ */
+	var $name = 'ContactTagsContact';
+
+/**
+ * Default schema
+ *
+ * @var array
+ * @access public
+ */
+	var $_schema = array(
+		'contact_id' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8'),
+		'contact_tag_id' => array(
+			'type' => 'integer', 'null' => '', 'default' => '', 'length' => '8'
+		)
+	);
+
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function setSchema($schema) {
+		$this->_schema = $schema;
+	}
+}
+
+/**
+ * ContactNonStandardPk class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class ContactNonStandardPk extends Contact {
+
+/**
+ * primaryKey property
+ *
+ * @var string 'pk'
+ * @access public
+ */
+	var $primaryKey = 'pk';
+
+/**
+ * name property
+ *
+ * @var string 'ContactNonStandardPk'
+ * @access public
+ */
+	var $name = 'ContactNonStandardPk';
+
+/**
+ * schema method
+ *
+ * @access public
+ * @return void
+ */
+	function schema() {
+		$this->_schema = parent::schema();
+		$this->_schema['pk'] = $this->_schema['id'];
+		unset($this->_schema['id']);
+		return $this->_schema;
+	}
+}
+
+/**
+ * ContactTag class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class ContactTag extends Model {
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * schema definition
+ *
+ * @var array
+ * @access protected
+ */
+	var $_schema = array(
+		'id' => array('type' => 'integer', 'null' => false, 'default' => '', 'length' => '8'),
+		'name' => array('type' => 'string', 'null' => false, 'default' => '', 'length' => '255'),
+		'created' => array('type' => 'date', 'null' => true, 'default' => '', 'length' => ''),
+		'modified' => array('type' => 'datetime', 'null' => true, 'default' => '', 'length' => null)
+	);
+}
+
+/**
+ * UserForm class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class UserForm extends CakeTestModel {
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * primaryKey property
+ *
+ * @var string 'id'
+ * @access public
+ */
+	var $primaryKey = 'id';
+
+/**
+ * name property
+ *
+ * @var string 'UserForm'
+ * @access public
+ */
+	var $name = 'UserForm';
+
+/**
+ * hasMany property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasMany = array(
+		'OpenidUrl' => array('className' => 'OpenidUrl', 'foreignKey' => 'user_form_id'
+	));
+
+/**
+ * schema definition
+ *
+ * @var array
+ * @access protected
+ */
+	var $_schema = array(
+		'id' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8'),
+		'published' => array('type' => 'date', 'null' => true, 'default' => null, 'length' => null),
+		'other' => array('type' => 'text', 'null' => true, 'default' => null, 'length' => null),
+		'stuff' => array('type' => 'string', 'null' => true, 'default' => null, 'length' => 10),
+		'something' => array('type' => 'string', 'null' => true, 'default' => null, 'length' => 255),
+		'active' => array('type' => 'boolean', 'null' => false, 'default' => false),
+		'created' => array('type' => 'date', 'null' => '1', 'default' => '', 'length' => ''),
+		'updated' => array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => null)
+	);
+}
+
+/**
+ * OpenidUrl class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class OpenidUrl extends CakeTestModel {
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * primaryKey property
+ *
+ * @var string 'id'
+ * @access public
+ */
+	var $primaryKey = 'id';
+
+/**
+ * name property
+ *
+ * @var string 'OpenidUrl'
+ * @access public
+ */
+	var $name = 'OpenidUrl';
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('UserForm' => array(
+		'className' => 'UserForm', 'foreignKey' => 'user_form_id'
+	));
+
+/**
+ * validate property
+ *
+ * @var array
+ * @access public
+ */
+	var $validate = array('openid_not_registered' => array());
+
+/**
+ * schema method
+ *
+ * @var array
+ * @access protected
+ */
+	var $_schema = array(
+		'id' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8'),
+		'user_form_id' => array(
+			'type' => 'user_form_id', 'null' => '', 'default' => '', 'length' => '8'
+		),
+		'url' => array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+	);
+
+/**
+ * beforeValidate method
+ *
+ * @access public
+ * @return void
+ */
+	function beforeValidate() {
+		$this->invalidate('openid_not_registered');
+		return true;
+	}
+}
+
+/**
+ * ValidateUser class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class ValidateUser extends CakeTestModel {
+
+/**
+ * primaryKey property
+ *
+ * @var string 'id'
+ * @access public
+ */
+	var $primaryKey = 'id';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * name property
+ *
+ * @var string 'ValidateUser'
+ * @access public
+ */
+	var $name = 'ValidateUser';
+
+/**
+ * hasOne property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasOne = array('ValidateProfile' => array(
+		'className' => 'ValidateProfile', 'foreignKey' => 'user_id'
+	));
+
+/**
+ * schema method
+ *
+ * @var array
+ * @access protected
+ */
+	var $_schema = array(
+		'id' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8'),
+		'name' => array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+		'email' => array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+		'balance' => array('type' => 'float', 'null' => false, 'length' => '5,2'),
+		'created' => array('type' => 'date', 'null' => '1', 'default' => '', 'length' => ''),
+		'updated' => array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => null)
+	);
+
+/**
+ * beforeValidate method
+ *
+ * @access public
+ * @return void
+ */
+	function beforeValidate() {
+		$this->invalidate('email');
+		return false;
+	}
+}
+
+/**
+ * ValidateProfile class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class ValidateProfile extends CakeTestModel {
+
+/**
+ * primaryKey property
+ *
+ * @var string 'id'
+ * @access public
+ */
+	var $primaryKey = 'id';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * schema property
+ *
+ * @var array
+ * @access protected
+ */
+	var $_schema = array(
+		'id' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8'),
+		'user_id' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8'),
+		'full_name' => array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+		'city' => array('type' => 'string', 'null' => '', 'default' => '', 'length' => '255'),
+		'created' => array('type' => 'date', 'null' => '1', 'default' => '', 'length' => ''),
+		'updated' => array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => null)
+	);
+
+/**
+ * name property
+ *
+ * @var string 'ValidateProfile'
+ * @access public
+ */
+	var $name = 'ValidateProfile';
+
+/**
+ * hasOne property
+ *
+ * @var array
+ * @access public
+ */
+	var $hasOne = array('ValidateItem' => array(
+		'className' => 'ValidateItem', 'foreignKey' => 'profile_id'
+	));
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('ValidateUser' => array(
+		'className' => 'ValidateUser', 'foreignKey' => 'user_id'
+	));
+
+/**
+ * beforeValidate method
+ *
+ * @access public
+ * @return void
+ */
+	function beforeValidate() {
+		$this->invalidate('full_name');
+		$this->invalidate('city');
+		return false;
+	}
+}
+
+/**
+ * ValidateItem class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class ValidateItem extends CakeTestModel {
+
+/**
+ * primaryKey property
+ *
+ * @var string 'id'
+ * @access public
+ */
+	var $primaryKey = 'id';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * name property
+ *
+ * @var string 'ValidateItem'
+ * @access public
+ */
+	var $name = 'ValidateItem';
+
+/**
+ * schema property
+ *
+ * @var array
+ * @access protected
+ */
+	var $_schema = array(
+		'id' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8'),
+		'profile_id' => array('type' => 'integer', 'null' => '', 'default' => '', 'length' => '8'),
+		'name' => array('type' => 'text', 'null' => '', 'default' => '', 'length' => '255'),
+		'description' => array(
+			'type' => 'string', 'null' => '', 'default' => '', 'length' => '255'
+		),
+		'created' => array('type' => 'date', 'null' => '1', 'default' => '', 'length' => ''),
+		'updated' => array('type' => 'datetime', 'null' => '1', 'default' => '', 'length' => null)
+	);
+
+/**
+ * belongsTo property
+ *
+ * @var array
+ * @access public
+ */
+	var $belongsTo = array('ValidateProfile' => array('foreignKey' => 'profile_id'));
+
+/**
+ * beforeValidate method
+ *
+ * @access public
+ * @return void
+ */
+	function beforeValidate() {
+		$this->invalidate('description');
+		return false;
+	}
+}
+
+/**
+ * TestMail class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class TestMail extends CakeTestModel {
+
+/**
+ * primaryKey property
+ *
+ * @var string 'id'
+ * @access public
+ */
+	var $primaryKey = 'id';
+
+/**
+ * useTable property
+ *
+ * @var bool false
+ * @access public
+ */
+	var $useTable = false;
+
+/**
+ * name property
+ *
+ * @var string 'TestMail'
+ * @access public
+ */
+	var $name = 'TestMail';
+}
+
+/**
+ * FormHelperTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class FormHelperTest extends CakeTestCase {
+
+/**
+ * fixtures property
+ *
+ * @var array
+ * @access public
+ */
+	var $fixtures = array(null);
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		parent::setUp();
+		Router::reload();
+
+		$this->Form =& new FormHelper();
+		$this->Form->Html =& new HtmlHelper();
+		$this->Controller =& new ContactTestController();
+		$this->View =& new View($this->Controller);
+		$this->Form->params['action'] = 'add';
+
+		ClassRegistry::addObject('view', $view);
+		ClassRegistry::addObject('Contact', new Contact());
+		ClassRegistry::addObject('ContactNonStandardPk', new ContactNonStandardPk());
+		ClassRegistry::addObject('OpenidUrl', new OpenidUrl());
+		ClassRegistry::addObject('UserForm', new UserForm());
+		ClassRegistry::addObject('ValidateItem', new ValidateItem());
+		ClassRegistry::addObject('ValidateUser', new ValidateUser());
+		ClassRegistry::addObject('ValidateProfile', new ValidateProfile());
+
+		$this->oldSalt = Configure::read('Security.salt');
+
+		$this->dateRegex = array(
+			'daysRegex' => 'preg:/(?:<option value="0?([\d]+)">\\1<\/option>[\r\n]*)*/',
+			'monthsRegex' => 'preg:/(?:<option value="[\d]+">[\w]+<\/option>[\r\n]*)*/',
+			'yearsRegex' => 'preg:/(?:<option value="([\d]+)">\\1<\/option>[\r\n]*)*/',
+			'hoursRegex' => 'preg:/(?:<option value="0?([\d]+)">\\1<\/option>[\r\n]*)*/',
+			'minutesRegex' => 'preg:/(?:<option value="([\d]+)">0?\\1<\/option>[\r\n]*)*/',
+			'meridianRegex' => 'preg:/(?:<option value="(am|pm)">\\1<\/option>[\r\n]*)*/',
+		);
+
+		Configure::write('Security.salt', 'foo!');
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		ClassRegistry::removeObject('view');
+		ClassRegistry::removeObject('Contact');
+		ClassRegistry::removeObject('ContactNonStandardPk');
+		ClassRegistry::removeObject('ContactTag');
+		ClassRegistry::removeObject('OpenidUrl');
+		ClassRegistry::removeObject('UserForm');
+		ClassRegistry::removeObject('ValidateItem');
+		ClassRegistry::removeObject('ValidateUser');
+		ClassRegistry::removeObject('ValidateProfile');
+		unset($this->Form->Html, $this->Form, $this->Controller, $this->View);
+		Configure::write('Security.salt', $this->oldSalt);
+	}
+
+
+
+/**
+ * testFormCreateWithSecurity method
+ *
+ * Test form->create() with security key.
+ *
+ * @access public
+ * @return void
+ */
+	function testCreateWithSecurity() {
+		$this->Form->params['_Token'] = array('key' => 'testKey');
+		$encoding = strtolower(Configure::read('App.encoding'));
+		$result = $this->Form->create('Contact', array('url' => '/contacts/add'));
+		$expected = array(
+			'form' => array('method' => 'post', 'action' => '/contacts/add', 'accept-charset' => $encoding),
+			'div' => array('style' => 'display:none;'),
+			array('input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST')),
+			array('input' => array(
+				'type' => 'hidden', 'name' => 'data[_Token][key]', 'value' => 'testKey', 'id'
+			)),
+			'/div'
+		);
+		$this->assertTags($result, $expected,true);
+
+		$result = $this->Form->create('Contact', array('url' => '/contacts/add', 'id' => 'MyForm'));
+		$expected['form']['id'] = 'MyForm';
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test that create() clears the fields property so it starts fresh
+ *
+ * @return void
+ */
+	function testCreateClearingFields() {
+		$this->Form->fields = array('model_id');
+		$this->Form->create('Contact');
+		$this->assertEqual($this->Form->fields, array());
+	}
+
+/**
+ * Tests form hash generation with model-less data
+ *
+ * @access public
+ * @return void
+ */
+	function testValidateHashNoModel() {
+		$this->Form->params['_Token'] = array('key' => 'foo');
+		$result = $this->Form->secure(array('anything'));
+		$this->assertPattern('/540ac9c60d323c22bafe997b72c0790f39a8bdef/', $result);
+	}
+
+/**
+ * Tests that models with identical field names get resolved properly
+ *
+ * @access public
+ * @return void
+ */
+	function testDuplicateFieldNameResolution() {
+		$result = $this->Form->create('ValidateUser');
+		$this->assertEqual($this->View->entity(), array('ValidateUser'));
+
+		$result = $this->Form->input('ValidateItem.name');
+		$this->assertEqual($this->View->entity(), array('ValidateItem', 'name'));
+
+		$result = $this->Form->input('ValidateUser.name');
+		$this->assertEqual($this->View->entity(), array('ValidateUser', 'name'));
+		$this->assertPattern('/name="data\[ValidateUser\]\[name\]"/', $result);
+		$this->assertPattern('/type="text"/', $result);
+
+		$result = $this->Form->input('ValidateItem.name');
+		$this->assertEqual($this->View->entity(), array('ValidateItem', 'name'));
+		$this->assertPattern('/name="data\[ValidateItem\]\[name\]"/', $result);
+		$this->assertPattern('/<textarea/', $result);
+
+		$result = $this->Form->input('name');
+		$this->assertEqual($this->View->entity(), array('ValidateUser', 'name'));
+		$this->assertPattern('/name="data\[ValidateUser\]\[name\]"/', $result);
+		$this->assertPattern('/type="text"/', $result);
+	}
+
+/**
+ * Tests that hidden fields generated for checkboxes don't get locked
+ *
+ * @access public
+ * @return void
+ */
+	function testNoCheckboxLocking() {
+		$this->Form->params['_Token'] = array('key' => 'foo');
+		$this->assertIdentical($this->Form->fields, array());
+
+		$this->Form->checkbox('check', array('value' => '1'));
+		$this->assertIdentical($this->Form->fields, array('check'));
+	}
+
+/**
+ * testFormSecurityFields method
+ *
+ * Test generation of secure form hash generation.
+ *
+ * @access public
+ * @return void
+ */
+	function testFormSecurityFields() {
+		$key = 'testKey';
+		$fields = array('Model.password', 'Model.username', 'Model.valid' => '0');
+		$this->Form->params['_Token']['key'] = $key;
+		$result = $this->Form->secure($fields);
+
+		$expected = Security::hash(serialize($fields) . Configure::read('Security.salt'));
+		$expected .= ':' . 'Model.valid';
+
+		$expected = array(
+			'div' => array('style' => 'display:none;'),
+			'input' => array(
+				'type' => 'hidden', 'name' => 'data[_Token][fields]',
+				'value' => urlencode($expected), 'id' => 'preg:/TokenFields\d+/'
+			),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * Tests correct generation of text fields for double and float fields
+ *
+ * @access public
+ * @return void
+ */
+	function testTextFieldGenerationForFloats() {
+		$model = ClassRegistry::getObject('Contact');
+		$model->setSchema(array('foo' => array(
+			'type' => 'float',
+			'null' => false,
+			'default' => null,
+			'length' => null
+		)));
+
+		$this->Form->create('Contact');
+		$result = $this->Form->input('foo');
+		$expected = array(
+			'div' => array('class' => 'input text'),
+			'label' => array('for' => 'ContactFoo'),
+			'Foo',
+			'/label',
+			array('input' => array(
+				'type' => 'text', 'name' => 'data[Contact][foo]',
+				'id' => 'ContactFoo'
+			)),
+			'/div'
+		);
+	}
+
+/**
+ * testFormSecurityMultipleFields method
+ *
+ * Test secure() with multiple row form. Ensure hash is correct.
+ *
+ * @access public
+ * @return void
+ */
+	function testFormSecurityMultipleFields() {
+		$key = 'testKey';
+
+		$fields = array(
+			'Model.0.password', 'Model.0.username', 'Model.0.hidden' => 'value',
+			'Model.0.valid' => '0', 'Model.1.password', 'Model.1.username',
+			'Model.1.hidden' => 'value', 'Model.1.valid' => '0'
+		);
+		$this->Form->params['_Token']['key'] = $key;
+		$result = $this->Form->secure($fields);
+
+		$hash  = '51e3b55a6edd82020b3f29c9ae200e14bbeb7ee5%3AModel.0.hidden%7CModel.0.valid';
+		$hash .= '%7CModel.1.hidden%7CModel.1.valid';
+
+		$expected = array(
+			'div' => array('style' => 'display:none;'),
+			'input' => array(
+				'type' => 'hidden', 'name' => 'data[_Token][fields]',
+				'value' => $hash, 'id' => 'preg:/TokenFields\d+/'
+			),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testFormSecurityMultipleSubmitButtons
+ *
+ * test form submit generation and ensure that _Token is only created on end()
+ *
+ * @return void
+ */
+	function testFormSecurityMultipleSubmitButtons() {
+		$key = 'testKey';
+		$this->Form->params['_Token']['key'] = $key;
+
+		$this->Form->create('Addresses');
+		$this->Form->input('Address.title');
+		$this->Form->input('Address.first_name');
+
+		$result = $this->Form->submit('Save', array('name' => 'save'));
+		$expected = array(
+			'div' => array('class' => 'submit'),
+			'input' => array('type' => 'submit', 'name' => 'save', 'value' => 'Save'),
+			'/div',
+		);
+		$this->assertTags($result, $expected);
+		$result = $this->Form->submit('Cancel', array('name' => 'cancel'));
+		$expected = array(
+			'div' => array('class' => 'submit'),
+			'input' => array('type' => 'submit', 'name' => 'cancel', 'value' => 'Cancel'),
+			'/div',
+		);
+		$this->assertTags($result, $expected);
+		$result = $this->Form->end(null);
+
+		$expected = array(
+			'div' => array('style' => 'display:none;'),
+			'input' => array(
+				'type' => 'hidden', 'name' => 'data[_Token][fields]',
+				'value' => 'preg:/.+/', 'id' => 'preg:/TokenFields\d+/'
+			),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testFormSecurityMultipleInputFields method
+ *
+ * Test secure form creation with multiple row creation.  Checks hidden, text, checkbox field types
+ *
+ * @access public
+ * @return void
+ */
+	function testFormSecurityMultipleInputFields() {
+		$key = 'testKey';
+
+		$this->Form->params['_Token']['key'] = $key;
+		$this->Form->create('Addresses');
+
+		$this->Form->hidden('Addresses.0.id', array('value' => '123456'));
+		$this->Form->input('Addresses.0.title');
+		$this->Form->input('Addresses.0.first_name');
+		$this->Form->input('Addresses.0.last_name');
+		$this->Form->input('Addresses.0.address');
+		$this->Form->input('Addresses.0.city');
+		$this->Form->input('Addresses.0.phone');
+		$this->Form->input('Addresses.0.primary', array('type' => 'checkbox'));
+
+		$this->Form->hidden('Addresses.1.id', array('value' => '654321'));
+		$this->Form->input('Addresses.1.title');
+		$this->Form->input('Addresses.1.first_name');
+		$this->Form->input('Addresses.1.last_name');
+		$this->Form->input('Addresses.1.address');
+		$this->Form->input('Addresses.1.city');
+		$this->Form->input('Addresses.1.phone');
+		$this->Form->input('Addresses.1.primary', array('type' => 'checkbox'));
+
+		$result = $this->Form->secure($this->Form->fields);
+
+		$hash = 'c9118120e680a7201b543f562e5301006ccfcbe2%3AAddresses.0.id%7CAddresses.1.id';
+
+		$expected = array(
+			'div' => array('style' => 'display:none;'),
+			'input' => array(
+				'type' => 'hidden', 'name' => 'data[_Token][fields]',
+				'value' => $hash, 'id' => 'preg:/TokenFields\d+/'
+			),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * Test form security with Model.field.0 style inputs
+ *
+ * @return void
+ */
+	function testFormSecurityArrayFields() {
+		$key = 'testKey';
+
+		$this->Form->params['_Token']['key'] = $key;
+		$this->Form->create('Address');
+		$this->Form->input('Address.primary.1');
+		$this->assertEqual('Address.primary', $this->Form->fields[0]);
+	}
+
+/**
+ * testFormSecurityMultipleInputDisabledFields method
+ *
+ * test secure form generation with multiple records and disabled fields.
+ *
+ * @access public
+ * @return void
+ */
+	function testFormSecurityMultipleInputDisabledFields() {
+		$key = 'testKey';
+		$this->Form->params['_Token']['key'] = $key;
+		$this->Form->params['_Token']['disabledFields'] = array('first_name', 'address');
+		$this->Form->create();
+
+		$this->Form->hidden('Addresses.0.id', array('value' => '123456'));
+		$this->Form->input('Addresses.0.title');
+		$this->Form->input('Addresses.0.first_name');
+		$this->Form->input('Addresses.0.last_name');
+		$this->Form->input('Addresses.0.address');
+		$this->Form->input('Addresses.0.city');
+		$this->Form->input('Addresses.0.phone');
+		$this->Form->hidden('Addresses.1.id', array('value' => '654321'));
+		$this->Form->input('Addresses.1.title');
+		$this->Form->input('Addresses.1.first_name');
+		$this->Form->input('Addresses.1.last_name');
+		$this->Form->input('Addresses.1.address');
+		$this->Form->input('Addresses.1.city');
+		$this->Form->input('Addresses.1.phone');
+
+		$result = $this->Form->secure($this->Form->fields);
+		$hash = '774df31936dc850b7d8a5277dc0b890123788b09%3AAddresses.0.id%7CAddresses.1.id';
+
+		$expected = array(
+			'div' => array('style' => 'display:none;'),
+			'input' => array(
+				'type' => 'hidden', 'name' => 'data[_Token][fields]',
+				'value' => $hash, 'id' => 'preg:/TokenFields\d+/'
+			),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testFormSecurityInputDisabledFields method
+ *
+ * Test single record form with disabled fields.
+ *
+ * @access public
+ * @return void
+ */
+	function testFormSecurityInputDisabledFields() {
+		$key = 'testKey';
+		$this->Form->params['_Token']['key'] = $key;
+		$this->Form->params['_Token']['disabledFields'] = array('first_name', 'address');
+		$this->Form->create();
+
+		$this->Form->hidden('Addresses.id', array('value' => '123456'));
+		$this->Form->input('Addresses.title');
+		$this->Form->input('Addresses.first_name');
+		$this->Form->input('Addresses.last_name');
+		$this->Form->input('Addresses.address');
+		$this->Form->input('Addresses.city');
+		$this->Form->input('Addresses.phone');
+
+		$result = $this->Form->fields;
+		$expected = array(
+			'Addresses.id' => '123456', 'Addresses.title', 'Addresses.last_name',
+			'Addresses.city', 'Addresses.phone'
+		);
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Form->secure($expected);
+
+		$hash = '449b7e889128e8e52c5e81d19df68f5346571492%3AAddresses.id';
+		$expected = array(
+			'div' => array('style' => 'display:none;'),
+			'input' => array(
+				'type' => 'hidden', 'name' => 'data[_Token][fields]',
+				'value' => $hash, 'id' => 'preg:/TokenFields\d+/'
+			),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test securing inputs with custom name attributes.
+ *
+ * @return void
+ */
+	function testFormSecureWithCustomNameAttribute() {
+		$this->Form->params['_Token']['key'] = 'testKey';
+
+		$this->Form->text('UserForm.published', array('name' => 'data[User][custom]'));
+		$this->assertEqual('User.custom', $this->Form->fields[0]);
+
+		$this->Form->text('UserForm.published', array('name' => 'data[User][custom][another][value]'));
+		$this->assertEqual('User.custom.another.value', $this->Form->fields[1]);
+	}
+
+/**
+ * testFormSecuredInput method
+ *
+ * Test generation of entire secure form, assertions made on input() output.
+ *
+ * @access public
+ * @return void
+ */
+	function testFormSecuredInput() {
+		$this->Form->params['_Token']['key'] = 'testKey';
+
+		$result = $this->Form->create('Contact', array('url' => '/contacts/add'));
+		$encoding = strtolower(Configure::read('App.encoding'));
+		$expected = array(
+			'form' => array('method' => 'post', 'action' => '/contacts/add', 'accept-charset' => $encoding),
+			'div' => array('style' => 'display:none;'),
+			array('input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST')),
+			array('input' => array(
+				'type' => 'hidden', 'name' => 'data[_Token][key]',
+				'value' => 'testKey', 'id' => 'preg:/Token\d+/'
+			)),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('UserForm.published', array('type' => 'text'));
+		$expected = array(
+			'div' => array('class' => 'input text'),
+			'label' => array('for' => 'UserFormPublished'),
+			'Published',
+			'/label',
+			array('input' => array(
+				'type' => 'text', 'name' => 'data[UserForm][published]',
+				'id' => 'UserFormPublished'
+			)),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('UserForm.other', array('type' => 'text'));
+		$expected = array(
+			'div' => array('class' => 'input text'),
+			'label' => array('for' => 'UserFormOther'),
+			'Other',
+			'/label',
+			array('input' => array(
+				'type' => 'text', 'name' => 'data[UserForm][other]',
+				'id' => 'UserFormOther'
+			)),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->hidden('UserForm.stuff');
+		$expected = array('input' => array(
+				'type' => 'hidden', 'name' => 'data[UserForm][stuff]',
+				'id' => 'UserFormStuff'
+		));
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->hidden('UserForm.hidden', array('value' => '0'));
+		$expected = array('input' => array(
+			'type' => 'hidden', 'name' => 'data[UserForm][hidden]',
+			'value' => '0', 'id' => 'UserFormHidden'
+		));
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('UserForm.something', array('type' => 'checkbox'));
+		$expected = array(
+			'div' => array('class' => 'input checkbox'),
+			array('input' => array(
+				'type' => 'hidden', 'name' => 'data[UserForm][something]',
+				'value' => '0', 'id' => 'UserFormSomething_'
+			)),
+			array('input' => array(
+				'type' => 'checkbox', 'name' => 'data[UserForm][something]',
+				'value' => '1', 'id' => 'UserFormSomething'
+			)),
+			'label' => array('for' => 'UserFormSomething'),
+			'Something',
+			'/label',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->fields;
+		$expected = array(
+			'UserForm.published', 'UserForm.other', 'UserForm.stuff' => '',
+			'UserForm.hidden' => '0', 'UserForm.something'
+		);
+		$this->assertEqual($result, $expected);
+
+		$hash = 'bd7c4a654e5361f9a433a43f488ff9a1065d0aaf%3AUserForm.hidden%7CUserForm.stuff';
+
+		$result = $this->Form->secure($this->Form->fields);
+		$expected = array(
+			'div' => array('style' => 'display:none;'),
+			array('input' => array(
+				'type' => 'hidden', 'name' => 'data[_Token][fields]',
+				'value' => $hash, 'id' => 'preg:/TokenFields\d+/'
+			)),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * Tests that the correct keys are added to the field hash index
+ *
+ * @access public
+ * @return void
+ */
+	function testFormSecuredFileInput() {
+		$this->Form->params['_Token']['key'] = 'testKey';
+		$this->assertEqual($this->Form->fields, array());
+
+		$result = $this->Form->file('Attachment.file');
+		$expected = array (
+			'Attachment.file.name', 'Attachment.file.type', 'Attachment.file.tmp_name',
+			'Attachment.file.error', 'Attachment.file.size'
+		);
+		$this->assertEqual($this->Form->fields, $expected);
+	}
+
+/**
+ * test that multiple selects keys are added to field hash
+ *
+ * @access public
+ * @return void
+ */
+	function testFormSecuredMultipleSelect() {
+		$this->Form->params['_Token']['key'] = 'testKey';
+		$this->assertEqual($this->Form->fields, array());
+		$options = array('1' => 'one', '2' => 'two');
+
+		$this->Form->select('Model.select', $options);
+		$expected = array('Model.select');
+		$this->assertEqual($this->Form->fields, $expected);
+
+		$this->Form->fields = array();
+		$this->Form->select('Model.select', $options, null, array('multiple' => true));
+		$this->assertEqual($this->Form->fields, $expected);
+	}
+
+/**
+ * testFormSecuredRadio method
+ *
+ * @access public
+ * @return void
+ */
+	function testFormSecuredRadio() {
+		$this->Form->params['_Token']['key'] = 'testKey';
+		$this->assertEqual($this->Form->fields, array());
+		$options = array('1' => 'option1', '2' => 'option2');
+
+		$this->Form->radio('Test.test', $options);
+		$expected = array('Test.test');
+		$this->assertEqual($this->Form->fields, $expected);
+	}
+
+/**
+ * testDisableSecurityUsingForm method
+ *
+ * @access public
+ * @return void
+ */
+	function testDisableSecurityUsingForm() {
+		$this->Form->params['_Token']['key'] = 'testKey';
+		$this->Form->params['_Token']['disabledFields'] = array();
+		$this->Form->create();
+
+		$this->Form->hidden('Addresses.id', array('value' => '123456'));
+		$this->Form->input('Addresses.title');
+		$this->Form->input('Addresses.first_name', array('secure' => false));
+		$this->Form->input('Addresses.city', array('type' => 'textarea', 'secure' => false));
+		$this->Form->input('Addresses.zip', array(
+			'type' => 'select', 'options' => array(1,2), 'secure' => false
+		));
+
+		$result = $this->Form->fields;
+		$expected = array(
+			'Addresses.id' => '123456', 'Addresses.title',
+		);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testPasswordValidation method
+ *
+ * test validation errors on password input.
+ *
+ * @access public
+ * @return void
+ */
+	function testPasswordValidation() {
+		$this->Form->validationErrors['Contact']['password'] = 'Please provide a password';
+		$result = $this->Form->input('Contact.password');
+		$expected = array(
+			'div' => array('class' => 'input password error'),
+			'label' => array('for' => 'ContactPassword'),
+			'Password',
+			'/label',
+			'input' => array(
+				'type' => 'password', 'name' => 'data[Contact][password]',
+				'id' => 'ContactPassword', 'class' => 'form-error'
+			),
+			array('div' => array('class' => 'error-message')),
+			'Please provide a password',
+			'/div',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testEmptyErrorValidation method
+ *
+ * test validation error div when validation message is an empty string
+ *
+ * @access public
+ * @return void
+ */
+	function testEmptyErrorValidation() {
+		$this->Form->validationErrors['Contact']['password'] = '';
+		$result = $this->Form->input('Contact.password');
+		$expected = array(
+			'div' => array('class' => 'input password error'),
+			'label' => array('for' => 'ContactPassword'),
+			'Password',
+			'/label',
+			'input' => array(
+				'type' => 'password', 'name' => 'data[Contact][password]',
+				'id' => 'ContactPassword', 'class' => 'form-error'
+			),
+			array('div' => array('class' => 'error-message')),
+			array(),
+			'/div',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testEmptyInputErrorValidation method
+ *
+ * test validation error div when validation message is overriden by an empty string when calling input()
+ *
+ * @access public
+ * @return void
+ */
+	function testEmptyInputErrorValidation() {
+		$this->Form->validationErrors['Contact']['password'] = 'Please provide a password';
+		$result = $this->Form->input('Contact.password', array('error' => ''));
+		$expected = array(
+			'div' => array('class' => 'input password error'),
+			'label' => array('for' => 'ContactPassword'),
+			'Password',
+			'/label',
+			'input' => array(
+				'type' => 'password', 'name' => 'data[Contact][password]',
+				'id' => 'ContactPassword', 'class' => 'form-error'
+			),
+			array('div' => array('class' => 'error-message')),
+			array(),
+			'/div',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testFormValidationAssociated method
+ *
+ * test display of form errors in conjunction with model::validates.
+ *
+ * @access public
+ * @return void
+ */
+	function testFormValidationAssociated() {
+		$this->UserForm =& ClassRegistry::getObject('UserForm');
+		$this->UserForm->OpenidUrl =& ClassRegistry::getObject('OpenidUrl');
+
+		$data = array(
+			'UserForm' => array('name' => 'user'),
+			'OpenidUrl' => array('url' => 'http://www.cakephp.org')
+		);
+
+		$this->assertTrue($this->UserForm->OpenidUrl->create($data));
+		$this->assertFalse($this->UserForm->OpenidUrl->validates());
+
+		$result = $this->Form->create('UserForm', array('type' => 'post', 'action' => 'login'));
+		$encoding = strtolower(Configure::read('App.encoding'));
+		$expected = array(
+			'form' => array(
+				'method' => 'post', 'action' => '/user_forms/login', 'id' => 'UserFormLoginForm',
+				'accept-charset' => $encoding
+			),
+			'div' => array('style' => 'display:none;'),
+			'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$expected = array('OpenidUrl' => array('openid_not_registered' => 1));
+		$this->assertEqual($this->Form->validationErrors, $expected);
+
+		$result = $this->Form->error(
+			'OpenidUrl.openid_not_registered', 'Error, not registered', array('wrap' => false)
+		);
+		$this->assertEqual($result, 'Error, not registered');
+
+		unset($this->UserForm->OpenidUrl, $this->UserForm);
+	}
+
+/**
+ * testFormValidationAssociatedFirstLevel method
+ *
+ * test form error display with associated model.
+ *
+ * @access public
+ * @return void
+ */
+	function testFormValidationAssociatedFirstLevel() {
+		$this->ValidateUser =& ClassRegistry::getObject('ValidateUser');
+		$this->ValidateUser->ValidateProfile =& ClassRegistry::getObject('ValidateProfile');
+
+		$data = array(
+			'ValidateUser' => array('name' => 'mariano'),
+			'ValidateProfile' => array('full_name' => 'Mariano Iglesias')
+		);
+
+		$this->assertTrue($this->ValidateUser->create($data));
+		$this->assertFalse($this->ValidateUser->validates());
+		$this->assertFalse($this->ValidateUser->ValidateProfile->validates());
+
+		$result = $this->Form->create('ValidateUser', array('type' => 'post', 'action' => 'add'));
+		$encoding = strtolower(Configure::read('App.encoding'));
+		$expected = array(
+			'form' => array('method' => 'post', 'action' => '/validate_users/add', 'id','accept-charset' => $encoding),
+			'div' => array('style' => 'display:none;'),
+			'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$expected = array(
+			'ValidateUser' => array('email' => 1),
+			'ValidateProfile' => array('full_name' => 1, 'city' => 1)
+		);
+		$this->assertEqual($this->Form->validationErrors, $expected);
+
+		unset($this->ValidateUser->ValidateProfile);
+		unset($this->ValidateUser);
+	}
+
+/**
+ * testFormValidationAssociatedSecondLevel method
+ *
+ * test form error display with associated model.
+ *
+ * @access public
+ * @return void
+ */
+	function testFormValidationAssociatedSecondLevel() {
+		$this->ValidateUser =& ClassRegistry::getObject('ValidateUser');
+		$this->ValidateUser->ValidateProfile =& ClassRegistry::getObject('ValidateProfile');
+		$this->ValidateUser->ValidateProfile->ValidateItem =& ClassRegistry::getObject('ValidateItem');
+
+		$data = array(
+			'ValidateUser' => array('name' => 'mariano'),
+			'ValidateProfile' => array('full_name' => 'Mariano Iglesias'),
+			'ValidateItem' => array('name' => 'Item')
+		);
+
+		$this->assertTrue($this->ValidateUser->create($data));
+		$this->assertFalse($this->ValidateUser->validates());
+		$this->assertFalse($this->ValidateUser->ValidateProfile->validates());
+		$this->assertFalse($this->ValidateUser->ValidateProfile->ValidateItem->validates());
+
+		$result = $this->Form->create('ValidateUser', array('type' => 'post', 'action' => 'add'));
+		$encoding = strtolower(Configure::read('App.encoding'));
+		$expected = array(
+			'form' => array('method' => 'post', 'action' => '/validate_users/add', 'id','accept-charset' => $encoding),
+			'div' => array('style' => 'display:none;'),
+			'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$expected = array(
+			'ValidateUser' => array('email' => 1),
+			'ValidateProfile' => array('full_name' => 1, 'city' => 1),
+			'ValidateItem' => array('description' => 1)
+		);
+		$this->assertEqual($this->Form->validationErrors, $expected);
+
+		unset($this->ValidateUser->ValidateProfile->ValidateItem);
+		unset($this->ValidateUser->ValidateProfile);
+		unset($this->ValidateUser);
+	}
+
+/**
+ * testFormValidationMultiRecord method
+ *
+ * test form error display with multiple records.
+ *
+ * @access public
+ * @return void
+ */
+	function testFormValidationMultiRecord() {
+		$this->Form->validationErrors['Contact'] = array(2 => array(
+			'name' => 'This field cannot be left blank'
+		));
+		$result = $this->Form->input('Contact.2.name');
+		$expected = array(
+			'div' => array('class'),
+			'label' => array('for'),
+			'preg:/[^<]+/',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name', 'id',
+				'class' => 'form-error', 'maxlength' => 255
+			),
+			array('div' => array('class' => 'error-message')),
+			'This field cannot be left blank',
+			'/div',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->validationErrors['UserForm'] = array(
+			'OpenidUrl' => array('url' => 'You must provide a URL'
+		));
+		$this->Form->create('UserForm');
+		$result = $this->Form->input('OpenidUrl.url');
+		$expected = array(
+			'div' => array('class'),
+			'label' => array('for'),
+			'preg:/[^<]+/',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name', 'id', 'class' => 'form-error'
+			),
+			array('div' => array('class' => 'error-message')),
+			'You must provide a URL',
+			'/div',
+			'/div'
+		);
+	}
+
+/**
+ * testMultipleInputValidation method
+ *
+ * test multiple record form validation error display.
+ *
+ * @access public
+ * @return void
+ */
+	function testMultipleInputValidation() {
+		$this->Form->create();
+		$this->Form->validationErrors['Address'][0]['title'] = 'This field cannot be empty';
+		$this->Form->validationErrors['Address'][0]['first_name'] = 'This field cannot be empty';
+		$this->Form->validationErrors['Address'][1]['last_name'] = 'You must have a last name';
+
+		$result = $this->Form->input('Address.0.title');
+		$expected = array(
+			'div' => array('class'),
+			'label' => array('for'),
+			'preg:/[^<]+/',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name', 'id', 'class' => 'form-error'
+			),
+			array('div' => array('class' => 'error-message')),
+			'This field cannot be empty',
+			'/div',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Address.0.first_name');
+		$expected = array(
+			'div' => array('class'),
+			'label' => array('for'),
+			'preg:/[^<]+/',
+			'/label',
+			'input' => array('type' => 'text', 'name', 'id', 'class' => 'form-error'),
+			array('div' => array('class' => 'error-message')),
+			'This field cannot be empty',
+			'/div',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Address.0.last_name');
+		$expected = array(
+			'div' => array('class'),
+			'label' => array('for'),
+			'preg:/[^<]+/',
+			'/label',
+			'input' => array('type' => 'text', 'name', 'id'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Address.1.last_name');
+		$expected = array(
+			'div' => array('class'),
+			'label' => array('for'),
+			'preg:/[^<]+/',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'preg:/[^<]+/',
+				'id' => 'preg:/[^<]+/', 'class' => 'form-error'
+			),
+			array('div' => array('class' => 'error-message')),
+			'You must have a last name',
+			'/div',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testInput method
+ *
+ * Test various incarnations of input().
+ *
+ * @access public
+ * @return void
+ */
+	function testInput() {
+		$result = $this->Form->input('ValidateUser.balance');
+		$expected = array(
+			'div' => array('class'),
+			'label' => array('for'),
+			'Balance',
+			'/label',
+			'input' => array('name', 'type' => 'text', 'maxlength' => 8, 'id'),
+			'/div',
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Contact.email', array('id' => 'custom'));
+		$expected = array(
+			'div' => array('class' => 'input text'),
+			'label' => array('for' => 'custom'),
+			'Email',
+			'/label',
+			array('input' => array(
+				'type' => 'text', 'name' => 'data[Contact][email]',
+				'id' => 'custom', 'maxlength' => 255
+			)),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Contact.email', array('div' => array('class' => false)));
+		$expected = array(
+			'<div',
+			'label' => array('for' => 'ContactEmail'),
+			'Email',
+			'/label',
+			array('input' => array(
+				'type' => 'text', 'name' => 'data[Contact][email]',
+				'id' => 'ContactEmail', 'maxlength' => 255
+			)),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->hidden('Contact.idontexist');
+		$expected = array('input' => array(
+				'type' => 'hidden', 'name' => 'data[Contact][idontexist]',
+				'id' => 'ContactIdontexist'
+		));
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Contact.email', array('type' => 'text'));
+		$expected = array(
+			'div' => array('class' => 'input text'),
+			'label' => array('for' => 'ContactEmail'),
+			'Email',
+			'/label',
+			array('input' => array(
+				'type' => 'text', 'name' => 'data[Contact][email]',
+				'id' => 'ContactEmail'
+			)),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Contact.5.email', array('type' => 'text'));
+		$expected = array(
+			'div' => array('class' => 'input text'),
+			'label' => array('for' => 'Contact5Email'),
+			'Email',
+			'/label',
+			array('input' => array(
+				'type' => 'text', 'name' => 'data[Contact][5][email]',
+				'id' => 'Contact5Email'
+			)),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Contact.password');
+		$expected = array(
+			'div' => array('class' => 'input password'),
+			'label' => array('for' => 'ContactPassword'),
+			'Password',
+			'/label',
+			array('input' => array(
+				'type' => 'password', 'name' => 'data[Contact][password]',
+				'id' => 'ContactPassword'
+			)),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Contact.email', array(
+			'type' => 'file', 'class' => 'textbox'
+		));
+		$expected = array(
+			'div' => array('class' => 'input file'),
+			'label' => array('for' => 'ContactEmail'),
+			'Email',
+			'/label',
+			array('input' => array(
+				'type' => 'file', 'name' => 'data[Contact][email]', 'class' => 'textbox',
+				'id' => 'ContactEmail'
+			)),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data = array('Contact' => array('phone' => 'Hello & World > weird chars'));
+		$result = $this->Form->input('Contact.phone');
+		$expected = array(
+			'div' => array('class' => 'input text'),
+			'label' => array('for' => 'ContactPhone'),
+			'Phone',
+			'/label',
+			array('input' => array(
+				'type' => 'text', 'name' => 'data[Contact][phone]',
+				'value' => 'Hello &amp; World &gt; weird chars',
+				'id' => 'ContactPhone', 'maxlength' => 255
+			)),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data['Model']['0']['OtherModel']['field'] = 'My value';
+		$result = $this->Form->input('Model.0.OtherModel.field', array('id' => 'myId'));
+		$expected = array(
+			'div' => array('class' => 'input text'),
+			'label' => array('for' => 'myId'),
+			'Field',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Model][0][OtherModel][field]',
+				'value' => 'My value', 'id' => 'myId'
+			),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		unset($this->Form->data);
+
+		$this->Form->validationErrors['Model']['field'] = 'Badness!';
+		$result = $this->Form->input('Model.field');
+		$expected = array(
+			'div' => array('class' => 'input text error'),
+			'label' => array('for' => 'ModelField'),
+			'Field',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Model][field]',
+				'id' => 'ModelField', 'class' => 'form-error'
+			),
+			array('div' => array('class' => 'error-message')),
+			'Badness!',
+			'/div',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Model.field', array(
+			'div' => false, 'error' => array('wrap' => 'span')
+		));
+		$expected = array(
+			'label' => array('for' => 'ModelField'),
+			'Field',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Model][field]',
+				'id' => 'ModelField', 'class' => 'form-error'
+			),
+			array('span' => array('class' => 'error-message')),
+			'Badness!',
+			'/span'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Model.field', array(
+			'div' => array('tag' => 'span'), 'error' => array('wrap' => false)
+		));
+		$expected = array(
+			'span' => array('class' => 'input text error'),
+			'label' => array('for' => 'ModelField'),
+			'Field',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Model][field]',
+				'id' => 'ModelField', 'class' => 'form-error'
+			),
+			'Badness!',
+			'/span'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Model.field', array('after' => 'A message to you, Rudy'));
+		$expected = array(
+			'div' => array('class' => 'input text error'),
+			'label' => array('for' => 'ModelField'),
+			'Field',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Model][field]',
+				'id' => 'ModelField', 'class' => 'form-error'
+			),
+			'A message to you, Rudy',
+			array('div' => array('class' => 'error-message')),
+			'Badness!',
+			'/div',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->setEntity(null);
+		$this->Form->setEntity('Model.field');
+		$result = $this->Form->input('Model.field', array(
+			'after' => 'A message to you, Rudy', 'error' => false
+		));
+		$expected = array(
+			'div' => array('class' => 'input text'),
+			'label' => array('for' => 'ModelField'),
+			'Field',
+			'/label',
+			'input' => array('type' => 'text', 'name' => 'data[Model][field]', 'id' => 'ModelField', 'class' => 'form-error'),
+			'A message to you, Rudy',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		unset($this->Form->validationErrors['Model']['field']);
+		$result = $this->Form->input('Model.field', array('after' => 'A message to you, Rudy'));
+		$expected = array(
+			'div' => array('class' => 'input text'),
+			'label' => array('for' => 'ModelField'),
+			'Field',
+			'/label',
+			'input' => array('type' => 'text', 'name' => 'data[Model][field]', 'id' => 'ModelField'),
+			'A message to you, Rudy',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->validationErrors['Model']['field'] = 'minLength';
+		$result = $this->Form->input('Model.field', array(
+			'error' => array(
+				'minLength' => 'Le login doit contenir au moins 2 caractères',
+				'maxLength' => 'login too large'
+			)
+		));
+		$expected = array(
+			'div' => array('class' => 'input text error'),
+			'label' => array('for' => 'ModelField'),
+			'Field',
+			'/label',
+			'input' => array('type' => 'text', 'name' => 'data[Model][field]', 'id' => 'ModelField', 'class' => 'form-error'),
+			array('div' => array('class' => 'error-message')),
+			'Le login doit contenir au moins 2 caractères',
+			'/div',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->validationErrors['Model']['field'] = 'maxLength';
+		$result = $this->Form->input('Model.field', array(
+			'error' => array(
+				'wrap' => 'span',
+				'attributes' => array('rel' => 'fake'),
+				'minLength' => 'Le login doit contenir au moins 2 caractères',
+				'maxLength' => 'login too large',
+			)
+		));
+		$expected = array(
+			'div' => array('class' => 'input text error'),
+			'label' => array('for' => 'ModelField'),
+			'Field',
+			'/label',
+			'input' => array('type' => 'text', 'name' => 'data[Model][field]', 'id' => 'ModelField', 'class' => 'form-error'),
+			array('span' => array('class' => 'error-message', 'rel' => 'fake')),
+			'login too large',
+			'/span',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test input() with checkbox creation
+ *
+ * @return void
+ */
+	function testInputCheckbox() {
+		$result = $this->Form->input('User.active', array('label' => false, 'checked' => true));
+		$expected = array(
+			'div' => array('class' => 'input checkbox'),
+			'input' => array('type' => 'hidden', 'name' => 'data[User][active]', 'value' => '0', 'id' => 'UserActive_'),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[User][active]', 'value' => '1', 'id' => 'UserActive', 'checked' => 'checked')),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('User.active', array('label' => false, 'checked' => 1));
+		$expected = array(
+			'div' => array('class' => 'input checkbox'),
+			'input' => array('type' => 'hidden', 'name' => 'data[User][active]', 'value' => '0', 'id' => 'UserActive_'),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[User][active]', 'value' => '1', 'id' => 'UserActive', 'checked' => 'checked')),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('User.active', array('label' => false, 'checked' => '1'));
+		$expected = array(
+			'div' => array('class' => 'input checkbox'),
+			'input' => array('type' => 'hidden', 'name' => 'data[User][active]', 'value' => '0', 'id' => 'UserActive_'),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[User][active]', 'value' => '1', 'id' => 'UserActive', 'checked' => 'checked')),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test form->input() with time types.
+ *
+ */
+	function testInputTime() {
+		extract($this->dateRegex);
+		$result = $this->Form->input('Contact.created', array('type' => 'time', 'timeFormat' => 24));
+		$result = explode(':', $result);
+		$this->assertPattern('/option value="23"/', $result[0]);
+		$this->assertNoPattern('/option value="24"/', $result[0]);
+
+		$result = $this->Form->input('Contact.created', array('type' => 'time', 'timeFormat' => 24));
+		$result = explode(':', $result);
+		$this->assertPattern('/option value="23"/', $result[0]);
+		$this->assertNoPattern('/option value="24"/', $result[0]);
+
+		$result = $this->Form->input('Model.field', array(
+			'type' => 'time', 'timeFormat' => 24, 'interval' => 15
+		));
+		$result = explode(':', $result);
+		$this->assertNoPattern('#<option value="12"[^>]*>12</option>#', $result[1]);
+		$this->assertNoPattern('#<option value="50"[^>]*>50</option>#', $result[1]);
+		$this->assertPattern('#<option value="15"[^>]*>15</option>#', $result[1]);
+
+		$result = $this->Form->input('Model.field', array(
+			'type' => 'time', 'timeFormat' => 12, 'interval' => 15
+		));
+		$result = explode(':', $result);
+		$this->assertNoPattern('#<option value="12"[^>]*>12</option>#', $result[1]);
+		$this->assertNoPattern('#<option value="50"[^>]*>50</option>#', $result[1]);
+		$this->assertPattern('#<option value="15"[^>]*>15</option>#', $result[1]);
+
+		$result = $this->Form->input('prueba', array(
+			'type' => 'time', 'timeFormat'=> 24 , 'dateFormat'=>'DMY' , 'minYear' => 2008,
+			'maxYear' => date('Y') + 1 ,'interval' => 15
+		));
+		$result = explode(':', $result);
+		$this->assertNoPattern('#<option value="12"[^>]*>12</option>#', $result[1]);
+		$this->assertNoPattern('#<option value="50"[^>]*>50</option>#', $result[1]);
+		$this->assertPattern('#<option value="15"[^>]*>15</option>#', $result[1]);
+		$this->assertPattern('#<option value="30"[^>]*>30</option>#', $result[1]);
+
+		$result = $this->Form->input('Random.start_time', array(
+			'type' => 'time',
+			'selected' => '18:15'
+		));
+		$this->assertPattern('#<option value="06"[^>]*>6</option>#', $result);
+		$this->assertPattern('#<option value="15"[^>]*>15</option>#', $result);
+		$this->assertPattern('#<option value="pm"[^>]*>pm</option>#', $result);
+	}
+
+/**
+ * test form->input() with datetime, date and time types
+ *
+ * @return void
+ */
+	function testInputDatetime() {
+		extract($this->dateRegex);
+		$result = $this->Form->input('prueba', array(
+			'type' => 'datetime', 'timeFormat'=> 24 , 'dateFormat'=>'DMY' , 'minYear' => 2008,
+			'maxYear' => date('Y') + 1 ,'interval' => 15
+		));
+		$result = explode(':', $result);
+		$this->assertNoPattern('#<option value="12"[^>]*>12</option>#', $result[1]);
+		$this->assertNoPattern('#<option value="50"[^>]*>50</option>#', $result[1]);
+		$this->assertPattern('#<option value="15"[^>]*>15</option>#', $result[1]);
+		$this->assertPattern('#<option value="30"[^>]*>30</option>#', $result[1]);
+
+		//related to ticket #5013
+		$result = $this->Form->input('Contact.date', array(
+			'type' => 'date', 'class' => 'customClass', 'onChange' => 'function(){}'
+		));
+		$this->assertPattern('/class="customClass"/', $result);
+		$this->assertPattern('/onChange="function\(\)\{\}"/', $result);
+
+		$result = $this->Form->input('Contact.date', array(
+			'type' => 'date', 'id' => 'customId', 'onChange' => 'function(){}'
+		));
+		$this->assertPattern('/id="customIdDay"/', $result);
+		$this->assertPattern('/id="customIdMonth"/', $result);
+		$this->assertPattern('/onChange="function\(\)\{\}"/', $result);
+
+		$result = $this->Form->input('Model.field', array(
+			'type' => 'datetime', 'timeFormat' => 24, 'id' => 'customID'
+		));
+		$this->assertPattern('/id="customIDDay"/', $result);
+		$this->assertPattern('/id="customIDHour"/', $result);
+		$result = explode('</select><select', $result);
+		$result = explode(':', $result[1]);
+		$this->assertPattern('/option value="23"/', $result[0]);
+		$this->assertNoPattern('/option value="24"/', $result[0]);
+
+		$result = $this->Form->input('Model.field', array(
+			'type' => 'datetime', 'timeFormat' => 12
+		));
+		$result = explode('</select><select', $result);
+		$result = explode(':', $result[1]);
+		$this->assertPattern('/option value="12"/', $result[0]);
+		$this->assertNoPattern('/option value="13"/', $result[0]);
+
+		$this->Form->data = array('Contact' => array('created' => null));
+		$result = $this->Form->input('Contact.created', array('empty' => 'Date Unknown'));
+		$expected = array(
+			'div' => array('class' => 'input date'),
+			'label' => array('for' => 'ContactCreatedMonth'),
+			'Created',
+			'/label',
+			array('select' => array('name' => 'data[Contact][created][month]', 'id' => 'ContactCreatedMonth')),
+			array('option' => array('value' => '')), 'Date Unknown', '/option',
+			$monthsRegex,
+			'/select', '-',
+			array('select' => array('name' => 'data[Contact][created][day]', 'id' => 'ContactCreatedDay')),
+			array('option' => array('value' => '')), 'Date Unknown', '/option',
+			$daysRegex,
+			'/select', '-',
+			array('select' => array('name' => 'data[Contact][created][year]', 'id' => 'ContactCreatedYear')),
+			array('option' => array('value' => '')), 'Date Unknown', '/option',
+			$yearsRegex,
+			'/select',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data = array('Contact' => array('created' => null));
+		$result = $this->Form->input('Contact.created', array('type' => 'datetime', 'dateFormat' => 'NONE'));
+		$this->assertPattern('/for\="ContactCreatedHour"/', $result);
+
+		$this->Form->data = array('Contact' => array('created' => null));
+		$result = $this->Form->input('Contact.created', array('type' => 'datetime', 'timeFormat' => 'NONE'));
+		$this->assertPattern('/for\="ContactCreatedMonth"/', $result);
+
+		$result = $this->Form->input('Contact.created', array(
+			'type' => 'date',
+			'id' => array('day' => 'created-day', 'month' => 'created-month', 'year' => 'created-year'),
+			'timeFormat' => 'NONE'
+		));
+		$this->assertPattern('/for\="created-month"/', $result);
+	}
+
+/**
+ * Test generating checkboxes in a loop.
+ *
+ * @return void
+ */
+	function testInputCheckboxesInLoop() {
+		for ($i = 1; $i < 5; $i++) {
+			$result = $this->Form->input("Contact.{$i}.email", array('type' => 'checkbox', 'value' => $i));
+			$expected = array(
+				'div' => array('class' => 'input checkbox'),
+				'input' => array('type' => 'hidden', 'name' => "data[Contact][{$i}][email]", 'value' => '0', 'id' => "Contact{$i}Email_"),
+				array('input' => array('type' => 'checkbox', 'name' => "data[Contact][{$i}][email]", 'value' => $i, 'id' => "Contact{$i}Email")),
+				'label' => array('for' => "Contact{$i}Email"),
+				'Email',
+				'/label',
+				'/div'
+			);
+			$this->assertTags($result, $expected);
+		}
+	}
+
+/**
+ * test input name with leading integer, ensure attributes are generated correctly.
+ *
+ * @return void
+ */
+	function testInputWithLeadingInteger() {
+		$result = $this->Form->text('0.Node.title');
+		$expected = array(
+			'input' => array('name' => 'data[0][Node][title]', 'id' => '0NodeTitle', 'type' => 'text')
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test form->input() with select type inputs.
+ *
+ * @return void
+ */
+	function testInputSelectType() {
+		$result = $this->Form->input('email', array(
+			'options' => array('è' => 'Firést', 'é' => 'Secoènd'), 'empty' => true)
+		);
+		$expected = array(
+			'div' => array('class' => 'input select'),
+			'label' => array('for' => 'email'),
+			'Email',
+			'/label',
+			array('select' => array('name' => 'data[email]', 'id' => 'email')),
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => 'è')),
+			'Firést',
+			'/option',
+			array('option' => array('value' => 'é')),
+			'Secoènd',
+			'/option',
+			'/select',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('email', array(
+			'options' => array('First', 'Second'), 'empty' => true)
+		);
+		$expected = array(
+			'div' => array('class' => 'input select'),
+			'label' => array('for' => 'email'),
+			'Email',
+			'/label',
+			array('select' => array('name' => 'data[email]', 'id' => 'email')),
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => '0')),
+			'First',
+			'/option',
+			array('option' => array('value' => '1')),
+			'Second',
+			'/option',
+			'/select',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data = array('Model' => array('user_id' => 'value'));
+		$view =& ClassRegistry::getObject('view');
+		$view->viewVars['users'] = array('value' => 'good', 'other' => 'bad');
+		$result = $this->Form->input('Model.user_id', array('empty' => true));
+		$expected = array(
+			'div' => array('class' => 'input select'),
+			'label' => array('for' => 'ModelUserId'),
+			'User',
+			'/label',
+			'select' => array('name' => 'data[Model][user_id]', 'id' => 'ModelUserId'),
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => 'value', 'selected' => 'selected')),
+			'good',
+			'/option',
+			array('option' => array('value' => 'other')),
+			'bad',
+			'/option',
+			'/select',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data = array('Model' => array('user_id' => null));
+		$view =& ClassRegistry::getObject('view');
+		$view->viewVars['users'] = array('value' => 'good', 'other' => 'bad');
+		$result = $this->Form->input('Model.user_id', array('empty' => 'Some Empty'));
+		$expected = array(
+			'div' => array('class' => 'input select'),
+			'label' => array('for' => 'ModelUserId'),
+			'User',
+			'/label',
+			'select' => array('name' => 'data[Model][user_id]', 'id' => 'ModelUserId'),
+			array('option' => array('value' => '')),
+			'Some Empty',
+			'/option',
+			array('option' => array('value' => 'value')),
+			'good',
+			'/option',
+			array('option' => array('value' => 'other')),
+			'bad',
+			'/option',
+			'/select',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data = array('Model' => array('user_id' => 'value'));
+		$view =& ClassRegistry::getObject('view');
+		$view->viewVars['users'] = array('value' => 'good', 'other' => 'bad');
+		$result = $this->Form->input('Model.user_id', array('empty' => 'Some Empty'));
+		$expected = array(
+			'div' => array('class' => 'input select'),
+			'label' => array('for' => 'ModelUserId'),
+			'User',
+			'/label',
+			'select' => array('name' => 'data[Model][user_id]', 'id' => 'ModelUserId'),
+			array('option' => array('value' => '')),
+			'Some Empty',
+			'/option',
+			array('option' => array('value' => 'value', 'selected' => 'selected')),
+			'good',
+			'/option',
+			array('option' => array('value' => 'other')),
+			'bad',
+			'/option',
+			'/select',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data = array('User' => array('User' => array('value')));
+		$view =& ClassRegistry::getObject('view');
+		$view->viewVars['users'] = array('value' => 'good', 'other' => 'bad');
+		$result = $this->Form->input('User.User', array('empty' => true));
+		$expected = array(
+			'div' => array('class' => 'input select'),
+			'label' => array('for' => 'UserUser'),
+			'User',
+			'/label',
+			'input' => array('type' => 'hidden', 'name' => 'data[User][User]', 'value' => '', 'id' => 'UserUser_'),
+			'select' => array('name' => 'data[User][User][]', 'id' => 'UserUser', 'multiple' => 'multiple'),
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => 'value', 'selected' => 'selected')),
+			'good',
+			'/option',
+			array('option' => array('value' => 'other')),
+			'bad',
+			'/option',
+			'/select',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data = array();
+		$result = $this->Form->input('Publisher.id', array(
+				'label'		=> 'Publisher',
+				'type'		=> 'select',
+				'multiple'	=> 'checkbox',
+				'options'	=> array('Value 1' => 'Label 1', 'Value 2' => 'Label 2')
+		));
+		$expected = array(
+			array('div' => array('class' => 'input select')),
+				array('label' => array('for' => 'PublisherId')),
+				'Publisher',
+				'/label',
+				'input' => array('type' => 'hidden', 'name' => 'data[Publisher][id]', 'value' => '', 'id' => 'PublisherId'),
+				array('div' => array('class' => 'checkbox')),
+				array('input' => array('type' => 'checkbox', 'name' => 'data[Publisher][id][]', 'value' => 'Value 1', 'id' => 'PublisherIdValue1')),
+				array('label' => array('for' => 'PublisherIdValue1')),
+				'Label 1',
+				'/label',
+				'/div',
+				array('div' => array('class' => 'checkbox')),
+				array('input' => array('type' => 'checkbox', 'name' => 'data[Publisher][id][]', 'value' => 'Value 2', 'id' => 'PublisherIdValue2')),
+				array('label' => array('for' => 'PublisherIdValue2')),
+				'Label 2',
+				'/label',
+				'/div',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test that input() and a non standard primary key makes a hidden input by default.
+ *
+ * @return void
+ */
+	function testInputWithNonStandardPrimaryKeyMakesHidden() {
+		$this->Form->create('User');
+		$this->Form->fieldset = array(
+			'User' => array(
+				'fields' => array(
+					'model_id' => array('type' => 'integer')
+				),
+				'validates' => array(),
+				'key' => 'model_id'
+			)
+		);
+		$result = $this->Form->input('model_id');
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[User][model_id]', 'id' => 'UserModelId'),
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test that overriding the magic select type widget is possible
+ *
+ * @return void
+ */
+	function testInputOverridingMagicSelectType() {
+		$view =& ClassRegistry::getObject('view');
+		$view->viewVars['users'] = array('value' => 'good', 'other' => 'bad');
+		$result = $this->Form->input('Model.user_id', array('type' => 'text'));
+		$expected = array(
+			'div' => array('class' => 'input text'),
+			'label' => array('for' => 'ModelUserId'), 'User', '/label',
+			'input' => array('name' => 'data[Model][user_id]', 'type' => 'text', 'id' => 'ModelUserId'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		//Check that magic types still work for plural/singular vars
+		$view =& ClassRegistry::getObject('view');
+		$view->viewVars['types'] = array('value' => 'good', 'other' => 'bad');
+		$result = $this->Form->input('Model.type');
+		$expected = array(
+			'div' => array('class' => 'input select'),
+			'label' => array('for' => 'ModelType'), 'Type', '/label',
+			'select' => array('name' => 'data[Model][type]', 'id' => 'ModelType'),
+			array('option' => array('value' => 'value')), 'good', '/option',
+			array('option' => array('value' => 'other')), 'bad', '/option',
+			'/select',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * Test that magic input() selects can easily be converted into radio types without error.
+ *
+ * @return void
+ */
+	function testInputMagicSelectChangeToRadio() {
+		$view =& ClassRegistry::getObject('view');
+		$view->viewVars['users'] = array('value' => 'good', 'other' => 'bad');
+		$result = $this->Form->input('Model.user_id', array('type' => 'radio'));
+		$this->assertPattern('/input type="radio"/', $result);
+	}
+
+/**
+ * fields with the same name as the model should work.
+ *
+ * @return void
+ */
+	function testInputWithMatchingFieldAndModelName() {
+		$this->Form->create('User');
+		$this->Form->fieldset = array(
+			'User' => array(
+				'fields' => array(
+					'User' => array('type' => 'text')
+				),
+				'validates' => array(),
+				'key' => 'id'
+			)
+		);
+		$this->Form->data['User']['User'] = 'ABC, Inc.';
+		$result = $this->Form->input('User', array('type' => 'text'));
+		$expected = array(
+			'div' => array('class' => 'input text'),
+			'label' => array('for' => 'UserUser'), 'User', '/label',
+			'input' => array('name' => 'data[User][User]', 'type' => 'text', 'id' => 'UserUser', 'value' => 'ABC, Inc.'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testFormInputs method
+ *
+ * test correct results from form::inputs().
+ *
+ * @access public
+ * @return void
+ */
+	function testFormInputs() {
+		$this->Form->create('Contact');
+		$result = $this->Form->inputs('The Legend');
+		$expected = array(
+			'<fieldset',
+			'<legend',
+			'The Legend',
+			'/legend',
+			'*/fieldset',
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->inputs(array('legend' => 'Field of Dreams', 'fieldset' => 'classy-stuff'));
+		$expected = array(
+			'fieldset' => array('class' => 'classy-stuff'),
+			'<legend',
+			'Field of Dreams',
+			'/legend',
+			'*/fieldset'
+		);
+		$this->assertTags($result, $expected);
+
+		$View = ClassRegistry::getObject('view');
+		$this->Form->create('Contact');
+		$this->Form->params['prefix'] = 'admin';
+		$this->Form->action = 'admin_edit';
+		$result = $this->Form->inputs();
+		$expected = array(
+			'<fieldset',
+			'<legend',
+			'Edit Contact',
+			'/legend',
+			'*/fieldset',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->create('Contact');
+		$result = $this->Form->inputs(false);
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Contact][id]', 'id' => 'ContactId'),
+			array('div' => array('class' => 'input text')),
+			'*/div',
+			array('div' => array('class' => 'input text')),
+			'*/div',
+			array('div' => array('class' => 'input text')),
+			'*/div',
+			array('div' => array('class' => 'input password')),
+			'*/div',
+			array('div' => array('class' => 'input date')),
+			'*/div',
+			array('div' => array('class' => 'input date')),
+			'*/div',
+			array('div' => array('class' => 'input datetime')),
+			'*/div',
+			array('div' => array('class' => 'input select')),
+			'*/div',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->create('Contact');
+		$result = $this->Form->inputs(array('fieldset' => false, 'legend' => false));
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Contact][id]', 'id' => 'ContactId'),
+			array('div' => array('class' => 'input text')),
+			'*/div',
+			array('div' => array('class' => 'input text')),
+			'*/div',
+			array('div' => array('class' => 'input text')),
+			'*/div',
+			array('div' => array('class' => 'input password')),
+			'*/div',
+			array('div' => array('class' => 'input date')),
+			'*/div',
+			array('div' => array('class' => 'input date')),
+			'*/div',
+			array('div' => array('class' => 'input datetime')),
+			'*/div',
+			array('div' => array('class' => 'input select')),
+			'*/div',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->create('Contact');
+		$result = $this->Form->inputs(array('fieldset' => true, 'legend' => false));
+		$expected = array(
+			'fieldset' => array(),
+			'input' => array('type' => 'hidden', 'name' => 'data[Contact][id]', 'id' => 'ContactId'),
+			array('div' => array('class' => 'input text')),
+			'*/div',
+			array('div' => array('class' => 'input text')),
+			'*/div',
+			array('div' => array('class' => 'input text')),
+			'*/div',
+			array('div' => array('class' => 'input password')),
+			'*/div',
+			array('div' => array('class' => 'input date')),
+			'*/div',
+			array('div' => array('class' => 'input date')),
+			'*/div',
+			array('div' => array('class' => 'input datetime')),
+			'*/div',
+			array('div' => array('class' => 'input select')),
+			'*/div',
+			'/fieldset'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->create('Contact');
+		$result = $this->Form->inputs(array('fieldset' => false, 'legend' => 'Hello'));
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Contact][id]', 'id' => 'ContactId'),
+			array('div' => array('class' => 'input text')),
+			'*/div',
+			array('div' => array('class' => 'input text')),
+			'*/div',
+			array('div' => array('class' => 'input text')),
+			'*/div',
+			array('div' => array('class' => 'input password')),
+			'*/div',
+			array('div' => array('class' => 'input date')),
+			'*/div',
+			array('div' => array('class' => 'input date')),
+			'*/div',
+			array('div' => array('class' => 'input datetime')),
+			'*/div',
+			array('div' => array('class' => 'input select')),
+			'*/div',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->create('Contact');
+		$result = $this->Form->inputs('Hello');
+		$expected = array(
+			'fieldset' => array(),
+			'legend' => array(),
+			'Hello',
+			'/legend',
+			'input' => array('type' => 'hidden', 'name' => 'data[Contact][id]', 'id' => 'ContactId'),
+			array('div' => array('class' => 'input text')),
+			'*/div',
+			array('div' => array('class' => 'input text')),
+			'*/div',
+			array('div' => array('class' => 'input text')),
+			'*/div',
+			array('div' => array('class' => 'input password')),
+			'*/div',
+			array('div' => array('class' => 'input date')),
+			'*/div',
+			array('div' => array('class' => 'input date')),
+			'*/div',
+			array('div' => array('class' => 'input datetime')),
+			'*/div',
+			array('div' => array('class' => 'input select')),
+			'*/div',
+			'/fieldset'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->create('Contact');
+		$result = $this->Form->inputs(array('legend' => 'Hello'));
+		$expected = array(
+			'fieldset' => array(),
+			'legend' => array(),
+			'Hello',
+			'/legend',
+			'input' => array('type' => 'hidden', 'name' => 'data[Contact][id]', 'id' => 'ContactId'),
+			array('div' => array('class' => 'input text')),
+			'*/div',
+			array('div' => array('class' => 'input text')),
+			'*/div',
+			array('div' => array('class' => 'input text')),
+			'*/div',
+			array('div' => array('class' => 'input password')),
+			'*/div',
+			array('div' => array('class' => 'input date')),
+			'*/div',
+			array('div' => array('class' => 'input date')),
+			'*/div',
+			array('div' => array('class' => 'input datetime')),
+			'*/div',
+			array('div' => array('class' => 'input select')),
+			'*/div',
+			'/fieldset'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testSelectAsCheckbox method
+ *
+ * test multi-select widget with checkbox formatting.
+ *
+ * @access public
+ * @return void
+ */
+	function testSelectAsCheckbox() {
+		$result = $this->Form->select('Model.multi_field', array('first', 'second', 'third'), array(0, 1), array('multiple' => 'checkbox'));
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][multi_field]', 'value' => '', 'id' => 'ModelMultiField'),
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][multi_field][]', 'checked' => 'checked', 'value' => '0', 'id' => 'ModelMultiField0')),
+			array('label' => array('for' => 'ModelMultiField0', 'class' => 'selected')),
+			'first',
+			'/label',
+			'/div',
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][multi_field][]', 'checked' => 'checked', 'value' => '1', 'id' => 'ModelMultiField1')),
+			array('label' => array('for' => 'ModelMultiField1', 'class' => 'selected')),
+			'second',
+			'/label',
+			'/div',
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][multi_field][]', 'value' => '2', 'id' => 'ModelMultiField2')),
+			array('label' => array('for' => 'ModelMultiField2')),
+			'third',
+			'/label',
+			'/div',
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->select('Model.multi_field', array('1/2' => 'half'), null, array('multiple' => 'checkbox'));
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][multi_field]', 'value' => '', 'id' => 'ModelMultiField'),
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][multi_field][]', 'value' => '1/2', 'id' => 'ModelMultiField12')),
+			array('label' => array('for' => 'ModelMultiField12')),
+			'half',
+			'/label',
+			'/div',
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testLabel method
+ *
+ * test label generation.
+ *
+ * @access public
+ * @return void
+ */
+	function testLabel() {
+		$this->Form->text('Person.name');
+		$result = $this->Form->label();
+		$this->assertTags($result, array('label' => array('for' => 'PersonName'), 'Name', '/label'));
+
+		$this->Form->text('Person.name');
+		$result = $this->Form->label();
+		$this->assertTags($result, array('label' => array('for' => 'PersonName'), 'Name', '/label'));
+
+		$result = $this->Form->label('Person.first_name');
+		$this->assertTags($result, array('label' => array('for' => 'PersonFirstName'), 'First Name', '/label'));
+
+		$result = $this->Form->label('Person.first_name', 'Your first name');
+		$this->assertTags($result, array('label' => array('for' => 'PersonFirstName'), 'Your first name', '/label'));
+
+		$result = $this->Form->label('Person.first_name', 'Your first name', array('class' => 'my-class'));
+		$this->assertTags($result, array('label' => array('for' => 'PersonFirstName', 'class' => 'my-class'), 'Your first name', '/label'));
+
+		$result = $this->Form->label('Person.first_name', 'Your first name', array('class' => 'my-class', 'id' => 'LabelID'));
+		$this->assertTags($result, array('label' => array('for' => 'PersonFirstName', 'class' => 'my-class', 'id' => 'LabelID'), 'Your first name', '/label'));
+
+		$result = $this->Form->label('Person.first_name', '');
+		$this->assertTags($result, array('label' => array('for' => 'PersonFirstName'), '/label'));
+
+		$result = $this->Form->label('Person.2.name', '');
+		$this->assertTags($result, array('label' => array('for' => 'Person2Name'), '/label'));
+	}
+
+/**
+ * testTextbox method
+ *
+ * test textbox element generation
+ *
+ * @access public
+ * @return void
+ */
+	function testTextbox() {
+		$result = $this->Form->text('Model.field');
+		$this->assertTags($result, array('input' => array('type' => 'text', 'name' => 'data[Model][field]', 'id' => 'ModelField')));
+
+		$result = $this->Form->text('Model.field', array('type' => 'password'));
+		$this->assertTags($result, array('input' => array('type' => 'password', 'name' => 'data[Model][field]', 'id' => 'ModelField')));
+
+		$result = $this->Form->text('Model.field', array('id' => 'theID'));
+		$this->assertTags($result, array('input' => array('type' => 'text', 'name' => 'data[Model][field]', 'id' => 'theID')));
+
+		$this->Form->data['Model']['text'] = 'test <strong>HTML</strong> values';
+		$result = $this->Form->text('Model.text');
+		$this->assertTags($result, array('input' => array('type' => 'text', 'name' => 'data[Model][text]', 'value' => 'test &lt;strong&gt;HTML&lt;/strong&gt; values', 'id' => 'ModelText')));
+
+		$this->Form->validationErrors['Model']['text'] = 1;
+		$this->Form->data['Model']['text'] = 'test';
+		$result = $this->Form->text('Model.text', array('id' => 'theID'));
+		$this->assertTags($result, array('input' => array('type' => 'text', 'name' => 'data[Model][text]', 'value' => 'test', 'id' => 'theID', 'class' => 'form-error')));
+
+		$this->Form->data['Model']['0']['OtherModel']['field'] = 'My value';
+		$result = $this->Form->text('Model.0.OtherModel.field', array('id' => 'myId'));
+		$expected = array(
+			'input' => array('type' => 'text', 'name' => 'data[Model][0][OtherModel][field]', 'value' => 'My value', 'id' => 'myId')
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testDefaultValue method
+ *
+ * Test default value setting
+ *
+ * @access public
+ * @return void
+ */
+	function testDefaultValue() {
+		$this->Form->data['Model']['field'] = 'test';
+		$result = $this->Form->text('Model.field', array('default' => 'default value'));
+		$this->assertTags($result, array('input' => array('type' => 'text', 'name' => 'data[Model][field]', 'value' => 'test', 'id' => 'ModelField')));
+
+		unset($this->Form->data['Model']['field']);
+		$result = $this->Form->text('Model.field', array('default' => 'default value'));
+		$this->assertTags($result, array('input' => array('type' => 'text', 'name' => 'data[Model][field]', 'value' => 'default value', 'id' => 'ModelField')));
+	}
+
+/**
+ * testError method
+ *
+ * Test field error generation
+ *
+ * @access public
+ * @return void
+ */
+	function testError() {
+		$this->Form->validationErrors['Model']['field'] = 1;
+		$result = $this->Form->error('Model.field');
+		$this->assertTags($result, array('div' => array('class' => 'error-message'), 'Error in field Field', '/div'));
+
+		$result = $this->Form->error('Model.field', null, array('wrap' => false));
+		$this->assertEqual($result, 'Error in field Field');
+
+		$this->Form->validationErrors['Model']['field'] = "This field contains invalid input";
+		$result = $this->Form->error('Model.field', null, array('wrap' => false));
+		$this->assertEqual($result, 'This field contains invalid input');
+
+		$this->Form->validationErrors['Model']['field'] = "This field contains invalid input";
+		$result = $this->Form->error('Model.field', null, array('wrap' => 'span'));
+		$this->assertTags($result, array('span' => array('class' => 'error-message'), 'This field contains invalid input', '/span'));
+
+		$result = $this->Form->error('Model.field', 'There is an error fool!', array('wrap' => 'span'));
+		$this->assertTags($result, array('span' => array('class' => 'error-message'), 'There is an error fool!', '/span'));
+
+		$result = $this->Form->error('Model.field', "<strong>Badness!</strong>", array('wrap' => false));
+		$this->assertEqual($result, '&lt;strong&gt;Badness!&lt;/strong&gt;');
+
+		$result = $this->Form->error('Model.field', "<strong>Badness!</strong>", array('wrap' => false, 'escape' => true));
+		$this->assertEqual($result, '&lt;strong&gt;Badness!&lt;/strong&gt;');
+
+		$result = $this->Form->error('Model.field', "<strong>Badness!</strong>", array('wrap' => false, 'escape' => false));
+		$this->assertEqual($result, '<strong>Badness!</strong>');
+
+		$this->Form->validationErrors['Model']['field'] = "email";
+		$result = $this->Form->error('Model.field', array('class' => 'field-error', 'email' => 'No good!'));
+		$expected = array(
+			'div' => array('class' => 'field-error'),
+			'No good!',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test error options when using form->input();
+ *
+ * @return void
+ */
+	function testInputErrorEscape() {
+		$this->Form->create('ValidateProfile');
+		$this->Form->validationErrors['ValidateProfile']['city'] = 'required<br>';
+		$result = $this->Form->input('city',array('error' => array('escape' => true)));
+		$this->assertPattern('/required&lt;br&gt;/', $result);
+
+		$result = $this->Form->input('city',array('error' => array('escape' => false)));
+		$this->assertPattern('/required<br>/', $result);
+	}
+
+/**
+ * testPassword method
+ *
+ * Test password element generation
+ *
+ * @access public
+ * @return void
+ */
+	function testPassword() {
+		$result = $this->Form->password('Model.field');
+		$this->assertTags($result, array('input' => array('type' => 'password', 'name' => 'data[Model][field]', 'id' => 'ModelField')));
+
+		$this->Form->validationErrors['Model']['passwd'] = 1;
+		$this->Form->data['Model']['passwd'] = 'test';
+		$result = $this->Form->password('Model.passwd', array('id' => 'theID'));
+		$this->assertTags($result, array('input' => array('type' => 'password', 'name' => 'data[Model][passwd]', 'value' => 'test', 'id' => 'theID', 'class' => 'form-error')));
+	}
+
+/**
+ * testRadio method
+ *
+ * Test radio element set generation
+ *
+ * @access public
+ * @return void
+ */
+	function testRadio() {
+		$result = $this->Form->radio('Model.field', array('option A'));
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][field]', 'value' => '', 'id' => 'ModelField_'),
+			array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '0', 'id' => 'ModelField0')),
+			'label' => array('for' => 'ModelField0'),
+			'option A',
+			'/label'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->radio('Model.field', array('1/2' => 'half'));
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][field]', 'value' => '', 'id' => 'ModelField_'),
+			array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '1/2', 'id' => 'ModelField12')),
+			'label' => array('for' => 'ModelField12'),
+			'half',
+			'/label'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->radio('Model.field', array('option A', 'option B'));
+		$expected = array(
+			'fieldset' => array(),
+			'legend' => array(),
+			'Field',
+			'/legend',
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][field]', 'value' => '', 'id' => 'ModelField_'),
+			array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '0', 'id' => 'ModelField0')),
+			array('label' => array('for' => 'ModelField0')),
+			'option A',
+			'/label',
+			array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '1', 'id' => 'ModelField1')),
+			array('label' => array('for' => 'ModelField1')),
+			'option B',
+			'/label',
+			'/fieldset'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->radio('Model.field', array('option A', 'option B'), array('separator' => '<br/>'));
+		$expected = array(
+			'fieldset' => array(),
+			'legend' => array(),
+			'Field',
+			'/legend',
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][field]', 'value' => '', 'id' => 'ModelField_'),
+			array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '0', 'id' => 'ModelField0')),
+			array('label' => array('for' => 'ModelField0')),
+			'option A',
+			'/label',
+			'br' => array(),
+			array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '1', 'id' => 'ModelField1')),
+			array('label' => array('for' => 'ModelField1')),
+			'option B',
+			'/label',
+			'/fieldset'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->radio('Model.field', array('1' => 'Yes', '0' => 'No'), array('value' => '1'));
+		$expected = array(
+			'fieldset' => array(),
+			'legend' => array(),
+			'Field',
+			'/legend',
+			array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '1', 'id' => 'ModelField1', 'checked' => 'checked')),
+			array('label' => array('for' => 'ModelField1')),
+			'Yes',
+			'/label',
+			array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '0', 'id' => 'ModelField0')),
+			array('label' => array('for' => 'ModelField0')),
+			'No',
+			'/label',
+			'/fieldset'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->radio('Model.field', array('1' => 'Yes', '0' => 'No'), array('value' => '0'));
+		$expected = array(
+			'fieldset' => array(),
+			'legend' => array(),
+			'Field',
+			'/legend',
+			array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '1', 'id' => 'ModelField1')),
+			array('label' => array('for' => 'ModelField1')),
+			'Yes',
+			'/label',
+			array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '0', 'id' => 'ModelField0', 'checked' => 'checked')),
+			array('label' => array('for' => 'ModelField0')),
+			'No',
+			'/label',
+			'/fieldset'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->radio('Model.field', array('1' => 'Yes', '0' => 'No'), array('value' => null));
+		$expected = array(
+			'fieldset' => array(),
+			'legend' => array(),
+			'Field',
+			'/legend',
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][field]', 'value' => '', 'id' => 'ModelField_'),
+			array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '1', 'id' => 'ModelField1')),
+			array('label' => array('for' => 'ModelField1')),
+			'Yes',
+			'/label',
+			array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '0', 'id' => 'ModelField0')),
+			array('label' => array('for' => 'ModelField0')),
+			'No',
+			'/label',
+			'/fieldset'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->radio('Model.field', array('1' => 'Yes', '0' => 'No'));
+		$expected = array(
+			'fieldset' => array(),
+			'legend' => array(),
+			'Field',
+			'/legend',
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][field]', 'value' => '', 'id' => 'ModelField_'),
+			array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '1', 'id' => 'ModelField1')),
+			array('label' => array('for' => 'ModelField1')),
+			'Yes',
+			'/label',
+			array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '0', 'id' => 'ModelField0')),
+			array('label' => array('for' => 'ModelField0')),
+			'No',
+			'/label',
+			'/fieldset'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data = array('Model' => array('field' => ''));
+		$result = $this->Form->radio('Model.field', array('1' => 'Yes', '0' => 'No'));
+		$expected = array(
+			'fieldset' => array(),
+			'legend' => array(),
+			'Field',
+			'/legend',
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][field]', 'value' => '', 'id' => 'ModelField_'),
+			array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '1', 'id' => 'ModelField1')),
+			array('label' => array('for' => 'ModelField1')),
+			'Yes',
+			'/label',
+			array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '0', 'id' => 'ModelField0')),
+			array('label' => array('for' => 'ModelField0')),
+			'No',
+			'/label',
+			'/fieldset'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Newsletter.subscribe', array('legend' => 'Legend title', 'type' => 'radio', 'options' => array('0' => 'Unsubscribe', '1' => 'Subscribe')));
+		$expected = array(
+			'div' => array('class' => 'input radio'),
+			'fieldset' => array(),
+			'legend' => array(),
+			'Legend title',
+			'/legend',
+			'input' => array('type' => 'hidden', 'name' => 'data[Newsletter][subscribe]', 'value' => '', 'id' => 'NewsletterSubscribe_'),
+			array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '0', 'id' => 'NewsletterSubscribe0')),
+			array('label' => array('for' => 'NewsletterSubscribe0')),
+			'Unsubscribe',
+			'/label',
+			array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '1', 'id' => 'NewsletterSubscribe1')),
+			array('label' => array('for' => 'NewsletterSubscribe1')),
+			'Subscribe',
+			'/label',
+			'/fieldset',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Newsletter.subscribe', array('legend' => false, 'type' => 'radio', 'options' => array('0' => 'Unsubscribe', '1' => 'Subscribe')));
+		$expected = array(
+			'div' => array('class' => 'input radio'),
+			'input' => array('type' => 'hidden', 'name' => 'data[Newsletter][subscribe]', 'value' => '', 'id' => 'NewsletterSubscribe_'),
+			array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '0', 'id' => 'NewsletterSubscribe0')),
+			array('label' => array('for' => 'NewsletterSubscribe0')),
+			'Unsubscribe',
+			'/label',
+			array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '1', 'id' => 'NewsletterSubscribe1')),
+			array('label' => array('for' => 'NewsletterSubscribe1')),
+			'Subscribe',
+			'/label',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Newsletter.subscribe', array('legend' => 'Legend title', 'label' => false, 'type' => 'radio', 'options' => array('0' => 'Unsubscribe', '1' => 'Subscribe')));
+		$expected = array(
+			'div' => array('class' => 'input radio'),
+			'fieldset' => array(),
+			'legend' => array(),
+			'Legend title',
+			'/legend',
+			'input' => array('type' => 'hidden', 'name' => 'data[Newsletter][subscribe]', 'value' => '', 'id' => 'NewsletterSubscribe_'),
+			array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '0', 'id' => 'NewsletterSubscribe0')),
+			'Unsubscribe',
+			array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '1', 'id' => 'NewsletterSubscribe1')),
+			'Subscribe',
+			'/fieldset',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Newsletter.subscribe', array('legend' => false, 'label' => false, 'type' => 'radio', 'options' => array('0' => 'Unsubscribe', '1' => 'Subscribe')));
+		$expected = array(
+			'div' => array('class' => 'input radio'),
+			'input' => array('type' => 'hidden', 'name' => 'data[Newsletter][subscribe]', 'value' => '', 'id' => 'NewsletterSubscribe_'),
+			array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '0', 'id' => 'NewsletterSubscribe0')),
+			'Unsubscribe',
+			array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '1', 'id' => 'NewsletterSubscribe1')),
+			'Subscribe',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Newsletter.subscribe', array('legend' => false, 'label' => false, 'type' => 'radio', 'value' => '1', 'options' => array('0' => 'Unsubscribe', '1' => 'Subscribe')));
+		$expected = array(
+			'div' => array('class' => 'input radio'),
+			array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '0', 'id' => 'NewsletterSubscribe0')),
+			'Unsubscribe',
+			array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '1', 'id' => 'NewsletterSubscribe1', 'checked' => 'checked')),
+			'Subscribe',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->radio('Employee.gender', array('male' => 'Male', 'female' => 'Female'));
+		$expected = array(
+			'fieldset' => array(),
+			'legend' => array(),
+			'Gender',
+			'/legend',
+			'input' => array('type' => 'hidden', 'name' => 'data[Employee][gender]', 'value' => '', 'id' => 'EmployeeGender_'),
+			array('input' => array('type' => 'radio', 'name' => 'data[Employee][gender]', 'value' => 'male', 'id' => 'EmployeeGenderMale')),
+			array('label' => array('for' => 'EmployeeGenderMale')),
+			'Male',
+			'/label',
+			array('input' => array('type' => 'radio', 'name' => 'data[Employee][gender]', 'value' => 'female', 'id' => 'EmployeeGenderFemale')),
+			array('label' => array('for' => 'EmployeeGenderFemale')),
+			'Female',
+			'/label',
+			'/fieldset',
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->radio('Officer.gender', array('male' => 'Male', 'female' => 'Female'));
+		$expected = array(
+			'fieldset' => array(),
+			'legend' => array(),
+			'Gender',
+			'/legend',
+			'input' => array('type' => 'hidden', 'name' => 'data[Officer][gender]', 'value' => '', 'id' => 'OfficerGender_'),
+			array('input' => array('type' => 'radio', 'name' => 'data[Officer][gender]', 'value' => 'male', 'id' => 'OfficerGenderMale')),
+			array('label' => array('for' => 'OfficerGenderMale')),
+			'Male',
+			'/label',
+			array('input' => array('type' => 'radio', 'name' => 'data[Officer][gender]', 'value' => 'female', 'id' => 'OfficerGenderFemale')),
+			array('label' => array('for' => 'OfficerGenderFemale')),
+			'Female',
+			'/label',
+			'/fieldset',
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->radio('Contact.1.imrequired', array('option A'));
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Contact][1][imrequired]', 'value' => '', 'id' => 'Contact1Imrequired_'),
+			array('input' => array('type' => 'radio', 'name' => 'data[Contact][1][imrequired]', 'value' => '0', 'id' => 'Contact1Imrequired0')),
+			'label' => array('for' => 'Contact1Imrequired0'),
+			'option A',
+			'/label'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->radio('Model.1.field', array('option A'));
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][1][field]', 'value' => '', 'id' => 'Model1Field_'),
+			array('input' => array('type' => 'radio', 'name' => 'data[Model][1][field]', 'value' => '0', 'id' => 'Model1Field0')),
+			'label' => array('for' => 'Model1Field0'),
+			'option A',
+			'/label'
+		);
+		$this->assertTags($result, $expected);
+
+
+		$result = $this->Form->radio('Model.field', array('option A', 'option B'), array('name' => 'data[Model][custom]'));
+		$expected = array(
+			'fieldset' => array(),
+			'legend' => array(),
+			'Field',
+			'/legend',
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][custom]', 'value' => '', 'id' => 'ModelField_'),
+			array('input' => array('type' => 'radio', 'name' => 'data[Model][custom]', 'value' => '0', 'id' => 'ModelField0')),
+			array('label' => array('for' => 'ModelField0')),
+			'option A',
+			'/label',
+			array('input' => array('type' => 'radio', 'name' => 'data[Model][custom]', 'value' => '1', 'id' => 'ModelField1')),
+			array('label' => array('for' => 'ModelField1')),
+			'option B',
+			'/label',
+			'/fieldset'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test disabling the hidden input for radio buttons
+ *
+ * @return void
+ */
+	function testRadioHiddenInputDisabling() {
+		$result = $this->Form->input('Model.1.field', array(
+				'type' => 'radio',
+				'options' => array('option A'),
+				'hiddenField' => false
+			)
+		);
+		$expected = array(
+			'div' => array('class' => 'input radio'),
+			'input' => array('type' => 'radio', 'name' => 'data[Model][1][field]', 'value' => '0', 'id' => 'Model1Field0'),
+			'label' => array('for' => 'Model1Field0'),
+			'option A',
+			'/label',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->radio('Model.1.field', array('option A'), array('hiddenField' => false));
+		$expected = array(
+			'input' => array('type' => 'radio', 'name' => 'data[Model][1][field]', 'value' => '0', 'id' => 'Model1Field0'),
+			'label' => array('for' => 'Model1Field0'),
+			'option A',
+			'/label'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testSelect method
+ *
+ * Test select element generation.
+ *
+ * @access public
+ * @return void
+ */
+	function testSelect() {
+		$result = $this->Form->select('Model.field', array());
+		$expected = array(
+			'select' => array('name' => 'data[Model][field]', 'id' => 'ModelField'),
+			array('option' => array('value' => '')),
+			'/option',
+			'/select'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data = array('Model' => array('field' => 'value'));
+		$result = $this->Form->select('Model.field', array('value' => 'good', 'other' => 'bad'));
+		$expected = array(
+			'select' => array('name' => 'data[Model][field]', 'id' => 'ModelField'),
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => 'value', 'selected' => 'selected')),
+			'good',
+			'/option',
+			array('option' => array('value' => 'other')),
+			'bad',
+			'/option',
+			'/select'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data = array();
+		$result = $this->Form->select('Model.field', array('value' => 'good', 'other' => 'bad'));
+		$expected = array(
+			'select' => array('name' => 'data[Model][field]', 'id' => 'ModelField'),
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => 'value')),
+			'good',
+			'/option',
+			array('option' => array('value' => 'other')),
+			'bad',
+			'/option',
+			'/select'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->select(
+			'Model.field', array('first' => 'first "html" <chars>', 'second' => 'value'),
+			null, array('empty' => false)
+		);
+		$expected = array(
+			'select' => array('name' => 'data[Model][field]', 'id' => 'ModelField'),
+			array('option' => array('value' => 'first')),
+			'first &quot;html&quot; &lt;chars&gt;',
+			'/option',
+			array('option' => array('value' => 'second')),
+			'value',
+			'/option',
+			'/select'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->select(
+			'Model.field',
+			array('first' => 'first "html" <chars>', 'second' => 'value'),
+			null, array('escape' => false, 'empty' => false)
+		);
+		$expected = array(
+			'select' => array('name' => 'data[Model][field]', 'id' => 'ModelField'),
+			array('option' => array('value' => 'first')),
+			'first "html" <chars>',
+			'/option',
+			array('option' => array('value' => 'second')),
+			'value',
+			'/option',
+			'/select'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data = array('Model' => array('contact_id' => 228));
+		$result = $this->Form->select(
+			'Model.contact_id',
+			array('228' => '228 value', '228-1' => '228-1 value', '228-2' => '228-2 value'),
+			null, array('escape' => false, 'empty' => 'pick something')
+		);
+
+		$expected = array(
+			'select' => array('name' => 'data[Model][contact_id]', 'id' => 'ModelContactId'),
+			array('option' => array('value' => '')), 'pick something', '/option',
+			array('option' => array('value' => '228', 'selected' => 'selected')), '228 value', '/option',
+			array('option' => array('value' => '228-1')), '228-1 value', '/option',
+			array('option' => array('value' => '228-2')), '228-2 value', '/option',
+			'/select'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test that select() with optiongroups listens to the escape param.
+ *
+ * @return void
+ */
+	function testSelectOptionGroupEscaping() {
+		$options = array(
+			'>< Key' => array(
+				1 => 'One',
+				2 => 'Two'
+			)
+		);
+		$result = $this->Form->select('Model.field', $options, null, array('empty' => false));
+		$expected = array(
+			'select' => array('name' => 'data[Model][field]', 'id' => 'ModelField'),
+			'optgroup' => array('label' => '&gt;&lt; Key'),
+			array('option' => array('value' => '1')), 'One', '/option',
+			array('option' => array('value' => '2')), 'Two', '/option',
+			'/optgroup',
+			'/select'
+		);
+		$this->assertTags($result, $expected);
+
+		$options = array(
+			'>< Key' => array(
+				1 => 'One',
+				2 => 'Two'
+			)
+		);
+		$result = $this->Form->select('Model.field', $options, null, array('empty' => false, 'escape' => false));
+		$expected = array(
+			'select' => array('name' => 'data[Model][field]', 'id' => 'ModelField'),
+			'optgroup' => array('label' => '>< Key'),
+			array('option' => array('value' => '1')), 'One', '/option',
+			array('option' => array('value' => '2')), 'Two', '/option',
+			'/optgroup',
+			'/select'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * Tests that FormHelper::select() allows null to be passed in the $attributes parameter
+ *
+ * @access public
+ * @return void
+ */
+	function testSelectWithNullAttributes() {
+		$result = $this->Form->select('Model.field', array('first', 'second'), null, array('empty' => false));
+		$expected = array(
+			'select' => array('name' => 'data[Model][field]', 'id' => 'ModelField'),
+			array('option' => array('value' => '0')),
+			'first',
+			'/option',
+			array('option' => array('value' => '1')),
+			'second',
+			'/option',
+			'/select'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testNestedSelect method
+ *
+ * test select element generation with optgroups
+ *
+ * @access public
+ * @return void
+ */
+	function testNestedSelect() {
+		$result = $this->Form->select(
+			'Model.field',
+			array(1 => 'One', 2 => 'Two', 'Three' => array(
+				3 => 'Three', 4 => 'Four', 5 => 'Five'
+			)), null, array('empty' => false)
+		);
+		$expected = array(
+			'select' => array('name' => 'data[Model][field]',
+					'id' => 'ModelField'),
+					array('option' => array('value' => 1)),
+					'One',
+					'/option',
+					array('option' => array('value' => 2)),
+					'Two',
+					'/option',
+					array('optgroup' => array('label' => 'Three')),
+						array('option' => array('value' => 4)),
+						'Four',
+						'/option',
+						array('option' => array('value' => 5)),
+						'Five',
+						'/option',
+					'/optgroup',
+					'/select'
+					);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->select(
+			'Model.field',
+			array(1 => 'One', 2 => 'Two', 'Three' => array(3 => 'Three', 4 => 'Four')), null,
+			array('showParents' => true, 'empty' => false)
+		);
+
+		$expected = array(
+			'select' => array('name' => 'data[Model][field]', 'id' => 'ModelField'),
+				array('option' => array('value' => 1)),
+				'One',
+				'/option',
+				array('option' => array('value' => 2)),
+				'Two',
+				'/option',
+				array('optgroup' => array('label' => 'Three')),
+					array('option' => array('value' => 3)),
+					'Three',
+					'/option',
+					array('option' => array('value' => 4)),
+					'Four',
+					'/option',
+				'/optgroup',
+			'/select'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testSelectMultiple method
+ *
+ * test generation of multiple select elements
+ *
+ * @access public
+ * @return void
+ */
+	function testSelectMultiple() {
+		$options = array('first', 'second', 'third');
+		$result = $this->Form->select(
+			'Model.multi_field', $options, null, array('multiple' => true)
+		);
+		$expected = array(
+			'input' => array(
+				'type' => 'hidden', 'name' => 'data[Model][multi_field]', 'value' => '', 'id' => 'ModelMultiField_'
+			),
+			'select' => array(
+				'name' => 'data[Model][multi_field][]',
+				'id' => 'ModelMultiField', 'multiple' => 'multiple'
+			),
+			array('option' => array('value' => '0')),
+			'first',
+			'/option',
+			array('option' => array('value' => '1')),
+			'second',
+			'/option',
+			array('option' => array('value' => '2')),
+			'third',
+			'/option',
+			'/select'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->select(
+			'Model.multi_field', $options, null, array('multiple' => 'multiple')
+		);
+		$expected = array(
+			'input' => array(
+				'type' => 'hidden', 'name' => 'data[Model][multi_field]', 'value' => '', 'id' => 'ModelMultiField_'
+			),
+			'select' => array(
+				'name' => 'data[Model][multi_field][]',
+				'id' => 'ModelMultiField', 'multiple' => 'multiple'
+			),
+			array('option' => array('value' => '0')),
+			'first',
+			'/option',
+			array('option' => array('value' => '1')),
+			'second',
+			'/option',
+			array('option' => array('value' => '2')),
+			'third',
+			'/option',
+			'/select'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->select(
+			'Model.multi_field', $options, array(0, 1), array('multiple' => true)
+		);
+		$expected = array(
+			'input' => array(
+				'type' => 'hidden', 'name' => 'data[Model][multi_field]', 'value' => '', 'id' => 'ModelMultiField_'
+			),
+			'select' => array(
+				'name' => 'data[Model][multi_field][]', 'id' => 'ModelMultiField',
+				'multiple' => 'multiple'
+			),
+			array('option' => array('value' => '0', 'selected' => 'selected')),
+			'first',
+			'/option',
+			array('option' => array('value' => '1', 'selected' => 'selected')),
+			'second',
+			'/option',
+			array('option' => array('value' => '2')),
+			'third',
+			'/option',
+			'/select'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->select(
+			'Model.multi_field', $options, array(0, 1), array('multiple' => false)
+		);
+		$expected = array(
+			'select' => array(
+				'name' => 'data[Model][multi_field]', 'id' => 'ModelMultiField'
+			),
+			array('option' => array('value' => '0', 'selected' => 'selected')),
+			'first',
+			'/option',
+			array('option' => array('value' => '1', 'selected' => 'selected')),
+			'second',
+			'/option',
+			array('option' => array('value' => '2')),
+			'third',
+			'/option',
+			'/select'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test generation of habtm select boxes.
+ *
+ * @return void
+ */
+	function testHabtmSelectBox() {
+		$view =& ClassRegistry::getObject('view');
+		$view->viewVars['contactTags'] = array(
+			1 => 'blue',
+			2 => 'red',
+			3 => 'green'
+		);
+		$this->Form->data = array(
+			'Contact' => array(),
+			'ContactTag' => array(
+				array(
+					'id' => 1,
+					'name' => 'blue'
+				),
+				array(
+					'id' => 3,
+					'name' => 'green'
+				)
+			)
+		);
+		$this->Form->create('Contact');
+		$result = $this->Form->input('ContactTag', array('div' => false, 'label' => false));
+		$expected = array(
+			'input' => array(
+				'type' => 'hidden', 'name' => 'data[ContactTag][ContactTag]', 'value' => '', 'id' => 'ContactTagContactTag_'
+			),
+			'select' => array(
+				'name' => 'data[ContactTag][ContactTag][]', 'id' => 'ContactTagContactTag',
+				'multiple' => 'multiple'
+			),
+			array('option' => array('value' => '1', 'selected' => 'selected')),
+			'blue',
+			'/option',
+			array('option' => array('value' => '2')),
+			'red',
+			'/option',
+			array('option' => array('value' => '3', 'selected' => 'selected')),
+			'green',
+			'/option',
+			'/select'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test generation of multi select elements in checkbox format
+ *
+ * @access public
+ * @return void
+ */
+	function testSelectMultipleCheckboxes() {
+		$result = $this->Form->select(
+			'Model.multi_field',
+			array('first', 'second', 'third'), null,
+			array('multiple' => 'checkbox')
+		);
+
+		$expected = array(
+			'input' => array(
+				'type' => 'hidden', 'name' => 'data[Model][multi_field]', 'value' => '', 'id' => 'ModelMultiField'
+			),
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array(
+				'type' => 'checkbox', 'name' => 'data[Model][multi_field][]',
+				'value' => '0', 'id' => 'ModelMultiField0'
+			)),
+			array('label' => array('for' => 'ModelMultiField0')),
+			'first',
+			'/label',
+			'/div',
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array(
+				'type' => 'checkbox', 'name' => 'data[Model][multi_field][]',
+				'value' => '1', 'id' => 'ModelMultiField1'
+			)),
+			array('label' => array('for' => 'ModelMultiField1')),
+			'second',
+			'/label',
+			'/div',
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array(
+				'type' => 'checkbox', 'name' => 'data[Model][multi_field][]',
+				'value' => '2', 'id' => 'ModelMultiField2'
+			)),
+			array('label' => array('for' => 'ModelMultiField2')),
+			'third',
+			'/label',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->select(
+			'Model.multi_field',
+			array('a' => 'first', 'b' => 'second', 'c' => 'third'), null,
+			array('multiple' => 'checkbox')
+		);
+		$expected = array(
+			'input' => array(
+				'type' => 'hidden', 'name' => 'data[Model][multi_field]', 'value' => '', 'id' => 'ModelMultiField'
+			),
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array(
+				'type' => 'checkbox', 'name' => 'data[Model][multi_field][]',
+				'value' => 'a', 'id' => 'ModelMultiFieldA'
+			)),
+			array('label' => array('for' => 'ModelMultiFieldA')),
+			'first',
+			'/label',
+			'/div',
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array(
+				'type' => 'checkbox', 'name' => 'data[Model][multi_field][]',
+				'value' => 'b', 'id' => 'ModelMultiFieldB'
+			)),
+			array('label' => array('for' => 'ModelMultiFieldB')),
+			'second',
+			'/label',
+			'/div',
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array(
+				'type' => 'checkbox', 'name' => 'data[Model][multi_field][]',
+				'value' => 'c', 'id' => 'ModelMultiFieldC'
+			)),
+			array('label' => array('for' => 'ModelMultiFieldC')),
+			'third',
+			'/label',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->select(
+			'Model.multi_field', array('1' => 'first'), null, array('multiple' => 'checkbox')
+		);
+		$expected = array(
+			'input' => array(
+				'type' => 'hidden', 'name' => 'data[Model][multi_field]', 'value' => '', 'id' => 'ModelMultiField'
+			),
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array(
+				'type' => 'checkbox', 'name' => 'data[Model][multi_field][]',
+				'value' => '1', 'id' => 'ModelMultiField1'
+			)),
+			array('label' => array('for' => 'ModelMultiField1')),
+			'first',
+			'/label',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data = array('Model' => array('tags' => array(1)));
+		$result = $this->Form->select(
+			'Model.tags', array('1' => 'first', 'Array' => 'Array'), null, array('multiple' => 'checkbox')
+		);
+		$expected = array(
+			'input' => array(
+				'type' => 'hidden', 'name' => 'data[Model][tags]', 'value' => '', 'id' => 'ModelTags'
+			),
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array(
+				'type' => 'checkbox', 'name' => 'data[Model][tags][]',
+				'value' => '1', 'id' => 'ModelTags1', 'checked' => 'checked'
+			)),
+			array('label' => array('for' => 'ModelTags1', 'class' => 'selected')),
+			'first',
+			'/label',
+			'/div',
+
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array(
+				'type' => 'checkbox', 'name' => 'data[Model][tags][]',
+				'value' => 'Array', 'id' => 'ModelTagsArray'
+			)),
+			array('label' => array('for' => 'ModelTagsArray')),
+			'Array',
+			'/label',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test multiple checkboxes with div styles.
+ *
+ * @return void
+ */
+	function testSelectMultipleCheckboxDiv() {
+		$result = $this->Form->select(
+			'Model.tags',
+			array('first', 'second'),
+			null,
+			array('multiple' => 'checkbox', 'class' => 'my-class')
+		);
+		$expected = array(
+			'input' => array(
+				'type' => 'hidden', 'name' => 'data[Model][tags]', 'value' => '', 'id' => 'ModelTags'
+			),
+			array('div' => array('class' => 'my-class')),
+			array('input' => array(
+				'type' => 'checkbox', 'name' => 'data[Model][tags][]',
+				'value' => '0', 'id' => 'ModelTags0'
+			)),
+			array('label' => array('for' => 'ModelTags0')), 'first', '/label',
+			'/div',
+
+			array('div' => array('class' => 'my-class')),
+			array('input' => array(
+				'type' => 'checkbox', 'name' => 'data[Model][tags][]',
+				'value' => '1', 'id' => 'ModelTags1'
+			)),
+			array('label' => array('for' => 'ModelTags1')), 'second', '/label',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Model.tags', array(
+			'options' => array('first', 'second'),
+			'multiple' => 'checkbox',
+			'class' => 'my-class',
+			'div' => false,
+			'label' => false
+		));
+		$this->assertTags($result, $expected);
+
+		$this->Form->validationErrors['Model']['tags'] = 'Select atleast one option';
+		$result = $this->Form->input('Model.tags', array(
+			'options' => array('one'),
+			'multiple' => 'checkbox',
+			'label' => false,
+			'div' => false
+		));
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][tags]', 'value' => '', 'id' => 'ModelTags'),
+			array('div' => array('class' => 'checkbox form-error')),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][tags][]', 'value' => '0', 'id' => 'ModelTags0')),
+			array('label' => array('for' => 'ModelTags0')),
+			'one',
+			'/label',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Model.tags', array(
+			'options' => array('one'),
+			'multiple' => 'checkbox',
+			'class' => 'mycheckbox',
+			'label' => false,
+			'div' => false
+		));
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][tags]', 'value' => '', 'id' => 'ModelTags'),
+			array('div' => array('class' => 'mycheckbox form-error')),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][tags][]', 'value' => '0', 'id' => 'ModelTags0')),
+			array('label' => array('for' => 'ModelTags0')),
+			'one',
+			'/label',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * Checks the security hash array generated for multiple-input checkbox elements
+ *
+ * @access public
+ * @return void
+ */
+	function testSelectMultipleCheckboxSecurity() {
+		$this->Form->params['_Token']['key'] = 'testKey';
+		$this->assertEqual($this->Form->fields, array());
+
+		$result = $this->Form->select(
+			'Model.multi_field', array('1' => 'first', '2' => 'second', '3' => 'third'),
+			null, array('multiple' => 'checkbox')
+		);
+		$this->assertEqual($this->Form->fields, array('Model.multi_field'));
+
+		$result = $this->Form->secure($this->Form->fields);
+		$key = 'f7d573650a295b94e0938d32b323fde775e5f32b%3A';
+		$this->assertPattern('/"' . $key . '"/', $result);
+	}
+
+/**
+ * testInputMultipleCheckboxes method
+ *
+ * test input() resulting in multi select elements being generated.
+ *
+ * @access public
+ * @return void
+ */
+	function testInputMultipleCheckboxes() {
+		$result = $this->Form->input('Model.multi_field', array(
+			'options' => array('first', 'second', 'third'),
+			'multiple' => 'checkbox'
+		));
+		$expected = array(
+			array('div' => array('class' => 'input select')),
+			array('label' => array('for' => 'ModelMultiField')),
+			'Multi Field',
+			'/label',
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][multi_field]', 'value' => '', 'id' => 'ModelMultiField'),
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][multi_field][]', 'value' => '0', 'id' => 'ModelMultiField0')),
+			array('label' => array('for' => 'ModelMultiField0')),
+			'first',
+			'/label',
+			'/div',
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][multi_field][]', 'value' => '1', 'id' => 'ModelMultiField1')),
+			array('label' => array('for' => 'ModelMultiField1')),
+			'second',
+			'/label',
+			'/div',
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][multi_field][]', 'value' => '2', 'id' => 'ModelMultiField2')),
+			array('label' => array('for' => 'ModelMultiField2')),
+			'third',
+			'/label',
+			'/div',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Model.multi_field', array(
+			'options' => array('a' => 'first', 'b' => 'second', 'c' => 'third'),
+			'multiple' => 'checkbox'
+		));
+		$expected = array(
+			array('div' => array('class' => 'input select')),
+			array('label' => array('for' => 'ModelMultiField')),
+			'Multi Field',
+			'/label',
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][multi_field]', 'value' => '', 'id' => 'ModelMultiField'),
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][multi_field][]', 'value' => 'a', 'id' => 'ModelMultiFieldA')),
+			array('label' => array('for' => 'ModelMultiFieldA')),
+			'first',
+			'/label',
+			'/div',
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][multi_field][]', 'value' => 'b', 'id' => 'ModelMultiFieldB')),
+			array('label' => array('for' => 'ModelMultiFieldB')),
+			'second',
+			'/label',
+			'/div',
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][multi_field][]', 'value' => 'c', 'id' => 'ModelMultiFieldC')),
+			array('label' => array('for' => 'ModelMultiFieldC')),
+			'third',
+			'/label',
+			'/div',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Model.multi_field', array(
+			'options' => array('1' => 'first'),
+			'multiple' => 'checkbox',
+			'label' => false,
+			'div' => false
+		));
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][multi_field]', 'value' => '', 'id' => 'ModelMultiField'),
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][multi_field][]', 'value' => '1', 'id' => 'ModelMultiField1')),
+			array('label' => array('for' => 'ModelMultiField1')),
+			'first',
+			'/label',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Model.multi_field', array(
+			'options' => array('2' => 'second'),
+			'multiple' => 'checkbox',
+			'label' => false,
+			'div' => false
+		));
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][multi_field]', 'value' => '', 'id' => 'ModelMultiField'),
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][multi_field][]', 'value' => '2', 'id' => 'ModelMultiField2')),
+			array('label' => array('for' => 'ModelMultiField2')),
+			'second',
+			'/label',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+/**
+ * testSelectHiddenFieldOmission method
+ *
+ * test that select() with 'hiddenField' => false omits the hidden field
+ *
+ * @access public
+ * @return void
+ */
+	function testSelectHiddenFieldOmission() {
+		$result = $this->Form->select('Model.multi_field',
+			array('first', 'second'),
+			null,
+			array('multiple' => 'checkbox', 'hiddenField' => false)
+		);
+		$expected = array(
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][multi_field][]', 'value' => '0', 'id' => 'ModelMultiField0')),
+			array('label' => array('for' => 'ModelMultiField0')),
+			'first',
+			'/label',
+			'/div',
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][multi_field][]', 'value' => '1', 'id' => 'ModelMultiField1')),
+			array('label' => array('for' => 'ModelMultiField1')),
+			'second',
+			'/label',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Model.multi_field', array(
+			'options' => array('first', 'second'),
+			'multiple' => 'checkbox',
+			'hiddenField' => false
+		));
+		$expected = array(
+			array('div' => array('class' => 'input select')),
+			array('label' => array('for' => 'ModelMultiField')),
+			'Multi Field',
+			'/label',
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][multi_field][]', 'value' => '0', 'id' => 'ModelMultiField0')),
+			array('label' => array('for' => 'ModelMultiField0')),
+			'first',
+			'/label',
+			'/div',
+			array('div' => array('class' => 'checkbox')),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][multi_field][]', 'value' => '1', 'id' => 'ModelMultiField1')),
+			array('label' => array('for' => 'ModelMultiField1')),
+			'second',
+			'/label',
+			'/div',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test that select() with multiple = checkbox works with overriding name attribute.
+ *
+ * @return void
+ */
+	function testSelectCheckboxMultipleOverrideName() {
+		$result = $this->Form->input('category', array(
+			'type' => 'select',
+			'multiple' => 'checkbox',
+			'name' => 'data[fish]',
+			'options' => array('1', '2'),
+			'div' => false,
+			'label' => false,
+		));
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[fish]', 'value' => '', 'id' => 'category'),
+			array('div' => array('class' => 'checkbox')),
+				array('input' => array('type' => 'checkbox', 'name' => 'data[fish][]', 'value' => '0', 'id' => 'Category0')),
+				array('label' => array('for' => 'Category0')), '1', '/label',
+			'/div',
+			array('div' => array('class' => 'checkbox')),
+				array('input' => array('type' => 'checkbox', 'name' => 'data[fish][]', 'value' => '1', 'id' => 'Category1')),
+				array('label' => array('for' => 'Category1')), '2', '/label',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testCheckbox method
+ *
+ * Test generation of checkboxes
+ *
+ * @access public
+ * @return void
+ */
+	function testCheckbox() {
+		$result = $this->Form->checkbox('Model.field', array('id' => 'theID', 'value' => 'myvalue'));
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][field]', 'value' => '0', 'id' => 'theID_'),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][field]', 'value' => 'myvalue', 'id' => 'theID'))
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->validationErrors['Model']['field'] = 1;
+		$this->Form->data['Model']['field'] = 'myvalue';
+		$result = $this->Form->checkbox('Model.field', array('id' => 'theID', 'value' => 'myvalue'));
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][field]', 'value' => '0', 'id' => 'theID_'),
+			array('input' => array('preg:/[^<]+/', 'value' => 'myvalue', 'id' => 'theID', 'checked' => 'checked', 'class' => 'form-error'))
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->checkbox('Model.field', array('value' => 'myvalue'));
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][field]', 'value' => '0', 'id' => 'ModelField_'),
+			array('input' => array('preg:/[^<]+/', 'value' => 'myvalue', 'id' => 'ModelField', 'checked' => 'checked', 'class' => 'form-error'))
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data['Model']['field'] = '';
+		$result = $this->Form->checkbox('Model.field', array('id' => 'theID'));
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][field]', 'value' => '0', 'id' => 'theID_'),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][field]', 'value' => '1', 'id' => 'theID', 'class' => 'form-error'))
+		);
+		$this->assertTags($result, $expected);
+
+		unset($this->Form->validationErrors['Model']['field']);
+		$result = $this->Form->checkbox('Model.field', array('value' => 'myvalue'));
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][field]', 'value' => '0', 'id' => 'ModelField_'),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][field]', 'value' => 'myvalue', 'id' => 'ModelField'))
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->checkbox('Contact.name', array('value' => 'myvalue'));
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Contact][name]', 'value' => '0', 'id' => 'ContactName_'),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Contact][name]', 'value' => 'myvalue', 'id' => 'ContactName'))
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->checkbox('Model.field');
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][field]', 'value' => '0', 'id' => 'ModelField_'),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][field]', 'value' => '1', 'id' => 'ModelField'))
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->checkbox('Model.field', array('checked' => false));
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][field]', 'value' => '0', 'id' => 'ModelField_'),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][field]', 'value' => '1', 'id' => 'ModelField'))
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->validationErrors['Model']['field'] = 1;
+		$this->Form->data['Contact']['published'] = 1;
+		$result = $this->Form->checkbox('Contact.published', array('id' => 'theID'));
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Contact][published]', 'value' => '0', 'id' => 'theID_'),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Contact][published]', 'value' => '1', 'id' => 'theID', 'checked' => 'checked'))
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->validationErrors['Model']['field'] = 1;
+		$this->Form->data['Contact']['published'] = 0;
+		$result = $this->Form->checkbox('Contact.published', array('id'=>'theID'));
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Contact][published]', 'value' => '0', 'id' => 'theID_'),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Contact][published]', 'value' => '1', 'id' => 'theID'))
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->checkbox('Model.CustomField.1.value');
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][CustomField][1][value]', 'value' => '0', 'id' => 'ModelCustomField1Value_'),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][CustomField][1][value]', 'value' => '1', 'id' => 'ModelCustomField1Value'))
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->checkbox('CustomField.1.value');
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[CustomField][1][value]', 'value' => '0', 'id' => 'CustomField1Value_'),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[CustomField][1][value]', 'value' => '1', 'id' => 'CustomField1Value'))
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->checkbox('Test.test', array('name' => 'myField'));
+		$expected = array(
+				'input' => array('type' => 'hidden', 'name' => 'myField', 'value' => '0', 'id' => 'TestTest_'),
+				array('input' => array('type' => 'checkbox', 'name' => 'myField', 'value' => '1', 'id' => 'TestTest'))
+			);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test the checked option for checkboxes.
+ *
+ * @return void
+ */
+	function testCheckboxCheckedOption() {
+		$result = $this->Form->checkbox('Model.field', array('checked' => 'checked'));
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][field]', 'value' => '0', 'id' => 'ModelField_'),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][field]', 'value' => '1', 'id' => 'ModelField', 'checked' => 'checked'))
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->checkbox('Model.field', array('checked' => 1));
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][field]', 'value' => '0', 'id' => 'ModelField_'),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][field]', 'value' => '1', 'id' => 'ModelField', 'checked' => 'checked'))
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->checkbox('Model.field', array('checked' => true));
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][field]', 'value' => '0', 'id' => 'ModelField_'),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][field]', 'value' => '1', 'id' => 'ModelField', 'checked' => 'checked'))
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->checkbox('Model.field', array('checked' => false));
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][field]', 'value' => '0', 'id' => 'ModelField_'),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][field]', 'value' => '1', 'id' => 'ModelField'))
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data['Model']['field'] = 1;
+		$result = $this->Form->checkbox('Model.field', array('checked' => false));
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[Model][field]', 'value' => '0', 'id' => 'ModelField_'),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Model][field]', 'value' => '1', 'id' => 'ModelField'))
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * Test that disabling a checkbox also disables the hidden input so no value is submitted
+ *
+ * @return void
+ */
+	function testCheckboxDisabling() {
+		$result = $this->Form->checkbox('Account.show_name', array('disabled' => 'disabled'));
+		$expected = array(
+			array('input' => array('type' => 'hidden', 'name' => 'data[Account][show_name]', 'value' => '0', 'id' => 'AccountShowName_', 'disabled' => 'disabled')),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Account][show_name]', 'value' => '1', 'id' => 'AccountShowName', 'disabled' => 'disabled'))
+		);
+		$this->assertTags($result, $expected,true);
+	}
+
+/**
+ * Test that specifying false in the 'disabled' option will not disable either the hidden input or the checkbox input
+ *
+ * @return void
+ */
+	function testCheckboxHiddenDisabling() {
+		$result = $this->Form->checkbox('Account.show_name', array('disabled' => false));
+		$expected = array(
+			array('input' => array('type' => 'hidden', 'name' => 'data[Account][show_name]', 'value' => '0', 'id' => 'AccountShowName_')),
+			array('input' => array('type' => 'checkbox', 'name' => 'data[Account][show_name]', 'value' => '1', 'id' => 'AccountShowName'))
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * Test that the hidden input for checkboxes can be removed/omitted from the output.
+ *
+ * @return void
+ */
+	function testCheckboxHiddenFieldOmission() {
+		$result = $this->Form->input('UserForm.something', array(
+				'type' => 'checkbox',
+				'hiddenField' => false
+			)
+		);
+		$expected = array(
+			'div' => array('class' => 'input checkbox'),
+			array('input' => array(
+				'type' => 'checkbox', 'name' => 'data[UserForm][something]',
+				'value' => '1', 'id' => 'UserFormSomething'
+			)),
+			'label' => array('for' => 'UserFormSomething'),
+			'Something',
+			'/label',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testDateTime method
+ *
+ * Test generation of date/time select elements
+ *
+ * @access public
+ * @return void
+ */
+	function testDateTime() {
+		extract($this->dateRegex);
+
+		$result = $this->Form->dateTime('Contact.date', 'DMY', '12', null, array('empty' => false));
+		$now = strtotime('now');
+		$expected = array(
+			array('select' => array('name' => 'data[Contact][date][day]', 'id' => 'ContactDateDay')),
+			$daysRegex,
+			array('option' => array('value' => date('d', $now), 'selected' => 'selected')),
+			date('j', $now),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array('name' => 'data[Contact][date][month]', 'id' => 'ContactDateMonth')),
+			$monthsRegex,
+			array('option' => array('value' => date('m', $now), 'selected' => 'selected')),
+			date('F', $now),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array('name' => 'data[Contact][date][year]', 'id' => 'ContactDateYear')),
+			$yearsRegex,
+			array('option' => array('value' => date('Y', $now), 'selected' => 'selected')),
+			date('Y', $now),
+			'/option',
+			'*/select',
+			array('select' => array('name' => 'data[Contact][date][hour]', 'id' => 'ContactDateHour')),
+			$hoursRegex,
+			array('option' => array('value' => date('h', $now), 'selected' => 'selected')),
+			date('g', $now),
+			'/option',
+			'*/select',
+			':',
+			array('select' => array('name' => 'data[Contact][date][min]', 'id' => 'ContactDateMin')),
+			$minutesRegex,
+			array('option' => array('value' => date('i', $now), 'selected' => 'selected')),
+			date('i', $now),
+			'/option',
+			'*/select',
+			' ',
+			array('select' => array('name' => 'data[Contact][date][meridian]', 'id' => 'ContactDateMeridian')),
+			$meridianRegex,
+			array('option' => array('value' => date('a', $now), 'selected' => 'selected')),
+			date('a', $now),
+			'/option',
+			'*/select'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->dateTime('Contact.date', 'DMY', '12');
+		$expected = array(
+			array('select' => array('name' => 'data[Contact][date][day]', 'id' => 'ContactDateDay')),
+			$daysRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array('name' => 'data[Contact][date][month]', 'id' => 'ContactDateMonth')),
+			$monthsRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array('name' => 'data[Contact][date][year]', 'id' => 'ContactDateYear')),
+			$yearsRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			array('select' => array('name' => 'data[Contact][date][hour]', 'id' => 'ContactDateHour')),
+			$hoursRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			':',
+			array('select' => array('name' => 'data[Contact][date][min]', 'id' => 'ContactDateMin')),
+			$minutesRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			' ',
+			array('select' => array('name' => 'data[Contact][date][meridian]', 'id' => 'ContactDateMeridian')),
+			$meridianRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select'
+		);
+		$this->assertTags($result, $expected);
+		$this->assertNoPattern('/<option[^<>]+value=""[^<>]+selected="selected"[^>]*>/', $result);
+
+		$result = $this->Form->dateTime('Contact.date', 'DMY', '12', false);
+		$expected = array(
+			array('select' => array('name' => 'data[Contact][date][day]', 'id' => 'ContactDateDay')),
+			$daysRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array('name' => 'data[Contact][date][month]', 'id' => 'ContactDateMonth')),
+			$monthsRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array('name' => 'data[Contact][date][year]', 'id' => 'ContactDateYear')),
+			$yearsRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			array('select' => array('name' => 'data[Contact][date][hour]', 'id' => 'ContactDateHour')),
+			$hoursRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			':',
+			array('select' => array('name' => 'data[Contact][date][min]', 'id' => 'ContactDateMin')),
+			$minutesRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			' ',
+			array('select' => array('name' => 'data[Contact][date][meridian]', 'id' => 'ContactDateMeridian')),
+			$meridianRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select'
+		);
+		$this->assertTags($result, $expected);
+		$this->assertNoPattern('/<option[^<>]+value=""[^<>]+selected="selected"[^>]*>/', $result);
+
+		$result = $this->Form->dateTime('Contact.date', 'DMY', '12', '');
+		$expected = array(
+			array('select' => array('name' => 'data[Contact][date][day]', 'id' => 'ContactDateDay')),
+			$daysRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array('name' => 'data[Contact][date][month]', 'id' => 'ContactDateMonth')),
+			$monthsRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array('name' => 'data[Contact][date][year]', 'id' => 'ContactDateYear')),
+			$yearsRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			array('select' => array('name' => 'data[Contact][date][hour]', 'id' => 'ContactDateHour')),
+			$hoursRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			':',
+			array('select' => array('name' => 'data[Contact][date][min]', 'id' => 'ContactDateMin')),
+			$minutesRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			' ',
+			array('select' => array('name' => 'data[Contact][date][meridian]', 'id' => 'ContactDateMeridian')),
+			$meridianRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select'
+		);
+		$this->assertTags($result, $expected);
+		$this->assertNoPattern('/<option[^<>]+value=""[^<>]+selected="selected"[^>]*>/', $result);
+
+		$result = $this->Form->dateTime('Contact.date', 'DMY', '12', '', array('interval' => 5));
+		$expected = array(
+			array('select' => array('name' => 'data[Contact][date][day]', 'id' => 'ContactDateDay')),
+			$daysRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array('name' => 'data[Contact][date][month]', 'id' => 'ContactDateMonth')),
+			$monthsRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array('name' => 'data[Contact][date][year]', 'id' => 'ContactDateYear')),
+			$yearsRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			array('select' => array('name' => 'data[Contact][date][hour]', 'id' => 'ContactDateHour')),
+			$hoursRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			':',
+			array('select' => array('name' => 'data[Contact][date][min]', 'id' => 'ContactDateMin')),
+			$minutesRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => '00')),
+			'00',
+			'/option',
+			array('option' => array('value' => '05')),
+			'05',
+			'/option',
+			array('option' => array('value' => '10')),
+			'10',
+			'/option',
+			'*/select',
+			' ',
+			array('select' => array('name' => 'data[Contact][date][meridian]', 'id' => 'ContactDateMeridian')),
+			$meridianRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select'
+		);
+		$this->assertTags($result, $expected);
+		$this->assertNoPattern('/<option[^<>]+value=""[^<>]+selected="selected"[^>]*>/', $result);
+
+		$result = $this->Form->dateTime('Contact.date', 'DMY', '12', '', array('minuteInterval' => 5));
+		$expected = array(
+			array('select' => array('name' => 'data[Contact][date][day]', 'id' => 'ContactDateDay')),
+			$daysRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array('name' => 'data[Contact][date][month]', 'id' => 'ContactDateMonth')),
+			$monthsRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array('name' => 'data[Contact][date][year]', 'id' => 'ContactDateYear')),
+			$yearsRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			array('select' => array('name' => 'data[Contact][date][hour]', 'id' => 'ContactDateHour')),
+			$hoursRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			':',
+			array('select' => array('name' => 'data[Contact][date][min]', 'id' => 'ContactDateMin')),
+			$minutesRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => '00')),
+			'00',
+			'/option',
+			array('option' => array('value' => '05')),
+			'05',
+			'/option',
+			array('option' => array('value' => '10')),
+			'10',
+			'/option',
+			'*/select',
+			' ',
+			array('select' => array('name' => 'data[Contact][date][meridian]', 'id' => 'ContactDateMeridian')),
+			$meridianRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select'
+		);
+		$this->assertTags($result, $expected);
+		$this->assertNoPattern('/<option[^<>]+value=""[^<>]+selected="selected"[^>]*>/', $result);
+
+		$this->Form->data['Contact']['data'] = null;
+		$result = $this->Form->dateTime('Contact.date', 'DMY', '12');
+		$expected = array(
+			array('select' => array('name' => 'data[Contact][date][day]', 'id' => 'ContactDateDay')),
+			$daysRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array('name' => 'data[Contact][date][month]', 'id' => 'ContactDateMonth')),
+			$monthsRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array('name' => 'data[Contact][date][year]', 'id' => 'ContactDateYear')),
+			$yearsRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			array('select' => array('name' => 'data[Contact][date][hour]', 'id' => 'ContactDateHour')),
+			$hoursRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			':',
+			array('select' => array('name' => 'data[Contact][date][min]', 'id' => 'ContactDateMin')),
+			$minutesRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			' ',
+			array('select' => array('name' => 'data[Contact][date][meridian]', 'id' => 'ContactDateMeridian')),
+			$meridianRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select'
+		);
+		$this->assertTags($result, $expected);
+		$this->assertNoPattern('/<option[^<>]+value=""[^<>]+selected="selected"[^>]*>/', $result);
+
+		$this->Form->data['Model']['field'] = date('Y') . '-01-01 00:00:00';
+		$now = strtotime($this->Form->data['Model']['field']);
+		$result = $this->Form->dateTime('Model.field', 'DMY', '12', null, array('empty' => false));
+		$expected = array(
+			array('select' => array('name' => 'data[Model][field][day]', 'id' => 'ModelFieldDay')),
+			$daysRegex,
+			array('option' => array('value' => date('d', $now), 'selected' => 'selected')),
+			date('j', $now),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array('name' => 'data[Model][field][month]', 'id' => 'ModelFieldMonth')),
+			$monthsRegex,
+			array('option' => array('value' => date('m', $now), 'selected' => 'selected')),
+			date('F', $now),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array('name' => 'data[Model][field][year]', 'id' => 'ModelFieldYear')),
+			$yearsRegex,
+			array('option' => array('value' => date('Y', $now), 'selected' => 'selected')),
+			date('Y', $now),
+			'/option',
+			'*/select',
+			array('select' => array('name' => 'data[Model][field][hour]', 'id' => 'ModelFieldHour')),
+			$hoursRegex,
+			array('option' => array('value' => date('h', $now), 'selected' => 'selected')),
+			date('g', $now),
+			'/option',
+			'*/select',
+			':',
+			array('select' => array('name' => 'data[Model][field][min]', 'id' => 'ModelFieldMin')),
+			$minutesRegex,
+			array('option' => array('value' => date('i', $now), 'selected' => 'selected')),
+			date('i', $now),
+			'/option',
+			'*/select',
+			' ',
+			array('select' => array('name' => 'data[Model][field][meridian]', 'id' => 'ModelFieldMeridian')),
+			$meridianRegex,
+			array('option' => array('value' => date('a', $now), 'selected' => 'selected')),
+			date('a', $now),
+			'/option',
+			'*/select'
+		);
+		$this->assertTags($result, $expected);
+
+		$selected = strtotime('2008-10-26 12:33:00');
+		$result = $this->Form->dateTime('Model.field', 'DMY', '12', $selected);
+		$this->assertPattern('/<option[^<>]+value="2008"[^<>]+selected="selected"[^>]*>2008<\/option>/', $result);
+		$this->assertPattern('/<option[^<>]+value="10"[^<>]+selected="selected"[^>]*>October<\/option>/', $result);
+		$this->assertPattern('/<option[^<>]+value="26"[^<>]+selected="selected"[^>]*>26<\/option>/', $result);
+		$this->assertPattern('/<option[^<>]+value="12"[^<>]+selected="selected"[^>]*>12<\/option>/', $result);
+		$this->assertPattern('/<option[^<>]+value="33"[^<>]+selected="selected"[^>]*>33<\/option>/', $result);
+		$this->assertPattern('/<option[^<>]+value="pm"[^<>]+selected="selected"[^>]*>pm<\/option>/', $result);
+
+		$this->Form->create('Contact');
+		$result = $this->Form->input('published');
+		$now = strtotime('now');
+		$expected = array(
+			'div' => array('class' => 'input date'),
+			'label' => array('for' => 'ContactPublishedMonth'),
+			'Published',
+			'/label',
+			array('select' => array('name' => 'data[Contact][published][month]', 'id' => 'ContactPublishedMonth')),
+			$monthsRegex,
+			array('option' => array('value' => date('m', $now), 'selected' => 'selected')),
+			date('F', $now),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array('name' => 'data[Contact][published][day]', 'id' => 'ContactPublishedDay')),
+			$daysRegex,
+			array('option' => array('value' => date('d', $now), 'selected' => 'selected')),
+			date('j', $now),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array('name' => 'data[Contact][published][year]', 'id' => 'ContactPublishedYear')),
+			$yearsRegex,
+			array('option' => array('value' => date('Y', $now), 'selected' => 'selected')),
+			date('Y', $now),
+			'/option',
+			'*/select',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('published2', array('type' => 'date'));
+		$now = strtotime('now');
+		$expected = array(
+			'div' => array('class' => 'input date'),
+			'label' => array('for' => 'ContactPublished2Month'),
+			'Published2',
+			'/label',
+			array('select' => array('name' => 'data[Contact][published2][month]', 'id' => 'ContactPublished2Month')),
+			$monthsRegex,
+			array('option' => array('value' => date('m', $now), 'selected' => 'selected')),
+			date('F', $now),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array('name' => 'data[Contact][published2][day]', 'id' => 'ContactPublished2Day')),
+			$daysRegex,
+			array('option' => array('value' => date('d', $now), 'selected' => 'selected')),
+			date('j', $now),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array('name' => 'data[Contact][published2][year]', 'id' => 'ContactPublished2Year')),
+			$yearsRegex,
+			array('option' => array('value' => date('Y', $now), 'selected' => 'selected')),
+			date('Y', $now),
+			'/option',
+			'*/select',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('ContactTag');
+		$expected = array(
+			'div' => array('class' => 'input select'),
+			'label' => array('for' => 'ContactTagContactTag'),
+			'Contact Tag',
+			'/label',
+			array('input' => array('type' => 'hidden', 'name' => 'data[ContactTag][ContactTag]', 'value' => '', 'id' => 'ContactTagContactTag_')),
+			array('select' => array('name' => 'data[ContactTag][ContactTag][]', 'multiple' => 'multiple', 'id' => 'ContactTagContactTag')),
+			'/select',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->create('Contact');
+		$result = $this->Form->input('published', array('monthNames' => false));
+		$now = strtotime('now');
+		$expected = array(
+			'div' => array('class' => 'input date'),
+			'label' => array('for' => 'ContactPublishedMonth'),
+			'Published',
+			'/label',
+			array('select' => array('name' => 'data[Contact][published][month]', 'id' => 'ContactPublishedMonth')),
+			'preg:/(?:<option value="([\d])+">[\d]+<\/option>[\r\n]*)*/',
+			array('option' => array('value' => date('m', $now), 'selected' => 'selected')),
+			date('m', $now),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array('name' => 'data[Contact][published][day]', 'id' => 'ContactPublishedDay')),
+			$daysRegex,
+			array('option' => array('value' => date('d', $now), 'selected' => 'selected')),
+			date('j', $now),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array('name' => 'data[Contact][published][year]', 'id' => 'ContactPublishedYear')),
+			$yearsRegex,
+			array('option' => array('value' => date('Y', $now), 'selected' => 'selected')),
+			date('Y', $now),
+			'/option',
+			'*/select',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('published', array('type' => 'time'));
+		$now = strtotime('now');
+		$expected = array(
+			'div' => array('class' => 'input time'),
+			'label' => array('for' => 'ContactPublishedHour'),
+			'Published',
+			'/label',
+			array('select' => array('name' => 'data[Contact][published][hour]', 'id' => 'ContactPublishedHour')),
+			'preg:/(?:<option value="([\d])+">[\d]+<\/option>[\r\n]*)*/',
+			array('option' => array('value' => date('h', $now), 'selected' => 'selected')),
+			date('g', $now),
+			'/option',
+			'*/select',
+			':',
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('published', array(
+			'timeFormat' => 24,
+			'interval' => 5,
+			'selected' => strtotime('2009-09-03 13:37:00'),
+			'type' => 'datetime'
+		));
+		$this->assertPattern('/<option[^<>]+value="2009"[^<>]+selected="selected"[^>]*>2009<\/option>/', $result);
+		$this->assertPattern('/<option[^<>]+value="09"[^<>]+selected="selected"[^>]*>September<\/option>/', $result);
+		$this->assertPattern('/<option[^<>]+value="03"[^<>]+selected="selected"[^>]*>3<\/option>/', $result);
+		$this->assertPattern('/<option[^<>]+value="13"[^<>]+selected="selected"[^>]*>13<\/option>/', $result);
+		$this->assertPattern('/<option[^<>]+value="35"[^<>]+selected="selected"[^>]*>35<\/option>/', $result);
+
+		$this->assertNoErrors();
+		$this->Form->data['Contact'] = array(
+			'date' => array(
+				'day' => '',
+				'month' => '',
+				'year' => '',
+				'hour' => '',
+				'min' => '',
+				'meridian' => ''
+			)
+		);
+		$result = $this->Form->dateTime('Contact.date', 'DMY', '12', null, array('empty' => false));
+	}
+
+/**
+ * test that datetime() and default values work.
+ *
+ * @return void
+ */
+	function testDatetimeWithDefault() {
+		$result = $this->Form->dateTime('Contact.updated', 'DMY', '12', null, array('value' => '2009-06-01 11:15:30'));
+		$this->assertPattern('/<option[^<>]+value="2009"[^<>]+selected="selected"[^>]*>2009<\/option>/', $result);
+		$this->assertPattern('/<option[^<>]+value="01"[^<>]+selected="selected"[^>]*>1<\/option>/', $result);
+		$this->assertPattern('/<option[^<>]+value="06"[^<>]+selected="selected"[^>]*>June<\/option>/', $result);
+
+		$result = $this->Form->dateTime('Contact.updated', 'DMY', '12', null, array(
+			'default' => '2009-06-01 11:15:30'
+		));
+		$this->assertPattern('/<option[^<>]+value="2009"[^<>]+selected="selected"[^>]*>2009<\/option>/', $result);
+		$this->assertPattern('/<option[^<>]+value="01"[^<>]+selected="selected"[^>]*>1<\/option>/', $result);
+		$this->assertPattern('/<option[^<>]+value="06"[^<>]+selected="selected"[^>]*>June<\/option>/', $result);
+	}
+
+/**
+ * test that bogus non-date time data doesn't cause errors.
+ *
+ * @return void
+ */
+	function testDateTimeWithBogusData() {
+		$result = $this->Form->dateTime('Contact.updated', 'DMY', '12', 'CURRENT_TIMESTAMP');
+		$this->assertNoPattern('/selected="selected">\d/', $result);
+	}
+
+/**
+ * testFormDateTimeMulti method
+ *
+ * test multiple datetime element generation
+ *
+ * @access public
+ * @return void
+ */
+	function testFormDateTimeMulti() {
+		extract($this->dateRegex);
+
+		$result = $this->Form->dateTime('Contact.1.updated');
+		$expected = array(
+			array('select' => array('name' => 'data[Contact][1][updated][day]', 'id' => 'Contact1UpdatedDay')),
+			$daysRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array('name' => 'data[Contact][1][updated][month]', 'id' => 'Contact1UpdatedMonth')),
+			$monthsRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array('name' => 'data[Contact][1][updated][year]', 'id' => 'Contact1UpdatedYear')),
+			$yearsRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			array('select' => array('name' => 'data[Contact][1][updated][hour]', 'id' => 'Contact1UpdatedHour')),
+			$hoursRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			':',
+			array('select' => array('name' => 'data[Contact][1][updated][min]', 'id' => 'Contact1UpdatedMin')),
+			$minutesRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			' ',
+			array('select' => array('name' => 'data[Contact][1][updated][meridian]', 'id' => 'Contact1UpdatedMeridian')),
+			$meridianRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->dateTime('Contact.2.updated');
+		$expected = array(
+			array('select' => array('name' => 'data[Contact][2][updated][day]', 'id' => 'Contact2UpdatedDay')),
+			$daysRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array('name' => 'data[Contact][2][updated][month]', 'id' => 'Contact2UpdatedMonth')),
+			$monthsRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array('name' => 'data[Contact][2][updated][year]', 'id' => 'Contact2UpdatedYear')),
+			$yearsRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			array('select' => array('name' => 'data[Contact][2][updated][hour]', 'id' => 'Contact2UpdatedHour')),
+			$hoursRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			':',
+			array('select' => array('name' => 'data[Contact][2][updated][min]', 'id' => 'Contact2UpdatedMin')),
+			$minutesRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select',
+			' ',
+			array('select' => array('name' => 'data[Contact][2][updated][meridian]', 'id' => 'Contact2UpdatedMeridian')),
+			$meridianRegex,
+			array('option' => array('value' => '')),
+			'/option',
+			'*/select'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testMonth method
+ *
+ * @access public
+ * @return void
+ */
+	function testMonth() {
+		$result = $this->Form->month('Model.field');
+		$expected = array(
+			array('select' => array('name' => 'data[Model][field][month]', 'id' => 'ModelFieldMonth')),
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => '01')),
+			date('F', strtotime('2008-01-01 00:00:00')),
+			'/option',
+			array('option' => array('value' => '02')),
+			date('F', strtotime('2008-02-01 00:00:00')),
+			'/option',
+			'*/select',
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->month('Model.field', null, array('empty' => true));
+		$expected = array(
+			array('select' => array('name' => 'data[Model][field][month]', 'id' => 'ModelFieldMonth')),
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => '01')),
+			date('F', strtotime('2008-01-01 00:00:00')),
+			'/option',
+			array('option' => array('value' => '02')),
+			date('F', strtotime('2008-02-01 00:00:00')),
+			'/option',
+			'*/select',
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->month('Model.field', null, array('monthNames' => false));
+		$expected = array(
+			array('select' => array('name' => 'data[Model][field][month]', 'id' => 'ModelFieldMonth')),
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => '01')),
+			'01',
+			'/option',
+			array('option' => array('value' => '02')),
+			'02',
+			'/option',
+			'*/select',
+		);
+		$this->assertTags($result, $expected);
+
+		$monthNames = array(
+			'01' => 'Jan', '02' => 'Feb', '03' => 'Mar', '04' => 'Apr', '05' => 'May', '06' => 'Jun',
+			'07' => 'Jul', '08' => 'Aug', '09' => 'Sep', '10' => 'Oct', '11' => 'Nov', '12' => 'Dec');
+		$result = $this->Form->month('Model.field', null, array('monthNames' => $monthNames));
+		$expected = array(
+			array('select' => array('name' => 'data[Model][field][month]', 'id' => 'ModelFieldMonth')),
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => '01')),
+			'Jan',
+			'/option',
+			array('option' => array('value' => '02')),
+			'Feb',
+			'/option',
+			'*/select',
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testDay method
+ *
+ * @access public
+ * @return void
+ */
+	function testDay() {
+		extract($this->dateRegex);
+
+		$result = $this->Form->day('Model.field', false);
+		$expected = array(
+			array('select' => array('name' => 'data[Model][field][day]', 'id' => 'ModelFieldDay')),
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => '01')),
+			'1',
+			'/option',
+			array('option' => array('value' => '02')),
+			'2',
+			'/option',
+			$daysRegex,
+			'/select',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data['Model']['field'] = '2006-10-10 23:12:32';
+		$result = $this->Form->day('Model.field');
+		$expected = array(
+			array('select' => array('name' => 'data[Model][field][day]', 'id' => 'ModelFieldDay')),
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => '01')),
+			'1',
+			'/option',
+			array('option' => array('value' => '02')),
+			'2',
+			'/option',
+			$daysRegex,
+			array('option' => array('value' => '10', 'selected' => 'selected')),
+			'10',
+			'/option',
+			$daysRegex,
+			'/select',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data['Model']['field'] = '';
+		$result = $this->Form->day('Model.field', '10');
+		$expected = array(
+			array('select' => array('name' => 'data[Model][field][day]', 'id' => 'ModelFieldDay')),
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => '01')),
+			'1',
+			'/option',
+			array('option' => array('value' => '02')),
+			'2',
+			'/option',
+			$daysRegex,
+			array('option' => array('value' => '10', 'selected' => 'selected')),
+			'10',
+			'/option',
+			$daysRegex,
+			'/select',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data['Model']['field'] = '2006-10-10 23:12:32';
+		$result = $this->Form->day('Model.field', true);
+		$expected = array(
+			array('select' => array('name' => 'data[Model][field][day]', 'id' => 'ModelFieldDay')),
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => '01')),
+			'1',
+			'/option',
+			array('option' => array('value' => '02')),
+			'2',
+			'/option',
+			$daysRegex,
+			array('option' => array('value' => '10', 'selected' => 'selected')),
+			'10',
+			'/option',
+			$daysRegex,
+			'/select',
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testMinute method
+ *
+ * @access public
+ * @return void
+ */
+	function testMinute() {
+		extract($this->dateRegex);
+
+		$result = $this->Form->minute('Model.field');
+		$expected = array(
+			array('select' => array('name' => 'data[Model][field][min]', 'id' => 'ModelFieldMin')),
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => '00')),
+			'00',
+			'/option',
+			array('option' => array('value' => '01')),
+			'01',
+			'/option',
+			array('option' => array('value' => '02')),
+			'02',
+			'/option',
+			$minutesRegex,
+			'/select',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data['Model']['field'] = '2006-10-10 00:12:32';
+		$result = $this->Form->minute('Model.field');
+		$expected = array(
+			array('select' => array('name' => 'data[Model][field][min]', 'id' => 'ModelFieldMin')),
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => '00')),
+			'00',
+			'/option',
+			array('option' => array('value' => '01')),
+			'01',
+			'/option',
+			array('option' => array('value' => '02')),
+			'02',
+			'/option',
+			$minutesRegex,
+			array('option' => array('value' => '12', 'selected' => 'selected')),
+			'12',
+			'/option',
+			$minutesRegex,
+			'/select',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data['Model']['field'] = '';
+		$result = $this->Form->minute('Model.field', null, array('interval' => 5));
+		$expected = array(
+			array('select' => array('name' => 'data[Model][field][min]', 'id' => 'ModelFieldMin')),
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => '00')),
+			'00',
+			'/option',
+			array('option' => array('value' => '05')),
+			'05',
+			'/option',
+			array('option' => array('value' => '10')),
+			'10',
+			'/option',
+			$minutesRegex,
+			'/select',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data['Model']['field'] = '2006-10-10 00:10:32';
+		$result = $this->Form->minute('Model.field', null, array('interval' => 5));
+		$expected = array(
+			array('select' => array('name' => 'data[Model][field][min]', 'id' => 'ModelFieldMin')),
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => '00')),
+			'00',
+			'/option',
+			array('option' => array('value' => '05')),
+			'05',
+			'/option',
+			array('option' => array('value' => '10', 'selected' => 'selected')),
+			'10',
+			'/option',
+			$minutesRegex,
+			'/select',
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testHour method
+ *
+ * @access public
+ * @return void
+ */
+	function testHour() {
+		extract($this->dateRegex);
+
+		$result = $this->Form->hour('Model.field', false);
+		$expected = array(
+			array('select' => array('name' => 'data[Model][field][hour]', 'id' => 'ModelFieldHour')),
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => '01')),
+			'1',
+			'/option',
+			array('option' => array('value' => '02')),
+			'2',
+			'/option',
+			$hoursRegex,
+			'/select',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data['Model']['field'] = '2006-10-10 00:12:32';
+		$result = $this->Form->hour('Model.field', false);
+		$expected = array(
+			array('select' => array('name' => 'data[Model][field][hour]', 'id' => 'ModelFieldHour')),
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => '01')),
+			'1',
+			'/option',
+			array('option' => array('value' => '02')),
+			'2',
+			'/option',
+			$hoursRegex,
+			array('option' => array('value' => '12', 'selected' => 'selected')),
+			'12',
+			'/option',
+			'/select',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data['Model']['field'] = '';
+		$result = $this->Form->hour('Model.field', true, '23');
+		$expected = array(
+			array('select' => array('name' => 'data[Model][field][hour]', 'id' => 'ModelFieldHour')),
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => '00')),
+			'0',
+			'/option',
+			array('option' => array('value' => '01')),
+			'1',
+			'/option',
+			array('option' => array('value' => '02')),
+			'2',
+			'/option',
+			$hoursRegex,
+			array('option' => array('value' => '23', 'selected' => 'selected')),
+			'23',
+			'/option',
+			'/select',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data['Model']['field'] = '2006-10-10 00:12:32';
+		$result = $this->Form->hour('Model.field', true);
+		$expected = array(
+			array('select' => array('name' => 'data[Model][field][hour]', 'id' => 'ModelFieldHour')),
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => '00', 'selected' => 'selected')),
+			'0',
+			'/option',
+			array('option' => array('value' => '01')),
+			'1',
+			'/option',
+			array('option' => array('value' => '02')),
+			'2',
+			'/option',
+			$hoursRegex,
+			'/select',
+		);
+		$this->assertTags($result, $expected);
+
+		unset($this->Form->data['Model']['field']);
+		$result = $this->Form->hour('Model.field', true, 'now');
+		$thisHour = date('H');
+		$optValue = date('G');
+		$this->assertPattern('/<option value="' . $thisHour . '" selected="selected">'. $optValue .'<\/option>/', $result);
+	}
+
+/**
+ * testYear method
+ *
+ * @access public
+ * @return void
+ */
+	function testYear() {
+		$result = $this->Form->year('Model.field', 2006, 2007);
+		$expected = array(
+			array('select' => array('name' => 'data[Model][field][year]', 'id' => 'ModelFieldYear')),
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => '2007')),
+			'2007',
+			'/option',
+			array('option' => array('value' => '2006')),
+			'2006',
+			'/option',
+			'/select',
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->year('Model.field', 2006, 2007, null, array('orderYear' => 'asc'));
+		$expected = array(
+			array('select' => array('name' => 'data[Model][field][year]', 'id' => 'ModelFieldYear')),
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => '2006')),
+			'2006',
+			'/option',
+			array('option' => array('value' => '2007')),
+			'2007',
+			'/option',
+			'/select',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->data['Contact']['published'] = '';
+		$result = $this->Form->year('Contact.published', 2006, 2007, null, array('class' => 'year'));
+		$expected = array(
+			array('select' => array('name' => 'data[Contact][published][year]', 'id' => 'ContactPublishedYear', 'class' => 'year')),
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => '2007')),
+			'2007',
+			'/option',
+			array('option' => array('value' => '2006')),
+			'2006',
+			'/option',
+			'/select',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data['Contact']['published'] = '2006-10-10';
+		$result = $this->Form->year('Contact.published', 2006, 2007, null, array('empty' => false));
+		$expected = array(
+			array('select' => array('name' => 'data[Contact][published][year]', 'id' => 'ContactPublishedYear')),
+			array('option' => array('value' => '2007')),
+			'2007',
+			'/option',
+			array('option' => array('value' => '2006', 'selected' => 'selected')),
+			'2006',
+			'/option',
+			'/select',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data['Contact']['published'] = '';
+		$result = $this->Form->year('Contact.published', 2006, 2007, false);
+		$expected = array(
+			array('select' => array('name' => 'data[Contact][published][year]', 'id' => 'ContactPublishedYear')),
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => '2007')),
+			'2007',
+			'/option',
+			array('option' => array('value' => '2006')),
+			'2006',
+			'/option',
+			'/select',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data['Contact']['published'] = '2006-10-10';
+		$result = $this->Form->year('Contact.published', 2006, 2007, false, array('empty' => false));
+		$expected = array(
+			array('select' => array('name' => 'data[Contact][published][year]', 'id' => 'ContactPublishedYear')),
+			array('option' => array('value' => '2007')),
+			'2007',
+			'/option',
+			array('option' => array('value' => '2006', 'selected' => 'selected')),
+			'2006',
+			'/option',
+			'/select',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data['Contact']['published'] = '';
+		$result = $this->Form->year('Contact.published', 2006, 2007, 2007);
+		$expected = array(
+			array('select' => array('name' => 'data[Contact][published][year]', 'id' => 'ContactPublishedYear')),
+			array('option' => array('value' => '')),
+			'/option',
+			array('option' => array('value' => '2007', 'selected' => 'selected')),
+			'2007',
+			'/option',
+			array('option' => array('value' => '2006')),
+			'2006',
+			'/option',
+			'/select',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data['Contact']['published'] = '2006-10-10';
+		$result = $this->Form->year('Contact.published', 2006, 2007, 2007, array('empty' => false));
+		$expected = array(
+			array('select' => array('name' => 'data[Contact][published][year]', 'id' => 'ContactPublishedYear')),
+			array('option' => array('value' => '2007', 'selected' => 'selected')),
+			'2007',
+			'/option',
+			array('option' => array('value' => '2006')),
+			'2006',
+			'/option',
+			'/select',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data['Contact']['published'] = '';
+		$result = $this->Form->year('Contact.published', 2006, 2008, 2007, array('empty' => false));
+		$expected = array(
+			array('select' => array('name' => 'data[Contact][published][year]', 'id' => 'ContactPublishedYear')),
+			array('option' => array('value' => '2008')),
+			'2008',
+			'/option',
+			array('option' => array('value' => '2007', 'selected' => 'selected')),
+			'2007',
+			'/option',
+			array('option' => array('value' => '2006')),
+			'2006',
+			'/option',
+			'/select',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data['Contact']['published'] = '2006-10-10';
+		$result = $this->Form->year('Contact.published', 2006, 2008, null, array('empty' => false));
+		$expected = array(
+			array('select' => array('name' => 'data[Contact][published][year]', 'id' => 'ContactPublishedYear')),
+			array('option' => array('value' => '2008')),
+			'2008',
+			'/option',
+			array('option' => array('value' => '2007')),
+			'2007',
+			'/option',
+			array('option' => array('value' => '2006', 'selected' => 'selected')),
+			'2006',
+			'/option',
+			'/select',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data = array();
+		$this->Form->create('Contact');
+		$result = $this->Form->year('published', 2006, 2008, null, array('empty' => false));
+		$expected = array(
+			array('select' => array('name' => 'data[Contact][published][year]', 'id' => 'ContactPublishedYear')),
+			array('option' => array('value' => '2008')),
+			'2008',
+			'/option',
+			array('option' => array('value' => '2007')),
+			'2007',
+			'/option',
+			array('option' => array('value' => '2006')),
+			'2006',
+			'/option',
+			'/select',
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testTextArea method
+ *
+ * @access public
+ * @return void
+ */
+	function testTextArea() {
+		$this->Form->data = array('Model' => array('field' => 'some test data'));
+		$result = $this->Form->textarea('Model.field');
+		$expected = array(
+			'textarea' => array('name' => 'data[Model][field]', 'id' => 'ModelField'),
+			'some test data',
+			'/textarea',
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->textarea('Model.tmp');
+		$expected = array(
+			'textarea' => array('name' => 'data[Model][tmp]', 'id' => 'ModelTmp'),
+			'/textarea',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data = array('Model' => array('field' => 'some <strong>test</strong> data with <a href="#">HTML</a> chars'));
+		$result = $this->Form->textarea('Model.field');
+		$expected = array(
+			'textarea' => array('name' => 'data[Model][field]', 'id' => 'ModelField'),
+			htmlentities('some <strong>test</strong> data with <a href="#">HTML</a> chars'),
+			'/textarea',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data = array('Model' => array('field' => 'some <strong>test</strong> data with <a href="#">HTML</a> chars'));
+		$result = $this->Form->textarea('Model.field', array('escape' => false));
+		$expected = array(
+			'textarea' => array('name' => 'data[Model][field]', 'id' => 'ModelField'),
+			'some <strong>test</strong> data with <a href="#">HTML</a> chars',
+			'/textarea',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data['Model']['0']['OtherModel']['field'] = null;
+		$result = $this->Form->textarea('Model.0.OtherModel.field');
+		$expected = array(
+			'textarea' => array('name' => 'data[Model][0][OtherModel][field]', 'id' => 'Model0OtherModelField'),
+			'/textarea'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testTextAreaWithStupidCharacters method
+ *
+ * test text area with non-ascii characters
+ *
+ * @access public
+ * @return void
+ */
+	function testTextAreaWithStupidCharacters() {
+		$result = $this->Form->input('Post.content', array(
+			'label' => 'Current Text', 'value' => "GREAT®", 'rows' => '15', 'cols' => '75'
+		));
+		$expected = array(
+			'div' => array('class' => 'input text'),
+				'label' => array('for' => 'PostContent'),
+					'Current Text',
+				'/label',
+				'textarea' => array('name' => 'data[Post][content]', 'id' => 'PostContent', 'rows' => '15', 'cols' => '75'),
+				'GREAT®',
+				'/textarea',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testHiddenField method
+ *
+ * @access public
+ * @return void
+ */
+	function testHiddenField() {
+		$this->Form->validationErrors['Model']['field'] = 1;
+		$this->Form->data['Model']['field'] = 'test';
+		$result = $this->Form->hidden('Model.field', array('id' => 'theID'));
+		$this->assertTags($result, array('input' => array('type' => 'hidden', 'name' => 'data[Model][field]', 'id' => 'theID', 'value' => 'test')));
+	}
+
+/**
+ * testFileUploadField method
+ *
+ * @access public
+ * @return void
+ */
+	function testFileUploadField() {
+		$result = $this->Form->file('Model.upload');
+		$this->assertTags($result, array('input' => array('type' => 'file', 'name' => 'data[Model][upload]', 'id' => 'ModelUpload')));
+
+		$this->Form->data['Model.upload'] = array("name" => "", "type" => "", "tmp_name" => "", "error" => 4, "size" => 0);
+		$result = $this->Form->input('Model.upload', array('type' => 'file'));
+		$expected = array(
+			'div' => array('class' => 'input file'),
+			'label' => array('for' => 'ModelUpload'),
+			'Upload',
+			'/label',
+			'input' => array('type' => 'file', 'name' => 'data[Model][upload]', 'id' => 'ModelUpload'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test File upload input on a model not used in create();
+ *
+ * @return void
+ */
+	function testFileUploadOnOtherModel() {
+		ClassRegistry::removeObject('view');
+		$controller =& new Controller();
+		$controller->name = 'ValidateUsers';
+		$controller->uses = array('ValidateUser');
+		$controller->constructClasses();
+		$view =& new View($controller, true);
+
+		$this->Form->create('ValidateUser', array('type' => 'file'));
+		$result = $this->Form->file('ValidateProfile.city');
+		$expected = array(
+			'input' => array('type' => 'file', 'name' => 'data[ValidateProfile][city]', 'id' => 'ValidateProfileCity')
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testButton method
+ *
+ * @access public
+ * @return void
+ */
+	function testButton() {
+		$result = $this->Form->button('Hi');
+		$this->assertTags($result, array('button' => array('type' => 'submit'), 'Hi', '/button'));
+
+		$result = $this->Form->button('Clear Form >', array('type' => 'reset'));
+		$this->assertTags($result, array('button' => array('type' => 'reset'), 'Clear Form >', '/button'));
+
+		$result = $this->Form->button('Clear Form >', array('type' => 'reset', 'id' => 'clearForm'));
+		$this->assertTags($result, array('button' => array('type' => 'reset', 'id' => 'clearForm'), 'Clear Form >', '/button'));
+
+		$result = $this->Form->button('<Clear Form>', array('type' => 'reset', 'escape' => true));
+		$this->assertTags($result, array('button' => array('type' => 'reset'), '&lt;Clear Form&gt;', '/button'));
+
+		$result = $this->Form->button('Upload Text', array('onClick' => "$('#postAddForm').ajaxSubmit({target: '#postTextUpload', url: '/posts/text'});return false;'", 'escape' => false));
+		$this->assertNoPattern('/\&039/', $result);
+	}
+
+/**
+ * testSubmitButton method
+ *
+ * @access public
+ * @return void
+ */
+	function testSubmitButton() {
+		$result = $this->Form->submit('');
+		$expected = array(
+			'div' => array('class' => 'submit'),
+			'input' => array('type' => 'submit', 'value' => ''),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->submit('Test Submit');
+		$expected = array(
+			'div' => array('class' => 'submit'),
+			'input' => array('type' => 'submit', 'value' => 'Test Submit'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->submit('Test Submit', array('div' => array('tag' => 'span')));
+		$expected = array(
+			'span' => array('class' => 'submit'),
+			'input' => array('type' => 'submit', 'value' => 'Test Submit'),
+			'/span'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->submit('Test Submit', array('class' => 'save', 'div' => false));
+		$expected = array('input' => array('type' => 'submit', 'value' => 'Test Submit', 'class' => 'save'));
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->submit('Test Submit', array('div' => array('id' => 'SaveButton')));
+		$expected = array(
+			'div' => array('class' => 'submit', 'id' => 'SaveButton'),
+			'input' => array('type' => 'submit', 'value' => 'Test Submit'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->submit('Next >');
+		$expected = array(
+			'div' => array('class' => 'submit'),
+			'input' => array('type' => 'submit', 'value' => 'Next &gt;'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->submit('Next >', array('escape' => false));
+		$expected = array(
+			'div' => array('class' => 'submit'),
+			'input' => array('type' => 'submit', 'value' => 'Next >'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->submit('Reset!', array('type' => 'reset'));
+		$expected = array(
+			'div' => array('class' => 'submit'),
+			'input' => array('type' => 'reset', 'value' => 'Reset!'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$before = '--before--';
+		$after = '--after--';
+		$result = $this->Form->submit('Test', array('before' => $before));
+		$expected = array(
+			'div' => array('class' => 'submit'),
+			'--before--',
+			'input' => array('type' => 'submit', 'value' => 'Test'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->submit('Test', array('after' => $after));
+		$expected = array(
+			'div' => array('class' => 'submit'),
+			'input' => array('type' => 'submit', 'value' => 'Test'),
+			'--after--',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->submit('Test', array('before' => $before, 'after' => $after));
+		$expected = array(
+			'div' => array('class' => 'submit'),
+			'--before--',
+			'input' => array('type' => 'submit', 'value' => 'Test'),
+			'--after--',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test image submit types.
+ *
+ * @return void
+ */
+	function testSubmitImage() {
+		$result = $this->Form->submit('http://example.com/cake.power.gif');
+		$expected = array(
+			'div' => array('class' => 'submit'),
+			'input' => array('type' => 'image', 'src' => 'http://example.com/cake.power.gif'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->submit('/relative/cake.power.gif');
+		$expected = array(
+			'div' => array('class' => 'submit'),
+			'input' => array('type' => 'image', 'src' => 'relative/cake.power.gif'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->submit('cake.power.gif');
+		$expected = array(
+			'div' => array('class' => 'submit'),
+			'input' => array('type' => 'image', 'src' => 'img/cake.power.gif'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->submit('Not.an.image');
+		$expected = array(
+			'div' => array('class' => 'submit'),
+			'input' => array('type' => 'submit', 'value' => 'Not.an.image'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$after = '--after--';
+		$before = '--before--';
+		$result = $this->Form->submit('cake.power.gif', array('after' => $after));
+		$expected = array(
+			'div' => array('class' => 'submit'),
+			'input' => array('type' => 'image', 'src' => 'img/cake.power.gif'),
+			'--after--',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->submit('cake.power.gif', array('before' => $before));
+		$expected = array(
+			'div' => array('class' => 'submit'),
+			'--before--',
+			'input' => array('type' => 'image', 'src' => 'img/cake.power.gif'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->submit('cake.power.gif', array('before' => $before, 'after' => $after));
+		$expected = array(
+			'div' => array('class' => 'submit'),
+			'--before--',
+			'input' => array('type' => 'image', 'src' => 'img/cake.power.gif'),
+			'--after--',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->submit('Not.an.image', array('before' => $before, 'after' => $after));
+		$expected = array(
+			'div' => array('class' => 'submit'),
+			'--before--',
+			'input' => array('type' => 'submit', 'value' => 'Not.an.image'),
+			'--after--',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * Test submit image with timestamps.
+ *
+ * @return void
+ */
+	function testSubmitImageTimestamp() {
+		Configure::write('Asset.timestamp', 'force');
+
+		$result = $this->Form->submit('cake.power.gif');
+		$expected = array(
+			'div' => array('class' => 'submit'),
+			'input' => array('type' => 'image', 'src' => 'preg:/img\/cake\.power\.gif\?\d*/'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test the create() method
+ *
+ * @access public
+ * @return void
+ */
+	function testCreate() {
+		$result = $this->Form->create('Contact');
+		$encoding = strtolower(Configure::read('App.encoding'));
+		$expected = array(
+			'form' => array(
+				'id' => 'ContactAddForm', 'method' => 'post', 'action' => '/contacts/add',
+				'accept-charset' => $encoding
+			),
+			'div' => array('style' => 'preg:/display\s*\:\s*none;\s*/'),
+			'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->create('Contact', array('type' => 'GET'));
+		$expected = array('form' => array(
+			'id' => 'ContactAddForm', 'method' => 'get', 'action' => '/contacts/add',
+			'accept-charset' => $encoding
+		));
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->create('Contact', array('type' => 'get'));
+		$expected = array('form' => array(
+			'id' => 'ContactAddForm', 'method' => 'get', 'action' => '/contacts/add',
+			'accept-charset' => $encoding
+		));
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->create('Contact', array('type' => 'put'));
+		$expected = array(
+			'form' => array(
+				'id' => 'ContactAddForm', 'method' => 'post', 'action' => '/contacts/add',
+				'accept-charset' => $encoding
+			),
+			'div' => array('style' => 'display:none;'),
+			'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'PUT'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->create('Contact', array('type' => 'file'));
+		$expected = array(
+			'form' => array(
+				'id' => 'ContactAddForm', 'method' => 'post', 'action' => '/contacts/add',
+				'accept-charset' => $encoding, 'enctype' => 'multipart/form-data'
+			),
+			'div' => array('style' => 'display:none;'),
+			'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data['Contact']['id'] = 1;
+		$this->Form->params['action'] = 'edit';
+		$result = $this->Form->create('Contact');
+		$expected = array(
+			'form' => array(
+				'id' => 'ContactEditForm', 'method' => 'post', 'action' => '/contacts/edit/1',
+				'accept-charset' => $encoding
+			),
+			'div' => array('style' => 'display:none;'),
+			'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'PUT'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data['Contact']['id'] = 1;
+		$this->Form->params['action'] = 'edit';
+		$result = $this->Form->create('Contact', array('type' => 'file'));
+		$expected = array(
+			'form' => array(
+				'id' => 'ContactEditForm', 'method' => 'post', 'action' => '/contacts/edit/1',
+				'accept-charset' => $encoding, 'enctype' => 'multipart/form-data'
+			),
+			'div' => array('style' => 'display:none;'),
+			'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'PUT'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data['ContactNonStandardPk']['pk'] = 1;
+		$result = $this->Form->create('ContactNonStandardPk');
+		$expected = array(
+			'form' => array(
+				'id' => 'ContactNonStandardPkEditForm', 'method' => 'post',
+				'action' => '/contact_non_standard_pks/edit/1','accept-charset' => $encoding
+			),
+			'div' => array('style' => 'display:none;'),
+			'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'PUT'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->create('Contact', array('id' => 'TestId'));
+		$expected = array(
+			'form' => array(
+				'id' => 'TestId', 'method' => 'post', 'action' => '/contacts/edit/1',
+				'accept-charset' => $encoding
+			),
+			'div' => array('style' => 'display:none;'),
+			'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'PUT'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->params['action'] = 'add';
+		$result = $this->Form->create('User', array('url' => array('action' => 'login')));
+		$expected = array(
+			'form' => array(
+				'id' => 'UserAddForm', 'method' => 'post', 'action' => '/users/login',
+				'accept-charset' => $encoding
+			),
+			'div' => array('style' => 'display:none;'),
+			'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->create('User', array('action' => 'login'));
+		$expected = array(
+			'form' => array(
+				'id' => 'UserLoginForm', 'method' => 'post', 'action' => '/users/login',
+				'accept-charset' => $encoding
+			),
+			'div' => array('style' => 'display:none;'),
+			'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->create('User', array('url' => '/users/login'));
+		$expected = array(
+			'form' => array('method' => 'post', 'action' => '/users/login','accept-charset' => $encoding),
+			'div' => array('style' => 'display:none;'),
+			'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->params['controller'] = 'pages';
+		$this->Form->params['models'] = array('User', 'Post');
+		$result = $this->Form->create('User', array('action' => 'signup'));
+		$expected = array(
+			'form' => array(
+				'id' => 'UserSignupForm', 'method' => 'post', 'action' => '/users/signup',
+				'accept-charset' => $encoding
+			),
+			'div' => array('style' => 'display:none;'),
+			'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data = array();
+		$this->Form->params['controller'] = 'contacts';
+		$this->Form->params['models'] = array('Contact');
+		$result = $this->Form->create(array('url' => array('action' => 'index', 'param')));
+		$expected = array(
+			'form' => array(
+				'id' => 'ContactAddForm', 'method' => 'post', 'action' => '/contacts/index/param',
+				'accept-charset' => 'utf-8'
+			),
+			'div' => array('style' => 'display:none;'),
+			'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+	}
+
+/**
+ * Test the onsubmit option for create()
+ *
+ * @return void
+ */
+	public function testCreateOnSubmit() {
+		$this->Form->data = array();
+		$this->Form->params['controller'] = 'contacts';
+		$this->Form->params['models'] = array('Contact');
+		$result = $this->Form->create(array('url' => array('action' => 'index', 'param'), 'default' => false));
+		$expected = array(
+			'form' => array(
+				'id' => 'ContactAddForm', 'method' => 'post', 'onsubmit' => 'event.returnValue = false; return false;', 'action' => '/contacts/index/param',
+				'accept-charset' => 'utf-8'
+			),
+			'div' => array('style' => 'display:none;'),
+			'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data = array();
+		$this->Form->params['controller'] = 'contacts';
+		$this->Form->params['models'] = array('Contact');
+		$result = $this->Form->create(array(
+			'url' => array('action' => 'index', 'param'),
+			'default' => false,
+			'onsubmit' => 'someFunction();'
+		));
+
+		$expected = array(
+			'form' => array(
+				'id' => 'ContactAddForm', 'method' => 'post',
+				'onsubmit' => 'someFunction();event.returnValue = false; return false;',
+				'action' => '/contacts/index/param',
+				'accept-charset' => 'utf-8'
+			),
+			'div' => array('style' => 'display:none;'),
+			'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test that inputDefaults are stored and used.
+ *
+ * @return void
+ */
+	function testCreateWithInputDefaults() {
+		$this->Form->create('User', array(
+			'inputDefaults' => array('div' => false, 'label' => false)
+		));
+		$result = $this->Form->input('username');
+		$expected = array(
+			'input' => array('type' => 'text', 'name' => 'data[User][username]', 'id' => 'UserUsername')
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('username', array('div' => true, 'label' => 'username'));
+		$expected = array(
+			'div' => array('class' => 'input text'),
+			'label' => array('for' => 'UserUsername'), 'username', '/label',
+			'input' => array('type' => 'text', 'name' => 'data[User][username]', 'id' => 'UserUsername'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test automatic accept-charset overriding
+ *
+ * @return void
+ */
+	function testCreateWithAcceptCharset() {
+		$result = $this->Form->create('UserForm', array(
+				'type' => 'post', 'action' => 'login','encoding' => 'iso-8859-1'
+			)
+		);
+		$expected = array(
+			'form' => array(
+				'method' => 'post', 'action' => '/user_forms/login', 'id' => 'UserFormLoginForm',
+				'accept-charset' => 'iso-8859-1'
+			),
+			'div' => array('style' => 'display:none;'),
+			'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * Test base form url when url param is passed with multiple parameters (&)
+ *
+ */
+	function testCreateQuerystringParams() {
+		$encoding = strtolower(Configure::read('App.encoding'));
+		$result = $this->Form->create('Contact', array(
+			'type' => 'post',
+			'escape' => false,
+			'url' => array(
+				'controller' => 'controller',
+				'action' => 'action',
+				'?' => array('param1' => 'value1', 'param2' => 'value2')
+			)
+		));
+		$expected = array(
+			'form' => array(
+				'id' => 'ContactAddForm',
+				'method' => 'post',
+				'action' => '/controller/action?param1=value1&amp;param2=value2',
+				'accept-charset' => $encoding
+			),
+			'div' => array('style' => 'display:none;'),
+			'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->create('Contact', array(
+			'type' => 'post',
+			'url' => array(
+				'controller' => 'controller',
+				'action' => 'action',
+				'?' => array('param1' => 'value1', 'param2' => 'value2')
+			)
+		));
+		$expected = array(
+			'form' => array(
+				'id' => 'ContactAddForm',
+				'method' => 'post',
+				'action' => '/controller/action?param1=value1&amp;param2=value2',
+				'accept-charset' => $encoding
+			),
+			'div' => array('style' => 'display:none;'),
+			'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test that create() doesn't cause errors by multiple id's being in the primary key
+ * as could happen with multiple select or checkboxes.
+ *
+ * @return void
+ */
+	function testCreateWithMultipleIdInData() {
+		$encoding = strtolower(Configure::read('App.encoding'));
+
+		$this->Form->data['Contact']['id'] = array(1, 2);
+		$result = $this->Form->create('Contact');
+		$expected = array(
+			'form' => array(
+				'id' => 'ContactAddForm',
+				'method' => 'post',
+				'action' => '/contacts/add',
+				'accept-charset' => $encoding
+			),
+			'div' => array('style' => 'display:none;'),
+			'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test that create() doesn't add in extra passed params.
+ *
+ * @return void
+ */
+	function testCreatePassedArgs() {
+		$encoding = strtolower(Configure::read('App.encoding'));
+		$this->Form->data['Contact']['id'] = 1;
+		$result = $this->Form->create('Contact', array(
+			'type' => 'post',
+			'escape' => false,
+			'url' => array(
+				'action' => 'edit',
+				'myparam'
+			)
+		));
+		$expected = array(
+			'form' => array(
+				'id' => 'ContactAddForm',
+				'method' => 'post',
+				'action' => '/contacts/edit/myparam',
+				'accept-charset' => $encoding
+			),
+			'div' => array('style' => 'display:none;'),
+			'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST'),
+			'/div'
+		);
+		$this->assertTags($result, $expected, true);
+	}
+
+/**
+ * test creating a get form, and get form inputs.
+ *
+ * @access public
+ * @return void
+ */
+	function testGetFormCreate() {
+		$encoding = strtolower(Configure::read('App.encoding'));
+		$result = $this->Form->create('Contact', array('type' => 'get'));
+		$this->assertTags($result, array('form' => array(
+			'id' => 'ContactAddForm', 'method' => 'get', 'action' => '/contacts/add',
+			'accept-charset' => $encoding
+		)));
+
+		$result = $this->Form->text('Contact.name');
+		$this->assertTags($result, array('input' => array(
+			'name' => 'name', 'type' => 'text', 'id' => 'ContactName',
+		)));
+
+		$result = $this->Form->password('password');
+		$this->assertTags($result, array('input' => array(
+			'name' => 'password', 'type' => 'password', 'id' => 'ContactPassword'
+		)));
+		$this->assertNoPattern('/<input[^<>]+[^id|name|type|value]=[^<>]*>$/', $result);
+
+		$result = $this->Form->text('user_form');
+		$this->assertTags($result, array('input' => array(
+			'name' => 'user_form', 'type' => 'text', 'id' => 'ContactUserForm'
+		)));
+	}
+
+/**
+ * test get form, and inputs when the model param is false
+ *
+ * @return void
+ */
+	function testGetFormWithFalseModel() {
+		$encoding = strtolower(Configure::read('App.encoding'));
+		$result = $this->Form->create(false, array('type' => 'get'));
+
+		$expected = array('form' => array(
+			'id' => 'addForm', 'method' => 'get', 'action' => '/contact_test/add',
+			'accept-charset' => $encoding
+		));
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->text('reason');
+		$expected = array(
+			'input' => array('type' => 'text', 'name' => 'reason', 'id' => 'reason')
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test that datetime() works with GET style forms.
+ *
+ * @return void
+ */
+	function testDateTimeWithGetForms() {
+		extract($this->dateRegex);
+		$this->Form->create('Contact', array('type' => 'get'));
+		$result = $this->Form->datetime('created');
+
+		$this->assertPattern('/name="created\[year\]"/', $result, 'year name attribute is wrong.');
+		$this->assertPattern('/name="created\[month\]"/', $result, 'month name attribute is wrong.');
+		$this->assertPattern('/name="created\[day\]"/', $result, 'day name attribute is wrong.');
+		$this->assertPattern('/name="created\[hour\]"/', $result, 'hour name attribute is wrong.');
+		$this->assertPattern('/name="created\[min\]"/', $result, 'min name attribute is wrong.');
+		$this->assertPattern('/name="created\[meridian\]"/', $result, 'meridian name attribute is wrong.');
+	}
+/**
+ * testEditFormWithData method
+ *
+ * test auto populating form elements from submitted data.
+ *
+ * @access public
+ * @return void
+ */
+	function testEditFormWithData() {
+		$this->Form->data = array('Person' => array(
+			'id' => 1,
+			'first_name' => 'Nate',
+			'last_name' => 'Abele',
+			'email' => 'nate****@examp*****'
+		));
+		$this->Form->params = array('models' => array('Person'), 'controller'	=> 'people', 'action' => 'add');
+		$options = array(1 => 'Nate', 2 => 'Garrett', 3 => 'Larry');
+
+		$this->Form->create();
+		$result = $this->Form->select('People.People', $options, null, array('multiple' => true));
+		$expected = array(
+			'input' => array('type' => 'hidden', 'name' => 'data[People][People]', 'value' => '', 'id' => 'PeoplePeople_'),
+			'select' => array(
+				'name' => 'data[People][People][]', 'multiple' => 'multiple', 'id' => 'PeoplePeople'
+			),
+			array('option' => array('value' => 1)), 'Nate', '/option',
+			array('option' => array('value' => 2)), 'Garrett', '/option',
+			array('option' => array('value' => 3)), 'Larry', '/option',
+			'/select'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testFormMagicInput method
+ *
+ * @access public
+ * @return void
+ */
+	function testFormMagicInput() {
+		$encoding = strtolower(Configure::read('App.encoding'));
+		$result = $this->Form->create('Contact');
+		$expected = array(
+			'form' => array(
+				'id' => 'ContactAddForm', 'method' => 'post', 'action' => '/contacts/add',
+				'accept-charset' => $encoding
+			),
+			'div' => array('style' => 'display:none;'),
+			'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('name');
+		$expected = array(
+			'div' => array('class' => 'input text'),
+			'label' => array('for' => 'ContactName'),
+			'Name',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Contact][name]',
+				'id' => 'ContactName', 'maxlength' => '255'
+			),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('non_existing_field_in_contact_model');
+		$expected = array(
+			'div' => array('class' => 'input text'),
+			'label' => array('for' => 'ContactNonExistingFieldInContactModel'),
+			'Non Existing Field In Contact Model',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Contact][non_existing_field_in_contact_model]',
+				'id' => 'ContactNonExistingFieldInContactModel'
+			),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Address.street');
+		$expected = array(
+			'div' => array('class' => 'input text'),
+			'label' => array('for' => 'AddressStreet'),
+			'Street',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Address][street]',
+				'id' => 'AddressStreet'
+			),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Address.non_existing_field_in_model');
+		$expected = array(
+			'div' => array('class' => 'input text'),
+			'label' => array('for' => 'AddressNonExistingFieldInModel'),
+			'Non Existing Field In Model',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Address][non_existing_field_in_model]',
+				'id' => 'AddressNonExistingFieldInModel'
+			),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('name', array('div' => false));
+		$expected = array(
+			'label' => array('for' => 'ContactName'),
+			'Name',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Contact][name]',
+				'id' => 'ContactName', 'maxlength' => '255'
+			)
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Contact.non_existing');
+		$expected = array(
+			'div' => array('class' => 'input text required'),
+			'label' => array('for' => 'ContactNonExisting'),
+			'Non Existing',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Contact][non_existing]',
+				'id' => 'ContactNonExisting'
+			),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Contact.imrequired');
+		$expected = array(
+			'div' => array('class' => 'input text required'),
+			'label' => array('for' => 'ContactImrequired'),
+			'Imrequired',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Contact][imrequired]',
+				'id' => 'ContactImrequired'
+			),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Contact.imalsorequired');
+		$expected = array(
+			'div' => array('class' => 'input text required'),
+			'label' => array('for' => 'ContactImalsorequired'),
+			'Imalsorequired',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Contact][imalsorequired]',
+				'id' => 'ContactImalsorequired'
+			),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Contact.imrequiredtoo');
+		$expected = array(
+			'div' => array('class' => 'input text required'),
+			'label' => array('for' => 'ContactImrequiredtoo'),
+			'Imrequiredtoo',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Contact][imrequiredtoo]',
+				'id' => 'ContactImrequiredtoo'
+			),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Contact.required_one');
+		$expected = array(
+			'div' => array('class' => 'input text required'),
+			'label' => array('for' => 'ContactRequiredOne'),
+			'Required One',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Contact][required_one]',
+				'id' => 'ContactRequiredOne'
+			),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Contact.imnotrequired');
+		$expected = array(
+			'div' => array('class' => 'input text'),
+			'label' => array('for' => 'ContactImnotrequired'),
+			'Imnotrequired',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Contact][imnotrequired]',
+				'id' => 'ContactImnotrequired'
+			),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Contact.imalsonotrequired');
+		$expected = array(
+			'div' => array('class' => 'input text'),
+			'label' => array('for' => 'ContactImalsonotrequired'),
+			'Imalsonotrequired',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Contact][imalsonotrequired]',
+				'id' => 'ContactImalsonotrequired'
+			),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Contact.imnotrequiredeither');
+		$expected = array(
+			'div' => array('class' => 'input text'),
+			'label' => array('for' => 'ContactImnotrequiredeither'),
+			'Imnotrequiredeither',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Contact][imnotrequiredeither]',
+				'id' => 'ContactImnotrequiredeither'
+			),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		extract($this->dateRegex);
+		$now = strtotime('now');
+
+		$result = $this->Form->input('Contact.published', array('div' => false));
+		$expected = array(
+			'label' => array('for' => 'ContactPublishedMonth'),
+			'Published',
+			'/label',
+			array('select' => array(
+				'name' => 'data[Contact][published][month]', 'id' => 'ContactPublishedMonth'
+			)),
+			$monthsRegex,
+			array('option' => array('value' => date('m', $now), 'selected' => 'selected')),
+			date('F', $now),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array(
+				'name' => 'data[Contact][published][day]', 'id' => 'ContactPublishedDay'
+			)),
+			$daysRegex,
+			array('option' => array('value' => date('d', $now), 'selected' => 'selected')),
+			date('j', $now),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array(
+				'name' => 'data[Contact][published][year]', 'id' => 'ContactPublishedYear'
+			)),
+			$yearsRegex,
+			array('option' => array('value' => date('Y', $now), 'selected' => 'selected')),
+			date('Y', $now),
+			'*/select'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Contact.updated', array('div' => false));
+		$expected = array(
+			'label' => array('for' => 'ContactUpdatedMonth'),
+			'Updated',
+			'/label',
+			array('select' => array(
+				'name' => 'data[Contact][updated][month]', 'id' => 'ContactUpdatedMonth'
+			)),
+			$monthsRegex,
+			array('option' => array('value' => date('m', $now), 'selected' => 'selected')),
+			date('F', $now),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array(
+				'name' => 'data[Contact][updated][day]', 'id' => 'ContactUpdatedDay'
+			)),
+			$daysRegex,
+			array('option' => array('value' => date('d', $now), 'selected' => 'selected')),
+			date('j', $now),
+			'/option',
+			'*/select',
+			'-',
+			array('select' => array(
+				'name' => 'data[Contact][updated][year]', 'id' => 'ContactUpdatedYear'
+			)),
+			$yearsRegex,
+			array('option' => array('value' => date('Y', $now), 'selected' => 'selected')),
+			date('Y', $now),
+			'*/select'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('User.stuff');
+		$expected = array(
+			'div' => array('class' => 'input text'),
+			'label' => array('for' => 'UserStuff'),
+			'Stuff',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[User][stuff]',
+				'id' => 'UserStuff', 'maxlength' => 10
+			),
+			'/div'
+		);
+		$this->assertTags($result, $expected, true);
+	}
+
+/**
+ * testForMagicInputNonExistingNorValidated method
+ *
+ * @access public
+ * @return void
+ */
+	function testForMagicInputNonExistingNorValidated() {
+		$encoding = strtolower(Configure::read('App.encoding'));
+		$result = $this->Form->create('Contact');
+		$expected = array(
+			'form' => array(
+				'id' => 'ContactAddForm', 'method' => 'post', 'action' => '/contacts/add',
+				'accept-charset' => $encoding
+			),
+			'div' => array('style' => 'display:none;'),
+			'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Contact.non_existing_nor_validated', array('div' => false));
+		$expected = array(
+			'label' => array('for' => 'ContactNonExistingNorValidated'),
+			'Non Existing Nor Validated',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Contact][non_existing_nor_validated]',
+				'id' => 'ContactNonExistingNorValidated'
+			)
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Contact.non_existing_nor_validated', array(
+			'div' => false, 'value' => 'my value'
+		));
+		$expected = array(
+			'label' => array('for' => 'ContactNonExistingNorValidated'),
+			'Non Existing Nor Validated',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Contact][non_existing_nor_validated]',
+				'value' => 'my value', 'id' => 'ContactNonExistingNorValidated'
+			)
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->data = array(
+			'Contact' => array('non_existing_nor_validated' => 'CakePHP magic'
+		));
+		$result = $this->Form->input('Contact.non_existing_nor_validated', array('div' => false));
+		$expected = array(
+			'label' => array('for' => 'ContactNonExistingNorValidated'),
+			'Non Existing Nor Validated',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Contact][non_existing_nor_validated]',
+				'value' => 'CakePHP magic', 'id' => 'ContactNonExistingNorValidated'
+			)
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testFormMagicInputLabel method
+ *
+ * @access public
+ * @return void
+ */
+	function testFormMagicInputLabel() {
+		$encoding = strtolower(Configure::read('App.encoding'));
+		$result = $this->Form->create('Contact');
+		$expected = array(
+			'form' => array(
+				'id' => 'ContactAddForm', 'method' => 'post', 'action' => '/contacts/add',
+				'accept-charset' => $encoding
+			),
+			'div' => array('style' => 'display:none;'),
+			'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Contact.name', array('div' => false, 'label' => false));
+		$this->assertTags($result, array('input' => array(
+			'name' => 'data[Contact][name]', 'type' => 'text',
+			'id' => 'ContactName', 'maxlength' => '255')
+		));
+
+		$result = $this->Form->input('Contact.name', array('div' => false, 'label' => 'My label'));
+		$expected = array(
+			'label' => array('for' => 'ContactName'),
+			'My label',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Contact][name]',
+				'id' => 'ContactName', 'maxlength' => '255'
+			)
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Contact.name', array(
+			'div' => false, 'label' => array('class' => 'mandatory')
+		));
+		$expected = array(
+			'label' => array('for' => 'ContactName', 'class' => 'mandatory'),
+			'Name',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Contact][name]',
+				'id' => 'ContactName', 'maxlength' => '255'
+			)
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Contact.name', array(
+			'div' => false, 'label' => array('class' => 'mandatory', 'text' => 'My label')
+		));
+		$expected = array(
+			'label' => array('for' => 'ContactName', 'class' => 'mandatory'),
+			'My label',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Contact][name]',
+				'id' => 'ContactName', 'maxlength' => '255'
+			)
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Contact.name', array(
+			'div' => false, 'id' => 'my_id', 'label' => array('for' => 'my_id')
+		));
+		$expected = array(
+			'label' => array('for' => 'my_id'),
+			'Name',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Contact][name]',
+				'id' => 'my_id', 'maxlength' => '255'
+			)
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('1.id');
+		$this->assertTags($result, array('input' => array(
+			'type' => 'hidden', 'name' => 'data[Contact][1][id]',
+			'id' => 'Contact1Id'
+		)));
+
+		$result = $this->Form->input("1.name");
+		$expected = array(
+			'div' => array('class' => 'input text'),
+			'label' => array('for' => 'Contact1Name'),
+			'Name',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Contact][1][name]',
+				'id' => 'Contact1Name', 'maxlength' => '255'
+			),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Contact.1.id');
+		$this->assertTags($result, array(
+			'input' => array(
+				'type' => 'hidden', 'name' => 'data[Contact][1][id]',
+				'id' => 'Contact1Id'
+			)
+		));
+
+		$result = $this->Form->input("Model.1.name");
+		$expected = array(
+			'div' => array('class' => 'input text'),
+			'label' => array('for' => 'Model1Name'),
+			'Name',
+			'/label',
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Model][1][name]',
+				'id' => 'Model1Name'
+			),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testFormEnd method
+ *
+ * @access public
+ * @return void
+ */
+	function testFormEnd() {
+		$this->assertEqual($this->Form->end(), '</form>');
+
+		$result = $this->Form->end('');
+		$expected = array(
+			'div' => array('class' => 'submit'),
+			'input' => array('type' => 'submit', 'value' => ''),
+			'/div',
+			'/form'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->end(array('label' => ''));
+		$expected = array(
+			'div' => array('class' => 'submit'),
+			'input' => array('type' => 'submit', 'value' => ''),
+			'/div',
+			'/form'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->end('save');
+		$expected = array(
+			'div' => array('class' => 'submit'),
+			'input' => array('type' => 'submit', 'value' => 'save'),
+			'/div',
+			'/form'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->end(array('label' => 'save'));
+		$expected = array(
+			'div' => array('class' => 'submit'),
+			'input' => array('type' => 'submit', 'value' => 'save'),
+			'/div',
+			'/form'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->end(array('label' => 'save', 'name' => 'Whatever'));
+		$expected = array(
+			'div' => array('class' => 'submit'),
+			'input' => array('type' => 'submit', 'value' => 'save', 'name' => 'Whatever'),
+			'/div',
+			'/form'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->end(array('name' => 'Whatever'));
+		$expected = array(
+			'div' => array('class' => 'submit'),
+			'input' => array('type' => 'submit', 'value' => 'Submit', 'name' => 'Whatever'),
+			'/div',
+			'/form'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->end(array('label' => 'save', 'name' => 'Whatever', 'div' => 'good'));
+		$expected = array(
+			'div' => array('class' => 'good'),
+			'input' => array('type' => 'submit', 'value' => 'save', 'name' => 'Whatever'),
+			'/div',
+			'/form'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->end(array(
+			'label' => 'save', 'name' => 'Whatever', 'div' => array('class' => 'good')
+		));
+		$expected = array(
+			'div' => array('class' => 'good'),
+			'input' => array('type' => 'submit', 'value' => 'save', 'name' => 'Whatever'),
+			'/div',
+			'/form'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testMultipleFormWithIdFields method
+ *
+ * @access public
+ * @return void
+ */
+	function testMultipleFormWithIdFields() {
+		$this->Form->create('UserForm');
+
+		$result = $this->Form->input('id');
+		$this->assertTags($result, array('input' => array(
+			'type' => 'hidden', 'name' => 'data[UserForm][id]', 'id' => 'UserFormId'
+		)));
+
+		$result = $this->Form->input('ValidateItem.id');
+		$this->assertTags($result, array('input' => array(
+			'type' => 'hidden', 'name' => 'data[ValidateItem][id]',
+			'id' => 'ValidateItemId'
+		)));
+
+		$result = $this->Form->input('ValidateUser.id');
+		$this->assertTags($result, array('input' => array(
+			'type' => 'hidden', 'name' => 'data[ValidateUser][id]',
+			'id' => 'ValidateUserId'
+		)));
+	}
+
+/**
+ * testDbLessModel method
+ *
+ * @access public
+ * @return void
+ */
+	function testDbLessModel() {
+		$this->Form->create('TestMail');
+
+		$result = $this->Form->input('name');
+		$expected = array(
+			'div' => array('class' => 'input text'),
+			'label' => array('for' => 'TestMailName'),
+			'Name',
+			'/label',
+			'input' => array(
+				'name' => 'data[TestMail][name]', 'type' => 'text',
+				'id' => 'TestMailName'
+			),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		ClassRegistry::init('TestMail');
+		$this->Form->create('TestMail');
+		$result = $this->Form->input('name');
+		$expected = array(
+			'div' => array('class' => 'input text'),
+			'label' => array('for' => 'TestMailName'),
+			'Name',
+			'/label',
+			'input' => array(
+				'name' => 'data[TestMail][name]', 'type' => 'text',
+				'id' => 'TestMailName'
+			),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testBrokenness method
+ *
+ * @access public
+ * @return void
+ */
+	function testBrokenness() {
+		/*
+		 * #4 This test has two parents and four children. By default (as of r7117) both
+		 * parents are show but the first parent is missing a child. This is the inconsistency
+		 * in the default behaviour - one parent has all children, the other does not - dependent
+		 * on the data values.
+		 */
+		$result = $this->Form->select('Model.field', array(
+			'Fred' => array(
+				'freds_son_1' => 'Fred',
+				'freds_son_2' => 'Freddie'
+			),
+			'Bert' => array(
+				'berts_son_1' => 'Albert',
+				'berts_son_2' => 'Bertie')
+			),
+			null,
+			array('showParents' => true, 'empty' => false)
+		);
+
+		$expected = array(
+			'select' => array('name' => 'data[Model][field]', 'id' => 'ModelField'),
+				array('optgroup' => array('label' => 'Fred')),
+					array('option' => array('value' => 'freds_son_1')),
+						'Fred',
+					'/option',
+					array('option' => array('value' => 'freds_son_2')),
+						'Freddie',
+					'/option',
+				'/optgroup',
+				array('optgroup' => array('label' => 'Bert')),
+					array('option' => array('value' => 'berts_son_1')),
+						'Albert',
+					'/option',
+					array('option' => array('value' => 'berts_son_2')),
+						'Bertie',
+					'/option',
+				'/optgroup',
+			'/select'
+		);
+		$this->assertTags($result, $expected);
+
+		/*
+		 * #2 This is structurally identical to the test above (#1) - only the parent name has
+		 * changed, so we should expect the same select list data, just with a different name
+		 * for the parent.  As of #7117, this test fails because option 3 => 'Three' disappears.
+		 * This is where data corruption can occur, because when a select value is missing from
+		 * a list a form will substitute the first value in the list - without the user knowing.
+		 * If the optgroup name 'Parent' (above) is updated to 'Three' (below), this should not
+		 * affect the availability of 3 => 'Three' as a valid option.
+		 */
+		$options = array(1 => 'One', 2 => 'Two', 'Three' => array(
+			3 => 'Three', 4 => 'Four', 5 => 'Five'
+		));
+		$result = $this->Form->select(
+			'Model.field', $options, null, array('showParents' => true, 'empty' => false)
+		);
+
+		$expected = array(
+			'select' => array('name' => 'data[Model][field]', 'id' => 'ModelField'),
+				array('option' => array('value' => 1)),
+					'One',
+				'/option',
+				array('option' => array('value' => 2)),
+					'Two',
+				'/option',
+				array('optgroup' => array('label' => 'Three')),
+					array('option' => array('value' => 3)),
+						'Three',
+					'/option',
+					array('option' => array('value' => 4)),
+						'Four',
+					'/option',
+					array('option' => array('value' => 5)),
+						'Five',
+					'/option',
+				'/optgroup',
+			'/select'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * Test the generation of fields for a multi record form.
+ *
+ * @return void
+ */
+	function testMultiRecordForm() {
+		$this->Form->create('ValidateProfile');
+		$this->Form->data['ValidateProfile'][1]['ValidateItem'][2]['name'] = 'Value';
+		$result = $this->Form->input('ValidateProfile.1.ValidateItem.2.name');
+		$expected = array(
+			'div' => array('class' => 'input textarea'),
+				'label' => array('for' => 'ValidateProfile1ValidateItem2Name'),
+					'Name',
+				'/label',
+				'textarea' => array(
+					'id' => 'ValidateProfile1ValidateItem2Name',
+					'name' => 'data[ValidateProfile][1][ValidateItem][2][name]',
+					'cols' => 30,
+					'rows' => 6
+				),
+				'Value',
+				'/textarea',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('ValidateProfile.1.ValidateItem.2.created',array('empty' => true));
+		$expected = array(
+			'div' => array('class' => 'input date'),
+			'label' => array('for' => 'ValidateProfile1ValidateItem2CreatedMonth'),
+			'Created',
+			'/label',
+			array('select' => array(
+				'name' => 'data[ValidateProfile][1][ValidateItem][2][created][month]',
+				'id' => 'ValidateProfile1ValidateItem2CreatedMonth'
+				)
+			),
+			array('option' => array('value' => '')), '/option',
+			$this->dateRegex['monthsRegex'],
+			'/select', '-',
+			array('select' => array(
+				'name' => 'data[ValidateProfile][1][ValidateItem][2][created][day]',
+				'id' => 'ValidateProfile1ValidateItem2CreatedDay'
+				)
+			),
+			array('option' => array('value' => '')), '/option',
+			$this->dateRegex['daysRegex'],
+			'/select', '-',
+			array('select' => array(
+				'name' => 'data[ValidateProfile][1][ValidateItem][2][created][year]',
+				'id' => 'ValidateProfile1ValidateItem2CreatedYear'
+				)
+			),
+			array('option' => array('value' => '')), '/option',
+			$this->dateRegex['yearsRegex'],
+			'/select',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Form->validationErrors['ValidateProfile'][1]['ValidateItem'][2]['profile_id'] = 'Error';
+		$this->Form->data['ValidateProfile'][1]['ValidateItem'][2]['profile_id'] = '1';
+		$result = $this->Form->input('ValidateProfile.1.ValidateItem.2.profile_id');
+		$expected = array(
+			'div' => array('class' => 'input select error'),
+			'label' => array('for' => 'ValidateProfile1ValidateItem2ProfileId'),
+			'Profile',
+			'/label',
+			'select' => array(
+				'name' => 'data[ValidateProfile][1][ValidateItem][2][profile_id]',
+				'id' => 'ValidateProfile1ValidateItem2ProfileId',
+				'class' => 'form-error'
+			),
+			'/select',
+			array('div' => array('class' => 'error-message')),
+			'Error',
+			'/div',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test the correct display of multi-record form validation errors.
+ *
+ * @return void
+ */
+	function testMultiRecordFormValidationErrors() {
+		$this->Form->create('ValidateProfile');
+		$this->Form->validationErrors['ValidateProfile'][2]['ValidateItem'][1]['name'] = 'Error in field name';
+		$result = $this->Form->error('ValidateProfile.2.ValidateItem.1.name');
+		$this->assertTags($result, array('div' => array('class' => 'error-message'), 'Error in field name', '/div'));
+
+		$this->Form->validationErrors['ValidateProfile'][2]['city'] = 'Error in field city';
+		$result = $this->Form->error('ValidateProfile.2.city');
+		$this->assertTags($result, array('div' => array('class' => 'error-message'), 'Error in field city', '/div'));
+
+		$result = $this->Form->error('2.city');
+		$this->assertTags($result, array('div' => array('class' => 'error-message'), 'Error in field city', '/div'));
+	}
+
+/**
+ * tests the ability to change the order of the form input placeholder "input", "label", "before", "between", "after", "error"
+ *
+ * @return void
+ */
+	function testInputTemplate() {
+		$result = $this->Form->input('Contact.email', array(
+			'type' => 'text', 'format' => array('input')
+		));
+		$expected = array(
+			'div' => array('class' => 'input text'),
+			'input' => array(
+				'type' => 'text', 'name' => 'data[Contact][email]',
+				'id' => 'ContactEmail'
+			),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Contact.email', array(
+			'type' => 'text', 'format' => array('input', 'label'),
+			'label' => '<em>Email (required)</em>'
+		));
+		$expected = array(
+			'div' => array('class' => 'input text'),
+			array('input' => array(
+				'type' => 'text', 'name' => 'data[Contact][email]',
+				'id' => 'ContactEmail'
+			)),
+			'label' => array('for' => 'ContactEmail'),
+			'em' => array(),
+			'Email (required)',
+			'/em',
+			'/label',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Form->input('Contact.email', array(
+			'type' => 'text', 'format' => array('input', 'between', 'label', 'after'),
+			'between' => '<div>Something in the middle</div>',
+			'after' => '<span>Some text at the end</span>'
+		));
+		$expected = array(
+			'div' => array('class' => 'input text'),
+			array('input' => array(
+				'type' => 'text', 'name' => 'data[Contact][email]',
+				'id' => 'ContactEmail'
+			)),
+			array('div' => array()),
+			'Something in the middle',
+			'/div',
+			'label' => array('for' => 'ContactEmail'),
+			'Email',
+			'/label',
+			'span' => array(),
+			'Some text at the end',
+			'/span',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/view/helpers/html.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/view/helpers/html.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/view/helpers/html.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,1340 @@
+<?php
+/**
+ * HtmlHelperTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', array('Helper', 'AppHelper', 'ClassRegistry', 'Controller', 'Model'));
+App::import('Helper', array('Html', 'Form'));
+
+if (!defined('FULL_BASE_URL')) {
+	define('FULL_BASE_URL', 'http://cakephp.org');
+}
+
+/**
+ * TheHtmlTestController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class TheHtmlTestController extends Controller {
+
+/**
+ * name property
+ *
+ * @var string 'TheTest'
+ * @access public
+ */
+	var $name = 'TheTest';
+
+/**
+ * uses property
+ *
+ * @var mixed null
+ * @access public
+ */
+	var $uses = null;
+}
+
+Mock::generate('View', 'HtmlHelperMockView');
+
+/**
+ * HtmlHelperTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class HtmlHelperTest extends CakeTestCase {
+
+/**
+ * Regexp for CDATA start block
+ *
+ * @var string
+ */
+	var $cDataStart = 'preg:/^\/\/<!\[CDATA\[[\n\r]*/';
+/**
+ * Regexp for CDATA end block
+ *
+ * @var string
+ */
+	var $cDataEnd = 'preg:/[^\]]*\]\]\>[\s\r\n]*/';
+/**
+ * html property
+ *
+ * @var object
+ * @access public
+ */
+	var $Html = null;
+
+/**
+ * Backup of app encoding configuration setting
+ *
+ * @var string
+ * @access protected
+ */
+	var $_appEncoding;
+
+/**
+ * Backup of asset configuration settings
+ *
+ * @var string
+ * @access protected
+ */
+	var $_asset;
+
+/**
+ * Backup of debug configuration setting
+ *
+ * @var integer
+ * @access protected
+ */
+	var $_debug;
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function startTest() {
+		$this->Html =& new HtmlHelper();
+		$view =& new View(new TheHtmlTestController());
+		ClassRegistry::addObject('view', $view);
+		$this->_appEncoding = Configure::read('App.encoding');
+		$this->_asset = Configure::read('Asset');
+		$this->_debug = Configure::read('debug');
+	}
+
+/**
+ * endTest method
+ *
+ * @access public
+ * @return void
+ */
+	function endTest() {
+		Configure::write('App.encoding', $this->_appEncoding);
+		Configure::write('Asset', $this->_asset);
+		Configure::write('debug', $this->_debug);
+		ClassRegistry::flush();
+		unset($this->Html);
+	}
+
+/**
+ * testDocType method
+ *
+ * @access public
+ * @return void
+ */
+	function testDocType() {
+		$result = $this->Html->docType();
+		$expected = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Html->docType('html4-strict');
+		$expected = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">';
+		$this->assertEqual($result, $expected);
+
+		$this->assertNull($this->Html->docType('non-existing-doctype'));
+	}
+
+/**
+ * testLink method
+ *
+ * @access public
+ * @return void
+ */
+	function testLink() {
+		$result = $this->Html->link('/home');
+		$expected = array('a' => array('href' => '/home'), 'preg:/\/home/', '/a');
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->link('Posts', array('controller' => 'posts', 'action' => 'index', 'full_base' => true));
+		$expected = array('a' => array('href' => FULL_BASE_URL . '/posts'), 'Posts', '/a');
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->link('Home', '/home', array('confirm' => 'Are you sure you want to do this?'));
+		$expected = array(
+			'a' => array('href' => '/home', 'onclick' => 'return confirm(&#039;Are you sure you want to do this?&#039;);'),
+			'Home',
+			'/a'
+		);
+		$this->assertTags($result, $expected, true);
+
+		$result = $this->Html->link('Home', '/home', array('default' => false));
+		$expected = array(
+			'a' => array('href' => '/home', 'onclick' => 'event.returnValue = false; return false;'),
+			'Home',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->link('Next >', '#');
+		$expected = array(
+			'a' => array('href' => '#'),
+			'Next &gt;',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->link('Next >', '#', array('escape' => true));
+		$expected = array(
+			'a' => array('href' => '#'),
+			'Next &gt;',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->link('Next >', '#', array('escape' => 'utf-8'));
+		$expected = array(
+			'a' => array('href' => '#'),
+			'Next &gt;',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->link('Next >', '#', array('escape' => false));
+		$expected = array(
+			'a' => array('href' => '#'),
+			'Next >',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->link('Next >', '#', array(
+			'title' => 'to escape &#8230; or not escape?',
+			'escape' => false
+		));
+		$expected = array(
+			'a' => array('href' => '#', 'title' => 'to escape &#8230; or not escape?'),
+			'Next >',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->link('Next >', '#', array(
+			'title' => 'to escape &#8230; or not escape?',
+			'escape' => true
+		));
+		$expected = array(
+			'a' => array('href' => '#', 'title' => 'to escape &amp;#8230; or not escape?'),
+			'Next &gt;',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->link('Original size', array(
+			'controller' => 'images', 'action' => 'view', 3, '?' => array('height' => 100, 'width' => 200)
+		));
+		$expected = array(
+			'a' => array('href' => '/images/view/3?height=100&amp;width=200'),
+			'Original size',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		Configure::write('Asset.timestamp', false);
+
+		$result = $this->Html->link($this->Html->image('test.gif'), '#', array('escape' => false));
+		$expected = array(
+			'a' => array('href' => '#'),
+			'img' => array('src' => 'img/test.gif', 'alt' => ''),
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->image('test.gif', array('url' => '#'));
+		$expected = array(
+			'a' => array('href' => '#'),
+			'img' => array('src' => 'img/test.gif', 'alt' => ''),
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		Configure::write('Asset.timestamp', 'force');
+
+ 		$result = $this->Html->link($this->Html->image('test.gif'), '#', array('escape' => false));
+ 		$expected = array(
+ 			'a' => array('href' => '#'),
+			'img' => array('src' => 'preg:/img\/test\.gif\?\d*/', 'alt' => ''),
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->image('test.gif', array('url' => '#'));
+		$expected = array(
+			'a' => array('href' => '#'),
+			'img' => array('src' => 'preg:/img\/test\.gif\?\d*/', 'alt' => ''),
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testImageTag method
+ *
+ * @access public
+ * @return void
+ */
+	function testImageTag() {
+		Configure::write('Asset.timestamp', false);
+
+		$result = $this->Html->image('test.gif');
+		$this->assertTags($result, array('img' => array('src' => 'img/test.gif', 'alt' => '')));
+
+		$result = $this->Html->image('http://google.com/logo.gif');
+		$this->assertTags($result, array('img' => array('src' => 'http://google.com/logo.gif', 'alt' => '')));
+
+		$result = $this->Html->image(array('controller' => 'test', 'action' => 'view', 1, 'ext' => 'gif'));
+		$this->assertTags($result, array('img' => array('src' => '/test/view/1.gif', 'alt' => '')));
+
+		$result = $this->Html->image('/test/view/1.gif');
+		$this->assertTags($result, array('img' => array('src' => '/test/view/1.gif', 'alt' => '')));
+	}
+
+/**
+ * test image() with Asset.timestamp
+ *
+ * @return void
+ */
+	function testImageWithTimestampping() {
+		Configure::write('Asset.timestamp', 'force');
+
+		$this->Html->webroot = '/';
+		$result = $this->Html->image('cake.icon.png');
+		$this->assertTags($result, array('img' => array('src' => 'preg:/\/img\/cake\.icon\.png\?\d+/', 'alt' => '')));
+
+		Configure::write('debug', 0);
+		Configure::write('Asset.timestamp', 'force');
+
+		$result = $this->Html->image('cake.icon.png');
+		$this->assertTags($result, array('img' => array('src' => 'preg:/\/img\/cake\.icon\.png\?\d+/', 'alt' => '')));
+
+		$webroot = $this->Html->webroot;
+		$this->Html->webroot = '/testing/longer/';
+		$result = $this->Html->image('cake.icon.png');
+		$expected = array(
+			'img' => array('src' => 'preg:/\/testing\/longer\/img\/cake\.icon\.png\?[0-9]+/', 'alt' => '')
+		);
+		$this->assertTags($result, $expected);
+		$this->Html->webroot = $webroot;
+	}
+
+/**
+ * Tests creation of an image tag using a theme and asset timestamping
+ *
+ * @access public
+ * @return void
+ * @link https://trac.cakephp.org/ticket/6490
+ */
+	function testImageTagWithTheme() {
+		if ($this->skipIf(!is_writable(WWW_ROOT . 'theme'), 'Cannot write to webroot/theme')) {
+			return;
+		}
+		App::import('Core', 'File');
+
+		$testfile = WWW_ROOT . 'theme' . DS . 'test_theme' . DS . 'img' . DS . '__cake_test_image.gif';
+		$file =& new File($testfile, true);
+
+		App::build(array(
+			'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS)
+		));
+		Configure::write('Asset.timestamp', true);
+		Configure::write('debug', 1);
+
+		$this->Html->webroot = '/';
+		$this->Html->theme = 'test_theme';
+		$result = $this->Html->image('__cake_test_image.gif');
+		$this->assertTags($result, array(
+			'img' => array(
+				'src' => 'preg:/\/theme\/test_theme\/img\/__cake_test_image\.gif\?\d+/',
+				'alt' => ''
+		)));
+
+		$webroot = $this->Html->webroot;
+		$this->Html->webroot = '/testing/';
+		$result = $this->Html->image('__cake_test_image.gif');
+
+		$this->assertTags($result, array(
+			'img' => array(
+				'src' => 'preg:/\/testing\/theme\/test_theme\/img\/__cake_test_image\.gif\?\d+/',
+				'alt' => ''
+		)));
+		$this->Html->webroot = $webroot;
+
+		$dir =& new Folder(WWW_ROOT . 'theme' . DS . 'test_theme');
+		$dir->delete();
+	}
+
+/**
+ * test theme assets in main webroot path
+ *
+ * @access public
+ * @return void
+ */
+	function testThemeAssetsInMainWebrootPath() {
+		Configure::write('Asset.timestamp', false);
+		App::build(array(
+			'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS)
+		));
+		$webRoot = Configure::read('App.www_root');
+		Configure::write('App.www_root', TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'webroot' . DS);
+
+		$webroot = $this->Html->webroot;
+		$this->Html->theme = 'test_theme';
+		$result = $this->Html->css('webroot_test');
+		$expected = array(
+			'link' => array('rel' => 'stylesheet', 'type' => 'text/css', 'href' => 'preg:/.*theme\/test_theme\/css\/webroot_test\.css/')
+		);
+		$this->assertTags($result, $expected);
+
+		$webroot = $this->Html->webroot;
+		$this->Html->theme = 'test_theme';
+		$result = $this->Html->css('theme_webroot');
+		$expected = array(
+			'link' => array('rel' => 'stylesheet', 'type' => 'text/css', 'href' => 'preg:/.*theme\/test_theme\/css\/theme_webroot\.css/')
+		);
+		$this->assertTags($result, $expected);
+
+		Configure::write('App.www_root', $webRoot);
+	}
+
+/**
+ * testStyle method
+ *
+ * @access public
+ * @return void
+ */
+	function testStyle() {
+		$result = $this->Html->style(array('display'=> 'none', 'margin'=>'10px'));
+		$expected = 'display:none; margin:10px;';
+		$this->assertPattern('/^display\s*:\s*none\s*;\s*margin\s*:\s*10px\s*;?$/', $expected);
+
+		$result = $this->Html->style(array('display'=> 'none', 'margin'=>'10px'), false);
+		$lines = explode("\n", $result);
+		$this->assertPattern('/^\s*display\s*:\s*none\s*;\s*$/', $lines[0]);
+		$this->assertPattern('/^\s*margin\s*:\s*10px\s*;?$/', $lines[1]);
+	}
+
+/**
+ * testCssLink method
+ *
+ * @access public
+ * @return void
+ */
+	function testCssLink() {
+		Configure::write('Asset.timestamp', false);
+		Configure::write('Asset.filter.css', false);
+
+		$result = $this->Html->css('screen');
+		$expected = array(
+			'link' => array('rel' => 'stylesheet', 'type' => 'text/css', 'href' => 'preg:/.*css\/screen\.css/')
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->css('screen.css');
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->css('my.css.library');
+		$expected['link']['href'] = 'preg:/.*css\/my\.css\.library\.css/';
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->css('screen.css?1234');
+		$expected['link']['href'] = 'preg:/.*css\/screen\.css\?1234/';
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->css('http://whatever.com/screen.css?1234');
+		$expected['link']['href'] = 'preg:/http:\/\/.*\/screen\.css\?1234/';
+		$this->assertTags($result, $expected);
+
+		Configure::write('Asset.filter.css', 'css.php');
+		$result = $this->Html->css('cake.generic');
+		$expected['link']['href'] = 'preg:/.*ccss\/cake\.generic\.css/';
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->css('//example.com/css/cake.generic.css');
+		$expected['link']['href'] = 'preg:/.*example\.com\/css\/cake\.generic\.css/';
+		$this->assertTags($result, $expected);
+
+		Configure::write('Asset.filter.css', false);
+
+		$result = explode("\n", trim($this->Html->css(array('cake.generic', 'vendor.generic'))));
+		$expected['link']['href'] = 'preg:/.*css\/cake\.generic\.css/';
+		$this->assertTags($result[0], $expected);
+		$expected['link']['href'] = 'preg:/.*css\/vendor\.generic\.css/';
+		$this->assertTags($result[1], $expected);
+		$this->assertEqual(count($result), 2);
+
+		ClassRegistry::removeObject('view');
+		$view =& new HtmlHelperMockView();
+		ClassRegistry::addObject('view', $view);
+		$view->expectAt(0, 'addScript', array(new PatternExpectation('/css_in_head.css/')));
+		$result = $this->Html->css('css_in_head', null, array('inline' => false));
+		$this->assertNull($result);
+
+		$view =& ClassRegistry::getObject('view');
+		$view->expectAt(1, 'addScript', array(new NoPatternExpectation('/inline=""/')));
+		$result = $this->Html->css('more_css_in_head', null, array('inline' => false));
+		$this->assertNull($result);
+	}
+
+/**
+ * test use of css() and timestamping
+ *
+ * @return void
+ */
+	function testCssTimestamping() {
+		Configure::write('debug', 2);
+		Configure::write('Asset.timestamp', true);
+
+		$expected = array(
+			'link' => array('rel' => 'stylesheet', 'type' => 'text/css', 'href' => '')
+		);
+
+		$result = $this->Html->css('cake.generic');
+		$expected['link']['href'] = 'preg:/.*css\/cake\.generic\.css\?[0-9]+/';
+		$this->assertTags($result, $expected);
+
+		Configure::write('debug', 0);
+
+		$result = $this->Html->css('cake.generic');
+		$expected['link']['href'] = 'preg:/.*css\/cake\.generic\.css/';
+		$this->assertTags($result, $expected);
+
+		Configure::write('Asset.timestamp', 'force');
+
+		$result = $this->Html->css('cake.generic');
+		$expected['link']['href'] = 'preg:/.*css\/cake\.generic\.css\?[0-9]+/';
+		$this->assertTags($result, $expected);
+
+		$webroot = $this->Html->webroot;
+		$this->Html->webroot = '/testing/';
+		$result = $this->Html->css('cake.generic');
+		$expected['link']['href'] = 'preg:/\/testing\/css\/cake\.generic\.css\?[0-9]+/';
+		$this->assertTags($result, $expected);
+		$this->Html->webroot = $webroot;
+
+		$webroot = $this->Html->webroot;
+		$this->Html->webroot = '/testing/longer/';
+		$result = $this->Html->css('cake.generic');
+		$expected['link']['href'] = 'preg:/\/testing\/longer\/css\/cake\.generic\.css\?[0-9]+/';
+		$this->assertTags($result, $expected);
+		$this->Html->webroot = $webroot;
+	}
+
+/**
+ * test timestamp enforcement for script tags.
+ *
+ * @return void
+ */
+	function testScriptTimestamping() {
+		$skip = $this->skipIf(!is_writable(JS), 'webroot/js is not Writable, timestamp testing has been skipped');
+		if ($skip) {
+			return;
+		}
+		Configure::write('debug', 2);
+		Configure::write('Asset.timestamp', true);
+
+		touch(WWW_ROOT . 'js' . DS. '__cake_js_test.js');
+		$timestamp = substr(strtotime('now'), 0, 8);
+
+		$result = $this->Html->script('__cake_js_test', array('inline' => true, 'once' => false));
+		$this->assertPattern('/__cake_js_test.js\?' . $timestamp . '[0-9]{2}"/', $result, 'Timestamp value not found %s');
+
+		Configure::write('debug', 0);
+		Configure::write('Asset.timestamp', 'force');
+		$result = $this->Html->script('__cake_js_test', array('inline' => true, 'once' => false));
+		$this->assertPattern('/__cake_js_test.js\?' . $timestamp . '[0-9]{2}"/', $result, 'Timestamp value not found %s');
+		unlink(WWW_ROOT . 'js' . DS. '__cake_js_test.js');
+		Configure::write('Asset.timestamp', false);
+	}
+
+/**
+ * test that scripts added with uses() are only ever included once.
+ * test script tag generation
+ *
+ * @return void
+ */
+	function testScript() {
+		Configure::write('Asset.timestamp', false);
+		$result = $this->Html->script('foo');
+		$expected = array(
+			'script' => array('type' => 'text/javascript', 'src' => 'js/foo.js')
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->script(array('foobar', 'bar'));
+		$expected = array(
+			array('script' => array('type' => 'text/javascript', 'src' => 'js/foobar.js')),
+			'/script',
+			array('script' => array('type' => 'text/javascript', 'src' => 'js/bar.js')),
+			'/script',
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->script('jquery-1.3');
+		$expected = array(
+			'script' => array('type' => 'text/javascript', 'src' => 'js/jquery-1.3.js')
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->script('test.json');
+		$expected = array(
+			'script' => array('type' => 'text/javascript', 'src' => 'js/test.json.js')
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->script('http://example.com/test.json');
+		$expected = array(
+			'script' => array('type' => 'text/javascript', 'src' => 'http://example.com/test.json')
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->script('/plugin/js/jquery-1.3.2.js?someparam=foo');
+		$expected = array(
+			'script' => array('type' => 'text/javascript', 'src' => '/plugin/js/jquery-1.3.2.js?someparam=foo')
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->script('test.json.js?foo=bar');
+		$expected = array(
+			'script' => array('type' => 'text/javascript', 'src' => 'js/test.json.js?foo=bar')
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->script('foo');
+		$this->assertNull($result, 'Script returned upon duplicate inclusion %s');
+
+		$result = $this->Html->script(array('foo', 'bar', 'baz'));
+		$this->assertNoPattern('/foo.js/', $result);
+
+		$result = $this->Html->script('foo', array('inline' => true, 'once' => false));
+		$this->assertNotNull($result);
+
+		$result = $this->Html->script('jquery-1.3.2', array('defer' => true, 'encoding' => 'utf-8'));
+		$expected = array(
+			'script' => array('type' => 'text/javascript', 'src' => 'js/jquery-1.3.2.js', 'defer' => 'defer', 'encoding' => 'utf-8')
+		);
+		$this->assertTags($result, $expected);
+
+		$view =& ClassRegistry::getObject('view');
+		$view =& new HtmlHelperMockView();
+		$view->expectAt(0, 'addScript', array(new PatternExpectation('/script_in_head.js/')));
+		$result = $this->Html->script('script_in_head', array('inline' => false));
+		$this->assertNull($result);
+	}
+
+/**
+ * Test that Asset.filter.js works.
+ *
+ * @return void
+ */
+	function testScriptAssetFilter() {
+		Configure::write('Asset.filter.js', 'js.php');
+	
+		$result = $this->Html->script('jquery-1.3');
+		$expected = array(
+			'script' => array('type' => 'text/javascript', 'src' => 'cjs/jquery-1.3.js')
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->script('//example.com/js/jquery-1.3.js');
+		$expected = array(
+			'script' => array('type' => 'text/javascript', 'src' => '//example.com/js/jquery-1.3.js')
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test a script file in the webroot/theme dir.
+ *
+ * @return void
+ */
+	function testScriptInTheme() {
+		if ($this->skipIf(!is_writable(WWW_ROOT . 'theme'), 'Cannot write to webroot/theme')) {
+			return;
+		}
+		App::import('Core', 'File');
+
+		$testfile = WWW_ROOT . 'theme' . DS . 'test_theme' . DS . 'js' . DS . '__test_js.js';
+		$file =& new File($testfile, true);
+
+		App::build(array(
+			'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS)
+		));
+
+		$this->Html->webroot = '/';
+		$this->Html->theme = 'test_theme';
+		$result = $this->Html->script('__test_js.js');
+		$expected = array(
+			'script' => array('src' => '/theme/test_theme/js/__test_js.js', 'type' => 'text/javascript')
+		);
+		$this->assertTags($result, $expected);
+		App::build();
+	}
+
+/**
+ * test Script block generation
+ *
+ * @return void
+ */
+	function testScriptBlock() {
+		$result = $this->Html->scriptBlock('window.foo = 2;');
+		$expected = array(
+			'script' => array('type' => 'text/javascript'),
+			$this->cDataStart,
+			'window.foo = 2;',
+			$this->cDataEnd,
+			'/script',
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->scriptBlock('window.foo = 2;', array('safe' => false));
+		$expected = array(
+			'script' => array('type' => 'text/javascript'),
+			'window.foo = 2;',
+			'/script',
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->scriptBlock('window.foo = 2;', array('safe' => true));
+		$expected = array(
+			'script' => array('type' => 'text/javascript'),
+			$this->cDataStart,
+			'window.foo = 2;',
+			$this->cDataEnd,
+			'/script',
+		);
+		$this->assertTags($result, $expected);
+
+		$view =& ClassRegistry::getObject('view');
+		$view =& new HtmlHelperMockView();
+		$view->expectAt(0, 'addScript', array(new PatternExpectation('/window\.foo\s\=\s2;/')));
+
+		$result = $this->Html->scriptBlock('window.foo = 2;', array('inline' => false));
+		$this->assertNull($result);
+
+		$result = $this->Html->scriptBlock('window.foo = 2;', array('safe' => false, 'encoding' => 'utf-8'));
+		$expected = array(
+			'script' => array('type' => 'text/javascript', 'encoding' => 'utf-8'),
+			'window.foo = 2;',
+			'/script',
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test script tag output buffering when using scriptStart() and scriptEnd();
+ *
+ * @return void
+ */
+	function testScriptStartAndScriptEnd() {
+		$result = $this->Html->scriptStart(array('safe' => true));
+		$this->assertNull($result);
+		echo 'this is some javascript';
+
+		$result = $this->Html->scriptEnd();
+		$expected = array(
+			'script' => array('type' => 'text/javascript'),
+			$this->cDataStart,
+			'this is some javascript',
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+
+		$result = $this->Html->scriptStart(array('safe' => false));
+		$this->assertNull($result);
+		echo 'this is some javascript';
+
+		$result = $this->Html->scriptEnd();
+		$expected = array(
+			'script' => array('type' => 'text/javascript'),
+			'this is some javascript',
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		ClassRegistry::removeObject('view');
+		$View =& new HtmlHelperMockView();
+
+		$View->expectOnce('addScript');
+		ClassRegistry::addObject('view', $View);
+
+		$result = $this->Html->scriptStart(array('safe' => false, 'inline' => false));
+		$this->assertNull($result);
+		echo 'this is some javascript';
+
+		$result = $this->Html->scriptEnd();
+		$this->assertNull($result);
+	}
+
+/**
+ * testCharsetTag method
+ *
+ * @access public
+ * @return void
+ */
+	function testCharsetTag() {
+		Configure::write('App.encoding', null);
+		$result = $this->Html->charset();
+		$this->assertTags($result, array('meta' => array('http-equiv' => 'Content-Type', 'content' => 'text/html; charset=utf-8')));
+
+		Configure::write('App.encoding', 'ISO-8859-1');
+		$result = $this->Html->charset();
+		$this->assertTags($result, array('meta' => array('http-equiv' => 'Content-Type', 'content' => 'text/html; charset=iso-8859-1')));
+
+		$result = $this->Html->charset('UTF-7');
+		$this->assertTags($result, array('meta' => array('http-equiv' => 'Content-Type', 'content' => 'text/html; charset=UTF-7')));
+	}
+
+/**
+ * testBreadcrumb method
+ *
+ * @access public
+ * @return void
+ */
+	function testBreadcrumb() {
+		$this->Html->addCrumb('First', '#first');
+		$this->Html->addCrumb('Second', '#second');
+		$this->Html->addCrumb('Third', '#third');
+
+		$result = $this->Html->getCrumbs();
+		$expected = array(
+			array('a' => array('href' => '#first')),
+			'First',
+			'/a',
+			'&raquo;',
+			array('a' => array('href' => '#second')),
+			'Second',
+			'/a',
+			'&raquo;',
+			array('a' => array('href' => '#third')),
+			'Third',
+			'/a',
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->getCrumbs(' &gt; ');
+		$expected = array(
+			array('a' => array('href' => '#first')),
+			'First',
+			'/a',
+			' &gt; ',
+			array('a' => array('href' => '#second')),
+			'Second',
+			'/a',
+			' &gt; ',
+			array('a' => array('href' => '#third')),
+			'Third',
+			'/a',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->assertPattern('/^<a[^<>]+>First<\/a> &gt; <a[^<>]+>Second<\/a> &gt; <a[^<>]+>Third<\/a>$/', $result);
+		$this->assertPattern('/<a\s+href=["\']+\#first["\']+[^<>]*>First<\/a>/', $result);
+		$this->assertPattern('/<a\s+href=["\']+\#second["\']+[^<>]*>Second<\/a>/', $result);
+		$this->assertPattern('/<a\s+href=["\']+\#third["\']+[^<>]*>Third<\/a>/', $result);
+		$this->assertNoPattern('/<a[^<>]+[^href]=[^<>]*>/', $result);
+
+		$this->Html->addCrumb('Fourth', null);
+
+		$result = $this->Html->getCrumbs();
+		$expected = array(
+			array('a' => array('href' => '#first')),
+			'First',
+			'/a',
+			'&raquo;',
+			array('a' => array('href' => '#second')),
+			'Second',
+			'/a',
+			'&raquo;',
+			array('a' => array('href' => '#third')),
+			'Third',
+			'/a',
+			'&raquo;',
+			'Fourth'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testNestedList method
+ *
+ * @access public
+ * @return void
+ */
+	function testNestedList() {
+		$list = array(
+			'Item 1',
+			'Item 2' => array(
+				'Item 2.1'
+			),
+			'Item 3',
+			'Item 4' => array(
+				'Item 4.1',
+				'Item 4.2',
+				'Item 4.3' => array(
+					'Item 4.3.1',
+					'Item 4.3.2'
+				)
+			),
+			'Item 5' => array(
+				'Item 5.1',
+				'Item 5.2'
+			)
+		);
+
+		$result = $this->Html->nestedList($list);
+		$expected = array(
+			'<ul',
+			'<li', 'Item 1', '/li',
+			'<li', 'Item 2',
+			'<ul', '<li', 'Item 2.1', '/li', '/ul',
+			'/li',
+			'<li', 'Item 3', '/li',
+			'<li', 'Item 4',
+			'<ul',
+			'<li', 'Item 4.1', '/li',
+			'<li', 'Item 4.2', '/li',
+			'<li', 'Item 4.3',
+			'<ul',
+			'<li', 'Item 4.3.1', '/li',
+			'<li', 'Item 4.3.2', '/li',
+			'/ul',
+			'/li',
+			'/ul',
+			'/li',
+			'<li', 'Item 5',
+			'<ul',
+			'<li', 'Item 5.1', '/li',
+			'<li', 'Item 5.2', '/li',
+			'/ul',
+			'/li',
+			'/ul'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->nestedList($list, null);
+		$expected = array(
+			'<ul',
+			'<li', 'Item 1', '/li',
+			'<li', 'Item 2',
+			'<ul', '<li', 'Item 2.1', '/li', '/ul',
+			'/li',
+			'<li', 'Item 3', '/li',
+			'<li', 'Item 4',
+			'<ul',
+			'<li', 'Item 4.1', '/li',
+			'<li', 'Item 4.2', '/li',
+			'<li', 'Item 4.3',
+			'<ul',
+			'<li', 'Item 4.3.1', '/li',
+			'<li', 'Item 4.3.2', '/li',
+			'/ul',
+			'/li',
+			'/ul',
+			'/li',
+			'<li', 'Item 5',
+			'<ul',
+			'<li', 'Item 5.1', '/li',
+			'<li', 'Item 5.2', '/li',
+			'/ul',
+			'/li',
+			'/ul'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->nestedList($list, array(), array(), 'ol');
+		$expected = array(
+			'<ol',
+			'<li', 'Item 1', '/li',
+			'<li', 'Item 2',
+			'<ol', '<li', 'Item 2.1', '/li', '/ol',
+			'/li',
+			'<li', 'Item 3', '/li',
+			'<li', 'Item 4',
+			'<ol',
+			'<li', 'Item 4.1', '/li',
+			'<li', 'Item 4.2', '/li',
+			'<li', 'Item 4.3',
+			'<ol',
+			'<li', 'Item 4.3.1', '/li',
+			'<li', 'Item 4.3.2', '/li',
+			'/ol',
+			'/li',
+			'/ol',
+			'/li',
+			'<li', 'Item 5',
+			'<ol',
+			'<li', 'Item 5.1', '/li',
+			'<li', 'Item 5.2', '/li',
+			'/ol',
+			'/li',
+			'/ol'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->nestedList($list, 'ol');
+		$expected = array(
+			'<ol',
+			'<li', 'Item 1', '/li',
+			'<li', 'Item 2',
+			'<ol', '<li', 'Item 2.1', '/li', '/ol',
+			'/li',
+			'<li', 'Item 3', '/li',
+			'<li', 'Item 4',
+			'<ol',
+			'<li', 'Item 4.1', '/li',
+			'<li', 'Item 4.2', '/li',
+			'<li', 'Item 4.3',
+			'<ol',
+			'<li', 'Item 4.3.1', '/li',
+			'<li', 'Item 4.3.2', '/li',
+			'/ol',
+			'/li',
+			'/ol',
+			'/li',
+			'<li', 'Item 5',
+			'<ol',
+			'<li', 'Item 5.1', '/li',
+			'<li', 'Item 5.2', '/li',
+			'/ol',
+			'/li',
+			'/ol'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->nestedList($list, array('class'=>'list'));
+		$expected = array(
+			array('ul' => array('class' => 'list')),
+			'<li', 'Item 1', '/li',
+			'<li', 'Item 2',
+			array('ul' => array('class' => 'list')), '<li', 'Item 2.1', '/li', '/ul',
+			'/li',
+			'<li', 'Item 3', '/li',
+			'<li', 'Item 4',
+			array('ul' => array('class' => 'list')),
+			'<li', 'Item 4.1', '/li',
+			'<li', 'Item 4.2', '/li',
+			'<li', 'Item 4.3',
+			array('ul' => array('class' => 'list')),
+			'<li', 'Item 4.3.1', '/li',
+			'<li', 'Item 4.3.2', '/li',
+			'/ul',
+			'/li',
+			'/ul',
+			'/li',
+			'<li', 'Item 5',
+			array('ul' => array('class' => 'list')),
+			'<li', 'Item 5.1', '/li',
+			'<li', 'Item 5.2', '/li',
+			'/ul',
+			'/li',
+			'/ul'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->nestedList($list, array(), array('class' => 'item'));
+		$expected = array(
+			'<ul',
+			array('li' => array('class' => 'item')), 'Item 1', '/li',
+			array('li' => array('class' => 'item')), 'Item 2',
+			'<ul', array('li' => array('class' => 'item')), 'Item 2.1', '/li', '/ul',
+			'/li',
+			array('li' => array('class' => 'item')), 'Item 3', '/li',
+			array('li' => array('class' => 'item')), 'Item 4',
+			'<ul',
+			array('li' => array('class' => 'item')), 'Item 4.1', '/li',
+			array('li' => array('class' => 'item')), 'Item 4.2', '/li',
+			array('li' => array('class' => 'item')), 'Item 4.3',
+			'<ul',
+			array('li' => array('class' => 'item')), 'Item 4.3.1', '/li',
+			array('li' => array('class' => 'item')), 'Item 4.3.2', '/li',
+			'/ul',
+			'/li',
+			'/ul',
+			'/li',
+			array('li' => array('class' => 'item')), 'Item 5',
+			'<ul',
+			array('li' => array('class' => 'item')), 'Item 5.1', '/li',
+			array('li' => array('class' => 'item')), 'Item 5.2', '/li',
+			'/ul',
+			'/li',
+			'/ul'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->nestedList($list, array(), array('even' => 'even', 'odd' => 'odd'));
+		$expected = array(
+			'<ul',
+			array('li' => array('class' => 'odd')), 'Item 1', '/li',
+			array('li' => array('class' => 'even')), 'Item 2',
+			'<ul', array('li' => array('class' => 'odd')), 'Item 2.1', '/li', '/ul',
+			'/li',
+			array('li' => array('class' => 'odd')), 'Item 3', '/li',
+			array('li' => array('class' => 'even')), 'Item 4',
+			'<ul',
+			array('li' => array('class' => 'odd')), 'Item 4.1', '/li',
+			array('li' => array('class' => 'even')), 'Item 4.2', '/li',
+			array('li' => array('class' => 'odd')), 'Item 4.3',
+			'<ul',
+			array('li' => array('class' => 'odd')), 'Item 4.3.1', '/li',
+			array('li' => array('class' => 'even')), 'Item 4.3.2', '/li',
+			'/ul',
+			'/li',
+			'/ul',
+			'/li',
+			array('li' => array('class' => 'odd')), 'Item 5',
+			'<ul',
+			array('li' => array('class' => 'odd')), 'Item 5.1', '/li',
+			array('li' => array('class' => 'even')), 'Item 5.2', '/li',
+			'/ul',
+			'/li',
+			'/ul'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->nestedList($list, array('class'=>'list'), array('class' => 'item'));
+		$expected = array(
+			array('ul' => array('class' => 'list')),
+			array('li' => array('class' => 'item')), 'Item 1', '/li',
+			array('li' => array('class' => 'item')), 'Item 2',
+			array('ul' => array('class' => 'list')), array('li' => array('class' => 'item')), 'Item 2.1', '/li', '/ul',
+			'/li',
+			array('li' => array('class' => 'item')), 'Item 3', '/li',
+			array('li' => array('class' => 'item')), 'Item 4',
+			array('ul' => array('class' => 'list')),
+			array('li' => array('class' => 'item')), 'Item 4.1', '/li',
+			array('li' => array('class' => 'item')), 'Item 4.2', '/li',
+			array('li' => array('class' => 'item')), 'Item 4.3',
+			array('ul' => array('class' => 'list')),
+			array('li' => array('class' => 'item')), 'Item 4.3.1', '/li',
+			array('li' => array('class' => 'item')), 'Item 4.3.2', '/li',
+			'/ul',
+			'/li',
+			'/ul',
+			'/li',
+			array('li' => array('class' => 'item')), 'Item 5',
+			array('ul' => array('class' => 'list')),
+			array('li' => array('class' => 'item')), 'Item 5.1', '/li',
+			array('li' => array('class' => 'item')), 'Item 5.2', '/li',
+			'/ul',
+			'/li',
+			'/ul'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testMeta method
+ *
+ * @access public
+ * @return void
+ */
+	function testMeta() {
+		$result = $this->Html->meta('this is an rss feed', array('controller' => 'posts', 'ext' => 'rss'));
+		$this->assertTags($result, array('link' => array('href' => 'preg:/.*\/posts\.rss/', 'type' => 'application/rss+xml', 'rel' => 'alternate', 'title' => 'this is an rss feed')));
+
+		$result = $this->Html->meta('rss', array('controller' => 'posts', 'ext' => 'rss'), array('title' => 'this is an rss feed'));
+		$this->assertTags($result, array('link' => array('href' => 'preg:/.*\/posts\.rss/', 'type' => 'application/rss+xml', 'rel' => 'alternate', 'title' => 'this is an rss feed')));
+
+		$result = $this->Html->meta('atom', array('controller' => 'posts', 'ext' => 'xml'));
+		$this->assertTags($result, array('link' => array('href' => 'preg:/.*\/posts\.xml/', 'type' => 'application/atom+xml', 'title' => 'atom')));
+
+		$result = $this->Html->meta('non-existing');
+		$this->assertTags($result, array('<meta'));
+
+		$result = $this->Html->meta('non-existing', '/posts.xpp');
+		$this->assertTags($result, array('link' => array('href' => 'preg:/.*\/posts\.xpp/', 'type' => 'application/rss+xml', 'rel' => 'alternate', 'title' => 'non-existing')));
+
+		$result = $this->Html->meta('non-existing', '/posts.xpp', array('type' => 'atom'));
+		$this->assertTags($result, array('link' => array('href' => 'preg:/.*\/posts\.xpp/', 'type' => 'application/atom+xml', 'title' => 'non-existing')));
+
+		$result = $this->Html->meta('atom', array('controller' => 'posts', 'ext' => 'xml'), array('link' => '/articles.rss'));
+		$this->assertTags($result, array('link' => array('href' => 'preg:/.*\/articles\.rss/', 'type' => 'application/atom+xml', 'title' => 'atom')));
+
+		$result = $this->Html->meta(array('link' => 'favicon.ico', 'rel' => 'icon'));
+		$expected = array(
+			'link' => array('href' => 'preg:/.*favicon\.ico/', 'rel' => 'icon'),
+			array('link' => array('href' => 'preg:/.*favicon\.ico/', 'rel' => 'shortcut icon'))
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->meta('icon', 'favicon.ico');
+		$expected = array(
+			'link' => array('href' => 'preg:/.*favicon\.ico/', 'type' => 'image/x-icon', 'rel' => 'icon'),
+			array('link' => array('href' => 'preg:/.*favicon\.ico/', 'type' => 'image/x-icon', 'rel' => 'shortcut icon'))
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Html->meta('keywords', 'these, are, some, meta, keywords');
+		$this->assertTags($result, array('meta' => array('name' => 'keywords', 'content' => 'these, are, some, meta, keywords')));
+		$this->assertPattern('/\s+\/>$/', $result);
+
+
+		$result = $this->Html->meta('description', 'this is the meta description');
+		$this->assertTags($result, array('meta' => array('name' => 'description', 'content' => 'this is the meta description')));
+
+		$result = $this->Html->meta(array('name' => 'ROBOTS', 'content' => 'ALL'));
+		$this->assertTags($result, array('meta' => array('name' => 'ROBOTS', 'content' => 'ALL')));
+
+		$this->assertNull($this->Html->meta(array('name' => 'ROBOTS', 'content' => 'ALL'), null, array('inline' => false)));
+		$view =& ClassRegistry::getObject('view');
+		$result = $view->__scripts[0];
+		$this->assertTags($result, array('meta' => array('name' => 'ROBOTS', 'content' => 'ALL')));
+	}
+
+/**
+ * testTableHeaders method
+ *
+ * @access public
+ * @return void
+ */
+	function testTableHeaders() {
+		$result = $this->Html->tableHeaders(array('ID', 'Name', 'Date'));
+		$expected = array('<tr', '<th', 'ID', '/th', '<th', 'Name', '/th', '<th', 'Date', '/th', '/tr');
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testTableCells method
+ *
+ * @access public
+ * @return void
+ */
+	function testTableCells() {
+		$tr = array(
+			'td content 1',
+			array('td content 2', array("width" => "100px")),
+			array('td content 3', "width=100px")
+		);
+		$result = $this->Html->tableCells($tr);
+		$expected = array(
+			'<tr',
+			'<td', 'td content 1', '/td',
+			array('td' => array('width' => '100px')), 'td content 2', '/td',
+			array('td' => array('width' => 'preg:/100px/')), 'td content 3', '/td',
+			'/tr'
+		);
+		$this->assertTags($result, $expected);
+
+		$tr = array('td content 1', 'td content 2', 'td content 3');
+		$result = $this->Html->tableCells($tr, null, null, true);
+		$expected = array(
+			'<tr',
+			array('td' => array('class' => 'column-1')), 'td content 1', '/td',
+			array('td' => array('class' => 'column-2')), 'td content 2', '/td',
+			array('td' => array('class' => 'column-3')), 'td content 3', '/td',
+			'/tr'
+		);
+		$this->assertTags($result, $expected);
+
+		$tr = array('td content 1', 'td content 2', 'td content 3');
+		$result = $this->Html->tableCells($tr, true);
+		$expected = array(
+			'<tr',
+			array('td' => array('class' => 'column-1')), 'td content 1', '/td',
+			array('td' => array('class' => 'column-2')), 'td content 2', '/td',
+			array('td' => array('class' => 'column-3')), 'td content 3', '/td',
+			'/tr'
+		);
+		$this->assertTags($result, $expected);
+
+		$tr = array(
+			array('td content 1', 'td content 2', 'td content 3'),
+			array('td content 1', 'td content 2', 'td content 3'),
+			array('td content 1', 'td content 2', 'td content 3')
+		);
+		$result = $this->Html->tableCells($tr, array('class' => 'odd'), array('class' => 'even'));
+		$expected = "<tr class=\"even\"><td>td content 1</td> <td>td content 2</td> <td>td content 3</td></tr>\n<tr class=\"odd\"><td>td content 1</td> <td>td content 2</td> <td>td content 3</td></tr>\n<tr class=\"even\"><td>td content 1</td> <td>td content 2</td> <td>td content 3</td></tr>";
+		$this->assertEqual($result, $expected);
+
+		$tr = array(
+			array('td content 1', 'td content 2', 'td content 3'),
+			array('td content 1', 'td content 2', 'td content 3'),
+			array('td content 1', 'td content 2', 'td content 3'),
+			array('td content 1', 'td content 2', 'td content 3')
+		);
+		$result = $this->Html->tableCells($tr, array('class' => 'odd'), array('class' => 'even'));
+		$expected = "<tr class=\"odd\"><td>td content 1</td> <td>td content 2</td> <td>td content 3</td></tr>\n<tr class=\"even\"><td>td content 1</td> <td>td content 2</td> <td>td content 3</td></tr>\n<tr class=\"odd\"><td>td content 1</td> <td>td content 2</td> <td>td content 3</td></tr>\n<tr class=\"even\"><td>td content 1</td> <td>td content 2</td> <td>td content 3</td></tr>";
+		$this->assertEqual($result, $expected);
+
+		$tr = array(
+			array('td content 1', 'td content 2', 'td content 3'),
+			array('td content 1', 'td content 2', 'td content 3'),
+			array('td content 1', 'td content 2', 'td content 3')
+		);
+		$this->Html->tableCells($tr, array('class' => 'odd'), array('class' => 'even'));
+		$result = $this->Html->tableCells($tr, array('class' => 'odd'), array('class' => 'even'), false, false);
+		$expected = "<tr class=\"odd\"><td>td content 1</td> <td>td content 2</td> <td>td content 3</td></tr>\n<tr class=\"even\"><td>td content 1</td> <td>td content 2</td> <td>td content 3</td></tr>\n<tr class=\"odd\"><td>td content 1</td> <td>td content 2</td> <td>td content 3</td></tr>";
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testTag method
+ *
+ * @access public
+ * @return void
+ */
+	function testTag() {
+		$result = $this->Html->tag('div');
+		$this->assertTags($result, '<div');
+
+		$result = $this->Html->tag('div', 'text');
+		$this->assertTags($result, '<div', 'text', '/div');
+
+		$result = $this->Html->tag('div', '<text>', 'class-name');
+		$this->assertTags($result, array('div' => array('class' => 'class-name'), 'preg:/<text>/', '/div'));
+
+		$result = $this->Html->tag('div', '<text>', array('class' => 'class-name', 'escape' => true));
+		$this->assertTags($result, array('div' => array('class' => 'class-name'), '&lt;text&gt;', '/div'));
+	}
+
+/**
+ * testDiv method
+ *
+ * @access public
+ * @return void
+ */
+	function testDiv() {
+		$result = $this->Html->div('class-name');
+		$this->assertTags($result, array('div' => array('class' => 'class-name')));
+
+		$result = $this->Html->div('class-name', 'text');
+		$this->assertTags($result, array('div' => array('class' => 'class-name'), 'text', '/div'));
+
+		$result = $this->Html->div('class-name', '<text>', array('escape' => true));
+		$this->assertTags($result, array('div' => array('class' => 'class-name'), '&lt;text&gt;', '/div'));
+	}
+
+/**
+ * testPara method
+ *
+ * @access public
+ * @return void
+ */
+	function testPara() {
+		$result = $this->Html->para('class-name', '');
+		$this->assertTags($result, array('p' => array('class' => 'class-name')));
+
+		$result = $this->Html->para('class-name', 'text');
+		$this->assertTags($result, array('p' => array('class' => 'class-name'), 'text', '/p'));
+
+		$result = $this->Html->para('class-name', '<text>', array('escape' => true));
+		$this->assertTags($result, array('p' => array('class' => 'class-name'), '&lt;text&gt;', '/p'));
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/view/helpers/javascript.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/view/helpers/javascript.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/view/helpers/javascript.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,911 @@
+<?php
+/**
+ * JavascriptHelperTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', array('Controller', 'View', 'ClassRegistry', 'View'));
+App::import('Helper', array('Javascript', 'Html', 'Form'));
+
+/**
+ * TheJsTestController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class TheJsTestController extends Controller {
+
+/**
+ * name property
+ *
+ * @var string 'TheTest'
+ * @access public
+ */
+	var $name = 'TheTest';
+
+/**
+ * uses property
+ *
+ * @var mixed null
+ * @access public
+ */
+	var $uses = null;
+}
+
+/**
+ * TheView class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class TheView extends View {
+
+/**
+ * scripts method
+ *
+ * @access public
+ * @return void
+ */
+	function scripts() {
+		return $this->__scripts;
+	}
+}
+
+/**
+ * TestJavascriptObject class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class TestJavascriptObject {
+
+/**
+ * property1 property
+ *
+ * @var string 'value1'
+ * @access public
+ */
+	var $property1 = 'value1';
+
+/**
+ * property2 property
+ *
+ * @var int 2
+ * @access public
+ */
+	var $property2 = 2;
+}
+
+/**
+ * JavascriptTest class
+ *
+ * @package       test_suite
+ * @subpackage    test_suite.cases.libs
+ * @since         CakePHP Test Suite v 1.0.0.0
+ */
+class JavascriptTest extends CakeTestCase {
+
+/**
+ * Regexp for CDATA start block
+ *
+ * @var string
+ */
+	var $cDataStart = 'preg:/^\/\/<!\[CDATA\[[\n\r]*/';
+
+/**
+ * Regexp for CDATA end block
+ *
+ * @var string
+ */
+	var $cDataEnd = 'preg:/[^\]]*\]\]\>[\s\r\n]*/';
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function startTest() {
+		$this->Javascript =& new JavascriptHelper();
+		$this->Javascript->Html =& new HtmlHelper();
+		$this->Javascript->Form =& new FormHelper();
+		$this->View =& new TheView(new TheJsTestController());
+		ClassRegistry::addObject('view', $this->View);
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function endTest() {
+		unset($this->Javascript->Html);
+		unset($this->Javascript->Form);
+		unset($this->Javascript);
+		ClassRegistry::removeObject('view');
+		unset($this->View);
+	}
+
+/**
+ * testConstruct method
+ *
+ * @access public
+ * @return void
+ */
+	function testConstruct() {
+		$Javascript =& new JavascriptHelper(array('safe'));
+		$this->assertTrue($Javascript->safe);
+
+		$Javascript =& new JavascriptHelper(array('safe' => false));
+		$this->assertFalse($Javascript->safe);
+	}
+
+/**
+ * testLink method
+ *
+ * @access public
+ * @return void
+ */
+	function testLink() {
+		Configure::write('Asset.timestamp', false);
+		$result = $this->Javascript->link('script.js');
+		$expected = '<script type="text/javascript" src="js/script.js"></script>';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->link('script');
+		$expected = '<script type="text/javascript" src="js/script.js"></script>';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->link('scriptaculous.js?load=effects');
+		$expected = '<script type="text/javascript" src="js/scriptaculous.js?load=effects"></script>';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->link('some.json.libary');
+		$expected = '<script type="text/javascript" src="js/some.json.libary.js"></script>';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->link('jquery-1.1.2');
+		$expected = '<script type="text/javascript" src="js/jquery-1.1.2.js"></script>';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->link('jquery-1.1.2');
+		$expected = '<script type="text/javascript" src="js/jquery-1.1.2.js"></script>';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->link('/plugin/js/jquery-1.1.2');
+		$expected = '<script type="text/javascript" src="/plugin/js/jquery-1.1.2.js"></script>';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->link('/some_other_path/myfile.1.2.2.min.js');
+		$expected = '<script type="text/javascript" src="/some_other_path/myfile.1.2.2.min.js"></script>';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->link('some_other_path/myfile.1.2.2.min.js');
+		$expected = '<script type="text/javascript" src="js/some_other_path/myfile.1.2.2.min.js"></script>';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->link('some_other_path/myfile.1.2.2.min');
+		$expected = '<script type="text/javascript" src="js/some_other_path/myfile.1.2.2.min.js"></script>';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->link('http://example.com/jquery.js');
+		$expected = '<script type="text/javascript" src="http://example.com/jquery.js"></script>';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->link(array('prototype.js', 'scriptaculous.js'));
+		$this->assertPattern('/^\s*<script\s+type="text\/javascript"\s+src=".*js\/prototype\.js"[^<>]*><\/script>/', $result);
+		$this->assertPattern('/<\/script>\s*<script[^<>]+>/', $result);
+		$this->assertPattern('/<script\s+type="text\/javascript"\s+src=".*js\/scriptaculous\.js"[^<>]*><\/script>\s*$/', $result);
+
+		$result = $this->Javascript->link('jquery-1.1.2', false);
+		$resultScripts = $this->View->scripts();
+		reset($resultScripts);
+		$expected = '<script type="text/javascript" src="js/jquery-1.1.2.js"></script>';
+		$this->assertNull($result);
+		$this->assertEqual(count($resultScripts), 1);
+		$this->assertEqual(current($resultScripts), $expected);
+	}
+
+/**
+ * testFilteringAndTimestamping method
+ *
+ * @access public
+ * @return void
+ */
+	function testFilteringAndTimestamping() {
+		if ($this->skipIf(!is_writable(JS), 'JavaScript directory not writable, skipping JS asset timestamp tests. %s')) {
+			return;
+		}
+
+		cache(str_replace(WWW_ROOT, '', JS) . '__cake_js_test.js', 'alert("test")', '+999 days', 'public');
+		$timestamp = substr(strtotime('now'), 0, 8);
+
+		Configure::write('Asset.timestamp', true);
+		$result = $this->Javascript->link('__cake_js_test');
+		$this->assertPattern('/^<script[^<>]+src=".*js\/__cake_js_test\.js\?' . $timestamp . '[0-9]{2}"[^<>]*>/', $result);
+
+		$debug = Configure::read('debug');
+		Configure::write('debug', 0);
+		$result = $this->Javascript->link('__cake_js_test');
+		$expected = '<script type="text/javascript" src="js/__cake_js_test.js"></script>';
+		$this->assertEqual($result, $expected);
+
+		Configure::write('Asset.timestamp', 'force');
+		$result = $this->Javascript->link('__cake_js_test');
+		$this->assertPattern('/^<script[^<>]+src=".*js\/__cake_js_test.js\?' . $timestamp . '[0-9]{2}"[^<>]*>/', $result);
+
+		Configure::write('debug', $debug);
+		Configure::write('Asset.timestamp', false);
+
+		$old = Configure::read('Asset.filter.js');
+
+		Configure::write('Asset.filter.js', 'js.php');
+		$result = $this->Javascript->link('__cake_js_test');
+		$this->assertPattern('/^<script[^<>]+src=".*cjs\/__cake_js_test\.js"[^<>]*>/', $result);
+
+		Configure::write('Asset.filter.js', true);
+		$result = $this->Javascript->link('jquery-1.1.2');
+		$expected = '<script type="text/javascript" src="cjs/jquery-1.1.2.js"></script>';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->link('folderjs/jquery-1.1.2');
+		$expected = '<script type="text/javascript" src="cjs/folderjs/jquery-1.1.2.js"></script>';
+		$this->assertEqual($result, $expected);
+
+		if ($old === null) {
+			Configure::delete('Asset.filter.js');
+		}
+
+		$debug = Configure::read('debug');
+		$webroot = $this->Javascript->webroot;
+
+		Configure::write('debug', 0);
+		Configure::write('Asset.timestamp', 'force');
+
+		$this->Javascript->webroot = '/testing/';
+		$result = $this->Javascript->link('__cake_js_test');
+		$this->assertPattern('/^<script[^<>]+src="\/testing\/js\/__cake_js_test\.js\?\d+"[^<>]*>/', $result);
+
+		$this->Javascript->webroot = '/testing/longer/';
+		$result = $this->Javascript->link('__cake_js_test');
+		$this->assertPattern('/^<script[^<>]+src="\/testing\/longer\/js\/__cake_js_test\.js\?\d+"[^<>]*>/', $result);
+
+		$this->Javascript->webroot = $webroot;
+		Configure::write('debug', $debug);
+
+		unlink(JS . '__cake_js_test.js');
+	}
+
+/**
+ * testValue method
+ *
+ * @access public
+ * @return void
+ */
+	function testValue() {
+		$result = $this->Javascript->value(array('title' => 'New thing', 'indexes' => array(5, 6, 7, 8)));
+		$expected = '{"title":"New thing","indexes":[5,6,7,8]}';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->value(null);
+		$this->assertEqual($result, 'null');
+
+		$result = $this->Javascript->value(true);
+		$this->assertEqual($result, 'true');
+
+		$result = $this->Javascript->value(false);
+		$this->assertEqual($result, 'false');
+
+		$result = $this->Javascript->value(5);
+		$this->assertEqual($result, '5');
+
+		$result = $this->Javascript->value(floatval(5.3));
+		$this->assertPattern('/^5.3[0]+$/', $result);
+
+		$result = $this->Javascript->value('');
+		$expected = '""';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->value('CakePHP' . "\n" . 'Rapid Development Framework');
+		$expected = '"CakePHP\\nRapid Development Framework"';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->value('CakePHP' . "\r\n" . 'Rapid Development Framework' . "\r" . 'For PHP');
+		$expected = '"CakePHP\\nRapid Development Framework\\nFor PHP"';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->value('CakePHP: "Rapid Development Framework"');
+		$expected = '"CakePHP: \\"Rapid Development Framework\\""';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->value('CakePHP: \'Rapid Development Framework\'');
+		$expected = '"CakePHP: \\\'Rapid Development Framework\\\'"';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testObjectGeneration method
+ *
+ * @access public
+ * @return void
+ */
+	function testObjectGeneration() {
+		$object = array('title' => 'New thing', 'indexes' => array(5, 6, 7, 8));
+		$result = $this->Javascript->object($object);
+		$expected = '{"title":"New thing","indexes":[5,6,7,8]}';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->object(array('default' => 0));
+		$expected = '{"default":0}';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->object(array(
+			'2007' => array(
+				'Spring' => array('1' => array('id' => 1, 'name' => 'Josh'), '2' => array('id' => 2, 'name' => 'Becky')),
+				'Fall' => array('1' => array('id' => 1, 'name' => 'Josh'), '2' => array('id' => 2, 'name' => 'Becky'))
+			), '2006' => array(
+				'Spring' => array('1' => array('id' => 1, 'name' => 'Josh'), '2' => array('id' => 2, 'name' => 'Becky')),
+				'Fall' => array('1' => array('id' => 1, 'name' => 'Josh'), '2' => array('id' => 2, 'name' => 'Becky')
+			))
+		));
+		$expected = '{"2007":{"Spring":{"1":{"id":1,"name":"Josh"},"2":{"id":2,"name":"Becky"}},"Fall":{"1":{"id":1,"name":"Josh"},"2":{"id":2,"name":"Becky"}}},"2006":{"Spring":{"1":{"id":1,"name":"Josh"},"2":{"id":2,"name":"Becky"}},"Fall":{"1":{"id":1,"name":"Josh"},"2":{"id":2,"name":"Becky"}}}}';
+		$this->assertEqual($result, $expected);
+
+		if (ini_get('precision') >= 12) {
+			$number = 3.141592653589;
+			if (!$this->Javascript->useNative) {
+				$number = sprintf("%.11f", $number);
+			}
+
+			$result = $this->Javascript->object(array('Object' => array(true, false, 1, '02101', 0, -1, 3.141592653589, "1")));
+			$expected = '{"Object":[true,false,1,"02101",0,-1,' . $number . ',"1"]}';
+			$this->assertEqual($result, $expected);
+
+			$result = $this->Javascript->object(array('Object' => array(true => true, false, -3.141592653589, -10)));
+			$expected = '{"Object":{"1":true,"2":false,"3":' . (-1 * $number) . ',"4":-10}}';
+			$this->assertEqual($result, $expected);
+		}
+
+		$result = $this->Javascript->object(new TestJavascriptObject());
+		$expected = '{"property1":"value1","property2":2}';
+		$this->assertEqual($result, $expected);
+
+		$object = array('title' => 'New thing', 'indexes' => array(5, 6, 7, 8));
+		$result = $this->Javascript->object($object, array('block' => true));
+		$expected = array(
+			'script' => array('type' => 'text/javascript'),
+			$this->cDataStart,
+			'{"title":"New thing","indexes":[5,6,7,8]}',
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$object = array('title' => 'New thing', 'indexes' => array(5, 6, 7, 8), 'object' => array('inner' => array('value' => 1)));
+		$result = $this->Javascript->object($object);
+		$expected = '{"title":"New thing","indexes":[5,6,7,8],"object":{"inner":{"value":1}}}';
+		$this->assertEqual($result, $expected);
+
+		foreach (array('true' => true, 'false' => false, 'null' => null) as $expected => $data) {
+			$result = $this->Javascript->object($data);
+			$this->assertEqual($result, $expected);
+		}
+		
+		$object = array('title' => 'New thing', 'indexes' => array(5, 6, 7, 8), 'object' => array('inner' => array('value' => 1)));
+		$result = $this->Javascript->object($object, array('prefix' => 'PREFIX', 'postfix' => 'POSTFIX'));
+		$this->assertPattern('/^PREFIX/', $result);
+		$this->assertPattern('/POSTFIX$/', $result);
+		$this->assertNoPattern('/.PREFIX./', $result);
+		$this->assertNoPattern('/.POSTFIX./', $result);
+
+		if ($this->Javascript->useNative) {
+			$this->Javascript->useNative = false;
+			$this->testObjectGeneration();
+			$this->Javascript->useNative = true;
+		}
+	}
+
+/**
+ * testObjectNonNative method
+ *
+ * @access public
+ * @return void
+ */
+	function testObjectNonNative() {
+		$oldNative = $this->Javascript->useNative;
+		$this->Javascript->useNative = false;
+
+		$object = array(
+			'Object' => array(
+				'key1' => 'val1',
+				'key2' => 'val2',
+				'key3' => 'val3'
+			)
+		);
+
+		$expected = '{"Object":{"key1":val1,"key2":"val2","key3":val3}}';
+		$result = $this->Javascript->object($object, array('quoteKeys' => false, 'stringKeys' => array('key1', 'key3')));
+		$this->assertEqual($result, $expected);
+
+		$expected = '{?Object?:{?key1?:"val1",?key2?:"val2",?key3?:"val3"}}';
+		$result = $this->Javascript->object($object, array('q' => '?'));
+		$this->assertEqual($result, $expected);
+		
+		$expected = '{?Object?:{?key1?:"val1",?key2?:val2,?key3?:"val3"}}';
+		$result = $this->Javascript->object($object, array(
+			'q' => '?', 'stringKeys' => array('key3', 'key1')
+		));
+		$this->assertEqual($result, $expected);
+
+		$expected = '{?Object?:{?key1?:val1,?key2?:"val2",?key3?:val3}}';
+		$result = $this->Javascript->object($object, array(
+			'q' => '?', 'stringKeys' => array('key3', 'key1'), 'quoteKeys' => false
+		));
+		$this->assertEqual($result, $expected);
+
+		$this->Javascript->useNative = $oldNative;
+	}
+
+/**
+ * testScriptBlock method
+ *
+ * @access public
+ * @return void
+ */
+	function testScriptBlock() {
+		$result = $this->Javascript->codeBlock('something');
+		$expected = array(
+			'script' => array('type' => 'text/javascript'),
+			$this->cDataStart,
+			'something',
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Javascript->codeBlock('something', array('allowCache' => true, 'safe' => false));
+		$expected = array(
+			'script' => array('type' => 'text/javascript'),
+			'something',
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Javascript->codeBlock('something', array('allowCache' => false, 'safe' => false));
+		$expected = array(
+			'script' => array('type' => 'text/javascript'),
+			'something',
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Javascript->codeBlock('something', true);
+		$expected = array(
+			'script' => array('type' => 'text/javascript'),
+			$this->cDataStart,
+			'something',
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Javascript->codeBlock('something', false);
+		$expected = array(
+			'script' => array('type' => 'text/javascript'),
+			$this->cDataStart,
+			'something',
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Javascript->codeBlock('something', array('safe' => false));
+		$expected = array(
+			'script' => array('type' => 'text/javascript'),
+			'something',
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Javascript->codeBlock('something', array('safe' => true));
+		$expected = array(
+			'script' => array('type' => 'text/javascript'),
+			$this->cDataStart,
+			'something',
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Javascript->codeBlock(null, array('safe' => true, 'allowCache' => false));
+		$this->assertNull($result);
+		echo 'this is some javascript';
+
+		$result = $this->Javascript->blockEnd();
+		$expected = array(
+			'script' => array('type' => 'text/javascript'),
+			$this->cDataStart,
+			'this is some javascript',
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Javascript->codeBlock();
+		$this->assertNull($result);
+		echo "alert('hey');";
+		$result = $this->Javascript->blockEnd();
+
+		$expected = array(
+			'script' => array('type' => 'text/javascript'),
+			$this->cDataStart,
+			"alert('hey');",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Javascript->cacheEvents(false, true);
+		$this->assertFalse($this->Javascript->inBlock);
+
+		$result = $this->Javascript->codeBlock();
+		$this->assertIdentical($result, null);
+		$this->assertTrue($this->Javascript->inBlock);
+		echo 'alert("this is a buffered script");';
+
+		$result = $this->Javascript->blockEnd();
+		$this->assertIdentical($result, null);
+		$this->assertFalse($this->Javascript->inBlock);
+
+		$result = $this->Javascript->getCache();
+		$this->assertEqual('alert("this is a buffered script");', $result);
+	}
+
+/**
+ * testOutOfLineScriptWriting method
+ *
+ * @access public
+ * @return void
+ */
+	function testOutOfLineScriptWriting() {
+		echo $this->Javascript->codeBlock('$(document).ready(function() { });', array('inline' => false));
+
+		$this->Javascript->codeBlock(null, array('inline' => false));
+		echo '$(function(){ });';
+		$this->Javascript->blockEnd();
+		$script = $this->View->scripts();
+
+		$this->assertEqual(count($script), 2);
+		$this->assertPattern('/' . preg_quote('$(document).ready(function() { });', '/') . '/', $script[0]);
+		$this->assertPattern('/' . preg_quote('$(function(){ });', '/') . '/', $script[1]);
+	}
+
+/**
+ * testEvent method
+ *
+ * @access public
+ * @return void
+ */
+	function testEvent() {
+		$result = $this->Javascript->event('myId', 'click', 'something();');
+		$expected = array(
+			'script' => array('type' => 'text/javascript'),
+			$this->cDataStart,
+			"Event.observe($('myId'), 'click', function(event) { something(); }, false);",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Javascript->event('myId', 'click', 'something();', array('safe' => false));
+		$expected = array(
+			'script' => array('type' => 'text/javascript'),
+			"Event.observe($('myId'), 'click', function(event) { something(); }, false);",
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Javascript->event('myId', 'click');
+		$expected = array(
+			'script' => array('type' => 'text/javascript'),
+			$this->cDataStart,
+			"Event.observe($('myId'), 'click', function(event) {  }, false);",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Javascript->event('myId', 'click', 'something();', false);
+		$expected = array(
+			'script' => array('type' => 'text/javascript'),
+			$this->cDataStart,
+			"Event.observe($('myId'), 'click', function(event) { something(); }, false);",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Javascript->event('myId', 'click', 'something();', array('useCapture' => true));
+		$expected = array(
+			'script' => array('type' => 'text/javascript'),
+			$this->cDataStart,
+			"Event.observe($('myId'), 'click', function(event) { something(); }, true);",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Javascript->event('document', 'load');
+		$expected = array(
+			'script' => array('type' => 'text/javascript'),
+			$this->cDataStart,
+			"Event.observe(document, 'load', function(event) {  }, false);",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Javascript->event('$(\'myId\')', 'click', 'something();', array('safe' => false));
+		$expected = array(
+			'script' => array('type' => 'text/javascript'),
+			"Event.observe($('myId'), 'click', function(event) { something(); }, false);",
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Javascript->event('\'document\'', 'load', 'something();', array('safe' => false));
+		$expected = array(
+			'script' => array('type' => 'text/javascript'),
+			"Event.observe('document', 'load', function(event) { something(); }, false);",
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Javascript->cacheEvents();
+		$result = $this->Javascript->event('myId', 'click', 'something();');
+		$this->assertNull($result);
+
+		$result = $this->Javascript->getCache();
+		$this->assertPattern('/^' . str_replace('/', '\\/', preg_quote('Event.observe($(\'myId\'), \'click\', function(event) { something(); }, false);')) . '$/s', $result);
+
+		$result = $this->Javascript->event('#myId', 'alert(event);');
+		$this->assertNull($result);
+
+		$result = $this->Javascript->getCache();
+		$this->assertPattern('/^\s*var Rules = {\s*\'#myId\': function\(element, event\)\s*{\s*alert\(event\);\s*}\s*}\s*EventSelectors\.start\(Rules\);\s*$/s', $result);
+	}
+
+/**
+ * testWriteEvents method
+ *
+ * @access public
+ * @return void
+ */
+	function testWriteEvents() {
+		$this->Javascript->cacheEvents();
+		$result = $this->Javascript->event('myId', 'click', 'something();');
+		$this->assertNull($result);
+
+		$result = $this->Javascript->writeEvents();
+		$expected = array(
+			'script' => array('type' => 'text/javascript'),
+			$this->cDataStart,
+			"Event.observe($('myId'), 'click', function(event) { something(); }, false);",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Javascript->getCache();
+		$this->assertTrue(empty($result));
+
+		$this->Javascript->cacheEvents();
+		$result = $this->Javascript->event('myId', 'click', 'something();');
+		$this->assertNull($result);
+
+		$result = $this->Javascript->writeEvents(false);
+		$resultScripts = $this->View->scripts();
+		reset($resultScripts);
+		$this->assertNull($result);
+		$this->assertEqual(count($resultScripts), 1);
+		$result = current($resultScripts);
+
+		$expected = array(
+			'script' => array('type' => 'text/javascript'),
+			$this->cDataStart,
+			"Event.observe($('myId'), 'click', function(event) { something(); }, false);",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Javascript->getCache();
+		$this->assertTrue(empty($result));
+	}
+
+/**
+ * testEscapeScript method
+ *
+ * @access public
+ * @return void
+ */
+	function testEscapeScript() {
+		$result = $this->Javascript->escapeScript('');
+		$expected = '';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->escapeScript('CakePHP' . "\n" . 'Rapid Development Framework');
+		$expected = 'CakePHP\\nRapid Development Framework';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->escapeScript('CakePHP' . "\r\n" . 'Rapid Development Framework' . "\r" . 'For PHP');
+		$expected = 'CakePHP\\nRapid Development Framework\\nFor PHP';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->escapeScript('CakePHP: "Rapid Development Framework"');
+		$expected = 'CakePHP: \\"Rapid Development Framework\\"';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->escapeScript('CakePHP: \'Rapid Development Framework\'');
+		$expected = 'CakePHP: \\\'Rapid Development Framework\\\'';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testEscapeString method
+ *
+ * @access public
+ * @return void
+ */
+	function testEscapeString() {
+		$result = $this->Javascript->escapeString('');
+		$expected = '';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->escapeString('CakePHP' . "\n" . 'Rapid Development Framework');
+		$expected = 'CakePHP\\nRapid Development Framework';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->escapeString('CakePHP' . "\r\n" . 'Rapid Development Framework' . "\r" . 'For PHP');
+		$expected = 'CakePHP\\nRapid Development Framework\\nFor PHP';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->escapeString('CakePHP: "Rapid Development Framework"');
+		$expected = 'CakePHP: \\"Rapid Development Framework\\"';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->escapeString('CakePHP: \'Rapid Development Framework\'');
+		$expected = "CakePHP: \\'Rapid Development Framework\\'";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->escapeString('my \\"string\\"');
+		$expected = 'my \\\\\\"string\\\\\\"';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->escapeString('my string\nanother line');
+		$expected = 'my string\\\nanother line';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->escapeString('String with \n string that looks like newline');
+		$expected = 'String with \\\n string that looks like newline';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Javascript->escapeString('String with \n string that looks like newline');
+		$expected = 'String with \\\n string that looks like newline';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test string escaping and compare to json_encode()
+ *
+ * @return void
+ */
+	function testStringJsonEncodeCompliance() {
+		if (!function_exists('json_encode')) {
+			return;
+		}
+		$this->Javascript->useNative = false;
+		$data = array();
+		$data['mystring'] = "simple string";
+		$this->assertEqual(json_encode($data), $this->Javascript->object($data));
+
+		$data['mystring'] = "strïng with spécial chârs";
+		$this->assertEqual(json_encode($data), $this->Javascript->object($data));
+
+		$data['mystring'] = "a two lines\nstring";
+		$this->assertEqual(json_encode($data), $this->Javascript->object($data));
+
+		$data['mystring'] = "a \t tabbed \t string";
+		$this->assertEqual(json_encode($data), $this->Javascript->object($data));
+
+		$data['mystring'] = "a \"double-quoted\" string";
+		$this->assertEqual(json_encode($data), $this->Javascript->object($data));
+
+		$data['mystring'] = 'a \\"double-quoted\\" string';
+		$this->assertEqual(json_encode($data), $this->Javascript->object($data));
+	}
+
+/**
+ * test that text encoded with Javascript::object decodes properly
+ *
+ * @return void
+ */
+	function testObjectDecodeCompatibility() {
+		if (!function_exists('json_decode')) {
+			return;
+		}
+		$this->Javascript->useNative = false;
+
+		$data = array("simple string");
+		$result = $this->Javascript->object($data);
+		$this->assertEqual(json_decode($result), $data);
+
+		$data = array('my \"string\"');
+		$result = $this->Javascript->object($data);
+		$this->assertEqual(json_decode($result), $data);
+
+		$data = array('my \\"string\\"');
+		$result = $this->Javascript->object($data);
+		$this->assertEqual(json_decode($result), $data);
+	}
+
+/**
+ * testAfterRender method
+ *
+ * @access public
+ * @return void
+ */
+	function testAfterRender() {
+		$this->Javascript->cacheEvents();
+		$result = $this->Javascript->event('myId', 'click', 'something();');
+		$this->assertNull($result);
+
+		ob_start();
+		$this->Javascript->afterRender();
+		$result = ob_get_clean();
+
+		$expected = array(
+			'script' => array('type' => 'text/javascript'),
+			$this->cDataStart,
+			"Event.observe($('myId'), 'click', function(event) { something(); }, false);",
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Javascript->getCache();
+		$this->assertTrue(empty($result));
+
+		$old = $this->Javascript->enabled;
+		$this->Javascript->enabled = false;
+
+		$this->Javascript->cacheEvents();
+		$result = $this->Javascript->event('myId', 'click', 'something();');
+		$this->assertNull($result);
+
+		ob_start();
+		$this->Javascript->afterRender();
+		$result = ob_get_clean();
+
+		$this->assertTrue(empty($result));
+
+		$result = $this->Javascript->getCache();
+		$this->assertPattern('/^' . str_replace('/', '\\/', preg_quote('Event.observe($(\'myId\'), \'click\', function(event) { something(); }, false);')) . '$/s', $result);
+
+		$this->Javascript->enabled = $old;
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/view/helpers/jquery_engine.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/view/helpers/jquery_engine.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/view/helpers/jquery_engine.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,367 @@
+<?php
+/**
+ * JqueryEngineTestCase
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP : Rapid Development Framework <http://www.cakephp.org/>
+ * Copyright 2005-2012, 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.
+ *
+ * @copyright       Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link            http://cakephp.org CakePHP Project
+ * @package         cake.tests
+ * @subpackage      cake.tests.cases.views.helpers
+ * @license         MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Helper', array('Html', 'Js', 'JqueryEngine'));
+
+class JqueryEngineHelperTestCase extends CakeTestCase {
+/**
+ * startTest
+ *
+ * @return void
+ */
+	function startTest() {
+		$this->Jquery =& new JqueryEngineHelper();
+	}
+
+/**
+ * end test
+ *
+ * @return void
+ */
+	function endTest() {
+		unset($this->Jquery);
+	}
+
+/**
+ * test selector method
+ *
+ * @return void
+ */
+	function testSelector() {
+		$result = $this->Jquery->get('#content');
+		$this->assertEqual($result, $this->Jquery);
+		$this->assertEqual($this->Jquery->selection, '$("#content")');
+
+		$result = $this->Jquery->get('document');
+		$this->assertEqual($result, $this->Jquery);
+		$this->assertEqual($this->Jquery->selection, '$(document)');
+
+		$result = $this->Jquery->get('window');
+		$this->assertEqual($result, $this->Jquery);
+		$this->assertEqual($this->Jquery->selection, '$(window)');
+
+		$result = $this->Jquery->get('ul');
+		$this->assertEqual($result, $this->Jquery);
+		$this->assertEqual($this->Jquery->selection, '$("ul")');
+	}
+
+/**
+ * test event binding
+ *
+ * @return void
+ */
+	function testEvent() {
+		$this->Jquery->get('#myLink');
+		$result = $this->Jquery->event('click', 'doClick', array('wrap' => false));
+		$expected = '$("#myLink").bind("click", doClick);';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Jquery->event('click', '$(this).show();', array('stop' => false));
+		$expected = '$("#myLink").bind("click", function (event) {$(this).show();});';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Jquery->event('click', '$(this).hide();');
+		$expected = '$("#myLink").bind("click", function (event) {$(this).hide();'."\n".'return false;});';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test dom ready event creation
+ *
+ * @return void
+ */
+	function testDomReady() {
+		$result = $this->Jquery->domReady('foo.name = "bar";');
+		$expected = '$(document).ready(function () {foo.name = "bar";});';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test Each method
+ *
+ * @return void
+ */
+	function testEach() {
+		$this->Jquery->get('#foo');
+		$result = $this->Jquery->each('$(this).hide();');
+		$expected = '$("#foo").each(function () {$(this).hide();});';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test Effect generation
+ *
+ * @return void
+ */
+	function testEffect() {
+		$this->Jquery->get('#foo');
+		$result = $this->Jquery->effect('show');
+		$expected = '$("#foo").show();';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Jquery->effect('hide');
+		$expected = '$("#foo").hide();';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Jquery->effect('hide', array('speed' => 'fast'));
+		$expected = '$("#foo").hide("fast");';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Jquery->effect('fadeIn');
+		$expected = '$("#foo").fadeIn();';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Jquery->effect('fadeOut');
+		$expected = '$("#foo").fadeOut();';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Jquery->effect('slideIn');
+		$expected = '$("#foo").slideDown();';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Jquery->effect('slideOut');
+		$expected = '$("#foo").slideUp();';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Jquery->effect('slideDown');
+		$expected = '$("#foo").slideDown();';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Jquery->effect('slideUp');
+		$expected = '$("#foo").slideUp();';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * Test Request Generation
+ *
+ * @return void
+ */
+	function testRequest() {
+		$result = $this->Jquery->request(array('controller' => 'posts', 'action' => 'view', 1));
+		$expected = '$.ajax({url:"\\/posts\\/view\\/1"});';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Jquery->request(array('controller' => 'posts', 'action' => 'view', 1), array(
+			'update' => '#content'
+		));
+		$expected = '$.ajax({dataType:"html", success:function (data, textStatus) {$("#content").html(data);}, url:"\/posts\/view\/1"});';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Jquery->request('/people/edit/1', array(
+			'method' => 'post',
+			'before' => 'doBefore',
+			'complete' => 'doComplete',
+			'success' => 'doSuccess',
+			'error' => 'handleError',
+			'type' => 'json',
+			'data' => array('name' => 'jim', 'height' => '185cm'),
+			'wrapCallbacks' => false
+		));
+		$expected = '$.ajax({beforeSend:doBefore, complete:doComplete, data:"name=jim&height=185cm", dataType:"json", error:handleError, success:doSuccess, type:"post", url:"\\/people\\/edit\\/1"});';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Jquery->request('/people/edit/1', array(
+			'update' => '#updated',
+			'success' => 'doFoo',
+			'method' => 'post',
+			'wrapCallbacks' => false
+		));
+		$expected = '$.ajax({dataType:"html", success:function (data, textStatus) {doFoo$("#updated").html(data);}, type:"post", url:"\\/people\\/edit\\/1"});';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Jquery->request('/people/edit/1', array(
+			'update' => '#updated',
+			'success' => 'doFoo',
+			'method' => 'post',
+			'dataExpression' => true,
+			'data' => '$("#someId").serialize()',
+			'wrapCallbacks' => false
+		));
+		$expected = '$.ajax({data:$("#someId").serialize(), dataType:"html", success:function (data, textStatus) {doFoo$("#updated").html(data);}, type:"post", url:"\\/people\\/edit\\/1"});';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Jquery->request('/people/edit/1', array(
+			'success' => 'doFoo',
+			'before' => 'doBefore',
+			'method' => 'post',
+			'dataExpression' => true,
+			'data' => '$("#someId").serialize()',
+		));
+		$expected = '$.ajax({beforeSend:function (XMLHttpRequest) {doBefore}, data:$("#someId").serialize(), success:function (data, textStatus) {doFoo}, type:"post", url:"\\/people\\/edit\\/1"});';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that alternate jQuery object values work for request()
+ *
+ * @return void
+ */
+	function testRequestWithAlternateJqueryObject() {
+		$this->Jquery->jQueryObject = '$j';
+
+		$result = $this->Jquery->request('/people/edit/1', array(
+			'update' => '#updated',
+			'success' => 'doFoo',
+			'method' => 'post',
+			'dataExpression' => true,
+			'data' => '$j("#someId").serialize()',
+			'wrapCallbacks' => false
+		));
+		$expected = '$j.ajax({data:$j("#someId").serialize(), dataType:"html", success:function (data, textStatus) {doFoo$j("#updated").html(data);}, type:"post", url:"\\/people\\/edit\\/1"});';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test sortable list generation
+ *
+ * @return void
+ */
+	function testSortable() {
+		$this->Jquery->get('#myList');
+		$result = $this->Jquery->sortable(array(
+			'distance' => 5,
+			'containment' => 'parent',
+			'start' => 'onStart',
+			'complete' => 'onStop',
+			'sort' => 'onSort',
+			'wrapCallbacks' => false
+		));
+		$expected = '$("#myList").sortable({containment:"parent", distance:5, sort:onSort, start:onStart, stop:onStop});';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Jquery->sortable(array(
+			'distance' => 5,
+			'containment' => 'parent',
+			'start' => 'onStart',
+			'complete' => 'onStop',
+			'sort' => 'onSort',
+		));
+		$expected = '$("#myList").sortable({containment:"parent", distance:5, sort:function (event, ui) {onSort}, start:function (event, ui) {onStart}, stop:function (event, ui) {onStop}});';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test drag() method
+ *
+ * @return void
+ */
+	function testDrag() {
+		$this->Jquery->get('#element');
+		$result = $this->Jquery->drag(array(
+			'container' => '#content',
+			'start' => 'onStart',
+			'drag' => 'onDrag',
+			'stop' => 'onStop',
+			'snapGrid' => array(10, 10),
+			'wrapCallbacks' => false
+		));
+		$expected = '$("#element").draggable({containment:"#content", drag:onDrag, grid:[10,10], start:onStart, stop:onStop});';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Jquery->drag(array(
+			'container' => '#content',
+			'start' => 'onStart',
+			'drag' => 'onDrag',
+			'stop' => 'onStop',
+			'snapGrid' => array(10, 10),
+		));
+		$expected = '$("#element").draggable({containment:"#content", drag:function (event, ui) {onDrag}, grid:[10,10], start:function (event, ui) {onStart}, stop:function (event, ui) {onStop}});';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test drop() method
+ *
+ * @return void
+ */
+	function testDrop() {
+		$this->Jquery->get('#element');
+		$result = $this->Jquery->drop(array(
+			'accept' => '.items',
+			'hover' => 'onHover',
+			'leave' => 'onExit',
+			'drop' => 'onDrop',
+			'wrapCallbacks' => false
+		));
+		$expected = '$("#element").droppable({accept:".items", drop:onDrop, out:onExit, over:onHover});';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Jquery->drop(array(
+			'accept' => '.items',
+			'hover' => 'onHover',
+			'leave' => 'onExit',
+			'drop' => 'onDrop',
+		));
+		$expected = '$("#element").droppable({accept:".items", drop:function (event, ui) {onDrop}, out:function (event, ui) {onExit}, over:function (event, ui) {onHover}});';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test slider generation
+ *
+ * @return void
+ */
+	function testSlider() {
+		$this->Jquery->get('#element');
+		$result = $this->Jquery->slider(array(
+			'complete' => 'onComplete',
+			'change' => 'onChange',
+			'min' => 0,
+			'max' => 10,
+			'value' => 2,
+			'direction' => 'vertical',
+			'wrapCallbacks' => false
+		));
+		$expected = '$("#element").slider({change:onChange, max:10, min:0, orientation:"vertical", stop:onComplete, value:2});';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Jquery->slider(array(
+			'complete' => 'onComplete',
+			'change' => 'onChange',
+			'min' => 0,
+			'max' => 10,
+			'value' => 2,
+			'direction' => 'vertical',
+		));
+		$expected = '$("#element").slider({change:function (event, ui) {onChange}, max:10, min:0, orientation:"vertical", stop:function (event, ui) {onComplete}, value:2});';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test the serializeForm method
+ *
+ * @return void
+ */
+	function testSerializeForm() {
+		$this->Jquery->get('#element');
+		$result = $this->Jquery->serializeForm(array('isForm' => false));
+		$expected = '$("#element").closest("form").serialize();';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Jquery->serializeForm(array('isForm' => true));
+		$expected = '$("#element").serialize();';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Jquery->serializeForm(array('isForm' => false, 'inline' => true));
+		$expected = '$("#element").closest("form").serialize()';
+		$this->assertEqual($result, $expected);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/view/helpers/js.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/view/helpers/js.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/view/helpers/js.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,833 @@
+<?php
+/**
+ * JsHelper Test Case
+ *
+ * TestCase for the JsHelper
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ * @since         CakePHP(tm) v 1.3
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Helper', array('Js', 'Html', 'Form'));
+App::import('Core', array('View', 'ClassRegistry'));
+
+Mock::generate('JsBaseEngineHelper', 'TestJsEngineHelper', array('methodOne'));
+Mock::generate('View', 'JsHelperMockView');
+
+class OptionEngineHelper extends JsBaseEngineHelper {
+	var $_optionMap = array(
+		'request' => array(
+			'complete' => 'success',
+			'request' => 'beforeSend',
+			'type' => 'dataType'
+		)
+	);
+
+/**
+ * test method for testing option mapping
+ *
+ * @return array
+ */
+	function testMap($options = array()) {
+		return $this->_mapOptions('request', $options);
+	}
+/**
+ * test method for option parsing
+ *
+ * @return void
+ */
+	function testParseOptions($options, $safe = array()) {
+		return $this->_parseOptions($options, $safe);
+	}
+}
+
+/**
+ * JsHelper TestCase.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class JsHelperTestCase extends CakeTestCase {
+/**
+ * Regexp for CDATA start block
+ *
+ * @var string
+ */
+	var $cDataStart = 'preg:/^\/\/<!\[CDATA\[[\n\r]*/';
+
+/**
+ * Regexp for CDATA end block
+ *
+ * @var string
+ */
+	var $cDataEnd = 'preg:/[^\]]*\]\]\>[\s\r\n]*/';
+
+/**
+ * startTest method
+ *
+ * @access public
+ * @return void
+ */
+	function startTest() {
+		$this->_asset = Configure::read('Asset.timestamp');
+		Configure::write('Asset.timestamp', false);
+
+		$this->Js =& new JsHelper('JsBase');
+		$this->Js->Html =& new HtmlHelper();
+		$this->Js->Form =& new FormHelper();
+		$this->Js->Form->Html =& new HtmlHelper();
+		$this->Js->JsBaseEngine =& new JsBaseEngineHelper();
+
+		$view =& new JsHelperMockView();
+		ClassRegistry::addObject('view', $view);
+	}
+
+/**
+ * endTest method
+ *
+ * @access public
+ * @return void
+ */
+	function endTest() {
+		Configure::write('Asset.timestamp', $this->_asset);
+		ClassRegistry::removeObject('view');
+		unset($this->Js);
+	}
+
+/**
+ * Switches $this->Js to a mocked engine.
+ *
+ * @return void
+ */
+	function _useMock() {
+		$this->Js =& new JsHelper(array('TestJs'));
+		$this->Js->TestJsEngine =& new TestJsEngineHelper($this);
+		$this->Js->Html =& new HtmlHelper();
+		$this->Js->Form =& new FormHelper();
+		$this->Js->Form->Html =& new HtmlHelper();
+	}
+
+/**
+ * test object construction
+ *
+ * @return void
+ */
+	function testConstruction() {
+		$js =& new JsHelper();
+		$this->assertEqual($js->helpers, array('Html', 'Form', 'JqueryEngine'));
+
+		$js =& new JsHelper(array('mootools'));
+		$this->assertEqual($js->helpers, array('Html', 'Form', 'mootoolsEngine'));
+
+		$js =& new JsHelper('prototype');
+		$this->assertEqual($js->helpers, array('Html', 'Form', 'prototypeEngine'));
+
+		$js =& new JsHelper('MyPlugin.Dojo');
+		$this->assertEqual($js->helpers, array('Html', 'Form', 'MyPlugin.DojoEngine'));
+	}
+
+/**
+ * test that methods dispatch internally and to the engine class
+ *
+ * @return void
+ */
+	function testMethodDispatching() {
+		$this->_useMock();
+		$this->Js->TestJsEngine->expectOnce('dispatchMethod', array(new PatternExpectation('/methodOne/i'), array()));
+
+		$this->Js->methodOne();
+
+		$this->Js->TestEngine =& new StdClass();
+		$this->expectError();
+		$this->Js->someMethodThatSurelyDoesntExist();
+	}
+
+/**
+ * Test that method dispatching respects buffer parameters and bufferedMethods Lists.
+ *
+ * @return void
+ */
+	function testMethodDispatchWithBuffering() {
+		$this->_useMock();
+
+		$this->Js->TestJsEngine->bufferedMethods = array('event', 'sortables');
+		$this->Js->TestJsEngine->setReturnValue('dispatchMethod', 'This is an event call', array('event', '*'));
+
+		$this->Js->event('click', 'foo');
+		$result = $this->Js->getBuffer();
+		$this->assertEqual(count($result), 1);
+		$this->assertEqual($result[0], 'This is an event call');
+
+		$result = $this->Js->event('click', 'foo', array('buffer' => false));
+		$buffer = $this->Js->getBuffer();
+		$this->assertTrue(empty($buffer));
+		$this->assertEqual($result, 'This is an event call');
+
+		$result = $this->Js->event('click', 'foo', false);
+		$buffer = $this->Js->getBuffer();
+		$this->assertTrue(empty($buffer));
+		$this->assertEqual($result, 'This is an event call');
+
+		$this->Js->TestJsEngine->setReturnValue('dispatchMethod', 'I am not buffered.', array('effect', '*'));
+
+		$result = $this->Js->effect('slideIn');
+		$buffer = $this->Js->getBuffer();
+		$this->assertTrue(empty($buffer));
+		$this->assertEqual($result, 'I am not buffered.');
+
+		$result = $this->Js->effect('slideIn', true);
+		$buffer = $this->Js->getBuffer();
+		$this->assertNull($result);
+		$this->assertEqual(count($buffer), 1);
+		$this->assertEqual($buffer[0], 'I am not buffered.');
+
+		$result = $this->Js->effect('slideIn', array('speed' => 'slow'), true);
+		$buffer = $this->Js->getBuffer();
+		$this->assertNull($result);
+		$this->assertEqual(count($buffer), 1);
+		$this->assertEqual($buffer[0], 'I am not buffered.');
+
+		$result = $this->Js->effect('slideIn', array('speed' => 'slow', 'buffer' => true));
+		$buffer = $this->Js->getBuffer();
+		$this->assertNull($result);
+		$this->assertEqual(count($buffer), 1);
+		$this->assertEqual($buffer[0], 'I am not buffered.');
+	}
+
+/**
+ * test that writeScripts generates scripts inline.
+ *
+ * @return void
+ */
+	function testWriteScriptsNoFile() {
+		$this->_useMock();
+		$this->Js->buffer('one = 1;');
+		$this->Js->buffer('two = 2;');
+		$result = $this->Js->writeBuffer(array('onDomReady' => false, 'cache' => false, 'clear' => false));
+		$expected = array(
+			'script' => array('type' => 'text/javascript'),
+			$this->cDataStart,
+			"one = 1;\ntwo = 2;",
+			$this->cDataEnd,
+			'/script',
+		);
+		$this->assertTags($result, $expected, true);
+
+		$this->Js->TestJsEngine->expectAtLeastOnce('domReady');
+		$result = $this->Js->writeBuffer(array('onDomReady' => true, 'cache' => false, 'clear' => false));
+
+		ClassRegistry::removeObject('view');
+		$view =& new JsHelperMockView();
+		ClassRegistry::addObject('view', $view);
+
+		$view->expectCallCount('addScript', 1);
+		$view->expectAt(0, 'addScript', array(new PatternExpectation('/one\s\=\s1;\ntwo\s\=\s2;/')));
+		$result = $this->Js->writeBuffer(array('onDomReady' => false, 'inline' => false, 'cache' => false));
+	}
+
+/**
+ * test that writing the buffer with inline = false includes a script tag.
+ *
+ * @return void
+ */
+	function testWriteBufferNotInline() {
+		$this->Js->set('foo', 1);
+
+		$view =& new JsHelperMockView();
+		ClassRegistry::removeObject('view');
+		ClassRegistry::addObject('view', $view);
+		$view->expectCallCount('addScript', 1);
+
+		$pattern = new PatternExpectation('#<script type="text\/javascript">window.app \= \{"foo"\:1\}\;<\/script>#');
+		$view->expectAt(0, 'addScript', array($pattern));
+
+		$result = $this->Js->writeBuffer(array('onDomReady' => false, 'inline' => false, 'safe' => false));
+	}
+
+/**
+ * test that writeBuffer() sets domReady = false when the request is done by XHR.
+ * Including a domReady() when in XHR can cause issues as events aren't triggered by some libraries
+ *
+ * @return void
+ */
+	function testWriteBufferAndXhr() {
+		$this->_useMock();
+		$this->Js->params['isAjax'] = true;
+		$this->Js->buffer('alert("test");');
+		$this->Js->TestJsEngine->expectCallCount('dispatchMethod', 0);
+		$result = $this->Js->writeBuffer();
+	}
+
+/**
+ * test that writeScripts makes files, and puts the events into them.
+ *
+ * @return void
+ */
+	function testWriteScriptsInFile() {
+		if ($this->skipIf(!is_writable(JS), 'webroot/js is not Writable, script caching test has been skipped')) {
+			return;
+		}
+		$this->Js->JsBaseEngine = new TestJsEngineHelper();
+		$this->Js->buffer('one = 1;');
+		$this->Js->buffer('two = 2;');
+		$result = $this->Js->writeBuffer(array('onDomReady' => false, 'cache' => true));
+		$expected = array(
+			'script' => array('type' => 'text/javascript', 'src' => 'preg:/(.)*\.js/'),
+		);
+		$this->assertTags($result, $expected);
+		preg_match('/src="(.*\.js)"/', $result, $filename);
+		$this->assertTrue(file_exists(WWW_ROOT . $filename[1]));
+		$contents = file_get_contents(WWW_ROOT . $filename[1]);
+		$this->assertPattern('/one\s=\s1;\ntwo\s=\s2;/', $contents);
+
+		@unlink(WWW_ROOT . $filename[1]);
+	}
+
+/**
+ * test link()
+ *
+ * @return void
+ */
+	function testLinkWithMock() {
+		$this->_useMock();
+		$options = array('update' => '#content');
+
+		$this->Js->TestJsEngine->setReturnValue('dispatchMethod', 'ajax code', array('request', '*'));
+		$this->Js->TestJsEngine->expectAt(0, 'dispatchMethod', array('get', new AnythingExpectation()));
+		$this->Js->TestJsEngine->expectAt(1, 'dispatchMethod', array(
+			'request', array('/posts/view/1', $options)
+		));
+		$this->Js->TestJsEngine->expectAt(2, 'dispatchMethod', array(
+			'event', array('click', 'ajax code', $options + array('buffer' => null))
+		));
+
+		$result = $this->Js->link('test link', '/posts/view/1', $options);
+		$expected = array(
+			'a' => array('id' => 'preg:/link-\d+/', 'href' => '/posts/view/1'),
+			'test link',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$options = array(
+			'confirm' => 'Are you sure?',
+			'update' => '#content',
+			'class' => 'my-class',
+			'id' => 'custom-id',
+			'escape' => false
+		);
+		$this->Js->TestJsEngine->expectAt(0, 'confirm', array($options['confirm']));
+		$this->Js->TestJsEngine->expectAt(1, 'request', array('/posts/view/1', '*'));
+$code = <<<CODE
+var _confirm = confirm("Are you sure?");
+if (!_confirm) {
+	return false;
+}
+CODE;
+		$this->Js->TestJsEngine->expectAt(1, 'event', array('click', $code));
+		$result = $this->Js->link('test link »', '/posts/view/1', $options);
+		$expected = array(
+			'a' => array('id' => $options['id'], 'class' => $options['class'], 'href' => '/posts/view/1'),
+			'test link »',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$options = array('id' => 'something', 'htmlAttributes' => array('arbitrary' => 'value', 'batman' => 'robin'));
+		$result = $this->Js->link('test link', '/posts/view/1', $options);
+		$expected = array(
+			'a' => array('id' => $options['id'], 'href' => '/posts/view/1', 'arbitrary' => 'value',
+				'batman' => 'robin'),
+			'test link',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test that link() and no buffering returns an <a> and <script> tags.
+ *
+ * @return void
+ */
+	function testLinkWithNoBuffering() {
+		$this->_useMock();
+		$this->Js->TestJsEngine->setReturnValue('dispatchMethod', 'ajax code', array(
+			'request', array('/posts/view/1', array('update' => '#content'))
+		));
+		$this->Js->TestJsEngine->setReturnValue('dispatchMethod', '-event handler-', array('event', '*'));
+
+		$options = array('update' => '#content', 'buffer' => false);
+		$result = $this->Js->link('test link', '/posts/view/1', $options);
+		$expected = array(
+			'a' => array('id' => 'preg:/link-\d+/', 'href' => '/posts/view/1'),
+			'test link',
+			'/a',
+			'script' => array('type' => 'text/javascript'),
+			$this->cDataStart,
+			'-event handler-',
+			$this->cDataEnd,
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+
+		$options = array('update' => '#content', 'buffer' => false, 'safe' => false);
+		$result = $this->Js->link('test link', '/posts/view/1', $options);
+		$expected = array(
+			'a' => array('id' => 'preg:/link-\d+/', 'href' => '/posts/view/1'),
+			'test link',
+			'/a',
+			'script' => array('type' => 'text/javascript'),
+			'-event handler-',
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test submit() with a Mock to check Engine method calls
+ *
+ * @return void
+ */
+	function testSubmitWithMock() {
+		$this->_useMock();
+
+		$options = array('update' => '#content', 'id' => 'test-submit', 'style' => 'margin: 0');
+		$this->Js->TestJsEngine->setReturnValue('dispatchMethod', 'serialize-code', array('serializeform', '*'));
+		$this->Js->TestJsEngine->setReturnValue('dispatchMethod', 'serialize-code', array('serializeForm', '*'));
+		$this->Js->TestJsEngine->setReturnValue('dispatchMethod', 'ajax-code', array('request', '*'));
+
+		$this->Js->TestJsEngine->expectAt(0, 'dispatchMethod', array('get', '*'));
+		$this->Js->TestJsEngine->expectAt(1, 'dispatchMethod', array(new PatternExpectation('/serializeForm/i'), '*'));
+		$this->Js->TestJsEngine->expectAt(2, 'dispatchMethod', array('request', '*'));
+
+		$params = array(
+			'update' => $options['update'], 'data' => 'serialize-code',
+			'method' => 'post', 'dataExpression' => true, 'buffer' => null
+		);
+		$this->Js->TestJsEngine->expectAt(3, 'dispatchMethod', array(
+			'event', array('click', "ajax-code", $params)
+		));
+
+		$result = $this->Js->submit('Save', $options);
+		$expected = array(
+			'div' => array('class' => 'submit'),
+			'input' => array('type' => 'submit', 'id' => $options['id'], 'value' => 'Save', 'style' => 'margin: 0'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+
+		$this->Js->TestJsEngine->expectAt(4, 'dispatchMethod', array('get', '*'));
+		$this->Js->TestJsEngine->expectAt(5, 'dispatchMethod', array(new PatternExpectation('/serializeForm/i'), '*'));
+		$requestParams = array(
+			'/custom/url', array(
+				'update' => '#content',
+				'data' => 'serialize-code',
+				'method' => 'post',
+				'dataExpression' => true
+			)
+		);
+		$this->Js->TestJsEngine->expectAt(6, 'dispatchMethod', array('request', $requestParams));
+
+		$params = array(
+			'update' => '#content', 'data' => 'serialize-code',
+			'method' => 'post', 'dataExpression' => true, 'buffer' => null
+		);
+		$this->Js->TestJsEngine->expectAt(7, 'dispatchMethod', array(
+			'event', array('click', "ajax-code", $params)
+		));
+
+		$options = array('update' => '#content', 'id' => 'test-submit', 'url' => '/custom/url');
+		$result = $this->Js->submit('Save', $options);
+		$expected = array(
+			'div' => array('class' => 'submit'),
+			'input' => array('type' => 'submit', 'id' => $options['id'], 'value' => 'Save'),
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test that no buffer works with submit() and that parameters are leaking into the script tag.
+ *
+ * @return void
+ */
+	function testSubmitWithNoBuffer() {
+		$this->_useMock();
+		$options = array('update' => '#content', 'id' => 'test-submit', 'buffer' => false, 'safe' => false);
+		$this->Js->TestJsEngine->setReturnValue('dispatchMethod', 'serialize-code', array('serializeform', '*'));
+		$this->Js->TestJsEngine->setReturnValue('dispatchMethod', 'serialize-code', array('serializeForm', '*'));
+		$this->Js->TestJsEngine->setReturnValue('dispatchMethod', 'ajax-code', array('request', '*'));
+		$this->Js->TestJsEngine->setReturnValue('dispatchMethod', 'event-handler', array('event', '*'));
+
+		$this->Js->TestJsEngine->expectAt(0, 'dispatchMethod', array('get', '*'));
+		$this->Js->TestJsEngine->expectAt(1, 'dispatchMethod', array(new PatternExpectation('/serializeForm/i'), '*'));
+		$this->Js->TestJsEngine->expectAt(2, 'dispatchMethod', array('request', array(
+			'', array('update' => $options['update'], 'data' => 'serialize-code', 'method' => 'post', 'dataExpression' => true)
+		)));
+
+		$params = array(
+			'update' => $options['update'], 'data' => 'serialize-code',
+			'method' => 'post', 'dataExpression' => true, 'buffer' => false
+		);
+		$this->Js->TestJsEngine->expectAt(3, 'dispatchMethod', array(
+			'event', array('click', "ajax-code", $params)
+		));
+
+		$result = $this->Js->submit('Save', $options);
+		$expected = array(
+			'div' => array('class' => 'submit'),
+			'input' => array('type' => 'submit', 'id' => $options['id'], 'value' => 'Save'),
+			'/div',
+			'script' => array('type' => 'text/javascript'),
+			'event-handler',
+			'/script'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * Test that Object::Object() is not breaking json output in JsHelper
+ *
+ * @return void
+ */
+	function testObjectPassThrough() {
+		$result = $this->Js->object(array('one' => 'first', 'two' => 'second'));
+		$expected = '{"one":"first","two":"second"}';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * Test that inherited Helper::value() is overwritten in JsHelper::value()
+ * and calls JsBaseEngineHelper::value().
+ *
+ * @return void
+ */
+	function testValuePassThrough() {
+		$result = $this->Js->value('string "quote"', true);
+		$expected = '"string \"quote\""';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test set()'ing variables to the Javascript buffer and controlling the output var name.
+ *
+ * @return void
+ */
+	function testSet() {
+		$this->Js->set('loggedIn', true);
+		$this->Js->set(array('height' => 'tall', 'color' => 'purple'));
+		$result = $this->Js->getBuffer();
+		$expected = 'window.app = {"loggedIn":true,"height":"tall","color":"purple"};';
+		$this->assertEqual($result[0], $expected);
+
+		$this->Js->set('loggedIn', true);
+		$this->Js->set(array('height' => 'tall', 'color' => 'purple'));
+		$this->Js->setVariable = 'WICKED';
+		$result = $this->Js->getBuffer();
+		$expected = 'window.WICKED = {"loggedIn":true,"height":"tall","color":"purple"};';
+		$this->assertEqual($result[0], $expected);
+
+		$this->Js->set('loggedIn', true);
+		$this->Js->set(array('height' => 'tall', 'color' => 'purple'));
+		$this->Js->setVariable = 'Application.variables';
+		$result = $this->Js->getBuffer();
+		$expected = 'Application.variables = {"loggedIn":true,"height":"tall","color":"purple"};';
+		$this->assertEqual($result[0], $expected);
+	}
+
+/**
+ * test that vars set with Js->set() go to the top of the buffered scripts list.
+ *
+ * @return void
+ */
+	function testSetVarsAtTopOfBufferedScripts() {
+		$this->Js->set(array('height' => 'tall', 'color' => 'purple'));
+		$this->Js->alert('hey you!', array('buffer' => true));
+		$this->Js->confirm('Are you sure?', array('buffer' => true));
+		$result = $this->Js->getBuffer(false);
+
+		$expected = 'window.app = {"height":"tall","color":"purple"};';
+		$this->assertEqual($result[0], $expected);
+		$this->assertEqual($result[1], 'alert("hey you!");');
+		$this->assertEqual($result[2], 'confirm("Are you sure?");');
+	}
+}
+
+/**
+ * JsBaseEngine Class Test case
+ *
+ * @package cake.tests.view.helpers
+ */
+class JsBaseEngineTestCase extends CakeTestCase {
+/**
+ * startTest method
+ *
+ * @access public
+ * @return void
+ */
+	function startTest() {
+		$this->JsEngine = new JsBaseEngineHelper();
+	}
+/**
+ * endTest method
+ *
+ * @access public
+ * @return void
+ */
+	function endTest() {
+		ClassRegistry::removeObject('view');
+		unset($this->JsEngine);
+	}
+
+/**
+ * test escape string skills
+ *
+ * @return void
+ */
+	function testEscaping() {
+		$result = $this->JsEngine->escape('');
+		$expected = '';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->JsEngine->escape('CakePHP' . "\n" . 'Rapid Development Framework');
+		$expected = 'CakePHP\\nRapid Development Framework';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->JsEngine->escape('CakePHP' . "\r\n" . 'Rapid Development Framework' . "\r" . 'For PHP');
+		$expected = 'CakePHP\\r\\nRapid Development Framework\\rFor PHP';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->JsEngine->escape('CakePHP: "Rapid Development Framework"');
+		$expected = 'CakePHP: \\"Rapid Development Framework\\"';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->JsEngine->escape("CakePHP: 'Rapid Development Framework'");
+		$expected = "CakePHP: 'Rapid Development Framework'";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->JsEngine->escape('my \\"string\\"');
+		$expected = 'my \\\\\\"string\\\\\\"';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test prompt() creation
+ *
+ * @return void
+ */
+	function testPrompt() {
+		$result = $this->JsEngine->prompt('Hey, hey you', 'hi!');
+		$expected = 'prompt("Hey, hey you", "hi!");';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->JsEngine->prompt('"Hey"', '"hi"');
+		$expected = 'prompt("\"Hey\"", "\"hi\"");';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test alert generation
+ *
+ * @return void
+ */
+	function testAlert() {
+		$result = $this->JsEngine->alert('Hey there');
+		$expected = 'alert("Hey there");';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->JsEngine->alert('"Hey"');
+		$expected = 'alert("\"Hey\"");';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test confirm generation
+ *
+ * @return void
+ */
+	function testConfirm() {
+		$result = $this->JsEngine->confirm('Are you sure?');
+		$expected = 'confirm("Are you sure?");';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->JsEngine->confirm('"Are you sure?"');
+		$expected = 'confirm("\"Are you sure?\"");';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test Redirect
+ *
+ * @return void
+ */
+	function testRedirect() {
+		$result = $this->JsEngine->redirect(array('controller' => 'posts', 'action' => 'view', 1));
+		$expected = 'window.location = "/posts/view/1";';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testObject encoding with non-native methods.
+ *
+ * @return void
+ */
+	function testObject() {
+		$this->JsEngine->useNative = false;
+
+		$object = array('title' => 'New thing', 'indexes' => array(5, 6, 7, 8));
+		$result = $this->JsEngine->object($object);
+		$expected = '{"title":"New thing","indexes":[5,6,7,8]}';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->JsEngine->object(array('default' => 0));
+		$expected = '{"default":0}';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->JsEngine->object(array(
+			'2007' => array(
+				'Spring' => array(
+					'1' => array('id' => 1, 'name' => 'Josh'), '2' => array('id' => 2, 'name' => 'Becky')
+				),
+				'Fall' => array(
+					'1' => array('id' => 1, 'name' => 'Josh'), '2' => array('id' => 2, 'name' => 'Becky')
+				)
+			),
+			'2006' => array(
+				'Spring' => array(
+				    '1' => array('id' => 1, 'name' => 'Josh'), '2' => array('id' => 2, 'name' => 'Becky')
+				),
+				'Fall' => array(
+				    '1' => array('id' => 1, 'name' => 'Josh'), '2' => array('id' => 2, 'name' => 'Becky')
+				)
+			)
+		));
+		$expected = '{"2007":{"Spring":{"1":{"id":1,"name":"Josh"},"2":{"id":2,"name":"Becky"}},"Fall":{"1":{"id":1,"name":"Josh"},"2":{"id":2,"name":"Becky"}}},"2006":{"Spring":{"1":{"id":1,"name":"Josh"},"2":{"id":2,"name":"Becky"}},"Fall":{"1":{"id":1,"name":"Josh"},"2":{"id":2,"name":"Becky"}}}}';
+		$this->assertEqual($result, $expected);
+
+		foreach (array('true' => true, 'false' => false, 'null' => null) as $expected => $data) {
+			$result = $this->JsEngine->object($data);
+			$this->assertEqual($result, $expected);
+		}
+
+		$object = array('title' => 'New thing', 'indexes' => array(5, 6, 7, 8), 'object' => array('inner' => array('value' => 1)));
+		$result = $this->JsEngine->object($object, array('prefix' => 'PREFIX', 'postfix' => 'POSTFIX'));
+		$this->assertPattern('/^PREFIX/', $result);
+		$this->assertPattern('/POSTFIX$/', $result);
+		$this->assertNoPattern('/.PREFIX./', $result);
+		$this->assertNoPattern('/.POSTFIX./', $result);
+	}
+
+/**
+ * test compatibility of JsBaseEngineHelper::object() vs. json_encode()
+ *
+ * @return void
+ */
+	function testObjectAgainstJsonEncode() {
+		$skip = $this->skipIf(!function_exists('json_encode'), 'json_encode() not found, comparison tests skipped. %s');
+		if ($skip) {
+			return;
+		}
+		$this->JsEngine->useNative = false;
+		$data = array();
+		$data['mystring'] = "simple string";
+		$this->assertEqual(json_encode($data), $this->JsEngine->object($data));
+
+		$data['mystring'] = "strïng with spécial chârs";
+		$this->assertEqual(json_encode($data), $this->JsEngine->object($data));
+
+		$data['mystring'] = "a two lines\nstring";
+		$this->assertEqual(json_encode($data), $this->JsEngine->object($data));
+
+		$data['mystring'] = "a \t tabbed \t string";
+		$this->assertEqual(json_encode($data), $this->JsEngine->object($data));
+
+		$data['mystring'] = "a \"double-quoted\" string";
+		$this->assertEqual(json_encode($data), $this->JsEngine->object($data));
+
+		$data['mystring'] = 'a \\"double-quoted\\" string';
+		$this->assertEqual(json_encode($data), $this->JsEngine->object($data));
+
+		unset($data['mystring']);
+		$data[3] = array(1, 2, 3);
+		$this->assertEqual(json_encode($data), $this->JsEngine->object($data));
+
+		unset($data[3]);
+		$data = array('mystring' => null, 'bool' => false, 'array' => array(1, 44, 66));
+		$this->assertEqual(json_encode($data), $this->JsEngine->object($data));
+	}
+
+/**
+ * test that JSON made with JsBaseEngineHelper::object() against json_decode()
+ *
+ * @return void
+ */
+	function testObjectAgainstJsonDecode() {
+		$skip = $this->skipIf(!function_exists('json_encode'), 'json_encode() not found, comparison tests skipped. %s');
+		if ($skip) {
+			return;
+		}
+		$this->JsEngine->useNative = false;
+
+		$data = array("simple string");
+		$result = $this->JsEngine->object($data);
+		$this->assertEqual(json_decode($result), $data);
+
+		$data = array('my "string"');
+		$result = $this->JsEngine->object($data);
+		$this->assertEqual(json_decode($result), $data);
+
+		$data = array('my \\"string\\"');
+		$result = $this->JsEngine->object($data);
+		$this->assertEqual(json_decode($result), $data);
+	}
+
+/**
+ * test Mapping of options.
+ *
+ * @return void
+ */
+	function testOptionMapping() {
+		$JsEngine = new OptionEngineHelper();
+		$result = $JsEngine->testMap();
+		$this->assertEqual($result, array());
+
+		$result = $JsEngine->testMap(array('foo' => 'bar', 'baz' => 'sho'));
+		$this->assertEqual($result, array('foo' => 'bar', 'baz' => 'sho'));
+
+		$result = $JsEngine->testMap(array('complete' => 'myFunc', 'type' => 'json', 'update' => '#element'));
+		$this->assertEqual($result, array('success' => 'myFunc', 'dataType' => 'json', 'update' => '#element'));
+
+		$result = $JsEngine->testMap(array('success' => 'myFunc', 'dataType' => 'json', 'update' => '#element'));
+		$this->assertEqual($result, array('success' => 'myFunc', 'dataType' => 'json', 'update' => '#element'));
+	}
+
+/**
+ * test that option parsing escapes strings and saves what is supposed to be saved.
+ *
+ * @return void
+ */
+	function testOptionParsing() {
+		$JsEngine = new OptionEngineHelper();
+
+		$result = $JsEngine->testParseOptions(array('url' => '/posts/view/1', 'key' => 1));
+		$expected = 'key:1, url:"\\/posts\\/view\\/1"';
+		$this->assertEqual($result, $expected);
+
+		$result = $JsEngine->testParseOptions(array('url' => '/posts/view/1', 'success' => 'doSuccess'), array('success'));
+		$expected = 'success:doSuccess, url:"\\/posts\\/view\\/1"';
+		$this->assertEqual($result, $expected);
+	}
+
+}

Added: trunk/src/Web/cake/tests/cases/libs/view/helpers/mootools_engine.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/view/helpers/mootools_engine.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/view/helpers/mootools_engine.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,354 @@
+<?php
+/**
+ * MooEngineTestCase
+ *
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP : Rapid Development Framework <http://www.cakephp.org/>
+ * Copyright 2005-2012, 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.
+ *
+ * @copyright       Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link            http://cakephp.org CakePHP Project
+ * @package         cake.tests
+ * @subpackage      cake.tests.cases.views.helpers
+ * @license         MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Helper', array('Html', 'Js', 'MootoolsEngine'));
+
+class MooEngineHelperTestCase extends CakeTestCase {
+/**
+ * startTest
+ *
+ * @return void
+ */
+	function startTest() {
+		$this->Moo =& new MootoolsEngineHelper();
+	}
+/**
+ * end test
+ *
+ * @return void
+ */
+	function endTest() {
+		unset($this->Moo);
+	}
+/**
+ * test selector method
+ *
+ * @return void
+ */
+	function testSelector() {
+		$result = $this->Moo->get('#content');
+		$this->assertEqual($result, $this->Moo);
+		$this->assertEqual($this->Moo->selection, '$("content")');
+
+		$result = $this->Moo->get('a .remove');
+		$this->assertEqual($result, $this->Moo);
+		$this->assertEqual($this->Moo->selection, '$$("a .remove")');
+
+		$result = $this->Moo->get('document');
+		$this->assertEqual($result, $this->Moo);
+		$this->assertEqual($this->Moo->selection, "$(document)");
+
+		$result = $this->Moo->get('window');
+		$this->assertEqual($result, $this->Moo);
+		$this->assertEqual($this->Moo->selection, "$(window)");
+
+		$result = $this->Moo->get('ul');
+		$this->assertEqual($result, $this->Moo);
+		$this->assertEqual($this->Moo->selection, '$$("ul")');
+
+		$result = $this->Moo->get('#some_long-id.class');
+		$this->assertEqual($result, $this->Moo);
+		$this->assertEqual($this->Moo->selection, '$$("#some_long-id.class")');
+	}
+/**
+ * test event binding
+ *
+ * @return void
+ */
+	function testEvent() {
+		$this->Moo->get('#myLink');
+		$result = $this->Moo->event('click', 'doClick', array('wrap' => false));
+		$expected = '$("myLink").addEvent("click", doClick);';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Moo->event('click', 'this.setStyle("display", "");', array('stop' => false));
+		$expected = '$("myLink").addEvent("click", function (event) {this.setStyle("display", "");});';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Moo->event('click', 'this.setStyle("display", "none");');
+		$expected = "\$(\"myLink\").addEvent(\"click\", function (event) {event.stop();\nthis.setStyle(\"display\", \"none\");});";
+		$this->assertEqual($result, $expected);
+	}
+/**
+ * test dom ready event creation
+ *
+ * @return void
+ */
+	function testDomReady() {
+		$result = $this->Moo->domReady('foo.name = "bar";');
+		$expected = 'window.addEvent("domready", function (event) {foo.name = "bar";});';
+		$this->assertEqual($result, $expected);
+	}
+/**
+ * test Each method
+ *
+ * @return void
+ */
+	function testEach() {
+		$this->Moo->get('#foo');
+		$result = $this->Moo->each('item.setStyle("display", "none");');
+		$expected = '$("foo").each(function (item, index) {item.setStyle("display", "none");});';
+		$this->assertEqual($result, $expected);
+	}
+/**
+ * test Effect generation
+ *
+ * @return void
+ */
+	function testEffect() {
+		$this->Moo->get('#foo');
+		$result = $this->Moo->effect('show');
+		$expected = '$("foo").setStyle("display", "");';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Moo->effect('hide');
+		$expected = '$("foo").setStyle("display", "none");';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Moo->effect('fadeIn');
+		$expected = '$("foo").fade("in");';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Moo->effect('fadeOut');
+		$expected = '$("foo").fade("out");';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Moo->effect('slideIn');
+		$expected = '$("foo").slide("in");';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Moo->effect('slideOut');
+		$expected = '$("foo").slide("out");';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Moo->effect('slideOut', array('speed' => 'fast'));
+		$expected = '$("foo").set("slide", {duration:"short"}).slide("out");';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Moo->effect('slideOut', array('speed' => 'slow'));
+		$expected = '$("foo").set("slide", {duration:"long"}).slide("out");';
+		$this->assertEqual($result, $expected);
+	}
+/**
+ * Test Request Generation
+ *
+ * @return void
+ */
+	function testRequest() {
+		$result = $this->Moo->request(array('controller' => 'posts', 'action' => 'view', 1));
+		$expected = 'var jsRequest = new Request({url:"\\/posts\\/view\\/1"}).send();';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Moo->request('/posts/view/1', array('update' => 'content'));
+		$expected = 'var jsRequest = new Request.HTML({update:"content", url:"\\/posts\\/view\\/1"}).send();';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Moo->request('/people/edit/1', array(
+			'method' => 'post',
+			'complete' => 'doSuccess',
+			'error' => 'handleError',
+			'type' => 'json',
+			'data' => array('name' => 'jim', 'height' => '185cm'),
+			'wrapCallbacks' => false
+		));
+		$expected = 'var jsRequest = new Request.JSON({method:"post", onComplete:doSuccess, onFailure:handleError, url:"\\/people\\/edit\\/1"}).send({"name":"jim","height":"185cm"});';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Moo->request('/people/edit/1', array(
+			'method' => 'post',
+			'complete' => 'doSuccess',
+			'update' => '#update-zone',
+			'wrapCallbacks' => false
+		));
+		$expected = 'var jsRequest = new Request.HTML({method:"post", onComplete:doSuccess, update:"update-zone", url:"\\/people\\/edit\\/1"}).send();';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Moo->request('/people/edit/1', array(
+			'method' => 'post',
+			'complete' => 'doComplete',
+			'success' => 'doSuccess',
+			'error' => 'doFailure',
+			'before' => 'doBefore',
+			'update' => 'update-zone',
+			'wrapCallbacks' => false
+		));
+		$expected = 'var jsRequest = new Request.HTML({method:"post", onComplete:doComplete, onFailure:doFailure, onRequest:doBefore, onSuccess:doSuccess, update:"update-zone", url:"\\/people\\/edit\\/1"}).send();';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Moo->request('/people/edit/1', array(
+			'method' => 'post',
+			'complete' => 'doComplete',
+			'success' => 'doSuccess',
+			'error' => 'doFailure',
+			'before' => 'doBefore',
+			'update' => 'update-zone',
+			'dataExpression' => true,
+			'data' => '$("foo").toQueryString()',
+			'wrapCallbacks' => false
+		));
+		$expected = 'var jsRequest = new Request.HTML({method:"post", onComplete:doComplete, onFailure:doFailure, onRequest:doBefore, onSuccess:doSuccess, update:"update-zone", url:"\\/people\\/edit\\/1"}).send($("foo").toQueryString());';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Moo->request('/people/edit/1', array(
+			'method' => 'post',
+			'before' => 'doBefore',
+			'success' => 'doSuccess',
+			'complete' => 'doComplete',
+			'update' => '#update-zone',
+		));
+		$expected = 'var jsRequest = new Request.HTML({method:"post", onComplete:function () {doComplete}, onRequest:function () {doBefore}, onSuccess:function (responseText, responseXML) {doSuccess}, update:"update-zone", url:"\\/people\\/edit\\/1"}).send();';
+		$this->assertEqual($result, $expected);
+	}
+/**
+ * test sortable list generation
+ *
+ * @return void
+ */
+	function testSortable() {
+		$this->Moo->get('#myList');
+		$result = $this->Moo->sortable(array(
+			'distance' => 5,
+			'containment' => 'parent',
+			'start' => 'onStart',
+			'complete' => 'onStop',
+			'sort' => 'onSort',
+			'wrapCallbacks' => false
+		));
+		$expected = 'var jsSortable = new Sortables($("myList"), {constrain:"parent", onComplete:onStop, onSort:onSort, onStart:onStart, snap:5});';
+		$this->assertEqual($result, $expected);
+	}
+/**
+ * test drag() method
+ *
+ * @return void
+ */
+	function testDrag() {
+		$this->Moo->get('#drag-me');
+		$result = $this->Moo->drag(array(
+			'start' => 'onStart',
+			'drag' => 'onDrag',
+			'stop' => 'onStop',
+			'snapGrid' => array(10,10),
+			'wrapCallbacks' => false
+		));
+		$expected = '$("drag-me").makeDraggable({onComplete:onStop, onDrag:onDrag, onStart:onStart, snap:[10,10]});';
+		$this->assertEqual($result, $expected);
+	}
+/**
+ * test drop() method
+ *
+ * @return void
+ */
+	function testDrop() {
+		$this->expectError();
+		$this->Moo->get('#drop-me');
+		$this->Moo->drop(array(
+			'drop' => 'onDrop',
+			'leave' => 'onLeave',
+			'hover' => 'onHover',
+		));
+
+		$result = $this->Moo->drop(array(
+			'drop' => 'onDrop',
+			'leave' => 'onLeave',
+			'hover' => 'onHover',
+			'drag' => '#my-drag',
+			'wrapCallbacks' => false
+		));
+		$expected = '$("my-drag").makeDraggable({droppables:$("drop-me"), onDrop:onDrop, onEnter:onHover, onLeave:onLeave});';
+		$this->assertEqual($result, $expected);
+		$this->assertEqual($this->Moo->selection, '$("drop-me")');
+
+		$result = $this->Moo->drop(array(
+			'drop' => 'onDrop',
+			'leave' => 'onLeave',
+			'hover' => 'onHover',
+			'drag' => '#my-drag',
+		));
+		$expected = '$("my-drag").makeDraggable({droppables:$("drop-me"), onDrop:function (element, droppable, event) {onDrop}, onEnter:function (element, droppable) {onHover}, onLeave:function (element, droppable) {onLeave}});';
+		$this->assertEqual($result, $expected);
+	}
+/**
+ * test slider generation
+ *
+ * @return void
+ */
+	function testSlider() {
+		$this->Moo->get('#slider');
+		$result = $this->Moo->slider(array(
+			'handle' => '#my-handle',
+			'complete' => 'onComplete',
+			'change' => 'onChange',
+			'direction' => 'horizontal',
+			'wrapCallbacks' => false
+		));
+		$expected = 'var jsSlider = new Slider($("slider"), $("my-handle"), {mode:"horizontal", onChange:onChange, onComplete:onComplete});';
+		$this->assertEqual($result, $expected);
+		$this->assertEqual($this->Moo->selection, '$("slider")');
+
+		$this->Moo->get('#slider');
+		$result = $this->Moo->slider(array(
+			'handle' => '#my-handle',
+			'complete' => 'onComplete',
+			'change' => 'onChange',
+			'direction' => 'horizontal',
+			'min' => 10,
+			'max' => 40,
+			'wrapCallbacks' => false
+		));
+		$expected = 'var jsSlider = new Slider($("slider"), $("my-handle"), {mode:"horizontal", onChange:onChange, onComplete:onComplete, range:[10,40]});';
+		$this->assertEqual($result, $expected);
+
+		$this->Moo->get('#slider');
+		$result = $this->Moo->slider(array(
+			'handle' => '#my-handle',
+			'complete' => 'complete;',
+			'change' => 'change;',
+			'direction' => 'horizontal',
+		));
+		$expected = 'var jsSlider = new Slider($("slider"), $("my-handle"), {mode:"horizontal", onChange:function (step) {change;}, onComplete:function (event) {complete;}});';
+		$this->assertEqual($result, $expected);
+	}
+/**
+ * test the serializeForm implementation.
+ *
+ * @return void
+ */
+	function testSerializeForm() {
+		$this->Moo->get('#element');
+		$result = $this->Moo->serializeForm(array('isForm' => true));
+		$expected = '$("element").toQueryString();';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Moo->serializeForm(array('isForm' => true, 'inline' => true));
+		$expected = '$("element").toQueryString()';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Moo->serializeForm(array('isForm' => false));
+		$expected = '$($("element").form).toQueryString();';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Moo->serializeForm(array('isForm' => false, 'inline' => true));
+		$expected = '$($("element").form).toQueryString()';
+		$this->assertEqual($result, $expected);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/view/helpers/number.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/view/helpers/number.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/view/helpers/number.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,445 @@
+<?php
+/**
+ * NumberHelperTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Helper', 'Number');
+
+/**
+ * NumberHelperTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class NumberHelperTest extends CakeTestCase {
+
+/**
+ * helper property
+ *
+ * @var mixed null
+ * @access public
+ */
+	var $helper = null;
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function startTest() {
+		$this->Number =& new NumberHelper();
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function endTest() {
+		unset($this->Number);
+	}
+
+/**
+ * testFormatAndCurrency method
+ *
+ * @access public
+ * @return void
+ */
+	function testFormat() {
+		$value = '100100100';
+
+		$result = $this->Number->format($value, '#');
+		$expected = '#100,100,100';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->format($value, 3);
+		$expected = '100,100,100.000';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->format($value);
+		$expected = '100,100,100';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->format($value, '-');
+		$expected = '100-100-100';
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * Test currency method.
+ *
+ * @access public
+ * @return void
+ */
+	function testCurrency() {
+		$value = '100100100';
+
+		$result = $this->Number->currency($value);
+		$expected = '$100,100,100.00';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency($value, '#');
+		$expected = '#100,100,100.00';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency($value, false);
+		$expected = '100,100,100.00';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency($value, 'USD');
+		$expected = '$100,100,100.00';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency($value, 'EUR');
+		$expected = '&#8364;100.100.100,00';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency($value, 'GBP');
+		$expected = '&#163;100,100,100.00';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency($value, '', array('thousands' =>' ', 'after' => '€', 'decimals' => ',', 'zero' => 'Gratuit'));
+		$expected = '100 100 100,00€';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency(1000.45, NULL, array('after'=>'øre','before'=>'Kr. ','decimals'=>',','thousands'=>'.'));
+		$expected = 'Kr. 1.000,45';
+		$this->assertEqual($expected,$result);
+
+		$result = $this->Number->currency(0.5, 'USD');
+		$expected = '50c';
+		$this->assertEqual($expected,$result);
+
+		$result = $this->Number->currency(0.5, NULL, array('after'=>'øre'));
+		$expected = '50øre';
+		$this->assertEqual($expected,$result);
+	}
+
+/**
+ * Test adding currency format options to the number helper
+ *
+ * @access public
+ * @return void
+ */
+	function testCurrencyAddFormat() {
+		$this->Number->addFormat('NOK', array('before' => 'Kr. '));
+		$result = $this->Number->currency(1000, 'NOK');
+		$expected = 'Kr. 1,000.00';
+		$this->assertEqual($expected,$result);
+
+		$this->Number->addFormat('Other', array('before' => '$$ ', 'after' => 'c!'));
+		$result = $this->Number->currency(0.22, 'Other');
+		$expected = '22c!';
+		$this->assertEqual($expected,$result);
+
+		$result = $this->Number->currency(-10, 'Other');
+		$expected = '($$ 10.00)';
+		$this->assertEqual($expected,$result);
+
+		$this->Number->addFormat('Other2', array('before' => '$ '));
+		$result = $this->Number->currency(0.22, 'Other2');
+		$expected = '$ 0.22';
+		$this->assertEqual($expected,$result);
+	}
+
+/**
+ * testCurrencyPositive method
+ *
+ * @access public
+ * @return void
+ */
+	function testCurrencyPositive() {
+		$value = '100100100';
+
+		$result = $this->Number->currency($value);
+		$expected = '$100,100,100.00';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency($value, 'USD', array('before'=> '#'));
+		$expected = '#100,100,100.00';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency($value, false);
+		$expected = '100,100,100.00';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency($value, 'USD');
+		$expected = '$100,100,100.00';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency($value, 'EUR');
+		$expected = '&#8364;100.100.100,00';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency($value, 'GBP');
+		$expected = '&#163;100,100,100.00';
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * testCurrencyNegative method
+ *
+ * @access public
+ * @return void
+ */
+	function testCurrencyNegative() {
+		$value = '-100100100';
+
+		$result = $this->Number->currency($value);
+		$expected = '($100,100,100.00)';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency($value, 'EUR');
+		$expected = '(&#8364;100.100.100,00)';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency($value, 'GBP');
+		$expected = '(&#163;100,100,100.00)';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency($value, 'USD', array('negative'=>'-'));
+		$expected = '-$100,100,100.00';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency($value, 'EUR', array('negative'=>'-'));
+		$expected = '-&#8364;100.100.100,00';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency($value, 'GBP', array('negative'=>'-'));
+		$expected = '-&#163;100,100,100.00';
+		$this->assertEqual($expected, $result);
+
+	}
+
+/**
+ * testCurrencyCentsPositive method
+ *
+ * @access public
+ * @return void
+ */
+	function testCurrencyCentsPositive() {
+		$value = '0.99';
+
+		$result = $this->Number->currency($value, 'USD');
+		$expected = '99c';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency($value, 'EUR');
+		$expected = '&#8364;0,99';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency($value, 'GBP');
+		$expected = '99p';
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * testCurrencyCentsNegative method
+ *
+ * @access public
+ * @return void
+ */
+	function testCurrencyCentsNegative() {
+		$value = '-0.99';
+
+		$result = $this->Number->currency($value, 'USD');
+		$expected = '(99c)';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency($value, 'EUR');
+		$expected = '(&#8364;0,99)';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency($value, 'GBP');
+		$expected = '(99p)';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency($value, 'USD', array('negative'=>'-'));
+		$expected = '-99c';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency($value, 'EUR', array('negative'=>'-'));
+		$expected = '-&#8364;0,99';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency($value, 'GBP', array('negative'=>'-'));
+		$expected = '-99p';
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * testCurrencyZero method
+ *
+ * @access public
+ * @return void
+ */
+	function testCurrencyZero() {
+		$value = '0';
+
+		$result = $this->Number->currency($value, 'USD');
+		$expected = '$0.00';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency($value, 'EUR');
+		$expected = '&#8364;0,00';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency($value, 'GBP');
+		$expected = '&#163;0.00';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency($value, 'GBP', array('zero' => 'FREE!'));
+		$expected = 'FREE!';
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * testCurrencyOptions method
+ *
+ * @access public
+ * @return void
+ */
+	function testCurrencyOptions() {
+		$value = '1234567.89';
+
+		$result = $this->Number->currency($value, null, array('before' => 'GBP'));
+		$expected = 'GBP1,234,567.89';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency($value, 'GBP', array('places' => 0));
+		$expected = '&#163;1,234,568';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency('1234567.8912345', null, array('before' => 'GBP', 'places' => 3));
+		$expected = 'GBP1,234,567.891';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency('650.120001', null, array('before' => 'GBP', 'places' => 4));
+		$expected = 'GBP650.1200';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency($value, 'GBP', array('escape' => true));
+		$expected = '&amp;#163;1,234,567.89';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency('0.35', 'USD', array('after' => false));
+		$expected = '$0.35';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency('0.35', 'GBP', array('after' => false));
+		$expected = '&#163;0.35';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->currency('0.35', 'GBP');
+		$expected = '35p';
+		$this->assertEqual($expected, $result);
+		
+		$result = $this->Number->currency('0.35', 'EUR');
+		$expected = '&#8364;0,35';
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * testToReadableSize method
+ *
+ * @access public
+ * @return void
+ */
+	function testToReadableSize() {
+		$result = $this->Number->toReadableSize(0);
+		$expected = '0 Bytes';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->toReadableSize(1);
+		$expected = '1 Byte';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->toReadableSize(45);
+		$expected = '45 Bytes';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->toReadableSize(1023);
+		$expected = '1023 Bytes';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->toReadableSize(1024);
+		$expected = '1 KB';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->toReadableSize(1024*512);
+		$expected = '512 KB';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->toReadableSize(1024*1024-1);
+		$expected = '1.00 MB';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->toReadableSize(1024*1024*512);
+		$expected = '512.00 MB';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->toReadableSize(1024*1024*1024-1);
+		$expected = '1.00 GB';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->toReadableSize(1024*1024*1024*512);
+		$expected = '512.00 GB';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->toReadableSize(1024*1024*1024*1024-1);
+		$expected = '1.00 TB';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->toReadableSize(1024*1024*1024*1024*512);
+		$expected = '512.00 TB';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->toReadableSize(1024*1024*1024*1024*1024-1);
+		$expected = '1024.00 TB';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Number->toReadableSize(1024*1024*1024*1024*1024*1024);
+		$expected = (1024 * 1024) . '.00 TB';
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * testToPercentage method
+ *
+ * @access public
+ * @return void
+ */
+	function testToPercentage() {
+		$result = $this->Number->toPercentage(45, 0);
+		$expected = '45%';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Number->toPercentage(45, 2);
+		$expected = '45.00%';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Number->toPercentage(0, 0);
+		$expected = '0%';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Number->toPercentage(0, 4);
+		$expected = '0.0000%';
+		$this->assertEqual($result, $expected);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/view/helpers/paginator.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/view/helpers/paginator.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/view/helpers/paginator.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,1927 @@
+<?php
+/**
+ * PaginatorHelperTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Helper', array('Html', 'Paginator', 'Form', 'Ajax', 'Javascript', 'Js'));
+
+Mock::generate('JsHelper', 'PaginatorMockJsHelper');
+
+if (!defined('FULL_BASE_URL')) {
+	define('FULL_BASE_URL', 'http://cakephp.org');
+}
+
+/**
+ * PaginatorHelperTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class PaginatorHelperTest extends CakeTestCase {
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		$this->Paginator = new PaginatorHelper(array('ajax' => 'Ajax'));
+		$this->Paginator->params['paging'] = array(
+			'Article' => array(
+				'current' => 9,
+				'count' => 62,
+				'prevPage' => false,
+				'nextPage' => true,
+				'pageCount' => 7,
+				'defaults' => array(
+					'order' => array('Article.date' => 'asc'),
+					'limit' => 9,
+					'conditions' => array()
+				),
+				'options' => array(
+					'order' => array('Article.date' => 'asc'),
+					'limit' => 9,
+					'page' => 1,
+					'conditions' => array()
+				)
+			)
+		);
+		$this->Paginator->Html =& new HtmlHelper();
+		$this->Paginator->Ajax =& new AjaxHelper();
+		$this->Paginator->Ajax->Html =& new HtmlHelper();
+		$this->Paginator->Ajax->Javascript =& new JavascriptHelper();
+		$this->Paginator->Ajax->Form =& new FormHelper();
+
+		Configure::write('Routing.prefixes', array());
+		Router::reload();
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		unset($this->Paginator);
+	}
+
+/**
+ * testHasPrevious method
+ *
+ * @access public
+ * @return void
+ */
+	function testHasPrevious() {
+		$this->assertIdentical($this->Paginator->hasPrev(), false);
+		$this->Paginator->params['paging']['Article']['prevPage'] = true;
+		$this->assertIdentical($this->Paginator->hasPrev(), true);
+		$this->Paginator->params['paging']['Article']['prevPage'] = false;
+	}
+
+/**
+ * testHasNext method
+ *
+ * @access public
+ * @return void
+ */
+	function testHasNext() {
+		$this->assertIdentical($this->Paginator->hasNext(), true);
+		$this->Paginator->params['paging']['Article']['nextPage'] = false;
+		$this->assertIdentical($this->Paginator->hasNext(), false);
+		$this->Paginator->params['paging']['Article']['nextPage'] = true;
+	}
+
+/**
+ * testDisabledLink method
+ *
+ * @access public
+ * @return void
+ */
+	function testDisabledLink() {
+		$this->Paginator->params['paging']['Article']['nextPage'] = false;
+		$this->Paginator->params['paging']['Article']['page'] = 1;
+		$result = $this->Paginator->next('Next', array(), true);
+		$expected = '<span class="next">Next</span>';
+		$this->assertEqual($result, $expected);
+
+		$this->Paginator->params['paging']['Article']['prevPage'] = false;
+		$result = $this->Paginator->prev('prev', array('update' => 'theList', 'indicator' => 'loading', 'url' => array('controller' => 'posts')), null, array('class' => 'disabled', 'tag' => 'span'));
+		$expected = array(
+			'span' => array('class' => 'disabled'), 'prev', '/span'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testSortLinks method
+ *
+ * @access public
+ * @return void
+ */
+	function testSortLinks() {
+		Router::reload();
+		Router::parse('/');
+		Router::setRequestInfo(array(
+			array('plugin' => null, 'controller' => 'accounts', 'action' => 'index', 'pass' => array(), 'form' => array(), 'url' => array('url' => 'accounts/'), 'bare' => 0),
+			array('plugin' => null, 'controller' => null, 'action' => null, 'base' => '/officespace', 'here' => '/officespace/accounts/', 'webroot' => '/officespace/', 'passedArgs' => array())
+		));
+		$this->Paginator->options(array('url' => array('param')));
+		$result = $this->Paginator->sort('title');
+		$expected = array(
+			'a' => array('href' => '/officespace/accounts/index/param/page:1/sort:title/direction:asc'),
+			'Title',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->sort('date');
+		$expected = array(
+			'a' => array('href' => '/officespace/accounts/index/param/page:1/sort:date/direction:desc', 'class' => 'asc'),
+			'Date',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->numbers(array('modulus'=> '2', 'url'=> array('controller'=>'projects', 'action'=>'sort'),'update'=>'list'));
+		$this->assertPattern('/\/projects\/sort\/page:2/', $result);
+		$this->assertPattern('/<script type="text\/javascript">\s*' . str_replace('/', '\\/', preg_quote('//<![CDATA[')) . '\s*Event.observe/', $result);
+
+		$result = $this->Paginator->sort('TestTitle', 'title');
+		$expected = array(
+			'a' => array('href' => '/officespace/accounts/index/param/page:1/sort:title/direction:asc'),
+			'TestTitle',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->sort(array('asc' => 'ascending', 'desc' => 'descending'), 'title');
+		$expected = array(
+			'a' => array('href' => '/officespace/accounts/index/param/page:1/sort:title/direction:asc'),
+			'ascending',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->params['paging']['Article']['options']['sort'] = 'title';
+		$result = $this->Paginator->sort(array('asc' => 'ascending', 'desc' => 'descending'), 'title');
+		$expected = array(
+			'a' => array('href' => '/officespace/accounts/index/param/page:1/sort:title/direction:desc', 'class' => 'asc'),
+			'descending',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->params['paging']['Article']['options']['order'] = array('Article.title' => 'desc');
+		$this->Paginator->params['paging']['Article']['options']['sort'] = null;
+		$result = $this->Paginator->sort('title');
+		$this->assertPattern('/\/accounts\/index\/param\/page:1\/sort:title\/direction:asc" class="desc">Title<\/a>$/', $result);
+
+		$this->Paginator->params['paging']['Article']['options']['order'] = array('Article.title' => 'asc');
+		$this->Paginator->params['paging']['Article']['options']['sort'] = null;
+		$result = $this->Paginator->sort('title');
+		$this->assertPattern('/\/accounts\/index\/param\/page:1\/sort:title\/direction:desc" class="asc">Title<\/a>$/', $result);
+
+		$this->Paginator->params['paging']['Article']['options']['order'] = array('Article.title' => 'desc');
+		$this->Paginator->params['paging']['Article']['options']['sort'] = null;
+		$result = $this->Paginator->sort('Title', 'title', array('direction' => 'desc'));
+		$this->assertPattern('/\/accounts\/index\/param\/page:1\/sort:title\/direction:asc" class="desc">Title<\/a>$/', $result);
+
+		$this->Paginator->params['paging']['Article']['options']['order'] = array('Article.title' => 'desc');
+		$this->Paginator->params['paging']['Article']['options']['sort'] = null;
+		$result = $this->Paginator->sort('Title', 'title', array('direction' => 'asc'));
+		$this->assertPattern('/\/accounts\/index\/param\/page:1\/sort:title\/direction:asc" class="desc">Title<\/a>$/', $result);
+
+		$this->Paginator->params['paging']['Article']['options']['order'] = array('Article.title' => 'asc');
+		$this->Paginator->params['paging']['Article']['options']['sort'] = null;
+		$result = $this->Paginator->sort('Title', 'title', array('direction' => 'asc'));
+		$this->assertPattern('/\/accounts\/index\/param\/page:1\/sort:title\/direction:desc" class="asc">Title<\/a>$/', $result);
+
+		$this->Paginator->params['paging']['Article']['options']['order'] = array('Article.title' => 'asc');
+		$this->Paginator->params['paging']['Article']['options']['sort'] = null;
+		$result = $this->Paginator->sort('Title', 'title', array('direction' => 'desc'));
+		$this->assertPattern('/\/accounts\/index\/param\/page:1\/sort:title\/direction:desc" class="asc">Title<\/a>$/', $result);
+
+		$this->Paginator->params['paging']['Article']['options']['order'] = array('Article.title' => 'asc');
+		$this->Paginator->params['paging']['Article']['options']['sort'] = null;
+		$result = $this->Paginator->sort('Title', 'title', array('direction' => 'desc', 'class' => 'foo'));
+		$this->assertPattern('/\/accounts\/index\/param\/page:1\/sort:title\/direction:desc" class="foo asc">Title<\/a>$/', $result);
+	}
+
+/**
+ * test that sort() works with virtual field order options.
+ *
+ * @return void
+ */
+	function testSortLinkWithVirtualField() {
+		Router::setRequestInfo(array(
+			array('plugin' => null, 'controller' => 'accounts', 'action' => 'index', 'pass' => array(), 'form' => array(), 'url' => array('url' => 'accounts/')),
+			array('base' => '', 'here' => '/accounts/', 'webroot' => '/')
+		));
+		$this->Paginator->params['paging']['Article']['options']['order'] = array('full_name' => 'asc');
+
+		$result = $this->Paginator->sort('Article.full_name');
+		$expected = array(
+			'a' => array('href' => '/accounts/index/page:1/sort:Article.full_name/direction:desc', 'class' => 'asc'),
+			'Article.full Name',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->sort('full_name');
+		$expected = array(
+			'a' => array('href' => '/accounts/index/page:1/sort:full_name/direction:desc', 'class' => 'asc'),
+			'Full Name',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->params['paging']['Article']['options']['order'] = array('full_name' => 'desc');
+		$result = $this->Paginator->sort('Article.full_name');
+		$expected = array(
+			'a' => array('href' => '/accounts/index/page:1/sort:Article.full_name/direction:asc', 'class' => 'desc'),
+			'Article.full Name',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->sort('full_name');
+		$expected = array(
+			'a' => array('href' => '/accounts/index/page:1/sort:full_name/direction:asc', 'class' => 'desc'),
+			'Full Name',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testSortLinksUsingDirectionOption method
+ *
+ * @access public
+ * @return void
+ */
+	function testSortLinksUsingDirectionOption(){
+		Router::reload();
+		Router::parse('/');
+		Router::setRequestInfo(array(
+			array('plugin' => null, 'controller' => 'accounts', 'action' => 'index', 'pass' => array(),
+				'form' => array(), 'url' => array('url' => 'accounts/', 'mod_rewrite' => 'true'), 'bare' => 0),
+			array('plugin' => null, 'controller' => null, 'action' => null, 'base' => '/', 'here' => '/accounts/',
+				'webroot' => '/', 'passedArgs' => array())
+		));
+		$this->Paginator->options(array('url' => array('param')));
+
+		$result = $this->Paginator->sort('TestTitle', 'title', array('direction' => 'desc'));
+		$expected = array(
+			'a' => array('href' => '/accounts/index/param/page:1/sort:title/direction:desc'),
+			'TestTitle',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->sort(array('asc' => 'ascending', 'desc' => 'descending'), 'title', array('direction' => 'desc'));
+		$expected = array(
+			'a' => array('href' => '/accounts/index/param/page:1/sort:title/direction:desc'),
+			'descending',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testSortLinksUsingDotNotation method
+ *
+ * @access public
+ * @return void
+ */
+	function testSortLinksUsingDotNotation() {
+		Router::reload();
+		Router::parse('/');
+		Router::setRequestInfo(array(
+			array('plugin' => null, 'controller' => 'accounts', 'action' => 'index', 'pass' => array(),  'form' => array(), 'url' => array('url' => 'accounts/', 'mod_rewrite' => 'true'), 'bare' => 0),
+			array('plugin' => null, 'controller' => null, 'action' => null, 'base' => '/officespace', 'here' => '/officespace/accounts/', 'webroot' => '/officespace/', 'passedArgs' => array())
+		));
+
+		$this->Paginator->params['paging']['Article']['options']['order'] = array('Article.title' => 'desc');
+		$result = $this->Paginator->sort('Title','Article.title');
+		$expected = array(
+			'a' => array('href' => '/officespace/accounts/index/page:1/sort:Article.title/direction:asc', 'class' => 'desc'),
+			'Title',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->params['paging']['Article']['options']['order'] = array('Article.title' => 'asc');
+		$result = $this->Paginator->sort('Title','Article.title');
+		$expected = array(
+			'a' => array('href' => '/officespace/accounts/index/page:1/sort:Article.title/direction:desc', 'class' => 'asc'),
+			'Title',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->params['paging']['Article']['options']['order'] = array('Account.title' => 'asc');
+		$result = $this->Paginator->sort('title');
+		$expected = array(
+			'a' => array('href' => '/officespace/accounts/index/page:1/sort:title/direction:asc'),
+			'Title',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testSortKey method
+ *
+ * @access public
+ * @return void
+ */
+	function testSortKey() {
+		$result = $this->Paginator->sortKey(null, array(
+			'order' => array('Article.title' => 'desc'
+		)));
+		$this->assertEqual('Article.title', $result);
+
+		$result = $this->Paginator->sortKey('Article', array('sort' => 'Article.title'));
+		$this->assertEqual($result, 'Article.title');
+
+		$result = $this->Paginator->sortKey('Article', array('sort' => 'Article'));
+		$this->assertEqual($result, 'Article');
+	}
+
+/**
+ * testSortDir method
+ *
+ * @access public
+ * @return void
+ */
+	function testSortDir() {
+		$result = $this->Paginator->sortDir();
+		$expected = 'asc';
+
+		$this->assertEqual($result, $expected);
+
+		$this->Paginator->params['paging']['Article']['options']['order'] = array('Article.title' => 'desc');
+		$result = $this->Paginator->sortDir();
+		$expected = 'desc';
+
+		$this->assertEqual($result, $expected);
+
+		unset($this->Paginator->params['paging']['Article']['options']);
+		$this->Paginator->params['paging']['Article']['options']['order'] = array('Article.title' => 'asc');
+		$result = $this->Paginator->sortDir();
+		$expected = 'asc';
+
+		$this->assertEqual($result, $expected);
+
+		unset($this->Paginator->params['paging']['Article']['options']);
+		$this->Paginator->params['paging']['Article']['options']['order'] = array('title' => 'desc');
+		$result = $this->Paginator->sortDir();
+		$expected = 'desc';
+
+		$this->assertEqual($result, $expected);
+
+		unset($this->Paginator->params['paging']['Article']['options']);
+		$this->Paginator->params['paging']['Article']['options']['order'] = array('title' => 'asc');
+		$result = $this->Paginator->sortDir();
+		$expected = 'asc';
+
+		$this->assertEqual($result, $expected);
+
+		unset($this->Paginator->params['paging']['Article']['options']);
+		$this->Paginator->params['paging']['Article']['options']['direction'] = 'asc';
+		$result = $this->Paginator->sortDir();
+		$expected = 'asc';
+
+		$this->assertEqual($result, $expected);
+
+		unset($this->Paginator->params['paging']['Article']['options']);
+		$this->Paginator->params['paging']['Article']['options']['direction'] = 'desc';
+		$result = $this->Paginator->sortDir();
+		$expected = 'desc';
+
+		$this->assertEqual($result, $expected);
+
+		unset($this->Paginator->params['paging']['Article']['options']);
+		$result = $this->Paginator->sortDir('Article', array('direction' => 'asc'));
+		$expected = 'asc';
+
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Paginator->sortDir('Article', array('direction' => 'desc'));
+		$expected = 'desc';
+
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Paginator->sortDir('Article', array('direction' => 'asc'));
+		$expected = 'asc';
+
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testSortAdminLinks method
+ *
+ * @access public
+ * @return void
+ */
+	function testSortAdminLinks() {
+		Configure::write('Routing.prefixes', array('admin'));
+
+		Router::reload();
+		Router::setRequestInfo(array(
+			array('pass' => array(), 'named' => array(), 'controller' => 'users', 'plugin' => null, 'action' => 'admin_index', 'prefix' => 'admin', 'admin' => true, 'url' => array('ext' => 'html', 'url' => 'admin/users'), 'form' => array()),
+			array('base' => '', 'here' => '/admin/users', 'webroot' => '/')
+		));
+		Router::parse('/admin/users');
+		$this->Paginator->params['paging']['Article']['page'] = 1;
+		$result = $this->Paginator->next('Next');
+		$expected = array(
+			'<span',
+			'a' => array('href' => '/admin/users/index/page:2', 'class' => 'next'),
+			'Next',
+			'/a',
+			'/span'
+		);
+		$this->assertTags($result, $expected);
+
+		Router::reload();
+		Router::setRequestInfo(array(
+			array('plugin' => null, 'controller' => 'test', 'action' => 'admin_index', 'pass' => array(), 'prefix' => 'admin', 'admin' => true, 'form' => array(), 'url' => array('url' => 'admin/test')),
+			array('plugin' => null, 'controller' => null, 'action' => null, 'base' => '', 'here' => '/admin/test', 'webroot' => '/')
+		));
+		Router::parse('/');
+		$this->Paginator->options(array('url' => array('param')));
+		$result = $this->Paginator->sort('title');
+		$expected = array(
+			'a' => array('href' => '/admin/test/index/param/page:1/sort:title/direction:asc'),
+			'Title',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->options(array('url' => array('param')));
+		$result = $this->Paginator->sort('Title', 'Article.title');
+		$expected = array(
+			'a' => array('href' => '/admin/test/index/param/page:1/sort:Article.title/direction:asc'),
+			'Title',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testUrlGeneration method
+ *
+ * @access public
+ * @return void
+ */
+	function testUrlGeneration() {
+		$result = $this->Paginator->sort('controller');
+		$expected = array(
+			'a' => array('href' => '/index/page:1/sort:controller/direction:asc'),
+			'Controller',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->url();
+		$this->assertEqual($result, '/index/page:1');
+
+		$this->Paginator->params['paging']['Article']['options']['page'] = 2;
+		$result = $this->Paginator->url();
+		$this->assertEqual($result, '/index/page:2');
+
+		$options = array('order' => array('Article' => 'desc'));
+		$result = $this->Paginator->url($options);
+		$this->assertEqual($result, '/index/page:2/sort:Article/direction:desc');
+
+		$this->Paginator->params['paging']['Article']['options']['page'] = 3;
+		$options = array('order' => array('Article.name' => 'desc'));
+		$result = $this->Paginator->url($options);
+		$this->assertEqual($result, '/index/page:3/sort:Article.name/direction:desc');
+	}
+
+/**
+ * test URL generation with prefix routes
+ *
+ * @access public
+ * @return void
+ */
+	function testUrlGenerationWithPrefixes() {
+		$_back = Configure::read('Routing');
+
+		Configure::write('Routing.prefixes', array('members'));
+		Router::reload();
+
+		Router::parse('/');
+
+		Router::setRequestInfo( array(
+			array('controller' => 'posts', 'action' => 'index', 'form' => array(), 'url' => array(), 'plugin' => null),
+			array('plugin' => null, 'controller' => null, 'action' => null, 'base' => '', 'here' => 'posts/index', 'webroot' => '/')
+		));
+
+		$this->Paginator->params['paging']['Article']['options']['page'] = 2;
+		$this->Paginator->params['paging']['Article']['page'] = 2;
+		$this->Paginator->params['paging']['Article']['prevPage'] = true;
+		$options = array('members' => true);
+
+		$result = $this->Paginator->url($options);
+		$expected = '/members/posts/index/page:2';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Paginator->sort('name', null, array('url' => $options));
+		$expected = array(
+			'a' => array('href' => '/members/posts/index/page:2/sort:name/direction:asc'),
+			'Name',
+			'/a'
+		);
+		$this->assertTags($result, $expected, true);
+
+		$result = $this->Paginator->next('next', array('url' => $options));
+		$expected = array(
+			'<span',
+			'a' => array('href' => '/members/posts/index/page:3', 'class' => 'next'),
+			'next',
+			'/a',
+			'/span'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->prev('prev', array('url' => $options));
+		$expected = array(
+			'<span',
+			'a' => array('href' => '/members/posts/index/page:1', 'class' => 'prev'),
+			'prev',
+			'/a',
+			'/span'
+		);
+		$this->assertTags($result, $expected);
+
+		$options = array('members' => true, 'controller' => 'posts', 'order' => array('name' => 'desc'));
+		$result = $this->Paginator->url($options);
+		$expected = '/members/posts/index/page:2/sort:name/direction:desc';
+		$this->assertEqual($result, $expected);
+
+		$options = array('controller' => 'posts', 'order' => array('Article.name' => 'desc'));
+		$result = $this->Paginator->url($options);
+		$expected = '/posts/index/page:2/sort:Article.name/direction:desc';
+		$this->assertEqual($result, $expected);
+
+		Configure::write('Routing', $_back);
+	}
+
+/**
+ * testOptions method
+ *
+ * @access public
+ * @return void
+ */
+	function testOptions() {
+		$this->Paginator->options('myDiv');
+		$this->assertEqual('myDiv', $this->Paginator->options['update']);
+
+		$this->Paginator->options = array();
+		$this->Paginator->params = array();
+
+		$options = array('paging' => array('Article' => array(
+			'order' => 'desc',
+			'sort' => 'title'
+		)));
+		$this->Paginator->options($options);
+
+		$expected = array('Article' => array(
+			'order' => 'desc',
+			'sort' => 'title'
+		));
+		$this->assertEqual($expected, $this->Paginator->params['paging']);
+
+		$this->Paginator->options = array();
+		$this->Paginator->params = array();
+
+		$options = array('Article' => array(
+			'order' => 'desc',
+			'sort' => 'title'
+		));
+		$this->Paginator->options($options);
+		$this->assertEqual($expected, $this->Paginator->params['paging']);
+
+		$options = array('paging' => array('Article' => array(
+			'order' => 'desc',
+			'sort' => 'Article.title'
+		)));
+		$this->Paginator->options($options);
+
+		$expected = array('Article' => array(
+			'order' => 'desc',
+			'sort' => 'Article.title'
+		));
+		$this->assertEqual($expected, $this->Paginator->params['paging']);
+	}
+
+/**
+ * testPassedArgsMergingWithUrlOptions method
+ *
+ * @access public
+ * @return void
+ */
+	function testPassedArgsMergingWithUrlOptions() {
+		Router::reload();
+		Router::parse('/');
+		Router::setRequestInfo(array(
+			array('plugin' => null, 'controller' => 'articles', 'action' => 'index', 'pass' => array('2'), 'named' => array('foo' => 'bar'), 'form' => array(), 'url' => array('url' => 'articles/index/2/foo:bar'), 'bare' => 0),
+			array('plugin' => null, 'controller' => null, 'action' => null, 'base' => '/', 'here' => '/articles/', 'webroot' => '/', 'passedArgs' => array(0 => '2', 'foo' => 'bar'))
+		));
+		$this->Paginator->params['paging'] = array(
+			'Article' => array(
+				'page' => 1, 'current' => 3, 'count' => 13,
+				'prevPage' => false, 'nextPage' => true, 'pageCount' => 8,
+				'defaults' => array(
+					'limit' => 3, 'step' => 1, 'order' => array(), 'conditions' => array()
+				),
+				'options' => array(
+					'page' => 1, 'limit' => 3, 'order' => array(), 'conditions' => array()
+				)
+			)
+		);
+
+		$this->Paginator->params['pass'] = array(2);
+		$this->Paginator->params['named'] = array('foo' => 'bar');
+		$this->Paginator->beforeRender();
+
+		$result = $this->Paginator->sort('title');
+		$expected = array(
+			'a' => array('href' => '/articles/index/2/page:1/foo:bar/sort:title/direction:asc'),
+			'Title',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->numbers();
+		$expected = array(
+			array('span' => array('class' => 'current')), '1', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/articles/index/2/page:2/foo:bar')), '2', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/articles/index/2/page:3/foo:bar')), '3', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/articles/index/2/page:4/foo:bar')), '4', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/articles/index/2/page:5/foo:bar')), '5', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/articles/index/2/page:6/foo:bar')), '6', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/articles/index/2/page:7/foo:bar')), '7', '/a', '/span',
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->next('Next');
+		$expected = array(
+			'<span',
+			'a' => array('href' => '/articles/index/2/page:2/foo:bar', 'class' => 'next'),
+			'Next',
+			'/a',
+			'/span'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testPagingLinks method
+ *
+ * @access public
+ * @return void
+ */
+	function testPagingLinks() {
+		$this->Paginator->params['paging'] = array('Client' => array(
+			'page' => 1, 'current' => 3, 'count' => 13, 'prevPage' => false, 'nextPage' => true, 'pageCount' => 5,
+			'defaults' => array('limit' => 3, 'step' => 1, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()),
+			'options' => array('page' => 1, 'limit' => 3, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()))
+		);
+		$result = $this->Paginator->prev('<< Previous', null, null, array('class' => 'disabled'));
+		$expected = array(
+			'span' => array('class' => 'disabled'),
+			'&lt;&lt; Previous',
+			'/span'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->prev('<< Previous', null, null, array('class' => 'disabled', 'tag' => 'div'));
+		$expected = array(
+			'div' => array('class' => 'disabled'),
+			'&lt;&lt; Previous',
+			'/div'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->params['paging']['Client']['page'] = 2;
+		$this->Paginator->params['paging']['Client']['prevPage'] = true;
+		$result = $this->Paginator->prev('<< Previous', null, null, array('class' => 'disabled'));
+		$expected = array(
+			'<span',
+			'a' => array('href' => '/index/page:1', 'class' => 'prev'),
+			'&lt;&lt; Previous',
+			'/a',
+			'/span'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->next('Next');
+		$expected = array(
+			'<span',
+			'a' => array('href' => '/index/page:3', 'class' => 'next'),
+			'Next',
+			'/a',
+			'/span'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->next('Next', array('tag' => 'li'));
+		$expected = array(
+			'<li',
+			'a' => array('href' => '/index/page:3', 'class' => 'next'),
+			'Next',
+			'/a',
+			'/li'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->prev('<< Previous', array('escape' => true));
+		$expected = array(
+			'<span',
+			'a' => array('href' => '/index/page:1', 'class' => 'prev'),
+			'&lt;&lt; Previous',
+			'/a',
+			'/span'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->prev('<< Previous', array('escape' => false));
+		$expected = array(
+			'<span',
+			'a' => array('href' => '/index/page:1', 'class' => 'prev'),
+			'preg:/<< Previous/',
+			'/a',
+			'/span'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->params['paging'] = array('Client' => array(
+			'page' => 1, 'current' => 1, 'count' => 13, 'prevPage' => false, 'nextPage' => true, 'pageCount' => 5,
+			'defaults' => array(),
+			'options' => array('page' => 1, 'limit' => 3, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()))
+		);
+
+		$result = $this->Paginator->prev('<< Previous', null, '<strong>Disabled</strong>');
+		$expected = array(
+			'span' => array('class' => 'prev'),
+			'&lt;strong&gt;Disabled&lt;/strong&gt;',
+			'/span'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->prev('<< Previous', null, '<strong>Disabled</strong>', array('escape' => true));
+		$expected = array(
+			'span' => array('class' => 'prev'),
+			'&lt;strong&gt;Disabled&lt;/strong&gt;',
+			'/span'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->prev('<< Previous', null, '<strong>Disabled</strong>', array('escape' => false));
+		$expected = array(
+			'span' => array('class' => 'prev'),
+			'<strong', 'Disabled', '/strong',
+			'/span'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->params['paging'] = array('Client' => array(
+			'page' => 1, 'current' => 3, 'count' => 13, 'prevPage' => false, 'nextPage' => true, 'pageCount' => 5,
+			'defaults' => array(),
+			'options' => array('page' => 1, 'limit' => 3, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()))
+		);
+
+		$this->Paginator->params['paging']['Client']['page'] = 2;
+		$this->Paginator->params['paging']['Client']['prevPage'] = true;
+		$result = $this->Paginator->prev('<< Previous', null, null, array('class' => 'disabled'));
+		$expected = array(
+			'<span',
+			'a' => array('href' => '/index/page:1/limit:3/sort:Client.name/direction:DESC', 'class' => 'prev'),
+			'&lt;&lt; Previous',
+			'/a',
+			'/span'
+		);
+		$this->assertTags($result, $expected, true);
+
+		$result = $this->Paginator->next('Next');
+		$expected = array(
+			'<span',
+			'a' => array('href' => '/index/page:3/limit:3/sort:Client.name/direction:DESC', 'class' => 'next'),
+			'Next',
+			'/a',
+			'/span'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->params['paging'] = array('Client' => array(
+			'page' => 2, 'current' => 1, 'count' => 13, 'prevPage' => true, 'nextPage' => false, 'pageCount' => 2,
+			'defaults' => array(),
+			'options' => array('page' => 2, 'limit' => 10, 'order' => array(), 'conditions' => array())
+		));
+		$result = $this->Paginator->prev('Prev');
+		$expected = array(
+			'<span',
+			'a' => array('href' => '/index/page:1/limit:10', 'class' => 'prev'),
+			'Prev',
+			'/a',
+			'/span'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->params['paging'] = array(
+			'Client' => array(
+				'page' => 2, 'current' => 1, 'count' => 13, 'prevPage' => true,
+				'nextPage' => false, 'pageCount' => 2,
+				'defaults' => array(),
+				'options' => array(
+					'page' => 2, 'limit' => 10, 'order' => array(), 'conditions' => array()
+				)
+			)
+		);
+		$this->Paginator->options(array('url' => array(12, 'page' => 3)));
+		$result = $this->Paginator->prev('Prev', array('url' => array('foo' => 'bar')));
+		$expected = array(
+			'<span',
+			'a' => array('href' => '/index/12/page:1/limit:10/foo:bar', 'class' => 'prev'),
+			'Prev',
+			'/a',
+			'/span'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test that __pagingLink methods use $options when $disabledOptions is an empty value.
+ * allowing you to use shortcut syntax
+ *
+ * @return void
+ */
+	function testPagingLinksOptionsReplaceEmptyDisabledOptions() {
+		$this->Paginator->params['paging'] = array(
+			'Client' => array(
+				'page' => 1, 'current' => 3, 'count' => 13, 'prevPage' => false,
+				'nextPage' => true, 'pageCount' => 5,
+				'defaults' => array(
+					'limit' => 3, 'step' => 1, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()
+				),
+				'options' => array(
+					'page' => 1, 'limit' => 3, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()
+				)
+			)
+		);
+		$result = $this->Paginator->prev('<< Previous', array('escape' => false));
+		$expected = array(
+			'span' => array('class' => 'prev'),
+			'preg:/<< Previous/',
+			'/span'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->next('Next >>', array('escape' => false));
+		$expected = array(
+			'<span',
+			'a' => array('href' => '/index/page:2', 'class' => 'next'),
+			'preg:/Next >>/',
+			'/a',
+			'/span'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testPagingLinksNotDefaultModel
+ *
+ * Test the creation of paging links when the non default model is used.
+ *
+ * @access public
+ * @return void
+ */
+	function testPagingLinksNotDefaultModel() {
+		// Multiple Model Paginate
+		$this->Paginator->params['paging'] = array(
+			'Client' => array(
+				'page' => 1, 'current' => 3, 'count' => 13, 'prevPage' => false, 'nextPage' => true, 'pageCount' => 5,
+				'defaults' => array( 'limit'=>3, 'order' => array('Client.name' => 'DESC')),
+				'options' => array('page' => 1, 'limit' => 3, 'order' => array('Client.name' => 'DESC'), 'conditions' => array())
+			),
+			'Server' => array(
+				'page' => 1, 'current' => 1, 'count' => 5, 'prevPage' => false, 'nextPage' => false, 'pageCount' => 5,
+				'defaults' => array(),
+				'options' => array('page' => 1, 'limit' => 5, 'order' => array('Server.name' => 'ASC'), 'conditions' => array())
+			)
+		);
+		$result = $this->Paginator->next('Next', array('model' => 'Client'));
+		$expected = array(
+			'<span',
+			'a' => array('href' => '/index/page:2', 'class' => 'next'),
+			'Next',
+			'/a',
+			'/span'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->next('Next', array('model' => 'Server'), 'No Next', array('model' => 'Server'));
+		$expected = array(
+			'span' => array('class' => 'next'), 'No Next', '/span'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testGenericLinks method
+ *
+ * @access public
+ * @return void
+ */
+	function testGenericLinks() {
+		$result = $this->Paginator->link('Sort by title on page 5', array('sort' => 'title', 'page' => 5, 'direction' => 'desc'));
+		$expected = array(
+			'a' => array('href' => '/index/page:5/sort:title/direction:desc'),
+			'Sort by title on page 5',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->params['paging']['Article']['options']['page'] = 2;
+		$result = $this->Paginator->link('Sort by title', array('sort' => 'title', 'direction' => 'desc'));
+		$expected = array(
+			'a' => array('href' => '/index/page:2/sort:title/direction:desc'),
+			'Sort by title',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->params['paging']['Article']['options']['page'] = 4;
+		$result = $this->Paginator->link('Sort by title on page 4', array('sort' => 'Article.title', 'direction' => 'desc'));
+		$expected = array(
+			'a' => array('href' => '/index/page:4/sort:Article.title/direction:desc'),
+			'Sort by title on page 4',
+			'/a'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * Tests generation of generic links with preset options
+ *
+ * @access public
+ * @return void
+ */
+	function testGenericLinksWithPresetOptions() {
+		$result = $this->Paginator->link('Foo!', array('page' => 1));
+		$this->assertTags($result, array('a' => array('href' => '/index/page:1'), 'Foo!', '/a'));
+
+		$this->Paginator->options(array('sort' => 'title', 'direction' => 'desc'));
+		$result = $this->Paginator->link('Foo!', array('page' => 1));
+		$this->assertTags($result, array(
+			'a' => array(
+				'href' => '/index/page:1',
+				'sort' => 'title',
+				'direction' => 'desc'
+			),
+			'Foo!',
+			'/a'
+		));
+
+		$this->Paginator->options(array('sort' => null, 'direction' => null));
+		$result = $this->Paginator->link('Foo!', array('page' => 1));
+		$this->assertTags($result, array('a' => array('href' => '/index/page:1'), 'Foo!', '/a'));
+
+		$this->Paginator->options(array('url' => array(
+			'sort' => 'title',
+			'direction' => 'desc'
+		)));
+		$result = $this->Paginator->link('Foo!', array('page' => 1));
+		$this->assertTags($result, array(
+			'a' => array('href' => '/index/page:1/sort:title/direction:desc'),
+			'Foo!',
+			'/a'
+		));
+	}
+
+/**
+ * testNumbers method
+ *
+ * @access public
+ * @return void
+ */
+	function testNumbers() {
+		$this->Paginator->params['paging'] = array('Client' => array(
+			'page' => 8, 'current' => 3, 'count' => 30, 'prevPage' => false, 'nextPage' => 2, 'pageCount' => 15,
+			'defaults' => array('limit' => 3, 'step' => 1, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()),
+			'options' => array('page' => 1, 'limit' => 3, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()))
+		);
+		$result = $this->Paginator->numbers();
+		$expected = array(
+			array('span' => array()), array('a' => array('href' => '/index/page:4')), '4', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:5')), '5', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:6')), '6', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:7')), '7', '/a', '/span',
+			' | ',
+			array('span' => array('class' => 'current')), '8', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:9')), '9', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:10')), '10', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:11')), '11', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:12')), '12', '/a', '/span',
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->numbers(array('tag' => 'li'));
+		$expected = array(
+			array('li' => array()), array('a' => array('href' => '/index/page:4')), '4', '/a', '/li',
+			' | ',
+			array('li' => array()), array('a' => array('href' => '/index/page:5')), '5', '/a', '/li',
+			' | ',
+			array('li' => array()), array('a' => array('href' => '/index/page:6')), '6', '/a', '/li',
+			' | ',
+			array('li' => array()), array('a' => array('href' => '/index/page:7')), '7', '/a', '/li',
+			' | ',
+			array('li' => array('class' => 'current')), '8', '/li',
+			' | ',
+			array('li' => array()), array('a' => array('href' => '/index/page:9')), '9', '/a', '/li',
+			' | ',
+			array('li' => array()), array('a' => array('href' => '/index/page:10')), '10', '/a', '/li',
+			' | ',
+			array('li' => array()), array('a' => array('href' => '/index/page:11')), '11', '/a', '/li',
+			' | ',
+			array('li' => array()), array('a' => array('href' => '/index/page:12')), '12', '/a', '/li',
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->numbers(array('tag' => 'li', 'separator' => false));
+		$expected = array(
+			array('li' => array()), array('a' => array('href' => '/index/page:4')), '4', '/a', '/li',
+			array('li' => array()), array('a' => array('href' => '/index/page:5')), '5', '/a', '/li',
+			array('li' => array()), array('a' => array('href' => '/index/page:6')), '6', '/a', '/li',
+			array('li' => array()), array('a' => array('href' => '/index/page:7')), '7', '/a', '/li',
+			array('li' => array('class' => 'current')), '8', '/li',
+			array('li' => array()), array('a' => array('href' => '/index/page:9')), '9', '/a', '/li',
+			array('li' => array()), array('a' => array('href' => '/index/page:10')), '10', '/a', '/li',
+			array('li' => array()), array('a' => array('href' => '/index/page:11')), '11', '/a', '/li',
+			array('li' => array()), array('a' => array('href' => '/index/page:12')), '12', '/a', '/li',
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->numbers(true);
+		$expected = array(
+			array('span' => array()), array('a' => array('href' => '/index/page:1')), 'first', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4')), '4', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:5')), '5', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:6')), '6', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:7')), '7', '/a', '/span',
+			' | ',
+			array('span' => array('class' => 'current')), '8', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:9')), '9', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:10')), '10', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:11')), '11', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:12')), '12', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:15')), 'last', '/a', '/span',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->params['paging'] = array('Client' => array(
+			'page' => 1, 'current' => 3, 'count' => 30, 'prevPage' => false, 'nextPage' => 2, 'pageCount' => 15,
+			'defaults' => array('limit' => 3, 'step' => 1, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()),
+			'options' => array('page' => 1, 'limit' => 3, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()))
+		);
+		$result = $this->Paginator->numbers();
+		$expected = array(
+			array('span' => array('class' => 'current')), '1', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:2')), '2', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:3')), '3', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4')), '4', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:5')), '5', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:6')), '6', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:7')), '7', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:8')), '8', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:9')), '9', '/a', '/span',
+		);
+		$this->assertTags($result, $expected);
+
+
+		$this->Paginator->params['paging'] = array('Client' => array(
+			'page' => 14, 'current' => 3, 'count' => 30, 'prevPage' => false, 'nextPage' => 2, 'pageCount' => 15,
+			'defaults' => array('limit' => 3, 'step' => 1, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()),
+			'options' => array('page' => 1, 'limit' => 3, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()))
+		);
+		$result = $this->Paginator->numbers();
+		$expected = array(
+			array('span' => array()), array('a' => array('href' => '/index/page:7')), '7', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:8')), '8', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:9')), '9', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:10')), '10', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:11')), '11', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:12')), '12', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:13')), '13', '/a', '/span',
+			' | ',
+			array('span' => array('class' => 'current')), '14', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:15')), '15', '/a', '/span',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->params['paging'] = array('Client' => array(
+			'page' => 2, 'current' => 3, 'count' => 27, 'prevPage' => false, 'nextPage' => 2, 'pageCount' => 9,
+			'defaults' => array('limit' => 3, 'step' => 1, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()),
+			'options' => array('page' => 1, 'limit' => 3, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()))
+		);
+
+		$result = $this->Paginator->numbers(array('first' => 1));
+		$expected = array(
+			array('span' => array()), array('a' => array('href' => '/index/page:1')), '1', '/a', '/span',
+			' | ',
+			array('span' => array('class' => 'current')), '2', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:3')), '3', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4')), '4', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:5')), '5', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:6')), '6', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:7')), '7', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:8')), '8', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:9')), '9', '/a', '/span',
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->numbers(array('last' => 1));
+		$expected = array(
+			array('span' => array()), array('a' => array('href' => '/index/page:1')), '1', '/a', '/span',
+			' | ',
+			array('span' => array('class' => 'current')), '2', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:3')), '3', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4')), '4', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:5')), '5', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:6')), '6', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:7')), '7', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:8')), '8', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:9')), '9', '/a', '/span',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->params['paging'] = array('Client' => array(
+			'page' => 15, 'current' => 3, 'count' => 30, 'prevPage' => false, 'nextPage' => 2, 'pageCount' => 15,
+			'defaults' => array('limit' => 3, 'step' => 1, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()),
+			'options' => array('page' => 1, 'limit' => 3, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()))
+		);
+
+		$result = $this->Paginator->numbers(array('first' => 1));
+		$expected = array(
+			array('span' => array()), array('a' => array('href' => '/index/page:1')), '1', '/a', '/span',
+			'...',
+			array('span' => array()), array('a' => array('href' => '/index/page:7')), '7', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:8')), '8', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:9')), '9', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:10')), '10', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:11')), '11', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:12')), '12', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:13')), '13', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:14')), '14', '/a', '/span',
+			' | ',
+			array('span' => array('class' => 'current')), '15', '/span',
+
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->params['paging'] = array('Client' => array(
+			'page' => 10, 'current' => 3, 'count' => 30, 'prevPage' => false, 'nextPage' => 2, 'pageCount' => 15,
+			'defaults' => array('limit' => 3, 'step' => 1, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()),
+			'options' => array('page' => 1, 'limit' => 3, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()))
+		);
+
+		$result = $this->Paginator->numbers(array('first' => 1, 'last' => 1));
+		$expected = array(
+			array('span' => array()), array('a' => array('href' => '/index/page:1')), '1', '/a', '/span',
+			'...',
+			array('span' => array()), array('a' => array('href' => '/index/page:6')), '6', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:7')), '7', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:8')), '8', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:9')), '9', '/a', '/span',
+			' | ',
+			array('span' => array('class' => 'current')), '10', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:11')), '11', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:12')), '12', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:13')), '13', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:14')), '14', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:15')), '15', '/a', '/span',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->params['paging'] = array('Client' => array(
+			'page' => 6, 'current' => 15, 'count' => 623, 'prevPage' => 1, 'nextPage' => 1, 'pageCount' => 42,
+			'defaults' => array('limit' => 15, 'step' => 1, 'page' => 1, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()),
+			'options' => array('page' => 6, 'limit' => 15, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()))
+		);
+
+		$result = $this->Paginator->numbers(array('first' => 1, 'last' => 1));
+		$expected = array(
+			array('span' => array()), array('a' => array('href' => '/index/page:1')), '1', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:2')), '2', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:3')), '3', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4')), '4', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:5')), '5', '/a', '/span',
+			' | ',
+			array('span' => array('class' => 'current')), '6', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:7')), '7', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:8')), '8', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:9')), '9', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:10')), '10', '/a', '/span',
+			'...',
+			array('span' => array()), array('a' => array('href' => '/index/page:42')), '42', '/a', '/span',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->params['paging'] = array('Client' => array(
+			'page' => 37, 'current' => 15, 'count' => 623, 'prevPage' => 1, 'nextPage' => 1, 'pageCount' => 42,
+			'defaults' => array('limit' => 15, 'step' => 1, 'page' => 1, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()),
+			'options' => array('page' => 37, 'limit' => 15, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()))
+		);
+
+		$result = $this->Paginator->numbers(array('first' => 1, 'last' => 1));
+		$expected = array(
+			array('span' => array()), array('a' => array('href' => '/index/page:1')), '1', '/a', '/span',
+			'...',
+			array('span' => array()), array('a' => array('href' => '/index/page:33')), '33', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:34')), '34', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:35')), '35', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:36')), '36', '/a', '/span',
+			' | ',
+			array('span' => array('class' => 'current')), '37', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:38')), '38', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:39')), '39', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:40')), '40', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:41')), '41', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:42')), '42', '/a', '/span',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->params['paging'] = array(
+			'Client' => array(
+				'page' => 1,
+				'current' => 10,
+				'count' => 30,
+				'prevPage' => false,
+				'nextPage' => 2,
+				'pageCount' => 3,
+				'defaults' => array(
+					'limit' => 3,
+					'step' => 1,
+					'order' => array('Client.name' => 'DESC'),
+					'conditions' => array()
+				),
+				'options' => array(
+					'page' => 1,
+					'limit' => 3,
+					'order' => array('Client.name' => 'DESC'),
+					'conditions' => array()
+				)
+			)
+		);
+		$options = array('modulus' => 10);
+		$result = $this->Paginator->numbers($options);
+		$expected = array(
+			array('span' => array('class' => 'current')), '1', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:2')), '2', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:3')), '3', '/a', '/span',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->params['paging'] = array('Client' => array(
+			'page' => 2, 'current' => 10, 'count' => 31, 'prevPage' => true, 'nextPage' => true, 'pageCount' => 4,
+			'defaults' => array('limit' => 10),
+			'options' => array('page' => 1, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()))
+		);
+		$result = $this->Paginator->numbers();
+		$expected = array(
+			array('span' => array()), array('a' => array('href' => '/index/page:1/sort:Client.name/direction:DESC')), '1', '/a', '/span',
+			' | ',
+			array('span' => array('class' => 'current')), '2', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:3/sort:Client.name/direction:DESC')), '3', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4/sort:Client.name/direction:DESC')), '4', '/a', '/span',
+		);
+		$this->assertTags($result, $expected);
+
+
+		$this->Paginator->params['paging'] = array('Client' => array(
+			'page' => 4895, 'current' => 10, 'count' => 48962, 'prevPage' => 1, 'nextPage' => 1, 'pageCount' => 4897,
+			'defaults' => array('limit' => 10),
+			'options' => array('page' => 4894, 'limit' => 10, 'order' => 'Client.name DESC', 'conditions' => array()))
+		);
+
+		$result = $this->Paginator->numbers(array('first' => 2, 'modulus' => 2, 'last' => 2));
+		$expected = array(
+			array('span' => array()), array('a' => array('href' => '/index/page:1')), '1', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:2')), '2', '/a', '/span',
+			'...',
+			array('span' => array()), array('a' => array('href' => '/index/page:4894')), '4894', '/a', '/span',
+			' | ',
+			array('span' => array('class' => 'current')), '4895', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4896')), '4896', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4897')), '4897', '/a', '/span',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->params['paging']['Client']['page'] = 3;
+
+		$result = $this->Paginator->numbers(array('first' => 2, 'modulus' => 2, 'last' => 2));
+		$expected = array(
+			array('span' => array()), array('a' => array('href' => '/index/page:1')), '1', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:2')), '2', '/a', '/span',
+			' | ',
+			array('span' => array('class' => 'current')), '3', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4')), '4', '/a', '/span',
+			'...',
+			array('span' => array()), array('a' => array('href' => '/index/page:4896')), '4896', '/a', '/span',
+			' | ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4897')), '4897', '/a', '/span',
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->numbers(array('first' => 2, 'modulus' => 2, 'last' => 2, 'separator' => ' - '));
+		$expected = array(
+			array('span' => array()), array('a' => array('href' => '/index/page:1')), '1', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:2')), '2', '/a', '/span',
+			' - ',
+			array('span' => array('class' => 'current')), '3', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4')), '4', '/a', '/span',
+			'...',
+			array('span' => array()), array('a' => array('href' => '/index/page:4896')), '4896', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4897')), '4897', '/a', '/span',
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->numbers(array('first' => 5, 'modulus' => 5, 'last' => 5, 'separator' => ' - '));
+		$expected = array(
+			array('span' => array()), array('a' => array('href' => '/index/page:1')), '1', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:2')), '2', '/a', '/span',
+			' - ',
+			array('span' => array('class' => 'current')), '3', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4')), '4', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:5')), '5', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:6')), '6', '/a', '/span',
+			'...',
+			array('span' => array()), array('a' => array('href' => '/index/page:4893')), '4893', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4894')), '4894', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4895')), '4895', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4896')), '4896', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4897')), '4897', '/a', '/span',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->params['paging']['Client']['page'] = 4893;
+		$result = $this->Paginator->numbers(array('first' => 5, 'modulus' => 4, 'last' => 5, 'separator' => ' - '));
+		$expected = array(
+			array('span' => array()), array('a' => array('href' => '/index/page:1')), '1', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:2')), '2', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:3')), '3', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4')), '4', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:5')), '5', '/a', '/span',
+			'...',
+			array('span' => array()), array('a' => array('href' => '/index/page:4891')), '4891', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4892')), '4892', '/a', '/span',
+			' - ',
+			array('span' => array('class' => 'current')), '4893', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4894')), '4894', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4895')), '4895', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4896')), '4896', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4897')), '4897', '/a', '/span',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->params['paging']['Client']['page'] = 58;
+		$result = $this->Paginator->numbers(array('first' => 5, 'modulus' => 4, 'last' => 5, 'separator' => ' - '));
+		$expected = array(
+			array('span' => array()), array('a' => array('href' => '/index/page:1')), '1', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:2')), '2', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:3')), '3', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4')), '4', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:5')), '5', '/a', '/span',
+			'...',
+			array('span' => array()), array('a' => array('href' => '/index/page:56')), '56', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:57')), '57', '/a', '/span',
+			' - ',
+			array('span' => array('class' => 'current')), '58', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:59')), '59', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:60')), '60', '/a', '/span',
+			'...',
+			array('span' => array()), array('a' => array('href' => '/index/page:4893')), '4893', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4894')), '4894', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4895')), '4895', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4896')), '4896', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4897')), '4897', '/a', '/span',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->params['paging']['Client']['page'] = 5;
+		$result = $this->Paginator->numbers(array('first' => 5, 'modulus' => 4, 'last' => 5, 'separator' => ' - '));
+		$expected = array(
+			array('span' => array()), array('a' => array('href' => '/index/page:1')), '1', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:2')), '2', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:3')), '3', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4')), '4', '/a', '/span',
+			' - ',
+			array('span' => array('class' => 'current')), '5', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:6')), '6', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:7')), '7', '/a', '/span',
+			'...',
+			array('span' => array()), array('a' => array('href' => '/index/page:4893')), '4893', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4894')), '4894', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4895')), '4895', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4896')), '4896', '/a', '/span',
+			' - ',
+			array('span' => array()), array('a' => array('href' => '/index/page:4897')), '4897', '/a', '/span',
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testFirstAndLast method
+ *
+ * @access public
+ * @return void
+ */
+	function testFirstAndLast() {
+		$this->Paginator->params['paging'] = array('Client' => array(
+			'page' => 1, 'current' => 3, 'count' => 30, 'prevPage' => false, 'nextPage' => 2, 'pageCount' => 15,
+			'defaults' => array('limit' => 3, 'step' => 1, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()),
+			'options' => array('page' => 1, 'limit' => 3, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()))
+		);
+
+		$result = $this->Paginator->first();
+		$expected = '';
+		$this->assertEqual($result, $expected);
+
+		$this->Paginator->params['paging'] = array('Client' => array(
+			'page' => 4, 'current' => 3, 'count' => 30, 'prevPage' => false, 'nextPage' => 2, 'pageCount' => 15,
+			'defaults' => array('limit' => 3, 'step' => 1, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()),
+			'options' => array('page' => 1, 'limit' => 3, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()))
+		);
+
+		$result = $this->Paginator->first();
+		$expected = array(
+			'<span',
+			'a' => array('href' => '/index/page:1'),
+			'&lt;&lt; first',
+			'/a',
+			'/span'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->first('<<', array('tag' => 'li'));
+		$expected = array(
+			'<li',
+			'a' => array('href' => '/index/page:1'),
+			'&lt;&lt;',
+			'/a',
+			'/li'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->last();
+		$expected = array(
+			'<span',
+			'a' => array('href' => '/index/page:15'),
+			'last &gt;&gt;',
+			'/a',
+			'/span'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->last(1);
+		$expected = array(
+			'...',
+			'<span',
+			'a' => array('href' => '/index/page:15'),
+			'15',
+			'/a',
+			'/span'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->last(2);
+		$expected = array(
+			'...',
+			'<span',
+			array('a' => array('href' => '/index/page:14')), '14', '/a',
+			'/span',
+			' | ',
+			'<span',
+			array('a' => array('href' => '/index/page:15')), '15', '/a',
+			'/span',
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->last(2, array('tag' => 'li'));
+		$expected = array(
+			'...',
+			'<li',
+			array('a' => array('href' => '/index/page:14')), '14', '/a',
+			'/li',
+			' | ',
+			'<li',
+			array('a' => array('href' => '/index/page:15')), '15', '/a',
+			'/li',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->params['paging'] = array('Client' => array(
+			'page' => 15, 'current' => 3, 'count' => 30, 'prevPage' => false, 'nextPage' => 2, 'pageCount' => 15,
+			'defaults' => array('limit' => 3, 'step' => 1, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()),
+			'options' => array('page' => 1, 'limit' => 3, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()))
+		);
+		$result = $this->Paginator->last();
+		$expected = '';
+		$this->assertEqual($result, $expected);
+
+		$this->Paginator->params['paging'] = array('Client' => array(
+			'page' => 4, 'current' => 3, 'count' => 30, 'prevPage' => false, 'nextPage' => 2, 'pageCount' => 15,
+			'defaults' => array('limit' => 3),
+			'options' => array('page' => 1, 'limit' => 3, 'order' => array('Client.name' => 'DESC'), 'conditions' => array()))
+		);
+
+		$result = $this->Paginator->first();
+		$expected = array(
+			'<span',
+			array('a' => array('href' => '/index/page:1/sort:Client.name/direction:DESC')), '&lt;&lt; first', '/a',
+			'/span',
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->last();
+		$expected = array(
+			'<span',
+			array('a' => array('href' => '/index/page:15/sort:Client.name/direction:DESC')), 'last &gt;&gt;', '/a',
+			'/span',
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->last(1);
+		$expected = array(
+			'...',
+			'<span',
+			array('a' => array('href' => '/index/page:15/sort:Client.name/direction:DESC')), '15', '/a',
+			'/span',
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Paginator->last(2);
+		$expected = array(
+			'...',
+			'<span',
+			array('a' => array('href' => '/index/page:14/sort:Client.name/direction:DESC')), '14', '/a',
+			'/span',
+			' | ',
+			'<span',
+			array('a' => array('href' => '/index/page:15/sort:Client.name/direction:DESC')), '15', '/a',
+			'/span',
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->options(array('url' => array('full_base' => true)));
+		$result = $this->Paginator->first();
+
+		$expected = array(
+			'<span',
+			array('a' => array('href' => FULL_BASE_URL . '/index/page:1/sort:Client.name/direction:DESC')), '&lt;&lt; first', '/a',
+			'/span',
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testCounter method
+ *
+ * @access public
+ * @return void
+ */
+	function testCounter() {
+		$this->Paginator->params['paging'] = array(
+			'Client' => array(
+				'page' => 1,
+				'current' => 3,
+				'count' => 13,
+				'prevPage' => false,
+				'nextPage' => true,
+				'pageCount' => 5,
+				'defaults' => array(
+					'limit' => 3,
+					'step' => 1,
+					'order' => array('Client.name' => 'DESC'),
+					'conditions' => array()
+				),
+				'options' => array(
+					'page' => 1,
+					'limit' => 3,
+					'order' => array('Client.name' => 'DESC'),
+					'conditions' => array(),
+					'separator' => 'of'
+				),
+			)
+		);
+		$input = 'Page %page% of %pages%, showing %current% records out of %count% total, ';
+		$input .= 'starting on record %start%, ending on %end%';
+		$result = $this->Paginator->counter($input);
+		$expected = 'Page 1 of 5, showing 3 records out of 13 total, starting on record 1, ';
+		$expected .= 'ending on 3';
+		$this->assertEqual($result, $expected);
+
+		$input = 'Page {:page} of {:pages}, showing {:current} records out of {:count} total, ';
+		$input .= 'starting on record {:start}, ending on {:end}';
+		$result = $this->Paginator->counter($input);
+		$this->assertEqual($result, $expected);
+
+		$input = 'Page %page% of %pages%';
+		$result = $this->Paginator->counter($input);
+		$expected = 'Page 1 of 5';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Paginator->counter(array('format' => $input));
+		$expected = 'Page 1 of 5';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Paginator->counter(array('format' => 'pages'));
+		$expected = '1 of 5';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Paginator->counter(array('format' => 'range'));
+		$expected = '1 - 3 of 13';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testHasPage method
+ *
+ * @access public
+ * @return void
+ */
+	function testHasPage() {
+		$result = $this->Paginator->hasPage('Article', 15);
+		$this->assertFalse($result);
+
+		$result = $this->Paginator->hasPage('UndefinedModel', 2);
+		$this->assertFalse($result);
+
+		$result = $this->Paginator->hasPage('Article', 2);
+		$this->assertTrue($result);
+
+		$result = $this->Paginator->hasPage(2);
+		$this->assertTrue($result);
+	}
+
+/**
+ * testWithPlugin method
+ *
+ * @access public
+ * @return void
+ */
+	function testWithPlugin() {
+		Router::reload();
+		Router::setRequestInfo(array(
+			array(
+				'pass' => array(), 'named' => array(), 'prefix' => null, 'form' => array(),
+				'controller' => 'magazines', 'plugin' => 'my_plugin', 'action' => 'index',
+				'url' => array('ext' => 'html', 'url' => 'my_plugin/magazines')),
+			array('base' => '', 'here' => '/my_plugin/magazines', 'webroot' => '/')
+		));
+
+		$result = $this->Paginator->link('Page 3', array('page' => 3));
+		$expected = array(
+			'a' => array('href' => '/my_plugin/magazines/index/page:3'), 'Page 3', '/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->options(array('url' => array('action' => 'another_index')));
+		$result = $this->Paginator->link('Page 3', array('page' => 3));
+		$expected = array(
+			'a' => array('href' => '/my_plugin/magazines/another_index/page:3'), 'Page 3', '/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->options(array('url' => array('controller' => 'issues')));
+		$result = $this->Paginator->link('Page 3', array('page' => 3));
+		$expected = array(
+			'a' => array('href' => '/my_plugin/issues/index/page:3'), 'Page 3', '/a'
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Paginator->options(array('url' => array('plugin' => null)));
+		$result = $this->Paginator->link('Page 3', array('page' => 3));
+		$expected = array(
+			'a' => array('/magazines/index/page:3'), 'Page 3', '/a'
+		);
+
+		$this->Paginator->options(array('url' => array('plugin' => null, 'controller' => 'issues')));
+		$result = $this->Paginator->link('Page 3', array('page' => 3));
+		$expected = array(
+			'a' => array('href' => '/issues/index/page:3'), 'Page 3', '/a'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testNextLinkUsingDotNotation method
+ *
+ * @access public
+ * @return void
+ */
+	function testNextLinkUsingDotNotation() {
+		Router::reload();
+		Router::parse('/');
+		Router::setRequestInfo(array(
+			array('plugin' => null, 'controller' => 'accounts', 'action' => 'index', 'pass' => array(), 'form' => array(), 'url' => array('url' => 'accounts/', 'mod_rewrite' => 'true'), 'bare' => 0),
+			array('plugin' => null, 'controller' => null, 'action' => null, 'base' => '/officespace', 'here' => '/officespace/accounts/', 'webroot' => '/officespace/', 'passedArgs' => array())
+		));
+
+		$this->Paginator->params['paging']['Article']['options']['order'] = array('Article.title' => 'asc');
+		$this->Paginator->params['paging']['Article']['page'] = 1;
+
+		$test = array('url'=> array(
+			'page'=> '1',
+			'sort'=>'Article.title',
+			'direction'=>'asc',
+		));
+		$this->Paginator->options($test);
+
+		$result = $this->Paginator->next('Next');
+		$expected = array(
+			'<span',
+			'a' => array('href' => '/officespace/accounts/index/page:2/sort:Article.title/direction:asc', 'class' => 'next'),
+			'Next',
+			'/a',
+			'/span',
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test that mock classes injected into paginatorHelper are called when using link()
+ *
+ * @return void
+ */
+	function testMockAjaxProviderClassInjection() {
+		$Paginator =& new PaginatorHelper(array('ajax' => 'PaginatorMockJs'));
+		$Paginator->params['paging'] = array(
+			'Article' => array(
+				'current' => 9,
+				'count' => 62,
+				'prevPage' => false,
+				'nextPage' => true,
+				'pageCount' => 7,
+				'defaults' => array(),
+				'options' => array()
+			)
+		);
+		$Paginator->PaginatorMockJs =& new PaginatorMockJsHelper();
+		$Paginator->PaginatorMockJs->expectOnce('link');
+		$result = $Paginator->link('Page 2', array('page' => 2), array('update' => '#content'));
+
+		$this->expectError();
+		$Paginator =& new PaginatorHelper(array('ajax' => 'Form'));
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/view/helpers/prototype_engine.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/view/helpers/prototype_engine.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/view/helpers/prototype_engine.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,380 @@
+<?php
+/**
+ * PrototypeEngine TestCase
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP : Rapid Development Framework <http://www.cakephp.org/>
+ * Copyright 2005-2012, 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.
+ *
+ * @copyright       Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link            http://cakephp.org CakePHP Project
+ * @package         cake.tests
+ * @subpackage      cake.tests.cases.views.helpers
+ * @license         MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Helper', array('Html', 'Js', 'PrototypeEngine'));
+
+class PrototypeEngineHelperTestCase extends CakeTestCase {
+/**
+ * startTest
+ *
+ * @return void
+ */
+	function startTest() {
+		$this->Proto =& new PrototypeEngineHelper();
+	}
+
+/**
+ * end test
+ *
+ * @return void
+ */
+	function endTest() {
+		unset($this->Proto);
+	}
+
+/**
+ * test selector method
+ *
+ * @return void
+ */
+	function testSelector() {
+		$result = $this->Proto->get('#content');
+		$this->assertEqual($result, $this->Proto);
+		$this->assertEqual($this->Proto->selection, '$("content")');
+
+		$result = $this->Proto->get('a .remove');
+		$this->assertEqual($result, $this->Proto);
+		$this->assertEqual($this->Proto->selection, '$$("a .remove")');
+
+		$result = $this->Proto->get('document');
+		$this->assertEqual($result, $this->Proto);
+		$this->assertEqual($this->Proto->selection, "$(document)");
+
+		$result = $this->Proto->get('window');
+		$this->assertEqual($result, $this->Proto);
+		$this->assertEqual($this->Proto->selection, "$(window)");
+
+		$result = $this->Proto->get('ul');
+		$this->assertEqual($result, $this->Proto);
+		$this->assertEqual($this->Proto->selection, '$$("ul")');
+
+		$result = $this->Proto->get('#some_long-id.class');
+		$this->assertEqual($result, $this->Proto);
+		$this->assertEqual($this->Proto->selection, '$$("#some_long-id.class")');
+	}
+
+/**
+ * test event binding
+ *
+ * @return void
+ */
+	function testEvent() {
+		$this->Proto->get('#myLink');
+		$result = $this->Proto->event('click', 'doClick', array('wrap' => false));
+		$expected = '$("myLink").observe("click", doClick);';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Proto->event('click', 'Element.hide(this);', array('stop' => false));
+		$expected = '$("myLink").observe("click", function (event) {Element.hide(this);});';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Proto->event('click', 'Element.hide(this);');
+		$expected = "\$(\"myLink\").observe(\"click\", function (event) {event.stop();\nElement.hide(this);});";
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test dom ready event creation
+ *
+ * @return void
+ */
+	function testDomReady() {
+		$result = $this->Proto->domReady('foo.name = "bar";');
+		$expected = 'document.observe("dom:loaded", function (event) {foo.name = "bar";});';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test Each method
+ *
+ * @return void
+ */
+	function testEach() {
+		$this->Proto->get('#foo li');
+		$result = $this->Proto->each('item.hide();');
+		$expected = '$$("#foo li").each(function (item, index) {item.hide();});';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test Effect generation
+ *
+ * @return void
+ */
+	function testEffect() {
+		$this->Proto->get('#foo');
+		$result = $this->Proto->effect('show');
+		$expected = '$("foo").show();';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Proto->effect('hide');
+		$expected = '$("foo").hide();';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Proto->effect('fadeIn');
+		$expected = '$("foo").appear();';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Proto->effect('fadeIn', array('speed' => 'fast'));
+		$expected = '$("foo").appear({duration:0.50000000000});';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Proto->effect('fadeIn', array('speed' => 'slow'));
+		$expected = '$("foo").appear({duration:2});';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Proto->effect('fadeOut');
+		$expected = '$("foo").fade();';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Proto->effect('fadeOut', array('speed' => 'fast'));
+		$expected = '$("foo").fade({duration:0.50000000000});';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Proto->effect('fadeOut', array('speed' => 'slow'));
+		$expected = '$("foo").fade({duration:2});';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Proto->effect('slideIn');
+		$expected = 'Effect.slideDown($("foo"));';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Proto->effect('slideOut');
+		$expected = 'Effect.slideUp($("foo"));';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Proto->effect('slideOut', array('speed' => 'fast'));
+		$expected = 'Effect.slideUp($("foo"), {duration:0.50000000000});';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Proto->effect('slideOut', array('speed' => 'slow'));
+		$expected = 'Effect.slideUp($("foo"), {duration:2});';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * Test Request Generation
+ *
+ * @return void
+ */
+	function testRequest() {
+		$result = $this->Proto->request(array('controller' => 'posts', 'action' => 'view', 1));
+		$expected = 'var jsRequest = new Ajax.Request("/posts/view/1");';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Proto->request('/posts/view/1', array(
+			'method' => 'post',
+			'complete' => 'doComplete',
+			'before' => 'doBefore',
+			'success' => 'doSuccess',
+			'error' => 'doError',
+			'data' => array('name' => 'jim', 'height' => '185cm'),
+			'wrapCallbacks' => false
+		));
+		$expected = 'var jsRequest = new Ajax.Request("/posts/view/1", {method:"post", onComplete:doComplete, onCreate:doBefore, onFailure:doError, onSuccess:doSuccess, parameters:{"name":"jim","height":"185cm"}});';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Proto->request('/posts/view/1', array('update' => 'content'));
+		$expected = 'var jsRequest = new Ajax.Updater("content", "/posts/view/1");';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Proto->request('/people/edit/1', array(
+			'method' => 'post',
+			'complete' => 'doSuccess',
+			'update' => '#update-zone',
+			'wrapCallbacks' => false
+		));
+		$expected = 'var jsRequest = new Ajax.Updater("update-zone", "/people/edit/1", {method:"post", onComplete:doSuccess});';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Proto->request('/people/edit/1', array(
+			'method' => 'post',
+			'complete' => 'doSuccess',
+			'error' => 'handleError',
+			'type' => 'json',
+			'data' => array('name' => 'jim', 'height' => '185cm'),
+			'wrapCallbacks' => false
+		));
+		$expected = 'var jsRequest = new Ajax.Request("/people/edit/1", {method:"post", onComplete:doSuccess, onFailure:handleError, parameters:{"name":"jim","height":"185cm"}});';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Proto->request('/people/edit/1', array(
+			'method' => 'post',
+			'complete' => 'doSuccess',
+			'error' => 'handleError',
+			'type' => 'json',
+			'data' => '$("element").serialize()',
+			'dataExpression' => true,
+			'wrapCallbacks' => false
+		));
+		$expected = 'var jsRequest = new Ajax.Request("/people/edit/1", {method:"post", onComplete:doSuccess, onFailure:handleError, parameters:$("element").serialize()});';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Proto->request('/people/edit/1', array(
+			'method' => 'post',
+			'before' => 'doBefore();',
+			'success' => 'doSuccess();',
+			'complete' => 'doComplete();',
+			'error' => 'handleError();',
+		));
+		$expected = 'var jsRequest = new Ajax.Request("/people/edit/1", {method:"post", onComplete:function (transport) {doComplete();}, onCreate:function (transport) {doBefore();}, onFailure:function (response, jsonHeader) {handleError();}, onSuccess:function (response, jsonHeader) {doSuccess();}});';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Proto->request('/people/edit/1', array(
+			'async' => false,
+			'method' => 'post',
+			'before' => 'doBefore();',
+			'success' => 'doSuccess();',
+			'complete' => 'doComplete();',
+			'error' => 'handleError();',
+		));
+		$expected = 'var jsRequest = new Ajax.Request("/people/edit/1", {asynchronous:false, method:"post", onComplete:function (transport) {doComplete();}, onCreate:function (transport) {doBefore();}, onFailure:function (response, jsonHeader) {handleError();}, onSuccess:function (response, jsonHeader) {doSuccess();}});';
+		$this->assertEqual($result, $expected);
+
+		$this->Proto->get('#submit');
+		$result = $this->Proto->request('/users/login', array(
+			'before' => 'login.create(event)',
+			'complete' => 'login.complete(event)',
+			'update' => 'auth',
+			'data' => $this->Proto->serializeForm(array('isForm' => false, 'inline' => true)),
+			'dataExpression' => true
+		));
+		$this->assertTrue(strpos($result, '$($("submit").form).serialize()') > 0);
+		$this->assertFalse(strpos($result, 'parameters:function () {$($("submit").form).serialize()}') > 0);
+	}
+
+/**
+ * test sortable list generation
+ *
+ * @return void
+ */
+	function testSortable() {
+		$this->Proto->get('#myList');
+		$result = $this->Proto->sortable(array(
+			'complete' => 'onComplete',
+			'sort' => 'onSort',
+			'wrapCallbacks' => false
+		));
+		$expected = 'var jsSortable = Sortable.create($("myList"), {onChange:onSort, onUpdate:onComplete});';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test drag() method.  Scriptaculous lacks the ability to take an Array of Elements
+ * in new Drag() when selection is a multiple type.  Iterate over the array.
+ *
+ * @return void
+ */
+	function testDrag() {
+		$this->Proto->get('#element');
+		$result = $this->Proto->drag(array(
+			'start' => 'onStart',
+			'drag' => 'onDrag',
+			'stop' => 'onStop',
+			'snapGrid' => array(10, 10),
+			'wrapCallbacks' => false
+		));
+		$expected = 'var jsDrag = new Draggable($("element"), {onDrag:onDrag, onEnd:onStop, onStart:onStart, snap:[10,10]});';
+		$this->assertEqual($result, $expected);
+
+		$this->Proto->get('div.dragger');
+		$result = $this->Proto->drag(array(
+			'start' => 'onStart',
+			'drag' => 'onDrag',
+			'stop' => 'onStop',
+			'snapGrid' => array(10, 10),
+			'wrapCallbacks' => false
+		));
+		$expected = '$$("div.dragger").each(function (item, index) {new Draggable(item, {onDrag:onDrag, onEnd:onStop, onStart:onStart, snap:[10,10]});});';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test drop() method
+ *
+ * @return void
+ */
+	function testDrop() {
+		$this->Proto->get('#element');
+		$result = $this->Proto->drop(array(
+			'hover' => 'onHover',
+			'drop' => 'onDrop',
+			'accept' => '.drag-me',
+			'wrapCallbacks' => false
+		));
+		$expected = 'Droppables.add($("element"), {accept:".drag-me", onDrop:onDrop, onHover:onHover});';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * ensure that slider() method behaves properly
+ *
+ * @return void
+ */
+	function testSlider() {
+		$this->Proto->get('#element');
+		$result = $this->Proto->slider(array(
+			'handle' => '#handle',
+			'direction' => 'horizontal',
+			'change' => 'onChange',
+			'complete' => 'onComplete',
+			'value' => 4,
+			'wrapCallbacks' => false
+		));
+		$expected = 'var jsSlider = new Control.Slider($("handle"), $("element"), {axis:"horizontal", onChange:onComplete, onSlide:onChange, sliderValue:4});';
+		$this->assertEqual($result, $expected);
+
+		$this->Proto->get('#element');
+		$result = $this->Proto->slider(array(
+			'handle' => '#handle',
+			'change' => 'change();',
+			'complete' => 'complete();',
+			'value' => 4,
+			'min' => 10,
+			'max' => 100
+		));
+		$expected = 'var jsSlider = new Control.Slider($("handle"), $("element"), {onChange:function (value) {complete();}, onSlide:function (value) {change();}, range:$R(10,100), sliderValue:4});';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test the serializeForm implementation.
+ *
+ * @return void
+ */
+	function testSerializeForm() {
+		$this->Proto->get('#element');
+		$result = $this->Proto->serializeForm(array('isForm' => true));
+		$expected = '$("element").serialize();';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Proto->serializeForm(array('isForm' => true, 'inline' => true));
+		$expected = '$("element").serialize()';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Proto->serializeForm(array('isForm' => false));
+		$expected = '$($("element").form).serialize();';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Proto->serializeForm(array('isForm' => false, 'inline' => true));
+		$expected = '$($("element").form).serialize()';
+		$this->assertEqual($result, $expected);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/view/helpers/rss.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/view/helpers/rss.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/view/helpers/rss.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,593 @@
+<?php
+/**
+ * RssHelperTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Helper', array('Rss', 'Time'));
+
+/**
+ * RssHelperTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class RssHelperTest extends CakeTestCase {
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		$this->Rss =& new RssHelper();
+		$this->Rss->Time =& new TimeHelper();
+		$this->Rss->beforeRender();
+
+		$manager =& XmlManager::getInstance();
+		$manager->namespaces = array();
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		unset($this->Rss);
+	}
+
+/**
+ * testAddNamespace method
+ *
+ * @access public
+ * @return void
+ */
+	function testAddNamespace() {
+		$this->Rss->addNs('custom', 'http://example.com/dtd.xml');
+		$manager =& XmlManager::getInstance();
+
+		$expected = array('custom' => 'http://example.com/dtd.xml');
+		$this->assertEqual($manager->namespaces, $expected);
+
+		$this->Rss->removeNs('custom');
+
+		$this->Rss->addNs('dummy', 'http://dummy.com/1.0/');
+		$result = $this->Rss->document();
+		$expected = array(
+			'rss' => array(
+				'xmlns:dummy' => 'http://dummy.com/1.0/',
+				'version' => '2.0'
+			)
+		);
+		$this->assertTags($result, $expected);
+
+		$this->Rss->removeNs('dummy');
+	}
+
+/**
+ * testRemoveNamespace method
+ *
+ * @access public
+ * @return void
+ */
+	function testRemoveNamespace() {
+		$this->Rss->addNs('custom', 'http://example.com/dtd.xml');
+		$this->Rss->addNs('custom2', 'http://example.com/dtd2.xml');
+		$manager =& XmlManager::getInstance();
+
+		$expected = array('custom' => 'http://example.com/dtd.xml', 'custom2' => 'http://example.com/dtd2.xml');
+		$this->assertEqual($manager->namespaces, $expected);
+
+		$this->Rss->removeNs('custom');
+		$expected = array('custom2' => 'http://example.com/dtd2.xml');
+		$this->assertEqual($manager->namespaces, $expected);
+	}
+	/**
+ * testDocument method
+ *
+ * @access public
+ * @return void
+ */
+	function testDocument() {
+		$result = $this->Rss->document();
+		$expected = array(
+			'rss' => array(
+				'version' => '2.0'
+			)
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Rss->document(array('contrived' => 'parameter'));
+		$expected = array(
+			'rss' => array(
+				'version' => '2.0'
+			),
+			'<parameter'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Rss->document(null, 'content');
+		$expected = array(
+			'rss' => array(
+				'version' => '2.0'
+			),
+			'content'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Rss->document(array('contrived' => 'parameter'), 'content');
+		$expected = array(
+			'rss' => array(
+				'contrived' => 'parameter',
+				'version' => '2.0'
+			),
+			'content'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testChannel method
+ *
+ * @access public
+ * @return void
+ */
+	function testChannel() {
+		$attrib = array('a' => '1', 'b' => '2');
+		$elements = array('title' => 'title');
+		$content = 'content';
+
+		$result = $this->Rss->channel($attrib, $elements, $content);
+		$expected = array(
+			'channel' => array(
+				'a' => '1',
+				'b' => '2'
+			),
+			'<title',
+			'title',
+			'/title',
+			'<link',
+			RssHelper::url('/', true),
+			'/link',
+			'<description',
+			'content',
+			'/channel'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * test correct creation of channel sub elements.
+ *
+ * @access public
+ * @return void
+ */
+	function testChannelElements() {
+		$attrib = array();
+		$elements = array(
+			'title' => 'Title of RSS Feed',
+			'link' => 'http://example.com',
+			'description' => 'Description of RSS Feed',
+			'image' => array(
+				'title' => 'Title of image',
+				'url' => 'http://example.com/example.png',
+				'link' => 'http://example.com'
+			),
+			'cloud' => array(
+				'domain' => "rpc.sys.com",
+				'port' => "80",
+				'path' =>"/RPC2",
+				'registerProcedure' => "myCloud.rssPleaseNotify",
+				'protocol' => "xml-rpc"
+			)
+		);
+		$content = 'content-here';
+		$result = $this->Rss->channel($attrib, $elements, $content);
+		$expected = array(
+			'<channel',
+				'<title', 'Title of RSS Feed', '/title',
+				'<link', 'http://example.com', '/link',
+				'<description', 'Description of RSS Feed', '/description',
+				'<image',
+					'<title', 'Title of image', '/title',
+					'<url', 'http://example.com/example.png', '/url',
+					'<link', 'http://example.com', '/link',
+				'/image',
+				'cloud' => array(
+					'domain' => "rpc.sys.com",
+					'port' => "80",
+					'path' =>"/RPC2",
+					'registerProcedure' => "myCloud.rssPleaseNotify",
+					'protocol' => "xml-rpc"
+				),
+			'content-here',
+			'/channel',
+		);
+		$this->assertTags($result, $expected);
+	}
+	function testChannelElementAttributes() {
+		$attrib = array();
+		$elements = array(
+			'title' => 'Title of RSS Feed',
+			'link' => 'http://example.com',
+			'description' => 'Description of RSS Feed',
+			'image' => array(
+				'title' => 'Title of image',
+				'url' => 'http://example.com/example.png',
+				'link' => 'http://example.com'
+			),
+			'atom:link' => array(
+				'attrib' => array(
+					'href' => 'http://www.example.com/rss.xml',
+					'rel' => 'self',
+					'type' => 'application/rss+xml')
+			)
+		);
+		$content = 'content-here';
+		$result = $this->Rss->channel($attrib, $elements, $content);
+		$expected = array(
+			'<channel',
+				'<title', 'Title of RSS Feed', '/title',
+				'<link', 'http://example.com', '/link',
+				'<description', 'Description of RSS Feed', '/description',
+				'<image',
+					'<title', 'Title of image', '/title',
+					'<url', 'http://example.com/example.png', '/url',
+					'<link', 'http://example.com', '/link',
+				'/image',
+				'atom:link' => array(
+					'href' => "http://www.example.com/rss.xml",
+					'rel' => "self",
+					'type' =>"application/rss+xml"
+				),
+			'content-here',
+			'/channel',
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testItems method
+ *
+ * @access public
+ * @return void
+ */
+	function testItems() {
+		$items = array(
+			array('title' => 'title1', 'guid' => 'http://www.example.com/guid1', 'link' => 'http://www.example.com/link1', 'description' => 'description1'),
+			array('title' => 'title2', 'guid' => 'http://www.example.com/guid2', 'link' => 'http://www.example.com/link2', 'description' => 'description2'),
+			array('title' => 'title3', 'guid' => 'http://www.example.com/guid3', 'link' => 'http://www.example.com/link3', 'description' => 'description3')
+		);
+
+		$result = $this->Rss->items($items);
+		$expected = array(
+			'<item',
+				'<title', 'title1', '/title',
+				'<guid', 'http://www.example.com/guid1', '/guid',
+				'<link', 'http://www.example.com/link1', '/link',
+				'<description', 'description1', '/description',
+			'/item',
+			'<item',
+				'<title', 'title2', '/title',
+				'<guid', 'http://www.example.com/guid2', '/guid',
+				'<link', 'http://www.example.com/link2', '/link',
+				'<description', 'description2', '/description',
+			'/item',
+			'<item',
+				'<title', 'title3', '/title',
+				'<guid', 'http://www.example.com/guid3', '/guid',
+				'<link', 'http://www.example.com/link3', '/link',
+				'<description', 'description3', '/description',
+			'/item'
+		);
+		$this->assertTags($result, $expected);
+
+		$result = $this->Rss->items(array());
+		$expected = '';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testItem method
+ *
+ * @access public
+ * @return void
+ */
+	function testItem() {
+		$item = array(
+			'title' => 'My title',
+			'description' => 'My description',
+			'link' => 'http://www.google.com/'
+		);
+		$result = $this->Rss->item(null, $item);
+		$expected = array(
+			'<item',
+			'<title',
+			'My title',
+			'/title',
+			'<description',
+			'My description',
+			'/description',
+			'<link',
+			'http://www.google.com/',
+			'/link',
+			'<guid',
+			'http://www.google.com/',
+			'/guid',
+			'/item'
+		);
+		$this->assertTags($result, $expected);
+
+		$item = array(
+			'title' => array(
+				'value' => 'My Title',
+				'cdata' => true,
+			),
+			'link' => 'http://www.example.com/1',
+			'description' => array(
+				'value' => 'descriptive words',
+				'cdata' => true,
+			),
+			'pubDate' => '2008-05-31 12:00:00',
+			'guid' => 'http://www.example.com/1'
+		);
+		$result = $this->Rss->item(null, $item);
+
+		$expected = array(
+			'<item',
+			'<title',
+			'<![CDATA[My Title]]',
+			'/title',
+			'<link',
+			'http://www.example.com/1',
+			'/link',
+			'<description',
+			'<![CDATA[descriptive words]]',
+			'/description',
+			'<pubDate',
+			date('r', strtotime('2008-05-31 12:00:00')),
+			'/pubDate',
+			'<guid',
+			'http://www.example.com/1',
+			'/guid',
+			'/item'
+		);
+		$this->assertTags($result, $expected);
+
+		$item = array(
+			'title' => array(
+				'value' => 'My Title & more',
+				'cdata' => true
+			)
+		);
+		$result = $this->Rss->item(null, $item);
+		$expected = array(
+			'<item',
+			'<title',
+			'<![CDATA[My Title &amp; more]]',
+			'/title',
+			'/item'
+		);
+		$this->assertTags($result, $expected);
+
+		$item = array(
+			'title' => array(
+				'value' => 'My Title & more',
+				'convertEntities' => false
+			)
+		);
+		$result = $this->Rss->item(null, $item);
+		$expected = array(
+			'<item',
+			'<title',
+			'My Title & more',
+			'/title',
+			'/item'
+		);
+		$this->assertTags($result, $expected);
+
+		$item = array(
+			'title' => array(
+				'value' => 'My Title & more',
+				'cdata' => true,
+				'convertEntities' => false,
+			)
+		);
+		$result = $this->Rss->item(null, $item);
+		$expected = array(
+			'<item',
+			'<title',
+			'<![CDATA[My Title & more]]',
+			'/title',
+			'/item'
+		);
+		$this->assertTags($result, $expected);
+
+		$item = array(
+			'category' => array(
+				'value' => 'CakePHP',
+				'cdata' => true,
+				'domain' => 'http://www.cakephp.org'
+			)
+		);
+		$result = $this->Rss->item(null, $item);
+		$expected = array(
+			'<item',
+			'category' => array('domain' => 'http://www.cakephp.org'),
+			'<![CDATA[CakePHP]]',
+			'/category',
+			'/item'
+		);
+		$this->assertTags($result, $expected);
+
+		$item = array(
+			'category' => array(
+				array(
+					'value' => 'CakePHP',
+					'cdata' => true,
+					'domain' => 'http://www.cakephp.org'
+				),
+				array(
+					'value' => 'Bakery',
+					'cdata' => true
+				)
+			)
+		);
+		$result = $this->Rss->item(null, $item);
+		$expected = array(
+			'<item',
+			'category' => array('domain' => 'http://www.cakephp.org'),
+			'<![CDATA[CakePHP]]',
+			'/category',
+			'<category',
+			'<![CDATA[Bakery]]',
+			'/category',
+			'/item'
+		);
+		$this->assertTags($result, $expected);
+
+		$item = array(
+			'title' => array(
+				'value' => 'My Title',
+				'cdata' => true,
+			),
+			'link' => 'http://www.example.com/1',
+			'description' => array(
+				'value' => 'descriptive words',
+				'cdata' => true,
+			),
+			'enclosure' => array(
+				'url' => '/test.flv'
+			),
+			'pubDate' => '2008-05-31 12:00:00',
+			'guid' => 'http://www.example.com/1',
+			'category' => array(
+				array(
+					'value' => 'CakePHP',
+					'cdata' => true,
+					'domain' => 'http://www.cakephp.org'
+				),
+				array(
+					'value' => 'Bakery',
+					'cdata' => true
+				)
+			)
+		);
+		$result = $this->Rss->item(null, $item);
+		$expected = array(
+			'<item',
+			'<title',
+			'<![CDATA[My Title]]',
+			'/title',
+			'<link',
+			'http://www.example.com/1',
+			'/link',
+			'<description',
+			'<![CDATA[descriptive words]]',
+			'/description',
+			'enclosure' => array('url' => RssHelper::url('/test.flv', true)),
+			'<pubDate',
+			date('r', strtotime('2008-05-31 12:00:00')),
+			'/pubDate',
+			'<guid',
+			'http://www.example.com/1',
+			'/guid',
+			'category' => array('domain' => 'http://www.cakephp.org'),
+			'<![CDATA[CakePHP]]',
+			'/category',
+			'<category',
+			'<![CDATA[Bakery]]',
+			'/category',
+			'/item'
+		);
+		$this->assertTags($result, $expected);
+
+		$item = array(
+			'title' => 'Foo bar',
+			'link' => array(
+				'url' => 'http://example.com/foo?a=1&b=2',
+				'convertEntities' => false
+			),
+			'description' =>  array(
+				'value' => 'descriptive words',
+				'cdata' => true,
+			),
+			'pubDate' => '2008-05-31 12:00:00'
+		);
+		$result = $this->Rss->item(null, $item);
+		$expected = array(
+			'<item',
+			'<title',
+			'Foo bar',
+			'/title',
+			'<link',
+			'http://example.com/foo?a=1&amp;b=2',
+			'/link',
+			'<description',
+			'<![CDATA[descriptive words]]',
+			'/description',
+			'<pubDate',
+			date('r', strtotime('2008-05-31 12:00:00')),
+			'/pubDate',
+			'<guid',
+			'http://example.com/foo?a=1&amp;b=2',
+			'/guid',
+			'/item'
+		);
+		$this->assertTags($result, $expected);
+	}
+
+/**
+ * testTime method
+ *
+ * @access public
+ * @return void
+ */
+	function testTime() {
+	}
+
+/**
+ * testElementAttrNotInParent method
+ *
+ * @access public
+ * @return void
+ */
+	function testElementAttrNotInParent() {
+		$attributes = array(
+			'title' => 'Some Title',
+			'link' => 'http://link.com',
+			'description' => 'description'
+		);
+		$elements = array('enclosure' => array('url' => 'http://test.com'));
+
+		$result = $this->Rss->item($attributes, $elements);
+		$expected = array(
+			'item' => array(
+				'title' => 'Some Title',
+				'link' => 'http://link.com',
+				'description' => 'description'
+			),
+			'enclosure' => array(
+				'url' => 'http://test.com'
+			),
+			'/item'
+		);
+		$this->assertTags($result, $expected);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/view/helpers/session.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/view/helpers/session.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/view/helpers/session.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,236 @@
+<?php
+/**
+ * SessionHelperTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) {
+	define('CAKEPHP_UNIT_TEST_EXECUTION', 1);
+}
+App::import('Core', array('Helper', 'AppHelper', 'Controller', 'View'));
+App::import('Helper', array('Session'));
+
+/**
+ * SessionHelperTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class SessionHelperTest extends CakeTestCase {
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function startTest() {
+		$this->Session = new SessionHelper();
+
+		$_SESSION = array(
+			'test' => 'info',
+			'Message' => array(
+				'flash' => array(
+					'element' => 'default',
+					'params' => array(),
+					'message' => 'This is a calling'
+				),
+				'notification' => array(
+					'element' => 'session_helper',
+					'params' => array('title' => 'Notice!', 'name' => 'Alert!'),
+					'message' => 'This is a test of the emergency broadcasting system',
+				),
+				'classy' => array(
+					'element' => 'default',
+					'params' => array('class' => 'positive'),
+					'message' => 'Recorded'
+				),
+				'bare' => array(
+					'element' => null,
+					'message' => 'Bare message',
+					'params' => array(),
+				),
+			),
+			'Deeply' => array('nested' => array('key' => 'value')),
+		);
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		$_SESSION = array();
+		unset($this->Session);
+	}
+
+/**
+ * endTest
+ *
+ * @access public
+ * @return void
+ */
+	function endTest() {
+		App::build();
+	}
+
+/**
+ * test construction and initial property settings
+ *
+ * @return void
+ */
+	function testConstruct() {
+		$this->assertFalse(empty($this->Session->sessionTime));
+		$this->assertFalse(empty($this->Session->security));
+	}
+/**
+ * testRead method
+ *
+ * @access public
+ * @return void
+ */
+	function testRead() {
+		$result = $this->Session->read('Deeply.nested.key');
+		$this->assertEqual($result, 'value');
+
+		$result = $this->Session->read('test');
+		$this->assertEqual($result, 'info');
+	}
+
+/**
+ * testCheck method
+ *
+ * @access public
+ * @return void
+ */
+	function testCheck() {
+		$this->assertTrue($this->Session->check('test'));
+
+		$this->assertTrue($this->Session->check('Message.flash.element'));
+
+		$this->assertFalse($this->Session->check('Does.not.exist'));
+
+		$this->assertFalse($this->Session->check('Nope'));
+	}
+
+/**
+ * testWrite method
+ *
+ * @access public
+ * @return void
+ */
+	function testWrite() {
+		$this->expectError();
+		$this->Session->write('NoWay', 'AccessDenied');
+	}
+
+/**
+ * testFlash method
+ *
+ * @access public
+ * @return void
+ */
+	function testFlash() {
+		$result = $this->Session->flash('flash', true);
+		$expected = '<div id="flashMessage" class="message">This is a calling</div>';
+		$this->assertEqual($result, $expected);
+		$this->assertFalse($this->Session->check('Message.flash'));
+
+		$expected = '<div id="classyMessage" class="positive">Recorded</div>';
+		$result = $this->Session->flash('classy', true);
+		$this->assertEqual($result, $expected);
+
+		App::build(array(
+			'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS)
+		));
+		$controller = new Controller();
+		$this->Session->view = new View($controller);
+
+		$result = $this->Session->flash('notification', true);
+		$result = str_replace("\r\n", "\n", $result);
+		$expected = "<div id=\"notificationLayout\">\n\t<h1>Alert!</h1>\n\t<h3>Notice!</h3>\n\t<p>This is a test of the emergency broadcasting system</p>\n</div>";
+		$this->assertEqual($result, $expected);
+		$this->assertFalse($this->Session->check('Message.notification'));
+
+		$result = $this->Session->flash('bare');
+		$expected = 'Bare message';
+		$this->assertEqual($result, $expected);
+		$this->assertFalse($this->Session->check('Message.bare'));
+	}
+
+/**
+ * testID method
+ *
+ * @access public
+ * @return void
+ */
+	function testID() {
+		$id = session_id();
+		$result = $this->Session->id();
+		$this->assertEqual($id, $result);
+	}
+
+/**
+ * testError method
+ *
+ * @access public
+ * @return void
+ */
+	function testError() {
+		$result = $this->Session->error();
+		$this->assertFalse($result);
+
+		$this->Session->read('CauseError');
+		$result = $this->Session->error();
+		$expected = "CauseError doesn't exist";
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testDisabling method
+ *
+ * @access public
+ * @return void
+ */
+	function testDisabling() {
+		Configure::write('Session.start', false);
+		$this->Session = new SessionHelper();
+		$this->assertFalse($this->Session->check('test'));
+		$this->assertFalse($this->Session->read('test'));
+
+		$this->Session->read('CauseError');
+		$this->assertFalse($this->Session->error());
+
+		ob_start();
+		$this->assertFalse($this->Session->flash('bare'));
+		$result = ob_get_contents();
+		ob_clean();
+		$this->assertFalse($result);
+	}
+
+/**
+ * testValid method
+ *
+ * @access public
+ * @return void
+ */
+	function testValid() {
+		//wierd it always ends up false in the test suite
+		//$this->assertFalse($this->Session->valid());
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/view/helpers/text.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/view/helpers/text.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/view/helpers/text.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,446 @@
+<?php
+/**
+ * TextHelperTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Helper', 'Text');
+
+/**
+ * TextHelperTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class TextHelperTest extends CakeTestCase {
+
+/**
+ * helper property
+ *
+ * @var mixed null
+ * @access public
+ */
+	var $helper = null;
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		$this->Text = new TextHelper();
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		unset($this->Text);
+	}
+
+/**
+ * testTruncate method
+ *
+ * @access public
+ * @return void
+ */
+	function testTruncate() {
+		$text1 = 'The quick brown fox jumps over the lazy dog';
+		$text2 = 'Heiz&ouml;lr&uuml;cksto&szlig;abd&auml;mpfung';
+		$text3 = '<b>&copy; 2005-2007, Cake Software Foundation, Inc.</b><br />written by Alexander Wegener';
+		$text4 = '<img src="mypic.jpg"> This image tag is not XHTML conform!<br><hr/><b>But the following image tag should be conform <img src="mypic.jpg" alt="Me, myself and I" /></b><br />Great, or?';
+		$text5 = '0<b>1<i>2<span class="myclass">3</span>4<u>5</u>6</i>7</b>8<b>9</b>0';
+        $text6 = '<p><strong>Extra dates have been announced for this year\'s tour.</strong></p><p>Tickets for the new shows in</p>';
+        $text7 = 'El moño está en el lugar correcto. Eso fue lo que dijo la niña, ¿habrá dicho la verdad?';
+        $text8 = 'Vive la R'.chr(195).chr(169).'publique de France';
+		$text9 = 'НОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыь';
+
+		$this->assertIdentical($this->Text->truncate($text1, 15), 'The quick br...');
+		$this->assertIdentical($this->Text->truncate($text1, 15, array('exact' => false)), 'The quick...');
+		$this->assertIdentical($this->Text->truncate($text1, 100), 'The quick brown fox jumps over the lazy dog');
+		$this->assertIdentical($this->Text->truncate($text2, 10), 'Heiz&ou...');
+		$this->assertIdentical($this->Text->truncate($text2, 10, array('exact' => false)), '...');
+		$this->assertIdentical($this->Text->truncate($text3, 20), '<b>&copy; 2005-20...');
+		$this->assertIdentical($this->Text->truncate($text4, 15), '<img src="my...');
+		$this->assertIdentical($this->Text->truncate($text5, 6, array('ending' => '')), '0<b>1<');
+		$this->assertIdentical($this->Text->truncate($text1, 15, array('html' => true)), 'The quick br...');
+		$this->assertIdentical($this->Text->truncate($text1, 15, array('exact' => false, 'html' => true)), 'The quick...');
+		$this->assertIdentical($this->Text->truncate($text2, 10, array('html' => true)), 'Heiz&ouml;lr...');
+		$this->assertIdentical($this->Text->truncate($text2, 10, array('exact' => false, 'html' => true)), '...');
+		$this->assertIdentical($this->Text->truncate($text3, 20, array('html' => true)), '<b>&copy; 2005-2007, Cake...</b>');
+		$this->assertIdentical($this->Text->truncate($text4, 15, array('html' => true)), '<img src="mypic.jpg"> This image ...');
+		$this->assertIdentical($this->Text->truncate($text4, 45, array('html' => true)), '<img src="mypic.jpg"> This image tag is not XHTML conform!<br><hr/><b>But t...</b>');
+		$this->assertIdentical($this->Text->truncate($text4, 90, array('html' => true)), '<img src="mypic.jpg"> This image tag is not XHTML conform!<br><hr/><b>But the following image tag should be conform <img src="mypic.jpg" alt="Me, myself and I" /></b><br />Grea...');
+		$this->assertIdentical($this->Text->truncate($text5, 6, array('ending' => '', 'html' => true)), '0<b>1<i>2<span class="myclass">3</span>4<u>5</u></i></b>');
+		$this->assertIdentical($this->Text->truncate($text5, 20, array('ending' => '', 'html' => true)), $text5);
+		$this->assertIdentical($this->Text->truncate($text6, 57, array('exact' => false, 'html' => true)), "<p><strong>Extra dates have been announced for this year's...</strong></p>");
+		$this->assertIdentical($this->Text->truncate($text7, 255), $text7);
+		$this->assertIdentical($this->Text->truncate($text7, 15), 'El moño está...');
+		$this->assertIdentical($this->Text->truncate($text8, 15), 'Vive la R'.chr(195).chr(169).'pu...');
+		$this->assertIdentical($this->Text->truncate($text9, 10), 'НОПРСТУ...');
+	}
+/**
+ * testHighlight method
+ *
+ * @access public
+ * @return void
+ */
+	function testHighlight() {
+		$text = 'This is a test text';
+		$phrases = array('This', 'text');
+		$result = $this->Text->highlight($text, $phrases, array('format' => '<b>\1</b>'));
+		$expected = '<b>This</b> is a test <b>text</b>';
+		$this->assertEqual($expected, $result);
+
+		$text = 'This is a test text';
+		$phrases = null;
+		$result = $this->Text->highlight($text, $phrases, array('format' => '<b>\1</b>'));
+		$this->assertEqual($result, $text);
+
+		$text = 'This is a (test) text';
+		$phrases = '(test';
+		$result = $this->Text->highlight($text, $phrases, array('format' => '<b>\1</b>'));
+		$this->assertEqual('This is a <b>(test</b>) text', $result);
+
+		$text = 'Ich saß in einem Café am Übergang';
+		$expected = 'Ich <b>saß</b> in einem <b>Café</b> am <b>Übergang</b>';
+		$phrases = array('saß', 'café', 'übergang');
+		$result = $this->Text->highlight($text, $phrases, array('format' => '<b>\1</b>'));
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testHighlightHtml method
+ *
+ * @access public
+ * @return void
+ */
+	function testHighlightHtml() {
+		$text1 = '<p>strongbow isn&rsquo;t real cider</p>';
+		$text2 = '<p>strongbow <strong>isn&rsquo;t</strong> real cider</p>';
+		$text3 = '<img src="what-a-strong-mouse.png" alt="What a strong mouse!" />';
+		$text4 = 'What a strong mouse: <img src="what-a-strong-mouse.png" alt="What a strong mouse!" />';
+		$options = array('format' => '<b>\1</b>', 'html' => true);
+
+		$expected = '<p><b>strong</b>bow isn&rsquo;t real cider</p>';
+		$this->assertEqual($this->Text->highlight($text1, 'strong', $options), $expected);
+
+		$expected = '<p><b>strong</b>bow <strong>isn&rsquo;t</strong> real cider</p>';
+		$this->assertEqual($this->Text->highlight($text2, 'strong', $options), $expected);
+
+		$this->assertEqual($this->Text->highlight($text3, 'strong', $options), $text3);
+
+		$this->assertEqual($this->Text->highlight($text3, array('strong', 'what'), $options), $text3);
+
+		$expected = '<b>What</b> a <b>strong</b> mouse: <img src="what-a-strong-mouse.png" alt="What a strong mouse!" />';
+		$this->assertEqual($this->Text->highlight($text4, array('strong', 'what'), $options), $expected);
+	}
+
+/**
+ * testHighlightMulti method
+ *
+ * @access public
+ * @return void
+ */
+	function testHighlightMulti() {
+		$text = 'This is a test text';
+		$phrases = array('This', 'text');
+		$result = $this->Text->highlight($text, $phrases, array('format' => array('<b>\1</b>', '<em>\1</em>')));
+		$expected = '<b>This</b> is a test <em>text</em>';
+		$this->assertEqual($expected, $result);
+
+	}
+
+/**
+ * testStripLinks method
+ *
+ * @access public
+ * @return void
+ */
+	function testStripLinks() {
+		$text = 'This is a test text';
+		$expected = 'This is a test text';
+		$result = $this->Text->stripLinks($text);
+		$this->assertEqual($expected, $result);
+
+		$text = 'This is a <a href="#">test</a> text';
+		$expected = 'This is a test text';
+		$result = $this->Text->stripLinks($text);
+		$this->assertEqual($expected, $result);
+
+		$text = 'This <strong>is</strong> a <a href="#">test</a> <a href="#">text</a>';
+		$expected = 'This <strong>is</strong> a test text';
+		$result = $this->Text->stripLinks($text);
+		$this->assertEqual($expected, $result);
+
+		$text = 'This <strong>is</strong> a <a href="#">test</a> and <abbr>some</abbr> other <a href="#">text</a>';
+		$expected = 'This <strong>is</strong> a test and <abbr>some</abbr> other text';
+		$result = $this->Text->stripLinks($text);
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * testAutoLink method
+ *
+ * @access public
+ * @return void
+ */
+	function testAutoLink() {
+		$text = 'This is a test text';
+		$expected = 'This is a test text';
+		$result = $this->Text->autoLink($text);
+		$this->assertEqual($expected, $result);
+
+		$text = 'Text with a partial www.cakephp.org URL and test****@cakep***** email address';
+		$result = $this->Text->autoLink($text);
+		$expected = 'Text with a partial <a href="http://www.cakephp.org">www.cakephp.org</a> URL and <a href="mailto:test @ cakephp\.org">test @ cakephp\.org</a> email address';
+		$this->assertPattern('#^' . $expected . '$#', $result);
+
+		$text = 'This is a test text with URL http://www.cakephp.org';
+		$expected = 'This is a test text with URL <a href="http://www.cakephp.org">http://www.cakephp.org</a>';
+		$result = $this->Text->autoLink($text);
+		$this->assertEqual($result, $expected);
+
+		$text = 'This is a test text with URL http://www.cakephp.org and some more text';
+		$expected = 'This is a test text with URL <a href="http://www.cakephp.org">http://www.cakephp.org</a> and some more text';
+		$result = $this->Text->autoLink($text);
+		$this->assertEqual($result, $expected);
+
+		$text = "This is a test text with URL http://www.cakephp.org\tand some more text";
+		$expected = "This is a test text with URL <a href=\"http://www.cakephp.org\">http://www.cakephp.org</a>\tand some more text";
+		$result = $this->Text->autoLink($text);
+		$this->assertEqual($result, $expected);
+
+		$text = 'This is a test text with URL http://www.cakephp.org(and some more text)';
+		$expected = 'This is a test text with URL <a href="http://www.cakephp.org">http://www.cakephp.org</a>(and some more text)';
+		$result = $this->Text->autoLink($text);
+		$this->assertEqual($result, $expected);
+
+		$text = 'This is a test text with URL http://www.cakephp.org';
+		$expected = 'This is a test text with URL <a href="http://www.cakephp.org" class="link">http://www.cakephp.org</a>';
+		$result = $this->Text->autoLink($text, array('class'=>'link'));
+		$this->assertEqual($result, $expected);
+
+		$text = 'This is a test text with URL http://www.cakephp.org';
+		$expected = 'This is a test text with URL <a href="http://www.cakephp.org" class="link" id="MyLink">http://www.cakephp.org</a>';
+		$result = $this->Text->autoLink($text, array('class'=>'link', 'id'=>'MyLink'));
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testAutoLinkUrls method
+ *
+ * @access public
+ * @return void
+ */
+	function testAutoLinkUrls() {
+		$text = 'This is a test text';
+		$expected = 'This is a test text';
+		$result = $this->Text->autoLinkUrls($text);
+		$this->assertEqual($expected, $result);
+
+		$text = 'This is a test that includes (www.cakephp.org)';
+		$expected = 'This is a test that includes (<a href="http://www.cakephp.org">www.cakephp.org</a>)';
+		$result = $this->Text->autoLinkUrls($text);
+		$this->assertEqual($expected, $result);
+
+		$text = 'Text with a partial www.cakephp.org URL';
+		$expected = 'Text with a partial <a href="http://www.cakephp.org"\s*>www.cakephp.org</a> URL';
+		$result = $this->Text->autoLinkUrls($text);
+		$this->assertPattern('#^' . $expected . '$#', $result);
+
+		$text = 'Text with a partial www.cakephp.org URL';
+		$expected = 'Text with a partial <a href="http://www.cakephp.org" \s*class="link">www.cakephp.org</a> URL';
+		$result = $this->Text->autoLinkUrls($text, array('class' => 'link'));
+		$this->assertPattern('#^' . $expected . '$#', $result);
+
+		$text = 'Text with a partial WWW.cakephp.org URL';
+		$expected = 'Text with a partial <a href="http://WWW.cakephp.org"\s*>WWW.cakephp.org</a> URL';
+		$result = $this->Text->autoLinkUrls($text);
+		$this->assertPattern('#^' . $expected . '$#', $result);
+
+		$text = 'Text with a partial WWW.cakephp.org &copy; URL';
+		$expected = 'Text with a partial <a href="http://WWW.cakephp.org"\s*>WWW.cakephp.org</a> &copy; URL';
+		$result = $this->Text->autoLinkUrls($text, array('escape' => false));
+		$this->assertPattern('#^' . $expected . '$#', $result);
+
+		$text = 'Text with a url www.cot.ag/cuIb2Q and more';
+		$expected = 'Text with a url <a href="http://www.cot.ag/cuIb2Q">www.cot.ag/cuIb2Q</a> and more';
+		$result = $this->Text->autoLinkUrls($text);
+		$this->assertEqual($expected, $result);
+		
+		$text = 'Text with a url http://www.does--not--work.com and more';
+		$expected = 'Text with a url <a href="http://www.does--not--work.com">http://www.does--not--work.com</a> and more';
+		$result = $this->Text->autoLinkUrls($text);
+		$this->assertEqual($expected, $result);
+		
+		$text = 'Text with a url http://www.not--work.com and more';
+		$expected = 'Text with a url <a href="http://www.not--work.com">http://www.not--work.com</a> and more';
+		$result = $this->Text->autoLinkUrls($text);
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * testAutoLinkEmails method
+ *
+ * @access public
+ * @return void
+ */
+	function testAutoLinkEmails() {
+		$text = 'This is a test text';
+		$expected = 'This is a test text';
+		$result = $this->Text->autoLinkUrls($text);
+		$this->assertEqual($expected, $result);
+
+		$text = 'Text with email****@examp***** address';
+		$expected = 'Text with <a href="mailto:email****@examp*****"\s*>email****@examp*****</a> address';
+		$result = $this->Text->autoLinkEmails($text);
+		$this->assertPattern('#^' . $expected . '$#', $result);
+		
+		$text = "Text with o'hare._-bob****@examp***** address";
+		$expected = 'Text with <a href="mailto:o&#039;hare._-bob****@examp*****">o&#039;hare._-bob****@examp*****</a> address';
+		$result = $this->Text->autoLinkEmails($text);
+		$this->assertEqual($expected, $result);
+
+		$text = 'Text with email****@examp***** address';
+		$expected = 'Text with <a href="mailto:email****@examp*****" \s*class="link">email****@examp*****</a> address';
+		$result = $this->Text->autoLinkEmails($text, array('class' => 'link'));
+		$this->assertPattern('#^' . $expected . '$#', $result);
+	}
+
+/**
+ * test invalid email addresses.
+ *
+ * @return void
+ */
+	function testAutoLinkEmailInvalid() {
+		$result = $this->Text->autoLinkEmails('this is a myaddress @ gmx-de test');
+		$expected = 'this is a myaddress @ gmx-de test';
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * testHighlightCaseInsensitivity method
+ *
+ * @access public
+ * @return void
+ */
+	function testHighlightCaseInsensitivity() {
+		$text = 'This is a Test text';
+		$expected = 'This is a <b>Test</b> text';
+
+		$result = $this->Text->highlight($text, 'test', array('format' => '<b>\1</b>'));
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Text->highlight($text, array('test'), array('format' => '<b>\1</b>'));
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * testExcerpt method
+ *
+ * @access public
+ * @return void
+ */
+	function testExcerpt() {
+		$text = 'This is a phrase with test text to play with';
+
+		$expected = '...ase with test text to ...';
+		$result = $this->Text->excerpt($text, 'test', 9, '...');
+		$this->assertEqual($expected, $result);
+
+		$expected = 'This is a...';
+		$result = $this->Text->excerpt($text, 'not_found', 9, '...');
+		$this->assertEqual($expected, $result);
+
+		$expected = 'This is a phras...';
+		$result = $this->Text->excerpt($text, null, 9, '...');
+		$this->assertEqual($expected, $result);
+
+		$expected = $text;
+		$result = $this->Text->excerpt($text, null, 200, '...');
+		$this->assertEqual($expected, $result);
+
+		$expected = '...a phrase w...';
+		$result = $this->Text->excerpt($text, 'phrase', 2, '...');
+		$this->assertEqual($expected, $result);
+
+		$phrase = 'This is a phrase with test text';
+		$expected = $text;
+		$result = $this->Text->excerpt($text, $phrase, 13, '...');
+		$this->assertEqual($expected, $result);
+		
+		$text = 'aaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaa';
+		$phrase = 'bbbbbbbb';
+		$result = $this->Text->excerpt($text, $phrase, 10);
+		$expected = '...aaaaaaaaaabbbbbbbbaaaaaaaaaa...';
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * testExcerptCaseInsensitivity method
+ *
+ * @access public
+ * @return void
+ */
+	function testExcerptCaseInsensitivity() {
+		$text = 'This is a phrase with test text to play with';
+
+		$expected = '...ase with test text to ...';
+		$result = $this->Text->excerpt($text, 'TEST', 9, '...');
+		$this->assertEqual($expected, $result);
+
+		$expected = 'This is a...';
+		$result = $this->Text->excerpt($text, 'NOT_FOUND', 9, '...');
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * testListGeneration method
+ *
+ * @access public
+ * @return void
+ */
+	function testListGeneration() {
+		$result = $this->Text->toList(array());
+		$this->assertEqual($result, '');
+
+		$result = $this->Text->toList(array('One'));
+		$this->assertEqual($result, 'One');
+
+		$result = $this->Text->toList(array('Larry', 'Curly', 'Moe'));
+		$this->assertEqual($result, 'Larry, Curly and Moe');
+
+		$result = $this->Text->toList(array('Dusty', 'Lucky', 'Ned'), 'y');
+		$this->assertEqual($result, 'Dusty, Lucky y Ned');
+
+		$result = $this->Text->toList(array( 1 => 'Dusty', 2 => 'Lucky', 3 => 'Ned'), 'y');
+		$this->assertEqual($result, 'Dusty, Lucky y Ned');
+
+		$result = $this->Text->toList(array( 1 => 'Dusty', 2 => 'Lucky', 3 => 'Ned'), 'and', ' + ');
+		$this->assertEqual($result, 'Dusty + Lucky and Ned');
+
+		$result = $this->Text->toList(array( 'name1' => 'Dusty', 'name2' => 'Lucky'));
+		$this->assertEqual($result, 'Dusty and Lucky');
+		
+		$result = $this->Text->toList(array( 'test_0' => 'banana', 'test_1' => 'apple', 'test_2' => 'lemon'));
+		$this->assertEqual($result, 'banana, apple and lemon');
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/view/helpers/time.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/view/helpers/time.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/view/helpers/time.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,825 @@
+<?php
+/**
+ * TimeHelperTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Helper', 'Time');
+
+/**
+ * TimeHelperTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class TimeHelperTest extends CakeTestCase {
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		$this->Time = new TimeHelper();
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		unset($this->Time);
+	}
+
+/**
+ * testToQuarter method
+ *
+ * @access public
+ * @return void
+ */
+	function testToQuarter() {
+		$result = $this->Time->toQuarter('2007-12-25');
+		$this->assertEqual($result, 4);
+
+		$result = $this->Time->toQuarter('2007-9-25');
+		$this->assertEqual($result, 3);
+
+		$result = $this->Time->toQuarter('2007-3-25');
+		$this->assertEqual($result, 1);
+
+		$result = $this->Time->toQuarter('2007-3-25', true);
+		$this->assertEqual($result, array('2007-01-01', '2007-03-31'));
+
+		$result = $this->Time->toQuarter('2007-5-25', true);
+		$this->assertEqual($result, array('2007-04-01', '2007-06-30'));
+
+		$result = $this->Time->toQuarter('2007-8-25', true);
+		$this->assertEqual($result, array('2007-07-01', '2007-09-30'));
+
+		$result = $this->Time->toQuarter('2007-12-25', true);
+		$this->assertEqual($result, array('2007-10-01', '2007-12-31'));
+	}
+
+/**
+ * testTimeAgoInWords method
+ *
+ * @access public
+ * @return void
+ */
+	function testTimeAgoInWords() {
+		$result = $this->Time->timeAgoInWords(strtotime('+4 months +2 weeks +3 days'), array('end' => '8 years'), true);
+		$this->assertEqual($result, '4 months, 2 weeks, 3 days');
+
+		$result = $this->Time->timeAgoInWords(strtotime('+4 months +2 weeks +2 days'), array('end' => '8 years'), true);
+		$this->assertEqual($result, '4 months, 2 weeks, 2 days');
+
+		$result = $this->Time->timeAgoInWords(strtotime('+4 months +2 weeks +1 day'), array('end' => '8 years'), true);
+		$this->assertEqual($result, '4 months, 2 weeks, 1 day');
+
+		$result = $this->Time->timeAgoInWords(strtotime('+3 months +2 weeks +1 day'), array('end' => '8 years'), true);
+		$this->assertEqual($result, '3 months, 2 weeks, 1 day');
+
+		$result = $this->Time->timeAgoInWords(strtotime('+3 months +2 weeks'), array('end' => '8 years'), true);
+		$this->assertEqual($result, '3 months, 2 weeks');
+
+		$result = $this->Time->timeAgoInWords(strtotime('+3 months +1 week +6 days'), array('end' => '8 years'), true);
+		$this->assertEqual($result, '3 months, 1 week, 6 days');
+
+		$result = $this->Time->timeAgoInWords(strtotime('+2 months +2 weeks +1 day'), array('end' => '8 years'), true);
+		$this->assertEqual($result, '2 months, 2 weeks, 1 day');
+
+		$result = $this->Time->timeAgoInWords(strtotime('+2 months +2 weeks'), array('end' => '8 years'), true);
+		$this->assertEqual($result, '2 months, 2 weeks');
+
+		$result = $this->Time->timeAgoInWords(strtotime('+2 months +1 week +6 days'), array('end' => '8 years'), true);
+		$this->assertEqual($result, '2 months, 1 week, 6 days');
+
+		$result = $this->Time->timeAgoInWords(strtotime('+1 month +1 week +6 days'), array('end' => '8 years'), true);
+		$this->assertEqual($result, '1 month, 1 week, 6 days');
+
+		for($i = 0; $i < 200; $i ++) {
+			$years = mt_rand(0, 3);
+			$months = mt_rand(0, 11);
+			$weeks = mt_rand(0, 3);
+			$days = mt_rand(0, 6);
+			$hours = 0;
+			$minutes = 0;
+			$seconds = 0;
+			$relative_date = '';
+
+			if ($years > 0) {
+				// years and months and days
+				$relative_date .= ($relative_date ? ', -' : '-') . $years . ' year' . ($years > 1 ? 's' : '');
+				$relative_date .= $months > 0 ? ($relative_date ? ', -' : '-') . $months . ' month' . ($months > 1 ? 's' : '') : '';
+				$relative_date .= $weeks > 0 ? ($relative_date ? ', -' : '-') . $weeks . ' week' . ($weeks > 1 ? 's' : '') : '';
+				$relative_date .= $days > 0 ? ($relative_date ? ', -' : '-') . $days . ' day' . ($days > 1 ? 's' : '') : '';
+			} elseif (abs($months) > 0) {
+				// months, weeks and days
+				$relative_date .= ($relative_date ? ', -' : '-') . $months . ' month' . ($months > 1 ? 's' : '');
+				$relative_date .= $weeks > 0 ? ($relative_date ? ', -' : '-') . $weeks . ' week' . ($weeks > 1 ? 's' : '') : '';
+				$relative_date .= $days > 0 ? ($relative_date ? ', -' : '-') . $days . ' day' . ($days > 1 ? 's' : '') : '';
+			} elseif (abs($weeks) > 0) {
+				// weeks and days
+				$relative_date .= ($relative_date ? ', -' : '-') . $weeks . ' week' . ($weeks > 1 ? 's' : '');
+				$relative_date .= $days > 0 ? ($relative_date ? ', -' : '-') . $days . ' day' . ($days > 1 ? 's' : '') : '';
+			} elseif (abs($days) > 0) {
+				// days and hours
+				$relative_date .= ($relative_date ? ', -' : '-') . $days . ' day' . ($days > 1 ? 's' : '');
+				$relative_date .= $hours > 0 ? ($relative_date ? ', -' : '-') . $hours . ' hour' . ($hours > 1 ? 's' : '') : '';
+			} elseif (abs($hours) > 0) {
+				// hours and minutes
+				$relative_date .= ($relative_date ? ', -' : '-') . $hours . ' hour' . ($hours > 1 ? 's' : '');
+				$relative_date .= $minutes > 0 ? ($relative_date ? ', -' : '-') . $minutes . ' minute' . ($minutes > 1 ? 's' : '') : '';
+			} elseif (abs($minutes) > 0) {
+				// minutes only
+				$relative_date .= ($relative_date ? ', -' : '-') . $minutes . ' minute' . ($minutes > 1 ? 's' : '');
+			} else {
+				// seconds only
+				$relative_date .= ($relative_date ? ', -' : '-') . $seconds . ' second' . ($seconds != 1 ? 's' : '');
+			}
+
+			if (date('j/n/y', strtotime(str_replace(',','',$relative_date))) != '1/1/70') {
+				$result = $this->Time->timeAgoInWords(strtotime(str_replace(',','',$relative_date)), array('end' => '8 years'), true);
+				if ($relative_date == '0 seconds') {
+					$relative_date = '0 seconds ago';
+				}
+
+				$relative_date = str_replace('-', '', $relative_date) . ' ago';
+				$this->assertEqual($result, $relative_date);
+
+			}
+		}
+
+		for ($i = 0; $i < 200; $i ++) {
+			$years = mt_rand(0, 3);
+			$months = mt_rand(0, 11);
+			$weeks = mt_rand(0, 3);
+			$days = mt_rand(0, 6);
+			$hours = 0;
+			$minutes = 0;
+			$seconds = 0;
+
+			$relative_date = '';
+
+			if ($years > 0) {
+				// years and months and days
+				$relative_date .= ($relative_date ? ', ' : '') . $years . ' year' . ($years > 1 ? 's' : '');
+				$relative_date .= $months > 0 ? ($relative_date ? ', ' : '') . $months . ' month' . ($months > 1 ? 's' : '') : '';
+				$relative_date .= $weeks > 0 ? ($relative_date ? ', ' : '') . $weeks . ' week' . ($weeks > 1 ? 's' : '') : '';
+				$relative_date .= $days > 0 ? ($relative_date ? ', ' : '') . $days . ' day' . ($days > 1 ? 's' : '') : '';
+			} elseif (abs($months) > 0) {
+				// months, weeks and days
+				$relative_date .= ($relative_date ? ', ' : '') . $months . ' month' . ($months > 1 ? 's' : '');
+				$relative_date .= $weeks > 0 ? ($relative_date ? ', ' : '') . $weeks . ' week' . ($weeks > 1 ? 's' : '') : '';
+				$relative_date .= $days > 0 ? ($relative_date ? ', ' : '') . $days . ' day' . ($days > 1 ? 's' : '') : '';
+			} elseif (abs($weeks) > 0) {
+				// weeks and days
+				$relative_date .= ($relative_date ? ', ' : '') . $weeks . ' week' . ($weeks > 1 ? 's' : '');
+				$relative_date .= $days > 0 ? ($relative_date ? ', ' : '') . $days . ' day' . ($days > 1 ? 's' : '') : '';
+			} elseif (abs($days) > 0) {
+				// days and hours
+				$relative_date .= ($relative_date ? ', ' : '') . $days . ' day' . ($days > 1 ? 's' : '');
+				$relative_date .= $hours > 0 ? ($relative_date ? ', ' : '') . $hours . ' hour' . ($hours > 1 ? 's' : '') : '';
+			} elseif (abs($hours) > 0) {
+				// hours and minutes
+				$relative_date .= ($relative_date ? ', ' : '') . $hours . ' hour' . ($hours > 1 ? 's' : '');
+				$relative_date .= $minutes > 0 ? ($relative_date ? ', ' : '') . $minutes . ' minute' . ($minutes > 1 ? 's' : '') : '';
+			} elseif (abs($minutes) > 0) {
+				// minutes only
+				$relative_date .= ($relative_date ? ', ' : '') . $minutes . ' minute' . ($minutes > 1 ? 's' : '');
+			} else {
+				// seconds only
+				$relative_date .= ($relative_date ? ', ' : '') . $seconds . ' second' . ($seconds != 1 ? 's' : '');
+			}
+
+			if (date('j/n/y', strtotime(str_replace(',','',$relative_date))) != '1/1/70') {
+				$result = $this->Time->timeAgoInWords(strtotime(str_replace(',','',$relative_date)), array('end' => '8 years'), true);
+				if ($relative_date == '0 seconds') {
+					$relative_date = '0 seconds ago';
+				}
+
+				$relative_date = str_replace('-', '', $relative_date) . '';
+				$this->assertEqual($result, $relative_date);
+			}
+		}
+
+		$result = $this->Time->timeAgoInWords(strtotime('-2 years -5 months -2 days'), array('end' => '3 years'), true);
+		$this->assertEqual($result, '2 years, 5 months, 2 days ago');
+
+		$result = $this->Time->timeAgoInWords('2007-9-25');
+		$this->assertEqual($result, 'on 25/9/07');
+
+		$result = $this->Time->timeAgoInWords('2007-9-25', 'Y-m-d');
+		$this->assertEqual($result, 'on 2007-09-25');
+
+		$result = $this->Time->timeAgoInWords('2007-9-25', 'Y-m-d', true);
+		$this->assertEqual($result, 'on 2007-09-25');
+
+		$result = $this->Time->timeAgoInWords(strtotime('-2 weeks -2 days'), 'Y-m-d', false);
+		$this->assertEqual($result, '2 weeks, 2 days ago');
+
+		$result = $this->Time->timeAgoInWords(strtotime('+2 weeks +2 days'), 'Y-m-d', true);
+		$this->assertPattern('/^2 weeks, [1|2] day(s)?$/', $result);
+
+		$result = $this->Time->timeAgoInWords(strtotime('+2 months +2 days'), array('end' => '1 month'));
+		$this->assertEqual($result, 'on ' . date('j/n/y', strtotime('+2 months +2 days')));
+
+		$result = $this->Time->timeAgoInWords(strtotime('+2 months +2 days'), array('end' => '3 month'));
+		$this->assertPattern('/2 months/', $result);
+
+		$result = $this->Time->timeAgoInWords(strtotime('+2 months +12 days'), array('end' => '3 month'));
+		$this->assertPattern('/2 months, 1 week/', $result);
+
+		$result = $this->Time->timeAgoInWords(strtotime('+3 months +5 days'), array('end' => '4 month'));
+		$this->assertEqual($result, '3 months, 5 days');
+
+		$result = $this->Time->timeAgoInWords(strtotime('-2 months -2 days'), array('end' => '3 month'));
+		$this->assertEqual($result, '2 months, 2 days ago');
+
+		$result = $this->Time->timeAgoInWords(strtotime('-2 months -2 days'), array('end' => '3 month'));
+		$this->assertEqual($result, '2 months, 2 days ago');
+
+		$result = $this->Time->timeAgoInWords(strtotime('+2 months +2 days'), array('end' => '3 month'));
+		$this->assertPattern('/2 months/', $result);
+
+		$result = $this->Time->timeAgoInWords(strtotime('+2 months +2 days'), array('end' => '1 month', 'format' => 'Y-m-d'));
+		$this->assertEqual($result, 'on ' . date('Y-m-d', strtotime('+2 months +2 days')));
+
+		$result = $this->Time->timeAgoInWords(strtotime('-2 months -2 days'), array('end' => '1 month', 'format' => 'Y-m-d'));
+		$this->assertEqual($result, 'on ' . date('Y-m-d', strtotime('-2 months -2 days')));
+
+		$result = $this->Time->timeAgoInWords(strtotime('-13 months -5 days'), array('end' => '2 years'));
+		$this->assertEqual($result, '1 year, 1 month, 5 days ago');
+
+		$fourHours = $this->Time->timeAgoInWords(strtotime('-5 days -2 hours'), array('userOffset' => -4));
+		$result = $this->Time->timeAgoInWords(strtotime('-5 days -2 hours'), array('userOffset' => 4));
+		$this->assertEqual($fourHours, $result);
+
+		$result = $this->Time->timeAgoInWords(strtotime('-2 hours'));
+		$expected = '2 hours ago';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Time->timeAgoInWords(strtotime('-12 minutes'));
+		$expected = '12 minutes ago';
+		$this->assertEqual($expected, $result);
+
+		$result = $this->Time->timeAgoInWords(strtotime('-12 seconds'));
+		$expected = '12 seconds ago';
+		$this->assertEqual($expected, $result);
+
+		$time = strtotime('-3 years -12 months');
+		$result = $this->Time->timeAgoInWords($time);
+		$expected = 'on ' . date('j/n/y', $time);
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * testRelative method
+ *
+ * @access public
+ * @return void
+ */
+	function testRelative() {
+		$result = $this->Time->relativeTime('-1 week');
+		$this->assertEqual($result, '1 week ago');
+		$result = $this->Time->relativeTime('+1 week');
+		$this->assertEqual($result, '1 week');
+	}
+
+/**
+ * testNice method
+ *
+ * @access public
+ * @return void
+ */
+	function testNice() {
+		$time = time() + 2 * DAY;
+		$this->assertEqual(date('D, M jS Y, H:i', $time), $this->Time->nice($time));
+
+		$time = time() - 2 * DAY;
+		$this->assertEqual(date('D, M jS Y, H:i', $time), $this->Time->nice($time));
+
+		$time = time();
+		$this->assertEqual(date('D, M jS Y, H:i', $time), $this->Time->nice($time));
+
+		$time = 0;
+		$this->assertEqual(date('D, M jS Y, H:i', time()), $this->Time->nice($time));
+
+		$time = null;
+		$this->assertEqual(date('D, M jS Y, H:i', time()), $this->Time->nice($time));
+	}
+
+/**
+ * testNiceShort method
+ *
+ * @access public
+ * @return void
+ */
+	function testNiceShort() {
+		$time = time() + 2 * DAY;
+		if (date('Y', $time) == date('Y')) {
+			$this->assertEqual(date('M jS, H:i', $time), $this->Time->niceShort($time));
+		} else {
+			$this->assertEqual(date('M jS Y, H:i', $time), $this->Time->niceShort($time));
+		}
+
+		$time = time();
+		$this->assertEqual('Today, ' . date('H:i', $time), $this->Time->niceShort($time));
+
+		$time = time() - DAY;
+		$this->assertEqual('Yesterday, ' . date('H:i', $time), $this->Time->niceShort($time));
+	}
+
+/**
+ * testDaysAsSql method
+ *
+ * @access public
+ * @return void
+ */
+	function testDaysAsSql() {
+		$begin = time();
+		$end = time() + DAY;
+		$field = 'my_field';
+		$expected = '(my_field >= \''.date('Y-m-d', $begin).' 00:00:00\') AND (my_field <= \''.date('Y-m-d', $end).' 23:59:59\')';
+		$this->assertEqual($expected, $this->Time->daysAsSql($begin, $end, $field));
+	}
+
+/**
+ * testDayAsSql method
+ *
+ * @access public
+ * @return void
+ */
+	function testDayAsSql() {
+		$time = time();
+		$field = 'my_field';
+		$expected = '(my_field >= \''.date('Y-m-d', $time).' 00:00:00\') AND (my_field <= \''.date('Y-m-d', $time).' 23:59:59\')';
+		$this->assertEqual($expected, $this->Time->dayAsSql($time, $field));
+	}
+
+/**
+ * testToUnix method
+ *
+ * @access public
+ * @return void
+ */
+	function testToUnix() {
+		$this->assertEqual(time(), $this->Time->toUnix(time()));
+		$this->assertEqual(strtotime('+1 day'), $this->Time->toUnix('+1 day'));
+		$this->assertEqual(strtotime('+0 days'), $this->Time->toUnix('+0 days'));
+		$this->assertEqual(strtotime('-1 days'), $this->Time->toUnix('-1 days'));
+		$this->assertEqual(false, $this->Time->toUnix(''));
+		$this->assertEqual(false, $this->Time->toUnix(null));
+	}
+
+/**
+ * testToAtom method
+ *
+ * @access public
+ * @return void
+ */
+	function testToAtom() {
+		$this->assertEqual(date('Y-m-d\TH:i:s\Z'), $this->Time->toAtom(time()));
+	}
+
+/**
+ * testToRss method
+ *
+ * @access public
+ * @return void
+ */
+	function testToRss() {
+		$this->assertEqual(date('r'), $this->Time->toRss(time()));
+
+		if (!$this->skipIf(!class_exists('DateTimeZone'), '%s DateTimeZone class not available.')) {
+			$timezones = array('Europe/London', 'Europe/Brussels', 'UTC', 'America/Denver', 'America/Caracas', 'Asia/Kathmandu');
+			foreach($timezones as $timezone) {
+				$yourTimezone = new DateTimeZone($timezone);
+				$yourTime = new DateTime('now', $yourTimezone);
+				$userOffset = $yourTimezone->getOffset($yourTime) / HOUR;
+				$this->assertEqual($yourTime->format('r'), $this->Time->toRss(time(), $userOffset));	
+			}	
+		}
+	}
+
+/**
+ * testFormat method
+ *
+ * @access public
+ * @return void
+ */
+	function testFormat() {
+		$format = 'D-M-Y';
+		$arr = array(time(), strtotime('+1 days'), strtotime('+1 days'), strtotime('+0 days'));
+		foreach ($arr as $val) {
+			$this->assertEqual(date($format, $val), $this->Time->format($format, $val));
+		}
+
+		$result = $this->Time->format('Y-m-d', null, 'never');
+		$this->assertEqual($result, 'never');
+	}
+
+/**
+ * testOfGmt method
+ *
+ * @access public
+ * @return void
+ */
+	function testGmt() {
+		$hour = 3;
+		$min = 4;
+		$sec = 2;
+		$month = 5;
+		$day = 14;
+		$year = 2007;
+		$time = mktime($hour, $min, $sec, $month, $day, $year);
+		$expected = gmmktime($hour, $min, $sec, $month, $day, $year);
+		$this->assertEqual($expected, $this->Time->gmt(date('Y-n-j G:i:s', $time)));
+
+		$hour = date('H');
+		$min = date('i');
+		$sec = date('s');
+		$month = date('m');
+		$day = date('d');
+		$year = date('Y');
+		$expected = gmmktime($hour, $min, $sec, $month, $day, $year);
+		$this->assertEqual($expected, $this->Time->gmt(null));
+	}
+
+/**
+ * testIsToday method
+ *
+ * @access public
+ * @return void
+ */
+	function testIsToday() {
+		$result = $this->Time->isToday('+1 day');
+		$this->assertFalse($result);
+		$result = $this->Time->isToday('+1 days');
+		$this->assertFalse($result);
+		$result = $this->Time->isToday('+0 day');
+		$this->assertTrue($result);
+		$result = $this->Time->isToday('-1 day');
+		$this->assertFalse($result);
+	}
+
+/**
+ * testIsThisWeek method
+ *
+ * @access public
+ * @return void
+ */
+	function testIsThisWeek() {
+		// A map of days which goes from -1 day of week to +1 day of week
+		$map = array(
+			'Mon' => array(-1, 7), 'Tue' => array(-2, 6), 'Wed' => array(-3, 5),
+			'Thu' => array(-4, 4), 'Fri' => array(-5, 3), 'Sat' => array(-6, 2),
+			'Sun' => array(-7, 1)
+		);
+		$days = $map[date('D')];
+
+		for ($day = $days[0] + 1; $day < $days[1]; $day++) {
+			$this->assertTrue($this->Time->isThisWeek(($day > 0 ? '+' : '') . $day . ' days'));
+		}
+		$this->assertFalse($this->Time->isThisWeek($days[0] . ' days'));
+		$this->assertFalse($this->Time->isThisWeek('+' . $days[1] . ' days'));
+	}
+
+/**
+ * testIsThisMonth method
+ *
+ * @access public
+ * @return void
+ */
+	function testIsThisMonth() {
+		$result = $this->Time->isThisMonth('+0 day');
+		$this->assertTrue($result);
+		$result = $this->Time->isThisMonth($time = mktime(0, 0, 0, date('m'), mt_rand(1, 28), date('Y')));
+		$this->assertTrue($result);
+		$result = $this->Time->isThisMonth(mktime(0, 0, 0, date('m'), mt_rand(1, 28), date('Y') - mt_rand(1, 12)));
+		$this->assertFalse($result);
+		$result = $this->Time->isThisMonth(mktime(0, 0, 0, date('m'), mt_rand(1, 28), date('Y') + mt_rand(1, 12)));
+		$this->assertFalse($result);
+
+	}
+
+/**
+ * testIsThisYear method
+ *
+ * @access public
+ * @return void
+ */
+	function testIsThisYear() {
+		$result = $this->Time->isThisYear('+0 day');
+		$this->assertTrue($result);
+		$result = $this->Time->isThisYear(mktime(0, 0, 0, mt_rand(1, 12), mt_rand(1, 28), date('Y')));
+		$this->assertTrue($result);
+	}
+	/**
+ * testWasYesterday method
+ *
+ * @access public
+ * @return void
+ */
+	function testWasYesterday() {
+		$result = $this->Time->wasYesterday('+1 day');
+		$this->assertFalse($result);
+		$result = $this->Time->wasYesterday('+1 days');
+		$this->assertFalse($result);
+		$result = $this->Time->wasYesterday('+0 day');
+		$this->assertFalse($result);
+		$result = $this->Time->wasYesterday('-1 day');
+		$this->assertTrue($result);
+		$result = $this->Time->wasYesterday('-1 days');
+		$this->assertTrue($result);
+		$result = $this->Time->wasYesterday('-2 days');
+		$this->assertFalse($result);
+	}
+	/**
+ * testIsTomorrow method
+ *
+ * @access public
+ * @return void
+ */
+	function testIsTomorrow() {
+		$result = $this->Time->isTomorrow('+1 day');
+		$this->assertTrue($result);
+		$result = $this->Time->isTomorrow('+1 days');
+		$this->assertTrue($result);
+		$result = $this->Time->isTomorrow('+0 day');
+		$this->assertFalse($result);
+		$result = $this->Time->isTomorrow('-1 day');
+		$this->assertFalse($result);
+	}
+
+/**
+ * testWasWithinLast method
+ *
+ * @access public
+ * @return void
+ */
+	function testWasWithinLast() {
+		$this->assertTrue($this->Time->wasWithinLast('1 day', '-1 day'));
+		$this->assertTrue($this->Time->wasWithinLast('1 week', '-1 week'));
+		$this->assertTrue($this->Time->wasWithinLast('1 year', '-1 year'));
+		$this->assertTrue($this->Time->wasWithinLast('1 second', '-1 second'));
+		$this->assertTrue($this->Time->wasWithinLast('1 minute', '-1 minute'));
+		$this->assertTrue($this->Time->wasWithinLast('1 year', '-1 year'));
+		$this->assertTrue($this->Time->wasWithinLast('1 month', '-1 month'));
+		$this->assertTrue($this->Time->wasWithinLast('1 day', '-1 day'));
+
+		$this->assertTrue($this->Time->wasWithinLast('1 week', '-1 day'));
+		$this->assertTrue($this->Time->wasWithinLast('2 week', '-1 week'));
+		$this->assertFalse($this->Time->wasWithinLast('1 second', '-1 year'));
+		$this->assertTrue($this->Time->wasWithinLast('10 minutes', '-1 second'));
+		$this->assertTrue($this->Time->wasWithinLast('23 minutes', '-1 minute'));
+		$this->assertFalse($this->Time->wasWithinLast('0 year', '-1 year'));
+		$this->assertTrue($this->Time->wasWithinLast('13 month', '-1 month'));
+		$this->assertTrue($this->Time->wasWithinLast('2 days', '-1 day'));
+
+		$this->assertFalse($this->Time->wasWithinLast('1 week', '-2 weeks'));
+		$this->assertFalse($this->Time->wasWithinLast('1 second', '-2 seconds'));
+		$this->assertFalse($this->Time->wasWithinLast('1 day', '-2 days'));
+		$this->assertFalse($this->Time->wasWithinLast('1 hour', '-2 hours'));
+		$this->assertFalse($this->Time->wasWithinLast('1 month', '-2 months'));
+		$this->assertFalse($this->Time->wasWithinLast('1 year', '-2 years'));
+
+		$this->assertFalse($this->Time->wasWithinLast('1 day', '-2 weeks'));
+		$this->assertFalse($this->Time->wasWithinLast('1 day', '-2 days'));
+		$this->assertFalse($this->Time->wasWithinLast('0 days', '-2 days'));
+		$this->assertTrue($this->Time->wasWithinLast('1 hour', '-20 seconds'));
+		$this->assertTrue($this->Time->wasWithinLast('1 year', '-60 minutes -30 seconds'));
+		$this->assertTrue($this->Time->wasWithinLast('3 years', '-2 months'));
+		$this->assertTrue($this->Time->wasWithinLast('5 months', '-4 months'));
+
+		$this->assertTrue($this->Time->wasWithinLast('5 ', '-3 days'));
+		$this->assertTrue($this->Time->wasWithinLast('1   ', '-1 hour'));
+		$this->assertTrue($this->Time->wasWithinLast('1   ', '-1 minute'));
+		$this->assertTrue($this->Time->wasWithinLast('1   ', '-23 hours -59 minutes -59 seconds'));
+	}
+	/**
+ * testUserOffset method
+ *
+ * @access public
+ * @return void
+ */
+	function testUserOffset() {
+		if ($this->skipIf(!class_exists('DateTimeZone'), '%s DateTimeZone class not available.')) {
+			return;
+		}
+
+
+		$timezoneServer = new DateTimeZone(date_default_timezone_get());
+		$timeServer = new DateTime('now', $timezoneServer);
+		$yourTimezone = $timezoneServer->getOffset($timeServer) / HOUR;
+
+		$expected = time();
+		$result = $this->Time->fromString(time(), $yourTimezone);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test fromString()
+ *
+ * @access public
+ * @return void
+ */
+	function testFromString() {
+		$result = $this->Time->fromString('');
+		$this->assertFalse($result);
+
+		$result = $this->Time->fromString(0, 0);
+		$this->assertFalse($result);
+
+		$result = $this->Time->fromString('+1 hour');
+		$expected = strtotime('+1 hour');
+		$this->assertEqual($result, $expected);
+
+		$timezone = date('Z', time());
+		$result = $this->Time->fromString('+1 hour', $timezone);
+		$expected = $this->Time->convert(strtotime('+1 hour'), $timezone);
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test converting time specifiers using a time definition localfe file
+ *
+ * @access public
+ * @return void
+ */
+	function testConvertSpecifiers() {
+		App::build(array(
+			'locales' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'locale' . DS)
+		), true);
+		Configure::write('Config.language', 'time_test');
+		$time = strtotime('Thu Jan 14 11:43:39 2010');
+
+		$result = $this->Time->convertSpecifiers('%a', $time);
+		$expected = 'jue';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Time->convertSpecifiers('%A', $time);
+		$expected = 'jueves';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Time->convertSpecifiers('%c', $time);
+		$expected = 'jue %d ene %Y %H:%M:%S %Z';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Time->convertSpecifiers('%C', $time);
+		$expected = '20';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Time->convertSpecifiers('%D', $time);
+		$expected = '%m/%d/%y';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Time->convertSpecifiers('%b', $time);
+		$expected = 'ene';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Time->convertSpecifiers('%h', $time);
+		$expected = 'ene';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Time->convertSpecifiers('%B', $time);
+		$expected = 'enero';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Time->convertSpecifiers('%n', $time);
+		$expected = "\n";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Time->convertSpecifiers('%n', $time);
+		$expected = "\n";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Time->convertSpecifiers('%p', $time);
+		$expected = 'AM';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Time->convertSpecifiers('%P', $time);
+		$expected = 'am';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Time->convertSpecifiers('%r', $time);
+		$expected = '%I:%M:%S AM';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Time->convertSpecifiers('%R', $time);
+		$expected = '11:43';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Time->convertSpecifiers('%t', $time);
+		$expected = "\t";
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Time->convertSpecifiers('%T', $time);
+		$expected = '%H:%M:%S';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Time->convertSpecifiers('%u', $time);
+		$expected = 4;
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Time->convertSpecifiers('%x', $time);
+		$expected = '%d/%m/%y';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Time->convertSpecifiers('%X', $time);
+		$expected = '%H:%M:%S';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test convert %e on windows.
+ *
+ * @return void
+ */
+	function testConvertPercentE() {
+		if ($this->skipIf(DS !== '\\', 'Cannot run windows tests on non-windows OS')) {
+			return;
+		}
+		$time = strtotime('Thu Jan 14 11:43:39 2010');
+		$result = $this->Time->convertSpecifiers('%e', $time);
+		$expected = '14';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Time->convertSpecifiers('%e', strtotime('2011-01-01'));
+		$expected = ' 1';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test formatting dates taking in account preferred i18n locale file
+ *
+ * @access public
+ * @return void
+ */
+	function testI18nFormat() {
+		App::build(array(
+			'locales' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'locale' . DS)
+		), true);
+		Configure::write('Config.language', 'time_test');
+
+		$time = strtotime('Thu Jan 14 13:59:28 2010');
+
+		$result = $this->Time->i18nFormat($time);
+		$expected = '14/01/10';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Time->i18nFormat($time, '%c');
+		$expected = 'jue 14 ene 2010 13:59:28 ' . strftime('%Z', $time);
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Time->i18nFormat($time, 'Time is %r, and date is %x');
+		$expected = 'Time is 01:59:28 PM, and date is 14/01/10';
+		$this->assertEqual($result, $expected);
+
+		$time = strtotime('Wed Jan 13 13:59:28 2010');
+
+		$result = $this->Time->i18nFormat($time);
+		$expected = '13/01/10';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Time->i18nFormat($time, '%c');
+		$expected = 'mié 13 ene 2010 13:59:28 ' . strftime('%Z', $time);
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Time->i18nFormat($time, 'Time is %r, and date is %x');
+		$expected = 'Time is 01:59:28 PM, and date is 13/01/10';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Time->i18nFormat('invalid date', '%x', 'Date invalid');
+		$expected = 'Date invalid';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test new format() syntax which inverts first and secod parameters
+ *
+ * @access public
+ * @return void
+ */
+	function testFormatNewSyntax() {
+		$time = time();
+		$this->assertEqual($this->Time->format($time), $this->Time->i18nFormat($time));
+		$this->assertEqual($this->Time->format($time, '%c'), $this->Time->i18nFormat($time, '%c'));
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/view/helpers/xml.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/view/helpers/xml.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/view/helpers/xml.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,298 @@
+<?php
+/**
+ * XmlHelperTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) {
+	define('CAKEPHP_UNIT_TEST_EXECUTION', 1);
+}
+App::import('Helper', 'Xml');
+
+/**
+ * TestXml class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class TestXml extends Object {
+
+/**
+ * content property
+ *
+ * @var string ''
+ * @access public
+ */
+	var $content = '';
+
+/**
+ * construct method
+ *
+ * @param mixed $content
+ * @access private
+ * @return void
+ */
+	function __construct($content) {
+		$this->content = $content;
+	}
+
+/**
+ * toString method
+ *
+ * @access public
+ * @return void
+ */
+	function toString() {
+		return $this->content;
+	}
+}
+
+/**
+ * XmlHelperTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view.helpers
+ */
+class XmlHelperTest extends CakeTestCase {
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		$this->Xml =& new XmlHelper();
+		$this->Xml->beforeRender();
+		$manager =& XmlManager::getInstance();
+		$manager->namespaces = array();
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		unset($this->Xml);
+	}
+
+/**
+ * testAddNamespace method
+ *
+ * @access public
+ * @return void
+ */
+	function testAddNamespace() {
+		$this->Xml->addNs('custom', 'http://example.com/dtd.xml');
+		$manager =& XmlManager::getInstance();
+
+		$expected = array('custom' => 'http://example.com/dtd.xml');
+		$this->assertEqual($manager->namespaces, $expected);
+	}
+
+/**
+ * testRemoveNamespace method
+ *
+ * @access public
+ * @return void
+ */
+	function testRemoveNamespace() {
+		$this->Xml->addNs('custom', 'http://example.com/dtd.xml');
+		$this->Xml->addNs('custom2', 'http://example.com/dtd2.xml');
+		$manager =& XmlManager::getInstance();
+
+		$expected = array('custom' => 'http://example.com/dtd.xml', 'custom2' => 'http://example.com/dtd2.xml');
+		$this->assertEqual($manager->namespaces, $expected);
+
+		$this->Xml->removeNs('custom');
+		$expected = array('custom2' => 'http://example.com/dtd2.xml');
+		$this->assertEqual($manager->namespaces, $expected);
+	}
+
+/**
+ * testRenderZeroElement method
+ *
+ * @access public
+ * @return void
+ */
+	function testRenderZeroElement() {
+		$result = $this->Xml->elem('count', null, 0);
+		$expected = '<count>0</count>';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Xml->elem('count', null, array('cdata' => true, 'value' => null));
+		$expected = '<count />';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testRenderElementWithNamespace method
+ *
+ * @access public
+ * @return void
+ */
+	function testRenderElementWithNamespace() {
+		$result = $this->Xml->elem('count', array('namespace' => 'myNameSpace'), 'content');
+		$expected = '<myNameSpace:count>content</myNameSpace:count>';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Xml->elem('count', array('namespace' => 'myNameSpace'), 'content', false);
+		$expected = '<myNameSpace:count>content';
+		$this->assertEqual($result, $expected);
+
+		$expected .= '</myNameSpace:count>';
+		$result .= $this->Xml->closeElem();
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testRenderElementWithComplexContent method
+ *
+ * @access public
+ * @return void
+ */
+	function testRenderElementWithComplexContent() {
+		$result = $this->Xml->elem('count', array('namespace' => 'myNameSpace'), array('contrived' => 'content'));
+		$expected = '<myNameSpace:count><content /></myNameSpace:count>';
+		$this->assertEqual($result, $expected);
+
+		$result = $this->Xml->elem('count', array('namespace' => 'myNameSpace'), array('cdata' => true, 'value' => 'content'));
+		$expected = '<myNameSpace:count><![CDATA[content]]></myNameSpace:count>';
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testSerialize method
+ *
+ * @access public
+ * @return void
+ */
+	function testSerialize() {
+		$data = array(
+			'test1' => 'test with no quotes',
+			'test2' => 'test with "double quotes"'
+		);
+		$result = $this->Xml->serialize($data);
+		$expected = '<std_class test1="test with no quotes" test2="test with &quot;double quotes&quot;" />';
+		$this->assertIdentical($result, $expected);
+
+		$data = array(
+			'test1' => 'test with no quotes',
+			'test2' => 'test without double quotes'
+		);
+		$result = $this->Xml->serialize($data);
+		$expected = '<std_class test1="test with no quotes" test2="test without double quotes" />';
+		$this->assertIdentical($result, $expected);
+
+		$data = array(
+			'ServiceDay' => array('ServiceTime' => array('ServiceTimePrice' => array('dollar' => 1, 'cents' => '2')))
+		);
+		$result = $this->Xml->serialize($data);
+		$expected = '<service_day><service_time><service_time_price dollar="1" cents="2" /></service_time></service_day>';
+		$this->assertIdentical($result, $expected);
+
+		$data = array(
+			'ServiceDay' => array('ServiceTime' => array('ServiceTimePrice' => array('dollar' => 1, 'cents' => '2')))
+		);
+		$result = $this->Xml->serialize($data, array('format' => 'tags'));
+		$expected = '<service_day><service_time><service_time_price><dollar>1</dollar><cents>2</cents></service_time_price></service_time></service_day>';
+		$this->assertIdentical($result, $expected);
+		
+		$data = array(
+			'Pages' => array('id' => 2, 'url' => 'http://www.url.com/rb/153/?id=bbbb&t=access')
+		);
+		$result = $this->Xml->serialize($data);
+		$expected = '<pages id="2" url="http://www.url.com/rb/153/?id=bbbb&amp;t=access" />';
+		$this->assertIdentical($result, $expected);
+
+		$test = array(
+			'Test' => array('test' => true)
+		);
+		$expected = '<test test="1" />';
+		$result = $this->Xml->serialize($test);
+		$this->assertidentical($expected, $result);
+	}
+
+/**
+ * testSerializeOnMultiDimensionalArray method
+ *
+ * @access public
+ * @return void
+ */
+	function testSerializeOnMultiDimensionalArray() {
+		$data = array(
+			'Statuses' => array(
+				array('Status' => array('id' => 1)),
+				array('Status' => array('id' => 2))
+			)
+		);
+		$result = $this->Xml->serialize($data, array('format' => 'tags'));
+		$expected = '<statuses><status><id>1</id></status><status><id>2</id></status></statuses>';
+		$this->assertIdentical($result, $expected);
+
+	}
+
+/**
+ * testHeader method
+ *
+ * @access public
+ * @return void
+ */
+	function testHeader() {
+		$expectedDefaultEncoding = Configure::read('App.encoding');
+		if (empty($expectedDefaultEncoding)) {
+			$expectedDefaultEncoding = 'UTF-8';
+		}
+		$attrib = array();
+		$result = $this->Xml->header($attrib);
+		$expected = '<?xml version="1.0" encoding="'.$expectedDefaultEncoding.'" ?>';
+		$this->assertIdentical($result, $expected);
+
+		$attrib = array(
+			'encoding' => 'UTF-8',
+			'version' => '1.1'
+		);
+		$result = $this->Xml->header($attrib);
+		$expected = '<?xml version="1.1" encoding="UTF-8" ?>';
+		$this->assertIdentical($result, $expected);
+
+		$attrib = array(
+			'encoding' => 'UTF-8',
+			'version' => '1.2',
+			'additional' => 'attribute'
+		);
+		$result = $this->Xml->header($attrib);
+		$expected = '<?xml version="1.2" encoding="UTF-8" additional="attribute" ?>';
+		$this->assertIdentical($result, $expected);
+
+		$attrib = 'encoding="UTF-8" someOther="value"';
+		$result = $this->Xml->header($attrib);
+		$expected = '<?xml encoding="UTF-8" someOther="value" ?>';
+		$this->assertIdentical($result, $expected);
+	}
+
+/**
+ * test that calling elem() and then header() doesn't break
+ *
+ * @return void
+ */
+	function testElemThenHeader() {
+		$this->Xml->elem('test', array(), 'foo', false);
+		$this->assertPattern('/<\?xml/', $this->Xml->header());
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/view/media.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/view/media.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/view/media.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,204 @@
+<?php
+/**
+ * ThemeViewTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', array('Media', 'Controller'));
+
+if (!class_exists('ErrorHandler')) {
+	App::import('Core', array('Error'));
+}
+if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) {
+	define('CAKEPHP_UNIT_TEST_EXECUTION', 1);
+}
+
+/**
+ * ThemePostsController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view
+ */
+class MediaController extends Controller {
+
+/**
+ * name property
+ *
+ * @var string 'Media'
+ * @access public
+ */
+	var $name = 'Media';
+
+/**
+ * index download
+ *
+ * @access public
+ * @return void
+ */
+	function download() {
+		$path = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'vendors' . DS .'css' . DS;
+		$id = 'test_asset.css';
+		$extension = 'css';
+		$this->set(compact('path', 'id', 'extension'));
+	}
+	
+	function downloadUpper() {
+		$path = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'vendors' . DS .'img' . DS;
+		$id = 'test_2.JPG';
+		$extension = 'JPG';
+		$this->set(compact('path', 'id', 'extension'));
+	}
+}
+
+/**
+ * TestMediaView class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view
+ */
+class TestMediaView extends MediaView {
+
+/**
+ * headers public property as a copy from protected property _headers
+ *
+ * @var array
+ * @access public
+ */
+	var $headers = array();
+
+/**
+ * active property to mock the status of a remote connection
+ *
+ * @var boolean true
+ * @access public
+ */
+	var $active = true;
+
+	function _output() {
+		$this->headers = $this->_headers;
+	}
+
+/**
+ * _isActive method. Usted de $active property to mock an active (true) connection,
+ * or an aborted (false) one
+ *
+ * @access protected
+ * @return void
+ */
+	function _isActive() {
+		return $this->active;
+	}
+
+/**
+ * _clearBuffer method
+ *
+ * @access protected
+ * @return void
+ */
+	function _clearBuffer() {
+		return true;
+	}
+
+/**
+ * _flushBuffer method
+ *
+ * @access protected
+ * @return void
+ */
+	function _flushBuffer() {
+	}
+}
+
+/**
+ * ThemeViewTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class MediaViewTest extends CakeTestCase {
+
+/**
+ * startTest method
+ *
+ * @access public
+ * @return void
+ */
+	function startTest() {
+		Router::reload();
+		$this->Controller =& new Controller();
+		$this->MediaController =& new MediaController();
+		$this->MediaController->viewPath = 'posts';
+	}
+
+/**
+ * endTest method
+ *
+ * @access public
+ * @return void
+ */
+	function endTest() {
+		unset($this->MediaView);
+		unset($this->MediaController);
+		unset($this->Controller);
+		ClassRegistry::flush();
+	}
+
+/**
+ * testRender method
+ *
+ * @access public
+ * @return void
+ */
+	function testRender() {
+		ob_start();
+		$this->MediaController->download();
+		$this->MediaView =& new TestMediaView($this->MediaController);
+		$result = $this->MediaView->render();
+		$output = ob_get_clean();
+
+		$this->assertTrue($result !== false);
+		$this->assertEqual($output, 'this is the test asset css file');
+	}
+	
+	function testRenderUpperExtension() {
+		ob_start();
+		$this->MediaController->downloadUpper();
+		$this->MediaView =& new TestMediaView($this->MediaController);
+		$result = $this->MediaView->render();
+		$output = ob_get_clean();
+
+		$this->assertTrue($result !== false);
+		
+		$fileName = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'vendors' . DS .'img' . DS . 'test_2.JPG';
+		$file = file_get_contents($fileName, 'r');
+		
+		$this->assertEqual(base64_encode($output), base64_encode($file));		
+	}
+
+/**
+ * testConnectionAborted method
+ *
+ * @access public
+ * @return void
+ */
+	function testConnectionAborted() {
+		$this->MediaController->download();
+		$this->MediaView =& new TestMediaView($this->MediaController);
+		$this->MediaView->active = false;
+		$result = $this->MediaView->render();
+		$this->assertFalse($result);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/view/theme.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/view/theme.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/view/theme.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,343 @@
+<?php
+/**
+ * ThemeViewTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', array('Theme', 'Controller'));
+
+if (!class_exists('ErrorHandler')) {
+	App::import('Core', array('Error'));
+}
+if (!defined('CAKEPHP_UNIT_TEST_EXECUTION')) {
+	define('CAKEPHP_UNIT_TEST_EXECUTION', 1);
+}
+
+/**
+ * ThemePostsController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view
+ */
+class ThemePostsController extends Controller {
+
+/**
+ * name property
+ *
+ * @var string 'ThemePosts'
+ * @access public
+ */
+	var $name = 'ThemePosts';
+
+/**
+ * index method
+ *
+ * @access public
+ * @return void
+ */
+	function index() {
+		$this->set('testData', 'Some test data');
+		$test2 = 'more data';
+		$test3 = 'even more data';
+		$this->set(compact('test2', 'test3'));
+	}
+}
+
+/**
+ * ThemeViewTestErrorHandler class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view
+ */
+class ThemeViewTestErrorHandler extends ErrorHandler {
+
+/**
+ * stop method
+ *
+ * @access public
+ * @return void
+ */
+	function _stop() {
+		return;
+	}
+}
+
+/**
+ * TestThemeView class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view
+ */
+class TestThemeView extends ThemeView {
+
+/**
+ * renderElement method
+ *
+ * @param mixed $name
+ * @param array $params
+ * @access public
+ * @return void
+ */
+	function renderElement($name, $params = array()) {
+		return $name;
+	}
+
+/**
+ * getViewFileName method
+ *
+ * @param mixed $name
+ * @access public
+ * @return void
+ */
+	function getViewFileName($name = null) {
+		return $this->_getViewFileName($name);
+	}
+
+/**
+ * getLayoutFileName method
+ *
+ * @param mixed $name
+ * @access public
+ * @return void
+ */
+	function getLayoutFileName($name = null) {
+		return $this->_getLayoutFileName($name);
+	}
+
+/**
+ * cakeError method
+ *
+ * @param mixed $method
+ * @param mixed $messages
+ * @access public
+ * @return void
+ */
+	function cakeError($method, $messages) {
+		$error =& new ThemeViewTestErrorHandler($method, $messages);
+		return $error;
+	}
+}
+
+/**
+ * ThemeViewTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class ThemeViewTest extends CakeTestCase {
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		Router::reload();
+		$this->Controller =& new Controller();
+		$this->PostsController =& new ThemePostsController();
+		$this->PostsController->viewPath = 'posts';
+		$this->PostsController->index();
+		$this->ThemeView =& new ThemeView($this->PostsController);
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		unset($this->ThemeView);
+		unset($this->PostsController);
+		unset($this->Controller);
+		ClassRegistry::flush();
+	}
+/**
+ * test that the theme view can be constructed without going into the registry
+ *
+ * @return void
+ */
+	function testConstructionNoRegister() {
+		ClassRegistry::flush();
+		$controller = null;
+		$Theme =& new ThemeView($controller, false);
+		$ThemeTwo =& ClassRegistry::getObject('view');
+		$this->assertFalse($ThemeTwo);
+	}
+
+/**
+ * startTest
+ *
+ * @access public
+ * @return void
+ */
+	function startTest() {
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS),
+			'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS)
+		));
+	}
+
+/**
+ * endTest
+ *
+ * @access public
+ * @return void
+ */
+	function endTest() {
+		App::build();
+	}
+
+/**
+ * testPluginGetTemplate method
+ *
+ * @access public
+ * @return void
+ */
+	function testPluginThemedGetTemplate() {
+		$this->Controller->plugin = 'test_plugin';
+		$this->Controller->name = 'TestPlugin';
+		$this->Controller->viewPath = 'tests';
+		$this->Controller->action = 'index';
+		$this->Controller->theme = 'test_theme';
+
+		$ThemeView =& new TestThemeView($this->Controller);
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'themed' . DS . 'test_theme' . DS . 'plugins' . DS . 'test_plugin' . DS . 'tests' . DS .'index.ctp';
+		$result = $ThemeView->getViewFileName('index');
+		$this->assertEqual($result, $expected);
+
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'themed' . DS . 'test_theme' . DS . 'plugins' . DS . 'test_plugin' . DS . 'layouts' . DS .'plugin_default.ctp';
+		$result = $ThemeView->getLayoutFileName('plugin_default');
+		$this->assertEqual($result, $expected);
+
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'themed' . DS . 'test_theme' . DS . 'layouts' . DS .'default.ctp';
+		$result = $ThemeView->getLayoutFileName('default');
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testGetTemplate method
+ *
+ * @access public
+ * @return void
+ */
+	function testGetTemplate() {
+		$this->Controller->plugin = null;
+		$this->Controller->name = 'Pages';
+		$this->Controller->viewPath = 'pages';
+		$this->Controller->action = 'display';
+		$this->Controller->params['pass'] = array('home');
+
+		$ThemeView =& new TestThemeView($this->Controller);
+		$ThemeView->theme = 'test_theme';
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS .'pages' . DS .'home.ctp';
+		$result = $ThemeView->getViewFileName('home');
+		$this->assertEqual($result, $expected);
+
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'themed' . DS . 'test_theme' . DS . 'posts' . DS .'index.ctp';
+		$result = $ThemeView->getViewFileName('/posts/index');
+		$this->assertEqual($result, $expected);
+
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'themed' . DS . 'test_theme' . DS . 'layouts' . DS .'default.ctp';
+		$result = $ThemeView->getLayoutFileName();
+		$this->assertEqual($result, $expected);
+
+		$ThemeView->layoutPath = 'rss';
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'layouts' . DS . 'rss' . DS . 'default.ctp';
+		$result = $ThemeView->getLayoutFileName();
+		$this->assertEqual($result, $expected);
+
+		$ThemeView->layoutPath = 'email' . DS . 'html';
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'layouts' . DS . 'email' . DS . 'html' . DS . 'default.ctp';
+		$result = $ThemeView->getLayoutFileName();
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testMissingView method
+ *
+ * @access public
+ * @return void
+ */
+	function testMissingView() {
+		$this->Controller->plugin = null;
+		$this->Controller->name = 'Pages';
+		$this->Controller->viewPath = 'pages';
+		$this->Controller->action = 'display';
+		$this->Controller->theme = 'my_theme';
+
+		$this->Controller->params['pass'] = array('home');
+
+		restore_error_handler();
+		$View =& new TestThemeView($this->Controller);
+		ob_start();
+		$result = $View->getViewFileName('does_not_exist');
+		$expected = str_replace(array("\t", "\r\n", "\n"), "", ob_get_clean());
+		set_error_handler('simpleTestErrorHandler');
+		$this->assertPattern("/PagesController::/", $expected);
+		$this->assertPattern("/views(\/|\\\)themed(\/|\\\)my_theme(\/|\\\)pages(\/|\\\)does_not_exist.ctp/", $expected);
+	}
+
+/**
+ * testMissingLayout method
+ *
+ * @access public
+ * @return void
+ */
+	function testMissingLayout() {
+		$this->Controller->plugin = null;
+		$this->Controller->name = 'Posts';
+		$this->Controller->viewPath = 'posts';
+		$this->Controller->layout = 'whatever';
+		$this->Controller->theme = 'my_theme';
+
+		restore_error_handler();
+		$View =& new TestThemeView($this->Controller);
+		ob_start();
+		$result = $View->getLayoutFileName();
+		$expected = str_replace(array("\t", "\r\n", "\n"), "", ob_get_clean());
+		set_error_handler('simpleTestErrorHandler');
+		$this->assertPattern("/Missing Layout/", $expected);
+		$this->assertPattern("/views(\/|\\\)themed(\/|\\\)my_theme(\/|\\\)layouts(\/|\\\)whatever.ctp/", $expected);
+	}
+
+/**
+ * test memory leaks that existed in _paths at one point.
+ *
+ * @return void
+ */
+	function testMemoryLeakInPaths() {
+		if ($this->skipIf(!function_exists('memory_get_usage'), 'No memory measurement function, cannot test for possible memory leak. %s')) {
+			return;
+		}
+		$this->Controller->plugin = null;
+		$this->Controller->name = 'Posts';
+		$this->Controller->viewPath = 'posts';
+		$this->Controller->layout = 'whatever';
+		$this->Controller->theme = 'test_theme';
+
+		$View =& new ThemeView($this->Controller);
+		$View->element('test_element');
+
+		$start = memory_get_usage();
+		for ($i = 0; $i < 10; $i++) {
+			$View->element('test_element');
+		}
+		$end = memory_get_usage();
+		$this->assertWithinMargin($start, $end, 3500);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/view/view.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/view/view.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/view/view.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,1026 @@
+<?php
+/**
+ * ViewTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', array('View', 'Controller'));
+App::import('Helper', 'Cache');
+
+Mock::generate('Helper', 'CallbackMockHelper');
+Mock::generate('CacheHelper', 'ViewTestMockCacheHelper');
+
+if (!class_exists('ErrorHandler')) {
+	App::import('Core', array('Error'));
+}
+
+/**
+ * ViewPostsController class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view
+ */
+class ViewPostsController extends Controller {
+
+/**
+ * name property
+ *
+ * @var string 'Posts'
+ * @access public
+ */
+	var $name = 'Posts';
+
+/**
+ * uses property
+ *
+ * @var mixed null
+ * @access public
+ */
+	var $uses = null;
+
+/**
+ * index method
+ *
+ * @access public
+ * @return void
+ */
+	function index() {
+		$this->set('testData', 'Some test data');
+		$test2 = 'more data';
+		$test3 = 'even more data';
+		$this->set(compact('test2', 'test3'));
+	}
+
+/**
+ * nocache_tags_with_element method
+ *
+ * @access public
+ * @return void
+ */
+	function nocache_multiple_element() {
+		$this->set('foo', 'this is foo var');
+		$this->set('bar', 'this is bar var');
+	}
+}
+
+/**
+ * ViewTestErrorHandler class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view
+ */
+class ViewTestErrorHandler extends ErrorHandler {
+
+/**
+ * stop method
+ *
+ * @access public
+ * @return void
+ */
+	function _stop() {
+		return;
+	}
+}
+
+/**
+ * TestView class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view
+ */
+class TestView extends View {
+
+/**
+ * getViewFileName method
+ *
+ * @param mixed $name
+ * @access public
+ * @return void
+ */
+	function getViewFileName($name = null) {
+		return $this->_getViewFileName($name);
+	}
+
+/**
+ * getLayoutFileName method
+ *
+ * @param mixed $name
+ * @access public
+ * @return void
+ */
+	function getLayoutFileName($name = null) {
+		return $this->_getLayoutFileName($name);
+	}
+
+/**
+ * loadHelpers method
+ *
+ * @param mixed $loaded
+ * @param mixed $helpers
+ * @param mixed $parent
+ * @access public
+ * @return void
+ */
+	function loadHelpers(&$loaded, $helpers, $parent = null) {
+		return $this->_loadHelpers($loaded, $helpers, $parent);
+	}
+
+/**
+ * paths method
+ *
+ * @param string $plugin
+ * @param boolean $cached
+ * @access public
+ * @return void
+ */
+	function paths($plugin = null, $cached = true) {
+		return $this->_paths($plugin, $cached);
+	}
+
+/**
+ * cakeError method
+ *
+ * @param mixed $method
+ * @param mixed $messages
+ * @access public
+ * @return void
+ */
+	function cakeError($method, $messages) {
+		$error =& new ViewTestErrorHandler($method, $messages);
+		return $error;
+	}
+}
+
+/**
+ * TestAfterHelper class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs.view
+ */
+class TestAfterHelper extends Helper {
+
+/**
+ * property property
+ *
+ * @var string ''
+ * @access public
+ */
+	var $property = '';
+
+/**
+ * beforeLayout method
+ *
+ * @access public
+ * @return void
+ */
+	function beforeLayout() {
+		$this->property = 'Valuation';
+	}
+
+/**
+ * afterLayout method
+ *
+ * @access public
+ * @return void
+ */
+	function afterLayout() {
+		$View =& ClassRegistry::getObject('afterView');
+		$View->output .= 'modified in the afterlife';
+	}
+}
+
+
+/**
+ * ViewTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class ViewTest extends CakeTestCase {
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		Router::reload();
+		$this->Controller = new Controller();
+		$this->PostsController = new ViewPostsController();
+		$this->PostsController->viewPath = 'posts';
+		$this->PostsController->index();
+		$this->View = new View($this->PostsController);
+	}
+
+/**
+ * tearDown method
+ *
+ * @access public
+ * @return void
+ */
+	function tearDown() {
+		unset($this->View);
+		unset($this->PostsController);
+		unset($this->Controller);
+	}
+
+/**
+ * endTest
+ *
+ * @access public
+ * @return void
+ */
+	function startTest() {
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS),
+			'views' => array(
+				TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS,
+				TEST_CAKE_CORE_INCLUDE_PATH . 'libs' . DS . 'view' . DS
+			)
+		), true);
+	}
+
+/**
+ * endTest
+ *
+ * @access public
+ * @return void
+ */
+	function endTest() {
+		App::build();
+	}
+
+/**
+ * testPluginGetTemplate method
+ *
+ * @access public
+ * @return void
+ */
+	function testPluginGetTemplate() {
+		$this->Controller->plugin = 'test_plugin';
+		$this->Controller->name = 'TestPlugin';
+		$this->Controller->viewPath = 'tests';
+		$this->Controller->action = 'index';
+
+		$View = new TestView($this->Controller);
+
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS .'test_plugin' . DS . 'views' . DS .'tests' . DS .'index.ctp';
+		$result = $View->getViewFileName('index');
+		$this->assertEqual($result, $expected);
+
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS .'test_plugin' . DS . 'views' . DS . 'layouts' . DS .'default.ctp';
+		$result = $View->getLayoutFileName();
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that plugin/$plugin_name is only appended to the paths it should be.
+ *
+ * @return void
+ */
+	function testPluginPathGeneration() {
+		$this->Controller->plugin = 'test_plugin';
+		$this->Controller->name = 'TestPlugin';
+		$this->Controller->viewPath = 'tests';
+		$this->Controller->action = 'index';
+
+		$View = new TestView($this->Controller);
+		$paths = $View->paths();
+		$this->assertEqual($paths, App::path('views'));
+
+		$paths = $View->paths('test_plugin');
+
+		$expected = array(
+			TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'plugins' . DS . 'test_plugin' . DS,
+			TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS . 'test_plugin' . DS . 'views' . DS,
+			TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS,
+			TEST_CAKE_CORE_INCLUDE_PATH . 'libs' . DS . 'view' . DS
+		);
+		$this->assertEqual($paths, $expected);
+	}
+
+/**
+ * test that CamelCase plugins still find their view files.
+ *
+ * @return void
+ */
+	function testCamelCasePluginGetTemplate() {
+		$this->Controller->plugin = 'TestPlugin';
+		$this->Controller->name = 'TestPlugin';
+		$this->Controller->viewPath = 'tests';
+		$this->Controller->action = 'index';
+
+		$View = new TestView($this->Controller);
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS),
+			'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS)
+		));
+
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS .'test_plugin' . DS . 'views' . DS .'tests' . DS .'index.ctp';
+		$result = $View->getViewFileName('index');
+		$this->assertEqual($result, $expected);
+
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS .'test_plugin' . DS . 'views' . DS . 'layouts' . DS .'default.ctp';
+		$result = $View->getLayoutFileName();
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testGetTemplate method
+ *
+ * @access public
+ * @return void
+ */
+	function testGetTemplate() {
+		$this->Controller->plugin = null;
+		$this->Controller->name = 'Pages';
+		$this->Controller->viewPath = 'pages';
+		$this->Controller->action = 'display';
+		$this->Controller->params['pass'] = array('home');
+
+		$View = new TestView($this->Controller);
+
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS .'pages' . DS .'home.ctp';
+		$result = $View->getViewFileName('home');
+		$this->assertEqual($result, $expected);
+
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS .'posts' . DS .'index.ctp';
+		$result = $View->getViewFileName('/posts/index');
+		$this->assertEqual($result, $expected);
+
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS .'posts' . DS .'index.ctp';
+		$result = $View->getViewFileName('../posts/index');
+		$this->assertEqual($result, $expected);
+
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'layouts' . DS .'default.ctp';
+		$result = $View->getLayoutFileName();
+		$this->assertEqual($result, $expected);
+
+		$View->layoutPath = 'rss';
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'layouts' . DS . 'rss' . DS . 'default.ctp';
+		$result = $View->getLayoutFileName();
+		$this->assertEqual($result, $expected);
+
+		$View->layoutPath = 'email' . DS . 'html';
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'layouts' . DS . 'email' . DS . 'html' . DS . 'default.ctp';
+		$result = $View->getLayoutFileName();
+
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testMissingView method
+ *
+ * @access public
+ * @return void
+ */
+	function testMissingView() {
+		$this->Controller->plugin = null;
+		$this->Controller->name = 'Pages';
+		$this->Controller->viewPath = 'pages';
+		$this->Controller->action = 'display';
+		$this->Controller->params['pass'] = array('home');
+
+		$View = new TestView($this->Controller);
+		ob_start();
+		$result = $View->getViewFileName('does_not_exist');
+		$expected = str_replace(array("\t", "\r\n", "\n"), "", ob_get_clean());
+
+		$this->assertPattern("/PagesController::/", $expected);
+		$this->assertPattern("/pages(\/|\\\)does_not_exist.ctp/", $expected);
+	}
+
+/**
+ * testMissingLayout method
+ *
+ * @access public
+ * @return void
+ */
+	function testMissingLayout() {
+		$this->Controller->plugin = null;
+		$this->Controller->name = 'Posts';
+		$this->Controller->viewPath = 'posts';
+		$this->Controller->layout = 'whatever';
+
+		$View = new TestView($this->Controller);
+		ob_start();
+		$result = $View->getLayoutFileName();
+		$expected = str_replace(array("\t", "\r\n", "\n"), "", ob_get_clean());
+
+		$this->assertPattern("/Missing Layout/", $expected);
+		$this->assertPattern("/layouts(\/|\\\)whatever.ctp/", $expected);
+	}
+
+/**
+ * testViewVars method
+ *
+ * @access public
+ * @return void
+ */
+	function testViewVars() {
+		$this->assertEqual($this->View->viewVars, array('testData' => 'Some test data', 'test2' => 'more data', 'test3' => 'even more data'));
+	}
+
+/**
+ * testUUIDGeneration method
+ *
+ * @access public
+ * @return void
+ */
+	function testUUIDGeneration() {
+		$result = $this->View->uuid('form', array('controller' => 'posts', 'action' => 'index'));
+		$this->assertEqual($result, 'form5988016017');
+		$result = $this->View->uuid('form', array('controller' => 'posts', 'action' => 'index'));
+		$this->assertEqual($result, 'formc3dc6be854');
+		$result = $this->View->uuid('form', array('controller' => 'posts', 'action' => 'index'));
+		$this->assertEqual($result, 'form28f92cc87f');
+	}
+
+/**
+ * testAddInlineScripts method
+ *
+ * @access public
+ * @return void
+ */
+	function testAddInlineScripts() {
+		$this->View->addScript('prototype.js');
+		$this->View->addScript('prototype.js');
+		$this->assertEqual($this->View->__scripts, array('prototype.js'));
+
+		$this->View->addScript('mainEvent', 'Event.observe(window, "load", function() { doSomething(); }, true);');
+		$this->assertEqual($this->View->__scripts, array('prototype.js', 'mainEvent' => 'Event.observe(window, "load", function() { doSomething(); }, true);'));
+	}
+
+/**
+ * testElement method
+ *
+ * @access public
+ * @return void
+ */
+	function testElement() {
+		$result = $this->View->element('test_element');
+		$this->assertEqual($result, 'this is the test element');
+
+		$result = $this->View->element('plugin_element', array('plugin' => 'test_plugin'));
+		$this->assertEqual($result, 'this is the plugin element using params[plugin]');
+
+		$this->View->plugin = 'test_plugin';
+		$result = $this->View->element('test_plugin_element');
+		$this->assertEqual($result, 'this is the test set using View::$plugin plugin element');
+
+		$result = $this->View->element('non_existant_element');
+		$this->assertPattern('/Not Found:/', $result);
+		$this->assertPattern('/non_existant_element/', $result);
+	}
+
+/**
+ * Test that alternate extensions work with duplicated elements.
+ *
+ * @return void
+ */
+	function testElementExtensions() {
+		$this->View->ext = '.xml';
+		$result = $this->View->element('test_element');
+		$this->assertEqual(trim($result), '<p>test element</p>');
+	}
+
+/**
+ * test that additional element viewVars don't get overwritten with helpers.
+ *
+ * @return void
+ */
+	function testElementParamsDontOverwriteHelpers() {
+		$Controller = new ViewPostsController();
+		$Controller->helpers = array('Form');
+
+		$View = new View($Controller);
+		$result = $View->element('type_check', array('form' => 'string'), true);
+		$this->assertEqual('string', $result);
+
+		$View->set('form', 'string');
+		$result = $View->element('type_check', array(), true);
+		$this->assertEqual('string', $result);
+	}
+
+/**
+ * testElementCacheHelperNoCache method
+ *
+ * @access public
+ * @return void
+ */
+	function testElementCacheHelperNoCache() {
+		$Controller = new ViewPostsController();
+		$View = new View($Controller);
+		$empty = array();
+		$helpers = $View->_loadHelpers($empty, array('cache'));
+		$View->loaded = $helpers;
+		$result = $View->element('test_element', array('ram' => 'val', 'test' => array('foo', 'bar')));
+		$this->assertEqual($result, 'this is the test element');
+	}
+
+/**
+ * testElementCache method
+ *
+ * @access public
+ * @return void
+ */
+	function testElementCache() {
+		$writable = is_writable(CACHE . 'views' . DS);
+		if ($this->skipIf(!$writable, 'CACHE/views dir is not writable, cannot test elementCache. %s')) {
+			return;
+		}
+		$View = new TestView($this->PostsController);
+		$element = 'test_element';
+		$expected = 'this is the test element';
+		$result = $View->element($element);
+		$this->assertEqual($result, $expected);
+
+		$cached = false;
+		$result = $View->element($element, array('cache'=>'+1 second'));
+		if (file_exists(CACHE . 'views' . DS . 'element_cache_'.$element)) {
+			$cached = true;
+			unlink(CACHE . 'views' . DS . 'element_cache_'.$element);
+		}
+		$this->assertTrue($cached);
+
+		$cached = false;
+		$result = $View->element($element, array('cache' => true, 'param' => 'val'));
+		if (file_exists(CACHE . 'views' . DS . 'element_cache_param_'.$element)) {
+			$cached = true;
+			unlink(CACHE . 'views' . DS . 'element_cache_param_'.$element);
+		}
+		$this->assertTrue($cached);
+
+		$cached = false;
+		$result = $View->element($element, array('cache'=>'+1 second', 'other_param'=> true, 'anotherParam'=> true));
+		if (file_exists(CACHE . 'views' . DS . 'element_cache_other_param_anotherParam_'.$element)) {
+			$cached = true;
+			unlink(CACHE . 'views' . DS . 'element_cache_other_param_anotherParam_'.$element);
+		}
+		$this->assertTrue($cached);
+
+		$cached = false;
+		$result = $View->element($element, array('cache'=>array('time'=>'+1 second', 'key'=>'/whatever/here')));
+		if (file_exists(CACHE . 'views' . DS . 'element_'.Inflector::slug('/whatever/here').'_'.$element)) {
+			$cached = true;
+			unlink(CACHE . 'views' . DS . 'element_'.Inflector::slug('/whatever/here').'_'.$element);
+		}
+		$this->assertTrue($cached);
+
+		$cached = false;
+		$result = $View->element($element, array('cache'=>array('time'=>'+1 second', 'key'=>'whatever_here')));
+		if (file_exists(CACHE . 'views' . DS . 'element_whatever_here_'.$element)) {
+			$cached = true;
+			unlink(CACHE . 'views' . DS . 'element_whatever_here_'.$element);
+		}
+		$this->assertTrue($cached);
+		$this->assertEqual($result, $expected);
+
+	}
+
+/**
+ * test that ctp is used as a fallback file extension for elements
+ *
+ * @return void
+ */
+	function testElementCtpFallback() {
+		$View = new TestView($this->PostsController);
+		$View->ext = '.missing';
+		$element = 'test_element';
+		$expected = 'this is the test element';
+		$result = $View->element($element);
+
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * testLoadHelpers method
+ *
+ * @access public
+ * @return void
+ */
+	function testLoadHelpers() {
+		$View = new TestView($this->PostsController);
+
+		$loaded = array();
+		$result = $View->loadHelpers($loaded, array('Html', 'Form', 'Ajax'));
+		$this->assertTrue(is_object($result['Html']));
+		$this->assertTrue(is_object($result['Form']));
+		$this->assertTrue(is_object($result['Form']->Html));
+		$this->assertTrue(is_object($result['Ajax']->Html));
+
+		$View->plugin = 'test_plugin';
+		$result = $View->loadHelpers($loaded, array('TestPlugin.PluggedHelper'));
+		$this->assertTrue(is_object($result['PluggedHelper']));
+		$this->assertTrue(is_object($result['PluggedHelper']->OtherHelper));
+	}
+
+/**
+ * test the correct triggering of helper callbacks
+ *
+ * @return void
+ */
+	function testHelperCallbackTriggering() {
+		$this->PostsController->helpers = array('Session', 'Html', 'CallbackMock');
+		$View =& new TestView($this->PostsController);
+		$loaded = array();
+		$View->loaded = $View->loadHelpers($loaded, $this->PostsController->helpers);
+		$View->loaded['CallbackMock']->expectOnce('beforeRender');
+		$View->loaded['CallbackMock']->expectOnce('afterRender');
+		$View->loaded['CallbackMock']->expectOnce('beforeLayout');
+		$View->loaded['CallbackMock']->expectOnce('afterLayout');
+		$View->render('index');
+	}
+
+/**
+ * testBeforeLayout method
+ *
+ * @access public
+ * @return void
+ */
+	function testBeforeLayout() {
+		$this->PostsController->helpers = array('Session', 'TestAfter', 'Html');
+		$View =& new View($this->PostsController);
+		$out = $View->render('index');
+		$this->assertEqual($View->loaded['testAfter']->property, 'Valuation');
+	}
+
+/**
+ * testAfterLayout method
+ *
+ * @access public
+ * @return void
+ */
+	function testAfterLayout() {
+		$this->PostsController->helpers = array('Session', 'TestAfter', 'Html');
+		$this->PostsController->set('variable', 'values');
+
+		$View =& new View($this->PostsController);
+		ClassRegistry::addObject('afterView', $View);
+
+		$content = 'This is my view output';
+		$result = $View->renderLayout($content, 'default');
+		$this->assertPattern('/modified in the afterlife/', $result);
+		$this->assertPattern('/This is my view output/', $result);
+	}
+
+/**
+ * testRenderLoadHelper method
+ *
+ * @access public
+ * @return void
+ */
+	function testRenderLoadHelper() {
+		$this->PostsController->helpers = array('Session', 'Html', 'Form', 'Ajax');
+		$View =& new TestView($this->PostsController);
+
+		$result = $View->_render($View->getViewFileName('index'), array());
+		$this->assertEqual($result, 'posts index');
+
+		$helpers = $View->loaded;
+		$this->assertTrue(is_object($helpers['html']));
+		$this->assertTrue(is_object($helpers['form']));
+		$this->assertTrue(is_object($helpers['form']->Html));
+		$this->assertTrue(is_object($helpers['ajax']->Html));
+
+		$this->PostsController->helpers = array('Html', 'Form', 'Ajax', 'TestPlugin.PluggedHelper');
+		$View =& new TestView($this->PostsController);
+
+		$result = $View->_render($View->getViewFileName('index'), array());
+		$this->assertEqual($result, 'posts index');
+
+		$helpers = $View->loaded;
+		$this->assertTrue(is_object($helpers['html']));
+		$this->assertTrue(is_object($helpers['form']));
+		$this->assertTrue(is_object($helpers['form']->Html));
+		$this->assertTrue(is_object($helpers['ajax']->Html));
+		$this->assertTrue(is_object($helpers['pluggedHelper']->OtherHelper));
+
+		$this->assertTrue(is_object($View->Html));
+		$this->assertTrue(is_object($View->Form));
+		$this->assertTrue(is_object($View->Form->Html));
+		$this->assertTrue(is_object($View->PluggedHelper->OtherHelper));
+		$this->assertReference($View->Form, $View->loaded['form']);
+		$this->assertReference($View->Html, $View->loaded['html']);
+		$this->assertReference($View->PluggedHelper->OtherHelper, $View->loaded['otherHelper']);
+	}
+
+/**
+ * testRender method
+ *
+ * @access public
+ * @return void
+ */
+	function testRender() {
+		$View =& new TestView($this->PostsController);
+		$result = str_replace(array("\t", "\r\n", "\n"), "", $View->render('index'));
+
+		$this->assertPattern("/<meta http-equiv=\"Content-Type\" content=\"text\/html; charset=utf-8\" \/><title>/", $result);
+		$this->assertPattern("/<div id=\"content\">posts index<\/div>/", $result);
+		$this->assertPattern("/<div id=\"content\">posts index<\/div>/", $result);
+
+		$this->PostsController->set('url', 'flash');
+		$this->PostsController->set('message', 'yo what up');
+		$this->PostsController->set('pause', 3);
+		$this->PostsController->set('page_title', 'yo what up');
+
+		$View =& new TestView($this->PostsController);
+		$result = str_replace(array("\t", "\r\n", "\n"), "", $View->render(false, 'flash'));
+
+		$this->assertPattern("/<title>yo what up<\/title>/", $result);
+		$this->assertPattern("/<p><a href=\"flash\">yo what up<\/a><\/p>/", $result);
+
+		$this->assertTrue($View->render(false, 'flash'));
+
+		$this->PostsController->helpers = array('Session', 'Cache', 'Html');
+		$this->PostsController->constructClasses();
+		$this->PostsController->cacheAction = array('index' => 3600);
+		$this->PostsController->params['action'] = 'index';
+		Configure::write('Cache.check', true);
+
+		$View =& new TestView($this->PostsController);
+		$result = str_replace(array("\t", "\r\n", "\n"), "", $View->render('index'));
+
+		$this->assertPattern("/<meta http-equiv=\"Content-Type\" content=\"text\/html; charset=utf-8\" \/><title>/", $result);
+		$this->assertPattern("/<div id=\"content\">posts index<\/div>/", $result);
+		$this->assertPattern("/<div id=\"content\">posts index<\/div>/", $result);
+	}
+
+/**
+ * test rendering layout with cache helper loaded
+ *
+ * @return void
+ */
+	function testRenderLayoutWithMockCacheHelper() {
+		$_check = Configure::read('Cache.check');
+		Configure::write('Cache.check', true);
+
+		$Controller =& new ViewPostsController();
+		$Controller->cacheAction = '+1 day';
+		$View =& new View($Controller);
+		$View->loaded['cache'] = new ViewTestMockCacheHelper();
+		$View->loaded['cache']->expectCallCount('cache', 2);
+
+		$View->render('index');
+
+		Configure::write('Cache.check', $_check);
+	}
+
+/**
+ * test that view vars can replace the local helper variables
+ * and not overwrite the $this->Helper references
+ *
+ * @return void
+ */
+	function testViewVarOverwritingLocalHelperVar() {
+		$Controller =& new ViewPostsController();
+		$Controller->helpers = array('Session', 'Html');
+		$Controller->set('html', 'I am some test html');
+		$View =& new View($Controller);
+		$result = $View->render('helper_overwrite', false);
+
+		$this->assertPattern('/I am some test html/', $result);
+		$this->assertPattern('/Test link/', $result);
+	}
+
+/**
+ * testGetViewFileName method
+ *
+ * @access public
+ * @return void
+ */
+	function testViewFileName() {
+		$View =& new TestView($this->PostsController);
+
+		$result = $View->getViewFileName('index');
+		$this->assertPattern('/posts(\/|\\\)index.ctp/', $result);
+
+		$result = $View->getViewFileName('/pages/home');
+		$this->assertPattern('/pages(\/|\\\)home.ctp/', $result);
+
+		$result = $View->getViewFileName('../elements/test_element');
+		$this->assertPattern('/elements(\/|\\\)test_element.ctp/', $result);
+
+		$result = $View->getViewFileName('../themed/test_theme/posts/index');
+		$this->assertPattern('/themed(\/|\\\)test_theme(\/|\\\)posts(\/|\\\)index.ctp/', $result);
+
+		$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS .'posts' . DS .'index.ctp';
+		$result = $View->getViewFileName('../posts/index');
+		$this->assertEqual($result, $expected);
+
+	}
+
+/**
+ * testRenderCache method
+ *
+ * @access public
+ * @return void
+ */
+	function testRenderCache() {
+		$writable = is_writable(CACHE . 'views' . DS);
+		if ($this->skipIf(!$writable, 'CACHE/views dir is not writable, cannot test renderCache. %s')) {
+			return;
+		}
+		$view = 'test_view';
+		$View =& new View($this->PostsController);
+		$path = CACHE . 'views' . DS . 'view_cache_'.$view;
+
+		$cacheText = '<!--cachetime:'.time().'-->some cacheText';
+		$f = fopen($path, 'w+');
+		fwrite($f, $cacheText);
+		fclose($f);
+
+		$result = $View->renderCache($path, '+1 second');
+		$this->assertFalse($result);
+		@unlink($path);
+
+		$cacheText = '<!--cachetime:'.(time() + 10).'-->some cacheText';
+		$f = fopen($path, 'w+');
+		fwrite($f, $cacheText);
+		fclose($f);
+		ob_start();
+		$View->renderCache($path, '+1 second');
+		$result = ob_get_clean();
+
+		$expected = 'some cacheText';
+		$this->assertPattern('/^some cacheText/', $result);
+
+		@unlink($path);
+	}
+
+/**
+ * Test that render() will remove the cake:nocache tags when only the cachehelper is present.
+ *
+ * @return void
+ */
+	function testRenderStrippingNoCacheTagsOnlyCacheHelper() {
+		Configure::write('Cache.check', false);
+		$View =& new View($this->PostsController);
+		$View->set(array('superman' => 'clark', 'variable' => 'var'));
+		$View->helpers = array('Html', 'Form', 'Cache');
+		$View->layout = 'cache_layout';
+		$result = $View->render('index');
+		$this->assertNoPattern('/cake:nocache/', $result);
+	}
+
+/**
+ * Test that render() will remove the cake:nocache tags when only the Cache.check is true.
+ *
+ * @return void
+ */
+	function testRenderStrippingNoCacheTagsOnlyCacheCheck() {
+		Configure::write('Cache.check', true);
+		$View =& new View($this->PostsController);
+		$View->set(array('superman' => 'clark', 'variable' => 'var'));
+		$View->helpers = array('Html', 'Form');
+		$View->layout = 'cache_layout';
+		$result = $View->render('index');
+		$this->assertNoPattern('/cake:nocache/', $result);
+	}
+
+/**
+ * testRenderNocache method
+ *
+ * @access public
+ * @return void
+ */
+
+/* This is a new test case for a pending enhancement
+	function testRenderNocache() {
+		$this->PostsController->helpers = array('Cache', 'Html');
+		$this->PostsController->constructClasses();
+		$this->PostsController->cacheAction = 21600;
+		$this->PostsController->here = '/posts/nocache_multiple_element';
+		$this->PostsController->action = 'nocache_multiple_element';
+		$this->PostsController->nocache_multiple_element();
+		Configure::write('Cache.check', true);
+		Configure::write('Cache.disable', false);
+
+		$filename = CACHE . 'views' . DS . 'posts_nocache_multiple_element.php';
+
+		$View = new TestView($this->PostsController);
+		$View->render();
+
+		ob_start();
+		$View->renderCache($filename, getMicroTime());
+		$result = ob_get_clean();
+		@unlink($filename);
+
+		$this->assertPattern('/php echo \$foo;/', $result);
+		$this->assertPattern('/php echo \$bar;/', $result);
+		$this->assertPattern('/php \$barfoo = \'in sub2\';/', $result);
+		$this->assertPattern('/php echo \$barfoo;/', $result);
+		$this->assertPattern('/printing: "in sub2"/', $result);
+		$this->assertPattern('/php \$foobar = \'in sub1\';/', $result);
+		$this->assertPattern('/php echo \$foobar;/', $result);
+		$this->assertPattern('/printing: "in sub1"/', $result);
+	}
+*/
+
+/**
+ * testSet method
+ *
+ * @access public
+ * @return void
+ */
+	function testSet() {
+		$View =& new TestView($this->PostsController);
+		$View->viewVars = array();
+		$View->set('somekey', 'someValue');
+		$this->assertIdentical($View->viewVars, array('somekey' => 'someValue'));
+		$this->assertIdentical($View->getVars(), array('somekey'));
+
+		$View->viewVars = array();
+		$keys = array('key1', 'key2');
+		$values = array('value1', 'value2');
+		$View->set($keys, $values);
+		$this->assertIdentical($View->viewVars, array('key1' => 'value1', 'key2' => 'value2'));
+		$this->assertIdentical($View->getVars(), array('key1', 'key2'));
+		$this->assertIdentical($View->getVar('key1'), 'value1');
+		$this->assertNull($View->getVar('key3'));
+
+		$View->set(array('key3' => 'value3'));
+		$this->assertIdentical($View->getVar('key3'), 'value3');
+
+		$View->viewVars = array();
+		$View->set(array(3 => 'three', 4 => 'four'));
+		$View->set(array(1 => 'one', 2 => 'two'));
+		$expected = array(3 => 'three', 4 => 'four', 1 => 'one', 2 => 'two');
+		$this->assertEqual($View->viewVars, $expected);
+	}
+
+/**
+ * testEntityReference method
+ *
+ * @access public
+ * @return void
+ */
+	function testEntityReference() {
+		$View =& new TestView($this->PostsController);
+		$View->model = 'Post';
+		$View->field = 'title';
+		$this->assertEqual($View->entity(), array('Post', 'title'));
+
+		$View->association = 'Comment';
+		$View->field = 'user_id';
+		$this->assertEqual($View->entity(), array('Comment', 'user_id'));
+
+		$View->model = 0;
+		$View->association = null;
+		$View->field = 'Node';
+		$View->fieldSuffix = 'title';
+		$View->entityPath = '0.Node.title';
+		$expected = array(0, 'Node', 'title');
+		$this->assertEqual($View->entity(), $expected);
+
+		$View->model = 'HelperTestTag';
+		$View->field = 'HelperTestTag';
+		$View->modelId = null;
+		$View->association = null;
+		$View->fieldSuffix = null;
+		$View->entityPath = 'HelperTestTag';
+		$expected = array('HelperTestTag', 'HelperTestTag');
+		$this->assertEqual($View->entity(), $expected);
+	}
+
+/**
+ * testBadExt method
+ *
+ * @access public
+ * @return void
+ */
+	function testBadExt() {
+		$this->PostsController->action = 'something';
+		$this->PostsController->ext = '.whatever';
+		restore_error_handler();
+		ob_start();
+		$View = new TestView($this->PostsController);
+		$View->render('this_is_missing');
+		$result = str_replace(array("\t", "\r\n", "\n"), "", ob_get_clean());
+		set_error_handler('simpleTestErrorHandler');
+
+		$this->assertPattern("/<em>PostsController::<\/em><em>something\(\)<\/em>/", $result);
+		$this->assertPattern("/posts(\/|\\\)this_is_missing.whatever/", $result);
+
+		$this->PostsController->ext = ".bad";
+		$View =& new TestView($this->PostsController);
+		$result = str_replace(array("\t", "\r\n", "\n"), "", $View->render('index'));
+
+		$this->assertPattern("/<meta http-equiv=\"Content-Type\" content=\"text\/html; charset=utf-8\" \/><title>/", $result);
+		$this->assertPattern("/<div id=\"content\">posts index<\/div>/", $result);
+	}
+}

Added: trunk/src/Web/cake/tests/cases/libs/xml.test.php
===================================================================
--- trunk/src/Web/cake/tests/cases/libs/xml.test.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/cases/libs/xml.test.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,1640 @@
+<?php
+/**
+ * XmlTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.5432
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+App::import('Core', 'Xml');
+
+/**
+ * XmlTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class XmlTest extends CakeTestCase {
+
+/**
+ * setUp method
+ *
+ * @access public
+ * @return void
+ */
+	function setUp() {
+		$manager =& new XmlManager();
+		$manager->namespaces = array();
+	}
+
+/**
+ * testRootTagParsing method
+ *
+ * @access public
+ * @return void
+ */
+	function testRootTagParsing() {
+		$input = '<' . '?xml version="1.0" encoding="UTF-8" ?' . '>' . "\n" .
+			'<plugin id="1" version_id="1" name="my_plugin" title="My Plugin" author="Me" author_email="me****@cakep*****" description="My awesome package" created="2008-01-28 18:21:13" updated="2008-01-28 18:21:13">'
+			.'<current id="1" plugin_id="1" name="1.0" file="" created="2008-01-28 18:21:13" updated="2008-01-28 18:21:13" />'
+			.'<version id="1" plugin_id="1" name="1.0" file="" created="2008-01-28 18:21:13" updated="2008-01-28 18:21:13" />'
+			.'</plugin>';
+		$xml = new Xml($input);
+		$this->assertEqual($xml->children[0]->name, 'plugin');
+		$this->assertEqual($xml->children[0]->children[0]->name, 'current');
+		$this->assertEqual($xml->toString(true), $input);
+	}
+
+/**
+ * testSerialization method
+ *
+ * @access public
+ * @return void
+ */
+	function testSerialization() {
+		$input = array(
+			array(
+				'Project' => array('id' => 1, 'title' => null, 'client_id' => 1, 'show' => 1, 'is_spotlight' => null, 'style_id' => 0, 'job_type_id' => 1, 'industry_id' => 1, 'modified' => null, 'created' => null),
+				'Style' => array('id' => null, 'name' => null),
+				'JobType' => array('id' => 1, 'name' => 'Touch Screen Kiosk'),
+				'Industry' => array('id' => 1, 'name' => 'Financial')
+			),
+			array(
+				'Project' => array('id' => 2, 'title' => null, 'client_id' => 2, 'show' => 1, 'is_spotlight' => null, 'style_id' => 0, 'job_type_id' => 2, 'industry_id' => 2, 'modified' => '2007-11-26 14:48:36', 'created' => null),
+				'Style' => array('id' => null, 'name' => null),
+				'JobType' => array('id' => 2, 'name' => 'Awareness Campaign'),
+				'Industry' => array('id' => 2, 'name' => 'Education')
+			)
+		);
+
+		$xml = new Xml($input);
+		$result = preg_replace("/\n/",'', $xml->toString(false));
+		$expected = '<project id="1" title="" client_id="1" show="1" is_spotlight="" style_id="0" job_type_id="1" industry_id="1" modified="" created=""><style id="" name="" /><job_type id="1" name="Touch Screen Kiosk" /><industry id="1" name="Financial" /></project><project id="2" title="" client_id="2" show="1" is_spotlight="" style_id="0" job_type_id="2" industry_id="2" modified="2007-11-26 14:48:36" created=""><style id="" name="" /><job_type id="2" name="Awareness Campaign" /><industry id="2" name="Education" /></project>';
+		$this->assertEqual($result, $expected);
+
+		$input = array(
+			'Project' => array('id' => 1, 'title' => null, 'client_id' => 1, 'show' => 1, 'is_spotlight' => null, 'style_id' => 0, 'job_type_id' => 1, 'industry_id' => 1, 'modified' => null, 'created' => null),
+			'Style' => array('id' => null, 'name' => null),
+			'JobType' => array('id' => 1, 'name' => 'Touch Screen Kiosk'),
+			'Industry' => array('id' => 1, 'name' => 'Financial')
+		);
+		$expected = '<project id="1" title="" client_id="1" show="1" is_spotlight="" style_id="0" job_type_id="1" industry_id="1" modified="" created=""><style id="" name="" /><job_type id="1" name="Touch Screen Kiosk" /><industry id="1" name="Financial" /></project>';
+		$xml = new Xml($input);
+		$result = preg_replace("/\n/",'', $xml->toString(false));
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * testSerializeOnMultiDimensionalArray method
+ *
+ * @access public
+ * @return void
+ */
+	function testSerializeOnMultiDimensionalArray() {
+		$data = array(
+			'Statuses' => array(
+				array('Status' => array('id' => 1)),
+				array('Status' => array('id' => 2))
+			)
+		);
+		$result =& new Xml($data, array('format' => 'tags'));
+		$expected = '<statuses><status><id>1</id></status><status><id>2</id></status></statuses>';
+		$this->assertIdentical($result->toString(), $expected);
+	}
+
+/**
+ * testSerializeCapsWithoutSlug method
+ *
+ * @access public
+ * @return void
+ */
+	function testSerializeCapsWithoutSlug() {
+		$data = array(
+			'USERS' => array(
+				array('USER' => array('ID' => 1)),
+				array('USER' => array('ID' => 2))
+			)
+		);
+		$result =& new Xml($data, array('format' => 'tags', 'slug' => false));
+		$expected = '<USERS><USER><ID>1</ID></USER><USER><ID>2</ID></USER></USERS>';
+		$this->assertIdentical($result->toString(), $expected);
+	}
+
+/**
+ * test serialization of boolean and null values.  false = 0, true = 1, null = ''
+ *
+ * @return void
+ */
+	function testSerializationOfBooleanAndBooleanishValues() {
+		$xml =& new Xml(array('data' => array('example' => false)));
+		$result = $xml->toString(false);
+		$expected = '<data example="0" />';
+		$this->assertEqual($result, $expected, 'Boolean values incorrectly handled. %s');
+
+		$xml =& new Xml(array('data' => array('example' => true)));
+		$result = $xml->toString(false);
+		$expected = '<data example="1" />';
+		$this->assertEqual($result, $expected, 'Boolean values incorrectly handled. %s');
+
+		$xml =& new Xml(array('data' => array('example' => null)));
+		$result = $xml->toString(false);
+		$expected = '<data example="" />';
+		$this->assertEqual($result, $expected, 'Boolean values incorrectly handled. %s');
+
+		$xml =& new Xml(array('data' => array('example' => 0)));
+		$result = $xml->toString(false);
+		$expected = '<data example="0" />';
+		$this->assertEqual($result, $expected, 'Boolean-ish values incorrectly handled. %s');
+
+		$xml =& new Xml(array('data' => array('example' => 1)));
+		$result = $xml->toString(false);
+		$expected = '<data example="1" />';
+		$this->assertEqual($result, $expected, 'Boolean-ish values incorrectly handled. %s');
+	}
+
+/**
+ * testSimpleArray method
+ *
+ * @access public
+ * @return void
+ */
+	function testSimpleArray() {
+		$xml = new Xml(array('hello' => 'world'), array('format' => 'tags'));
+
+		$result = $xml->toString(false);
+		$expected = '<hello><![CDATA[world]]></hello>';
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * testSimpleObject method
+ *
+ * @access public
+ * @return void
+ */
+	function testSimpleObject() {
+		$input = new StdClass();
+		$input->hello = 'world';
+		$xml = new Xml($input, array('format' => 'tags'));
+
+		$result = $xml->toString(false);
+		$expected = '<hello><![CDATA[world]]></hello>';
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * testSimpleArrayWithZeroValues method
+ *
+ * @access public
+ * @return void
+ */
+	function testSimpleArrayWithZeroValues() {
+		$xml = new Xml(array('zero_string' => '0', 'zero_integer' => 0), array('format' => 'tags'));
+
+		$result = $xml->toString(false);
+		$expected = '<zero_string>0</zero_string><zero_integer>0</zero_integer>';
+		$this->assertEqual($expected, $result);
+
+		$data = array(
+			'Client' => array(
+				'id' => 3,
+				'object_id' => 9,
+				'key' => 'alt',
+				'name' => 'Client Two',
+				'created_by' => 4,
+				'status' => '0',
+				'num_projects' => 0
+			)
+		);
+		$xml = new Xml($data, array('format' => 'tags'));
+		$result = $xml->toString(array('format' => 'tags', 'header' => false));
+		$this->assertPattern('/<status>0<\/status>/', $result);
+		$this->assertPattern('/<num_projects>0<\/num_projects>/', $result);
+	}
+/**
+ * testHeader method
+ *
+ * @access public
+ * @return void
+ */
+	function testHeader() {
+		$input = new stdClass();
+		$input->hello = 'world';
+		$xml = new Xml($input, array('format' => 'tags'));
+
+		$result = $xml->toString(array('header' => true));
+		$expected = '<'.'?xml version="1.0" encoding="UTF-8" ?'.'>'."\n".'<hello><![CDATA[world]]></hello>';
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * testOwnerAssignment method
+ *
+ * @access public
+ * @return void
+ */
+	function testOwnerAssignment() {
+		$xml = new Xml();
+		$node =& $xml->createElement('hello', 'world');
+		$owner =& $node->document();
+		$this->assertTrue($xml === $owner);
+
+		$children =& $node->children;
+		$childOwner =& $children[0]->document();
+		$this->assertTrue($xml === $childOwner);
+	}
+
+/**
+ * testArraySingleSerialization method
+ *
+ * @access public
+ * @return void
+ */
+	function testArraySingleSerialization() {
+		$input = array(
+			'Post' => array(
+				'id' => '1', 'author_id' => '1', 'title' => 'First Post',
+				'body' => 'First Post Body', 'published' => 'Y',
+				'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'
+			),
+			'Author' => array(
+				'id' => '1', 'user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
+				'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31', 'test' => 'working'
+			)
+		);
+
+		$expected = '<post><id>1</id><author_id>1</author_id><title><![CDATA[First Post]]>';
+		$expected .= '</title><body><![CDATA[First Post Body]]></body><published><![CDATA[Y]]>';
+		$expected .= '</published><created><![CDATA[2007-03-18 10:39:23]]></created><updated>';
+		$expected .= '<![CDATA[2007-03-18 10:41:31]]></updated><author><id>1</id><user>';
+		$expected .= '<![CDATA[mariano]]></user><password><![CDATA[5f4dcc3b5aa765d61d8327deb882';
+		$expected .= 'cf99]]></password><created><![CDATA[2007-03-17 01:16:23]]></created>';
+		$expected .= '<updated><![CDATA[2007-03-17 01:18:31]]></updated><test><![CDATA[working]]>';
+		$expected .= '</test></author></post>';
+
+		$xml = new Xml($input, array('format' => 'tags'));
+		$result = $xml->toString(false);
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * testArraySerialization method
+ *
+ * @access public
+ * @return void
+ */
+	function testSerializationArray() {
+		$input = array(
+			array(
+				'Project' => array('id' => 1, 'title' => null, 'client_id' => 1, 'show' => 1, 'is_spotlight' => null, 'style_id' => 0, 'job_type_id' => 1, 'industry_id' => 1, 'modified' => null, 'created' => null),
+				'Style' => array('id' => null, 'name' => null),
+				'JobType' => array('id' => 1, 'name' => 'Touch Screen Kiosk'),
+				'Industry' => array('id' => 1, 'name' => 'Financial')
+			),
+			array(
+				'Project' => array('id' => 2, 'title' => null, 'client_id' => 2, 'show' => 1, 'is_spotlight' => null, 'style_id' => 0, 'job_type_id' => 2, 'industry_id' => 2, 'modified' => '2007-11-26 14:48:36', 'created' => null),
+				'Style' => array('id' => null, 'name' => null),
+				'JobType' => array('id' => 2, 'name' => 'Awareness Campaign'),
+				'Industry' => array('id' => 2, 'name' => 'Education'),
+			)
+		);
+		$expected = '<project><id>1</id><title /><client_id>1</client_id><show>1</show><is_spotlight /><style_id>0</style_id><job_type_id>1</job_type_id><industry_id>1</industry_id><modified /><created /><style><id /><name /></style><job_type><id>1</id><name>Touch Screen Kiosk</name></job_type><industry><id>1</id><name>Financial</name></industry></project><project><id>2</id><title /><client_id>2</client_id><show>1</show><is_spotlight /><style_id>0</style_id><job_type_id>2</job_type_id><industry_id>2</industry_id><modified>2007-11-26 14:48:36</modified><created /><style><id /><name /></style><job_type><id>2</id><name>Awareness Campaign</name></job_type><industry><id>2</id><name>Education</name></industry></project>';
+
+		$xml = new Xml($input, array('format' => 'tags'));
+		$result = $xml->toString(array('header' => false, 'cdata' => false));
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * testNestedArraySerialization method
+ *
+ * @access public
+ * @return void
+ */
+	function testSerializationNestedArray() {
+		$input = array(
+			array(
+				'Project' => array('id' => 1, 'title' => null, 'client_id' => 1, 'show' => 1, 'is_spotlight' => null, 'style_id' => 0, 'job_type_id' => 1, 'industry_id' => 1, 'modified' => null, 'created' => null),
+				'Style' => array('id' => null, 'name' => null),
+				'JobType' => array('id' => 1, 'name' => 'Touch Screen Kiosk'),
+				'Industry' => array('id' => 1, 'name' => 'Financial'),
+				'BusinessSolution' => array(array('id' => 6, 'name' => 'Convert Sales')),
+				'MediaType' => array(
+					array('id' => 15, 'name' => 'Print'),
+					array('id' => 7, 'name' => 'Web Demo'),
+					array('id' => 6, 'name' => 'CD-ROM')
+				)
+			),
+			array(
+				'Project' => array('id' => 2, 'title' => null, 'client_id' => 2, 'show' => 1, 'is_spotlight' => null, 'style_id' => 0, 'job_type_id' => 2, 'industry_id' => 2, 'modified' => '2007-11-26 14:48:36', 'created' => null),
+				'Style' => array('id' => null, 'name' => null),
+				'JobType' => array('id' => 2, 'name' => 'Awareness Campaign'),
+				'Industry' => array('id' => 2, 'name' => 'Education'),
+				'BusinessSolution' => array(
+					array('id' => 4, 'name' => 'Build Relationship'),
+					array('id' => 6, 'name' => 'Convert Sales')
+				),
+				'MediaType' => array(
+					array('id' => 17, 'name' => 'Web'),
+					array('id' => 6, 'name' => 'CD-ROM')
+				)
+			)
+		);
+		$expected = '<project><id>1</id><title /><client_id>1</client_id><show>1</show><is_spotlight /><style_id>0</style_id><job_type_id>1</job_type_id><industry_id>1</industry_id><modified /><created /><style><id /><name /></style><job_type><id>1</id><name>Touch Screen Kiosk</name></job_type><industry><id>1</id><name>Financial</name></industry><business_solution><id>6</id><name>Convert Sales</name></business_solution><media_type><id>15</id><name>Print</name></media_type><media_type><id>7</id><name>Web Demo</name></media_type><media_type><id>6</id><name>CD-ROM</name></media_type></project><project><id>2</id><title /><client_id>2</client_id><show>1</show><is_spotlight /><style_id>0</style_id><job_type_id>2</job_type_id><industry_id>2</industry_id><modified>2007-11-26 14:48:36</modified><created /><style><id /><name /></style><job_type><id>2</id><name>Awareness Campaign</name></job_type><industry><id>2</id><name>Education</name></industry><business_solution><id>4</id><name>Build Re
 lationship</name></business_solution><business_solution><id>6</id><name>Convert Sales</name></business_solution><media_type><id>17</id><name>Web</name></media_type><media_type><id>6</id><name>CD-ROM</name></media_type></project>';
+
+		$xml = new Xml($input, array('format' => 'tags'));
+		$result = $xml->toString(array('header' => false, 'cdata' => false));
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * Prove that serialization with a given root node works
+ * as expected.
+ *
+ * @access public
+ * @return void
+ * @link   https://trac.cakephp.org/ticket/6294
+ */
+	function testArraySerializationWithRoot() {
+		$input = array(
+			array('Shirt' => array('id' => 1, 'color' => 'green')),
+			array('Shirt' => array('id' => 2, 'color' => 'blue')),
+		);
+		$expected = '<collection><shirt id="1" color="green" />';
+		$expected .= '<shirt id="2" color="blue" /></collection>';
+
+		$Xml = new Xml($input, array('root' => 'collection'));
+		$result = $Xml->toString(array('header' => false));
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * testCloneNode
+ *
+ * @access public
+ * @return void
+ */
+	function testCloneNode() {
+		$node =& new XmlNode('element', 'myValue');
+		$twin =& $node->cloneNode();
+		$this->assertEqual($node, $twin);
+	}
+
+/**
+ * testNextSibling
+ *
+ * @access public
+ * @return void
+ */
+	function testNextSibling() {
+		$input = array(
+			array(
+				'Project' => array('id' => 1, 'title' => null, 'client_id' => 1, 'show' => '1', 'is_spotlight' => null, 'style_id' => 0, 'job_type_id' => '1.89', 'industry_id' => '1.56', 'modified' => null, 'created' => null),
+				'Style' => array('id' => null, 'name' => null),
+				'JobType' => array('id' => 1, 'name' => 'Touch Screen Kiosk'),
+				'Industry' => array('id' => 1, 'name' => 'Financial')
+			),
+			array(
+				'Project' => array('id' => 2, 'title' => null, 'client_id' => 2, 'show' => '1', 'is_spotlight' => null, 'style_id' => 0, 'job_type_id' => '2.2', 'industry_id' => 2.2, 'modified' => '2007-11-26 14:48:36', 'created' => null),
+				'Style' => array('id' => null, 'name' => null),
+				'JobType' => array('id' => 2, 'name' => 'Awareness Campaign'),
+				'Industry' => array('id' => 2, 'name' => 'Education'),
+			)
+		);
+		$xml =& new Xml($input, array('format' => 'tags'));
+		$node =& $xml->children[0]->children[0];
+
+		$nextSibling =& $node->nextSibling();
+		$this->assertEqual($nextSibling, $xml->children[0]->children[1]);
+
+		$nextSibling2 =& $nextSibling->nextSibling();
+		$this->assertEqual($nextSibling2, $xml->children[0]->children[2]);
+
+		$noFriends =& $xml->children[0]->children[12];
+		$this->assertNull($noFriends->nextSibling());
+	}
+
+/**
+ * testPreviousSibling
+ *
+ * @access public
+ * @return void
+ */
+	function testPreviousSibling() {
+		$input = array(
+			array(
+				'Project' => array('id' => 1, 'title' => null, 'client_id' => 1, 'show' => '1', 'is_spotlight' => null, 'style_id' => 0, 'job_type_id' => '1.89', 'industry_id' => '1.56', 'modified' => null, 'created' => null),
+				'Style' => array('id' => null, 'name' => null),
+				'JobType' => array('id' => 1, 'name' => 'Touch Screen Kiosk'),
+				'Industry' => array('id' => 1, 'name' => 'Financial')
+			),
+			array(
+				'Project' => array('id' => 2, 'title' => null, 'client_id' => 2, 'show' => '1', 'is_spotlight' => null, 'style_id' => 0, 'job_type_id' => '2.2', 'industry_id' => 2.2, 'modified' => '2007-11-26 14:48:36', 'created' => null),
+				'Style' => array('id' => null, 'name' => null),
+				'JobType' => array('id' => 2, 'name' => 'Awareness Campaign'),
+				'Industry' => array('id' => 2, 'name' => 'Education'),
+			)
+		);
+		$xml =& new Xml($input, array('format' => 'tags'));
+		$node =& $xml->children[0]->children[1];
+
+		$prevSibling =& $node->previousSibling();
+		$this->assertEqual($prevSibling, $xml->children[0]->children[0]);
+
+		$this->assertNull($prevSibling->previousSibling());
+	}
+
+/**
+ * testAddAndRemoveAttributes
+ *
+ * @access public
+ * @return void
+ */
+	function testAddAndRemoveAttributes() {
+		$node =& new XmlElement('myElement', 'superValue');
+		$this->assertTrue(empty($node->attributes));
+
+		$attrs = array(
+			'id' => 'test',
+			'show' => 1,
+			'is_spotlight' => 1,
+		);
+		$node->addAttribute($attrs);
+		$this->assertEqual($node->attributes, $attrs);
+
+		$node =& new XmlElement('myElement', 'superValue');
+		$node->addAttribute('test', 'value');
+		$this->assertTrue(isset($node->attributes['test']));
+
+		$node =& new XmlElement('myElement', 'superValue');
+		$obj =& new StdClass();
+		$obj->class = 'info';
+		$obj->id = 'primaryInfoBox';
+		$node->addAttribute($obj);
+		$expected = array(
+			'class' => 'info',
+			'id' => 'primaryInfoBox',
+		);
+		$this->assertEqual($node->attributes, $expected);
+
+		$result = $node->removeAttribute('class');
+		$this->assertTrue($result);
+		$this->assertFalse(isset($node->attributes['class']));
+
+		$result = $node->removeAttribute('missing');
+		$this->assertFalse($result);
+	}
+
+	/**
+	 * Tests that XML documents with non-standard spacing (i.e. leading whitespace, whole document
+	 * on one line) still parse properly.
+	 *
+	 * @return void
+	 */
+	function testParsingWithNonStandardWhitespace() {
+		$raw = '<?xml version="1.0" encoding="ISO-8859-1" ?><prices><price>1.0</price></prices>';
+		$array = array('Prices' => array('price' => 1.0));
+
+		$xml = new Xml($raw);
+		$this->assertEqual($xml->toArray(), $array);
+		$this->assertEqual($xml->__header, 'xml version="1.0" encoding="ISO-8859-1"');
+
+		$xml = new Xml(' ' . $raw);
+		$this->assertEqual($xml->toArray(), $array);
+		$this->assertEqual($xml->__header, 'xml version="1.0" encoding="ISO-8859-1"');
+
+		$xml = new Xml("\n" . $raw);
+		$this->assertEqual($xml->toArray(), $array);
+		$this->assertEqual($xml->__header, 'xml version="1.0" encoding="ISO-8859-1"');
+	}
+
+	/* Not implemented yet */
+	/* function testChildFilter() {
+	 	$input = array(
+	 		array(
+	 			'Project' => array('id' => 1, 'title' => null, 'client_id' => 1, 'show' => 1, 'is_spotlight' => null, 'style_id' => 0, 'job_type_id' => 1, 'industry_id' => 1, 'modified' => null, 'created' => null),
+	 			'Style' => array('id' => null, 'name' => null),
+	 			'JobType' => array('id' => 1, 'name' => 'Touch Screen Kiosk'),
+	 			'Industry' => array('id' => 1, 'name' => 'Financial'),
+	 			'BusinessSolution' => array(array('id' => 6, 'name' => 'Convert Sales')),
+	 			'MediaType' => array(
+	 				array('id' => 15, 'name' => 'Print'),
+	 				array('id' => 7, 'name' => 'Web Demo'),
+	 				array('id' => 6, 'name' => 'CD-ROM')
+	 			)
+	 		),
+	 		array(
+	 			'Project' => array('id' => 2, 'title' => null, 'client_id' => 2, 'show' => 1, 'is_spotlight' => null, 'style_id' => 0, 'job_type_id' => 2, 'industry_id' => 2, 'modified' => '2007-11-26 14:48:36', 'created' => null),
+	 			'Style' => array('id' => null, 'name' => null),
+	 			'JobType' => array('id' => 2, 'name' => 'Awareness Campaign'),
+	 			'Industry' => array('id' => 2, 'name' => 'Education'),
+	 			'BusinessSolution' => array(
+	 				array('id' => 4, 'name' => 'Build Relationship'),
+	 				array('id' => 6, 'name' => 'Convert Sales')
+	 			),
+	 			'MediaType' => array(
+	 				array('id' => 17, 'name' => 'Web'),
+	 				array('id' => 6, 'name' => 'CD-ROM')
+	 			)
+	 		)
+	 	);
+
+	 	$xml = new Xml($input, array('format' => 'tags', 'tags' => array(
+	 		'MediaType'	=> array('value' => 'id', 'children' => false),
+	 		'JobType'	=> array('children' => array()),
+	 		'Industry'	=> array('children' => array('name')),
+	 		'show'		=> false
+	 	)));
+
+	 	$result = $xml->toString(array('header' => false, 'cdata' => false));
+	 	$expected = '<project><id>1</id><title /><client_id>1</client_id><is_spotlight /><style_id>0</style_id><job_type_id>1</job_type_id><industry_id>1</industry_id><modified /><created /><style><id /><name /></style><job_type><id>1</id><name>Touch Screen Kiosk</name></job_type><industry><name>Financial</name></industry><business_solution><id>6</id><name>Convert Sales</name></business_solution><media_type>15</media_type><media_type>7</media_type><media_type>6</media_type></project><project><id>2</id><title /><client_id>2</client_id><is_spotlight /><style_id>0</style_id><job_type_id>2</job_type_id><industry_id>2</industry_id><modified>2007-11-26 14:48:36</modified><created /><style><id /><name /></style><job_type><id>2</id><name>Awareness Campaign</name></job_type><industry><name>Education</name></industry><business_solution><id>4</id><name>Build Relationship</name></business_solution><business_solution><id>6</id><name>Convert Sales</name></business_solution><media_type>17</medi
 a_type><media_type>6</media_type></project>';
+	 	$this->assertEqual($expected, $result);
+	} */
+
+	/* Broken due to a Set class issue */
+	/* function testMixedArray() {
+	 	$input = array('OptionGroup' => array(
+	 		array('name' => 'OptA', 'id' => 12, 'OptA 1', 'OptA 2', 'OptA 3', 'OptA 4', 'OptA 5', 'OptA 6'),
+	 		array('name' => 'OptB', 'id' => 12, 'OptB 1', 'OptB 2', 'OptB 3', 'OptB 4', 'OptB 5', 'OptB 6')
+	 	));
+	 	$expected = '<option_group><name>OptA</name><id>12</id><option_group>OptA 1</option_group><option_group>OptA 2</option_group><option_group>OptA 3</option_group><option_group>OptA 4</option_group><option_group>OptA 5</option_group><option_group>OptA 6</option_group></option_group><option_group><name>OptB</name><id>12</id><option_group>OptB 1</option_group><option_group>OptB 2</option_group><option_group>OptB 3</option_group><option_group>OptB 4</option_group><option_group>OptB 5</option_group><option_group>OptB 6</option_group></option_group>';
+	 	$xml = new Xml($input, array('format' => 'tags'));
+	 	$result = $xml->toString(array('header' => false, 'cdata' => false));
+	 	$this->assertEqual($expected, $result);
+	} */
+
+	/* function testMixedNestedArray() {
+	 	$input = array(
+	 		'OptionA' =>  array(
+	 			'name' => 'OptA',
+	 			'id' => 12,
+	 			'opt' => array('OptA 1', 'OptA 2', 'OptA 3', 'OptA 4', 'OptA 5', 'OptA 6')
+	 		),
+	 		'OptionB' 	=> array(
+	 			'name' => 'OptB',
+	 			'id' => 12,
+	 			'opt' => array('OptB 1', 'OptB 2', 'OptB 3', 'OptB 4', 'OptB 5', 'OptB 6')
+	 		)
+	 	);
+	 	$expected = '<option_a><name>OptA</name><id>12</id><opt>OptA 1</opt><opt>OptA 2</opt><opt>OptA 3</opt><opt>OptA 4</opt><opt>OptA 5</opt><opt>OptA 6</opt></option_a><option_b><name>OptB</name><id>12</id><opt>OptB 1</opt><opt>OptB 2</opt><opt>OptB 3</opt><opt>OptB 4</opt><opt>OptB 5</opt><opt>OptB 6</opt></option_b>';
+	 	$xml = new Xml($input, array('format' => 'tags'));
+	 	$result = $xml->toString(array('header' => false, 'cdata' => false));
+	 	$this->assertEqual($expected, $result);
+	} */
+
+	/* function testMixedArrayAttributes() {
+	 	$input = array('OptionGroup' => array(
+	 		array(
+	 			'name' => 'OptA',
+	 			'id' => 12,
+	 			array('opt' => 'OptA 1'),
+	 			array('opt' => 'OptA 2'),
+	 			array('opt' => 'OptA 3'),
+	 			array('opt' => 'OptA 4'),
+	 			array('opt' => 'OptA 5'),
+	 			array('opt' => 'OptA 6')
+	 		),
+	 		array(
+	 			'name' => 'OptB',
+	 			'id' => 12,
+	 			array('opt' => 'OptB 1'),
+	 			array('opt' => 'OptB 2'),
+	 			array('opt' => 'OptB 3'),
+	 			array('opt' => 'OptB 4'),
+	 			array('opt' => 'OptB 5'),
+	 			array('opt' => 'OptB 6')
+	 		)
+	 	));
+	 	$expected = '<option_group name="OptA" id="12"><opt>OptA 1</opt><opt>OptA 2</opt><opt>OptA 3</opt><opt>OptA 4</opt><opt>OptA 5</opt><opt>OptA 6</opt></option_group><option_group name="OptB" id="12"><opt>OptB 1</opt><opt>OptB 2</opt><opt>OptB 3</opt><opt>OptB 4</opt><opt>OptB 5</opt><opt>OptB 6</opt></option_group>';
+
+	 	$options = array('tags' => array('option_group' => array('attributes' => array('id', 'name'))));
+	 	$xml = new Xml($input, $options);
+	 	$result = $xml->toString(false);
+
+	 	$this->assertEqual($expected, $result);
+	} */
+
+	 /* Not implemented yet */
+	 /* function testTagMap() {
+	 	$input = array(
+	 		array(
+	 			'Project' => array('id' => 1, 'title' => null, 'show' => 1, 'is_spotlight' => null, 'style_id' => 0, 'job_type_id' => 1, 'industry_id' => 1, 'modified' => null, 'created' => null),
+	 			'Style' => array('id' => null, 'name' => null),
+	 			'JobType' => array('id' => 1, 'name' => 'Touch Screen Kiosk'),
+	 			'Industry' => array('id' => 1, 'name' => 'Financial')
+	 		),
+	 		array(
+	 			'Project' => array('id' => 2, 'title' => null, 'show' => 1, 'is_spotlight' => null, 'style_id' => 0, 'job_type_id' => 2, 'industry_id' => 2, 'modified' => '2007-11-26 14:48:36', 'created' => null),
+	 			'Style' => array('id' => null, 'name' => null),
+	 			'JobType' => array('id' => 2, 'name' => 'Awareness Campaign'),
+	 			'Industry' => array('id' => 2, 'name' => 'Education'),
+	 		)
+	 	);
+	 	$expected = '<project id="1"><title /><show>1</show><is_spotlight /><style_id>0</style_id><job_type_id>1</job_type_id><industry_id>1</industry_id><modified /><created /><style id=""><name /></style><jobtype id="1">Touch Screen Kiosk</jobtype><industry id="1"><name>Financial</name></industry></project><project id="2"><title /><show>1</show><is_spotlight /><style_id>0</style_id><job_type_id>2</job_type_id><industry_id>2</industry_id><modified>2007-11-26 14:48:36</modified><created /><style id=""><name /></style><jobtype id="2">Awareness Campaign</jobtype><industry id="2"><name>Education</name></industry></project>';
+
+	 	$xml = new Xml($input, array('tags' => array(
+	 		'Project'	=> array('attributes' => array('id')),
+	 		'style'		=> array('attributes' => array('id')),
+	 		'JobType'	=> array('name' => 'jobtype', 'attributes' => array('id'), 'value' => 'name'),
+	 		'Industry'	=> array('attributes' => array('id'))
+	 	)));
+	 	$result = $xml->toString(array('header' => false, 'cdata' => false));
+	 	$this->assertEqual($expected, $result);
+	} */
+
+/**
+ * testAllCData method
+ *
+ * @access public
+ * @return void
+ */
+	function testAllCData() {
+		$input = array(
+			array(
+				'Project' => array('id' => 1, 'title' => null, 'client_id' => 1, 'show' => '1', 'is_spotlight' => null, 'style_id' => 0, 'job_type_id' => '1.89', 'industry_id' => '1.56', 'modified' => null, 'created' => null),
+				'Style' => array('id' => null, 'name' => null),
+				'JobType' => array('id' => 1, 'name' => 'Touch Screen Kiosk'),
+				'Industry' => array('id' => 1, 'name' => 'Financial')
+			),
+			array(
+				'Project' => array('id' => 2, 'title' => null, 'client_id' => 2, 'show' => '1', 'is_spotlight' => null, 'style_id' => 0, 'job_type_id' => '2.2', 'industry_id' => 2.2, 'modified' => '2007-11-26 14:48:36', 'created' => null),
+				'Style' => array('id' => null, 'name' => null),
+				'JobType' => array('id' => 2, 'name' => 'Awareness Campaign'),
+				'Industry' => array('id' => 2, 'name' => 'Education'),
+			)
+		);
+		$expected = '<project><id>1</id><title /><client_id>1</client_id><show>1</show><is_spotlight /><style_id>0</style_id><job_type_id>1.89</job_type_id><industry_id>1.56</industry_id><modified /><created /><style><id /><name /></style><job_type><id>1</id><name><![CDATA[Touch Screen Kiosk]]></name></job_type><industry><id>1</id><name><![CDATA[Financial]]></name></industry></project><project><id>2</id><title /><client_id>2</client_id><show>1</show><is_spotlight /><style_id>0</style_id><job_type_id>2.2</job_type_id><industry_id>2.2</industry_id><modified><![CDATA[2007-11-26 14:48:36]]></modified><created /><style><id /><name /></style><job_type><id>2</id><name><![CDATA[Awareness Campaign]]></name></job_type><industry><id>2</id><name><![CDATA[Education]]></name></industry></project>';
+		$xml = new Xml($input, array('format' => 'tags'));
+		$result = $xml->toString(array('header' => false, 'cdata' => true));
+		$this->assertEqual($expected, $result);
+	}
+	/* PHP-native Unicode support pending */
+	/* function testConvertEntities() {
+	 	$input = array('project' => '&eacute;c&icirc;t');
+	 	$xml = new Xml($input);
+
+	 	$result = $xml->toString(array('header' => false, 'cdata' => false, 'convertEntities' => true));
+	 	$expected = '<project>&#233;c&#238;t</project>';
+	 	$this->assertEqual($result, $expected);
+	} */
+
+/**
+ * testWhitespace method
+ *
+ * @access public
+ * @return void
+ */
+	function testWhitespace() {
+		$input = array(
+			array(
+				'Project' => array('id' => 1, 'title' => null, 'client_id' => 1, 'show' => 1, 'is_spotlight' => null, 'style_id' => 0, 'job_type_id' => 1, 'industry_id' => 1, 'modified' => null, 'created' => null),
+				'Style' => array('id' => null, 'name' => null),
+				'JobType' => array('id' => 1, 'name' => 'Touch Screen Kiosk'),
+				'Industry' => array('id' => 1, 'name' => 'Financial')
+			),
+			array(
+				'Project' => array('id' => 2, 'title' => null, 'client_id' => 2, 'show' => 1, 'is_spotlight' => null, 'style_id' => 0, 'job_type_id' => 2, 'industry_id' => 2, 'modified' => '2007-11-26 14:48:36', 'created' => null),
+				'Style' => array('id' => null, 'name' => null),
+				'JobType' => array('id' => 2, 'name' => 'Awareness Campaign'),
+				'Industry' => array('id' => 2, 'name' => 'Education'),
+			)
+		);
+		$expected = "\n\t<project>\n\t\t<id>\n\t\t\t1\n\t\t</id>\n\t\t<title />\n\t\t<client_id>\n\t\t\t1\n\t\t</client_id>\n\t\t<show>\n\t\t\t1\n\t\t</show>\n\t\t<is_spotlight />\n\t\t<style_id>\n\t\t\t0\n\t\t</style_id>\n\t\t<job_type_id>\n\t\t\t1\n\t\t</job_type_id>\n\t\t<industry_id>\n\t\t\t1\n\t\t</industry_id>\n\t\t<modified />\n\t\t<created />\n\t\t<style>\n\t\t\t<id />\n\t\t\t<name />\n\t\t</style>\n\t\t<job_type>\n\t\t\t<id>\n\t\t\t\t1\n\t\t\t</id>\n\t\t\t<name>\n\t\t\t\tTouch Screen Kiosk\n\t\t\t</name>\n\t\t</job_type>\n\t\t<industry>\n\t\t\t<id>\n\t\t\t\t1\n\t\t\t</id>\n\t\t\t<name>\n\t\t\t\tFinancial\n\t\t\t</name>\n\t\t</industry>\n\t</project>\n\t<project>\n\t\t<id>\n\t\t\t2\n\t\t</id>\n\t\t<title />\n\t\t<client_id>\n\t\t\t2\n\t\t</client_id>\n\t\t<show>\n\t\t\t1\n\t\t</show>\n\t\t<is_spotlight />\n\t\t<style_id>\n\t\t\t0\n\t\t</style_id>\n\t\t<job_type_id>\n\t\t\t2\n\t\t</job_type_id>\n\t\t<industry_id>\n\t\t\t2\n\t\t</industry_id>\n\t\t<modified>\n\t\t\t2007-11-2
 6 14:48:36\n\t\t</modified>\n\t\t<created />\n\t\t<style>\n\t\t\t<id />\n\t\t\t<name />\n\t\t</style>\n\t\t<job_type>\n\t\t\t<id>\n\t\t\t\t2\n\t\t\t</id>\n\t\t\t<name>\n\t\t\t\tAwareness Campaign\n\t\t\t</name>\n\t\t</job_type>\n\t\t<industry>\n\t\t\t<id>\n\t\t\t\t2\n\t\t\t</id>\n\t\t\t<name>\n\t\t\t\tEducation\n\t\t\t</name>\n\t\t</industry>\n\t</project>\n";
+
+		$xml = new Xml($input, array('format' => 'tags'));
+		$result = $xml->toString(array('header' => false, 'cdata' => false, 'whitespace' => true));
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * testSetSerialization method
+ *
+ * @access public
+ * @return void
+ */
+	function testSetSerialization() {
+		$input = array(
+			array(
+				'Project' => array('id' => 1, 'title' => null, 'client_id' => 1, 'show' => 1, 'is_spotlight' => null, 'style_id' => 0, 'job_type_id' => 1, 'industry_id' => 1, 'modified' => null, 'created' => null),
+				'Style' => array('id' => null, 'name' => null),
+				'JobType' => array('id' => 1, 'name' => 'Touch Screen Kiosk'),
+				'Industry' => array('id' => 1, 'name' => 'Financial')
+			),
+			array(
+				'Project' => array('id' => 2, 'title' => null, 'client_id' => 2, 'show' => 1, 'is_spotlight' => null, 'style_id' => 0, 'job_type_id' => 2, 'industry_id' => 2, 'modified' => '2007-11-26 14:48:36', 'created' => null),
+				'Style' => array('id' => null, 'name' => null),
+				'JobType' => array('id' => 2, 'name' => 'Awareness Campaign'),
+				'Industry' => array('id' => 2, 'name' => 'Education'),
+			)
+		);
+		$expected = '<project><id>1</id><title /><client_id>1</client_id><show>1</show><is_spotlight /><style_id>0</style_id><job_type_id>1</job_type_id><industry_id>1</industry_id><modified /><created /><style><id /><name /></style><job_type><id>1</id><name>Touch Screen Kiosk</name></job_type><industry><id>1</id><name>Financial</name></industry></project><project><id>2</id><title /><client_id>2</client_id><show>1</show><is_spotlight /><style_id>0</style_id><job_type_id>2</job_type_id><industry_id>2</industry_id><modified>2007-11-26 14:48:36</modified><created /><style><id /><name /></style><job_type><id>2</id><name>Awareness Campaign</name></job_type><industry><id>2</id><name>Education</name></industry></project>';
+
+		$xml = new Xml(Set::map($input), array('format' => 'tags'));
+		$result = $xml->toString(array('header' => false, 'cdata' => false));
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * ensure that normalize does not add _name_ elements that come from Set::map sometimes.
+ *
+ * @return void
+ */
+	function testNormalizeNotAdding_name_Element() {
+		$input = array(
+			'output' => array(
+				'Vouchers' => array(
+					array('Voucher' => array('id' => 1)),
+					array('Voucher' => array('id' => 2)),
+				),
+			)
+		);
+		$xml = new Xml($input, array('attributes' => false, 'format' => 'tags'));
+		$this->assertFalse(isset($xml->children[0]->children[0]->children[1]), 'Too many children %s');
+		$this->assertEqual($xml->children[0]->children[0]->children[0]->name, 'voucher');
+	}
+/**
+ * testSimpleParsing method
+ *
+ * @access public
+ * @return void
+ */
+	function testSimpleParsing() {
+		$source = '<response><hello><![CDATA[happy world]]></hello><goodbye><![CDATA[cruel world]]></goodbye></response>';
+		$xml = new Xml($source);
+		$result = $xml->toString();
+		$this->assertEqual($source, $result);
+	}
+
+/**
+ * test that elements with empty tag values do not collapse and corrupt data structures
+ *
+ * @access public
+ * @return void
+ */
+	function testElementCollapsing() {
+		$xmlDataThatFails = '<resultpackage>
+		<result qid="46b1c46ed6208"><![CDATA[46b1c46ed3af9]]></result>
+		<result qid="46b1c46ed332a"><![CDATA[]]></result>
+		<result qid="46b1c46ed90e6"><![CDATA[46b1c46ed69d8]]></result>
+		<result qid="46b1c46ed71a7"><![CDATA[46b1c46ed5a38]]></result>
+		<result qid="46b1c46ed8146"><![CDATA[46b1c46ed98b6]]></result>
+		<result qid="46b1c46ed7978"><![CDATA[]]></result>
+		<result qid="46b1c46ed4a98"><![CDATA[]]></result>
+		<result qid="46b1c46ed42c8"><![CDATA[]]></result>
+		<result qid="46b1c46ed5268"><![CDATA[46b1c46ed8917]]></result>
+		</resultpackage>';
+
+		$Xml = new Xml();
+		$Xml->load('<?xml version="1.0" encoding="UTF-8" ?>' . $xmlDataThatFails);
+		$result = $Xml->toArray(false);
+
+		$this->assertTrue(is_array($result));
+		$expected = array(
+			'resultpackage' => array(
+				'result' => array(
+					0 => array(
+						'value' => '46b1c46ed3af9',
+						'qid' => '46b1c46ed6208'),
+					1 => array(
+						'qid' => '46b1c46ed332a'),
+					2 => array(
+						'value' => '46b1c46ed69d8',
+						'qid' => '46b1c46ed90e6'),
+					3 => array(
+						'value' => '46b1c46ed5a38',
+						'qid' => '46b1c46ed71a7'),
+					4 => array(
+						'value' => '46b1c46ed98b6',
+						'qid' => '46b1c46ed8146'),
+					5 => array(
+						'qid' => '46b1c46ed7978'),
+					6 => array(
+						'qid' => '46b1c46ed4a98'),
+					7 => array(
+						'qid' => '46b1c46ed42c8'),
+					8 => array(
+						'value' => '46b1c46ed8917',
+						'qid' => '46b1c46ed5268'),
+				)
+		));
+		$this->assertEqual(
+			count($result['resultpackage']['result']), count($expected['resultpackage']['result']),
+			'Incorrect array length %s');
+
+		$this->assertFalse(
+			isset($result['resultpackage']['result'][0][0]['qid']), 'Nested array exists, data is corrupt. %s');
+
+		$this->assertEqual($result, $expected);
+	}
+
+/**
+ * test that empty values do not casefold collapse
+ *
+ * @see http://code.cakephp.org/tickets/view/8
+ * @return void
+ */
+	function testCaseFoldingWithEmptyValues() {
+		$filledValue = '<method name="set_user_settings">
+			<title>update user information</title>
+			<user>1</user>
+			<User>
+				<id>1</id>
+				<name>varchar(45)</name>
+			</User>
+		</method>';
+		$xml =& new XML($filledValue);
+		$expected = array(
+			'Method' => array(
+				'name' => 'set_user_settings',
+				'title' => 'update user information',
+				'user' => '1',
+				'User' => array(
+					'id' => 1,
+					'name' => 'varchar(45)',
+				),
+			)
+		);
+		$result = $xml->toArray();
+		$this->assertEqual($result, $expected);
+
+		$emptyValue ='<method name="set_user_settings">
+			<title>update user information</title>
+			<user></user>
+			<User>
+				<id>1</id>
+				<name>varchar(45)</name>
+			</User>
+		</method>';
+
+		$xml =& new XML($emptyValue);
+		$expected = array(
+			'Method' => array(
+				'name' => 'set_user_settings',
+				'title' => 'update user information',
+				'user' => array(),
+				'User' => array(
+					'id' => 1,
+					'name' => 'varchar(45)',
+				),
+			)
+		);
+		$result = $xml->toArray();
+		$this->assertEqual($result, $expected);
+	}
+/**
+ * testMixedParsing method
+ *
+ * @access public
+ * @return void
+ */
+	function testMixedParsing() {
+		$source = '<response><body><hello><![CDATA[happy world]]></hello><![CDATA[in between]]><goodbye><![CDATA[cruel world]]></goodbye></body></response>';
+		$xml = new Xml($source);
+		$result = $xml->toString();
+		$this->assertEqual($source, $result);
+	}
+
+/**
+ * testComplexParsing method
+ *
+ * @access public
+ * @return void
+ */
+	function testComplexParsing() {
+		$source = '<projects><project><id>1</id><title /><client_id>1</client_id><show>1</show><is_spotlight /><style_id>0</style_id><job_type_id>1</job_type_id><industry_id>1</industry_id><modified /><created /><style><id /><name /></style><job_type><id>1</id><name>Touch Screen Kiosk</name></job_type><industry><id>1</id><name>Financial</name></industry></project><project><id>2</id><title /><client_id>2</client_id><show>1</show><is_spotlight /><style_id>0</style_id><job_type_id>2</job_type_id><industry_id>2</industry_id><modified>2007-11-26 14:48:36</modified><created /><style><id /><name /></style><job_type><id>2</id><name>Awareness Campaign</name></job_type><industry><id>2</id><name>Education</name></industry></project></projects>';
+		$xml = new Xml($source);
+		$result = $xml->toString(array('cdata' => false));
+		$this->assertEqual($source, $result);
+	}
+
+/**
+ * testNamespaceParsing method
+ *
+ * @access public
+ * @return void
+ */
+	function testNamespaceParsing() {
+		$source = '<a:container xmlns:a="http://example.com/a" xmlns:b="http://example.com/b" xmlns:c="http://example.com/c" xmlns:d="http://example.com/d" xmlns:e="http://example.com/e"><b:rule test=""><c:result>value</c:result></b:rule><d:rule test=""><e:result>value</e:result></d:rule></a:container>';
+		$xml = new Xml($source);
+
+		$result = $xml->toString(array('cdata' => false));
+		$this->assertEqual($source, $result);
+
+		$children = $xml->children('container');
+		$this->assertEqual($children[0]->namespace, 'a');
+
+		$children = $children[0]->children('rule');
+		$this->assertEqual($children[0]->namespace, 'b');
+	}
+
+/**
+ * testNamespaces method
+ *
+ * @access public
+ * @return void
+ */
+	function testNamespaces() {
+		$source = '<a:container xmlns:a="http://example.com/a" xmlns:b="http://example.com/b" xmlns:c="http://example.com/c" xmlns:d="http://example.com/d" xmlns:e="http://example.com/e"><b:rule test=""><c:result>value</c:result></b:rule><d:rule test=""><e:result>value</e:result></d:rule></a:container>';
+		$xml = new Xml($source);
+
+		$expects = '<a:container xmlns:a="http://example.com/a" xmlns:b="http://example.com/b" xmlns:c="http://example.com/c" xmlns:d="http://example.com/d" xmlns:e="http://example.com/e" xmlns:f="http://example.com/f"><b:rule test=""><c:result>value</c:result></b:rule><d:rule test=""><e:result>value</e:result></d:rule></a:container>';
+
+		$_xml =& XmlManager::getInstance();
+		$xml->addNamespace('f', 'http://example.com/f');
+		$result = $xml->toString(array('cdata' => false));
+		$this->assertEqual($expects, $result);
+	}
+
+/**
+ * testEscapeCharSerialization method
+ *
+ * @access public
+ * @return void
+ */
+	function testEscapeCharSerialization() {
+		$xml = new Xml(array('text' => 'JavaScript & DHTML'), array('attributes' => false, 'format' => 'attributes'));
+
+		$result = $xml->toString(false);
+		$expected = '<std_class text="JavaScript &amp; DHTML" />';
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * testCompleteEscapeCharSerialization method
+ *
+ * @access public
+ * @return void
+ */
+	function testCompleteEscapeCharSerialization() {
+		$xml = new Xml(array('text' => '<>&"\''), array('attributes' => false, 'format' => 'attributes'));
+
+		$result = $xml->toString(false);
+		$expected = '<std_class text="&lt;&gt;&amp;&quot;&#039;" />';
+		$this->assertEqual($expected, $result);
+	}
+
+/**
+ * testToArray method
+ *
+ * @access public
+ * @return void
+ */
+	function testToArray() {
+		App::import('Set');
+		$string = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+		<rss version="2.0">
+		<channel>
+			<title>Cake PHP Google Group</title>
+			<link>http://groups.google.com/group/cake-php</link>
+			<description>Search this group before posting anything. There are over 20,000 posts and it&amp;#39;s very likely your question was answered before. Visit the IRC channel #cakep****@irc***** for live chat with users and developers of Cake. If you post, tell us the version of Cake, PHP, and database.</description>
+			<language>en</language>
+			<item>
+				<title>constructng result array when using findall</title>
+				<link>http://groups.google.com/group/cake-php/msg/49bc00f3bc651b4f</link>
+				<description>i&#39;m using cakephp to construct a logical data model array that will be &lt;br&gt; passed to a flex app. I have the following model association: &lt;br&gt; ServiceDay-&amp;gt;(hasMany)ServiceTi me-&amp;gt;(hasMany)ServiceTimePrice. So what &lt;br&gt; the current output from my findall is something like this example: &lt;br&gt; &lt;p&gt;Array( &lt;br&gt; [0] =&amp;gt; Array(</description>
+				<guid isPermaLink="true">http://groups.google.com/group/cake-php/msg/49bc00f3bc651b4f</guid>
+				<author>bmil.****@gmail*****(bpscrugs)</author>
+				<pubDate>Fri, 28 Dec 2007 00:44:14 UT</pubDate>
+			</item>
+			<item>
+				<title>Re: share views between actions?</title>
+				<link>http://groups.google.com/group/cake-php/msg/8b350d898707dad8</link>
+				<description>Then perhaps you might do us all a favour and refrain from replying to &lt;br&gt; things you do not understand. That goes especially for asinine comments. &lt;br&gt; Indeed. &lt;br&gt; To sum up: &lt;br&gt; No comment. &lt;br&gt; In my day, a simple &amp;quot;RTFM&amp;quot; would suffice. I&#39;ll keep in mind to ignore any &lt;br&gt; further responses from you. &lt;br&gt; You (and I) were referring to the *online documentation*, not other</description>
+				<guid isPermaLink="true">http://groups.google.com/group/cake-php/msg/8b350d898707dad8</guid>
+				<author>subtr****@gmail*****(subtropolis zijn)</author>
+				<pubDate>Fri, 28 Dec 2007 00:45:01 UT</pubDate>
+			</item>
+		</channel>
+		</rss>';
+		$xml = new Xml($string);
+		$result = $xml->toArray();
+		$expected = array('Rss' => array(
+			'version' => '2.0',
+			'Channel' => array(
+				'title' => 'Cake PHP Google Group',
+				'link' => 'http://groups.google.com/group/cake-php',
+				'description' => 'Search this group before posting anything. There are over 20,000 posts and it&#39;s very likely your question was answered before. Visit the IRC channel #cakep****@irc***** for live chat with users and developers of Cake. If you post, tell us the version of Cake, PHP, and database.',
+				'language' => 'en',
+				'Item' => array(
+					array(
+						'title' => 'constructng result array when using findall',
+						'link' => 'http://groups.google.com/group/cake-php/msg/49bc00f3bc651b4f',
+						'description' => "i'm using cakephp to construct a logical data model array that will be <br> passed to a flex app. I have the following model association: <br> ServiceDay-&gt;(hasMany)ServiceTi me-&gt;(hasMany)ServiceTimePrice. So what <br> the current output from my findall is something like this example: <br><p>Array( <br> [0] =&gt; Array(",
+						'guid' => array('isPermaLink' => 'true', 'value' => 'http://groups.google.com/group/cake-php/msg/49bc00f3bc651b4f'),
+						'author' => 'bmil.****@gmail*****(bpscrugs)',
+						'pubDate' => 'Fri, 28 Dec 2007 00:44:14 UT',
+					),
+					array(
+						'title' => 'Re: share views between actions?',
+						'link' => 'http://groups.google.com/group/cake-php/msg/8b350d898707dad8',
+						'description' => 'Then perhaps you might do us all a favour and refrain from replying to <br> things you do not understand. That goes especially for asinine comments. <br> Indeed. <br> To sum up: <br> No comment. <br> In my day, a simple &quot;RTFM&quot; would suffice. I\'ll keep in mind to ignore any <br> further responses from you. <br> You (and I) were referring to the *online documentation*, not other',
+						'guid' => array('isPermaLink' => 'true', 'value' => 'http://groups.google.com/group/cake-php/msg/8b350d898707dad8'),
+						'author' => 'subtr****@gmail*****(subtropolis zijn)',
+						'pubDate' => 'Fri, 28 Dec 2007 00:45:01 UT'
+					)
+				)
+			)
+		));
+		$this->assertEqual($result, $expected);
+
+		$string ='<data><post title="Title of this post" description="cool"/></data>';
+		$xml = new Xml($string);
+		$result = $xml->toArray();
+		$expected = array('Data' => array('Post' => array('title' => 'Title of this post', 'description' => 'cool')));
+		$this->assertEqual($result, $expected);
+
+		$xml = new Xml('<example><item><title>An example of a correctly reversed XMLNode</title><desc/></item></example>');
+		$result = Set::reverse($xml);
+		$expected = array(
+			'Example' => array(
+				'Item' => array(
+					'title' => 'An example of a correctly reversed XMLNode',
+					'desc' => array(),
+				)
+			)
+		);
+		$this->assertIdentical($result, $expected);
+
+		$xml = new Xml('<example><item attr="123"><titles><title>title1</title><title>title2</title></titles></item></example>');
+		$result = $xml->toArray();
+		$expected = array(
+			'Example' => array(
+				'Item' => array(
+					'attr' => '123',
+					'Titles' => array(
+						'Title' => array('title1', 'title2')
+					)
+				)
+			)
+		);
+		$this->assertIdentical($result, $expected);
+
+		$xml = new Xml('<example attr="ex_attr"><item attr="123"><titles>list</titles>textforitems</item></example>');
+		$result = $xml->toArray();
+		$expected = array(
+			'Example' => array(
+				'attr' => 'ex_attr',
+				'Item' => array(
+					'attr' => '123',
+					'titles' => 'list',
+					'value'  => 'textforitems'
+				)
+			)
+		);
+		$this->assertIdentical($result, $expected);
+
+		$xml = new Xml('<example attr="ex_attr"><item attr="123"><titles>list</titles>textforitems</item></example>');
+		$example = $xml->child('example');
+		$item = $example->child('item');
+		$result = $item->toArray();
+
+		$expected = array(
+			'attr' => '123',
+			'titles' => 'list',
+			'value'  => 'textforitems'
+		);
+		$this->assertIdentical($result, $expected);
+
+		$string = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+		<rss version="2.0">
+		<channel>
+			<title>Cake PHP Google Group</title>
+			<link>http://groups.google.com/group/cake-php</link>
+			<description>Search this group before posting anything. There are over 20,000 posts and it&amp;#39;s very likely your question was answered before. Visit the IRC channel #cakep****@irc***** for live chat with users and developers of Cake. If you post, tell us the version of Cake, PHP, and database.</description>
+			<language>en</language>
+			<item>
+				<title>constructng result array when using findall</title>
+				<link>http://groups.google.com/group/cake-php/msg/49bc00f3bc651b4f</link>
+				<description>i&#39;m using cakephp to construct a logical data model array that will be &lt;br&gt; passed to a flex app. I have the following model association: &lt;br&gt; ServiceDay-&amp;gt;(hasMany)ServiceTi me-&amp;gt;(hasMany)ServiceTimePrice. So what &lt;br&gt; the current output from my findall is something like this example: &lt;br&gt; &lt;p&gt;Array( &lt;br&gt; [0] =&amp;gt; Array(</description>
+				<dc:creator>cakephp</dc:creator>
+				<category><![CDATA[cakephp]]></category>
+				<category><![CDATA[model]]></category>
+				<guid isPermaLink="true">http://groups.google.com/group/cake-php/msg/49bc00f3bc651b4f</guid>
+				<author>bmil.****@gmail*****(bpscrugs)</author>
+				<pubDate>Fri, 28 Dec 2007 00:44:14 UT</pubDate>
+			</item>
+			<item>
+				<title>Re: share views between actions?</title>
+				<link>http://groups.google.com/group/cake-php/msg/8b350d898707dad8</link>
+				<description>Then perhaps you might do us all a favour and refrain from replying to &lt;br&gt; things you do not understand. That goes especially for asinine comments. &lt;br&gt; Indeed. &lt;br&gt; To sum up: &lt;br&gt; No comment. &lt;br&gt; In my day, a simple &amp;quot;RTFM&amp;quot; would suffice. I&#39;ll keep in mind to ignore any &lt;br&gt; further responses from you. &lt;br&gt; You (and I) were referring to the *online documentation*, not other</description>
+				<dc:creator>cakephp</dc:creator>
+				<category><![CDATA[cakephp]]></category>
+				<category><![CDATA[model]]></category>
+				<guid isPermaLink="true">http://groups.google.com/group/cake-php/msg/8b350d898707dad8</guid>
+				<author>subtr****@gmail*****(subtropolis zijn)</author>
+				<pubDate>Fri, 28 Dec 2007 00:45:01 UT</pubDate>
+			</item>
+		</channel>
+		</rss>';
+
+		$xml = new Xml($string);
+		$result = $xml->toArray();
+
+		$expected = array('Rss' => array(
+			'version' => '2.0',
+			'Channel' => array(
+				'title' => 'Cake PHP Google Group',
+				'link' => 'http://groups.google.com/group/cake-php',
+				'description' => 'Search this group before posting anything. There are over 20,000 posts and it&#39;s very likely your question was answered before. Visit the IRC channel #cakep****@irc***** for live chat with users and developers of Cake. If you post, tell us the version of Cake, PHP, and database.',
+				'language' => 'en',
+				'Item' => array(
+					array(
+						'title' => 'constructng result array when using findall',
+						'link' => 'http://groups.google.com/group/cake-php/msg/49bc00f3bc651b4f',
+						'description' => "i'm using cakephp to construct a logical data model array that will be <br> passed to a flex app. I have the following model association: <br> ServiceDay-&gt;(hasMany)ServiceTi me-&gt;(hasMany)ServiceTimePrice. So what <br> the current output from my findall is something like this example: <br><p>Array( <br> [0] =&gt; Array(",
+						'creator' => 'cakephp',
+						'Category' => array('cakephp', 'model'),
+						'guid' => array('isPermaLink' => 'true', 'value' => 'http://groups.google.com/group/cake-php/msg/49bc00f3bc651b4f'),
+						'author' => 'bmil.****@gmail*****(bpscrugs)',
+						'pubDate' => 'Fri, 28 Dec 2007 00:44:14 UT',
+					),
+					array(
+						'title' => 'Re: share views between actions?',
+						'link' => 'http://groups.google.com/group/cake-php/msg/8b350d898707dad8',
+						'description' => 'Then perhaps you might do us all a favour and refrain from replying to <br> things you do not understand. That goes especially for asinine comments. <br> Indeed. <br> To sum up: <br> No comment. <br> In my day, a simple &quot;RTFM&quot; would suffice. I\'ll keep in mind to ignore any <br> further responses from you. <br> You (and I) were referring to the *online documentation*, not other',
+						'creator' => 'cakephp',
+						'Category' => array('cakephp', 'model'),
+						'guid' => array('isPermaLink' => 'true', 'value' => 'http://groups.google.com/group/cake-php/msg/8b350d898707dad8'),
+						'author' => 'subtr****@gmail*****(subtropolis zijn)',
+						'pubDate' => 'Fri, 28 Dec 2007 00:45:01 UT'
+					)
+				)
+			)
+		));
+		$this->assertEqual($result, $expected);
+
+		$text = "<?xml version='1.0' encoding='utf-8'?>
+		          <course>
+		            <comps>
+		              <comp>1</comp>
+		              <comp>2</comp>
+		              <comp>3</comp>
+		              <comp>4</comp>
+		            </comps>
+		          </course>";
+		$xml = new Xml($text);
+		$result = $xml->toArray();
+
+		$expected = array('Course' => array(
+			'Comps' => array(
+				'Comp' => array(
+					1, 2, 3, 4
+				)
+			)
+		));
+
+		$this->assertEqual($result, $expected);
+
+		$text = '<?xml version="1.0" encoding="UTF-8"?>
+		<XRDS xmlns="xri://$xrds">
+		<XRD xml:id="oauth" xmlns="xri://$XRD*($v*2.0)" version="2.0">
+			<Type>xri://$xrds*simple</Type>
+			<Expires>2008-04-13T07:34:58Z</Expires>
+			<Service>
+				<Type>http://oauth.net/core/1.0/endpoint/authorize</Type>
+				<Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
+				<Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
+				<URI priority="10">https://ma.gnolia.com/oauth/authorize</URI>
+				<URI priority="20">http://ma.gnolia.com/oauth/authorize</URI>
+			</Service>
+		</XRD>
+		<XRD xmlns="xri://$XRD*($v*2.0)" version="2.0">
+			<Type>xri://$xrds*simple</Type>
+				<Service priority="10">
+					<Type>http://oauth.net/discovery/1.0</Type>
+					<URI>#oauth</URI>
+				</Service>
+		</XRD>
+		</XRDS>';
+
+		$xml = new Xml($text);
+		$result = $xml->toArray();
+
+		$expected = array('XRDS' => array(
+			'xmlns' => 'xri://$xrds',
+			'XRD' => array(
+				array(
+					'xml:id' => 'oauth',
+					'xmlns' => 'xri://$XRD*($v*2.0)',
+					'version' => '2.0',
+					'Type' => 'xri://$xrds*simple',
+					'Expires' => '2008-04-13T07:34:58Z',
+					'Service' => array(
+						'Type' => array(
+							'http://oauth.net/core/1.0/endpoint/authorize',
+							'http://oauth.net/core/1.0/parameters/auth-header',
+							'http://oauth.net/core/1.0/parameters/uri-query'
+						),
+						'URI' => array(
+							array(
+								'value' => 'https://ma.gnolia.com/oauth/authorize',
+								'priority' => '10',
+							),
+							array(
+								'value' => 'http://ma.gnolia.com/oauth/authorize',
+								'priority' => '20'
+							)
+						)
+					)
+				),
+				array(
+					'xmlns' => 'xri://$XRD*($v*2.0)',
+					'version' => '2.0',
+					'Type' => 'xri://$xrds*simple',
+					'Service' => array(
+						'priority' => '10',
+						'Type' => 'http://oauth.net/discovery/1.0',
+						'URI' => '#oauth'
+					)
+				)
+			)
+		));
+		$this->assertEqual($result, $expected);
+
+		$text = '<?xml version="1.0" encoding="UTF-8"?>
+		<root>
+			<child id="1" other="1" />
+			<child id="2" other="1" />
+			<child id="3" other="1" />
+			<child id="4" other="1" />
+			<child id="5" other="1" />
+		</root>';
+		$xml = new Xml($text);
+		$result = $xml->toArray();
+		$expected = array(
+			'Root' => array(
+				'Child' => array(
+					array('id' => 1, 'other' => 1),
+					array('id' => 2, 'other' => 1),
+					array('id' => 3, 'other' => 1),
+					array('id' => 4, 'other' => 1),
+					array('id' => 5, 'other' => 1)
+				)
+			)
+		);
+		$this->assertEqual($result, $expected);
+
+		$text = '<main><first label="first type node 1" /><first label="first type node 2" /><second label="second type node" /></main>';
+		$xml =  new Xml($text);
+		$result = $xml->toArray();
+		$expected = array(
+		    'Main' => array(
+		        'First' => array(
+		            array('label' => 'first type node 1'),
+		            array('label' => 'first type node 2')
+		        ),
+		        'Second' => array('label'=>'second type node')
+		    )
+		);
+		$this->assertIdentical($result,$expected);
+
+		$text = '<main><first label="first type node 1" /><first label="first type node 2" /><second label="second type node" /><collection><fifth label="fifth type node"/><third label="third type node 1"/><third label="third type node 2"/><third label="third type node 3"/><fourth label="fourth type node"/></collection></main>';
+		$xml =  new Xml($text);
+		$result = $xml->toArray();
+		$expected = array(
+		    'Main' => array(
+		        'First' => array(
+		            array('label' => 'first type node 1'),
+		            array('label' => 'first type node 2')
+		        ),
+		        'Second' => array('label'=>'second type node'),
+				'Collection' => array(
+					'Fifth' => array('label' => 'fifth type node'),
+					'Third' => array(
+						array('label' => 'third type node 1'),
+						array('label' => 'third type node 2'),
+						array('label' => 'third type node 3'),
+					),
+					'Fourth' => array('label' => 'fourth type node'),
+				)
+		    )
+		);
+		$this->assertIdentical($result,$expected);
+	}
+
+/**
+ * testAppend method
+ *
+ * @access public
+ * @return void
+ */
+	function testAppend() {
+		$parentNode = new XmlNode('ourParentNode');
+		$parentNode->append( new XmlNode('ourChildNode'));
+		$first =& $parentNode->first();
+		$this->assertEqual($first->name, 'ourChildNode');
+
+		$string = 'ourChildNode';
+		$parentNode = new XmlNode('ourParentNode');
+		$parentNode->append($string);
+		$last =& $parentNode->last();
+		$this->assertEqual($last->name, 'ourChildNode');
+
+		$this->expectError();
+		$parentNode->append($parentNode);
+	}
+
+/**
+ * testNamespacing method
+ *
+ * @access public
+ * @return void
+ */
+	function testNamespacing() {
+		$node = new Xml('<xml></xml>');
+		$node->addNamespace('cake', 'http://cakephp.org');
+		$this->assertEqual($node->toString(), '<xml xmlns:cake="http://cakephp.org" />');
+
+		$this->assertTrue($node->removeNamespace('cake'));
+		$this->assertEqual($node->toString(), '<xml />');
+
+
+		$node = new Xml('<xml xmlns:cake="http://cakephp.org" />');
+		$this->assertTrue($node->removeNamespace('cake'));
+		$this->assertEqual($node->toString(), '<xml />');
+
+		$node->addNamespace('cake', 'http://cakephp.org');
+		$this->assertEqual($node->toString(), '<xml xmlns:cake="http://cakephp.org" />');
+	}
+
+/**
+ * testCamelize method
+ *
+ * @access public
+ * @return void
+ */
+	function testCamelize() {
+		$xmlString = '<methodCall><methodName>examples.getStateName</methodName>' .
+			'<params><param><value><i4>41</i4></value></param></params></methodCall>';
+
+		$Xml = new Xml($xmlString);
+		$expected = array(
+			'methodCall' => array(
+				'methodName' => 'examples.getStateName',
+					'params' => array(
+						'param' => array('value' => array('i4' => 41)))));
+		$this->assertEqual($expected, $Xml->toArray(false));
+
+		$Xml = new Xml($xmlString);
+		$expected = array(
+			'MethodCall' => array(
+				'methodName' => 'examples.getStateName',
+					'Params' => array(
+						'Param' => array('Value' => array('i4' => 41)))));
+		$this->assertEqual($expected, $Xml->toArray());
+	}
+
+/**
+ * testNumericDataHandling method
+ *
+ * @access public
+ * @return void
+ */
+	function testNumericDataHandling() {
+		$data = '<xml><data>012345</data></xml>';
+
+		$node = new Xml();
+		$node->load($data);
+		$node->parse();
+
+		$result = $node->first();
+		$result = $result->children("data");
+
+		$result = $result[0]->first();
+		$this->assertEqual($result->value, '012345');
+	}
+
+/**
+ * test that creating an xml object does not leak memory
+ *
+ * @return void
+ */
+	function testMemoryLeakInConstructor() {
+		if ($this->skipIf(!function_exists('memory_get_usage'), 'Cannot test memory leaks without memory_get_usage')) {
+			return;
+		}
+		$data = '<?xml version="1.0" encoding="UTF-8"?><content>TEST</content>';
+		$start = memory_get_usage();
+		for ($i = 0; $i <= 300; $i++) {
+			$test =& new XML($data);
+			$test->__destruct();
+			unset($test);
+		}
+		$end = memory_get_usage();
+		$this->assertWithinMargin($start, $end, 3600, 'Memory leaked %s');
+	}
+
+/**
+ * Test toArray with alternate inputs.
+ *
+ * @return void
+ */
+	function testToArrayAlternate() {
+		$sXml = 
+		'<t1>
+		 	<t2>A</t2>
+      		<t2><t3>AAA</t3>B</t2>
+	  		<t2>C</t2>
+		</t1>';
+		$xml = new Xml($sXml);
+		$result = $xml->toArray();
+		$expected = array(
+			'T1' => array(
+				'T2' => array(
+					'A',
+					array('t3' => 'AAA', 'value' => 'B'),
+					'C'
+			)
+		)
+		);
+		$this->assertIdentical($result, $expected);
+		$result = $xml->toArray(false);
+		$expected = array(
+			't1' => array(
+				't2' => array(
+					'A',
+					array('t3' => 'AAA', 'value' => 'B'),
+					'C'
+				)
+			)
+		);
+		$this->assertIdentical($result, $expected);
+		
+		$sXml = 
+		'<t1>
+		 	<t2>A</t2>
+	  		<t2>B</t2>
+      		<t2>
+	         	<t3>CCC</t3>
+	      	</t2>
+		</t1>';
+		$xml = new Xml($sXml);
+		$result = $xml->toArray();
+		$expected = array(
+			'T1' => array(
+				'T2' => array(
+					'A',
+					'B',
+					array('t3' => 'CCC'),
+				)
+			)
+		);
+		$this->assertIdentical($result, $expected);
+		$result = $xml->toArray(false);
+		$expected = array(
+			't1' => array(
+				't2' => array(
+					'A',
+					'B',
+					array('t3' => 'CCC'),
+				)
+			)
+		);
+		$this->assertIdentical($result, $expected);
+		
+		$sXml = 
+		'<t1>
+		 <t2>A</t2>
+		 <t2></t2>
+		 <t2>C</t2>
+		</t1>';
+		$xml = new Xml($sXml);
+		$result = $xml->toArray();
+		$expected = array(
+			'T1' => array(
+				'T2' => array(
+					'A',
+					array(),
+					'C'
+				)
+			)
+		);
+		$this->assertIdentical($result, $expected);
+
+		$result = $xml->toArray(false);
+		$expected = array(
+			't1' => array(
+				't2' => array(
+					'A',
+					array(),
+					'C'
+				)
+			)
+		);
+		$this->assertIdentical($result, $expected);
+		
+		$sXml = 
+		'<stuff>
+    <foo name="abc-16" profile-id="Default" />
+    <foo name="abc-17" profile-id="Default" >
+        <bar id="HelloWorld" />
+    </foo>
+    <foo name="abc-asdf" profile-id="Default" />
+    <foo name="cba-1A" profile-id="Default">
+        <bar id="Baz" />
+    </foo>
+    <foo name="cba-2A" profile-id="Default">
+        <bar id="Baz" />
+    </foo>
+    <foo name="qa" profile-id="Default" />
+</stuff>';
+		$xml = new Xml($sXml);
+		$result = $xml->toArray();
+		$expected = array(
+			'Stuff' => array(
+				'Foo' => array(
+					array('name' => 'abc-16', 'profile-id' => 'Default'),
+					array('name' => 'abc-17', 'profile-id' => 'Default', 
+						'Bar' => array('id' => 'HelloWorld')),
+					array('name' => 'abc-asdf', 'profile-id' => 'Default'),
+					array('name' => 'cba-1A', 'profile-id' => 'Default', 
+						'Bar' => array('id' => 'Baz')),
+					array('name' => 'cba-2A', 'profile-id' => 'Default', 
+						'Bar' => array('id' => 'Baz')),
+					array('name' => 'qa', 'profile-id' => 'Default'),
+				)
+			)
+		);
+		$this->assertIdentical($result, $expected);
+		$result = $xml->toArray(false);
+		$expected = array(
+			'stuff' => array(
+				'foo' => array(
+					array('name' => 'abc-16', 'profile-id' => 'Default'),
+					array('name' => 'abc-17', 'profile-id' => 'Default', 
+						'bar' => array('id' => 'HelloWorld')),
+					array('name' => 'abc-asdf', 'profile-id' => 'Default'),
+					array('name' => 'cba-1A', 'profile-id' => 'Default', 
+						'bar' => array('id' => 'Baz')),
+					array('name' => 'cba-2A', 'profile-id' => 'Default', 
+						'bar' => array('id' => 'Baz')),
+					array('name' => 'qa', 'profile-id' => 'Default'),
+				)
+			)
+		);
+		$this->assertIdentical($result, $expected);
+		
+		
+		$sXml = 
+		'<root>
+  <node name="first" />
+  <node name="second"><subnode name="first sub" /><subnode name="second sub" /></node>
+  <node name="third" />
+</root>';
+		$xml = new Xml($sXml);
+		$result = $xml->toArray();
+		$expected = array(
+			'Root' => array(
+				'Node' => array(
+					array('name' => 'first'),
+					array('name' => 'second', 
+						'Subnode' => array(
+							array('name' => 'first sub'), 
+							array('name' => 'second sub'))),
+					array('name' => 'third'),
+				)
+			)
+		);
+		$this->assertIdentical($result, $expected);
+		
+		$result = $xml->toArray(false);
+		$expected = array(
+			'root' => array(
+				'node' => array(
+					array('name' => 'first'),
+					array('name' => 'second', 
+						'subnode' => array(
+							array('name' => 'first sub'), 
+							array('name' => 'second sub'))),
+					array('name' => 'third'),
+				)
+			)
+		);
+		$this->assertIdentical($result, $expected);
+	}
+	
+
+	function testToStringSlugging() {
+		$array = array(
+			'Response' => array(
+				'OneKey' => 'foo',
+				'TwoKey' => array('bar', 'baz')
+			)
+		);
+		$xml = new Xml($array, array('format' => 'tags'));
+		$result = $xml->toString(array('cdata' => false));
+		$expected = '<response><one_key>foo</one_key><two_key>bar</two_key><two_key>baz</two_key></response>';
+		$this->assertEqual($result, $expected);
+	}
+}

Added: trunk/src/Web/cake/tests/fixtures/account_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/account_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/account_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class AccountFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Aco'
+ * @access public
+ */
+	var $name = 'Account';
+	var $table = 'Accounts';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'iAccountId'		=> array('type' => 'integer', 'key' => 'primary'),
+		'cDescription'	=> array('type' => 'string', 'length' => 10, 'null' => true)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('cDescription' => 'gwoo'),
+		array('cDescription' => 'phpnut'),
+		array('cDescription' => 'schreck'),
+		array('cDescription' => 'dude')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/aco_action_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/aco_action_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/aco_action_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class AcoActionFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'AcoAction'
+ * @access public
+ */
+	var $name = 'AcoAction';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'parent_id' => array('type' => 'integer', 'length' => 10, 'null' => true),
+		'model' => array('type' => 'string', 'default' => ''),
+		'foreign_key' => array('type' => 'integer', 'length' => 10, 'null' => true),
+		'alias' => array('type' => 'string', 'default' => ''),
+		'lft' => array('type' => 'integer', 'length' => 10, 'null' => true),
+		'rght' => array('type' => 'integer', 'length' => 10, 'null' => true)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array();
+}

Added: trunk/src/Web/cake/tests/fixtures/aco_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/aco_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/aco_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,73 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class AcoFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Aco'
+ * @access public
+ */
+	var $name = 'Aco';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id'		=> array('type' => 'integer', 'key' => 'primary'),
+		'parent_id'	=> array('type' => 'integer', 'length' => 10, 'null' => true),
+		'model'		=> array('type' => 'string', 'null' => true),
+		'foreign_key' => array('type' => 'integer', 'length' => 10, 'null' => true),
+		'alias' => array('type' => 'string', 'default' => ''),
+		'lft' => array('type' => 'integer', 'length' => 10, 'null' => true),
+		'rght' => array('type' => 'integer', 'length' => 10, 'null' => true)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('parent_id' => null, 'model' => null, 'foreign_key' => null, 'alias' => 'ROOT', 'lft' => 1,  'rght' => 24),
+		array('parent_id' => 1, 'model' => null, 'foreign_key' => null, 'alias' => 'Controller1', 'lft' => 2,  'rght' => 9),
+		array('parent_id' => 2, 'model' => null, 'foreign_key' => null, 'alias' => 'action1', 'lft' => 3,  'rght' => 6),
+		array('parent_id' => 3, 'model' => null, 'foreign_key' => null, 'alias' => 'record1', 'lft' => 4,  'rght' => 5),
+		array('parent_id' => 2, 'model' => null, 'foreign_key' => null, 'alias' => 'action2', 'lft' => 7,  'rght' => 8),
+		array('parent_id' => 1, 'model' => null, 'foreign_key' => null, 'alias' => 'Controller2','lft' => 10, 'rght' => 17),
+		array('parent_id' => 6, 'model' => null, 'foreign_key' => null, 'alias' => 'action1', 'lft' => 11, 'rght' => 14),
+		array('parent_id' => 7, 'model' => null, 'foreign_key' => null, 'alias' => 'record1', 'lft' => 12, 'rght' => 13),
+		array('parent_id' => 6, 'model' => null, 'foreign_key' => null, 'alias' => 'action2', 'lft' => 15, 'rght' => 16),
+		array('parent_id' => 1, 'model' => null, 'foreign_key' => null, 'alias' => 'Users', 'lft' => 18, 'rght' => 23),
+		array('parent_id' => 9, 'model' => null, 'foreign_key' => null, 'alias' => 'Users', 'lft' => 19, 'rght' => 22),
+		array('parent_id' => 10, 'model' => null, 'foreign_key' => null, 'alias' => 'view', 'lft' => 20, 'rght' => 21),
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/aco_two_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/aco_two_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/aco_two_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,71 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class AcoTwoFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'AcoTwo'
+ * @access public
+ */
+	var $name = 'AcoTwo';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id'		=> array('type' => 'integer', 'key' => 'primary'),
+		'parent_id'	=> array('type' => 'integer', 'length' => 10, 'null' => true),
+		'model'		=> array('type' => 'string', 'null' => true),
+		'foreign_key' => array('type' => 'integer', 'length' => 10, 'null' => true),
+		'alias'		=> array('type' => 'string', 'default' => ''),
+		'lft'		=> array('type' => 'integer', 'length' => 10, 'null' => true),
+		'rght'		=> array('type' => 'integer', 'length' => 10, 'null' => true)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('parent_id' => null,	'model' => null, 'foreign_key' => null, 'alias' => 'ROOT',		'lft' => 1,  'rght' => 20),
+		array('parent_id' => 1,		'model' => null, 'foreign_key' => null, 'alias' => 'tpsReports', 'lft' => 2,  'rght' => 9),
+		array('parent_id' => 2,		'model' => null, 'foreign_key' => null, 'alias' => 'view',		'lft' => 3,  'rght' => 6),
+		array('parent_id' => 3,		'model' => null, 'foreign_key' => null, 'alias' => 'current',	'lft' => 4,  'rght' => 5),
+		array('parent_id' => 2,		'model' => null, 'foreign_key' => null, 'alias' => 'update',	'lft' => 7,  'rght' => 8),
+		array('parent_id' => 1,		'model' => null, 'foreign_key' => null, 'alias' => 'printers',	'lft' => 10, 'rght' => 19),
+		array('parent_id' => 6,		'model' => null, 'foreign_key' => null, 'alias' => 'print',		'lft' => 11, 'rght' => 14),
+		array('parent_id' => 7,		'model' => null, 'foreign_key' => null, 'alias' => 'lettersize','lft' => 12, 'rght' => 13),
+		array('parent_id' => 6,		'model' => null, 'foreign_key' => null, 'alias' => 'refill',	'lft' => 15, 'rght' => 16),
+		array('parent_id' => 6,		'model' => null, 'foreign_key' => null, 'alias' => 'smash',		'lft' => 17, 'rght' => 18),
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/ad_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/ad_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/ad_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,68 @@
+<?php
+/**
+ * Short description for ad_fixture.php
+ *
+ * Long description for ad_fixture.php
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * @link          http://www.cakephp.org
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         1.2
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * AdFixture class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class AdFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Ad'
+ * @access public
+ */
+	var $name = 'Ad';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'campaign_id' => array('type' => 'integer'),
+		'parent_id' => array('type' => 'integer'),
+		'lft' => array('type' => 'integer'),
+		'rght' => array('type' => 'integer'),
+		'name' => array('type' => 'string', 'length' => 255, 'null' => false)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('parent_id' => null, 'lft' => 1,  'rght' => 2,  'campaign_id' => 1, 'name' => 'Nordover'),
+		array('parent_id' => null, 'lft' => 3,  'rght' => 4,  'campaign_id' => 1, 'name' => 'Statbergen'),
+		array('parent_id' => null, 'lft' => 5,  'rght' => 6,  'campaign_id' => 1, 'name' => 'Feroy'),
+		array('parent_id' => null, 'lft' => 7, 'rght' => 12,  'campaign_id' => 2, 'name' => 'Newcastle'),
+		array('parent_id' => null, 'lft' => 8,  'rght' => 9,  'campaign_id' => 2, 'name' => 'Dublin'),
+		array('parent_id' => null, 'lft' => 10, 'rght' => 11, 'campaign_id' => 2, 'name' => 'Alborg'),
+		array('parent_id' => null, 'lft' => 13, 'rght' => 14, 'campaign_id' => 3, 'name' => 'New York')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/advertisement_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/advertisement_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/advertisement_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class AdvertisementFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Advertisement'
+ * @access public
+ */
+	var $name = 'Advertisement';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'title' => array('type' => 'string', 'null' => false),
+		'created' => 'datetime',
+		'updated' => 'datetime'
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('title' => 'First Ad', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'),
+		array('title' => 'Second Ad', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/after_tree_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/after_tree_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/after_tree_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,67 @@
+<?php
+/**
+ * Short description for after_tree_fixture.php
+ *
+ * Long description for after_tree_fixture.php
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * @link          http://www.cakephp.org
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         1.2
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * AdFixture class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class AfterTreeFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'AfterTree'
+ * @access public
+ */
+	var $name = 'AfterTree';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'parent_id' => array('type' => 'integer'),
+		'lft' => array('type' => 'integer'),
+		'rght' => array('type' => 'integer'),
+		'name' => array('type' => 'string', 'length' => 255, 'null' => false)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('parent_id' => null, 'lft' => 1,  'rght' => 2, 'name' => 'One'),
+		array('parent_id' => null, 'lft' => 3,  'rght' => 4, 'name' => 'Two'),
+		array('parent_id' => null, 'lft' => 5,  'rght' => 6, 'name' => 'Three'),
+		array('parent_id' => null, 'lft' => 7, 'rght' => 12, 'name' => 'Four'),
+		array('parent_id' => null, 'lft' => 8,  'rght' => 9, 'name' => 'Five'),
+		array('parent_id' => null, 'lft' => 10, 'rght' => 11, 'name' => 'Six'),
+		array('parent_id' => null, 'lft' => 13, 'rght' => 14, 'name' => 'Seven')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/another_article_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/another_article_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/another_article_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class AnotherArticleFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'AnotherArticle'
+ * @access public
+ */
+	var $name = 'AnotherArticle';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'title' => array('type' => 'string', 'null' => false),
+		'created' => 'datetime',
+		'updated' => 'datetime'
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('title' => 'First Article', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'),
+		array('title' => 'Second Article', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'),
+		array('title' => 'Third Article', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/apple_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/apple_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/apple_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,69 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class AppleFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Apple'
+ * @access public
+ */
+	var $name = 'Apple';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'apple_id' => array('type' => 'integer', 'null' => true),
+		'color' => array('type' => 'string', 'length' => 40, 'null' => false),
+		'name' => array('type' => 'string', 'length' => 40, 'null' => false),
+		'created' => 'datetime',
+		'date' => 'date',
+		'modified' => 'datetime',
+		'mytime' => 'time'
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('apple_id' => 2, 'color' => 'Red 1', 'name' => 'Red Apple 1', 'created' => '2006-11-22 10:38:58', 'date' => '1951-01-04', 'modified' => '2006-12-01 13:31:26', 'mytime' => '22:57:17'),
+		array('apple_id' => 1, 'color' => 'Bright Red 1', 'name' => 'Bright Red Apple', 'created' => '2006-11-22 10:43:13', 'date' => '2014-01-01', 'modified' => '2006-11-30 18:38:10', 'mytime' => '22:57:17'),
+		array('apple_id' => 2, 'color' => 'blue green', 'name' => 'green blue', 'created' => '2006-12-25 05:13:36', 'date' => '2006-12-25', 'modified' => '2006-12-25 05:23:24', 'mytime' => '22:57:17'),
+		array('apple_id' => 2, 'color' => 'Blue Green', 'name' => 'Test Name', 'created' => '2006-12-25 05:23:36', 'date' => '2006-12-25', 'modified' => '2006-12-25 05:23:36', 'mytime' => '22:57:17'),
+		array('apple_id' => 5, 'color' => 'Green', 'name' => 'Blue Green', 'created' => '2006-12-25 05:24:06', 'date' => '2006-12-25', 'modified' => '2006-12-25 05:29:16', 'mytime' => '22:57:17'),
+		array('apple_id' => 4, 'color' => 'My new appleOrange', 'name' => 'My new apple', 'created' => '2006-12-25 05:29:39', 'date' => '2006-12-25', 'modified' => '2006-12-25 05:29:39', 'mytime' => '22:57:17'),
+		array('apple_id' => 6, 'color' => 'Some wierd color', 'name' => 'Some odd color', 'created' => '2006-12-25 05:34:21', 'date' => '2006-12-25', 'modified' => '2006-12-25 05:34:21', 'mytime' => '22:57:17')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/aro_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/aro_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/aro_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,65 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class AroFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Aro'
+ * @access public
+ */
+	var $name = 'Aro';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'parent_id' => array('type' => 'integer', 'length' => 10, 'null' => true),
+		'model' => array('type' => 'string', 'null' => true),
+		'foreign_key' => array('type' => 'integer', 'length' => 10, 'null' => true),
+		'alias' => array('type' => 'string', 'default' => ''),
+		'lft' => array('type' => 'integer', 'length' => 10, 'null' => true),
+		'rght' => array('type' => 'integer', 'length' => 10, 'null' => true)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('parent_id' => null, 'model' => null, 'foreign_key' => null, 'alias' => 'ROOT', 'lft' => 1, 'rght' => 8),
+		array('parent_id' => '1', 'model' => 'Group', 'foreign_key' => '1', 'alias' => 'admins', 'lft' => 2, 'rght' => 7),
+		array('parent_id' => '2', 'model' => 'AuthUser', 'foreign_key' => '1', 'alias' => 'Gandalf', 'lft' => 3, 'rght' => 4),
+		array('parent_id' => '2', 'model' => 'AuthUser', 'foreign_key' => '2', 'alias' => 'Elrond', 'lft' => 5, 'rght' => 6)
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/aro_two_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/aro_two_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/aro_two_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,71 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class AroTwoFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'AroTwo'
+ * @access public
+ */
+	var $name = 'AroTwo';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'parent_id' => array('type' => 'integer', 'length' => 10, 'null' => true),
+		'model' => array('type' => 'string', 'null' => true),
+		'foreign_key' => array('type' => 'integer', 'length' => 10, 'null' => true),
+		'alias' => array('type' => 'string', 'default' => ''),
+		'lft' => array('type' => 'integer', 'length' => 10, 'null' => true),
+		'rght' => array('type' => 'integer', 'length' => 10, 'null' => true)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('id' => 1, 'parent_id' => null, 'model' => null, 'foreign_key' => null, 'alias' => 'root', 	'lft' => '1',  'rght' => '20'),
+		array('id' => 2, 'parent_id' => 1, 'model' => 'Group', 'foreign_key' => '1', 'alias' => 'admin', 	'lft' => '2',   'rght' => '5'),
+		array('id' => 3, 'parent_id' => 1, 'model' => 'Group', 'foreign_key' => '2', 'alias' => 'managers', 'lft' => '6',  'rght' => '9'),
+		array('id' => 4, 'parent_id' => 1, 'model' => 'Group', 'foreign_key' => '3', 'alias' => 'users',    'lft' => '10', 'rght' => '19'),
+		array('id' => 5, 'parent_id' => 2, 'model' => 'User',  'foreign_key' => '1', 'alias' => 'Bobs',      'lft' => '3',  'rght' => '4' ),
+		array('id' => 6, 'parent_id' => 3, 'model' => 'User',  'foreign_key' => '2', 'alias' => 'Lumbergh',  'lft' => '7' ,  'rght' => '8'),
+		array('id' => 7, 'parent_id' => 4, 'model' => 'User',  'foreign_key' => '3', 'alias' => 'Samir',     'lft' => '11' ,  'rght' => '12'),
+		array('id' => 8, 'parent_id' => 4, 'model' => 'User',  'foreign_key' => '4', 'alias' => 'Micheal',   'lft' => '13',  'rght' => '14'),
+		array('id' => 9, 'parent_id' => 4, 'model' => 'User',  'foreign_key' => '5', 'alias' => 'Peter',     'lft' => '15',  'rght' => '16'),
+		array('id' => 10, 'parent_id' => 4, 'model' => 'User',  'foreign_key' => '6', 'alias' => 'Milton',   'lft' => '17',  'rght' => '18'),
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/aros_aco_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/aros_aco_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/aros_aco_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class ArosAcoFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'ArosAco'
+ * @access public
+ */
+	var $name = 'ArosAco';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'aro_id' => array('type' => 'integer', 'length' => 10, 'null' => false),
+		'aco_id' => array('type' => 'integer', 'length' => 10, 'null' => false),
+		'_create' => array('type' => 'string', 'length' => 2, 'default' => 0),
+		'_read' => array('type' => 'string', 'length' => 2, 'default' => 0),
+		'_update' => array('type' => 'string', 'length' => 2, 'default' => 0),
+		'_delete' => array('type' => 'string', 'length' => 2, 'default' => 0)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array();
+}

Added: trunk/src/Web/cake/tests/fixtures/aros_aco_two_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/aros_aco_two_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/aros_aco_two_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,81 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class ArosAcoTwoFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'ArosAcoTwo'
+ * @access public
+ */
+	var $name = 'ArosAcoTwo';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'aro_id' => array('type' => 'integer', 'length' => 10, 'null' => false),
+		'aco_id' => array('type' => 'integer', 'length' => 10, 'null' => false),
+		'_create' => array('type' => 'string', 'length' => 2, 'default' => 0),
+		'_read' => array('type' => 'string', 'length' => 2, 'default' => 0),
+		'_update' => array('type' => 'string', 'length' => 2, 'default' => 0),
+		'_delete' => array('type' => 'string', 'length' => 2, 'default' => 0)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('id' => 1, 'aro_id' => '1', 'aco_id' => '1', '_create' => '-1',  '_read' => '-1', '_update' => '-1', '_delete' => '-1'),
+		array('id' => 2, 'aro_id' => '2', 'aco_id' => '1', '_create' => '0',  '_read' => '1', '_update' => '1', '_delete' => '1'),
+		array('id' => 3, 'aro_id' => '3', 'aco_id' => '2', '_create' => '0',  '_read' => '1', '_update' => '0', '_delete' => '0'),
+		array('id' => 4, 'aro_id' => '4', 'aco_id' => '2', '_create' => '1',  '_read' => '1', '_update' => '0', '_delete' => '-1'),
+		array('id' => 5, 'aro_id' => '4', 'aco_id' => '6', '_create' => '1',  '_read' => '1', '_update' => '0', '_delete' => '0'),
+		array('id' => 6, 'aro_id' => '5', 'aco_id' => '1', '_create' => '1',  '_read' => '1', '_update' => '1', '_delete' => '1'),
+		array('id' => 7, 'aro_id' => '6', 'aco_id' => '3', '_create' => '-1',  '_read' => '1', '_update' => '-1', '_delete' => '-1'),
+		array('id' => 8, 'aro_id' => '6', 'aco_id' => '4', '_create' => '-1',  '_read' => '1', '_update' => '-1', '_delete' => '1'),
+		array('id' => 9, 'aro_id' => '6', 'aco_id' => '6', '_create' => '-1',  '_read' => '1', '_update' => '1', '_delete' => '-1'),
+		array('id' => 10, 'aro_id' => '7', 'aco_id' => '2', '_create' => '-1',  '_read' => '-1', '_update' => '-1', '_delete' => '-1'),
+		array('id' => 11, 'aro_id' => '7', 'aco_id' => '7', '_create' => '1',  '_read' => '1', '_update' => '1', '_delete' => '0'),
+		array('id' => 12, 'aro_id' => '7', 'aco_id' => '8', '_create' => '1',  '_read' => '1', '_update' => '1', '_delete' => '0'),
+		array('id' => 13, 'aro_id' => '7', 'aco_id' => '9', '_create' => '1',  '_read' => '1', '_update' => '1', '_delete' => '1'),
+		array('id' => 14, 'aro_id' => '7', 'aco_id' => '10', '_create' => '0',  '_read' => '0', '_update' => '0', '_delete' => '1'),
+		array('id' => 15, 'aro_id' => '8', 'aco_id' => '10', '_create' => '1',  '_read' => '1', '_update' => '1', '_delete' => '1'),
+		array('id' => 16, 'aro_id' => '8', 'aco_id' => '2', '_create' => '-1',  '_read' => '-1', '_update' => '-1', '_delete' => '-1'),
+		array('id' => 17, 'aro_id' => '9', 'aco_id' => '4', '_create' => '1',  '_read' => '1', '_update' => '1', '_delete' => '-1'),
+		array('id' => 18, 'aro_id' => '9', 'aco_id' => '9', '_create' => '0',  '_read' => '0', '_update' => '1', '_delete' => '1'),
+		array('id' => 19, 'aro_id' => '10', 'aco_id' => '9', '_create' => '1',  '_read' => '1', '_update' => '1', '_delete' => '1'),
+		array('id' => 20, 'aro_id' => '10', 'aco_id' => '10', '_create' => '-1',  '_read' => '-1', '_update' => '-1', '_delete' => '-1'),
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/article_featured_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/article_featured_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/article_featured_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,64 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class ArticleFeaturedFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'ArticleFeatured'
+ * @access public
+ */
+	var $name = 'ArticleFeatured';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'user_id' => array('type' => 'integer', 'null' => false),
+		'title' => array('type' => 'string', 'null' => false),
+		'body' => 'text',
+		'published' => array('type' => 'string', 'length' => 1, 'default' => 'N'),
+		'created' => 'datetime',
+		'updated' => 'datetime'
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'),
+		array('user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'),
+		array('user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/article_featureds_tags_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/article_featureds_tags_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/article_featureds_tags_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class ArticleFeaturedsTagsFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'ArticleFeaturedsTags'
+ * @access public
+ */
+	var $name = 'ArticleFeaturedsTags';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'article_featured_id' => array('type' => 'integer', 'null' => false),
+		'tag_id' => array('type' => 'integer', 'null' => false),
+		'indexes' => array('UNIQUE_FEATURED' => array('column'=> array('article_featured_id', 'tag_id'), 'unique'=> 1))
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/article_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/article_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/article_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,64 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class ArticleFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Article'
+ * @access public
+ */
+	var $name = 'Article';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'user_id' => array('type' => 'integer', 'null' => false),
+		'title' => array('type' => 'string', 'null' => false),
+		'body' => 'text',
+		'published' => array('type' => 'string', 'length' => 1, 'default' => 'N'),
+		'created' => 'datetime',
+		'updated' => 'datetime'
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('user_id' => 1, 'title' => 'First Article', 'body' => 'First Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'),
+		array('user_id' => 3, 'title' => 'Second Article', 'body' => 'Second Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'),
+		array('user_id' => 1, 'title' => 'Third Article', 'body' => 'Third Article Body', 'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/articles_tag_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/articles_tag_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/articles_tag_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class ArticlesTagFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'ArticlesTag'
+ * @access public
+ */
+	var $name = 'ArticlesTag';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'article_id' => array('type' => 'integer', 'null' => false),
+		'tag_id' => array('type' => 'integer', 'null' => false),
+		'indexes' => array('UNIQUE_TAG' => array('column'=> array('article_id', 'tag_id'), 'unique'=>1))
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('article_id' => 1, 'tag_id' => 1),
+		array('article_id' => 1, 'tag_id' => 2),
+		array('article_id' => 2, 'tag_id' => 1),
+		array('article_id' => 2, 'tag_id' => 3)
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/attachment_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/attachment_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/attachment_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class AttachmentFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Attachment'
+ * @access public
+ */
+	var $name = 'Attachment';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'comment_id' => array('type' => 'integer', 'null' => false),
+		'attachment' => array('type' => 'string', 'null' => false),
+		'created' => 'datetime',
+		'updated' => 'datetime'
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('comment_id' => 5, 'attachment' => 'attachment.zip',  'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/auth_user_custom_field_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/auth_user_custom_field_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/auth_user_custom_field_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,65 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.1.8013
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class AuthUserCustomFieldFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'AuthUser'
+ * @access public
+ */
+	var $name = 'AuthUserCustomField';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'email' => array('type' => 'string', 'null' => false),
+		'password' => array('type' => 'string', 'null' => false),
+		'created' => 'datetime',
+		'updated' => 'datetime'
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('email' => 'maria****@examp*****', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'),
+		array('email' => 'nate****@examp*****', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:18:23', 'updated' => '2007-03-17 01:20:31'),
+		array('email' => 'larry****@examp*****', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31'),
+		array('email' => 'garre****@examp*****', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:22:23', 'updated' => '2007-03-17 01:24:31'),
+		array('email' => 'chart****@examp*****', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:22:23', 'updated' => '2007-03-17 01:24:31'),
+
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/auth_user_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/auth_user_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/auth_user_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,65 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class AuthUserFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'AuthUser'
+ * @access public
+ */
+	var $name = 'AuthUser';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'username' => array('type' => 'string', 'null' => false),
+		'password' => array('type' => 'string', 'null' => false),
+		'created' => 'datetime',
+		'updated' => 'datetime'
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('username' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'),
+		array('username' => 'nate', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:18:23', 'updated' => '2007-03-17 01:20:31'),
+		array('username' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31'),
+		array('username' => 'garrett', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:22:23', 'updated' => '2007-03-17 01:24:31'),
+		array('username' => 'chartjes', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:22:23', 'updated' => '2007-03-17 01:24:31'),
+
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/author_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/author_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/author_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,63 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class AuthorFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Author'
+ * @access public
+ */
+	var $name = 'Author';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'user' => array('type' => 'string', 'default' => null),
+		'password' => array('type' => 'string', 'default' => null),
+		'created' => 'datetime',
+		'updated' => 'datetime'
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'),
+		array('user' => 'nate', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:18:23', 'updated' => '2007-03-17 01:20:31'),
+		array('user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31'),
+		array('user' => 'garrett', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:22:23', 'updated' => '2007-03-17 01:24:31'),
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/basket_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/basket_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/basket_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class BasketFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Basket'
+ * @access public
+ */
+	var $name = 'Basket';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'type' => array('type' => 'string', 'length' => 255),
+		'name' => array('type' => 'string', 'length' => 255),
+		'object_id' => array('type' => 'integer'),
+		'user_id' => array('type' => 'integer'),
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('id' => 1, 'type' => 'nonfile', 'name' => 'basket1', 'object_id' => 1, 'user_id' => 1),
+		array('id' => 2, 'type' => 'file', 'name' => 'basket2', 'object_id' => 2, 'user_id' => 1),
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/bid_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/bid_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/bid_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,62 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class BidFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Bid'
+ * @access public
+ */
+	var $name = 'Bid';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'message_id' => array('type' => 'integer', 'null' => false),
+		'name' => array('type' => 'string', 'null' => false)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('message_id' => 1, 'name' => 'Bid 1.1'),
+		array('message_id' => 1, 'name' => 'Bid 1.2'),
+		array('message_id' => 3, 'name' => 'Bid 3.1'),
+		array('message_id' => 2, 'name' => 'Bid 2.1'),
+		array('message_id' => 2, 'name' => 'Bid 2.2')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/bidding_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/bidding_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/bidding_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.3.14
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class BiddingFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Bidding'
+ * @access public
+ */
+	var $name = 'Bidding';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'bid' => array('type' => 'string', 'null' => false),
+		'name' => array('type' => 'string', 'null' => false)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('bid' => 'One', 'name' => 'Bid 1'),
+		array('bid' => 'Two', 'name' => 'Bid 2'),
+		array('bid' => 'Three', 'name' => 'Bid 3'),
+		array('bid' => 'Five', 'name' => 'Bid 5')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/bidding_message_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/bidding_message_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/bidding_message_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.3.14
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class BiddingMessageFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'BiddingMessage'
+ * @access public
+ */
+	var $name = 'BiddingMessage';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'bidding' => array('type' => 'string', 'null' => false, 'key' => 'primary'),
+		'name' => array('type' => 'string', 'null' => false)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('bidding' => 'One', 'name' => 'Message 1'),
+		array('bidding' => 'Two', 'name' => 'Message 2'),
+		array('bidding' => 'Three', 'name' => 'Message 3'),
+		array('bidding' => 'Four', 'name' => 'Message 4')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/binary_test_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/binary_test_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/binary_test_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,55 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.6700
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class BinaryTestFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'BinaryTest'
+ * @access public
+ */
+	var $name = 'BinaryTest';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'data' => 'binary'
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array();
+}

Added: trunk/src/Web/cake/tests/fixtures/book_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/book_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/book_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.7198
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class BookFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Book'
+ * @access public
+ */
+	var $name = 'Book';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'isbn' => array('type' => 'string', 'length' => 13),
+		'title' => array('type' => 'string', 'length' =>  255),
+		'author' => array('type' => 'string', 'length' => 255),
+		'year' => array('type' => 'integer', 'null' => true),
+		'pages' => array('type' => 'integer', 'null' => true)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('id' => 1, 'isbn' => '1234567890', 'title' => 'Faust', 'author' => 'Johann Wolfgang von Goethe')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/cache_test_model_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/cache_test_model_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/cache_test_model_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class CacheTestModelFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'CacheTestModel'
+ * @access public
+ */
+	var $name = 'CacheTestModel';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id'		=> array('type' => 'string', 'length' => 255, 'key' => 'primary'),
+		'data'		=> array('type' => 'string', 'length' => 255, 'default' => ''),
+		'expires'	=> array('type' => 'integer', 'length' => 10, 'default' => '0'),
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/callback_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/callback_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/callback_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,62 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class CallbackFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Callback'
+ * @access public
+ */
+	var $name = 'Callback';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'user' => array('type' => 'string', 'null' => false),
+		'password' => array('type' => 'string', 'null' => false),
+		'created' => 'datetime',
+		'updated' => 'datetime'
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('user' => 'user1', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:18:23', 'updated' => '2007-03-17 01:20:31'),
+		array('user' => 'user2', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31'),
+		array('user' => 'user3', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:22:23', 'updated' => '2007-03-17 01:24:31'),
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/campaign_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/campaign_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/campaign_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Short description for campaign_fixture.php
+ *
+ * Long description for campaign_fixture.php
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * @link          http://www.cakephp.org
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         1.2
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * CampaignFixture class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class CampaignFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Campaign'
+ * @access public
+ */
+	var $name = 'Campaign';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'name' => array('type' => 'string', 'length' => 255, 'null' => false),
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('name' => 'Hurtigruten'),
+		array('name' => 'Colorline'),
+		array('name' => 'Queen of Scandinavia')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/category_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/category_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/category_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,67 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class CategoryFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Category'
+ * @access public
+ */
+	var $name = 'Category';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'parent_id' => array('type' => 'integer', 'null' => false),
+		'name' => array('type' => 'string', 'null' => false),
+		'created' => 'datetime',
+		'updated' => 'datetime'
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('parent_id' => 0, 'name' => 'Category 1', 'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'),
+		array('parent_id' => 1, 'name' => 'Category 1.1', 'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'),
+		array('parent_id' => 1, 'name' => 'Category 1.2', 'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'),
+		array('parent_id' => 0, 'name' => 'Category 2', 'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'),
+		array('parent_id' => 0, 'name' => 'Category 3', 'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'),
+		array('parent_id' => 5, 'name' => 'Category 3.1', 'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'),
+		array('parent_id' => 2, 'name' => 'Category 1.1.1', 'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'),
+		array('parent_id' => 2, 'name' => 'Category 1.1.2', 'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'),
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/category_thread_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/category_thread_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/category_thread_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,66 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class CategoryThreadFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'CategoryThread'
+ * @access public
+ */
+	var $name = 'CategoryThread';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'parent_id' => array('type' => 'integer', 'null' => false),
+		'name' => array('type' => 'string', 'null' => false),
+		'created' => 'datetime',
+		'updated' => 'datetime'
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('parent_id' => 0, 'name' => 'Category 1', 'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'),
+		array('parent_id' => 1, 'name' => 'Category 1.1', 'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'),
+		array('parent_id' => 2, 'name' => 'Category 1.1.1', 'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'),
+		array('parent_id' => 3, 'name' => 'Category 1.1.2', 'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'),
+		array('parent_id' => 4, 'name' => 'Category 1.1.1.1', 'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'),
+		array('parent_id' => 5, 'name' => 'Category 2', 'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31'),
+		array('parent_id' => 6, 'name' => 'Category 2.1', 'created' => '2007-03-18 15:30:23', 'updated' => '2007-03-18 15:32:31')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/cd_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/cd_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/cd_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,59 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.7198
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class CdFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Cd'
+ * @access public
+ */
+	var $name = 'Cd';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'title' => array('type' => 'string', 'length' =>  255),
+		'artist' => array('type' => 'string', 'length' => 255, 'null' => true),
+		'genre' => array('type' => 'string', 'length' => 255, 'null' => true)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('id' => 1, 'title' => 'Grace', 'artist' => 'Jeff Buckley', 'genre' => 'awesome')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/comment_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/comment_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/comment_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,67 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class CommentFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Comment'
+ * @access public
+ */
+	var $name = 'Comment';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'article_id' => array('type' => 'integer', 'null'=>false),
+		'user_id' => array('type' => 'integer', 'null'=>false),
+		'comment' => 'text',
+		'published' => array('type' => 'string', 'length' => 1, 'default' => 'N'),
+		'created' => 'datetime',
+		'updated' => 'datetime'
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('article_id' => 1, 'user_id' => 2, 'comment' => 'First Comment for First Article', 'published' => 'Y', 'created' => '2007-03-18 10:45:23', 'updated' => '2007-03-18 10:47:31'),
+		array('article_id' => 1, 'user_id' => 4, 'comment' => 'Second Comment for First Article', 'published' => 'Y', 'created' => '2007-03-18 10:47:23', 'updated' => '2007-03-18 10:49:31'),
+		array('article_id' => 1, 'user_id' => 1, 'comment' => 'Third Comment for First Article', 'published' => 'Y', 'created' => '2007-03-18 10:49:23', 'updated' => '2007-03-18 10:51:31'),
+		array('article_id' => 1, 'user_id' => 1, 'comment' => 'Fourth Comment for First Article', 'published' => 'N', 'created' => '2007-03-18 10:51:23', 'updated' => '2007-03-18 10:53:31'),
+		array('article_id' => 2, 'user_id' => 1, 'comment' => 'First Comment for Second Article', 'published' => 'Y', 'created' => '2007-03-18 10:53:23', 'updated' => '2007-03-18 10:55:31'),
+		array('article_id' => 2, 'user_id' => 2, 'comment' => 'Second Comment for Second Article', 'published' => 'Y', 'created' => '2007-03-18 10:55:23', 'updated' => '2007-03-18 10:57:31')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/content_account_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/content_account_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/content_account_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,64 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class ContentAccountFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Aco'
+ * @access public
+ */
+	var $name = 'ContentAccount';
+	var $table = 'ContentAccounts';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'iContentAccountsId' => array('type' => 'integer', 'key' => 'primary'),
+		'iContentId'		=> array('type' => 'integer'),
+		'iAccountId'		=> array('type' => 'integer')
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('iContentId' => 1, 'iAccountId' => 1),
+		array('iContentId' => 2, 'iAccountId' => 2),
+		array('iContentId' => 3, 'iAccountId' => 3),
+		array('iContentId' => 4, 'iAccountId' => 4),
+		array('iContentId' => 1, 'iAccountId' => 2),
+		array('iContentId' => 2, 'iAccountId' => 3),
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/content_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/content_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/content_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class ContentFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Aco'
+ * @access public
+ */
+	var $name = 'Content';
+	var $table = 'Content';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'iContentId'		=> array('type' => 'integer', 'key' => 'primary'),
+		'cDescription'	=> array('type' => 'string', 'length' => 50, 'null' => true)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('cDescription' => 'Test Content 1'),
+		array('cDescription' => 'Test Content 2'),
+		array('cDescription' => 'Test Content 3'),
+		array('cDescription' => 'Test Content 4')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/counter_cache_post_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/counter_cache_post_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/counter_cache_post_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,42 @@
+<?php
+/**
+ * Counter Cache Test Fixtures
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class CounterCachePostFixture extends CakeTestFixture {
+
+	var $name = 'CounterCachePost';
+
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'title' => array('type' => 'string', 'length' => 255, 'null' => false),
+		'user_id' => array('type' => 'integer', 'null' => true),
+	);
+
+    var $records = array(
+		array('id' => 1, 'title' => 'Rock and Roll',  'user_id' => 66),
+		array('id' => 2, 'title' => 'Music',   'user_id' => 66),
+		array('id' => 3, 'title' => 'Food',   'user_id' => 301),
+    );
+}

Added: trunk/src/Web/cake/tests/fixtures/counter_cache_post_nonstandard_primary_key_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/counter_cache_post_nonstandard_primary_key_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/counter_cache_post_nonstandard_primary_key_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,42 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class CounterCachePostNonstandardPrimaryKeyFixture extends CakeTestFixture {
+
+	var $name = 'CounterCachePostNonstandardPrimaryKey';
+
+	var $fields = array(
+		'pid' => array('type' => 'integer', 'key' => 'primary'),
+		'title' => array('type' => 'string', 'length' => 255, 'null' => false),
+		'uid' => array('type' => 'integer', 'null' => true),
+	);
+
+    var $records = array(
+		array('pid' => 1, 'title' => 'Rock and Roll',  'uid' => 66),
+		array('pid' => 2, 'title' => 'Music',   'uid' => 66),
+		array('pid' => 3, 'title' => 'Food',   'uid' => 301),
+    );
+}

Added: trunk/src/Web/cake/tests/fixtures/counter_cache_user_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/counter_cache_user_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/counter_cache_user_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,41 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class CounterCacheUserFixture extends CakeTestFixture {
+
+	var $name = 'CounterCacheUser';
+
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'name' => array('type' => 'string', 'length' => 255, 'null' => false),
+		'post_count' => array('type' => 'integer', 'null' => true)
+	);
+
+	var $records = array(
+		array('id' => 66, 'name' => 'Alexander','post_count' => 2),
+		array('id' => 301, 'name' => 'Steven','post_count' => 1),
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/counter_cache_user_nonstandard_primary_key_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/counter_cache_user_nonstandard_primary_key_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/counter_cache_user_nonstandard_primary_key_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,41 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class CounterCacheUserNonstandardPrimaryKeyFixture extends CakeTestFixture {
+
+	var $name = 'CounterCacheUserNonstandardPrimaryKey';
+
+	var $fields = array(
+		'uid' => array('type' => 'integer', 'key' => 'primary'),
+		'name' => array('type' => 'string', 'length' => 255, 'null' => false),
+		'post_count' => array('type' => 'integer', 'null' => true)
+	);
+
+	var $records = array(
+		array('uid' => 66, 'name' => 'Alexander','post_count' => 2),
+		array('uid' => 301, 'name' => 'Steven','post_count' => 1),
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/data_test_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/data_test_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/data_test_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,66 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.6700
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class DataTestFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'DataTest'
+ * @access public
+ */
+	var $name = 'DataTest';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'count' => array('type' => 'integer', 'default' => 0),
+		'float' => array('type' => 'float', 'default' => 0),
+		//'timestamp' => array('type' => 'timestamp', 'default' => null, 'null' => true),
+		'created' => array('type' => 'datetime', 'default' => null),
+		'updated' => array('type' => 'datetime', 'default' => null)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array(
+			'count' => 2,
+			'float' => 2.4,
+			'created' => '2010-09-06 12:28:00',
+			'updated' => '2010-09-06 12:28:00'
+		)
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/datatype_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/datatype_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/datatype_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.7026
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class DatatypeFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Datatype'
+ * @access public
+ */
+	var $name = 'Datatype';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'null'=> false, 'default'=> 0, 'key' => 'primary'),
+		'float_field' => array('type' => 'float', 'length' => '5,2', 'null' => false, 'default' => null),
+		'bool' => array('type' => 'boolean', 'null' => false, 'default' => false),
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('id' => 1, 'float_field' => 42.23, 'bool' => false),
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/dependency_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/dependency_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/dependency_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.6879//Correct version number as needed**
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Short description for file.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.6879//Correct version number as needed**
+ */
+class DependencyFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Dependency'
+ * @access public
+ */
+	var $name = 'Dependency';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'child_id' => 'integer',
+		'parent_id' => 'integer'
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('child_id' => 1, 'parent_id' => 2),
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/device_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/device_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/device_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class DeviceFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Device'
+ * @access public
+ */
+	var $name = 'Device';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'device_type_id' => array('type' => 'integer', 'null' => false),
+		'name' => array('type' => 'string', 'null' => false),
+		'typ' => array('type' => 'integer', 'null' => false),
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('device_type_id' => 1, 'name' => 'Device 1', 'typ' => 1),
+		array('device_type_id' => 1, 'name' => 'Device 2', 'typ' => 1),
+		array('device_type_id' => 1, 'name' => 'Device 3', 'typ' => 2)
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/device_type_category_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/device_type_category_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/device_type_category_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,57 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class DeviceTypeCategoryFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'DeviceTypeCategory'
+ * @access public
+ */
+	var $name = 'DeviceTypeCategory';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'name' => array('type' => 'string', 'null' => false)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('name' => 'DeviceTypeCategory 1')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/device_type_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/device_type_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/device_type_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,64 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class DeviceTypeFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'DeviceType'
+ * @access public
+ */
+	var $name = 'DeviceType';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'device_type_category_id' => array('type' => 'integer', 'null' => false),
+		'feature_set_id' => array('type' => 'integer', 'null' => false),
+		'exterior_type_category_id' => array('type' => 'integer', 'null' => false),
+		'image_id' => array('type' => 'integer', 'null' => false),
+		'extra1_id' => array('type' => 'integer', 'null' => false),
+		'extra2_id' => array('type' => 'integer', 'null' => false),
+		'name' => array('type' => 'string', 'null' => false),
+		'order' => array('type' => 'integer', 'null' => false)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('device_type_category_id' => 1, 'feature_set_id' => 1, 'exterior_type_category_id' => 1, 'image_id' => 1, 'extra1_id' => 1, 'extra2_id' => 1, 'name' => 'DeviceType 1', 'order' => 0)
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/document_directory_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/document_directory_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/document_directory_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,57 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class DocumentDirectoryFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'DocumentDirectory'
+ * @access public
+ */
+	var $name = 'DocumentDirectory';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'name' => array('type' => 'string', 'null' => false)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('name' => 'DocumentDirectory 1')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/document_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/document_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/document_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class DocumentFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Document'
+ * @access public
+ */
+	var $name = 'Document';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'document_directory_id' => array('type' => 'integer', 'null' => false),
+		'name' => array('type' => 'string', 'null' => false)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('document_directory_id' => 1, 'name' => 'Document 1')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/exterior_type_category_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/exterior_type_category_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/exterior_type_category_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class ExteriorTypeCategoryFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'ExteriorTypeCategory'
+ * @access public
+ */
+	var $name = 'ExteriorTypeCategory';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'image_id' => array('type' => 'integer', 'null' => false),
+		'name' => array('type' => 'string', 'null' => false)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('image_id' => 1, 'name' => 'ExteriorTypeCategory 1')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/feature_set_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/feature_set_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/feature_set_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,57 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class FeatureSetFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'FeatureSet'
+ * @access public
+ */
+	var $name = 'FeatureSet';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'name' => array('type' => 'string', 'null' => false)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('name' => 'FeatureSet 1')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/featured_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/featured_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/featured_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,63 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class FeaturedFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Featured'
+ * @access public
+ */
+	var $name = 'Featured';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'article_featured_id' => array('type' => 'integer', 'null' => false),
+		'category_id' => array('type' => 'integer', 'null' => false),
+		'published_date' => 'datetime',
+		'end_date' => 'datetime',
+		'created' => 'datetime',
+		'updated' => 'datetime'
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('article_featured_id' => 1, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23', 'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'),
+		array('article_featured_id' => 2, 'category_id' => 1, 'published_date' => '2007-03-31 10:39:23', 'end_date' => '2007-05-15 10:39:23', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'),
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/film_file_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/film_file_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/film_file_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class FilmFileFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'FilmFile'
+ * @access public
+ */
+	var $name = 'FilmFile';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'name' => array('type' => 'string', 'length' => 255)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('id' => 1, 'name' => 'one'),
+		array('id' => 2, 'name' => 'two')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/flag_tree_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/flag_tree_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/flag_tree_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,55 @@
+<?php
+/**
+ * Tree behavior class test fixture.
+ *
+ * Enables a model object to act as a node-based tree.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.5331
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Flag Tree Test Fixture
+ *
+ * Like Number Tree, but uses a flag for testing scope parameters
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class FlagTreeFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'FlagTree'
+ * @access public
+ */
+	var $name = 'FlagTree';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id'	=> array('type' => 'integer','key' => 'primary'),
+		'name'	=> array('type' => 'string','null' => false),
+		'parent_id' => 'integer',
+		'lft'	=> array('type' => 'integer','null' => false),
+		'rght'	=> array('type' => 'integer','null' => false),
+		'flag'	=> array('type' => 'integer','null' => false, 'length' => 1, 'default' => 0)
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/fruit_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/fruit_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/fruit_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,63 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.7953
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class FruitFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Fruit'
+ * @access public
+ */
+	var $name = 'Fruit';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'string', 'length' => 36, 'key' => 'primary'),
+		'name' => array('type' => 'string', 'length' => 255),
+		'color' => array('type' => 'string', 'length' => 13),
+		'shape' => array('type' => 'string', 'length' =>  255),
+		'taste' => array('type' => 'string', 'length' => 255)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array(
+			'id' => '481fc6d0-b920-43e0-a40d-6d1740cf8569', 'name' => 'Orange',
+			'color' => 'orange', 'shape' => 'Spherical', 'taste' => 'Tangy & Sweet'
+		)
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/fruits_uuid_tag_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/fruits_uuid_tag_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/fruits_uuid_tag_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.7953
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class FruitsUuidTagFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'FruitsUuidTag'
+ * @access public
+ */
+	var $name = 'FruitsUuidTag';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'fruit_id' => array('type' => 'string', 'null' => false, 'length' => 36, 'key' => 'primary'),
+		'uuid_tag_id' => array('type' => 'string', 'null' => false, 'length' => 36, 'key' => 'primary'),
+		'indexes' => array(
+			'unique_fruits_tags' => array('unique' => true, 'column' => array('fruit_id', 'uuid_tag_id')),
+		),
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('fruit_id' => '481fc6d0-b920-43e0-a40d-6d1740cf8569', 'uuid_tag_id' => '481fc6d0-b920-43e0-e50f-6d1740cf8569')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/group_update_all_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/group_update_all_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/group_update_all_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,55 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class GroupUpdateAllFixture extends CakeTestFixture {
+    var $name = 'GroupUpdateAll';
+    var $table = 'group_update_all';
+
+    var $fields = array(
+            'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'),
+            'name' => array('type'=>'string', 'null' => false, 'length' => 29),
+            'code' => array('type'=>'integer', 'null' => false, 'length' => 4),
+            'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1))
+            );
+    var $records = array(
+        array(
+            'id'  => 1,
+            'name'  => 'group one',
+            'code'  => 120),
+        array(
+            'id'  => 2,
+            'name'  => 'group two',
+            'code'  => 125),
+        array(
+            'id'  => 3,
+            'name'  => 'group three',
+            'code'  => 130),
+        array(
+            'id'  => 4,
+            'name'  => 'group four',
+            'code'  => 135)
+        );
+}

Added: trunk/src/Web/cake/tests/fixtures/home_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/home_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/home_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,62 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class HomeFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Home'
+ * @access public
+ */
+	var $name = 'Home';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'another_article_id' => array('type' => 'integer', 'null' => false),
+		'advertisement_id' => array('type' => 'integer', 'null' => false),
+		'title' => array('type' => 'string', 'null' => false),
+		'created' => 'datetime',
+		'updated' => 'datetime'
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('another_article_id' => 1, 'advertisement_id' => 1, 'title' => 'First Home', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'),
+		array('another_article_id' => 3, 'advertisement_id' => 1, 'title' => 'Second Home', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/image_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/image_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/image_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class ImageFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Image'
+ * @access public
+ */
+	var $name = 'Image';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'name' => array('type' => 'string', 'null' => false)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('name' => 'Image 1'),
+		array('name' => 'Image 2'),
+		array('name' => 'Image 3'),
+		array('name' => 'Image 4'),
+		array('name' => 'Image 5')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/item_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/item_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/item_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,64 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class ItemFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Item'
+ * @access public
+ */
+	var $name = 'Item';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'syfile_id' => array('type' => 'integer', 'null' => false),
+		'published' => array('type' => 'boolean', 'null' => false),
+		'name' => array('type' => 'string', 'null' => false)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('syfile_id' => 1, 'published' => 0, 'name' => 'Item 1'),
+		array('syfile_id' => 2, 'published' => 0, 'name' => 'Item 2'),
+		array('syfile_id' => 3, 'published' => 0, 'name' => 'Item 3'),
+		array('syfile_id' => 4, 'published' => 0, 'name' => 'Item 4'),
+		array('syfile_id' => 5, 'published' => 0, 'name' => 'Item 5'),
+		array('syfile_id' => 6, 'published' => 0, 'name' => 'Item 6')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/items_portfolio_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/items_portfolio_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/items_portfolio_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,63 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class ItemsPortfolioFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'ItemsPortfolio'
+ * @access public
+ */
+	var $name = 'ItemsPortfolio';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'item_id' => array('type' => 'integer', 'null' => false),
+		'portfolio_id' => array('type' => 'integer', 'null' => false)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('item_id' => 1, 'portfolio_id' => 1),
+		array('item_id' => 2, 'portfolio_id' => 2),
+		array('item_id' => 3, 'portfolio_id' => 1),
+		array('item_id' => 4, 'portfolio_id' => 1),
+		array('item_id' => 5, 'portfolio_id' => 1),
+		array('item_id' => 6, 'portfolio_id' => 2)
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/join_a_b_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/join_a_b_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/join_a_b_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,63 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.6317
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class JoinABFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'JoinAsJoinB'
+ * @access public
+ */
+	var $name = 'JoinAsJoinB';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'join_a_id' => array('type' => 'integer', 'length' => 10, 'null' => true),
+		'join_b_id' => array('type' => 'integer', 'default' => null),
+		'other' => array('type' => 'string', 'default' => ''),
+		'created' => array('type' => 'datetime', 'null' => true),
+		'updated' => array('type' => 'datetime', 'null' => true)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('join_a_id' => 1, 'join_b_id' => 2, 'other' => 'Data for Join A 1 Join B 2', 'created' => '2008-01-03 10:56:33', 'updated' => '2008-01-03 10:56:33'),
+		array('join_a_id' => 2, 'join_b_id' => 3, 'other' => 'Data for Join A 2 Join B 3', 'created' => '2008-01-03 10:56:34', 'updated' => '2008-01-03 10:56:34'),
+		array('join_a_id' => 3, 'join_b_id' => 1, 'other' => 'Data for Join A 3 Join B 1', 'created' => '2008-01-03 10:56:35', 'updated' => '2008-01-03 10:56:35')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/join_a_c_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/join_a_c_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/join_a_c_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,63 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.6317
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class JoinACFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'JoinAsJoinC'
+ * @access public
+ */
+	var $name = 'JoinAsJoinC';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'join_a_id' => array('type' => 'integer', 'length' => 10, 'null' => true),
+		'join_c_id' => array('type' => 'integer', 'default' => null),
+		'other' => array('type' => 'string', 'default' => ''),
+		'created' => array('type' => 'datetime', 'null' => true),
+		'updated' => array('type' => 'datetime', 'null' => true)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('join_a_id' => 1, 'join_c_id' => 2, 'other' => 'Data for Join A 1 Join C 2', 'created' => '2008-01-03 10:57:22', 'updated' => '2008-01-03 10:57:22'),
+		array('join_a_id' => 2, 'join_c_id' => 3, 'other' => 'Data for Join A 2 Join C 3', 'created' => '2008-01-03 10:57:23', 'updated' => '2008-01-03 10:57:23'),
+		array('join_a_id' => 3, 'join_c_id' => 1, 'other' => 'Data for Join A 3 Join C 1', 'created' => '2008-01-03 10:57:24', 'updated' => '2008-01-03 10:57:24')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/join_a_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/join_a_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/join_a_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,62 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.6317
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class JoinAFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'JoinA'
+ * @access public
+ */
+	var $name = 'JoinA';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'name' => array('type' => 'string', 'default' => ''),
+		'body' => array('type' => 'text'),
+		'created' => array('type' => 'datetime', 'null' => true),
+		'updated' => array('type' => 'datetime', 'null' => true)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('name' => 'Join A 1', 'body' => 'Join A 1 Body', 'created' => '2008-01-03 10:54:23', 'updated' => '2008-01-03 10:54:23'),
+		array('name' => 'Join A 2', 'body' => 'Join A 2 Body', 'created' => '2008-01-03 10:54:24', 'updated' => '2008-01-03 10:54:24'),
+		array('name' => 'Join A 3', 'body' => 'Join A 2 Body', 'created' => '2008-01-03 10:54:25', 'updated' => '2008-01-03 10:54:24')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/join_b_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/join_b_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/join_b_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.6317
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class JoinBFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'JoinB'
+ * @access public
+ */
+	var $name = 'JoinB';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'name' => array('type' => 'string', 'default' => ''),
+		'created' => array('type' => 'datetime', 'null' => true),
+		'updated' => array('type' => 'datetime', 'null' => true)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('name' => 'Join B 1', 'created' => '2008-01-03 10:55:01', 'updated' => '2008-01-03 10:55:01'),
+		array('name' => 'Join B 2', 'created' => '2008-01-03 10:55:02', 'updated' => '2008-01-03 10:55:02'),
+		array('name' => 'Join B 3', 'created' => '2008-01-03 10:55:03', 'updated' => '2008-01-03 10:55:03')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/join_c_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/join_c_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/join_c_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.6317
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class JoinCFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'JoinC'
+ * @access public
+ */
+	var $name = 'JoinC';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'name' => array('type' => 'string', 'default' => ''),
+		'created' => array('type' => 'datetime', 'null' => true),
+		'updated' => array('type' => 'datetime', 'null' => true)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('name' => 'Join C 1', 'created' => '2008-01-03 10:56:11', 'updated' => '2008-01-03 10:56:11'),
+		array('name' => 'Join C 2', 'created' => '2008-01-03 10:56:12', 'updated' => '2008-01-03 10:56:12'),
+		array('name' => 'Join C 3', 'created' => '2008-01-03 10:56:13', 'updated' => '2008-01-03 10:56:13')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/join_thing_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/join_thing_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/join_thing_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,63 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class JoinThingFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'JoinThing'
+ * @access public
+ */
+	var $name = 'JoinThing';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'something_id' => array('type' => 'integer', 'length' => 10, 'null' => true),
+		'something_else_id' => array('type' => 'integer', 'default' => null),
+		'doomed' => array('type' => 'boolean', 'default' => '0'),
+		'created' => array('type' => 'datetime', 'null' => true),
+		'updated' => array('type' => 'datetime', 'null' => true)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('something_id' => 1, 'something_else_id' => 2, 'doomed' => '1', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'),
+		array('something_id' => 2, 'something_else_id' => 3, 'doomed' => '0', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'),
+		array('something_id' => 3, 'something_else_id' => 1, 'doomed' => '1', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/message_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/message_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/message_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class MessageFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Message'
+ * @access public
+ */
+	var $name = 'Message';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'thread_id' => array('type' => 'integer', 'null' => false),
+		'name' => array('type' => 'string', 'null' => false)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('thread_id' => 1, 'name' => 'Thread 1, Message 1'),
+		array('thread_id' => 2, 'name' => 'Thread 2, Message 1'),
+		array('thread_id' => 3, 'name' => 'Thread 3, Message 1')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/my_categories_my_products_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/my_categories_my_products_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/my_categories_my_products_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class MyCategoriesMyProductsFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'MyCategoriesMyProducts'
+ * @access public
+ */
+	var $name = 'MyCategoriesMyProducts';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'my_category_id' => array('type' => 'integer'),
+		'my_product_id' => array('type' => 'integer'),
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('my_category_id' => 1, 'my_product_id' => 1),
+		array('my_category_id' => 2, 'my_product_id' => 1),
+		array('my_category_id' => 2, 'my_product_id' => 2),
+		array('my_category_id' => 3, 'my_product_id' => 2),
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/my_categories_my_users_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/my_categories_my_users_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/my_categories_my_users_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class MyCategoriesMyUsersFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'MyCategoriesMyUsers'
+ * @access public
+ */
+	var $name = 'MyCategoriesMyUsers';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'my_category_id' => array('type' => 'integer'),
+		'my_user_id' => array('type' => 'integer'),
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('my_category_id' => 1, 'my_user_id' => 1),
+		array('my_category_id' => 3, 'my_user_id' => 1),
+		array('my_category_id' => 1, 'my_user_id' => 2),
+		array('my_category_id' => 2, 'my_user_id' => 2),
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/my_category_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/my_category_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/my_category_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,59 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class MyCategoryFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'MyCategory'
+ * @access public
+ */
+	var $name = 'MyCategory';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'name' => array('type' => 'string', 'null' => false),
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('id' => 1, 'name' => 'A'),
+		array('id' => 2, 'name' => 'B'),
+		array('id' => 3, 'name' => 'C'),
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/my_product_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/my_product_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/my_product_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class MyProductFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'MyProduct'
+ * @access public
+ */
+	var $name = 'MyProduct';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'name' => array('type' => 'string', 'null' => false),
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('id' => 1, 'name' => 'book'),
+		array('id' => 2, 'name' => 'computer'),
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/my_user_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/my_user_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/my_user_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class MyUserFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'MyUser'
+ * @access public
+ */
+	var $name = 'MyUser';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'firstname' => array('type' => 'string', 'null' => false),
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('id' => 1, 'firstname' => 'userA'),
+		array('id' => 2, 'firstname' => 'userB')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/node_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/node_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/node_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.6879 //Correct version number as needed**
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Short description for file.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.6879 //Correct version number as needed**
+ */
+class NodeFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Node'
+ * @access public
+ */
+	var $name = 'Node';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'name' => 'string',
+		'state' => 'integer'
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('id' => 1, 'name' => 'First', 'state' => 50),
+		array('id' => 2, 'name' => 'Second', 'state' => 60),
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/number_tree_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/number_tree_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/number_tree_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,54 @@
+<?php
+/**
+ * Tree behavior class.
+ *
+ * Enables a model object to act as a node-based tree.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.5331
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Number Tree Test Fixture
+ *
+ * Generates a tree of data for use testing the tree behavior
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class NumberTreeFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'NumberTree'
+ * @access public
+ */
+	var $name = 'NumberTree';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id'	=> array('type' => 'integer','key' => 'primary'),
+		'name'	=> array('type' => 'string','null' => false),
+		'parent_id' => 'integer',
+		'lft'	=> array('type' => 'integer','null' => false),
+		'rght'	=> array('type' => 'integer','null' => false)
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/number_tree_two_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/number_tree_two_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/number_tree_two_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,55 @@
+<?php
+/**
+ * Tree behavior class.
+ *
+ * Enables a model object to act as a node-based tree.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.5331
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Number Tree Test Fixture
+ *
+ * Generates a tree of data for use testing the tree behavior
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class NumberTreeTwoFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'NumberTree'
+ * @access public
+ */
+	var $name = 'NumberTreeTwo';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id'	=> array('type' => 'integer','key' => 'primary'),
+		'name'	=> array('type' => 'string','null' => false),
+		'number_tree_id' => array('type' => 'integer', 'null' => false),
+		'parent_id' => 'integer',
+		'lft'	=> array('type' => 'integer','null' => false),
+		'rght'	=> array('type' => 'integer','null' => false)
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/numeric_article_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/numeric_article_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/numeric_article_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class NumericArticleFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'NumericArticle'
+ * @access public
+ */
+	var $name = 'NumericArticle';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'title' => array('type' => 'string', 'null' => false),
+		'created' => 'datetime',
+		'updated' => 'datetime'
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('title' => 'First Article', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'),
+		array('title' => '12345abcde', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'),
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/overall_favorite_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/overall_favorite_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/overall_favorite_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.7198
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class OverallFavoriteFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'OverallFavorite'
+ * @access public
+ */
+	var $name = 'OverallFavorite';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'model_type' => array('type' => 'string', 'length' => 255),
+		'model_id' => array('type' => 'integer'),
+		'priority' => array('type' => 'integer')
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('id' => 1, 'model_type' => 'Cd', 'model_id' => '1', 'priority' => '1'),
+		array('id' => 2, 'model_type' => 'Book', 'model_id' => '1', 'priority' => '2')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/person_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/person_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/person_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,69 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.6700
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class PersonFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Person'
+ * @access public
+ */
+	var $name = 'Person';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'null' => false, 'key' => 'primary'),
+		'name' => array('type' => 'string', 'null' => false, 'length' => 32),
+		'mother_id' => array('type' => 'integer', 'null' => false, 'key' => 'index'),
+		'father_id' => array('type' => 'integer', 'null' => false),
+		'indexes' => array(
+			'PRIMARY' => array('column' => 'id', 'unique' => 1),
+			'mother_id' => array('column' => array('mother_id', 'father_id'), 'unique' => 0)
+		)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('name' => 'person', 'mother_id' => 2, 'father_id' => 3),
+		array('name' => 'mother', 'mother_id' => 4, 'father_id' => 5),
+		array('name' => 'father', 'mother_id' => 6, 'father_id' => 7),
+		array('name' => 'mother - grand mother', 'mother_id' => 0, 'father_id' => 0),
+		array('name' => 'mother - grand father', 'mother_id' => 0, 'father_id' => 0),
+		array('name' => 'father - grand mother', 'mother_id' => 0, 'father_id' => 0),
+		array('name' => 'father - grand father', 'mother_id' => 0, 'father_id' => 0)
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/portfolio_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/portfolio_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/portfolio_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class PortfolioFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Portfolio'
+ * @access public
+ */
+	var $name = 'Portfolio';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'seller_id' => array('type' => 'integer', 'null' => false),
+		'name' => array('type' => 'string', 'null' => false)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('seller_id' => 1, 'name' => 'Portfolio 1'),
+		array('seller_id' => 1, 'name' => 'Portfolio 2'),
+		array('seller_id' => 2, 'name' => 'Portfolio 1')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/post_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/post_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/post_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,64 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class PostFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Post'
+ * @access public
+ */
+	var $name = 'Post';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'author_id' => array('type' => 'integer', 'null' => false),
+		'title' => array('type' => 'string', 'null' => false),
+		'body' => 'text',
+		'published' => array('type' => 'string', 'length' => 1, 'default' => 'N'),
+		'created' => 'datetime',
+		'updated' => 'datetime'
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('author_id' => 1, 'title' => 'First Post', 'body' => 'First Post Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'),
+		array('author_id' => 3, 'title' => 'Second Post', 'body' => 'Second Post Body', 'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'),
+		array('author_id' => 1, 'title' => 'Third Post', 'body' => 'Third Post Body', 'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/posts_tag_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/posts_tag_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/posts_tag_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class PostsTagFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'PostsTag'
+ * @access public
+ */
+	var $name = 'PostsTag';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'post_id' => array('type' => 'integer', 'null' => false),
+		'tag_id' => array('type' => 'string', 'null' => false),
+		'indexes' => array('posts_tag' => array('column' => array('tag_id', 'post_id'), 'unique' => 1))
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('post_id' => 1, 'tag_id' => 'tag1'),
+		array('post_id' => 1, 'tag_id' => 'tag2'),
+		array('post_id' => 2, 'tag_id' => 'tag1'),
+		array('post_id' => 2, 'tag_id' => 'tag3')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/prefix_test_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/prefix_test_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/prefix_test_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class PrefixTestFixture extends CakeTestFixture {
+
+	var $name = 'PrefixTest';
+	var $table = 'prefix_prefix_tests';
+
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/primary_model_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/primary_model_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/primary_model_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,57 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class PrimaryModelFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'PrimaryModel'
+ * @access public
+ */
+	var $name = 'PrimaryModel';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'primary_name' => array('type' => 'string', 'null' => false)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('primary_name' => 'Primary Name Existing')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/product_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/product_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/product_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,66 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class ProductFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Product'
+ * @access public
+ */
+	var $name = 'Product';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'name' => array('type' => 'string', 'length' => 255, 'null' => false),
+		'type' => array('type' => 'string', 'length' => 255, 'null' => false),
+		'price' => array('type' => 'integer', 'null' => false)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('name' => 'Park\'s Great Hits', 'type' => 'Music', 'price' => 19),
+		array('name' => 'Silly Puddy', 'type' => 'Toy', 'price' => 3),
+		array('name' => 'Playstation', 'type' => 'Toy', 'price' => 89),
+		array('name' => 'Men\'s T-Shirt', 'type' => 'Clothing', 'price' => 32),
+		array('name' => 'Blouse', 'type' => 'Clothing', 'price' => 34),
+		array('name' => 'Electronica 2002', 'type' => 'Music', 'price' => 4),
+		array('name' => 'Country Tunes', 'type' => 'Music', 'price' => 21),
+		array('name' => 'Watermelon', 'type' => 'Food', 'price' => 9)
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/product_update_all_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/product_update_all_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/product_update_all_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class ProductUpdateAllFixture extends CakeTestFixture {
+	var $name = 'ProductUpdateAll';
+	var $table = 'product_update_all';
+
+    var $fields = array(
+            'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'),
+            'name' => array('type'=>'string', 'null' => false, 'length' => 29),
+            'groupcode' => array('type'=>'integer', 'null' => false, 'length' => 4),
+            'group_id' => array('type'=>'integer', 'null' => false, 'length' => 8),
+            'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1))
+            );
+    var $records = array(
+        array(
+            'id'  => 1,
+            'name'  => 'product one',
+            'groupcode'  => 120,
+            'group_id'  => 1
+            ),
+        array(
+            'id'  => 2,
+            'name'  => 'product two',
+            'groupcode'  => 120,
+            'group_id'  => 1),
+        array(
+            'id'  => 3,
+            'name'  => 'product three',
+            'groupcode'  => 125,
+            'group_id'  => 2),
+        array(
+            'id'  => 4,
+            'name'  => 'product four',
+            'groupcode'  => 135,
+            'group_id'  => 4)
+        );
+}

Added: trunk/src/Web/cake/tests/fixtures/project_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/project_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/project_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,59 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class ProjectFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Project'
+ * @access public
+ */
+	var $name = 'Project';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'name' => array('type' => 'string', 'null' => false)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('name' => 'Project 1'),
+		array('name' => 'Project 2'),
+		array('name' => 'Project 3')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/sample_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/sample_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/sample_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class SampleFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Sample'
+ * @access public
+ */
+	var $name = 'Sample';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'apple_id' => array('type' => 'integer', 'null' => false),
+		'name' => array('type' => 'string', 'length' => 40, 'null' => false)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('apple_id' => 3, 'name' => 'sample1'),
+		array('apple_id' => 2, 'name' => 'sample2'),
+		array('apple_id' => 4, 'name' => 'sample3'),
+		array('apple_id' => 5, 'name' => 'sample4')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/secondary_model_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/secondary_model_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/secondary_model_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,57 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class SecondaryModelFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'SecondaryModel'
+ * @access public
+ */
+	var $name = 'SecondaryModel';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'secondary_name' => array('type' => 'string', 'null' => false)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('secondary_name' => 'Secondary Name Existing')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/session_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/session_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/session_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,63 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class SessionFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Session'
+ * @access public
+ */
+	var $name = 'Session';
+
+/**
+ * table property.
+ *
+ * @var string
+ */
+	// var $table = 'sessions';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'string', 'length' => 255, 'key' => 'primary'),
+		'data' => array('type' => 'text','null' => true),
+		'expires' => array('type' => 'integer', 'length' => 11, 'null' => true)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array();
+}

Added: trunk/src/Web/cake/tests/fixtures/something_else_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/something_else_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/something_else_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,63 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class SomethingElseFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'SomethingElse'
+ * @access public
+ */
+	var $name = 'SomethingElse';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'title' => array('type' => 'string', 'default' => ''),
+		'body' => array('type' => 'text'),
+		'published' => array('type' => 'string', 'default' => ''),
+		'created' => array('type' => 'datetime', 'null' => true),
+		'updated' => array('type' => 'datetime', 'null' => true)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('title' => 'First Post', 'body' => 'First Post Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'),
+		array('title' => 'Second Post', 'body' => 'Second Post Body', 'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'),
+		array('title' => 'Third Post', 'body' => 'Third Post Body', 'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/something_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/something_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/something_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,63 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class SomethingFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Something'
+ * @access public
+ */
+	var $name = 'Something';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'title' => array('type' => 'string', 'default' => ''),
+		'body' => array('type' => 'text'),
+		'published' => array('type' => 'string', 'default' => ''),
+		'created' => array('type' => 'datetime', 'null' => true),
+		'updated' => array('type' => 'datetime', 'null' => true)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('title' => 'First Post', 'body' => 'First Post Body', 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'),
+		array('title' => 'Second Post', 'body' => 'Second Post Body', 'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'),
+		array('title' => 'Third Post', 'body' => 'Third Post Body', 'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/stories_tag_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/stories_tag_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/stories_tag_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class StoriesTagFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'StoriesTag'
+ * @access public
+ */
+	var $name = 'StoriesTag';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'story' => array('type' => 'integer', 'null' => false),
+		'tag_id' => array('type' => 'integer', 'null' => false),
+		'indexes' => array('UNIQUE_STORY_TAG' => array('column'=> array('story', 'tag_id'), 'unique'=>1))
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('story' => 1, 'tag_id' => 1)
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/story_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/story_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/story_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class StoryFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Story'
+ * @access public
+ */
+	var $name = 'Story';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'story' => array('type' => 'integer', 'key' => 'primary'),
+		'title' => array('type' => 'string', 'null' => false)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('title' => 'First Story'),
+		array('title' => 'Second Story')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/syfile_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/syfile_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/syfile_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,64 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class SyfileFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Syfile'
+ * @access public
+ */
+	var $name = 'Syfile';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'image_id' => array('type' => 'integer', 'null' => true),
+		'name' => array('type' => 'string', 'null' => false),
+		'item_count' => array('type' => 'integer', 'null' => true)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('image_id' => 1, 'name' => 'Syfile 1'),
+		array('image_id' => 2, 'name' => 'Syfile 2'),
+		array('image_id' => 5, 'name' => 'Syfile 3'),
+		array('image_id' => 3, 'name' => 'Syfile 4'),
+		array('image_id' => 4, 'name' => 'Syfile 5'),
+		array('image_id' => null, 'name' => 'Syfile 6')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/tag_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/tag_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/tag_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class TagFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Tag'
+ * @access public
+ */
+	var $name = 'Tag';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'tag' => array('type' => 'string', 'null' => false),
+		'created' => 'datetime',
+		'updated' => 'datetime'
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('tag' => 'tag1', 'created' => '2007-03-18 12:22:23', 'updated' => '2007-03-18 12:24:31'),
+		array('tag' => 'tag2', 'created' => '2007-03-18 12:24:23', 'updated' => '2007-03-18 12:26:31'),
+		array('tag' => 'tag3', 'created' => '2007-03-18 12:26:23', 'updated' => '2007-03-18 12:28:31')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/test_plugin_article_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/test_plugin_article_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/test_plugin_article_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,64 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 7660
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class TestPluginArticleFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Article'
+ * @access public
+ */
+	var $name = 'TestPluginArticle';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'user_id' => array('type' => 'integer', 'null' => false),
+		'title' => array('type' => 'string', 'null' => false),
+		'body' => 'text',
+		'published' => array('type' => 'string', 'length' => 1, 'default' => 'N'),
+		'created' => 'datetime',
+		'updated' => 'datetime'
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('user_id' => 1, 'title' => 'First Plugin Article', 'body' => 'First Plugin Article Body', 'published' => 'Y', 'created' => '2008-09-24 10:39:23', 'updated' => '2008-09-24 10:41:31'),
+		array('user_id' => 3, 'title' => 'Second Plugin Article', 'body' => 'Second Plugin Article Body', 'published' => 'Y', 'created' => '2008-09-24 10:41:23', 'updated' => '2008-09-24 10:43:31'),
+		array('user_id' => 1, 'title' => 'Third Plugin Article', 'body' => 'Third Plugin Article Body', 'published' => 'Y', 'created' => '2008-09-24 10:43:23', 'updated' => '2008-09-24 10:45:31')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/test_plugin_comment_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/test_plugin_comment_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/test_plugin_comment_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,67 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 7660
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class TestPluginCommentFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Comment'
+ * @access public
+ */
+	var $name = 'TestPluginComment';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'article_id' => array('type' => 'integer', 'null'=>false),
+		'user_id' => array('type' => 'integer', 'null'=>false),
+		'comment' => 'text',
+		'published' => array('type' => 'string', 'length' => 1, 'default' => 'N'),
+		'created' => 'datetime',
+		'updated' => 'datetime'
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('id' => 1, 'article_id' => 1, 'user_id' => 2, 'comment' => 'First Comment for First Plugin Article', 'published' => 'Y', 'created' => '2008-09-24 10:45:23', 'updated' => '2008-09-24 10:47:31'),
+		array('id' => 2, 'article_id' => 1, 'user_id' => 4, 'comment' => 'Second Comment for First Plugin Article', 'published' => 'Y', 'created' => '2008-09-24 10:47:23', 'updated' => '2008-09-24 10:49:31'),
+		array('id' => 3, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Third Comment for First Plugin Article', 'published' => 'Y', 'created' => '2008-09-24 10:49:23', 'updated' => '2008-09-24 10:51:31'),
+		array('id' => 4, 'article_id' => 1, 'user_id' => 1, 'comment' => 'Fourth Comment for First Plugin Article', 'published' => 'N', 'created' => '2008-09-24 10:51:23', 'updated' => '2008-09-24 10:53:31'),
+		array('id' => 5, 'article_id' => 2, 'user_id' => 1, 'comment' => 'First Comment for Second Plugin Article', 'published' => 'Y', 'created' => '2008-09-24 10:53:23', 'updated' => '2008-09-24 10:55:31'),
+		array('id' => 6, 'article_id' => 2, 'user_id' => 2, 'comment' => 'Second Comment for Second Plugin Article', 'published' => 'Y', 'created' => '2008-09-24 10:55:23', 'updated' => '2008-09-24 10:57:31')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/the_paper_monkies_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/the_paper_monkies_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/the_paper_monkies_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,55 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class ThePaperMonkiesFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'ThePaperMonkies'
+ * @access public
+ */
+	var $name = 'ThePaperMonkies';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'apple_id' => array('type' => 'integer', 'length' => 10, 'null' => true),
+		'device_id' => array('type' => 'integer', 'length' => 10, 'null' => true)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array();
+}

Added: trunk/src/Web/cake/tests/fixtures/thread_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/thread_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/thread_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class ThreadFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Thread'
+ * @access public
+ */
+	var $name = 'Thread';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'project_id' => array('type' => 'integer', 'null' => false),
+		'name' => array('type' => 'string', 'null' => false)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('project_id' => 1, 'name' => 'Project 1, Thread 1'),
+		array('project_id' => 1, 'name' => 'Project 1, Thread 2'),
+		array('project_id' => 2, 'name' => 'Project 2, Thread 1')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/translate_article_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/translate_article_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/translate_article_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,86 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.5669
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class TranslateArticleFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Translate'
+ * @access public
+ */
+	var $name = 'TranslateArticle';
+
+/**
+ * table property
+ *
+ * @var string 'i18n'
+ * @access public
+ */
+	var $table = 'article_i18n';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'locale' => array('type' => 'string', 'length' => 6, 'null' => false),
+		'model' => array('type' => 'string', 'null' => false),
+		'foreign_key' => array('type' => 'integer', 'null' => false),
+		'field' => array('type' => 'string', 'null' => false),
+		'content' => array('type' => 'text')
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('id' => 1, 'locale' => 'eng', 'model' => 'TranslatedArticle', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Title (eng) #1'),
+		array('id' => 2, 'locale' => 'eng', 'model' => 'TranslatedArticle', 'foreign_key' => 1, 'field' => 'body', 'content' => 'Body (eng) #1'),
+		array('id' => 3, 'locale' => 'deu', 'model' => 'TranslatedArticle', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Title (deu) #1'),
+		array('id' => 4, 'locale' => 'deu', 'model' => 'TranslatedArticle', 'foreign_key' => 1, 'field' => 'body', 'content' => 'Body (deu) #1'),
+		array('id' => 5, 'locale' => 'cze', 'model' => 'TranslatedArticle', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Title (cze) #1'),
+		array('id' => 6, 'locale' => 'cze', 'model' => 'TranslatedArticle', 'foreign_key' => 1, 'field' => 'body', 'content' => 'Body (cze) #1'),
+		array('id' => 7, 'locale' => 'eng', 'model' => 'TranslatedArticle', 'foreign_key' => 2, 'field' => 'title', 'content' => 'Title (eng) #2'),
+		array('id' => 8, 'locale' => 'eng', 'model' => 'TranslatedArticle', 'foreign_key' => 2, 'field' => 'body', 'content' => 'Body (eng) #2'),
+		array('id' => 9, 'locale' => 'deu', 'model' => 'TranslatedArticle', 'foreign_key' => 2, 'field' => 'title', 'content' => 'Title (deu) #2'),
+		array('id' => 10, 'locale' => 'deu', 'model' => 'TranslatedArticle', 'foreign_key' => 2, 'field' => 'body', 'content' => 'Body (deu) #2'),
+		array('id' => 11, 'locale' => 'cze', 'model' => 'TranslatedArticle', 'foreign_key' => 2, 'field' => 'title', 'content' => 'Title (cze) #2'),
+		array('id' => 12, 'locale' => 'cze', 'model' => 'TranslatedArticle', 'foreign_key' => 2, 'field' => 'body', 'content' => 'Body (cze) #2'),
+		array('id' => 13, 'locale' => 'eng', 'model' => 'TranslatedArticle', 'foreign_key' => 3, 'field' => 'title', 'content' => 'Title (eng) #3'),
+		array('id' => 14, 'locale' => 'eng', 'model' => 'TranslatedArticle', 'foreign_key' => 3, 'field' => 'body', 'content' => 'Body (eng) #3'),
+		array('id' => 15, 'locale' => 'deu', 'model' => 'TranslatedArticle', 'foreign_key' => 3, 'field' => 'title', 'content' => 'Title (deu) #3'),
+		array('id' => 16, 'locale' => 'deu', 'model' => 'TranslatedArticle', 'foreign_key' => 3, 'field' => 'body', 'content' => 'Body (deu) #3'),
+		array('id' => 17, 'locale' => 'cze', 'model' => 'TranslatedArticle', 'foreign_key' => 3, 'field' => 'title', 'content' => 'Title (cze) #3'),
+		array('id' => 18, 'locale' => 'cze', 'model' => 'TranslatedArticle', 'foreign_key' => 3, 'field' => 'body', 'content' => 'Body (cze) #3')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/translate_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/translate_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/translate_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,86 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.5669
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class TranslateFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Translate'
+ * @access public
+ */
+	var $name = 'Translate';
+
+/**
+ * table property
+ *
+ * @var string 'i18n'
+ * @access public
+ */
+	var $table = 'i18n';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'locale' => array('type' => 'string', 'length' => 6, 'null' => false),
+		'model' => array('type' => 'string', 'null' => false),
+		'foreign_key' => array('type' => 'integer', 'null' => false),
+		'field' => array('type' => 'string', 'null' => false),
+		'content' => array('type' => 'text')
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('id' => 1, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Title #1'),
+		array('id' => 2, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'content', 'content' => 'Content #1'),
+		array('id' => 3, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Titel #1'),
+		array('id' => 4, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'content', 'content' => 'Inhalt #1'),
+		array('id' => 5, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Titulek #1'),
+		array('id' => 6, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'content', 'content' => 'Obsah #1'),
+		array('id' => 7, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 2, 'field' => 'title', 'content' => 'Title #2'),
+		array('id' => 8, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 2, 'field' => 'content', 'content' => 'Content #2'),
+		array('id' => 9, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 2, 'field' => 'title', 'content' => 'Titel #2'),
+		array('id' => 10, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 2, 'field' => 'content', 'content' => 'Inhalt #2'),
+		array('id' => 11, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 2, 'field' => 'title', 'content' => 'Titulek #2'),
+		array('id' => 12, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 2, 'field' => 'content', 'content' => 'Obsah #2'),
+		array('id' => 13, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 3, 'field' => 'title', 'content' => 'Title #3'),
+		array('id' => 14, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 3, 'field' => 'content', 'content' => 'Content #3'),
+		array('id' => 15, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 3, 'field' => 'title', 'content' => 'Titel #3'),
+		array('id' => 16, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 3, 'field' => 'content', 'content' => 'Inhalt #3'),
+		array('id' => 17, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 3, 'field' => 'title', 'content' => 'Titulek #3'),
+		array('id' => 18, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 3, 'field' => 'content', 'content' => 'Obsah #3')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/translate_table_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/translate_table_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/translate_table_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,69 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.5669
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class TranslateTableFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'TranslateTable'
+ * @access public
+ */
+	var $name = 'TranslateTable';
+
+/**
+ * table property
+ *
+ * @var string 'another_i18n'
+ * @access public
+ */
+	var $table = 'another_i18n';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+			'id' => array('type' => 'integer', 'key' => 'primary'),
+			'locale' => array('type' => 'string', 'length' => 6, 'null' => false),
+			'model' => array('type' => 'string', 'null' => false),
+			'foreign_key' => array('type' => 'integer', 'null' => false),
+			'field' => array('type' => 'string', 'null' => false),
+			'content' => array('type' => 'text'));
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('locale' => 'eng', 'model' => 'TranslatedItemWithTable', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Another Title #1'),
+		array('locale' => 'eng', 'model' => 'TranslatedItemWithTable', 'foreign_key' => 1, 'field' => 'content', 'content' => 'Another Content #1')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/translate_with_prefix_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/translate_with_prefix_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/translate_with_prefix_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,87 @@
+<?php
+/* SVN FILE: $Id$ */
+/**
+ * Short description for file.
+ *
+ * Long description for file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.5669
+ * @version       $Revision$
+ * @modifiedby    $LastChangedBy$
+ * @lastmodified  $Date$
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class TranslateWithPrefixFixture extends CakeTestFixture {
+/**
+ * name property
+ *
+ * @var string 'Translate'
+ * @access public
+ */
+	var $name = 'TranslateWithPrefix';
+/**
+ * table property
+ *
+ * @var string 'i18n'
+ * @access public
+ */
+	var $table = 'i18n_translate_with_prefixes';
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'locale' => array('type' => 'string', 'length' => 6, 'null' => false),
+		'model' => array('type' => 'string', 'null' => false),
+		'foreign_key' => array('type' => 'integer', 'null' => false),
+		'field' => array('type' => 'string', 'null' => false),
+		'content' => array('type' => 'text')
+	);
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('id' => 1, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Title #1'),
+		array('id' => 2, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'content', 'content' => 'Content #1'),
+		array('id' => 3, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Titel #1'),
+		array('id' => 4, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'content', 'content' => 'Inhalt #1'),
+		array('id' => 5, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'title', 'content' => 'Titulek #1'),
+		array('id' => 6, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 1, 'field' => 'content', 'content' => 'Obsah #1'),
+		array('id' => 7, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 2, 'field' => 'title', 'content' => 'Title #2'),
+		array('id' => 8, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 2, 'field' => 'content', 'content' => 'Content #2'),
+		array('id' => 9, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 2, 'field' => 'title', 'content' => 'Titel #2'),
+		array('id' => 10, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 2, 'field' => 'content', 'content' => 'Inhalt #2'),
+		array('id' => 11, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 2, 'field' => 'title', 'content' => 'Titulek #2'),
+		array('id' => 12, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 2, 'field' => 'content', 'content' => 'Obsah #2'),
+		array('id' => 13, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 3, 'field' => 'title', 'content' => 'Title #3'),
+		array('id' => 14, 'locale' => 'eng', 'model' => 'TranslatedItem', 'foreign_key' => 3, 'field' => 'content', 'content' => 'Content #3'),
+		array('id' => 15, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 3, 'field' => 'title', 'content' => 'Titel #3'),
+		array('id' => 16, 'locale' => 'deu', 'model' => 'TranslatedItem', 'foreign_key' => 3, 'field' => 'content', 'content' => 'Inhalt #3'),
+		array('id' => 17, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 3, 'field' => 'title', 'content' => 'Titulek #3'),
+		array('id' => 18, 'locale' => 'cze', 'model' => 'TranslatedItem', 'foreign_key' => 3, 'field' => 'content', 'content' => 'Obsah #3')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/translated_article_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/translated_article_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/translated_article_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,62 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.5669
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class TranslatedArticleFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'TranslatedItem'
+ * @access public
+ */
+	var $name = 'TranslatedArticle';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'user_id' => array('type' => 'integer', 'null' => false),
+		'published' => array('type' => 'string', 'length' => 1, 'default' => 'N'),
+		'created' => 'datetime',
+		'updated' => 'datetime'
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('id' => 1, 'user_id' => 1, 'published' => 'Y', 'created' => '2007-03-18 10:39:23', 'updated' => '2007-03-18 10:41:31'),
+		array('id' => 2, 'user_id' => 3, 'published' => 'Y', 'created' => '2007-03-18 10:41:23', 'updated' => '2007-03-18 10:43:31'),
+		array('id' => 3, 'user_id' => 1, 'published' => 'Y', 'created' => '2007-03-18 10:43:23', 'updated' => '2007-03-18 10:45:31')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/translated_item_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/translated_item_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/translated_item_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,59 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.5669
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class TranslatedItemFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'TranslatedItem'
+ * @access public
+ */
+	var $name = 'TranslatedItem';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'slug' => array('type' => 'string', 'null' => false)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('slug' => 'first_translated'),
+		array('slug' => 'second_translated'),
+		array('slug' => 'third_translated')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/unconventional_tree_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/unconventional_tree_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/unconventional_tree_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,53 @@
+<?php
+/**
+ * Unconventional Tree behavior class test fixture.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.7879
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * UnconventionalTreeFixture class
+ *
+ * Like Number tree, but doesn't use the default values for lft and rght or parent_id
+ *
+ * @uses          CakeTestFixture
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class UnconventionalTreeFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'FlagTree'
+ * @access public
+ */
+	var $name = 'UnconventionalTree';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id'	=> array('type' => 'integer','key' => 'primary'),
+		'name'	=> array('type' => 'string','null' => false),
+		'join' => 'integer',
+		'left'	=> array('type' => 'integer','null' => false),
+		'right'	=> array('type' => 'integer','null' => false),
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/underscore_field_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/underscore_field_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/underscore_field_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,62 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * UnderscoreFieldFixture class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class UnderscoreFieldFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'UnderscoreField'
+ * @access public
+ */
+	var $name = 'UnderscoreField';
+	/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'user_id' => array('type' => 'integer', 'null' => false),
+		'my_model_has_a_field' => array('type' => 'string', 'null' => false),
+		'body_field' => 'text',
+		'published' => array('type' => 'string', 'length' => 1, 'default' => 'N'),
+		'another_field' => array('type' => 'integer', 'length' => 3),
+	);
+	/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('user_id' => 1, 'my_model_has_a_field' => 'First Article', 'body_field' => 'First Article Body', 'published' => 'Y', 'another_field' => 2),
+		array('user_id' => 3, 'my_model_has_a_field' => 'Second Article', 'body_field' => 'Second Article Body', 'published' => 'Y', 'another_field' => 3),
+		array('user_id' => 1, 'my_model_has_a_field' => 'Third Article', 'body_field' => 'Third Article Body', 'published' => 'Y', 'another_field' => 5),
+	);
+
+}

Added: trunk/src/Web/cake/tests/fixtures/user_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/user_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/user_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,63 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class UserFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'User'
+ * @access public
+ */
+	var $name = 'User';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'key' => 'primary'),
+		'user' => array('type' => 'string', 'null' => false),
+		'password' => array('type' => 'string', 'null' => false),
+		'created' => 'datetime',
+		'updated' => 'datetime'
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('user' => 'mariano', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:16:23', 'updated' => '2007-03-17 01:18:31'),
+		array('user' => 'nate', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:18:23', 'updated' => '2007-03-17 01:20:31'),
+		array('user' => 'larry', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:20:23', 'updated' => '2007-03-17 01:22:31'),
+		array('user' => 'garrett', 'password' => '5f4dcc3b5aa765d61d8327deb882cf99', 'created' => '2007-03-17 01:22:23', 'updated' => '2007-03-17 01:24:31'),
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/uuid_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/uuid_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/uuid_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,63 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.6700
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class UuidFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Uuid'
+ * @access public
+ */
+	var $name = 'Uuid';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'string', 'length' => 36, 'key' => 'primary'),
+		'title' => 'string',
+		'count' => array('type' => 'integer', 'default' => 0),
+		'created' => 'datetime',
+		'updated' => 'datetime'
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('id' => '47c36f9c-bc00-4d17-9626-4e183ca6822b', 'title' => 'Unique record 1', 'count' => 2, 'created' => '2008-03-13 01:16:23', 'updated' => '2008-03-13 01:18:31'),
+		array('id' => '47c36f9c-f2b0-43f5-b3f7-4e183ca6822b', 'title' => 'Unique record 2', 'count' => 4, 'created' => '2008-03-13 01:18:24', 'updated' => '2008-03-13 01:20:32'),
+		array('id' => '47c36f9c-0ffc-4084-9b03-4e183ca6822b', 'title' => 'Unique record 3', 'count' => 5, 'created' => '2008-03-13 01:20:25', 'updated' => '2008-03-13 01:22:33'),
+		array('id' => '47c36f9c-2578-4c2e-aeab-4e183ca6822b', 'title' => 'Unique record 4', 'count' => 3, 'created' => '2008-03-13 01:22:26', 'updated' => '2008-03-13 01:24:34'),
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/uuid_tag_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/uuid_tag_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/uuid_tag_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.7953
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class UuidTagFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'UuidTag'
+ * @access public
+ */
+	var $name = 'UuidTag';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'string', 'length' => 36, 'key' => 'primary'),
+		'name' => array('type' => 'string', 'length' => 255),
+		'created' => array('type' => 'datetime')
+);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('id' => '481fc6d0-b920-43e0-e50f-6d1740cf8569', 'name' => 'MyTag', 'created' => '2009-12-09 12:30:00')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/uuid_tree_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/uuid_tree_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/uuid_tree_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,51 @@
+<?php
+/**
+ * UUID Tree behavior fixture.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.7984
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * UuidTreeFixture class
+ *
+ * @uses          CakeTestFixture
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class UuidTreeFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'UuidTree'
+ * @access public
+ */
+	var $name = 'UuidTree';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id'	=> array('type' => 'string', 'length' => 36, 'key' => 'primary'),
+		'name'	=> array('type' => 'string','null' => false),
+		'parent_id' => array('type' => 'string', 'length' => 36, 'null' => true),
+		'lft'	=> array('type' => 'integer','null' => false),
+		'rght'	=> array('type' => 'integer','null' => false)
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/uuiditem_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/uuiditem_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/uuiditem_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,63 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class UuiditemFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Uuiditem'
+ * @access public
+ */
+	var $name = 'Uuiditem';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'string', 'length' => 36, 'key' => 'primary'),
+		'published' => array('type' => 'boolean', 'null' => false),
+		'name' => array('type' => 'string', 'null' => false)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('id' => '481fc6d0-b920-43e0-a40d-6d1740cf8569', 'published' => 0, 'name' => 'Item 1'),
+		array('id' => '48298a29-81c0-4c26-a7fb-413140cf8569', 'published' => 0, 'name' => 'Item 2'),
+		array('id' => '482b7756-8da0-419a-b21f-27da40cf8569', 'published' => 0, 'name' => 'Item 3'),
+		array('id' => '482cfd4b-0e7c-4ea3-9582-4cec40cf8569', 'published' => 0, 'name' => 'Item 4'),
+		array('id' => '4831181b-4020-4983-a29b-131440cf8569', 'published' => 0, 'name' => 'Item 5'),
+		array('id' => '483798c8-c7cc-430e-8cf9-4fcc40cf8569', 'published' => 0, 'name' => 'Item 6')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/uuiditems_uuidportfolio_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/uuiditems_uuidportfolio_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/uuiditems_uuidportfolio_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class UuiditemsUuidportfolioFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'UuiditemsUuidportfolio'
+ * @access public
+ */
+	var $name = 'UuiditemsUuidportfolio';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'string', 'length' => 36, 'key' => 'primary'),
+		'uuiditem_id' => array('type' => 'string', 'length' => 36, 'null' => false),
+		'uuidportfolio_id' => array('type' => 'string', 'length' => 36, 'null' => false)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('id' => '4850fd8f-cc5c-449f-bf34-0c5240cf8569', 'uuiditem_id' => '481fc6d0-b920-43e0-a40d-6d1740cf8569', 'uuidportfolio_id' => '4806e091-6940-4d2b-b227-303740cf8569'),
+		array('id' => '4850fee5-d24c-4ea0-9759-0c2e40cf8569', 'uuiditem_id' => '48298a29-81c0-4c26-a7fb-413140cf8569', 'uuidportfolio_id' => '480af662-eb8c-47d3-886b-230540cf8569'),
+		array('id' => '4851af6e-fa18-403d-b57e-437d40cf8569', 'uuiditem_id' => '482b7756-8da0-419a-b21f-27da40cf8569', 'uuidportfolio_id' => '4806e091-6940-4d2b-b227-303740cf8569'),
+		array('id' => '4851b94c-9790-42dc-b760-4f9240cf8569', 'uuiditem_id' => '482cfd4b-0e7c-4ea3-9582-4cec40cf8569', 'uuidportfolio_id' => '4806e091-6940-4d2b-b227-303740cf8569')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/uuiditems_uuidportfolio_numericid_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/uuiditems_uuidportfolio_numericid_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/uuiditems_uuidportfolio_numericid_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class UuiditemsUuidportfolioNumericidFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'UuiditemsUuidportfolioNumericid'
+ * @access public
+ */
+	var $name = 'UuiditemsUuidportfolioNumericid';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'integer', 'length' => 10, 'key' => 'primary'),
+		'uuiditem_id' => array('type' => 'string', 'length' => 36, 'null' => false),
+		'uuidportfolio_id' => array('type' => 'string', 'length' => 36, 'null' => false)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('uuiditem_id' => '481fc6d0-b920-43e0-a40d-6d1740cf8569', 'uuidportfolio_id' => '4806e091-6940-4d2b-b227-303740cf8569'),
+		array('uuiditem_id' => '48298a29-81c0-4c26-a7fb-413140cf8569', 'uuidportfolio_id' => '480af662-eb8c-47d3-886b-230540cf8569'),
+		array('uuiditem_id' => '482b7756-8da0-419a-b21f-27da40cf8569', 'uuidportfolio_id' => '4806e091-6940-4d2b-b227-303740cf8569'),
+		array('uuiditem_id' => '482cfd4b-0e7c-4ea3-9582-4cec40cf8569', 'uuidportfolio_id' => '4806e091-6940-4d2b-b227-303740cf8569')
+	);
+}

Added: trunk/src/Web/cake/tests/fixtures/uuidportfolio_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/fixtures/uuidportfolio_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/fixtures/uuidportfolio_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Short description for class.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.fixtures
+ */
+class UuidportfolioFixture extends CakeTestFixture {
+
+/**
+ * name property
+ *
+ * @var string 'Uuidportfolio'
+ * @access public
+ */
+	var $name = 'Uuidportfolio';
+
+/**
+ * fields property
+ *
+ * @var array
+ * @access public
+ */
+	var $fields = array(
+		'id' => array('type' => 'string', 'length' => 36, 'key' => 'primary'),
+		'name' => array('type' => 'string', 'null' => false)
+	);
+
+/**
+ * records property
+ *
+ * @var array
+ * @access public
+ */
+	var $records = array(
+		array('id' => '4806e091-6940-4d2b-b227-303740cf8569', 'name' => 'Portfolio 1'),
+		array('id' => '480af662-eb8c-47d3-886b-230540cf8569', 'name' => 'Portfolio 2'),
+	);
+}

Added: trunk/src/Web/cake/tests/groups/acl.group.php
===================================================================
--- trunk/src/Web/cake/tests/groups/acl.group.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/groups/acl.group.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,50 @@
+<?php
+/**
+ * AclAndAuthGroupTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * AclAndAuthGroupTest class
+ *
+ * This test group will run the Acl and Auth tests
+ *
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ */
+class AclAndAuthGroupTest extends TestSuite {
+
+/**
+ * label property
+ *
+ * @var string 'Acl and Auth Tests'
+ * @access public
+ */
+	var $label = 'Acl and Auth';
+
+/**
+ * AclAndAuthGroupTest method
+ *
+ * @access public
+ * @return void
+ */
+	function AclAndAuthGroupTest() {
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'model' . DS . 'db_acl');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'controller' . DS . 'components' . DS . 'acl');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'controller' . DS . 'components' . DS . 'auth');
+	}
+}

Added: trunk/src/Web/cake/tests/groups/bake.group.php
===================================================================
--- trunk/src/Web/cake/tests/groups/bake.group.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/groups/bake.group.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,59 @@
+<?php
+/**
+ * Bake Group test file
+ *
+ * Run all the test cases related to bake.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ * @since         CakePHP(tm) v 1.3
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * BakeGroupTest class
+ *
+ * This test group will run all bake tests
+ *
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ */
+class BakeGroupTest extends TestSuite {
+
+/**
+ * label property
+ *
+ * @var string 'All core cache engines'
+ * @access public
+ */
+	var $label = 'All Tasks related to bake.';
+
+/**
+ * BakeGroupTest method
+ *
+ * @access public
+ * @return void
+ */
+	function BakeGroupTest() {
+		$path = CORE_TEST_CASES . DS . 'console' . DS . 'libs' . DS . 'tasks' . DS;
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'console' . DS . 'libs' . DS . 'bake');
+		TestManager::addTestFile($this, $path . 'controller');
+		TestManager::addTestFile($this, $path . 'model');
+		TestManager::addTestFile($this, $path . 'view');
+		TestManager::addTestFile($this, $path . 'fixture');
+		TestManager::addTestFile($this, $path . 'test');
+		TestManager::addTestFile($this, $path . 'db_config');
+		TestManager::addTestFile($this, $path . 'plugin');
+		TestManager::addTestFile($this, $path . 'project');
+	}
+}

Added: trunk/src/Web/cake/tests/groups/behaviors.group.php
===================================================================
--- trunk/src/Web/cake/tests/groups/behaviors.group.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/groups/behaviors.group.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,48 @@
+<?php
+/**
+ * BehaviorsGroupTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ * @since         CakePHP(tm) v 1.3
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * BehaviorsGroupTest class
+ *
+ * This test group will run all tests related to i18n/l10n and multibyte
+ *
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ */
+class BehaviorsGroupTest extends TestSuite {
+
+/**
+ * label property
+ *
+ * @var string
+ * @access public
+ */
+	var $label = 'All core behavior test cases.';
+
+/**
+ * BehaviorsGroupTest method
+ *
+ * @access public
+ * @return void
+ */
+	function BehaviorsGroupTest() {
+		TestManager::addTestCasesFromDirectory($this, CORE_TEST_CASES . DS . 'libs' . DS . 'model' . DS . 'behaviors');
+	}
+}

Added: trunk/src/Web/cake/tests/groups/cache.group.php
===================================================================
--- trunk/src/Web/cake/tests/groups/cache.group.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/groups/cache.group.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,49 @@
+<?php
+/**
+ * CacheGroupTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * CacheGroupTest class
+ *
+ * This test group will run all the Cache class test and all core cache engine tests
+ *
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ */
+class CacheGroupTest extends TestSuite {
+
+/**
+ * label property
+ *
+ * @var string 'All core cache engines'
+ * @access public
+ */
+	var $label = 'Cache and all CacheEngines';
+
+/**
+ * CacheGroupTest method
+ *
+ * @access public
+ * @return void
+ */
+	function CacheGroupTest() {
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'cache');
+		TestManager::addTestCasesFromDirectory($this, CORE_TEST_CASES . DS . 'libs' . DS . 'cache');
+	}
+}

Added: trunk/src/Web/cake/tests/groups/components.group.php
===================================================================
--- trunk/src/Web/cake/tests/groups/components.group.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/groups/components.group.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,48 @@
+<?php
+/**
+ * ComponentsGroupTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * ComponentsGroupTest class
+ *
+ * This test group will run all tests in the cases/libs/controller/components directory.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ */
+class ComponentsGroupTest extends TestSuite {
+
+/**
+ * label property
+ *
+ * @var string 'All core components'
+ * @access public
+ */
+	var $label = 'All Components';
+
+/**
+ * CoreComponentsGroupTest method
+ *
+ * @access public
+ * @return void
+ */
+	function ComponentsGroupTest() {
+		TestManager::addTestCasesFromDirectory($this, CORE_TEST_CASES . DS . 'libs' . DS . 'controller' . DS . 'components');
+	}
+}

Added: trunk/src/Web/cake/tests/groups/configure.group.php
===================================================================
--- trunk/src/Web/cake/tests/groups/configure.group.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/groups/configure.group.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,49 @@
+<?php
+/**
+ * ConfigureGroupTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * ConfigureGroupTest class
+ *
+ * This test group will run all test for the configure and loader.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ */
+class ConfigureGroupTest extends TestSuite {
+
+/**
+ * label property
+ *
+ * @var string 'Configure, Loader, ClassRegistry Tests'
+ * @access public
+ */
+	var $label = 'Configure, App and ClassRegistry';
+
+/**
+ * ConfigureGroupTest method
+ *
+ * @access public
+ * @return void
+ */
+	function ConfigureGroupTest() {
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'configure');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'class_registry');
+	}
+}

Added: trunk/src/Web/cake/tests/groups/console.group.php
===================================================================
--- trunk/src/Web/cake/tests/groups/console.group.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/groups/console.group.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,65 @@
+<?php
+/**
+ * ConsoleGroupTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * ConsoleGroupTest class
+ *
+ * This test group will run all console tests
+ *
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ */
+class ConsoleGroupTest extends TestSuite {
+
+/**
+ * label property
+ *
+ * @var string 'All core cache engines'
+ * @access public
+ */
+	var $label = 'ShellDispatcher, Shell and all Tasks';
+
+/**
+ * ConsoleGroupTest method
+ *
+ * @access public
+ * @return void
+ */
+	function ConsoleGroupTest() {
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'console' . DS . 'cake');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'console' . DS . 'libs' . DS . 'acl');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'console' . DS . 'libs' . DS . 'api');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'console' . DS . 'libs' . DS . 'bake');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'console' . DS . 'libs' . DS . 'schema');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'console' . DS . 'libs' . DS . 'shell');
+
+		$path = CORE_TEST_CASES . DS . 'console' . DS . 'libs' . DS . 'tasks' . DS;
+		
+		TestManager::addTestFile($this, $path . 'controller');
+		TestManager::addTestFile($this, $path . 'db_config');
+		TestManager::addTestFile($this, $path . 'extract');
+		TestManager::addTestFile($this, $path . 'fixture');
+		TestManager::addTestFile($this, $path . 'model');
+		TestManager::addTestFile($this, $path . 'plugin');
+		TestManager::addTestFile($this, $path . 'project');
+		TestManager::addTestFile($this, $path . 'test');
+		TestManager::addTestFile($this, $path . 'view');
+	}
+}

Added: trunk/src/Web/cake/tests/groups/controller.group.php
===================================================================
--- trunk/src/Web/cake/tests/groups/controller.group.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/groups/controller.group.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,49 @@
+<?php
+/**
+ * ControllerGroupTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * ControllerGroupTest
+ *
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ */
+class ControllerGroupTest extends TestSuite {
+
+/**
+ * label property
+ *
+ * @var string 'All cake/libs/controller/* (Not yet implemented)'
+ * @access public
+ */
+	var $label = 'Component, Controllers, Scaffold test cases.';
+/**
+ * LibControllerGroupTest method
+ *
+ * @access public
+ * @return void
+ */
+	function ControllerGroupTest() {
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'controller' . DS . 'controller');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'controller' . DS . 'scaffold');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'controller' . DS . 'pages_controller');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'controller' . DS . 'component');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'controller' . DS . 'controller_merge_vars');
+	}
+}

Added: trunk/src/Web/cake/tests/groups/database.group.php
===================================================================
--- trunk/src/Web/cake/tests/groups/database.group.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/groups/database.group.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,52 @@
+<?php
+/**
+ * DatabaseGroupTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ * @since         CakePHP(tm) v 1.2.0.5517
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * DatabaseGroupTest class
+ *
+ * This test group will run all behavior, schema and datasource tests excluding database
+ * driver-specific tests
+ *
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ */
+class DatabaseGroupTest extends TestSuite {
+
+/**
+ * label property
+ *
+ * @var string 'All model tests'
+ * @access public
+ */
+	var $label = 'Datasources, Schema and DbAcl tests';
+
+/**
+ * ModelGroupTest method
+ *
+ * @access public
+ * @return void
+ */
+	function DatabaseGroupTest() {
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'model' . DS . 'db_acl');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'model' . DS . 'cake_schema');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'model' . DS . 'connection_manager');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'model' . DS . 'datasources' . DS . 'dbo_source');
+	}
+}

Added: trunk/src/Web/cake/tests/groups/helpers.group.php
===================================================================
--- trunk/src/Web/cake/tests/groups/helpers.group.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/groups/helpers.group.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,49 @@
+<?php
+/**
+ * HelpersGroupTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * HelpersGroupTest class
+ *
+ * This test group will run all test in the cases/libs/view/helpers directory.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ */
+class HelpersGroupTest extends TestSuite {
+
+/**
+ * label property
+ *
+ * @var string 'All core helpers'
+ * @access public
+ */
+	var $label = 'All Helpers';
+
+/**
+ * HelpersGroupTest method
+ *
+ * @access public
+ * @return void
+ */
+	function HelpersGroupTest() {
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'view' . DS . 'helper');
+		TestManager::addTestCasesFromDirectory($this, CORE_TEST_CASES . DS . 'libs' . DS . 'view' . DS . 'helpers');
+	}
+}

Added: trunk/src/Web/cake/tests/groups/i18n.group.php
===================================================================
--- trunk/src/Web/cake/tests/groups/i18n.group.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/groups/i18n.group.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,50 @@
+<?php
+/**
+ * i18nGroupTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ * @since         CakePHP(tm) v 1.3
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * i18nGroupTest class
+ *
+ * This test group will run all tests related to i18n/l10n and multibyte
+ *
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ */
+class i18nGroupTest extends TestSuite {
+
+/**
+ * label property
+ *
+ * @var string
+ * @access public
+ */
+	var $label = 'i18n, l10n and multibyte tests';
+
+/**
+ * LibGroupTest method
+ *
+ * @access public
+ * @return void
+ */
+	function i18nGroupTest() {
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'i18n');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'l10n');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'multibyte');
+	}
+}

Added: trunk/src/Web/cake/tests/groups/javascript.group.php
===================================================================
--- trunk/src/Web/cake/tests/groups/javascript.group.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/groups/javascript.group.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,51 @@
+<?php
+/**
+ * AllCoreJavascriptHelpersGroupTest file
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ * @since         CakePHP(tm) v 1.3
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+/**
+ * AllCoreJavascriptHelpersGroupTest class
+ *
+ * This test group will run all test in the cases/libs/view/helpers directory related
+ * to Js helper and its engines
+ *
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ */
+class AllCoreJavascriptHelpersGroupTest extends TestSuite {
+/**
+ * label property
+ *
+ * @var string 'All core helpers'
+ * @access public
+ */
+	var $label = 'Js Helper and all Engine Helpers';
+/**
+ * AllCoreHelpersGroupTest method
+ *
+ * @access public
+ * @return void
+ */
+	function AllCoreJavascriptHelpersGroupTest() {
+		$helperTestPath = CORE_TEST_CASES . DS . 'libs' . DS . 'view' . DS . 'helpers' . DS;
+		TestManager::addTestFile($this, $helperTestPath . 'js.test.php');
+		TestManager::addTestFile($this, $helperTestPath . 'jquery_engine.test.php');
+		TestManager::addTestFile($this, $helperTestPath . 'mootools_engine.test.php');
+		TestManager::addTestFile($this, $helperTestPath . 'prototype_engine.test.php');
+	}
+}

Added: trunk/src/Web/cake/tests/groups/lib.group.php
===================================================================
--- trunk/src/Web/cake/tests/groups/lib.group.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/groups/lib.group.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,63 @@
+<?php
+/**
+ * LibGroupTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * LibGroupTest class
+ *
+ * This test group will run all test in the cases/libs directory.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ */
+class LibGroupTest extends TestSuite {
+
+/**
+ * label property
+ *
+ * @var string
+ * @access public
+ */
+	var $label = 'All core, non MVC element libs';
+
+/**
+ * LibGroupTest method
+ *
+ * @access public
+ * @return void
+ */
+	function LibGroupTest() {
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'basics');
+		// TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'inflector');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'cake_session');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'debugger');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'error');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'file');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'folder');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'cake_log');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'log' . DS . 'file_log');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'class_registry');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'overloadable');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'sanitize');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'security');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'set');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'string');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'validation');
+	}
+}

Added: trunk/src/Web/cake/tests/groups/model.group.php
===================================================================
--- trunk/src/Web/cake/tests/groups/model.group.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/groups/model.group.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,54 @@
+<?php
+/**
+ * ModelGroupTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ * @since         CakePHP(tm) v 1.2.0.5517
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * ModelGroupTest class
+ *
+ * This test group will run all model-layer and related tests, (behaviors, etc.) excluding
+ * database driver-specific tests
+ *
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ */
+class ModelGroupTest extends TestSuite {
+
+/**
+ * label property
+ *
+ * @var string
+ * @access public
+ */
+	var $label = 'All Model tests';
+
+/**
+ * ModelGroupTest method
+ *
+ * @access public
+ * @return void
+ */
+	function ModelGroupTest() {
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'model' . DS . 'model_behavior');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'model' . DS . 'model_read');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'model' . DS . 'model_write');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'model' . DS . 'model_delete');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'model' . DS . 'model_integration');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'model' . DS . 'model_validation');
+	}
+}

Added: trunk/src/Web/cake/tests/groups/no_cross_contamination.group.php
===================================================================
--- trunk/src/Web/cake/tests/groups/no_cross_contamination.group.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/groups/no_cross_contamination.group.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,67 @@
+<?php
+/**
+ * NoCrossContaminationGroupTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * NoCrossContaminationGroupTest class
+ *
+ * This test group will run all tests
+ * that are proper isolated to be run in sequence
+ * without affected each other
+ *
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ */
+class NoCrossContaminationGroupTest extends TestSuite {
+
+/**
+ * label property
+ *
+ * @var string
+ * @access public
+ */
+	var $label = 'No Cross Contamination';
+
+/**
+ * blacklist property
+ *
+ * @var string
+ * @access public
+ */
+	var $blacklist = array('cake_test_case.test.php', 'object.test.php');
+
+/**
+ * NoCrossContaminationGroupTest method
+ *
+ * @access public
+ * @return void
+ */
+	function NoCrossContaminationGroupTest() {
+		App::import('Core', 'Folder');
+
+		$Folder = new Folder(CORE_TEST_CASES);
+
+		foreach ($Folder->findRecursive('.*\.test\.php', true) as $file) {
+			if (in_array(basename($file), $this->blacklist)) {
+				continue;
+			}
+			TestManager::addTestFile($this, $file);
+		}
+	}
+}

Added: trunk/src/Web/cake/tests/groups/routing_system.group.php
===================================================================
--- trunk/src/Web/cake/tests/groups/routing_system.group.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/groups/routing_system.group.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,49 @@
+<?php
+/**
+ * RoutingSystemGroupTest
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ * @since         CakePHP(tm) v 1.2.0.5517
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * RoutingSystemGroupTest class
+ *
+ * This test group will run all the Router/Dispatcher (and related) tests
+ *
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ */
+class RoutingSystemGroupTest extends TestSuite {
+
+/**
+ * label property
+ *
+ * @var string 'Routing system'
+ * @access public
+ */
+	var $label = 'Router and Dispatcher';
+
+/**
+ * RoutingSystemGroupTest method
+ *
+ * @access public
+ * @return void
+ */
+	function RoutingSystemGroupTest() {
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'dispatcher');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'router');
+	}
+}

Added: trunk/src/Web/cake/tests/groups/socket.group.php
===================================================================
--- trunk/src/Web/cake/tests/groups/socket.group.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/groups/socket.group.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,55 @@
+<?php
+/**
+ * Socket and HttpSocket Group tests
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake.tests
+ * @subpackage    cake.tests.groups
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/** Socket and HttpSocket tests
+ *
+ * This test group will run socket class tests (socket, http_socket).
+ *
+ * @package       cake.tests
+ * @subpackage    cake.tests.groups
+ */
+
+/**
+ * SocketGroupTest class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ */
+class SocketGroupTest extends TestSuite {
+
+/**
+ * label property
+ *
+ * @var string 'Socket and HttpSocket tests'
+ * @access public
+ */
+	var $label = 'CakeSocket and HttpSocket tests';
+
+/**
+ * SocketGroupTest method
+ *
+ * @access public
+ * @return void
+ */
+	function SocketGroupTest() {
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'cake_socket');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'http_socket');
+	}
+}

Added: trunk/src/Web/cake/tests/groups/test_suite.group.php
===================================================================
--- trunk/src/Web/cake/tests/groups/test_suite.group.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/groups/test_suite.group.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,52 @@
+<?php
+/**
+ * TestSuiteGroupTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * TestSuiteGroupTest class
+ *
+ * This test group will run the test cases for the test suite classes.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ */
+class TestSuiteGroupTest extends TestSuite {
+
+/**
+ * label property
+ *
+ * @var string 'Socket and HttpSocket tests'
+ * @access public
+ */
+	var $label = 'TestSuite';
+
+/**
+ * TestSuiteGroupTest method
+ *
+ * @access public
+ * @return void
+ */
+	function TestSuiteGroupTest() {
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'test_manager');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'code_coverage_manager');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'cake_test_case');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'cake_test_fixture');
+
+	}
+}

Added: trunk/src/Web/cake/tests/groups/view.group.php
===================================================================
--- trunk/src/Web/cake/tests/groups/view.group.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/groups/view.group.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,49 @@
+<?php
+/**
+ * ViewsGroupTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * ViewsGroupTest class
+ *
+ * This test group will run view class tests (view, theme)
+ *
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ */
+class ViewsGroupTest extends TestSuite {
+
+/**
+ * label property
+ *
+ * @var string 'All core views'
+ * @access public
+ */
+	var $label = 'View and ThemeView';
+
+/**
+ * ViewsGroupTest method
+ *
+ * @access public
+ * @return void
+ */
+	function ViewsGroupTest() {
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'view' . DS . 'view');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'view' . DS . 'theme');
+	}
+}

Added: trunk/src/Web/cake/tests/groups/xml.group.php
===================================================================
--- trunk/src/Web/cake/tests/groups/xml.group.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/groups/xml.group.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,50 @@
+<?php
+/**
+ * XmlGroupTest file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * XmlGroupTest class
+ *
+ * This test group will run view class tests (view, theme).
+ *
+ * @package       cake
+ * @subpackage    cake.tests.groups
+ */
+class XmlGroupTest extends TestSuite {
+
+/**
+ * label property
+ *
+ * @var string 'All core views'
+ * @access public
+ */
+	var $label = 'Xml based classes (Xml, XmlHelper and RssHelper)';
+
+/**
+ * XmlGroupTest method
+ *
+ * @access public
+ * @return void
+ */
+	function XmlGroupTest() {
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'xml');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'view' . DS . 'helpers' . DS .'rss');
+		TestManager::addTestFile($this, CORE_TEST_CASES . DS . 'libs' . DS . 'view' . DS . 'helpers' . DS .'xml');
+	}
+}

Added: trunk/src/Web/cake/tests/lib/cake_test_case.php
===================================================================
--- trunk/src/Web/cake/tests/lib/cake_test_case.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/lib/cake_test_case.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,835 @@
+<?php
+/**
+ * CakeTestCase file
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.libs
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @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');
+
+/**
+ * CakeTestDispatcher
+ *
+ * @package       cake
+ * @subpackage    cake.cake.tests.lib
+ */
+class CakeTestDispatcher extends Dispatcher {
+
+/**
+ * controller property
+ *
+ * @var Controller
+ * @access public
+ */
+	var $controller;
+	var $testCase;
+
+/**
+ * testCase method
+ *
+ * @param CakeTestCase $testCase
+ * @return void
+ * @access public
+ */
+	function testCase(&$testCase) {
+		$this->testCase =& $testCase;
+	}
+
+/**
+ * invoke method
+ *
+ * @param Controller $controller
+ * @param array $params
+ * @param boolean $missingAction
+ * @return Controller
+ * @access protected
+ */
+	function _invoke(&$controller, $params, $missingAction = false) {
+		$this->controller =& $controller;
+
+		if (array_key_exists('layout', $params)) {
+			$this->controller->layout = $params['layout'];
+		}
+
+		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;
+	}
+}
+
+/**
+ * CakeTestCase class
+ *
+ * @package       cake
+ * @subpackage    cake.cake.tests.lib
+ */
+class CakeTestCase extends UnitTestCase {
+
+/**
+ * Methods used internally.
+ *
+ * @var array
+ * @access public
+ */
+	var $methods = array('start', 'end', 'startcase', 'endcase', 'starttest', 'endtest');
+
+/**
+ * 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();
+
+/**
+ * truncated property
+ *
+ * @var boolean
+ * @access private
+ */
+	var $__truncated = true;
+
+/**
+ * savedGetData property
+ *
+ * @var array
+ * @access private
+ */
+	var $__savedGetData = 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.
+ * @return void
+ * @access public
+ */
+	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.
+ * @return void
+ * @access public
+ */
+	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.
+ * @return void
+ * @access public
+ */
+	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.
+ * @return void
+ * @access public
+ */
+	function endTest($method) {
+	}
+
+/**
+ * Overrides SimpleTestCase::assert to enable calling of skipIf() from within tests
+ *
+ * @param Expectation $expectation
+ * @param mixed $compare
+ * @param string $message
+ * @return boolean|null
+ * @access public
+ */
+	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
+ *
+ * @param boolean $shouldSkip
+ * @param string $message
+ * @return boolean
+ * @access public
+ */
+	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().
+ * @return void
+ * @access public
+ */
+	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().
+ * @return void
+ * @access public
+ */
+	function endController(&$controller, $params = array()) {
+		if (isset($this->db) && isset($this->_actionFixtures) && !empty($this->_actionFixtures) && $this->dropTables) {
+			foreach ($this->_actionFixtures as $fixture) {
+				$fixture->drop($this->db);
+			}
+		}
+	}
+
+/**
+ * Executes a Cake URL, and can get (depending on the $params['return'] value):
+ *
+ * Params:
+ * - 'return' has several possible values:
+ *   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
+ *
+ * - 'fixturize' - Set to true if you want to copy model data from 'connection' to the test_suite connection
+ * - 'data' - The data you want to insert into $this->data in the controller.
+ * - 'connection' - Which connection to use in conjunction with fixturize (defaults to 'default')
+ * - 'method' - What type of HTTP method to simulate (defaults to post)
+ *
+ * @param string $url Cake URL to execute (e.g: /articles/view/455)
+ * @param mixed $params Parameters (see above), 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.
+ * @return void
+ * @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.
+ *
+ * @return void
+ * @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) {
+				$table = $this->db->config['prefix'] . $fixture->table;
+				if (in_array($table, $sources)) {
+					$fixture->drop($this->db);
+					$fixture->create($this->db);
+				} elseif (!in_array($table, $sources)) {
+					$fixture->create($this->db);
+				}
+			}
+		}
+	}
+
+/**
+ * Runs as last test to drop tables.
+ *
+ * @return void
+ * @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.
+ * @return void
+ * @access public
+ */
+	function after($method) {
+		$isTestMethod = !in_array(strtolower($method), array('start', 'end'));
+
+		if (isset($this->_fixtures) && isset($this->db) && $isTestMethod) {
+			foreach (array_reverse($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() {
+		return array_merge(
+			array('start', 'startCase'),
+			array_diff(parent::getTests(), array('testAction', 'testaction')),
+			array('endCase', 'end')
+		);
+	}
+
+/**
+ * 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.
+ * @return void
+ * @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(sprintf(__('Referenced fixture class %s not found', true), $class), 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
+ * @return boolean
+ * @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) {
+			if (!is_array($tags)) {
+				$tags = (string)$tags;
+			}
+			$i++;
+			if (is_string($tags) && $tags{0} == '<') {
+				$tags = array(substr($tags, 1) => array());
+			} elseif (is_string($tags)) {
+				$tagsTrimmed = preg_replace('/\s+/m', '', $tags);
+
+				if (preg_match('/^\*?\//', $tags, $match) && $tagsTrimmed !== '//') {
+					$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();
+				$i = 1;
+				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);
+						} elseif (!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;
+					}
+					$i++;
+				}
+				if ($attrs) {
+					$permutations = $this->__array_permute($attrs);
+
+					$permutationTokens = array();
+					foreach ($permutations as $permutation) {
+						$permutationTokens[] = implode('', $permutation);
+					}
+					$regex[] = array(
+						sprintf('%s', implode(', ', $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');
+	}
+
+/**
+ * Initialize DB connection.
+ *
+ * @return void
+ * @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.
+ *
+ * @return void
+ * @access protected
+ */
+	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 (App::core('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::pluginPath($pluginName) . 'tests' . DS . 'fixtures',
+					TESTS . 'fixtures',
+					VENDORS . 'tests' . DS . 'fixtures'
+				);
+			} 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);
+		}
+	}
+
+/**
+ * Generates all permutation of an array $items and returns them in a new array.
+ *
+ * @param array $items An array of items
+ * @return array
+ * @access private
+ */
+	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;
+		}
+	}
+}

Added: trunk/src/Web/cake/tests/lib/cake_test_fixture.php
===================================================================
--- trunk/src/Web/cake/tests/lib/cake_test_fixture.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/lib/cake_test_fixture.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,213 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.libs
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @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 {
+
+/**
+ * Name of the object
+ *
+ * @var string
+ */
+	var $name = null;
+
+/**
+ * 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', 'CakeSchema');
+		$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';
+				$this->table = $db->fullTableName($model, false);
+				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);
+				ClassRegistry::flush();
+			}
+
+			if (!empty($db->config['prefix']) && strpos($this->table, $db->config['prefix']) === 0) {
+				$this->table = str_replace($db->config['prefix'], '', $this->table);
+			}
+
+			if (isset($import['records']) && $import['records'] !== false && isset($model) && isset($db)) {
+				$this->records = array();
+				$query = array(
+					'fields' => $db->fields($model, null, array_keys($this->fields)),
+					'table' => $db->fullTableName($model),
+					'alias' => $model->alias,
+					'conditions' => array(),
+					'order' => null,
+					'limit' => null,
+					'group' => null
+				);
+				$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) {
+		if (empty($this->fields)) {
+			return false;
+		}
+		$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)) {
+				$fields = array();
+				foreach($this->records as $record) {
+					$fields = array_merge($fields, array_keys(array_intersect_key($record, $this->fields)));
+				}
+				$fields = array_unique($fields);
+				$default = array_fill_keys($fields, null);
+				foreach ($this->records as $record) {
+					$recordValues = array();
+					foreach(array_merge($default, array_map(array(&$db, 'value'), $record)) as $value) {
+						$recordValues[] = is_null($value) ? 'NULL' : $value;
+					}
+					$values[] = '(' . implode(', ', $recordValues) . ')';
+				}
+				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;
+	}
+}

Added: trunk/src/Web/cake/tests/lib/cake_test_model.php
===================================================================
--- trunk/src/Web/cake/tests/lib/cake_test_model.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/lib/cake_test_model.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.libs
+ * @since         CakePHP(tm) v 1.2.0.4667
+ * @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;
+}

Added: trunk/src/Web/cake/tests/lib/cake_test_suite_dispatcher.php
===================================================================
--- trunk/src/Web/cake/tests/lib/cake_test_suite_dispatcher.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/lib/cake_test_suite_dispatcher.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,249 @@
+<?php
+/**
+ * CakeTestSuiteDispatcher controls dispatching TestSuite web based requests.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.tests.lib
+ * @since         CakePHP(tm) v 1.3
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+require_once CAKE_TESTS_LIB . 'test_manager.php';
+
+/**
+ * CakeTestSuiteDispatcher handles web requests to the test suite and runs the correct action.
+ *
+ * @package cake.tests.libs
+ */
+class CakeTestSuiteDispatcher {
+/**
+ * 'Request' parameters
+ *
+ * @var array
+ */
+	var $params = array(
+		'codeCoverage' => false,
+		'group' => null,
+		'case' => null,
+		'app' => false,
+		'plugin' => null,
+		'output' => 'html',
+		'show' => 'groups',
+		'show_passes' => false
+	);
+
+/**
+ * The classname for the TestManager being used
+ *
+ * @var string
+ */
+	var $_managerClass = 'TestManager';
+
+/**
+ * The Instance of the Manager being used.
+ *
+ * @var TestManager subclass
+ */
+	var $Manager;
+
+/**
+ * Baseurl for the request
+ *
+ * @var string
+ */
+	var $_baseUrl;
+
+/**
+ * Base dir of the request.  Used for accessing assets.
+ *
+ * @var string
+ */
+	var $_baseDir;
+
+/**
+ * constructor
+ *
+ * @return void
+ */
+	function CakeTestSuiteDispatcher() {
+		$this->_baseUrl = $_SERVER['PHP_SELF'];
+		$dir = rtrim(dirname($this->_baseUrl), '\\');
+		$this->_baseDir = ($dir === '/') ? $dir : $dir . '/';
+	}
+
+/**
+ * Runs the actions required by the URL parameters.
+ *
+ * @return void
+ */
+	function dispatch() {
+		$this->_checkSimpleTest();
+		$this->_parseParams();
+
+		if ($this->params['group']) {
+			$this->_runGroupTest();
+		} elseif ($this->params['case']) {
+			$this->_runTestCase();
+		} elseif (isset($_GET['show']) && $_GET['show'] == 'cases') {
+			$this->_testCaseList();
+		} else {
+			$this->_groupTestList();
+		}
+
+		$output = ob_get_clean();
+		echo $output;
+	}
+
+/**
+ * Checks that simpleTest is installed.  Will exit if it doesn't
+ *
+ * @return void
+ */
+	function _checkSimpleTest() {
+		if (!App::import('Vendor', 'simpletest' . DS . 'reporter')) {
+			$baseDir = $this->_baseDir;
+			include CAKE_TESTS_LIB . 'templates' . DS . 'simpletest.php';
+			exit();
+		}
+	}
+
+/**
+ * Checks for the xdebug extension required to do code coverage. Displays an error
+ * if xdebug isn't installed.
+ *
+ * @return void
+ */
+	function _checkXdebug() {
+		if (!extension_loaded('xdebug')) {
+			$baseDir = $this->_baseDir;
+			include CAKE_TESTS_LIB . 'templates' . DS . 'xdebug.php';
+			exit();
+		}
+	}
+
+/**
+ * Generates a page containing the a list of test cases that could be run.
+ *
+ * @return void
+ */
+	function _testCaseList() {
+		$Reporter =& $this->getReporter();
+		$Reporter->paintDocumentStart();
+		$Reporter->paintTestMenu();
+		$Reporter->testCaseList();
+		$Reporter->paintDocumentEnd();
+	}
+
+/**
+ * Generates a page containing a list of group tests that could be run.
+ *
+ * @return void
+ */
+	function _groupTestList() {
+		$Reporter =& $this->getReporter();
+		$Reporter->paintDocumentStart();
+		$Reporter->paintTestMenu();
+		$Reporter->groupTestList();
+		$Reporter->paintDocumentEnd();
+	}
+
+/**
+ * Sets the Manager to use for the request.
+ *
+ * @return string The manager class name
+ * @static
+ */
+	function &getManager() {
+		if (empty($this->Manager)) {
+			$this->Manager = new $this->_managerClass();
+		}
+		return $this->Manager;
+	}
+
+/**
+ * Gets the reporter based on the request parameters
+ *
+ * @return void
+ * @static
+ */
+	function &getReporter() {
+		static $Reporter = NULL;
+		if (!$Reporter) {
+			$type = strtolower($this->params['output']);
+			$coreClass = 'Cake' . ucwords($this->params['output']) . 'Reporter';
+			$coreFile = CAKE_TESTS_LIB . 'reporter' . DS . 'cake_' . $type . '_reporter.php';
+
+			$appClass = $this->params['output'] . 'Reporter';
+			$appFile = APPLIBS . 'test_suite' . DS . 'reporter' . DS . $type . '_reporter.php';
+			if (file_exists($appFile) && include_once $appFile) {
+				$Reporter =& new $appClass(null, $this->params);
+			} elseif (include_once $coreFile) {
+				$Reporter =& new $coreClass(null, $this->params);
+			}
+		}
+		return $Reporter;
+	}
+
+/**
+ * Parse url params into a 'request'
+ *
+ * @return void
+ */
+	function _parseParams() {
+		if (!isset($_SERVER['SERVER_NAME'])) {
+			$_SERVER['SERVER_NAME'] = '';
+		}
+		foreach ($this->params as $key => $value) {
+			if (isset($_GET[$key])) {
+				$this->params[$key] = $_GET[$key];
+			}
+		}
+		if (isset($_GET['code_coverage'])) {
+			$this->params['codeCoverage'] = true;
+			require_once CAKE_TESTS_LIB . 'code_coverage_manager.php';
+			$this->_checkXdebug();
+		}
+		$this->params['baseUrl'] = $this->_baseUrl;
+		$this->params['baseDir'] = $this->_baseDir;
+		$this->getManager();
+	}
+
+/**
+ * Runs the group test case.
+ *
+ * @return void
+ */
+	function _runGroupTest() {
+		$Reporter =& CakeTestSuiteDispatcher::getReporter();
+		if ($this->params['codeCoverage']) {
+			CodeCoverageManager::init($this->params['group'], $Reporter);
+		}
+		if ('all' == $this->params['group']) {
+			$this->Manager->runAllTests($Reporter);
+		} else {
+			$this->Manager->runGroupTest(ucfirst($this->params['group']), $Reporter);
+		}
+	}
+
+/**
+ * Runs a test case file.
+ *
+ * @return void
+ */
+	function _runTestCase() {
+		$Reporter =& CakeTestSuiteDispatcher::getReporter();
+		if ($this->params['codeCoverage']) {
+			CodeCoverageManager::init($this->params['case'], $Reporter);
+		}
+		$this->Manager->runTestCase($this->params['case'], $Reporter);
+	}
+}

Added: trunk/src/Web/cake/tests/lib/cake_web_test_case.php
===================================================================
--- trunk/src/Web/cake/tests/lib/cake_web_test_case.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/lib/cake_web_test_case.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,33 @@
+<?php
+/**
+ * CakeWebTestCase a simple wrapper around WebTestCase
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.lib
+ * @since         CakePHP(tm) v 1.2.0.4433
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * Ignore base class.
+ */
+	SimpleTest::ignore('CakeWebTestCase');
+
+/**
+ * Simple wrapper for the WebTestCase provided by SimpleTest
+ *
+ * @package       cake
+ * @subpackage    cake.cake.tests.lib
+ */
+class CakeWebTestCase extends WebTestCase {
+}

Added: trunk/src/Web/cake/tests/lib/code_coverage_manager.php
===================================================================
--- trunk/src/Web/cake/tests/lib/code_coverage_manager.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/lib/code_coverage_manager.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,804 @@
+<?php
+/**
+ * A class to manage all aspects for Code Coverage Analysis
+ *
+ * This class
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.lib
+ * @since         CakePHP(tm) v 1.2.0.4433
+ * @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];
+	}
+
+/**
+ * Initializes a new Coverage Analyzation for a given test case file
+ *
+ * @param string $testCaseFile The test case file being covered.
+ * @param object $reporter Instance of the reporter running.
+ * @return void
+ * @static
+ */
+	function init($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.', true), E_USER_ERROR);
+		}
+		$manager->setParams($reporter);
+		$manager->testCaseFile = $testCaseFile;
+	}
+
+/**
+ * Start/resume Code coverage collection.
+ *
+ * @return void
+ * @static
+ */
+	function start() {
+		xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);
+	}
+
+/**
+ * Stops/pauses code coverage collection. Does not clean the
+ * code coverage memory. Use clean() to clear code coverage memory
+ *
+ * @return void
+ * @static
+ */
+	function stop() {
+		xdebug_stop_code_coverage(false);
+	}
+
+/**
+ * Clears the existing code coverage information.  Also stops any
+ * running collection.
+ *
+ * @return void
+ * @static
+ */
+	function clear() {
+		xdebug_stop_code_coverage();
+	}
+
+/**
+ * Set the parameters from a reporter to the CodeCoverageManager
+ *
+ * @return void
+ */
+	function setParams(&$reporter) {
+		if ($reporter->params['app']) {
+			$this->appTest = true;
+		}
+
+		if ($reporter->params['group']) {
+			$this->groupTest = true;
+		}
+
+		if ($reporter->params['plugin']) {
+			$this->pluginTest = Inflector::underscore($reporter->params['plugin']);
+		}
+	}
+
+/**
+ * Stops the current code coverage analyzation and dumps a nice report
+ * depending on the reporter that was passed to start()
+ *
+ * @return void
+ * @static
+ */
+	function report($output = true) {
+		$manager =& CodeCoverageManager::getInstance();
+
+		CodeCoverageManager::stop();
+		CodeCoverageManager::clear();
+
+		list($coverageData, $testObjectFile) = $manager->_getCoverageData();
+
+		if (empty($coverageData) && $output) {
+			echo "The test object file is never loaded.\n";
+		}
+
+		if (!$manager->groupTest) {
+			$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 'CakeCliReporter':
+				default:
+					$result = $manager->reportCaseCli(@file($testObjectFile), $coverageData, $execCodeLines, $manager->numDiffContextLines);
+					break;
+			}
+		} else {
+			$execCodeLines = $manager->__getExecutableLines($testObjectFile);
+			$result = '';
+
+			switch (get_class($manager->reporter)) {
+				case 'CakeHtmlReporter':
+					$result = $manager->reportGroupHtml($testObjectFile, $coverageData, $execCodeLines, $manager->numDiffContextLines);
+					break;
+				case 'CakeCliReporter':
+				default:
+					$result = $manager->reportGroupCli($testObjectFile, $coverageData, $execCodeLines, $manager->numDiffContextLines);
+					break;
+			}
+		}
+
+		if ($output) {
+			echo $result;
+		}
+	}
+
+/**
+ * Gets the coverage data for the test case or group test that is being run.
+ *
+ * @return void
+ */
+	function _getCoverageData() {
+		$coverageData = array();
+		$dump = xdebug_get_code_coverage();
+
+		if ($this->groupTest) {
+			$testObjectFile = $this->__testObjectFilesFromGroupFile($this->testCaseFile, $this->appTest);
+			foreach ($testObjectFile as $file) {
+				if (!file_exists($file)) {
+					trigger_error(sprintf(__('This test object file is invalid: %s', true), $file));
+					return ;
+				}
+			}
+			foreach ($testObjectFile as $file) {
+				if (isset($dump[$file])) {
+					$coverageData[$file] = $dump[$file];
+				}
+			}
+		} else {
+			$testObjectFile = $this->__testObjectFileFromCaseFile($this->testCaseFile, $this->appTest);
+
+			if (!file_exists($testObjectFile)) {
+				trigger_error(sprintf(__('This test object file is invalid: %s', true), $testObjectFile));
+				return ;
+			}
+
+			if (isset($dump[$testObjectFile])) {
+				$coverageData = $dump[$testObjectFile];
+			}
+		}
+		return array($coverageData, $testObjectFile);
+	}
+
+/**
+ * Diff reporting
+ *
+ * @param string $testObjectFile
+ * @param string $coverageData
+ * @param string $execCodeLines
+ * @param string $output
+ * @return void
+ * @static
+ */
+	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
+ * @static
+ */
+	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
+ * @static
+ */
+	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
+ * @static
+ */
+	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->read();
+
+		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;
+		if (!$isApp) {
+			$path = CAKE_CORE_INCLUDE_PATH . DS . 'cake' . DS . 'tests';
+		}
+		if (!!$manager->pluginTest) {
+			$path = App::pluginPath($manager->pluginTest) . DS . 'tests';
+		}
+
+		$result = array();
+		if ($groupFile == 'all') {
+			$files = array_keys($testManager->getTestCaseList());
+			foreach ($files as $file) {
+				$file = str_replace(DS . 'tests' . DS . 'cases' . DS, DS, $file);
+				$file = str_replace('.test.php', '.php', $file);
+				$file = str_replace(DS . DS, DS, $file);
+				$result[] = $file;
+			}
+		} else {
+			$path .= DS . 'groups' . DS . $groupFile . $testManager->_groupExtension;
+			if (!file_exists($path)) {
+				trigger_error(__('This group file does not exist!', true));
+				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\'';
+			$pluginTest .= '|App::pluginPath\(\'' . $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);
+		$class = 'bad';
+		if ($codeCoverage > 50) {
+			$class = 'ok';
+		}
+		if ($codeCoverage > 80) {
+			$class = 'good';
+		}
+		return $report = "Code Coverage: $codeCoverage% ($class)\n";
+	}
+
+/**
+ * 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!', true));
+		}
+		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 = App::path('plugins');
+			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;
+	}
+}

Added: trunk/src/Web/cake/tests/lib/reporter/cake_base_reporter.php
===================================================================
--- trunk/src/Web/cake/tests/lib/reporter/cake_base_reporter.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/lib/reporter/cake_base_reporter.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,225 @@
+<?php
+/**
+ * CakeBaseReporter contains common functionality to all cake test suite reporters.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.tests.libs.reporter
+ * @since         CakePHP(tm) v 1.3
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+
+/**
+ * CakeBaseReporter contains common reporting features used in the CakePHP Test suite
+ *
+ * @package cake
+ * @subpackage cake.tests.lib
+ */
+class CakeBaseReporter extends SimpleReporter {
+
+/**
+ * Time the test runs started.
+ *
+ * @var integer
+ * @access protected
+ */
+	var $_timeStart = 0;
+
+/**
+ * Time the test runs ended
+ *
+ * @var integer
+ * @access protected
+ */
+	var $_timeEnd = 0;
+
+/**
+ * Duration of all test methods.
+ *
+ * @var integer
+ * @access protected
+ */
+	var $_timeDuration = 0;
+
+/**
+ * Array of request parameters.  Usually parsed GET params.
+ *
+ * @var array
+ */
+	var $params = array();
+
+/**
+ * Character set for the output of test reporting.
+ *
+ * @var string
+ * @access protected
+ */
+	var $_characterSet;
+
+/**
+ * Does nothing yet. The first output will
+ * be sent on the first test start.
+ *
+ * ### Params
+ *
+ * - show_passes - Should passes be shown
+ * - plugin - Plugin test being run?
+ * - app - App test being run.
+ * - case - The case being run
+ * - codeCoverage - Whether the case/group being run is being code covered.
+ * 
+ * @param string $charset The character set to output with. Defaults to UTF-8
+ * @param array $params Array of request parameters the reporter should use. See above.
+ * @access public
+ */
+	function CakeBaseReporter($charset = 'utf-8', $params = array()) {
+		$this->SimpleReporter();
+		if (!$charset) {
+			$charset = 'utf-8';
+		}
+		$this->_characterSet = $charset;
+		$this->params = $params;
+	}
+
+/**
+ * Signals / Paints the beginning of a TestSuite executing.
+ * Starts the timer for the TestSuite execution time.
+ *
+ * @param string $test_name Name of the test that is being run.
+ * @param integer $size 
+ * @return void
+ */
+	function paintGroupStart($test_name, $size) {
+		if (empty($this->_timeStart)) {
+			$this->_timeStart = $this->_getTime();
+		}
+		parent::paintGroupStart($test_name, $size);
+	}
+
+/**
+ * Signals/Paints the end of a TestSuite. All test cases have run
+ * and timers are stopped.
+ *
+ * @param string $test_name Name of the test that is being run.
+ * @return void
+ */
+	function paintGroupEnd($test_name) {
+		$this->_timeEnd = $this->_getTime();
+		$this->_timeDuration = $this->_timeEnd - $this->_timeStart;
+		parent::paintGroupEnd($test_name);
+	}
+
+/**
+ * Paints the beginning of a test method being run.  This is used
+ * to start/resume the code coverage tool.
+ *
+ * @param string $method The method name being run.
+ * @return void
+ */
+	function paintMethodStart($method) {
+		parent::paintMethodStart($method);
+		if (!empty($this->params['codeCoverage'])) {
+			CodeCoverageManager::start();
+		}
+	}
+
+/**
+ * Paints the end of a test method being run.  This is used
+ * to pause the collection of code coverage if its being used.
+ *
+ * @param string $method The name of the method being run.
+ * @return void
+ */
+	function paintMethodEnd($method) {
+		parent::paintMethodEnd($method);
+		if (!empty($this->params['codeCoverage'])) {
+			CodeCoverageManager::stop();
+		}
+	}
+
+/**
+ * Get the current time in microseconds. Similar to getMicrotime in basics.php
+ * but in a separate function to reduce dependancies.
+ *
+ * @return float Time in microseconds
+ * @access protected
+ */
+	function _getTime() {
+		list($usec, $sec) = explode(' ', microtime());
+		return ((float)$sec + (float)$usec);
+	}
+
+/**
+ * Retrieves a list of test cases from the active Manager class,
+ * displaying it in the correct format for the reporter subclass
+ *
+ * @return mixed
+ */
+	function testCaseList() {
+		$testList = TestManager::getTestCaseList();
+		return $testList;
+	}
+
+/**
+ * Retrieves a list of group test cases from the active Manager class
+ * displaying it in the correct format for the reporter subclass.
+ *
+ * @return void
+ */
+	function groupTestList() {
+		$testList = TestManager::getGroupTestList();
+		return $testList;
+	}
+
+/**
+ * Paints the start of the response from the test suite.
+ * Used to paint things like head elements in an html page.
+ *
+ * @return void
+ */
+	function paintDocumentStart() {
+
+	}
+
+/**
+ * Paints the end of the response from the test suite.
+ * Used to paint things like </body> in an html page.
+ *
+ * @return void
+ */
+	function paintDocumentEnd() {
+		
+	}
+
+/**
+ * Paint a list of test sets, core, app, and plugin test sets
+ * available.
+ *
+ * @return void
+ */
+	function paintTestMenu() {
+		
+	}
+
+/**
+ * Get the baseUrl if one is available.
+ *
+ * @return string The base url for the request.
+ */
+	function baseUrl() {
+		if (!empty($_SERVER['PHP_SELF'])) {
+			return $_SERVER['PHP_SELF'];
+		}
+		return '';
+	}
+
+}

Added: trunk/src/Web/cake/tests/lib/reporter/cake_cli_reporter.php
===================================================================
--- trunk/src/Web/cake/tests/lib/reporter/cake_cli_reporter.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/lib/reporter/cake_cli_reporter.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,178 @@
+<?php
+/**
+ * Cake CLI test reporter.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.libs
+ * @since         CakePHP(tm) v 1.2.0.4433
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+	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;'));
+	}
+
+include_once dirname(__FILE__) . DS . 'cake_base_reporter.php';
+
+/**
+ * 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.tests.libs.reporter
+ */
+class CakeCliReporter extends CakeBaseReporter {
+/**
+ * separator string for fail, error, exception, and skip messages.
+ *
+ * @var string
+ */
+	var $separator = '->';
+
+/**
+ * array of 'request' parameters
+ *
+ * @var array
+ */
+	var $params = array();
+
+/**
+ * Constructor
+ *
+ * @param string $separator 
+ * @param array $params 
+ * @return void
+ */
+	function CakeCLIReporter($charset = 'utf-8', $params = array()) {
+		$this->CakeBaseReporter($charset, $params);
+	}
+
+	function setFailDetailSeparator($separator) {
+		$this->separator = $separator;
+	}
+
+/**
+ * Paint fail faildetail to STDERR.
+ *
+ * @param string $message Message of the fail.
+ * @return void
+ * @access public
+ */
+	function paintFail($message) {
+		parent::paintFail($message);
+		$message .= $this->_getBreadcrumb();
+		fwrite(STDERR, 'FAIL' . $this->separator . $message);
+	}
+
+/**
+ * Paint PHP errors to STDERR.
+ *
+ * @param string $message Message of the Error
+ * @return void
+ * @access public
+ */
+	function paintError($message) {
+		parent::paintError($message);
+		$message .= $this->_getBreadcrumb();
+		fwrite(STDERR, 'ERROR' . $this->separator . $message);
+	}
+
+/**
+ * Paint exception faildetail to STDERR.
+ *
+ * @param object $exception Exception instance
+ * @return void
+ * @access public
+ */
+	function paintException($exception) {
+		parent::paintException($exception);
+		$message = sprintf('Unexpected exception of type [%s] with message [%s] in [%s] line [%s]',
+			get_class($exception),
+			$exception->getMessage(),
+			$exception->getFile(),
+			$exception->getLine()
+		);
+		$message .= $this->_getBreadcrumb();
+		fwrite(STDERR, 'EXCEPTION' . $this->separator . $message);
+	}
+
+/**
+ * Get the breadcrumb trail for the current test method/case
+ *
+ * @return string The string for the breadcrumb
+ */
+	function _getBreadcrumb() {
+		$breadcrumb = $this->getTestList();
+		array_shift($breadcrumb);
+		$out = "\n\tin " . implode("\n\tin ", array_reverse($breadcrumb));
+		$out .= "\n\n";
+		return $out;
+	}
+
+/**
+ * Paint a test skip message
+ *
+ * @param string $message The message of the skip
+ * @return void
+ */
+	function paintSkip($message) {
+		parent::paintSkip($message);
+		fwrite(STDOUT, 'SKIP' . $this->separator . $message . "\n\n");
+	}
+
+/**
+ * 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";
+			$buffer .= $this->_timeStats();
+			fwrite(STDOUT, $buffer);
+		} else {
+			fwrite(STDOUT, $buffer . $this->getPassCount() . " passes.\n" . $this->_timeStats());
+		}
+
+		if (
+			isset($this->params['codeCoverage']) && 
+			$this->params['codeCoverage'] && 
+			class_exists('CodeCoverageManager')
+		) {
+			CodeCoverageManager::report();
+		}
+	}
+
+/**
+ * Get the time and memory stats for this test case/group
+ *
+ * @return string String content to display
+ * @access protected
+ */
+	function _timeStats() {
+		$out = 'Time taken by tests (in seconds): ' . $this->_timeDuration . "\n";
+		if (function_exists('memory_get_peak_usage')) {
+			$out .= 'Peak memory use: (in bytes): ' . number_format(memory_get_peak_usage()) . "\n";
+		}
+		return $out;
+	}
+}

Added: trunk/src/Web/cake/tests/lib/reporter/cake_html_reporter.php
===================================================================
--- trunk/src/Web/cake/tests/lib/reporter/cake_html_reporter.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/lib/reporter/cake_html_reporter.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,378 @@
+<?php
+/**
+ * CakeHtmlReporter
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.tests.libs.reporter
+ * @since         CakePHP(tm) v 1.2.0.4433
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+include_once dirname(__FILE__) . DS . 'cake_base_reporter.php';
+
+/**
+ * CakeHtmlReporter Reports Results of TestSuites and Test Cases
+ * in an HTML format / context.
+ *
+ * @package cake
+ * @subpackage cake.tests.lib
+ */
+class CakeHtmlReporter extends CakeBaseReporter {
+/**
+ * Constructor
+ *
+ * @param string $charset 
+ * @param string $params 
+ * @return void
+ */
+	function CakeHtmlReporter($charset = 'utf-8', $params = array()) {
+		$params = array_map(array($this, '_htmlEntities'), $params);
+		$this->CakeBaseReporter($charset, $params);
+	}
+/**
+ * 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.
+ * @return void
+ * @access public
+ */
+	function paintHeader($testName) {
+		$this->sendNoCacheHeaders();
+		$this->paintDocumentStart();
+		$this->paintTestMenu();
+		printf("<h2>%s</h2>\n", $this->_htmlEntities($testName));
+		echo "<ul class='tests'>\n";
+	}
+
+/**
+ * Paints the document start content contained in header.php
+ *
+ * @return void
+ */
+	function paintDocumentStart() {
+		ob_start();
+		$baseDir = $this->params['baseDir'];
+		include CAKE_TESTS_LIB . 'templates' . DS . 'header.php';
+	}
+
+/**
+ * Paints the menu on the left side of the test suite interface.
+ * Contains all of the various plugin, core, and app buttons.
+ *
+ * @return void
+ */
+	function paintTestMenu() {
+		$groups = $this->baseUrl() . '?show=groups';
+		$cases = $this->baseUrl() . '?show=cases';
+		$plugins = App::objects('plugin', null, false);
+		sort($plugins);
+		include CAKE_TESTS_LIB . 'templates' . DS . 'menu.php';
+	}
+
+/**
+ * Retrieves and paints the list of tests cases in an HTML format.
+ *
+ * @return void
+ */
+	function testCaseList() {
+		$testCases = parent::testCaseList();
+		$app = $this->params['app'];
+		$plugin = $this->params['plugin'];
+
+		$buffer = "<h3>Core Test Cases:</h3>\n<ul>";
+		$urlExtra = null;
+		if ($app) {
+			$buffer = "<h3>App Test Cases:</h3>\n<ul>";
+			$urlExtra = '&app=true';
+		} elseif ($plugin) {
+			$buffer = "<h3>" . Inflector::humanize($plugin) . " Test Cases:</h3>\n<ul>";
+			$urlExtra = '&plugin=' . $plugin;
+		}
+
+		if (1 > count($testCases)) {
+			$buffer .= "<strong>EMPTY</strong>";
+		}
+
+		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 = implode(' / ', $title);
+				$buffer .= "<li><a href='" . $this->baseUrl() . "?case=" . urlencode($testCase) . $urlExtra ."'>" . $title . "</a></li>\n";
+		}
+		$buffer .= "</ul>\n";
+		echo $buffer;
+	}
+
+/**
+ * Retrieves and paints the list of group tests in an HTML format.
+ *
+ * @return void
+ */
+	function groupTestList() {
+		$groupTests = parent::groupTestList();
+		$app = $this->params['app'];
+		$plugin = $this->params['plugin'];
+
+		$buffer = "<h3>Core Test Groups:</h3>\n<ul>";
+		$urlExtra = null;
+		if ($app) {
+			$buffer = "<h3>App Test Groups:</h3>\n<ul>";
+			$urlExtra = '&app=true';
+		} else if ($plugin) {
+			$buffer = "<h3>" . Inflector::humanize($plugin) . " Test Groups:</h3>\n<ul>";
+			$urlExtra = '&plugin=' . $plugin;
+		}
+
+		$buffer .= "<li><a href='" . $this->baseURL() . "?group=all$urlExtra'>All tests</a></li>\n";
+
+		foreach ($groupTests as $groupTest) {
+			$buffer .= "<li><a href='" . $this->baseURL() . "?group={$groupTest}" . "{$urlExtra}'>" . $groupTest . "</a></li>\n";
+		}
+		$buffer .= "</ul>\n";
+		echo $buffer;
+	}
+
+/**
+ * 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.
+ *
+ * @return void
+ * @access public
+ */
+	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.
+ * @return void
+ * @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 '<div style="padding:0 0 5px;">';
+		echo '<p><strong>Time taken by tests (in seconds):</strong> ' . $this->_timeDuration . '</p>';
+		if (function_exists('memory_get_peak_usage')) {
+			echo '<p><strong>Peak memory use: (in bytes):</strong> ' . number_format(memory_get_peak_usage()) . '</p>';
+		}
+		echo $this->_paintLinks();
+		echo '</div>';
+		if (
+			isset($this->params['codeCoverage']) && 
+			$this->params['codeCoverage'] && 
+			class_exists('CodeCoverageManager')
+		) {
+			CodeCoverageManager::report();
+		}
+		$this->paintDocumentEnd();
+	}
+
+/**
+ * Renders the links that for accessing things in the test suite.
+ *
+ * @return void
+ */
+	function _paintLinks() {
+		$show = $query = array();
+		if (!empty($this->params['group'])) {
+			$show['show'] = 'groups';
+		} elseif (!empty($this->params['case'])) {
+			$show['show'] = 'cases';
+		}
+
+		if (!empty($this->params['app'])) {
+			$show['app'] = $query['app'] = 'true';
+		}
+		if (!empty($this->params['plugin'])) {
+			$show['plugin'] = $query['plugin'] = $this->params['plugin'];
+		}
+		if (!empty($this->params['case'])) {
+			$query['case'] = $this->params['case'];
+ 		} elseif (!empty($this->params['group'])) {
+			$query['group'] = $this->params['group'];
+		}
+		$show = $this->_queryString($show);
+		$query = $this->_queryString($query);
+
+		echo "<p><a href='" . $this->baseUrl() . $show . "'>Run more tests</a> | <a href='" . $this->baseUrl() . $query . "&show_passes=1'>Show Passes</a> | \n";
+		echo " <a href='" . $this->baseUrl() . $query . "&amp;code_coverage=true'>Analyze Code Coverage</a></p>\n";
+	}
+
+/**
+ * Convert an array of parameters into a query string url
+ *
+ * @param array $url Url hash to be converted
+ * @return string Converted url query string
+ */
+	function _queryString($url) {
+		$out = '?';
+		$params = array();
+		foreach ($url as $key => $value) {
+			$params[] = "$key=$value";
+		}
+		$out .= implode('&amp;', $params);
+		return $out;
+	}
+
+/**
+ * Paints the end of the document html.
+ *
+ * @return void
+ */
+	function paintDocumentEnd() {
+		$baseDir = $this->params['baseDir'];
+		include CAKE_TESTS_LIB . 'templates' . DS . 'footer.php';
+		ob_end_flush();
+	}
+
+/**
+ * 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.
+ * @return void
+ * @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 = $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.
+ * @return void
+ * @access public
+ */
+	function paintPass($message) {
+		parent::paintPass($message);
+
+		if (isset($this->params['show_passes']) && $this->params['show_passes']) {
+			echo "<li class='pass'>\n";
+			echo "<span>Passed</span> ";
+			$breadcrumb = $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.
+ * @return void
+ * @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 = $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.
+ * @return void
+ * @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 = $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.
+ * @return void
+ * @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.
+ * @return void
+ * @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->_characterSet);
+	}
+}

Added: trunk/src/Web/cake/tests/lib/reporter/cake_text_reporter.php
===================================================================
--- trunk/src/Web/cake/tests/lib/reporter/cake_text_reporter.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/lib/reporter/cake_text_reporter.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,198 @@
+<?php
+/**
+ * CakeTextReporter contains reporting features used for plain text based output
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.tests.libs.reporter
+ * @since         CakePHP(tm) v 1.3
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+include_once dirname(__FILE__) . DS . 'cake_base_reporter.php';
+
+/**
+ * CakeTextReporter contains reporting features used for plain text based output
+ *
+ * @package cake
+ * @subpackage cake.tests.lib
+ */
+class CakeTextReporter extends CakeBaseReporter {
+
+/**
+ * Sets the text/plain header if the test is not a CLI test.
+ *
+ * @return void
+ */
+	function paintDocumentStart() {
+		if (!SimpleReporter::inCli()) {
+			header('Content-type: text/plain');
+		}
+	}
+
+/**
+ * Paints the end of the test with a summary of
+ * the passes and failures.
+ *
+ * @param string $test_name Name class of test.
+ * @return void
+ * @access public
+ */
+	function paintFooter($test_name) {
+		if ($this->getFailCount() + $this->getExceptionCount() == 0) {
+			echo "OK\n";
+		} else {
+			echo "FAILURES!!!\n";
+		}
+		echo "Test cases run: " . $this->getTestCaseProgress() .
+				"/" . $this->getTestCaseCount() .
+				", Passes: " . $this->getPassCount() .
+				", Failures: " . $this->getFailCount() .
+				", Exceptions: " . $this->getExceptionCount() . "\n";
+
+		echo 'Time taken by tests (in seconds): ' . $this->_timeDuration . "\n";
+		if (function_exists('memory_get_peak_usage')) {
+			echo 'Peak memory use: (in bytes): ' . number_format(memory_get_peak_usage()) . "\n";
+		}
+		if (
+			isset($this->params['codeCoverage']) && 
+			$this->params['codeCoverage'] && 
+			class_exists('CodeCoverageManager')
+		) {
+			CodeCoverageManager::report();
+		}
+	}
+
+/**
+ * Paints the title only.
+ *
+ * @param string $test_name Name class of test.
+ * @return void
+ * @access public
+ */
+	function paintHeader($test_name) {
+		$this->paintDocumentStart();
+		echo "$test_name\n";
+		flush();
+	}
+
+/**
+ * Paints the test failure as a stack trace.
+ *
+ * @param string $message Failure message displayed in
+ *    the context of the other tests.
+ * @return void
+ * @access public
+ */
+	function paintFail($message) {
+		parent::paintFail($message);
+		echo $this->getFailCount() . ") $message\n";
+		$breadcrumb = $this->getTestList();
+		array_shift($breadcrumb);
+		echo "\tin " . implode("\n\tin ", array_reverse($breadcrumb));
+		echo "\n";
+	}
+
+/**
+ * Paints a PHP error.
+ *
+ * @param string $message Message to be shown.
+ * @return void
+ * @access public
+ */
+	function paintError($message) {
+		parent::paintError($message);
+		echo "Exception " . $this->getExceptionCount() . "!\n$message\n";
+		$breadcrumb = $this->getTestList();
+		array_shift($breadcrumb);
+		echo "\tin " . implode("\n\tin ", array_reverse($breadcrumb));
+		echo "\n";
+	}
+
+/**
+ * Paints a PHP exception.
+ *
+ * @param Exception $exception Exception to describe.
+ * @return void
+ * @access public
+ */
+	function paintException($exception) {
+		parent::paintException($exception);
+		$message = 'Unexpected exception of type [' . get_class($exception) .
+				'] with message ['. $exception->getMessage() .
+				'] in ['. $exception->getFile() .
+				' line ' . $exception->getLine() . ']';
+		echo "Exception " . $this->getExceptionCount() . "!\n$message\n";
+		$breadcrumb = $this->getTestList();
+		array_shift($breadcrumb);
+		echo "\tin " . implode("\n\tin ", array_reverse($breadcrumb));
+		echo "\n";
+	}
+
+/**
+ * Prints the message for skipping tests.
+ *
+ * @param string $message Text of skip condition.
+ * @return void
+ * @access public
+ */
+	function paintSkip($message) {
+		parent::paintSkip($message);
+		echo "Skip: $message\n";
+	}
+
+/**
+ * Paints formatted text such as dumped variables.
+ *
+ * @param string $message Text to show.
+ * @return void
+ * @access public
+ */
+	function paintFormattedMessage($message) {
+		echo "$message\n";
+		flush();
+	}
+
+/**
+ * Generate a test case list in plain text.
+ * Creates as series of url's for tests that can be run.
+ * One case per line.
+ *
+ * @return void
+ */
+	function testCaseList() {
+		$testCases = parent::testCaseList();
+		$app = $this->params['app'];
+		$plugin = $this->params['plugin'];
+
+		$buffer = "Core Test Cases:\n";
+		$urlExtra = '';
+		if ($app) {
+			$buffer = "App Test Cases:\n";
+			$urlExtra = '&app=true';
+		} elseif ($plugin) {
+			$buffer = Inflector::humanize($plugin) . " Test Cases:\n";
+			$urlExtra = '&plugin=' . $plugin;
+		}
+
+		if (1 > count($testCases)) {
+			$buffer .= "EMPTY";
+			echo $buffer;
+		}
+
+		foreach ($testCases as $testCaseFile => $testCase) {
+			$buffer .= $_SERVER['SERVER_NAME'] . $this->baseUrl() ."?case=" . $testCase . "&output=text"."\n";
+		}
+
+		$buffer .= "\n";
+		echo $buffer;
+	}
+}

Added: trunk/src/Web/cake/tests/lib/templates/footer.php
===================================================================
--- trunk/src/Web/cake/tests/lib/templates/footer.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/lib/templates/footer.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.lib
+ * @since         CakePHP(tm) v 1.2.0.4433
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+?>	</div>
+		</div>
+ 		<div id="footer">
+			<p>
+ 			<!--PLEASE USE ONE OF THE POWERED BY CAKEPHP LOGO-->
+ 			<a href="http://www.cakephp.org/" target="_blank">
+				<img src="<?php echo $baseDir; ?>img/cake.power.gif" alt="CakePHP(tm) :: Rapid Development Framework" /></a>
+			</p>
+ 		</div>
+		<?php
+			App::import('Core', 'View');
+			$null = null;
+			$View =& new View($null, false);
+			echo $View->element('sql_dump');
+		?>
+	</div>
+</body>
+</html>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/lib/templates/header.php
===================================================================
--- trunk/src/Web/cake/tests/lib/templates/header.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/lib/templates/header.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,125 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.lib
+ * @since         CakePHP(tm) v 1.2.0.4433
+ * @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; charset=utf-8" />
+		<title>CakePHP Test Suite 1.3</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(<?php echo $baseDir; ?>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(<?php echo $baseDir; ?>img/test-pass-icon.png);
+				border-color:green;
+			}
+			ul.tests li.skipped {
+				background-color: #edf1ff;
+				background-image: url(<?php echo $baseDir; ?>img/test-skip-icon.png);
+				border-color:navy;
+			}
+			ul.tests li.error {
+				background-color: #ffffe5;
+				background-image: url(<?php echo $baseDir; ?>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 $baseDir; ?>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 1.3</h2>

Added: trunk/src/Web/cake/tests/lib/templates/menu.php
===================================================================
--- trunk/src/Web/cake/tests/lib/templates/menu.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/lib/templates/menu.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,59 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.lib
+ * @since         CakePHP(tm) v 1.2.0.4433
+ * @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; ?>
+	</li>
+<?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">
\ No newline at end of file

Added: trunk/src/Web/cake/tests/lib/templates/simpletest.php
===================================================================
--- trunk/src/Web/cake/tests/lib/templates/simpletest.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/lib/templates/simpletest.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Missing SimpleTest error page.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.libs
+ * @since         CakePHP(tm) v 1.2.0.4433
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+?>
+<?php include dirname(__FILE__) . DS . 'header.php'; ?>
+<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>Changes made in SimpleTest v1.1 are incompatible with CakePHP. Please ensure you download SimpleTest v1.0.x</p>
+	<p><a href="http://simpletest.org/en/download.html" target="_blank">Download SimpleTest</a></p>
+</div>
+<?php include dirname(__FILE__) . DS . 'footer.php'; ?>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/lib/templates/xdebug.php
===================================================================
--- trunk/src/Web/cake/tests/lib/templates/xdebug.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/lib/templates/xdebug.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,27 @@
+<?php
+/**
+ * Xdebug error page
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.libs
+ * @since         CakePHP(tm) v 1.2.0.4433
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+?>
+<?php include dirname(__FILE__) . DS . 'header.php'; ?>
+<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>
+</div>
+<?php include dirname(__FILE__) . DS . 'footer.php'; ?>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/lib/test_manager.php
===================================================================
--- trunk/src/Web/cake/tests/lib/test_manager.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/lib/test_manager.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,421 @@
+<?php
+/**
+ * TestManager for CakePHP Test suite.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.cake.tests.lib
+ * @since         CakePHP(tm) v 1.2.0.4433
+ * @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');
+
+/**
+ * TestManager is the base class that handles loading and initiating the running
+ * of TestCase and TestSuite classes that the user has selected.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.tests.lib
+ */
+class TestManager {
+/**
+ * Extension suffix for test case files.
+ *
+ * @var string
+ */
+	var $_testExtension = '.test.php';
+
+/**
+ * Extension suffix for group test case files.
+ *
+ * @var string
+ */
+	var $_groupExtension = '.group.php';
+
+/**
+ * Is this test an AppTest?
+ *
+ * @var boolean
+ */
+	var $appTest = false;
+
+/**
+ * Is this test a plugin test?
+ *
+ * @var mixed boolean false or string name of the plugin being used.
+ */
+	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 = htmlentities($_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 Object $reporter Reporter object for the tests being run.
+ * @param boolean $testing Are tests supposed to be auto run.  Set to true to return testcase list.
+ * @return mixed
+ * @access public
+ */
+	function runAllTests(&$reporter, $testing = false) {
+		$testCases =& $this->_getTestFileList($this->_getTestsPath());
+		if ($this->appTest) {
+			$test =& new TestSuite(__('All App Tests', true));
+		} else if ($this->pluginTest) {
+			$test =& new TestSuite(sprintf(__('All %s Plugin Tests', true), Inflector::humanize($this->pluginTest)));
+		} else {
+			$test =& new TestSuite(__('All Core Tests', true));
+		}
+
+		if ($testing) {
+			return $testCases;
+		}
+
+		foreach ($testCases as $testCase) {
+			$test->addTestFile($testCase);
+		}
+
+		return $test->run($reporter);
+	}
+
+/**
+ * Runs a specific test case file
+ *
+ * @param string $testCaseFile Filename of the test to be run.
+ * @param Object $reporter Reporter instance to attach to the test case.
+ * @param boolean $testing Set to true if testing, otherwise test case will be run.
+ * @return mixed Result of test case being run.
+ * @access public
+ */
+	function runTestCase($testCaseFile, &$reporter, $testing = false) {
+		$testCaseFileWithPath = $this->_getTestsPath() . DS . $testCaseFile;
+
+		if (!file_exists($testCaseFileWithPath) || strpos($testCaseFileWithPath, '..')) {
+			trigger_error(
+				sprintf(__("Test case %s cannot be found", true), htmlentities($testCaseFile)),
+				E_USER_ERROR
+			);
+			return false;
+		}
+
+		if ($testing) {
+			return true;
+		}
+
+		$test =& new TestSuite(sprintf(__('Individual test case: %s', true), $testCaseFile));
+		$test->addTestFile($testCaseFileWithPath);
+		return $test->run($reporter);
+	}
+
+/**
+ * Runs a specific group test file
+ *
+ * @param string $groupTestName GroupTest that you want to run.
+ * @param Object $reporter Reporter instance to use with the group test being run.
+ * @return mixed Results of group test being run.
+ * @access public
+ */
+	function runGroupTest($groupTestName, &$reporter) {
+		$filePath = $this->_getTestsPath('groups') . DS . strtolower($groupTestName) . $this->_groupExtension;
+
+		if (!file_exists($filePath) || strpos($filePath, '..')) {
+			trigger_error(sprintf(
+					__("Group test %s cannot be found at %s", true), 
+					htmlentities($groupTestName), 
+					htmlentities($filePath)
+				),
+				E_USER_ERROR
+			);
+		}
+
+		require_once $filePath;
+		$test =& new TestSuite(sprintf(__('%s group test', true), $groupTestName));
+		foreach ($this->_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 object $groupTest Instance of TestSuite/GroupTest that files are to be added to.
+ * @param string $directory The directory to add tests from.
+ * @return void
+ * @access public
+ * @static
+ */
+	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 object $groupTest Instance of TestSuite/GroupTest that a file should be added to.
+ * @param string $file The file name, minus the suffix to add.
+ * @return void
+ * @access public
+ * @static
+ */
+	function addTestFile(&$groupTest, $file) {
+		$manager =& new TestManager();
+
+		if (file_exists($file . $manager->_testExtension)) {
+			$file .= $manager->_testExtension;
+		} elseif (file_exists($file . $manager->_groupExtension)) {
+			$file .= $manager->_groupExtension;
+		}
+		$groupTest->addTestFile($file);
+	}
+
+/**
+ * Returns a list of test cases found in the current valid test case path
+ *
+ * @access public
+ * @static
+ */
+	function &getTestCaseList() {
+		$manager =& new TestManager();
+		$return = $manager->_getTestCaseList($manager->_getTestsPath());
+		return $return;
+	}
+
+/**
+ * Builds the list of test cases from a given directory
+ *
+ * @param string $directory Directory to get test case list from.
+ * @access protected
+ */
+	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
+ *
+ * @param string $directory Directory to get test case files from.
+ * @access protected
+ */
+	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
+ * @static
+ */
+	function &getGroupTestList() {
+		$manager =& new TestManager();
+		$return = $manager->_getTestGroupList($manager->_getTestsPath('groups'));
+		return $return;
+	}
+
+/**
+ * Returns a list of group test files from a given directory
+ *
+ * @param string $directory The directory to get group test files from.
+ * @access protected
+ */
+	function &_getTestGroupFileList($directory = '.') {
+		$return = $this->_getRecursiveFileList($directory, array(&$this, '_isTestGroupFile'));
+		return $return;
+	}
+
+/**
+ * Returns a list of group test files from a given directory
+ *
+ * @param string $directory The directory to get group tests from.
+ * @access protected
+ */
+	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
+ *
+ * @param string $groupTestFile The groupTest file to scan for TestSuite classnames.
+ * @access protected
+ */
+	function &_getGroupTestClassNames($groupTestFile) {
+		$file = implode("\n", file($groupTestFile));
+		preg_match("~lass\s+?(.*)\s+?extends TestSuite~", $file, $matches);
+		if (!empty($matches)) {
+			unset($matches[0]);
+			return $matches;
+		}
+		$matches = array();
+		return $matches;
+	}
+
+/**
+ * Gets a recursive list of files from a given directory and matches then against
+ * a given fileTestFunction, like isTestCaseFile()
+ *
+ * @param string $directory The directory to scan for files.
+ * @param mixed $fileTestFunction
+ * @access protected
+ */
+	function &_getRecursiveFileList($directory = '.', $fileTestFunction) {
+		$fileList = array();
+		if (!is_dir($directory)) {
+			return $fileList;
+		}
+
+		$files = glob($directory . DS . '*');
+		$files = $files ? $files : array();
+
+		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 boolean Whether $file is a test case.
+ * @access protected
+ */
+	function _isTestCaseFile($file) {
+		return $this->_hasExpectedExtension($file, $this->_testExtension);
+	}
+
+/**
+ * Tests if a file has the correct group test extension
+ *
+ * @param string $file
+ * @return boolean Whether $file is a group
+ * @access protected
+ */
+	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 protected
+ */
+	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 either 'cases' or 'groups'
+ * @return string The path tests are located on
+ * @access protected
+ */
+	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';
+			$pluginPath = App::pluginPath($this->pluginTest);
+			if (file_exists($pluginPath . DS . 'tests')) {
+				$_pluginBasePath = $pluginPath . DS . 'tests';
+			}
+			$result = $_pluginBasePath . DS . $type;
+		} else {
+			if ($type == 'cases') {
+				$result = CORE_TEST_CASES;
+			} else if ($type == 'groups') {
+				$result = CORE_TEST_GROUPS;
+			}
+		}
+		return $result;
+	}
+
+/**
+ * Get the extension for either 'group' or 'test' types.
+ *
+ * @param string $type Type of test to get, either 'test' or 'group'
+ * @return string Extension suffix for test.
+ * @access public
+ */
+	function getExtension($type = 'test') {
+		if ($type == 'test' || $type == 'case') {
+			return $this->_testExtension;
+		}
+		return $this->_groupExtension;
+	}
+}

Added: trunk/src/Web/cake/tests/test_app/config/acl.ini.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/config/acl.ini.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/config/acl.ini.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,61 @@
+;<?php exit() ?>
+; SVN FILE: $Id$
+;/**
+; * Test App Ini Based Acl Config File
+; *
+; *
+; * PHP versions 4 and 5
+; *
+; * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+; * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+; *
+; *  Licensed under The MIT License
+; *  Redistributions of files must retain the above copyright notice.
+; *
+; * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+; * @link          http://cakephp.org CakePHP(tm) Project
+; * @package       cake
+; * @subpackage    cake.app.config
+; * @since         CakePHP(tm) v 0.10.0.1076
+; * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+; */
+
+;-------------------------------------
+;Users
+;-------------------------------------
+
+[admin]
+groups = administrators
+allow =
+deny = ads
+
+[paul]
+groups = users
+allow =
+deny =
+
+[jenny]
+groups = users
+allow = ads
+deny = images, files
+
+[nobody]
+groups = anonymous
+allow =
+deny =
+
+;-------------------------------------
+;Groups
+;-------------------------------------
+
+[administrators]
+deny =
+allow = posts, comments, images, files, stats, ads
+
+[users]
+allow = posts, comments, images, files
+deny = stats, ads
+
+[anonymous]
+allow =
+deny = posts, comments, images, files, stats, ads

Added: trunk/src/Web/cake/tests/test_app/controllers/components/empty
===================================================================
Added: trunk/src/Web/cake/tests/test_app/controllers/tests_apps_controller.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/controllers/tests_apps_controller.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/controllers/tests_apps_controller.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.test_app.plugins.test_plugin.views.helpers
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+class TestsAppsController extends AppController {
+	var $name = 'TestsApps';
+	var $uses = array();
+
+	function index() {
+	}
+
+	function some_method() {
+		return 5;
+	}
+
+	function set_action() {
+		$this->set('var', 'string');
+		$this->render('index');
+	}
+}

Added: trunk/src/Web/cake/tests/test_app/controllers/tests_apps_posts_controller.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/controllers/tests_apps_posts_controller.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/controllers/tests_apps_posts_controller.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,66 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.test_app.plugins.test_plugin.views.helpers
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+class TestsAppsPostsController extends AppController {
+	var $name = 'TestsAppsPosts';
+	var $uses = array('Post');
+	var $viewPath = 'tests_apps';
+
+	function add() {
+		$data = array(
+			'Post' => array(
+				'title' => 'Test article',
+				'body' => 'Body of article.',
+				'author_id' => 1
+			)
+		);
+		$this->Post->save($data);
+
+		$this->set('posts', $this->Post->find('all'));
+		$this->render('index');
+	}
+
+/**
+ * check url params
+ *
+ */
+	function url_var() {
+		$this->set('params', $this->params);
+		$this->render('index');
+	}
+
+/**
+ * post var testing
+ *
+ */
+	function post_var() {
+		$this->set('data', $this->data);
+		$this->render('index');
+	}
+
+/**
+ * Fixturized action for testAction()
+ *
+ */
+	function fixtured() {
+		$this->set('posts', $this->Post->find('all'));
+		$this->render('index');
+	}
+
+}

Added: trunk/src/Web/cake/tests/test_app/libs/cache/test_app_cache.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/libs/cache/test_app_cache.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/libs/cache/test_app_cache.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,22 @@
+<?php
+/**
+ * Test Suite Test App Cache Engine class.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.3
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+class TestAppCacheEngine extends CacheEngine {
+	
+}

Added: trunk/src/Web/cake/tests/test_app/libs/library.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/libs/library.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/libs/library.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,20 @@
+<?php
+/**
+ * Test Suite Library
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.3
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+class Library {}

Added: trunk/src/Web/cake/tests/test_app/libs/log/test_app_log.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/libs/log/test_app_log.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/libs/log/test_app_log.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,25 @@
+<?php
+/**
+ * Test Suite Test App Logging stream class.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.3
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+class TestAppLog {
+
+	function write($type, $message) {
+		
+	}
+}

Added: trunk/src/Web/cake/tests/test_app/locale/cache_test_po/LC_MESSAGES/default.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/cache_test_po/LC_MESSAGES/default.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/cache_test_po/LC_MESSAGES/default.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,5 @@
+msgid "default.foo"
+msgstr "Default Foo"
+
+msgid "default.bar"
+msgstr "Default Bar"

Added: trunk/src/Web/cake/tests/test_app/locale/cache_test_po/LC_MESSAGES/dom1.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/cache_test_po/LC_MESSAGES/dom1.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/cache_test_po/LC_MESSAGES/dom1.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,8 @@
+msgid ""
+msgstr "Test Domain 1"
+
+msgid "dom1.foo"
+msgstr "Dom 1 Foo"
+
+msgid "dom1.bar"
+msgstr "Dom 1 Bar"

Added: trunk/src/Web/cake/tests/test_app/locale/cache_test_po/LC_MESSAGES/dom2.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/cache_test_po/LC_MESSAGES/dom2.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/cache_test_po/LC_MESSAGES/dom2.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,8 @@
+msgid ""
+msgstr "Test Domain"
+
+msgid "dom2.foo"
+msgstr "Dom 2 Foo"
+
+msgid "dom2.bar"
+msgstr "Dom 2 Bar"

Added: trunk/src/Web/cake/tests/test_app/locale/ja_jp/LC_TIME
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/ja_jp/LC_TIME	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/ja_jp/LC_TIME	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,48 @@
+escape_char	/
+comment_char %
+abday	"<U65E5>";"<U6708>";"<U706B>";"<U6C34>";"<U6728>";"<U91D1>";"<U571F>"
+
+day	"<U65E5><U66DC><U65E5>";"<U6708><U66DC><U65E5>";/
+	"<U706B><U66DC><U65E5>";"<U6C34><U66DC><U65E5>";/
+	"<U6728><U66DC><U65E5>";"<U91D1><U66DC><U65E5>";/
+	"<U571F><U66DC><U65E5>"
+
+week    7;19971130;7
+first_weekday	1
+first_workday	2
+abmon	"<U0020><U0031><U6708>";"<U0020><U0032><U6708>";/
+	"<U0020><U0033><U6708>";"<U0020><U0034><U6708>";/
+	"<U0020><U0035><U6708>";"<U0020><U0036><U6708>";/
+	"<U0020><U0037><U6708>";"<U0020><U0038><U6708>";/
+	"<U0020><U0039><U6708>";"<U0031><U0030><U6708>";/
+	"<U0031><U0031><U6708>";"<U0031><U0032><U6708>"
+mon	"<U0031><U6708>";"<U0032><U6708>";/
+	"<U0033><U6708>";"<U0034><U6708>";/
+	"<U0035><U6708>";"<U0036><U6708>";/
+	"<U0037><U6708>";"<U0038><U6708>";/
+	"<U0039><U6708>";"<U0031><U0030><U6708>";/
+	"<U0031><U0031><U6708>";"<U0031><U0032><U6708>"
+% Appropriate date and time representation (%c)
+%	
+d_t_fmt	"<U0025><U0059><U5E74><U0025><U006D><U6708><U0025><U0064><U65E5><U0020><U0025><U0048><U6642><U0025><U004D><U5206><U0025><U0053><U79D2>"
+%
+% Appropriate date representation (%x)
+%	"%Y年%m月%d日"
+d_fmt	"<U0025><U0059><U5E74><U0025><U006D><U6708><U0025><U0064><U65E5>"
+%
+% Appropriate time representation (%X)
+%	
+t_fmt	"<U0025><U0048><U6642><U0025><U004D><U5206><U0025><U0053><U79D2>"
+%
+% Appropriate AM/PM time representation (%r)
+%	
+t_fmt_ampm "<U0025><U0070><U0025><U0049><U6642><U0025><U004D><U5206><U0025><U0053><U79D2>"
+%
+% Strings for AM/PM
+%
+am_pm	"<U5348><U524D>";"<U5348><U5F8C>"
+%
+% Appropriate date representation (date(1))   
+date_fmt	"<U0025><U0059><U5E74><U0020><U0025><U0062><U0020><U0025>/
+<U0065><U65E5><U0020><U0025><U0041><U0020><U0025><U0048><U003A><U0025>/
+<U004D><U003A><U0025><U0053><U0020><U0025><U005A>"

Added: trunk/src/Web/cake/tests/test_app/locale/po/LC_MESSAGES/default.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/po/LC_MESSAGES/default.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/po/LC_MESSAGES/default.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,76 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-05-15 02:51-0700\n"
+"PO-Revision-Date: \n"
+"Last-Translator: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
+"X-Poedit-Language: Three Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+msgid ""
+msgstr "header"
+
+msgid "Plural Rule 1"
+msgstr "Po (translated)"
+
+msgid "%d = 1"
+msgid_plural "%d = 0 or > 1"
+msgstr[0] "%d is 1 (po translated)"
+msgstr[1] "%d is 2-4 (po translated)"
+msgstr[2] "%d everything else (po translated)"
+
+msgid "This is a multiline translation\n"
+"broken up over multiple lines.\n"
+"This is the third line.\n"
+"This is the forth line."
+msgstr  "This is a multiline translation\n"
+"broken up over multiple lines.\n"
+"This is the third line.\n"
+"This is the forth line. (translated)"
+
+msgid "No Translation needed"
+msgstr ""
+
+msgid "test"
+msgid_plural "tests"
+msgstr[0] "test translated"
+msgstr[1] "test translated"
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "valid\n"
+"second line"
+msgid_plural "valids\n"
+"second line"
+msgstr[0] "v\n"
+"second line"
+msgstr[1] "vs\n"
+"second line"
+
+msgid "%d = 1\n"
+"This is a multiline translation\n"
+"broken up over multiple lines.\n"
+"This is the third line.\n"
+"This is the forth line."
+msgid_plural "%d = 0 or > 1\n"
+"This is a multiline translation\n"
+"broken up over multiple lines.\n"
+"This is the third line.\n"
+"This is the forth line."
+msgstr[0] "%d is 1\n"
+"This is a multiline translation\n"
+"broken up over multiple lines.\n"
+"This is the third line.\n"
+"This is the forth line."
+msgstr[1] "%d is 2-4\n"
+"This is a multiline translation\n"
+"broken up over multiple lines.\n"
+"This is the third line.\n"
+"This is the forth line."
+
+msgid "this is a \"quoted string\""
+msgstr "this is a \"quoted string\" (translated)"
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/locale/po/LC_MONETARY/default.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/po/LC_MONETARY/default.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/po/LC_MONETARY/default.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,18 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-05-15 02:51-0700\n"
+"PO-Revision-Date: \n"
+"Last-Translator: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
+"X-Poedit-Language: Three Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+msgid ""
+msgstr "header"
+
+msgid "Plural Rule 1"
+msgstr "Monetary Po (translated)"
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/locale/po/LC_TIME
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/po/LC_TIME	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/po/LC_TIME	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,60 @@
+escape_char	/
+comment_char %
+abday	"<U0053><U0075><U006E>";"<U004D><U006F><U006E>";/
+	"<U0054><U0075><U0065>";"<U0057><U0065><U0064>";/
+	"<U0054><U0068><U0075>";"<U0046><U0072><U0069>";/
+	"<U0053><U0061><U0074>"
+day	"<U0053><U0075><U006E><U0064><U0061><U0079>";/
+	"<U004D><U006F><U006E><U0064><U0061><U0079>";/
+	"<U0054><U0075><U0065><U0073><U0064><U0061><U0079>";/
+	"<U0057><U0065><U0064><U006E><U0065><U0073><U0064><U0061><U0079>";/
+	"<U0054><U0068><U0075><U0072><U0073><U0064><U0061><U0079>";/
+	"<U0046><U0072><U0069><U0064><U0061><U0079>";/
+	"<U0053><U0061><U0074><U0075><U0072><U0064><U0061><U0079>"
+
+week    7;19971130;7
+first_weekday	1
+first_workday	2
+abmon	"<U004A><U0061><U006E>";"<U0046><U0065><U0062>";/
+	"<U004D><U0061><U0072>";"<U0041><U0070><U0072>";/
+	"<U004D><U0061><U0079>";"<U004A><U0075><U006E>";/
+	"<U004A><U0075><U006C>";"<U0041><U0075><U0067>";/
+	"<U0053><U0065><U0070>";"<U004F><U0063><U0074>";/
+	"<U004E><U006F><U0076>";"<U0044><U0065><U0063>"
+mon	"<U004A><U0061><U006E><U0075><U0061><U0072><U0079>";/
+	"<U0046><U0065><U0062><U0072><U0075><U0061><U0072><U0079>";/
+	"<U004D><U0061><U0072><U0063><U0068>";/
+	"<U0041><U0070><U0072><U0069><U006C>";/
+	"<U004D><U0061><U0079>";/
+	"<U004A><U0075><U006E><U0065>";/
+	"<U004A><U0075><U006C><U0079>";/
+	"<U0041><U0075><U0067><U0075><U0073><U0074>";/
+	"<U0053><U0065><U0070><U0074><U0065><U006D><U0062><U0065><U0072>";/
+	"<U004F><U0063><U0074><U006F><U0062><U0065><U0072>";/
+	"<U004E><U006F><U0076><U0065><U006D><U0062><U0065><U0072>";/
+	"<U0044><U0065><U0063><U0065><U006D><U0062><U0065><U0072>"
+% Appropriate date and time representation (%c)
+%	"%a %d %b %Y %r %Z"
+d_t_fmt "<U0025><U0061><U0020><U0025><U0064><U0020><U0025><U0062><U0020><U0025><U0059><U0020><U0025><U0072><U0020><U0025><U005A>"
+%
+% Appropriate date representation (%x)
+%	"%m/%d/%Y"
+d_fmt   "<U0025><U006D><U002F><U0025><U0064><U002F><U0025><U0059>"
+%
+% Appropriate time representation (%X)
+%	"%r"
+t_fmt   "<U0025><U0072>"
+%
+% Appropriate AM/PM time representation (%r)
+%	"%I:%M:%S %p"
+t_fmt_ampm "<U0025><U0049><U003A><U0025><U004D><U003A><U0025><U0053><U0020>/
+<U0025><U0070>"
+%
+% Strings for AM/PM
+%
+am_pm	"<U0041><U004D>";"<U0050><U004D>"
+%
+% Appropriate date representation (date(1))   "%a %b %e %H:%M:%S %Z %Y"
+date_fmt	"<U0025><U0061><U0020><U0025><U0062><U0020><U0025><U0065>/
+<U0020><U0025><U0048><U003A><U0025><U004D><U003A><U0025><U0053><U0020>/
+<U0025><U005A><U0020><U0025><U0059>"

Added: trunk/src/Web/cake/tests/test_app/locale/rule_0_mo/LC_MESSAGES/core.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_0_mo/LC_MESSAGES/core.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_0_mo/LC_MESSAGES/default.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_0_mo/LC_MESSAGES/default.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_0_po/LC_MESSAGES/core.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_0_po/LC_MESSAGES/core.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_0_po/LC_MESSAGES/core.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,21 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-05-15 02:51-0700\n"
+"PO-Revision-Date: \n"
+"Last-Translator: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Poedit-Language: Single Form Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1 (from core)"
+msgstr "Plural Rule 0 (from core translated)"
+
+msgid "%d = 1 (from core)"
+msgid_plural "%d = 0 or > 1 (from core)"
+msgstr[0] "%d ends with any # (from core translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_0_po/LC_MESSAGES/default.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_0_po/LC_MESSAGES/default.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_0_po/LC_MESSAGES/default.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,24 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-05-15 02:51-0700\n"
+"PO-Revision-Date: \n"
+"Last-Translator: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Poedit-Language: Single Form Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1"
+msgstr "Plural Rule 0 (translated)"
+
+msgid "%d = 1"
+msgid_plural "%d = 0 or > 1"
+msgstr[0] "%d ends with any # (translated)"
+
+#~ msgid "Plural-Forms 1"
+#~ msgstr "Plural-Forms 0"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_10_mo/LC_MESSAGES/core.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_10_mo/LC_MESSAGES/core.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_10_mo/LC_MESSAGES/default.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_10_mo/LC_MESSAGES/default.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_10_po/LC_MESSAGES/core.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_10_po/LC_MESSAGES/core.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_10_po/LC_MESSAGES/core.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,24 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-05-15 02:51-0700\n"
+"PO-Revision-Date: \n"
+"Last-Translator: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3;\n"
+"X-Poedit-Language: Four Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1 (from core)"
+msgstr "Plural Rule 10 (from core translated)"
+
+msgid "%d = 1 (from core)"
+msgid_plural "%d = 0 or > 1 (from core)"
+msgstr[0] "%d ends in 1 (from core translated)"
+msgstr[1] "%d ends in 2 (from core translated)"
+msgstr[2] "%d ends in 03-04 (from core translated)"
+msgstr[3] "%d everything else (from core translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_10_po/LC_MESSAGES/default.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_10_po/LC_MESSAGES/default.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_10_po/LC_MESSAGES/default.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,27 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-05-15 02:51-0700\n"
+"PO-Revision-Date: \n"
+"Last-Translator: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3;\n"
+"X-Poedit-Language: Four Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1"
+msgstr "Plural Rule 10 (translated)"
+
+msgid "%d = 1"
+msgid_plural "%d = 0 or > 1"
+msgstr[0] "%d ends in 1 (translated)"
+msgstr[1] "%d ends in 2 (translated)"
+msgstr[2] "%d ends in 03-04 (translated)"
+msgstr[3] "%d everything else (translated)"
+
+#~ msgid "Plural-Forms 1"
+#~ msgstr "Plural-Forms 1 (translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_11_mo/LC_MESSAGES/core.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_11_mo/LC_MESSAGES/core.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_11_mo/LC_MESSAGES/default.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_11_mo/LC_MESSAGES/default.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_11_po/LC_MESSAGES/core.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_11_po/LC_MESSAGES/core.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_11_po/LC_MESSAGES/core.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,25 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-10-09 19:20-0300\n"
+"PO-Revision-Date: \n"
+"Last-Translator: Renan Gonçalves <renan****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=5; plural=n==1 ? 0 : n==2 ? 1 : n>=3 && n<=6 ? 2 : n>=7 && n<=10 ? 3 : 4;\n"
+"X-Poedit-Language: Five Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1 (from core)"
+msgstr "Plural Rule 11 (from core translated)"
+
+msgid "%d = 1 (from core)"
+msgid_plural "%d = 0 or > 1 (from core)"
+msgstr[0] "%d is 1 (from core translated)"
+msgstr[1] "%d is 2 (from core translated)"
+msgstr[2] "%d is 3-6 (from core translated)"
+msgstr[3] "%d is 7-10 (from core translated)"
+msgstr[4] "%d everything else (from core translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_11_po/LC_MESSAGES/default.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_11_po/LC_MESSAGES/default.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_11_po/LC_MESSAGES/default.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,25 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-10-09 19:20-0300\n"
+"PO-Revision-Date: \n"
+"Last-Translator: Renan Gonçalves <renan****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=5; plural=n==1 ? 0 : n==2 ? 1 : n>=3 && n<=6 ? 2 : n>=7 && n<=10 ? 3 : 4;\n"
+"X-Poedit-Language: Five Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1"
+msgstr "Plural Rule 11 (translated)"
+
+msgid "%d = 1"
+msgid_plural "%d = 0 or > 1"
+msgstr[0] "%d is 1 (translated)"
+msgstr[1] "%d is 2 (translated)"
+msgstr[2] "%d is 3-6 (translated)"
+msgstr[3] "%d is 7-10 (translated)"
+msgstr[4] "%d everything else (translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_12_mo/LC_MESSAGES/core.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_12_mo/LC_MESSAGES/core.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_12_mo/LC_MESSAGES/default.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_12_mo/LC_MESSAGES/default.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_12_po/LC_MESSAGES/core.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_12_po/LC_MESSAGES/core.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_12_po/LC_MESSAGES/core.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,24 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-10-09 19:20-0300\n"
+"PO-Revision-Date: \n"
+"Last-Translator: Renan Gonçalves <renan****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=4; plural=n==1 ? 0 : n==2 ? 1 : n==0 || (n>=3 && n<=10) ? 2 : 3;\n"
+"X-Poedit-Language: Four Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1 (from core)"
+msgstr "Plural Rule 12 (from core translated)"
+
+msgid "%d = 1 (from core)"
+msgid_plural "%d = 0 or > 1 (from core)"
+msgstr[0] "%d is 1 (from core translated)"
+msgstr[1] "%d is 2 (from core translated)"
+msgstr[2] "%d is 0 or 3-10 (from core translated)"
+msgstr[3] "%d everything else (from core translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_12_po/LC_MESSAGES/default.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_12_po/LC_MESSAGES/default.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_12_po/LC_MESSAGES/default.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,24 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-10-09 19:20-0300\n"
+"PO-Revision-Date: \n"
+"Last-Translator: Renan Gonçalves <renan****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=4; plural=n==1 ? 0 : n==2 ? 1 : n==0 || (n>=3 && n<=10) ? 2 : 3;\n"
+"X-Poedit-Language: Four Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1"
+msgstr "Plural Rule 12 (translated)"
+
+msgid "%d = 1"
+msgid_plural "%d = 0 or > 1"
+msgstr[0] "%d is 1 (translated)"
+msgstr[1] "%d is 2 (translated)"
+msgstr[2] "%d is 0 or 3-10 (translated)"
+msgstr[3] "%d everything else (translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_13_mo/LC_MESSAGES/core.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_13_mo/LC_MESSAGES/core.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_13_mo/LC_MESSAGES/default.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_13_mo/LC_MESSAGES/default.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_13_po/LC_MESSAGES/core.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_13_po/LC_MESSAGES/core.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_13_po/LC_MESSAGES/core.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,24 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-10-09 19:20-0300\n"
+"PO-Revision-Date: \n"
+"Last-Translator: Renan Gonçalves <renan****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=4; plural=n==1 ? 0 : n==0 || (n%100>=1 && n%100<=10) ? 1 : n%100>=11 && n%100<=20 ? 2 : 3;\n"
+"X-Poedit-Language: Four Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1 (from core)"
+msgstr "Plural Rule 13 (from core translated)"
+
+msgid "%d = 1 (from core)"
+msgid_plural "%d = 0 or > 1 (from core)"
+msgstr[0] "%d is 1 (from core translated)"
+msgstr[1] "%d is 0 or ends in 01-10 (from core translated)"
+msgstr[2] "%d ends in 11-20 (from core translated)"
+msgstr[3] "%d everything else (from core translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_13_po/LC_MESSAGES/default.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_13_po/LC_MESSAGES/default.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_13_po/LC_MESSAGES/default.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,24 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-10-09 19:20-0300\n"
+"PO-Revision-Date: \n"
+"Last-Translator: Renan Gonçalves <renan****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=4; plural=n==1 ? 0 : n==0 || (n%100>=1 && n%100<=10) ? 1 : n%100>=11 && n%100<=20 ? 2 : 3;\n"
+"X-Poedit-Language: Four Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1"
+msgstr "Plural Rule 13 (translated)"
+
+msgid "%d = 1"
+msgid_plural "%d = 0 or > 1"
+msgstr[0] "%d is 1 (translated)"
+msgstr[1] "%d is 0 or ends in 01-10 (translated)"
+msgstr[2] "%d ends in 11-20 (translated)"
+msgstr[3] "%d everything else (translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_14_mo/LC_MESSAGES/core.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_14_mo/LC_MESSAGES/core.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_14_mo/LC_MESSAGES/default.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_14_mo/LC_MESSAGES/default.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_14_po/LC_MESSAGES/core.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_14_po/LC_MESSAGES/core.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_14_po/LC_MESSAGES/core.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,23 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-10-09 19:20-0300\n"
+"PO-Revision-Date: \n"
+"Last-Translator: Renan Gonçalves <renan****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 ? 0 : n%10==2 ? 1 : 2;\n"
+"X-Poedit-Language: Three Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1 (from core)"
+msgstr "Plural Rule 14 (from core translated)"
+
+msgid "%d = 1 (from core)"
+msgid_plural "%d = 0 or > 1 (from core)"
+msgstr[0] "%d ends in 1 (from core translated)"
+msgstr[1] "%d ends in 2 (from core translated)"
+msgstr[2] "%d everything else (from core translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_14_po/LC_MESSAGES/default.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_14_po/LC_MESSAGES/default.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_14_po/LC_MESSAGES/default.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,23 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-10-09 19:20-0300\n"
+"PO-Revision-Date: \n"
+"Last-Translator: Renan Gonçalves <renan****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 ? 0 : n%10==2 ? 1 : 2;\n"
+"X-Poedit-Language: Three Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1"
+msgstr "Plural Rule 14 (translated)"
+
+msgid "%d = 1"
+msgid_plural "%d = 0 or > 1"
+msgstr[0] "%d ends in 1 (translated)"
+msgstr[1] "%d ends in 2 (translated)"
+msgstr[2] "%d everything else (translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_1_mo/LC_MESSAGES/core.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_1_mo/LC_MESSAGES/core.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_1_mo/LC_MESSAGES/default.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_1_mo/LC_MESSAGES/default.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_1_po/LC_MESSAGES/core.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_1_po/LC_MESSAGES/core.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_1_po/LC_MESSAGES/core.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,22 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-05-15 02:51-0700\n"
+"PO-Revision-Date: \n"
+"Last-Translator: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Poedit-Language: Two Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1 (from core)"
+msgstr "Plural Rule 1 (from core translated)"
+
+msgid "%d = 1 (from core)"
+msgid_plural "%d = 0 or > 1 (from core)"
+msgstr[0] "%d = 1 (from core translated)"
+msgstr[1] "%d = 0 or > 1 (from core translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_1_po/LC_MESSAGES/default.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_1_po/LC_MESSAGES/default.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_1_po/LC_MESSAGES/default.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,25 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-05-15 02:51-0700\n"
+"PO-Revision-Date: \n"
+"Last-Translator: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Poedit-Language: Two Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1"
+msgstr "Plural Rule 1 (translated)"
+
+msgid "%d = 1"
+msgid_plural "%d = 0 or > 1"
+msgstr[0] "%d = 1 (translated)"
+msgstr[1] "%d = 0 or > 1 (translated)"
+
+#~ msgid "Plural-Forms 1"
+#~ msgstr "Plural-Forms 1 (translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_2_mo/LC_MESSAGES/core.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_2_mo/LC_MESSAGES/core.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_2_mo/LC_MESSAGES/default.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_2_mo/LC_MESSAGES/default.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_2_po/LC_MESSAGES/core.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_2_po/LC_MESSAGES/core.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_2_po/LC_MESSAGES/core.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,22 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-05-15 02:51-0700\n"
+"PO-Revision-Date: \n"
+"Last-Translator: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n>1;\n"
+"X-Poedit-Language: Two Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1 (from core)"
+msgstr "Plural Rule 2 (from core translated)"
+
+msgid "%d = 1 (from core)"
+msgid_plural "%d = 0 or > 1 (from core)"
+msgstr[0] "%d = 0 or 1 (from core translated)"
+msgstr[1] "%d > 1 (from core translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_2_po/LC_MESSAGES/default.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_2_po/LC_MESSAGES/default.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_2_po/LC_MESSAGES/default.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,25 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-05-15 02:51-0700\n"
+"PO-Revision-Date: \n"
+"Last-Translator: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n>1;\n"
+"X-Poedit-Language: Two Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1"
+msgstr "Plural Rule 2 (translated)"
+
+msgid "%d = 1"
+msgid_plural "%d = 0 or > 1"
+msgstr[0] "%d = 0 or 1 (translated)"
+msgstr[1] "%d > 1 (translated)"
+
+#~ msgid "Plural-Forms 1"
+#~ msgstr "Plural-Forms 1 (translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_3_mo/LC_MESSAGES/core.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_3_mo/LC_MESSAGES/core.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_3_mo/LC_MESSAGES/default.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_3_mo/LC_MESSAGES/default.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_3_po/LC_MESSAGES/core.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_3_po/LC_MESSAGES/core.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_3_po/LC_MESSAGES/core.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,23 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-05-15 02:51-0700\n"
+"PO-Revision-Date: \n"
+"Last-Translator: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;\n"
+"X-Poedit-Language: Three Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1 (from core)"
+msgstr "Plural Rule 3 (from core translated)"
+
+msgid "%d = 1 (from core)"
+msgid_plural "%d = 0 or > 1 (from core)"
+msgstr[0] "%d ends 1 but not 11 (from core translated)"
+msgstr[1] "%d everything else (from core translated)"
+msgstr[2] "%d = 0 (from core translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_3_po/LC_MESSAGES/default.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_3_po/LC_MESSAGES/default.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_3_po/LC_MESSAGES/default.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,26 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-05-15 02:51-0700\n"
+"PO-Revision-Date: \n"
+"Last-Translator: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;\n"
+"X-Poedit-Language: Three Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1"
+msgstr "Plural Rule 3 (translated)"
+
+msgid "%d = 1"
+msgid_plural "%d = 0 or > 1"
+msgstr[0] "%d ends 1 but not 11 (translated)"
+msgstr[1] "%d everything else (translated)"
+msgstr[2] "%d = 0 (translated)"
+
+#~ msgid "Plural-Forms 1"
+#~ msgstr "Plural-Forms 1 (translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_4_mo/LC_MESSAGES/core.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_4_mo/LC_MESSAGES/core.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_4_mo/LC_MESSAGES/default.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_4_mo/LC_MESSAGES/default.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_4_po/LC_MESSAGES/core.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_4_po/LC_MESSAGES/core.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_4_po/LC_MESSAGES/core.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,23 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-05-15 02:51-0700\n"
+"PO-Revision-Date: \n"
+"Last-Translator: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n==2 ? 1 : 2;\n"
+"X-Poedit-Language: Three Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1 (from core)"
+msgstr "Plural Rule 4 (from core translated)"
+
+msgid "%d = 1 (from core)"
+msgid_plural "%d = 0 or > 1 (from core)"
+msgstr[0] "%d = 1 (from core translated)"
+msgstr[1] "%d = 2 (from core translated)"
+msgstr[2] "%d everything else (from core translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_4_po/LC_MESSAGES/default.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_4_po/LC_MESSAGES/default.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_4_po/LC_MESSAGES/default.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,26 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-05-15 02:51-0700\n"
+"PO-Revision-Date: \n"
+"Last-Translator: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n==2 ? 1 : 2;\n"
+"X-Poedit-Language: Three Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1"
+msgstr "Plural Rule 4 (translated)"
+
+msgid "%d = 1"
+msgid_plural "%d = 0 or > 1"
+msgstr[0] "%d = 1 (translated)"
+msgstr[1] "%d = 2 (translated)"
+msgstr[2] "%d everything else (translated)"
+
+#~ msgid "Plural-Forms 1"
+#~ msgstr "Plural-Forms 1 (translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_5_mo/LC_MESSAGES/core.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_5_mo/LC_MESSAGES/core.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_5_mo/LC_MESSAGES/default.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_5_mo/LC_MESSAGES/default.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_5_po/LC_MESSAGES/core.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_5_po/LC_MESSAGES/core.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_5_po/LC_MESSAGES/core.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,23 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-05-15 02:51-0700\n"
+"PO-Revision-Date: \n"
+"Last-Translator: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2;\n"
+"X-Poedit-Language: Three Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1 (from core)"
+msgstr "Plural Rule 5 (from core translated)"
+
+msgid "%d = 1 (from core)"
+msgid_plural "%d = 0 or > 1 (from core)"
+msgstr[0] "%d = 1 (from core translated)"
+msgstr[1] "%d = 0 or ends in 01-19 (from core translated)"
+msgstr[2] "%d everything else (from core translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_5_po/LC_MESSAGES/default.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_5_po/LC_MESSAGES/default.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_5_po/LC_MESSAGES/default.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,24 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-05-15 02:51-0700\n"
+"PO-Revision-Date: \n"
+"Last-Translator: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2;\n"
+
+msgid "Plural Rule 1"
+msgstr "Plural Rule 5 (translated)"
+
+msgid "%d = 1"
+msgid_plural "%d = 0 or > 1"
+msgstr[0] "%d = 1 (translated)"
+msgstr[1] "%d = 0 or ends in 01-19 (translated)"
+msgstr[2] "%d everything else (translated)"
+
+#~ msgid "Plural-Forms 1"
+#~ msgstr "Plural-Forms 1 (translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_6_mo/LC_MESSAGES/core.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_6_mo/LC_MESSAGES/core.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_6_mo/LC_MESSAGES/default.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_6_mo/LC_MESSAGES/default.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_6_po/LC_MESSAGES/core.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_6_po/LC_MESSAGES/core.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_6_po/LC_MESSAGES/core.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,23 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-05-15 02:51-0700\n"
+"PO-Revision-Date: \n"
+"Last-Translator: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Poedit-Language: Three Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1 (from core)"
+msgstr "Plural Rule 6 (from core translated)"
+
+msgid "%d = 1 (from core)"
+msgid_plural "%d = 0 or > 1 (from core)"
+msgstr[0] "%d ends in 1, not 11 (from core translated)"
+msgstr[1] "%d everything else (from core translated)"
+msgstr[2] "%d ends in 0 or ends in 10-20 (from core translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_6_po/LC_MESSAGES/default.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_6_po/LC_MESSAGES/default.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_6_po/LC_MESSAGES/default.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,26 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-05-15 02:51-0700\n"
+"PO-Revision-Date: \n"
+"Last-Translator: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Poedit-Language: Three Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1"
+msgstr "Plural Rule 6 (translated)"
+
+msgid "%d = 1"
+msgid_plural "%d = 0 or > 1"
+msgstr[0] "%d ends in 1, not 11 (translated)"
+msgstr[1] "%d everything else (translated)"
+msgstr[2] "%d ends in 0 or ends in 10-20 (translated)"
+
+#~ msgid "Plural-Forms 1"
+#~ msgstr "Plural-Forms 1 (translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_7_mo/LC_MESSAGES/core.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_7_mo/LC_MESSAGES/core.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_7_mo/LC_MESSAGES/default.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_7_mo/LC_MESSAGES/default.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_7_po/LC_MESSAGES/core.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_7_po/LC_MESSAGES/core.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_7_po/LC_MESSAGES/core.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,23 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-05-15 02:51-0700\n"
+"PO-Revision-Date: \n"
+"Last-Translator: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+"X-Poedit-Language: Three Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1 (from core)"
+msgstr "Plural Rule 7 (from core translated)"
+
+msgid "%d = 1 (from core)"
+msgid_plural "%d = 0 or > 1 (from core)"
+msgstr[0] "%d ends in 1, not 11 (from core translated)"
+msgstr[1] "%d ends in 2-4, not 12-14 (from core translated)"
+msgstr[2] "%d everything else (from core translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_7_po/LC_MESSAGES/default.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_7_po/LC_MESSAGES/default.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_7_po/LC_MESSAGES/default.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,26 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-05-15 02:51-0700\n"
+"PO-Revision-Date: \n"
+"Last-Translator: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+"X-Poedit-Language: Three Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1"
+msgstr "Plural Rule 7 (translated)"
+
+msgid "%d = 1"
+msgid_plural "%d = 0 or > 1"
+msgstr[0] "%d ends in 1, not 11 (translated)"
+msgstr[1] "%d ends in 2-4, not 12-14 (translated)"
+msgstr[2] "%d everything else (translated)"
+
+#~ msgid "Plural-Forms 1"
+#~ msgstr "Plural-Forms 1 (translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_8_mo/LC_MESSAGES/core.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_8_mo/LC_MESSAGES/core.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_8_mo/LC_MESSAGES/default.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_8_mo/LC_MESSAGES/default.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_8_po/LC_MESSAGES/core.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_8_po/LC_MESSAGES/core.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_8_po/LC_MESSAGES/core.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,23 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-05-15 02:51-0700\n"
+"PO-Revision-Date: \n"
+"Last-Translator: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
+"X-Poedit-Language: Three Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1 (from core)"
+msgstr "Plural Rule 8 (from core translated)"
+
+msgid "%d = 1 (from core)"
+msgid_plural "%d = 0 or > 1 (from core)"
+msgstr[0] "%d is 1 (from core translated)"
+msgstr[1] "%d is 2-4 (from core translated)"
+msgstr[2] "%d everything else (from core translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_8_po/LC_MESSAGES/default.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_8_po/LC_MESSAGES/default.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_8_po/LC_MESSAGES/default.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,26 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-05-15 02:51-0700\n"
+"PO-Revision-Date: \n"
+"Last-Translator: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
+"X-Poedit-Language: Three Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1"
+msgstr "Plural Rule 8 (translated)"
+
+msgid "%d = 1"
+msgid_plural "%d = 0 or > 1"
+msgstr[0] "%d is 1 (translated)"
+msgstr[1] "%d is 2-4 (translated)"
+msgstr[2] "%d everything else (translated)"
+
+#~ msgid "Plural-Forms 1"
+#~ msgstr "Plural-Forms 1 (translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_9_mo/LC_MESSAGES/core.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_9_mo/LC_MESSAGES/core.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_9_mo/LC_MESSAGES/default.mo
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/locale/rule_9_mo/LC_MESSAGES/default.mo
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/locale/rule_9_po/LC_MESSAGES/core.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_9_po/LC_MESSAGES/core.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_9_po/LC_MESSAGES/core.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,23 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-05-15 02:51-0700\n"
+"PO-Revision-Date: \n"
+"Last-Translator: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Poedit-Language: Three Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1 (from core)"
+msgstr "Plural Rule 9 (from core translated)"
+
+msgid "%d = 1 (from core)"
+msgid_plural "%d = 0 or > 1 (from core)"
+msgstr[0] "%d is 1 (from core translated)"
+msgstr[1] "%d ends in 2-4, not 12-14 (from core translated)"
+msgstr[2] "%d everything else (from core translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/rule_9_po/LC_MESSAGES/default.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/rule_9_po/LC_MESSAGES/default.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/rule_9_po/LC_MESSAGES/default.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,26 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-05-15 02:51-0700\n"
+"PO-Revision-Date: \n"
+"Last-Translator: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Poedit-Language: Three Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1"
+msgstr "Plural Rule 9 (translated)"
+
+msgid "%d = 1"
+msgid_plural "%d = 0 or > 1"
+msgstr[0] "%d is 1 (translated)"
+msgstr[1] "%d ends in 2-4, not 12-14 (translated)"
+msgstr[2] "%d everything else (translated)"
+
+#~ msgid "Plural-Forms 1"
+#~ msgstr "Plural-Forms 1 (translated)"
+

Added: trunk/src/Web/cake/tests/test_app/locale/time_test/LC_TIME
===================================================================
--- trunk/src/Web/cake/tests/test_app/locale/time_test/LC_TIME	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/locale/time_test/LC_TIME	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,42 @@
+escape_char /
+comment_char %
+abday   "<U0064><U006F><U006D>";"<U006C><U0075><U006E>";/
+        "<U006D><U0061><U0072>";"<U006D><U0069><U00E9>";/
+        "<U006A><U0075><U0065>";"<U0076><U0069><U0065>";/
+        "<U0073><U00E1><U0062>"
+day     "<U0064><U006F><U006D><U0069><U006E><U0067><U006F>";/
+        "<U006C><U0075><U006E><U0065><U0073>";/
+        "<U006D><U0061><U0072><U0074><U0065><U0073>";/
+        "<U006D><U0069><U00E9><U0072><U0063><U006F><U006C><U0065><U0073>";/
+        "<U006A><U0075><U0065><U0076><U0065><U0073>";/
+        "<U0076><U0069><U0065><U0072><U006E><U0065><U0073>";/
+        "<U0073><U00E1><U0062><U0061><U0064><U006F>"
+abmon   "<U0065><U006E><U0065>";"<U0066><U0065><U0062>";/
+        "<U006D><U0061><U0072>";"<U0061><U0062><U0072>";/
+        "<U006D><U0061><U0079>";"<U006A><U0075><U006E>";/
+        "<U006A><U0075><U006C>";"<U0061><U0067><U006F>";/
+        "<U0073><U0065><U0070>";"<U006F><U0063><U0074>";/
+        "<U006E><U006F><U0076>";"<U0064><U0069><U0063>"
+mon     "<U0065><U006E><U0065><U0072><U006F>";/
+        "<U0066><U0065><U0062><U0072><U0065><U0072><U006F>";/
+        "<U006D><U0061><U0072><U007A><U006F>";/
+        "<U0061><U0062><U0072><U0069><U006C>";/
+        "<U006D><U0061><U0079><U006F>";/
+        "<U006A><U0075><U006E><U0069><U006F>";/
+        "<U006A><U0075><U006C><U0069><U006F>";/
+        "<U0061><U0067><U006F><U0073><U0074><U006F>";/
+        "<U0073><U0065><U0070><U0074><U0069><U0065><U006D><U0062><U0072><U0065>";/
+        "<U006F><U0063><U0074><U0075><U0062><U0072><U0065>";/
+        "<U006E><U006F><U0076><U0069><U0065><U006D><U0062><U0072><U0065>";/
+        "<U0064><U0069><U0063><U0069><U0065><U006D><U0062><U0072><U0065>"
+d_t_fmt "<U0025><U0061><U0020><U0025><U0064><U0020><U0025><U0062><U0020><U0025><U0059><U0020><U0025><U0054><U0020><U0025><U005A>"
+d_fmt   "<U0025><U0064><U002F><U0025><U006D><U002F><U0025><U0079>"
+t_fmt   "<U0025><U0054>"
+am_pm   "<U0061><U006D>";"<U0070><U006D>"
+t_fmt_ampm "<U0025><U0049><U003A><U0025><U004D><U003A><U0025><U0053><U0020>/
+<U0025><U0070>"
+date_fmt	"<U0025><U0061><U0020><U0025><U0062><U0020><U0025><U0065>/
+<U0020><U0025><U0048><U003A><U0025><U004D><U003A><U0025><U0053><U0020>/
+<U0025><U005A><U0020><U0025><U0059>"
+%  FIXME: found in CLDR
+first_weekday 2

Added: trunk/src/Web/cake/tests/test_app/models/behaviors/empty
===================================================================
Added: trunk/src/Web/cake/tests/test_app/models/behaviors/persister_one_behavior.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/models/behaviors/persister_one_behavior.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/models/behaviors/persister_one_behavior.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,31 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.tests.test_app.models
+ * @since         CakePHP(tm) v 1.2.0.5669
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 PersisterOneBehaviorBehavior extends ModelBehavior {
+}

Added: trunk/src/Web/cake/tests/test_app/models/behaviors/persister_two_behavior.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/models/behaviors/persister_two_behavior.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/models/behaviors/persister_two_behavior.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,31 @@
+<?php
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.tests.test_app.models
+ * @since         CakePHP(tm) v 1.2.0.5669
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * 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 PersisterTwoBehaviorBehavior extends ModelBehavior {
+}

Added: trunk/src/Web/cake/tests/test_app/models/comment.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/models/comment.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/models/comment.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,25 @@
+<?php
+/**
+ * Test App Comment Model
+ *
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://cakephp.org CakePHP Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.
+ * @since         CakePHP v 1.2.0.7726
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+class Comment extends AppModel {
+	var $useTable = 'comments';
+	var $name = 'Comment';
+}

Added: trunk/src/Web/cake/tests/test_app/models/datasources/test/test_local_driver.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/models/datasources/test/test_local_driver.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/models/datasources/test/test_local_driver.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,5 @@
+<?php
+
+class TestLocalDriver extends TestSource {
+}
+

Added: trunk/src/Web/cake/tests/test_app/models/datasources/test2_other_source.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/models/datasources/test2_other_source.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/models/datasources/test2_other_source.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,27 @@
+<?php
+class Test2OtherSource extends DataSource {
+
+	function describe($model) {
+		return compact('model');
+	}
+
+	function listSources() {
+		return array('test_source');
+	}
+
+	function create($model, $fields = array(), $values = array()) {
+		return compact('model', 'fields', 'values');
+	}
+
+	function read($model, $queryData = array()) {
+		return compact('model', 'queryData');
+	}
+
+	function update($model, $fields = array(), $values = array()) {
+		return compact('model', 'fields', 'values');
+	}
+
+	function delete($model, $id) {
+		return compact('model', 'id');
+	}
+}

Added: trunk/src/Web/cake/tests/test_app/models/datasources/test2_source.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/models/datasources/test2_source.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/models/datasources/test2_source.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,27 @@
+<?php
+class Test2Source extends DataSource {
+
+	function describe($model) {
+		return compact('model');
+	}
+
+	function listSources() {
+		return array('test_source');
+	}
+
+	function create($model, $fields = array(), $values = array()) {
+		return compact('model', 'fields', 'values');
+	}
+
+	function read($model, $queryData = array()) {
+		return compact('model', 'queryData');
+	}
+
+	function update($model, $fields = array(), $values = array()) {
+		return compact('model', 'fields', 'values');
+	}
+
+	function delete($model, $id) {
+		return compact('model', 'id');
+	}
+}

Added: trunk/src/Web/cake/tests/test_app/models/persister_one.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/models/persister_one.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/models/persister_one.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,29 @@
+<?php
+/**
+ * Test App Comment Model
+ *
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://cakephp.org CakePHP Project
+ * @package       cake
+ * @subpackage    cake.tests.test_app.models
+ * @since         CakePHP v 1.2.0.7726
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+class PersisterOne extends AppModel {
+	var $useTable = 'posts';
+	var $name = 'PersisterOne';
+
+	var $actsAs = array('PersisterOneBehavior', 'TestPlugin.TestPluginPersisterOne');
+
+	var $hasMany = array('Comment', 'TestPlugin.TestPluginComment');
+}

Added: trunk/src/Web/cake/tests/test_app/models/persister_two.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/models/persister_two.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/models/persister_two.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,29 @@
+<?php
+/**
+ * Test App Comment Model
+ *
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://cakephp.org CakePHP Project
+ * @package       cake
+ * @subpackage    cake.tests.test_app.models
+ * @since         CakePHP v 1.2.0.7726
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+class PersisterTwo extends AppModel {
+	var $useTable = 'posts';
+	var $name = 'PersisterTwo';
+
+	var $actsAs = array('PersisterOneBehavior', 'TestPlugin.TestPluginPersisterOne');
+
+	var $hasMany = array('Comment', 'TestPlugin.TestPluginComment');
+}

Added: trunk/src/Web/cake/tests/test_app/models/post.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/models/post.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/models/post.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,25 @@
+<?php
+/**
+ * Test App Comment Model
+ *
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://cakephp.org CakePHP Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.
+ * @since         CakePHP v 1.2.0.7726
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+class Post extends AppModel {
+	var $useTable = 'posts';
+	var $name = 'Post';
+}

Added: trunk/src/Web/cake/tests/test_app/plugins/plugin_js/webroot/js/one/plugin_one.js
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/plugin_js/webroot/js/one/plugin_one.js	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/plugin_js/webroot/js/one/plugin_one.js	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+alert('plugin one nested js file');
\ No newline at end of file


Property changes on: trunk/src/Web/cake/tests/test_app/plugins/plugin_js/webroot/js/one/plugin_one.js
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Added: trunk/src/Web/cake/tests/test_app/plugins/plugin_js/webroot/js/plugin_js.js
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/plugin_js/webroot/js/plugin_js.js	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/plugin_js/webroot/js/plugin_js.js	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+alert('win sauce');
\ No newline at end of file


Property changes on: trunk/src/Web/cake/tests/test_app/plugins/plugin_js/webroot/js/plugin_js.js
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/config/load.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/config/load.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/config/load.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,20 @@
+<?php
+/**
+ * Test Suite TestPlugin config file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.test_app
+ * @since         CakePHP(tm) v 1.3
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+$config['plugin_load'] = '/test_app/plugins/test_plugin/config/load.php';

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/config/more.load.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/config/more.load.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/config/more.load.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,20 @@
+<?php
+/**
+ * Test Suite TestPlugin config file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.test_app
+ * @since         CakePHP(tm) v 1.3
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+$config['plugin_more_load'] = '/test_app/plugins/test_plugin/config/more.load.php';

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/config/schema/schema.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/config/schema/schema.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/config/schema/schema.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,36 @@
+<?php
+/**
+ * TestAppSchema file
+ *
+ * Use for testing the loading of schema files from plugins.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.app.config.sql
+ * @since         CakePHP(tm) v 1.3
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+class TestPluginAppSchema extends CakeSchema {
+
+	var $name = 'TestPluginApp';
+
+	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))
+	);
+}

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/controllers/components/other_component.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/controllers/components/other_component.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/controllers/components/other_component.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,21 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.test_app.plugins.test_plugin.views.helpers
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+class OtherComponentComponent extends Object {
+}

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/controllers/components/plugins_component.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/controllers/components/plugins_component.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/controllers/components/plugins_component.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,22 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.test_app.plugins.test_plugin.views.helpers
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+class PluginsComponentComponent extends Object {
+	var $components = array('TestPlugin.OtherComponent');
+}

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/controllers/components/test_plugin_component.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/controllers/components/test_plugin_component.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/controllers/components/test_plugin_component.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,22 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.test_app.plugins.test_plugin.views.helpers
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+class TestPluginComponentComponent extends Object {
+	var $components = array('TestPlugin.TestPluginOtherComponent');
+}

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/controllers/components/test_plugin_other_component.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/controllers/components/test_plugin_other_component.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/controllers/components/test_plugin_other_component.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,21 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.test_app.plugins.test_plugin.views.helpers
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+class TestPluginOtherComponentComponent extends Object {
+}

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/controllers/test_plugin_controller.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/controllers/test_plugin_controller.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/controllers/test_plugin_controller.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,30 @@
+<?php
+/**
+ * TestPluginController used by Dispatcher test to test plugin shortcut urls.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.test_app.plugins.test_plugin.views.helpers
+ * @since         CakePHP(tm) v 1.3
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+class TestPluginController extends TestPluginAppController {
+	var $uses = array();
+	
+	function index() {
+		$this->autoRender = false;
+	}
+
+	function add() {
+		$this->autoRender = false;
+	}
+}

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/controllers/tests_controller.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/controllers/tests_controller.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/controllers/tests_controller.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,32 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.test_app.plugins.test_plugin.views.helpers
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+class TestsController extends TestPluginAppController {
+	var $name = 'Tests';
+	var $uses = array();
+	var $helpers = array('TestPlugin.OtherHelper', 'Html');
+	var $components = array('TestPlugin.PluginsComponent');
+
+	function index() {
+	}
+
+	function some_method() {
+		return 25;
+	}
+}

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/libs/cache/test_plugin_cache.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/libs/cache/test_plugin_cache.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/libs/cache/test_plugin_cache.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,21 @@
+<?php
+/**
+ * Test Suite Test Plugin Cache Engine class.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.3
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+class TestPluginCacheEngine extends CacheEngine {
+}

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/libs/log/test_plugin_log.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/libs/log/test_plugin_log.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/libs/log/test_plugin_log.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,25 @@
+<?php
+/**
+ * Test Suite Test Plugin Logging stream class.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.3
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+class TestPluginLog {
+
+	function write($type, $message) {
+		
+	}
+}

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/libs/test_plugin_library.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/libs/test_plugin_library.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/libs/test_plugin_library.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,20 @@
+<?php
+/**
+ * Test Suite TestPlugin Library
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.5432
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+class TestPluginLibrary {}

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/locale/po/LC_MESSAGES/test_plugin.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/locale/po/LC_MESSAGES/test_plugin.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/locale/po/LC_MESSAGES/test_plugin.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,50 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-05-15 02:51-0700\n"
+"PO-Revision-Date: \n"
+"Last-Translator: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Poedit-Language: Two Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1"
+msgstr "Plural Rule 1 (from plugin)"
+
+msgid "%d = 1"
+msgid_plural "%d = 0 or > 1"
+msgstr[0] "%d = 1 (from plugin)"
+msgstr[1] "%d = 0 or > 1 (from plugin)"
+
+#~ msgid "Plural-Forms 1"
+#~ msgstr "Plural-Forms 1 (from plugin)"
+
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-05-15 02:51-0700\n"
+"PO-Revision-Date: \n"
+"Last-Translator: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Poedit-Language: Two Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1"
+msgstr "Plural Rule 1 (from plugin)"
+
+msgid "%d = 1"
+msgid_plural "%d = 0 or > 1"
+msgstr[0] "%d = 1 (from plugin)"
+msgstr[1] "%d = 0 or > 1 (from plugin)"
+
+#~ msgid "Plural-Forms 1"
+#~ msgstr "Plural-Forms 1 (from plugin)"
+

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/locale/po/LC_MONETARY/test_plugin.po
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/locale/po/LC_MONETARY/test_plugin.po	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/locale/po/LC_MONETARY/test_plugin.po	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,22 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: CakePHP Testsuite\n"
+"POT-Creation-Date: 2008-05-15 02:51-0700\n"
+"PO-Revision-Date: \n"
+"Last-Translator: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"Language-Team: CakePHP I18N & I10N Team <i10n.****@gmail*****>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Poedit-Language: Two Forms of Plurals\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+msgid "Plural Rule 1"
+msgstr "Monetary Plural Rule 1 (from plugin)"
+
+msgid "%d = 1"
+msgid_plural "%d = 0 or > 1"
+msgstr[0] "Monetary %d = 1 (from plugin)"
+msgstr[1] "Monetary %d = 0 or > 1 (from plugin)"
+

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/behaviors/test_plugin_persister_one.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/behaviors/test_plugin_persister_one.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/behaviors/test_plugin_persister_one.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,34 @@
+<?php
+/* SVN FILE: $Id$ */
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.tests.test_app.models
+ * @since         CakePHP(tm) v 1.2.0.5669
+ * @version       $Revision$
+ * @modifiedby    $LastChangedBy$
+ * @lastmodified  $Date$
+ * @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 TestPluginPersisterOneBehavior extends ModelBehavior {
+}

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/behaviors/test_plugin_persister_two.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/behaviors/test_plugin_persister_two.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/behaviors/test_plugin_persister_two.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,34 @@
+<?php
+/* SVN FILE: $Id$ */
+/**
+ * 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://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.tests.test_app.models
+ * @since         CakePHP(tm) v 1.2.0.5669
+ * @version       $Revision$
+ * @modifiedby    $LastChangedBy$
+ * @lastmodified  $Date$
+ * @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 TestPluginPersisterTwoBehavior extends ModelBehavior {
+}

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/datasources/dbo/dbo_dummy.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/datasources/dbo/dbo_dummy.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/datasources/dbo/dbo_dummy.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,7 @@
+<?php
+
+class DboDummy extends DboSource {
+	function connect() {
+		return true;
+	}
+}

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/datasources/test/test_driver.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/datasources/test/test_driver.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/datasources/test/test_driver.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,4 @@
+<?php
+
+class TestDriver extends TestSource {
+}

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/datasources/test_other_source.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/datasources/test_other_source.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/datasources/test_other_source.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,27 @@
+<?php
+class TestOtherSource extends DataSource {
+
+	function describe($model) {
+		return compact('model');
+	}
+
+	function listSources() {
+		return array('test_source');
+	}
+
+	function create($model, $fields = array(), $values = array()) {
+		return compact('model', 'fields', 'values');
+	}
+
+	function read($model, $queryData = array()) {
+		return compact('model', 'queryData');
+	}
+
+	function update($model, $fields = array(), $values = array()) {
+		return compact('model', 'fields', 'values');
+	}
+
+	function delete($model, $id) {
+		return compact('model', 'id');
+	}
+}

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/datasources/test_source.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/datasources/test_source.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/datasources/test_source.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,27 @@
+<?php
+class TestSource extends DataSource {
+
+	function describe($model) {
+		return compact('model');
+	}
+
+	function listSources() {
+		return array('test_source');
+	}
+
+	function create($model, $fields = array(), $values = array()) {
+		return compact('model', 'fields', 'values');
+	}
+
+	function read($model, $queryData = array()) {
+		return compact('model', 'queryData');
+	}
+
+	function update($model, $fields = array(), $values = array()) {
+		return compact('model', 'fields', 'values');
+	}
+
+	function delete($model, $id) {
+		return compact('model', 'id');
+	}
+}

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/test_plugin_auth_user.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/test_plugin_auth_user.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/test_plugin_auth_user.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,46 @@
+<?php
+/**
+ * Test Plugin Auth User Model
+ *
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://cakephp.org CakePHP Project
+ * @package       cake
+ * @subpackage    cake.cake.tests.test_app.plugins.test_plugin
+ * @since         CakePHP v 1.2.0.4487
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+class TestPluginAuthUser extends TestPluginAppModel {
+
+/**
+ * Name property
+ *
+ * @var string
+ */
+	var $name = 'TestPluginAuthUser';
+
+/**
+ * useTable property
+ *
+ * @var string
+ */
+	var $useTable = 'auth_users';
+
+/**
+ * useDbConfig property
+ *
+ * @var string 'test_suite'
+ * @access public
+ */
+	var $useDbConfig = 'test_suite';
+}

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/test_plugin_authors.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/test_plugin_authors.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/test_plugin_authors.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,29 @@
+<?php
+/* SVN FILE: $Id$ */
+/**
+ * Test App Comment Model
+ *
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP :  Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://cakefoundation.org/projects/info/cakephp CakePHP Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.
+ * @since         CakePHP v 1.2.0.7726
+ * @version       $Revision$
+ * @modifiedby    $LastChangedBy$
+ * @lastmodified  $Date$
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+class TestPluginAuthors extends TestPluginAppModel {
+	var $useTable = 'authors';
+	var $name = 'TestPluginAuthors';
+}

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/test_plugin_comment.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/test_plugin_comment.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/test_plugin_comment.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,29 @@
+<?php
+/* SVN FILE: $Id$ */
+/**
+ * Test App Comment Model
+ *
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP :  Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://cakefoundation.org/projects/info/cakephp CakePHP Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.
+ * @since         CakePHP v 1.2.0.7726
+ * @version       $Revision$
+ * @modifiedby    $LastChangedBy$
+ * @lastmodified  $Date$
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+class TestPluginComment extends TestPluginAppModel {
+	var $useTable = 'test_plugin_comments';
+	var $name = 'TestPluginComment';
+}

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/test_plugin_post.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/test_plugin_post.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/models/test_plugin_post.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Test Plugin Post Model
+ *
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc.
+ * @link          http://cakephp.org CakePHP Project
+ * @package       cake
+ * @subpackage    cake.cake.tests.test_app.plugins.test_plugin
+ * @since         CakePHP v 1.2.0.4487
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+class TestPluginPost extends TestPluginAppModel {
+
+/**
+ * Name property
+ *
+ * @var string
+ */
+	var $name = 'Post';
+
+/**
+ * useTable property
+ *
+ * @var string
+ */
+	var $useTable = 'posts';
+}

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/test_plugin_app_controller.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/test_plugin_app_controller.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/test_plugin_app_controller.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,20 @@
+<?php
+/**
+ * Test Suite TestPlugin AppController
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.5432
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+class TestPluginAppController extends AppController { }

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/test_plugin_app_model.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/test_plugin_app_model.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/test_plugin_app_model.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,20 @@
+<?php
+/**
+ * Test Suite TestPlugin AppModel
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ * @since         CakePHP(tm) v 1.2.0.5432
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+class TestPluginAppModel extends CakeTestModel {}

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/vendors/sample/sample_plugin.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/vendors/sample/sample_plugin.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/vendors/sample/sample_plugin.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,21 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.test_app.plugins.test_plugin.vendors.sample
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+class SamplePluginClassTestName {
+}

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/vendors/shells/example.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/vendors/shells/example.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/vendors/shells/example.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.test_app.plugins.test_plugin.vendors.shells
+ * @since         CakePHP(tm) v 1.2.0.7871
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+class ExampleShell extends Shell {
+
+/**
+ * main method
+ *
+ * @access public
+ * @return void
+ */
+	function main() {
+		$this->out('This is the main method called from TestPlugin.ExampleShell');
+	}
+}

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/vendors/shells/tasks/empty
===================================================================
Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/vendors/shells/templates/empty
===================================================================
Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/vendors/welcome.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/vendors/welcome.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/vendors/welcome.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,21 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.test_app.plugins.test_plugin.vendors
+ * @since         CakePHP(tm) v 1.2.0.7629
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+?>
+This is the welcome.php file in test_plugin/vendors directory
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/elements/plugin_element.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/elements/plugin_element.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/elements/plugin_element.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+<?php echo 'this is the plugin element using params[plugin]'; ?>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/elements/test_plugin_element.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/elements/test_plugin_element.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/elements/test_plugin_element.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+<?php echo 'this is the test set using View::$plugin plugin element'; ?>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/helpers/other_helper.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/helpers/other_helper.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/helpers/other_helper.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,20 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.test_app.plugins.test_plugin.views.helpers
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+class OtherHelperHelper extends AppHelper {}

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/helpers/plugged_helper.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/helpers/plugged_helper.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/helpers/plugged_helper.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,22 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.test_app.plugins.test_plugin.views.helpers
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+class PluggedHelperHelper extends AppHelper {
+	var $helpers = array('TestPlugin.OtherHelper');
+}

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/helpers/test_plugin_app.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/helpers/test_plugin_app.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/helpers/test_plugin_app.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,5 @@
+<?php
+
+class TestPluginAppHelper extends AppHelper {
+	
+}
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/layouts/default.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/layouts/default.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/layouts/default.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+test plugin default layout
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/tests/index.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/tests/index.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/tests/index.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+test plugin index
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/tests/scaffold.edit.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/tests/scaffold.edit.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/views/tests/scaffold.edit.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+test_plugin add/edit scaffold view
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/css/test_plugin_asset.css
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/css/test_plugin_asset.css	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/css/test_plugin_asset.css	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+this is the test plugin asset css file
\ No newline at end of file


Property changes on: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/css/test_plugin_asset.css
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/css/theme_one.htc
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/css/theme_one.htc	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/css/theme_one.htc	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+htc file
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/css/unknown.extension
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/css/unknown.extension	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/css/unknown.extension	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+Testing a file with unknown extension to mime mapping.
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/flash/plugin_test.swf
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/flash/plugin_test.swf	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/flash/plugin_test.swf	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+this is just a test to load swf file from the plugin.
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/img/cake.icon.gif
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/img/cake.icon.gif
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/js/test_plugin/test.js
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/js/test_plugin/test.js	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/js/test_plugin/test.js	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+alert("Test App");
\ No newline at end of file


Property changes on: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/js/test_plugin/test.js
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/pdfs/plugin_test.pdf
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/pdfs/plugin_test.pdf
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/root.js
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/root.js	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/root.js	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+alert('I am a root level file!');
\ No newline at end of file


Property changes on: trunk/src/Web/cake/tests/test_app/plugins/test_plugin/webroot/root.js
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin_two/vendors/shells/example.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin_two/vendors/shells/example.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin_two/vendors/shells/example.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.test_app.plugins.test_plugin_two.vendors.shells
+ * @since         CakePHP(tm) v 1.2.0.7871
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+class ExampleShell extends Shell {
+
+/**
+ * main method
+ *
+ * @access public
+ * @return void
+ */
+	function main() {
+		$this->out('This is the main method called from TestPluginTwo.ExampleShell');
+	}
+}

Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin_two/vendors/shells/tasks/empty
===================================================================
Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin_two/vendors/shells/templates/empty
===================================================================
Added: trunk/src/Web/cake/tests/test_app/plugins/test_plugin_two/vendors/shells/welcome.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/plugins/test_plugin_two/vendors/shells/welcome.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/plugins/test_plugin_two/vendors/shells/welcome.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.test_app.plugins.test_plugin_two.vendors.shells
+ * @since         CakePHP(tm) v 1.2.0.7871
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+class WelcomeShell extends Shell {
+
+/**
+ * say_hello method
+ *
+ * @access public
+ * @return void
+ */
+	function say_hello() {
+		$this->out('This is the say_hello method called from TestPluginTwo.WelcomeShell');
+	}
+}

Added: trunk/src/Web/cake/tests/test_app/tmp/dir_map
===================================================================
--- trunk/src/Web/cake/tests/test_app/tmp/dir_map	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/tmp/dir_map	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,2 @@
+1845415352
+a:4:{s:27:"C:\\\\dev\\\\prj2\\\\sites\\\\cake\\\\libs";a:24:{i:0;s:27:"C:\\\\dev\\\\prj2\\\\sites\\\\cake\\\\libs";i:1;s:32:"C:\\\\dev\\\\prj2\\\\sites\\\\cake\\\\libs\\\\view";i:2;s:42:"C:\\\\dev\\\\prj2\\\\sites\\\\cake\\\\libs\\\\view\\\\scaffolds";i:3;s:38:"C:\\\\dev\\\\prj2\\\\sites\\\\cake\\\\libs\\\\view\\\\pages";i:4;s:40:"C:\\\\dev\\\\prj2\\\\sites\\\\cake\\\\libs\\\\view\\\\layouts";i:5;s:44:"C:\\\\dev\\\\prj2\\\\sites\\\\cake\\\\libs\\\\view\\\\layouts\\\\xml";i:6;s:44:"C:\\\\dev\\\\prj2\\\\sites\\\\cake\\\\libs\\\\view\\\\layouts\\\\rss";i:7;s:43:"C:\\\\dev\\\\prj2\\\\sites\\\\cake\\\\libs\\\\view\\\\layouts\\\\js";i:8;s:46:"C:\\\\dev\\\\prj2\\\\sites\\\\cake\\\\libs\\\\view\\\\layouts\\\\email";i:9;s:51:"C:\\\\dev\\\\prj2\\\\sites\\\\cake\\\\libs\\\\view\\\\layouts\\\\email\\\\text";i:10;s:51:"C:\\\\dev\\\\prj2\\\\sites\\\\cake\\\\libs\\\\view\\\\layouts\\\\email\\\\html";i:11;s:40:"C:\\\\dev\\\\prj2\\\\sites\\\\cake\\\\libs\\\\view\\\\helpers";i:12;s:39:"C:\\\\
 dev\\\\prj2\\\\sites\\\\cake\\\\libs\\\\view\\\\errors";i:13;s:41:"C:\\\\dev\\\\prj2\\\\sites\\\\cake\\\\libs\\\\view\\\\elements";i:14;s:47:"C:\\\\dev\\\\prj2\\\\sites\\\\cake\\\\libs\\\\view\\\\elements\\\\email";i:15;s:52:"C:\\\\dev\\\\prj2\\\\sites\\\\cake\\\\libs\\\\view\\\\elements\\\\email\\\\text";i:16;s:52:"C:\\\\dev\\\\prj2\\\\sites\\\\cake\\\\libs\\\\view\\\\elements\\\\email\\\\html";i:17;s:33:"C:\\\\dev\\\\prj2\\\\sites\\\\cake\\\\libs\\\\model";i:18;s:45:"C:\\\\dev\\\\prj2\\\\sites\\\\cake\\\\libs\\\\model\\\\datasources";i:19;s:49:"C:\\\\dev\\\\prj2\\\\sites\\\\cake\\\\libs\\\\model\\\\datasources\\\\dbo";i:20;s:43:"C:\\\\dev\\\\prj2\\\\sites\\\\cake\\\\libs\\\\model\\\\behaviors";i:21;s:38:"C:\\\\dev\\\\prj2\\\\sites\\\\cake\\\\libs\\\\controller";i:22;s:49:"C:\\\\dev\\\\prj2\\\\sites\\\\cake\\\\libs\\\\controller\\\\components";i:23;s:33:"C:\\\\dev\\\\prj2\\\\sites\\\\cake\\\\libs\\\\cache";}s:35:"C:\\\\dev\\\\prj2\\\\sites\\\\main_site\\\\vendors";a:7:{i:0;
 s:35:"C:\\\\dev\\\\prj2\\\\sites\\\\main_site\\\\vendors";i:1;s:42:"C:\\\\dev\\\\prj2\\\\sites\\\\main_site\\\\vendors\\\\shells";i:2;s:52:"C:\\\\dev\\\\prj2\\\\sites\\\\main_site\\\\vendors\\\\shells\\\\templates";i:3;s:64:"C:\\\\dev\\\\prj2\\\\sites\\\\main_site\\\\vendors\\\\shells\\\\templates\\\\cdc_project";i:4;s:48:"C:\\\\dev\\\\prj2\\\\sites\\\\main_site\\\\vendors\\\\shells\\\\tasks";i:5;s:38:"C:\\\\dev\\\\prj2\\\\sites\\\\main_site\\\\vendors\\\\js";i:6;s:39:"C:\\\\dev\\\\prj2\\\\sites\\\\main_site\\\\vendors\\\\css";}s:25:"C:\\\\dev\\\\prj2\\\\sites\\\\vendors";a:10:{i:0;s:25:"C:\\\\dev\\\\prj2\\\\sites\\\\vendors";i:1;s:36:"C:\\\\dev\\\\prj2\\\\sites\\\\vendors\\\\simpletest";i:2;s:41:"C:\\\\dev\\\\prj2\\\\sites\\\\vendors\\\\simpletest\\\\test";i:3;s:49:"C:\\\\dev\\\\prj2\\\\sites\\\\vendors\\\\simpletest\\\\test\\\\support";i:4;s:59:"C:\\\\dev\\\\prj2\\\\sites\\\\vendors\\\\simpletest\\\\test\\\\support\\\\collector";i:5;s:47:"C:\\\\dev\\\\prj2\\\\sites\\\\vend
 ors\\\\simpletest\\\\extensions";i:6;s:55:"C:\\\\dev\\\\prj2\\\\sites\\\\vendors\\\\simpletest\\\\extensions\\\\testdox";i:7;s:41:"C:\\\\dev\\\\prj2\\\\sites\\\\vendors\\\\simpletest\\\\docs";i:8;s:44:"C:\\\\dev\\\\prj2\\\\sites\\\\vendors\\\\simpletest\\\\docs\\\\fr";i:9;s:44:"C:\\\\dev\\\\prj2\\\\sites\\\\vendors\\\\simpletest\\\\docs\\\\en";}s:41:"C:\\\\dev\\\\prj2\\\\sites\\\\main_site\\\\views\\\\helpers";a:1:{i:0;s:41:"C:\\\\dev\\\\prj2\\\\sites\\\\main_site\\\\views\\\\helpers";}}

Added: trunk/src/Web/cake/tests/test_app/vendors/Test/MyTest.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/vendors/Test/MyTest.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/vendors/Test/MyTest.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,21 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.test_app.vendors.somename
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+?>
+This is the MyTest.php file
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/vendors/Test/hello.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/vendors/Test/hello.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/vendors/Test/hello.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,21 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.test_app.vendors.Test
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+?>
+This is the hello.php file in Test directory
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/vendors/css/test_asset.css
===================================================================
--- trunk/src/Web/cake/tests/test_app/vendors/css/test_asset.css	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/vendors/css/test_asset.css	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+this is the test asset css file
\ No newline at end of file


Property changes on: trunk/src/Web/cake/tests/test_app/vendors/css/test_asset.css
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Added: trunk/src/Web/cake/tests/test_app/vendors/img/test.jpg
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/vendors/img/test.jpg
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/vendors/img/test_2.JPG
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/vendors/img/test_2.JPG
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/vendors/sample/configure_test_vendor_sample.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/vendors/sample/configure_test_vendor_sample.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/vendors/sample/configure_test_vendor_sample.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,21 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.test_app.vendors.sample
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+class ConfigureTestVendorSample {
+}

Added: trunk/src/Web/cake/tests/test_app/vendors/shells/sample.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/vendors/shells/sample.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/vendors/shells/sample.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.test_app.vendors.shells
+ * @since         CakePHP(tm) v 1.2.0.7871
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+class SampleShell extends Shell {
+
+/**
+ * main method
+ *
+ * @access public
+ * @return void
+ */
+	function main() {
+		$this->out('This is the main method called from SampleShell');
+	}
+}

Added: trunk/src/Web/cake/tests/test_app/vendors/shells/tasks/empty
===================================================================
Added: trunk/src/Web/cake/tests/test_app/vendors/shells/templates/test/classes/test_object.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/vendors/shells/templates/test/classes/test_object.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/vendors/shells/templates/test/classes/test_object.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,2 @@
+I got rendered
+<?php echo $test; ?>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/vendors/somename/some.name.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/vendors/somename/some.name.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/vendors/somename/some.name.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,21 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.test_app.vendors.somename
+ * @since         CakePHP(tm) v 1.2.0.4206
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+?>
+This is a file with dot in file name
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/vendors/welcome.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/vendors/welcome.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/vendors/welcome.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,21 @@
+<?php
+/**
+ * Short description for file.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ *  Licensed under The Open Group Test Suite License
+ *  Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.test_app.vendors
+ * @since         CakePHP(tm) v 1.2.0.7629
+ * @license       http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License
+ */
+?>
+This is the welcome.php file in vendors directory
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/elements/email/html/custom.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/elements/email/html/custom.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/elements/email/html/custom.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,26 @@
+<?php
+/* SVN FILE: $Id$ */
+
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2012, 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-2012, 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$
+ * @modifiedby    $LastChangedBy$
+ * @lastmodified  $Date$
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+<p>Here is your value: <b><?php echo $value; ?></b></p>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/elements/email/html/default.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/elements/email/html/default.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/elements/email/html/default.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,26 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.elements.email.html
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<?php
+$content = explode("\n", $content);
+
+foreach($content as $line):
+	echo '<p> ' . $line . '</p>';
+endforeach;
+?>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/elements/email/html/nested_element.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/elements/email/html/nested_element.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/elements/email/html/nested_element.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,3 @@
+Before the element.
+<?php echo $this->element('html_call'); ?>
+After the element.
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/elements/email/text/custom.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/elements/email/text/custom.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/elements/email/text/custom.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,26 @@
+<?php
+/* SVN FILE: $Id$ */
+
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
+ * Copyright 2005-2012, 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-2012, 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$
+ * @modifiedby    $LastChangedBy$
+ * @lastmodified  $Date$
+ * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+?>
+Here is your value: <?php echo $value; ?>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/elements/email/text/default.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/elements/email/text/default.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/elements/email/text/default.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,20 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.elements.email.text
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<?php echo $content; ?>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/elements/email/text/wide.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/elements/email/text/wide.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/elements/email/text/wide.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,21 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.elements.email.text
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+This element has some text that is just too wide to comply with email standards.
+<?php echo $content; ?>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/elements/empty
===================================================================
Added: trunk/src/Web/cake/tests/test_app/views/elements/html_call.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/elements/html_call.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/elements/html_call.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,3 @@
+<?php
+echo $this->Html->link('Test', 'http://example.com');
+?>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/elements/nocache/contains_nocache.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/elements/nocache/contains_nocache.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/elements/nocache/contains_nocache.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,5 @@
+<h2>Cache Me</h2>
+<cake:nocache>
+	<p>F. In Element With No Cache Tags</p>
+	<?php $this->log('6. In element with no cache tags') ?>
+</cake:nocache>

Added: trunk/src/Web/cake/tests/test_app/views/elements/nocache/plain.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/elements/nocache/plain.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/elements/nocache/plain.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,4 @@
+<h2>Cache Me</h2>
+	<p>B. In Plain Element</p>
+	<?php $this->log('2. in plain element') ?>
+

Added: trunk/src/Web/cake/tests/test_app/views/elements/nocache/sub1.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/elements/nocache/sub1.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/elements/nocache/sub1.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,8 @@
+<?php echo $this->element('nocache/sub2'); ?>
+
+<cake:nocache>
+	<?php $foobar = 'in sub1'; ?>
+	<?php echo $foobar; ?>
+</cake:nocache>
+
+<?php echo 'printing: "' . $foobar . '"'; ?>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/elements/nocache/sub2.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/elements/nocache/sub2.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/elements/nocache/sub2.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,6 @@
+<cake:nocache>
+	<?php $barfoo = 'in sub2'; ?>
+	<?php echo $barfoo; ?>
+</cake:nocache>
+
+<?php echo 'printing: "' . $barfoo . '"'; ?>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/elements/session_helper.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/elements/session_helper.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/elements/session_helper.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,5 @@
+<div id="notificationLayout">
+	<h1><?php echo $name; ?></h1>
+	<h3><?php echo $title; ?></h3>
+	<p><?php echo $message; ?></p>
+</div>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/elements/test_element.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/elements/test_element.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/elements/test_element.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+<?php echo 'this is the test element'; ?>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/elements/test_element.xml
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/elements/test_element.xml	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/elements/test_element.xml	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+<p>test element</p>


Property changes on: trunk/src/Web/cake/tests/test_app/views/elements/test_element.xml
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Added: trunk/src/Web/cake/tests/test_app/views/elements/type_check.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/elements/type_check.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/elements/type_check.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+<?php echo gettype($form); ?>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/errors/empty
===================================================================
Added: trunk/src/Web/cake/tests/test_app/views/helpers/banana.php
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/helpers/banana.php	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/helpers/banana.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,20 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.layouts
+ * @since         CakePHP(tm) v 1.3
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+class BananaHelper extends Helper {
+}

Added: trunk/src/Web/cake/tests/test_app/views/helpers/empty
===================================================================
Added: trunk/src/Web/cake/tests/test_app/views/layouts/ajax.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/layouts/ajax.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/layouts/ajax.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,20 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.layouts
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<?php echo $content_for_layout; ?>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/layouts/ajax2.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/layouts/ajax2.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/layouts/ajax2.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,23 @@
+<?php
+/* SVN FILE: $Id: ajax2.ctp 7062 2008-05-30 11:29:53Z nate $ */
+
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.layouts
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+Ajax!
+<?php echo $content_for_layout; ?>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/layouts/cache_empty_sections.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/layouts/cache_empty_sections.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/layouts/cache_empty_sections.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,13 @@
+<!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>
+	<title><?php echo $title_for_layout; ?></title>
+	<cake:nocache><?php $x = 1; ?></cake:nocache>
+</head>
+<body>
+	<cake:nocache><?php $x++; ?></cake:nocache>
+	<cake:nocache><?php $x++; ?></cake:nocache>
+	<?php echo $content_for_layout;	?>
+	<cake:nocache><?php echo 'cached count is: ' . $x; ?></cake:nocache>
+</body>
+</html>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/layouts/cache_layout.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/layouts/cache_layout.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/layouts/cache_layout.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,32 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.layouts
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<p>This is regular text</p>
+<cake:nocache>
+	<?php echo microtime(); ?>
+</cake:nocache>
+
+<?php echo $content_for_layout; ?>
+
+<?php echo $superman; ?>
+
+<cake:nocache>
+	<?php echo $variable; ?>
+</cake:nocache>
+<p>Additional regular text.</p>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/layouts/default.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/layouts/default.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/layouts/default.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,56 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.pages
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<!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 $this->Html->charset();?>
+	<title>
+		<?php __('CakePHP: the rapid development php framework:'); ?>
+		<?php echo $title_for_layout;?>
+	</title>
+
+	<link rel="icon" href="<?php echo $this->webroot;?>favicon.ico" type="image/x-icon" />
+	<link rel="shortcut icon" href="<?php echo $this->webroot;?>favicon.ico" type="image/x-icon" />
+	<?php echo $this->Html->css('cake.generic');?>
+	<?php echo $scripts_for_layout;?>
+</head>
+<body>
+	<div id="container">
+		<div id="header">
+			<h1><?php echo $this->Html->link(__('CakePHP: the rapid development php framework', true), 'http://cakephp.org');?></h1>
+		</div>
+		<div id="content">
+
+			<?php $this->Session->flash();?>
+
+			<?php echo $content_for_layout;?>
+
+		</div>
+		<div id="footer">
+			<?php echo $this->Html->link(
+							$this->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>
+</body>
+</html>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/layouts/email/html/default.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/layouts/email/html/default.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/layouts/email/html/default.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,32 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.layouts.email.html
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<!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

Added: trunk/src/Web/cake/tests/test_app/views/layouts/email/html/thin.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/layouts/email/html/thin.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/layouts/email/html/thin.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,32 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.layouts.email.html
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<!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 CakePHP Framework</p>
+</body>
+</html>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/layouts/email/text/default.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/layouts/email/text/default.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/layouts/email/text/default.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,23 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.layouts.email.text
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+
+<?php echo $content_for_layout;?>
+
+This email was sent using the CakePHP Framework, http://cakephp.org.
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/layouts/flash.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/layouts/flash.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/layouts/flash.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,38 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.layouts
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<!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>
+<title><?php echo $page_title?></title>
+<?php echo $this->Html->charset(); ?>
+
+<?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

Added: trunk/src/Web/cake/tests/test_app/views/layouts/js/default.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/layouts/js/default.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/layouts/js/default.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -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

Added: trunk/src/Web/cake/tests/test_app/views/layouts/multi_cache.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/layouts/multi_cache.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/layouts/multi_cache.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,40 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.layouts
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<p>This is regular text</p>
+<cake:nocache>
+	<p>A. Layout Before Content</p>
+	<?php $this->log('1. layout before content') ?>
+</cake:nocache>
+<cake:nocache><?php echo $this->element('nocache/plain'); ?></cake:nocache>
+<cake:nocache>
+	<p>C. Layout After Test Element But Before Content</p>
+	<?php $this->log('3. layout after test element but before content') ?>
+</cake:nocache>
+<?php echo $content_for_layout; ?>
+<cake:nocache>
+	<p>E. Layout After Content</p>
+	<?php $this->log('5. layout after content') ?>
+</cake:nocache>
+<p>Additional regular text.</p>
+<?php //echo $this->element('nocache/contains_nocache'); stub?>
+<cake:nocache>
+	<p>G. Layout After Content And After Element With No Cache Tags</p>
+	<?php $this->log('7. layout after content and after element with no cache tags') ?>
+</cake:nocache>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/layouts/rss/default.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/layouts/rss/default.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/layouts/rss/default.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -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

Added: trunk/src/Web/cake/tests/test_app/views/layouts/xml/default.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/layouts/xml/default.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/layouts/xml/default.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,2 @@
+<?php echo $xml->header(); ?>
+<?php echo $content_for_layout; ?>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/pages/empty
===================================================================
Added: trunk/src/Web/cake/tests/test_app/views/pages/extract.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/pages/extract.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/pages/extract.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,26 @@
+<?php
+$count = 10;
+$messages = array('count' => 10);
+
+// Plural
+__n('You have %d new message.', 'You have %d new messages.', $count);
+__n('You deleted %d message.', 'You deleted %d messages.', $messages['count']);
+
+// Domain Plural
+__dn('domain', 'You have %d new message (domain).', 'You have %d new messages (domain).', '10');
+__dn('domain', 'You deleted %d message (domain).', 'You deleted %d messages (domain).', $messages['count']);
+
+// Duplicated Message
+__('Editing this Page');
+
+// Multiline with comments
+__('Hot features!'
+  . "\n - No Configuration:"				// Comments will be stripped
+		. ' Set-up the database and let the magic begin'
+	. "\n - Extremely Simple:"				// Comments will be stripped
+		. ' Just look at the name...It\'s Cake'
+	. "\n - Active, Friendly Community:"	// Comments will be stripped
+		. ' Join us #cakephp on IRC. We\'d love to help you get started');
+
+// This throws an error and is not parsed
+__('Found ' . $count . ' new messages');
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/pages/home.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/pages/home.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/pages/home.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,85 @@
+<iframe src="http://cakephp.org/bake-banner" width="830" height="160" style="overflow:hidden; border:none;">
+	<p>For updates and important announcements, visit http://cakefest.org</p>
+</iframe>
+<h2>Sweet, "Test App" got Baked by CakePHP!</h2>
+
+<?php
+if (Configure::read() > 0):
+	Debugger::checkSecurityKeys();
+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">';
+				printf(__('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)):
+	if (!class_exists('ConnectionManager')) {
+		require LIBS . 'model' . DS . 'connection_manager.php';
+	}
+	$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
+	printf(__('To change the content of this page, edit: %s
+		To change its layout, edit: %s
+		You can also add some CSS styles for your pages at: %s', true),
+		APP . 'views' . DS . 'pages' . DS . 'home.ctp.<br />',  APP . 'views' . DS . 'layouts' . DS . 'default.ctp.<br />', APP . 'webroot' . DS . 'css');
+?>
+</p>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/posts/cache_empty_sections.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/posts/cache_empty_sections.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/posts/cache_empty_sections.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,2 @@
+View Content
+	<cake:nocache><?php $y = 1; ?></cake:nocache>

Added: trunk/src/Web/cake/tests/test_app/views/posts/cache_form.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/posts/cache_form.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/posts/cache_form.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,14 @@
+<div class="users form">
+<cake:nocache>
+	<?php echo $form->create('User');?>
+		<fieldset>
+	 		<legend><?php __('Add User');?></legend>
+		<?php
+			echo $form->input('username');
+			echo $form->input('email');
+			echo $form->input('password');
+		?>
+		</fieldset>
+	<?php echo $form->end('Submit');?>
+</cake:nocache>
+</div>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/posts/helper_overwrite.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/posts/helper_overwrite.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/posts/helper_overwrite.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,4 @@
+<?php 
+echo $html;
+echo $this->Html->link('Test link', '#');
+?>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/posts/index.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/posts/index.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/posts/index.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+posts index
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/posts/multiple_nocache.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/posts/multiple_nocache.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/posts/multiple_nocache.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,15 @@
+--view start--
+<cake:nocache>
+	<?php echo $batman ?>
+</cake:nocache>
+
+this view has 3 nocache blocks
+
+<cake:nocache>
+	<?php echo $spiderman; ?>
+</cake:nocache>
+
+<cake:nocache>
+	<?php echo 'some string'; ?>
+</cake:nocache>
+--view end--
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/posts/nocache_multiple_element.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/posts/nocache_multiple_element.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/posts/nocache_multiple_element.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,9 @@
+<cake:nocache>
+	<?php echo $foo; ?>
+</cake:nocache>
+
+<cake:nocache>
+	<?php echo $bar; ?>
+</cake:nocache>
+
+<?php echo $this->element('nocache/sub1'); ?>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/posts/scaffold.edit.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/posts/scaffold.edit.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/posts/scaffold.edit.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+test_app posts add/edit scaffold view
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/posts/sequencial_nocache.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/posts/sequencial_nocache.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/posts/sequencial_nocache.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,24 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.pages
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<h1>Content</h1>
+<cake:nocache>
+	<p>D. In View File</p>
+	<?php $this->log('4. in view file') ?>
+</cake:nocache>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/posts/test_nocache_tags.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/posts/test_nocache_tags.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/posts/test_nocache_tags.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,144 @@
+<?php
+/**
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.libs.view.templates.pages
+ * @since         CakePHP(tm) v 0.10.0.1076
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+?>
+<p>
+	<cake:nocache>
+	<span class="notice">
+		<?php
+			__('Your tmp directory is ');
+			if (is_writable(TMP)):
+				__('writable.');
+			else:
+				__('NOT writable.');
+			endif;
+		?>
+	</span>
+	</cake:nocache>
+</p>
+<p>
+	<span class="notice">
+		<?php
+			__('Your cache is ');
+			if (Cache::isInitialized()):
+				__('set up and initialized properly.');
+				$settings = Cache::settings();
+				echo '<p>' . $settings['engine'];
+				__(' is being used to cache, to change this edit config/core.php ');
+				echo '</p>';
+
+				echo 'Settings: <ul>';
+				foreach ($settings as $name => $value):
+					echo '<li>' . $name . ': ' . $value . '</li>';
+				endforeach;
+				echo '</ul>';
+
+			else:
+				__('NOT working.');
+				echo '<br />';
+				if (is_writable(TMP)):
+					__('Edit: config/core.php to insure you have the newset version of this file and the variable $cakeCache set properly');
+				endif;
+			endif;
+		?>
+	</span>
+</p>
+<p>
+	<span class="notice">
+		<?php
+			__('Your database configuration file is ');
+			$filePresent = null;
+			if (file_exists(CONFIGS.'database.php')):
+				__('present.');
+				$filePresent = true;
+			else:
+				__('NOT present.');
+				echo '<br/>';
+				__('Rename config/database.php.default to config/database.php');
+			endif;
+		?>
+	</span>
+</p>
+<?php
+if (!empty($filePresent)):
+	if (!class_exists('ConnectionManager')) {
+		require LIBS . 'model' . DS . 'connection_manager.php';
+	}
+	$db = ConnectionManager::getInstance();
+ 	$connected = $db->getDataSource('default');
+?>
+<p>
+	<span class="notice">
+		<?php
+			__('Cake');
+			if ($connected->isConnected()):
+		 		__(' is able to ');
+			else:
+				__(' is NOT able to ');
+			endif;
+			__('connect to the database.');
+		?>
+	</span>
+</p>
+<?php endif; ?>
+<h2><?php printf(__('Release Notes for CakePHP %s.', true), Configure::version()); ?></h2>
+<a href="https://trac.cakephp.org/wiki/notes/1.2.x.x"><?php __('Read the release notes and get the latest version'); ?> </a>
+<h2><?php __('Editing this Page'); ?></h2>
+<p>
+<?php __('To change the content of this page, create: /app/views/pages/home.ctp.'); ?><br />
+<?php __('To change its layout, create: /app/views/layouts/default.ctp.'); ?><br />
+<a href="http://manual.cakephp.org/"><?php __('See the views section of the manual for more info.'); ?> </a><br />
+<?php __('You can also add some CSS styles for your pages at: app/webroot/css/.'); ?>
+</p>
+<h2><?php __('Getting Started'); ?></h2>
+<p>
+<a href="http://manual.cakephp.org/appendix/blog_tutorial"><?php __('The 15 min Blog Tutorial'); ?></a><br />
+<a href="http://www-128.ibm.com/developerworks/edu/os-dw-os-php-cake1.html"><?php __('Cook up Web sites fast with CakePHP'); ?></a><br />
+<a href="http://www-128.ibm.com/developerworks/edu/os-dw-os-php-wiki1.html"><?php __('Create an interactive production wiki using PHP'); ?></a>
+</p>
+<h2><?php __('More about Cake'); ?></h2>
+<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>
+<ul>
+	<li><a href="http://cakefoundation.org/"><?php __('Cake Software Foundation'); ?> </a>
+	<ul><li><?php __('Promoting development related to CakePHP'); ?></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://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 __('CakeSchwag'); ?> </a>
+	<ul><li><?php __('Get your own CakePHP gear - Doughnate to Cake'); ?></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://manual.cakephp.org"><?php __('CakePHP Manual'); ?> </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 __('Docblock Your Best Friend'); ?></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="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://groups-beta.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>
+</ul>
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/scaffolds/empty
===================================================================
Added: trunk/src/Web/cake/tests/test_app/views/tests_apps/index.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/tests_apps/index.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/tests_apps/index.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+This is the TestsAppsController index view
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/themed/test_theme/elements/test_element.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/themed/test_theme/elements/test_element.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/themed/test_theme/elements/test_element.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+Hi, I'm the test element.
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/themed/test_theme/layouts/default.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/themed/test_theme/layouts/default.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/themed/test_theme/layouts/default.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+default test_theme layout
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/themed/test_theme/plugins/test_plugin/layouts/plugin_default.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/themed/test_theme/plugins/test_plugin/layouts/plugin_default.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/themed/test_theme/plugins/test_plugin/layouts/plugin_default.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+test_plugin test_plugin_theme default layout
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/themed/test_theme/plugins/test_plugin/tests/index.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/themed/test_theme/plugins/test_plugin/tests/index.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/themed/test_theme/plugins/test_plugin/tests/index.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+test plugin index theme view
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/themed/test_theme/posts/index.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/themed/test_theme/posts/index.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/themed/test_theme/posts/index.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+posts index themed view
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/themed/test_theme/posts/scaffold.index.ctp
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/themed/test_theme/posts/scaffold.index.ctp	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/themed/test_theme/posts/scaffold.index.ctp	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+I'm a themed scaffold file.
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/css/test_asset.css
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/css/test_asset.css	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/css/test_asset.css	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+this is the test asset css file
\ No newline at end of file


Property changes on: trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/css/test_asset.css
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Added: trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/css/theme_webroot.css
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/css/theme_webroot.css	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/css/theme_webroot.css	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+theme webroot css file
\ No newline at end of file


Property changes on: trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/css/theme_webroot.css
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Added: trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/flash/theme_test.swf
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/flash/theme_test.swf	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/flash/theme_test.swf	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+this is just a test to load swf file from the theme.
\ No newline at end of file

Added: trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/img/cake.power.gif
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/img/cake.power.gif
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/img/test.jpg
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/img/test.jpg
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/js/one/theme_one.js
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/js/one/theme_one.js	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/js/one/theme_one.js	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+nested theme js file
\ No newline at end of file


Property changes on: trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/js/one/theme_one.js
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Added: trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/js/theme.js
===================================================================
--- trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/js/theme.js	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/js/theme.js	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+root theme js file
\ No newline at end of file


Property changes on: trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/js/theme.js
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Added: trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/pdfs/theme_test.pdf
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/views/themed/test_theme/webroot/pdfs/theme_test.pdf
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/webroot/theme/test_theme/css/theme_webroot.css
===================================================================
--- trunk/src/Web/cake/tests/test_app/webroot/theme/test_theme/css/theme_webroot.css	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/webroot/theme/test_theme/css/theme_webroot.css	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+override the theme webroot css file
\ No newline at end of file


Property changes on: trunk/src/Web/cake/tests/test_app/webroot/theme/test_theme/css/theme_webroot.css
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Added: trunk/src/Web/cake/tests/test_app/webroot/theme/test_theme/css/webroot_test.css
===================================================================
--- trunk/src/Web/cake/tests/test_app/webroot/theme/test_theme/css/webroot_test.css	                        (rev 0)
+++ trunk/src/Web/cake/tests/test_app/webroot/theme/test_theme/css/webroot_test.css	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1 @@
+this is the webroot test asset css file
\ No newline at end of file


Property changes on: trunk/src/Web/cake/tests/test_app/webroot/theme/test_theme/css/webroot_test.css
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Added: trunk/src/Web/cake/tests/test_app/webroot/theme/test_theme/img/cake.power.gif
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/webroot/theme/test_theme/img/cake.power.gif
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/cake/tests/test_app/webroot/theme/test_theme/img/test.jpg
===================================================================
(Binary files differ)


Property changes on: trunk/src/Web/cake/tests/test_app/webroot/theme/test_theme/img/test.jpg
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/Web/index.php
===================================================================
--- trunk/src/Web/index.php	                        (rev 0)
+++ trunk/src/Web/index.php	2012-03-31 12:37:43 UTC (rev 78)
@@ -0,0 +1,55 @@
+<?php
+/**
+ * Requests collector.
+ *
+ *  This file collects requests if:
+ *	- no mod_rewrite is avilable or .htaccess files are not supported
+ *  - requires App.baseUrl to be uncommented in app/config/core.php
+ *	- app/webroot is not set as a document root.
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @since         CakePHP(tm) v 0.2.9
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+/**
+ *  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" directory is located
+ * outside of the distributed structure.
+ * Full path to the directory containing "cake". Do not add trailing directory separator
+ */
+	if (!defined('CAKE_CORE_INCLUDE_PATH')) {
+		define('CAKE_CORE_INCLUDE_PATH', ROOT);
+	}
+
+/**
+ * Set the include path or define app and core path
+ */
+	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 APP_DIR . DS . WEBROOT_DIR . DS . 'index.php';

Added: trunk/src/Web/plugins/empty
===================================================================
Added: trunk/src/Web/vendors/shells/tasks/empty
===================================================================
Added: trunk/src/Web/vendors/shells/templates/empty
===================================================================



Smartupload-svn メーリングリストの案内
Back to archive index