diff --git a/.gitignore b/.gitignore index cfc5c7a4b..9edf61016 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ -app/tmp -vendors \ No newline at end of file +/app/config +/app/tmp +/plugins +/vendors \ No newline at end of file diff --git a/README b/README index 5fe134356..b92cb46e9 100644 --- a/README +++ b/README @@ -1,7 +1,7 @@ CakePHP is a rapid development framework for PHP which uses commonly known design patterns like Active Record, Association Data Mapping, Front Controller and MVC. Our primary goal is to provide a structured framework that enables PHP users at all levels to rapidly develop robust web applications, without any loss to flexibility. The Cake Software Foundation - promoting development related to CakePHP -http://www.cakefoundation.org/ +http://cakefoundation.org/ CakePHP - the rapid development PHP framework http://www.cakephp.org diff --git a/app/config/acl.ini.php b/app/config/acl.ini.php index a9868e685..84fce6d91 100644 --- a/app/config/acl.ini.php +++ b/app/config/acl.ini.php @@ -6,22 +6,18 @@ ; * ; * PHP versions 4 and 5 ; * -; * CakePHP(tm) : Rapid Development Framework http://www.cakephp.org/ -; * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) +; * CakePHP(tm) : Rapid Development Framework http://cakephp.org +; * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org) ; * ; * Licensed under The MIT License ; * Redistributions of files must retain the above copyright notice. ; * -; * @filesource -; * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) -; * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project +; * @copyright Copyright 2005-2010, 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 -; * @version $Revision$ -; * @modifiedby $LastChangedBy$ -; * @lastmodified $Date$ -; * @license http://www.opensource.org/licenses/mit-license.php The MIT License +; * @license MIT License (http://www.opensource.org/licenses/mit-license.php) ; */ ; acl.ini.php - Cake ACL Configuration diff --git a/app/config/bootstrap.php b/app/config/bootstrap.php index b817172c7..b539b323b 100644 --- a/app/config/bootstrap.php +++ b/app/config/bootstrap.php @@ -1,44 +1,51 @@ 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/') + * )); * */ + /** - * 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) + * As of 1.3, additional rules for the inflector are added below * - * $modelPaths = array('full path to models', 'second full path to models', 'etc...'); - * $viewPaths = array('this path to views', 'second full path to views', 'etc...'); - * $controllerPaths = array('this path to controllers', 'second full path to controllers', 'etc...'); + * Inflector::rules('singular', array('rules' => array(), 'irregular' => array(), 'uninflected' => array())); + * Inflector::rules('plural', array('rules' => array(), 'irregular' => array(), 'uninflected' => array())); * */ -//EOF ?> \ No newline at end of file diff --git a/app/config/core.php b/app/config/core.php index fbd02e74a..e9f4f0290 100644 --- a/app/config/core.php +++ b/app/config/core.php @@ -1,5 +1,4 @@ admin_index() and /admin/controller/index - * 'superuser' -> superuser_index() and /superuser/controller/index */ - //Configure::write('Routing.admin', 'admin'); + //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, + * public $cacheAction inside your controllers to define caching settings. + * You can either set it controller-wide by setting public $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: * @@ -96,42 +119,64 @@ * To define a custom session handler, save it at /app/config/.php. * Set the value of 'Session.save' to to utilize it in CakePHP. * - * To use database sessions, execute the SQL file found at /app/config/sql/sessions.sql. + * To use database sessions, run the app/config/schema/sessions.php schema using + * the cake shell command: cake schema run 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. */ Configure::write('Session.cookie', 'CAKEPHP'); + /** * Session time out time (in seconds). * Actual value depends on 'Security.level' setting. */ Configure::write('Session.timeout', '120'); + /** * If set to false, sessions are not automatically started. */ Configure::write('Session.start', true); + /** * When set to false, HTTP_USER_AGENT will not be checked * in the session */ Configure::write('Session.checkAgent', true); + /** * The level of CakePHP security. The session timeout time defined * in 'Session.timeout' is multiplied according to the settings here. @@ -144,11 +189,27 @@ * CakePHP session IDs are also regenerated between requests if * 'Security.level' is set to 'high'. */ - Configure::write('Security.level', '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. @@ -157,6 +218,7 @@ * 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. @@ -164,17 +226,20 @@ * 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 diff --git a/app/config/database.php.default b/app/config/database.php.default index 5c20804d4..cf9a6327c 100644 --- a/app/config/database.php.default +++ b/app/config/database.php.default @@ -1,5 +1,4 @@ 'mysql', 'persistent' => false, 'host' => 'localhost', @@ -88,7 +83,7 @@ class DATABASE_CONFIG { 'prefix' => '', ); - var $test = array( + public $test = array( 'driver' => 'mysql', 'persistent' => false, 'host' => 'localhost', diff --git a/app/config/inflections.php b/app/config/inflections.php deleted file mode 100644 index ed8c08b3d..000000000 --- a/app/config/inflections.php +++ /dev/null @@ -1,70 +0,0 @@ - value array of regex used to match words. - * If key matches then the value is returned. - * - * $pluralRules = array('/(s)tatus$/i' => '\1\2tatuses', '/^(ox)$/i' => '\1\2en', '/([m|l])ouse$/i' => '\1ice'); - */ - $pluralRules = array(); -/** - * This is a key only array of plural words that should not be inflected. - * Notice the last comma - * - * $uninflectedPlural = array('.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox'); - */ - $uninflectedPlural = array(); -/** - * This is a key => value array of plural irregular words. - * If key matches then the value is returned. - * - * $irregularPlural = array('atlas' => 'atlases', 'beef' => 'beefs', 'brother' => 'brothers') - */ - $irregularPlural = array(); -/** - * This is a key => value array of regex used to match words. - * If key matches then the value is returned. - * - * $singularRules = array('/(s)tatuses$/i' => '\1\2tatus', '/(matr)ices$/i' =>'\1ix','/(vert|ind)ices$/i') - */ - $singularRules = array(); -/** - * This is a key only array of singular words that should not be inflected. - * You should not have to change this value below if you do change it use same format - * as the $uninflectedPlural above. - */ - $uninflectedSingular = $uninflectedPlural; -/** - * This is a key => value array of singular irregular words. - * Most of the time this will be a reverse of the above $irregularPlural array - * You should not have to change this value below if you do change it use same format - * - * $irregularSingular = array('atlases' => 'atlas', 'beefs' => 'beef', 'brothers' => 'brother') - */ - $irregularSingular = array_flip($irregularPlural); -?> \ No newline at end of file diff --git a/app/config/routes.php b/app/config/routes.php index b14e435c3..40738cb3f 100644 --- a/app/config/routes.php +++ b/app/config/routes.php @@ -1,5 +1,4 @@ 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), @@ -53,7 +48,7 @@ function after($event = array()) { 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)) ); - var $aros = array( + public $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), @@ -64,7 +59,7 @@ function after($event = array()) { 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)) ); - var $aros_acos = array( + public $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), diff --git a/cake/console/libs/templates/skel/config/sql/i18n.php b/app/config/schema/i18n.php similarity index 70% rename from cake/console/libs/templates/skel/config/sql/i18n.php rename to app/config/schema/i18n.php index b8158dd5a..dcd64bdd4 100644 --- a/cake/console/libs/templates/skel/config/sql/i18n.php +++ b/app/config/schema/i18n.php @@ -1,5 +1,4 @@ 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'), diff --git a/cake/console/libs/templates/skel/config/sql/sessions.php b/app/config/schema/sessions.php similarity index 60% rename from cake/console/libs/templates/skel/config/sql/sessions.php rename to app/config/schema/sessions.php index 197366108..dd7880fa8 100644 --- a/cake/console/libs/templates/skel/config/sql/sessions.php +++ b/app/config/schema/sessions.php @@ -1,5 +1,4 @@ array('type'=>'string', 'null' => false, 'key' => 'primary'), 'data' => array('type'=>'text', 'null' => true, 'default' => NULL), 'expires' => array('type'=>'integer', 'null' => true, 'default' => NULL), diff --git a/app/config/sql/db_acl.sql b/app/config/sql/db_acl.sql deleted file mode 100644 index 6fc01b8ea..000000000 --- a/app/config/sql/db_acl.sql +++ /dev/null @@ -1,40 +0,0 @@ -# $Id$ -# -# Copyright 2005-2008, Cake Software Foundation, Inc. -# -# Licensed under The MIT License -# Redistributions of files must retain the above copyright notice. -# http://www.opensource.org/licenses/mit-license.php The MIT License - -CREATE TABLE acos ( - id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, - parent_id INTEGER(10) DEFAULT NULL, - model VARCHAR(255) DEFAULT '', - foreign_key INTEGER(10) UNSIGNED DEFAULT NULL, - alias VARCHAR(255) DEFAULT '', - lft INTEGER(10) DEFAULT NULL, - rght INTEGER(10) DEFAULT NULL, - PRIMARY KEY (id) -); - -CREATE TABLE aros_acos ( - id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, - aro_id INTEGER(10) UNSIGNED NOT NULL, - aco_id INTEGER(10) UNSIGNED NOT NULL, - _create CHAR(2) NOT NULL DEFAULT 0, - _read CHAR(2) NOT NULL DEFAULT 0, - _update CHAR(2) NOT NULL DEFAULT 0, - _delete CHAR(2) NOT NULL DEFAULT 0, - PRIMARY KEY(id) -); - -CREATE TABLE aros ( - id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, - parent_id INTEGER(10) DEFAULT NULL, - model VARCHAR(255) DEFAULT '', - foreign_key INTEGER(10) UNSIGNED DEFAULT NULL, - alias VARCHAR(255) DEFAULT '', - lft INTEGER(10) DEFAULT NULL, - rght INTEGER(10) DEFAULT NULL, - PRIMARY KEY (id) -); diff --git a/app/config/sql/i18n.sql b/app/config/sql/i18n.sql deleted file mode 100644 index db1daea4a..000000000 --- a/app/config/sql/i18n.sql +++ /dev/null @@ -1,26 +0,0 @@ -# $Id$ -# -# Copyright 2005-2008, Cake Software Foundation, Inc. -# -# Licensed under The MIT License -# Redistributions of files must retain the above copyright notice. -# http://www.opensource.org/licenses/mit-license.php The MIT License - -CREATE TABLE i18n ( - id int(10) NOT NULL auto_increment, - locale varchar(6) NOT NULL, - model varchar(255) NOT NULL, - foreign_key int(10) NOT NULL, - field varchar(255) NOT NULL, - content mediumtext, - PRIMARY KEY (id), -# UNIQUE INDEX I18N_LOCALE_FIELD(locale, model, foreign_key, field), -# INDEX I18N_LOCALE_ROW(locale, model, foreign_key), -# INDEX I18N_LOCALE_MODEL(locale, model), -# INDEX I18N_FIELD(model, foreign_key, field), -# INDEX I18N_ROW(model, foreign_key), - INDEX locale (locale), - INDEX model (model), - INDEX row_id (foreign_key), - INDEX field (field) -); diff --git a/app/index.php b/app/index.php index 5724a1c4b..eaf9e2e48 100644 --- a/app/index.php +++ b/app/index.php @@ -1,24 +1,19 @@ \ No newline at end of file diff --git a/cake/console/libs/templates/skel/controllers/components/empty b/app/libs/empty similarity index 100% rename from cake/console/libs/templates/skel/controllers/components/empty rename to app/libs/empty diff --git a/app/tmp/cache/models/empty b/app/tmp/cache/models/empty old mode 100644 new mode 100755 diff --git a/app/tmp/cache/views/empty b/app/tmp/cache/views/empty old mode 100644 new mode 100755 diff --git a/app/tmp/logs/empty b/app/tmp/logs/empty old mode 100644 new mode 100755 diff --git a/app/tmp/sessions/empty b/app/tmp/sessions/empty old mode 100644 new mode 100755 diff --git a/app/tmp/tests/empty b/app/tmp/tests/empty old mode 100644 new mode 100755 diff --git a/cake/console/libs/templates/skel/locale/eng/LC_MESSAGES/empty b/app/views/elements/email/html/empty similarity index 100% rename from cake/console/libs/templates/skel/locale/eng/LC_MESSAGES/empty rename to app/views/elements/email/html/empty diff --git a/cake/console/libs/templates/skel/models/behaviors/empty b/app/views/elements/email/text/empty similarity index 100% rename from cake/console/libs/templates/skel/models/behaviors/empty rename to app/views/elements/email/text/empty diff --git a/cake/console/libs/templates/skel/models/datasources/empty b/app/views/layouts/email/html/empty similarity index 100% rename from cake/console/libs/templates/skel/models/datasources/empty rename to app/views/layouts/email/html/empty diff --git a/cake/console/libs/templates/skel/plugins/empty b/app/views/layouts/email/text/empty similarity index 100% rename from cake/console/libs/templates/skel/plugins/empty rename to app/views/layouts/email/text/empty diff --git a/cake/console/libs/templates/skel/tests/cases/behaviors/empty b/app/views/pages/empty similarity index 100% rename from cake/console/libs/templates/skel/tests/cases/behaviors/empty rename to app/views/pages/empty diff --git a/app/webroot/css.php b/app/webroot/css.php index 969dd0e27..1436c5f07 100644 --- a/app/webroot/css.php +++ b/app/webroot/css.php @@ -1,28 +1,21 @@ dispatch($url); + $Dispatcher->dispatch(); } if (Configure::read() > 0) { - echo ""; + echo ""; } ?> \ No newline at end of file diff --git a/cake/console/libs/templates/skel/tests/cases/controllers/empty b/app/webroot/js/empty similarity index 100% rename from cake/console/libs/templates/skel/tests/cases/controllers/empty rename to app/webroot/js/empty diff --git a/app/webroot/js/vendors.php b/app/webroot/js/vendors.php deleted file mode 100644 index 5fda0b7b4..000000000 --- a/app/webroot/js/vendors.php +++ /dev/null @@ -1,42 +0,0 @@ - \ No newline at end of file diff --git a/app/webroot/test.php b/app/webroot/test.php index a57d15747..d81e2aa50 100644 --- a/app/webroot/test.php +++ b/app/webroot/test.php @@ -1,31 +1,23 @@ - * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * Copyright 2005-2010, 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-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org) * @link https://trac.cakephp.org/wiki/Developement/TestSuite CakePHP(tm) Tests * @package cake * @subpackage cake.cake.tests.libs * @since CakePHP(tm) v 1.2.0.4433 - * @version $Revision$ - * @modifiedby $LastChangedBy$ - * @lastmodified $Date$ * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License */ set_time_limit(0); -ini_set('memory_limit','128M'); ini_set('display_errors', 1); /** * Use the DS to separate the directories in other defines @@ -85,96 +77,20 @@ trigger_error("CakePHP core could not be found. Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php. It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory.", E_USER_ERROR); } -$corePath = Configure::corePaths('cake'); +$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); } -require_once CAKE_TESTS_LIB . 'test_manager.php'; - if (Configure::read('debug') < 1) { die(__('Debug setting does not allow access to this url.', true)); } -if (!isset($_SERVER['SERVER_NAME'])) { - $_SERVER['SERVER_NAME'] = ''; -} -if (empty( $_GET['output'])) { - $_GET['output'] = 'html'; -} -/** - * - * Used to determine output to display - */ -define('CAKE_TEST_OUTPUT_HTML', 1); -define('CAKE_TEST_OUTPUT_TEXT', 2); +require_once CAKE_TESTS_LIB . 'cake_test_suite_dispatcher.php'; -if (isset($_GET['output']) && $_GET['output'] == 'html') { - define('CAKE_TEST_OUTPUT', CAKE_TEST_OUTPUT_HTML); -} else { - Debugger::output('txt'); - define('CAKE_TEST_OUTPUT', CAKE_TEST_OUTPUT_TEXT); -} - -if (!App::import('Vendor', 'simpletest' . DS . 'reporter')) { - CakePHPTestHeader(); - include CAKE_TESTS_LIB . 'simpletest.php'; - CakePHPTestSuiteFooter(); - exit(); -} +$Dispatcher = new CakeTestSuiteDispatcher(); +$Dispatcher->dispatch(); -$analyzeCodeCoverage = false; -if (isset($_GET['code_coverage'])) { - $analyzeCodeCoverage = true; - require_once CAKE_TESTS_LIB . 'code_coverage_manager.php'; - if (!extension_loaded('xdebug')) { - CakePHPTestHeader(); - include CAKE_TESTS_LIB . 'xdebug.php'; - CakePHPTestSuiteFooter(); - exit(); - } -} - -CakePHPTestHeader(); -CakePHPTestSuiteHeader(); -define('RUN_TEST_LINK', $_SERVER['PHP_SELF']); - -if (isset($_GET['group'])) { - if ('all' == $_GET['group']) { - TestManager::runAllTests(CakeTestsGetReporter()); - } else { - if ($analyzeCodeCoverage) { - CodeCoverageManager::start($_GET['group'], CakeTestsGetReporter()); - } - TestManager::runGroupTest(ucfirst($_GET['group']), CakeTestsGetReporter()); - if ($analyzeCodeCoverage) { - CodeCoverageManager::report(); - } - } - - CakePHPTestRunMore(); - CakePHPTestAnalyzeCodeCoverage(); -} elseif (isset($_GET['case'])) { - if ($analyzeCodeCoverage) { - CodeCoverageManager::start($_GET['case'], CakeTestsGetReporter()); - } - - TestManager::runTestCase($_GET['case'], CakeTestsGetReporter()); - - if ($analyzeCodeCoverage) { - CodeCoverageManager::report(); - } - - CakePHPTestRunMore(); - CakePHPTestAnalyzeCodeCoverage(); -} elseif (isset($_GET['show']) && $_GET['show'] == 'cases') { - CakePHPTestCaseList(); -} else { - CakePHPTestGroupTestList(); -} -CakePHPTestSuiteFooter(); -$output = ob_get_clean(); -echo $output; ?> \ No newline at end of file diff --git a/cake/LICENSE.txt b/cake/LICENSE.txt index e54a5572b..bf6f82dd4 100644 --- a/cake/LICENSE.txt +++ b/cake/LICENSE.txt @@ -1,7 +1,7 @@ The MIT License -CakePHP(tm) : The Rapid Development PHP Framework (http://www.cakephp.org) -Copyright 2005-2007, Cake Software Foundation, Inc. +CakePHP(tm) : The Rapid Development PHP Framework (http://cakephp.org) +Copyright 2005-2010, 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"), diff --git a/cake/VERSION.txt b/cake/VERSION.txt index 3a1f10eae..4276a6cf6 100644 --- a/cake/VERSION.txt +++ b/cake/VERSION.txt @@ -1 +1,22 @@ -1.2.5 \ No newline at end of file +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +--------------------------------------------------------------------------------------------+ // +// CakePHP Version +// +// Holds a static string representing the current version of CakePHP +// +// CakePHP(tm) : Rapid Development Framework (http://cakephp.org) +// Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org) +// +// Licensed under The MIT License +// Redistributions of files must retain the above copyright notice. +// +// @copyright Copyright 2005-2010, 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.0 + diff --git a/cake/basics.php b/cake/basics.php index 5e387c553..e91e48e90 100644 --- a/cake/basics.php +++ b/cake/basics.php @@ -1,5 +1,4 @@ 0) { @@ -115,18 +106,8 @@ function debug($var = false, $showHtml = false, $showFrom = true) { echo $var . "\n\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. * @@ -157,149 +138,63 @@ function sortByKey(&$array, $sortby, $order = 'asc', $type = SORT_NUMERIC) { return $out; } } -if (!function_exists('array_combine')) { -/** - * Combines given identical arrays by using the first array's values as keys, - * and the second one's values as values. (Implemented for backwards compatibility with PHP4) - * - * @param array $a1 Array to use for keys - * @param array $a2 Array to use for values - * @return mixed Outputs either combined array or false. - */ - function array_combine($a1, $a2) { - $a1 = array_values($a1); - $a2 = array_values($a2); - $c1 = count($a1); - $c2 = count($a2); - - if ($c1 != $c2) { - return false; - } - if ($c1 <= 0) { - return false; - } - $output = array(); - for ($i = 0; $i < $c1; $i++) { - $output[$a1[$i]] = $a2[$i]; - } - return $output; - } -} /** * Convenience method for htmlspecialchars. * * @param string $text Text to wrap through htmlspecialchars * @param string $charset Character set to use when escaping. Defaults to config value in 'App.encoding' or 'UTF-8' * @return string Wrapped text - * @link http://book.cakephp.org/view/703/h + * @link http://book.cakephp.org/view/1132/h */ function h($text, $charset = null) { if (is_array($text)) { return array_map('h', $text); } - if (empty($charset)) { - $charset = Configure::read('App.encoding'); + + static $defaultCharset = false; + if ($defaultCharset === false) { + $defaultCharset = Configure::read('App.encoding'); + if ($defaultCharset === null) { + $defaultCharset = 'UTF-8'; + } } - if (empty($charset)) { - $charset = 'UTF-8'; + if ($charset) { + return htmlspecialchars($text, ENT_QUOTES, $charset); + } else { + return htmlspecialchars($text, ENT_QUOTES, $defaultCharset); } - return htmlspecialchars($text, ENT_QUOTES, $charset); } + /** - * Returns an array of all the given parameters. - * - * Example: - * - * `a('a', 'b')` + * Splits a dot syntax plugin name into its plugin and classname. + * If $name does not have a dot, then index 0 will be null. * - * Would return: + * Commonly used like `list($plugin, $name) = pluginSplit($name);` * - * `array('a', 'b')` - * - * @return array Array of given parameters - * @link http://book.cakephp.org/view/694/a + * @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 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/695/aa - */ - function aa() { - $args = func_get_args(); - $argc = count($args); - for ($i = 0; $i < $argc; $i++) { - if ($i + 1 < $argc) { - $a[$args[$i]] = $args[$i + 1]; - } else { - $a[$args[$i]] = null; + function pluginSplit($name, $dotAppend = false, $plugin = null) { + if (strpos($name, '.') !== false) { + $parts = explode('.', $name, 2); + if ($dotAppend) { + $parts[0] .= '.'; } - $i++; + return $parts; } - return $a; - } -/** - * Convenience method for echo(). - * - * @param string $text String to echo - * @link http://book.cakephp.org/view/700/e - */ - function e($text) { - echo $text; - } -/** - * Convenience method for strtolower(). - * - * @param string $str String to lowercase - * @return string Lowercased string - * @link http://book.cakephp.org/view/705/low - */ - function low($str) { - return strtolower($str); - } -/** - * Convenience method for strtoupper(). - * - * @param string $str String to uppercase - * @return string Uppercased string - * @link http://book.cakephp.org/view/710/up - */ - function up($str) { - return strtoupper($str); - } -/** - * Convenience method for str_replace(). - * - * @param string $search String to be replaced - * @param string $replace String to insert - * @param string $subject String to search - * @return string Replaced string - * @link http://book.cakephp.org/view/708/r - */ - function r($search, $replace, $subject) { - return str_replace($search, $replace, $subject); + return array($plugin, $name); } + /** * Print_r convenience function, which prints out
 tags around
  * the output of given array. Similar to debug().
  *
  * @see	debug()
  * @param array $var Variable to print out
- * @param boolean $showFrom If set to true, the method prints from where the function was called
- * @link http://book.cakephp.org/view/707/pr
+ * @link http://book.cakephp.org/view/1136/pr
  */
 	function pr($var) {
 		if (Configure::read() > 0) {
@@ -308,21 +203,7 @@ function pr($var) {
 			echo '
'; } } -/** - * Display parameters. - * - * @param mixed $p Parameter as string or array - * @return string - */ - function params($p) { - if (!is_array($p) || count($p) == 0) { - return null; - } - if (is_array($p[0]) && count($p) == 1) { - return $p[0]; - } - return $p; - } + /** * Merge a group of arrays * @@ -331,7 +212,7 @@ function params($p) { * @param array Third array * @param array Etc... * @return array All array parameters merged into one - * @link http://book.cakephp.org/view/696/am + * @link http://book.cakephp.org/view/1124/am */ function am() { $r = array(); @@ -344,6 +225,7 @@ function am() { } return $r; } + /** * Gets an environment variable from available sources, and provides emulation * for unsupported or inconsistent environment variables (i.e. DOCUMENT_ROOT on @@ -352,7 +234,7 @@ function am() { * * @param string $key Environment variable name. * @return string Environment variable setting. - * @link http://book.cakephp.org/view/701/env + * @link http://book.cakephp.org/view/1130/env */ function env($key) { if ($key == 'HTTPS') { @@ -419,34 +301,7 @@ function env($key) { } return null; } -if (!function_exists('file_put_contents')) { -/** - * Writes data into file. - * - * If file exists, it will be overwritten. If data is an array, it will be join()ed with an empty string. - * - * @param string $fileName File name. - * @param mixed $data String or array. - * @return boolean Success - */ - function file_put_contents($fileName, $data) { - if (is_array($data)) { - $data = join('', $data); - } - $res = @fopen($fileName, 'w+b'); - if ($res) { - $write = @fwrite($res, $data); - if ($write === false) { - return false; - } else { - @fclose($res); - return $write; - } - } - return false; - } -} /** * Reads/writes temporary data to cache files or session. * @@ -467,7 +322,7 @@ function cache($path, $data = null, $expires = '+1 day', $target = 'cache') { $expires = strtotime($expires, $now); } - switch (low($target)) { + switch (strtolower($target)) { case 'cache': $filename = CACHE . $path; break; @@ -498,6 +353,7 @@ function cache($path, $data = null, $expires = '+1 day', $target = 'cache') { } return $data; } + /** * Used to delete files in the cache directories, or clear contents of cache directories * @@ -524,7 +380,7 @@ function clearCache($params = null, $type = 'views', $ext = '.php') { } foreach ($files as $file) { - if (is_file($file)) { + if (is_file($file) && strrpos($file, DS . 'empty') !== strlen($file) - 6) { @unlink($file); } } @@ -545,7 +401,7 @@ function clearCache($params = null, $type = 'views', $ext = '.php') { return false; } foreach ($files as $file) { - if (is_file($file)) { + if (is_file($file) && strrpos($file, DS . 'empty') !== strlen($file) - 6) { @unlink($file); } } @@ -559,12 +415,13 @@ function clearCache($params = null, $type = 'views', $ext = '.php') { } return false; } + /** * Recursively strips slashes from all values in an array * * @param array $values Array of values to strip slashes * @return mixed What is returned from calling stripslashes - * @link http://book.cakephp.org/view/709/stripslashes_deep + * @link http://book.cakephp.org/view/1138/stripslashes_deep */ function stripslashes_deep($values) { if (is_array($values)) { @@ -576,28 +433,25 @@ function stripslashes_deep($values) { } return $values; } + /** * Returns a translated string if one is found; Otherwise, the submitted message. * * @param string $singular Text to translate * @param boolean $return Set to true to return translated string, or false to echo * @return mixed translated string if $return is false string will be echoed - * @link http://book.cakephp.org/view/693/__ + * @link http://book.cakephp.org/view/1121/__ */ - function __($singular, $return = false) { + function __($singular) { if (!$singular) { return; } if (!class_exists('I18n')) { App::import('Core', 'i18n'); } - - if ($return === false) { - echo I18n::translate($singular); - } else { - return I18n::translate($singular); - } + 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. @@ -608,20 +462,16 @@ function __($singular, $return = false) { * @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) { + function __n($singular, $plural, $count) { 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); - } + return I18n::translate($singular, $plural, null, 6, $count); } + /** * Allows you to override the current domain for a single message lookup. * @@ -630,20 +480,16 @@ function __n($singular, $plural, $count, $return = false) { * @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) { + function __d($domain, $msg) { 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); - } + 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 @@ -656,20 +502,16 @@ function __d($domain, $msg, $return = false) { * @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) { + function __dn($domain, $singular, $plural, $count) { 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); - } + 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. @@ -693,20 +535,16 @@ function __dn($domain, $singular, $plural, $count, $return = false) { * @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) { + function __dc($domain, $msg, $category) { 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); - } + 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. @@ -734,20 +572,16 @@ function __dc($domain, $msg, $category, $return = false) { * @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) { + function __dcn($domain, $singular, $plural, $count, $category) { 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); - } + 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. @@ -767,72 +601,16 @@ function __dcn($domain, $singular, $plural, $count, $category, $return = false) * @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) { + function __c($msg, $category) { 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); - } + return I18n::translate($msg, null, null, $category); } -/** - * Computes the difference of arrays using keys for comparison. - * - * @param array First array - * @param array Second array - * @return array Array with different keys - */ - if (!function_exists('array_diff_key')) { - function array_diff_key() { - $valuesDiff = array(); - $argc = func_num_args(); - if ($argc < 2) { - return false; - } - - $args = func_get_args(); - foreach ($args as $param) { - if (!is_array($param)) { - return false; - } - } - - foreach ($args[0] as $valueKey => $valueData) { - for ($i = 1; $i < $argc; $i++) { - if (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 - */ - 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. * @@ -846,12 +624,13 @@ function LogError($message) { $good = ' '; CakeLog::write('error', str_replace($bad, $good, $message)); } + /** * Searches include path for files. * * @param string $file File to look for * @return Full path to file if exists, otherwise false - * @link http://book.cakephp.org/view/702/fileExistsInPath + * @link http://book.cakephp.org/view/1131/fileExistsInPath */ function fileExistsInPath($file) { $paths = explode(PATH_SEPARATOR, ini_get('include_path')); @@ -866,12 +645,13 @@ function fileExistsInPath($file) { } return false; } + /** * Convert forward slashes to underscores and removes first and last underscores in a string * * @param string String to convert * @return string with underscore remove from start and end of string - * @link http://book.cakephp.org/view/697/convertSlash + * @link http://book.cakephp.org/view/1126/convertSlash */ function convertSlash($string) { $string = trim($string, '/'); @@ -879,62 +659,5 @@ function convertSlash($string) { $string = str_replace('/', '_', $string); return $string; } -/** - * Implements http_build_query for PHP4. - * - * @param string $data Data to set in query string - * @param string $prefix If numeric indices, prepend this to index for elements in base array. - * @param string $argSep String used to separate arguments - * @param string $baseKey Base key - * @return string URL encoded query string - * @see http://php.net/http_build_query - */ - if (!function_exists('http_build_query')) { - function http_build_query($data, $prefix = null, $argSep = null, $baseKey = null) { - if (empty($argSep)) { - $argSep = ini_get('arg_separator.output'); - } - if (is_object($data)) { - $data = get_object_vars($data); - } - $out = array(); - - foreach ((array)$data as $key => $v) { - if (is_numeric($key) && !empty($prefix)) { - $key = $prefix . $key; - } - $key = urlencode($key); - if (!empty($baseKey)) { - $key = $baseKey . '[' . $key . ']'; - } - - if (is_array($v) || is_object($v)) { - $out[] = http_build_query($v, $prefix, $argSep, $key); - } else { - $out[] = $key . '=' . urlencode($v); - } - } - return implode($argSep, $out); - } - } -/** - * Wraps ternary operations. If $condition is a non-empty value, $val1 is returned, otherwise $val2. - * Don't use for isset() conditions, or wrap your variable with @ operator: - * Example: - * - * `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/704/ife - */ - function ife($condition, $val1 = null, $val2 = null) { - if (!empty($condition)) { - return $val1; - } - return $val2; - } ?> \ No newline at end of file diff --git a/cake/bootstrap.php b/cake/bootstrap.php index 0a28e5558..051e034fd 100644 --- a/cake/bootstrap.php +++ b/cake/bootstrap.php @@ -1,28 +1,23 @@ = 5)); @@ -31,22 +26,15 @@ define('E_DEPRECATED', 8192); } error_reporting(E_ALL & ~E_DEPRECATED); -/** - * Configuration, directory layout and standard libraries - */ - if (!isset($bootstrap)) { - require CORE_PATH . 'cake' . DS . 'basics.php'; - $TIME_START = getMicrotime(); - require CORE_PATH . 'cake' . DS . 'config' . DS . 'paths.php'; - require LIBS . 'object.php'; - require LIBS . 'inflector.php'; - require LIBS . 'configure.php'; - } - require LIBS . 'cache.php'; - - Configure::getInstance(); - - $url = null; - App::import('Core', array('Dispatcher')); +require CORE_PATH . 'cake' . DS . 'basics.php'; +$TIME_START = microtime(true); +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'; ?> \ No newline at end of file diff --git a/cake/config/config.php b/cake/config/config.php index 24718485b..2b603aede 100644 --- a/cake/config/config.php +++ b/cake/config/config.php @@ -1,26 +1,21 @@ \ No newline at end of file diff --git a/cake/config/paths.php b/cake/config/paths.php index 6a6449f0e..a9e8be92a 100644 --- a/cake/config/paths.php +++ b/cake/config/paths.php @@ -1,5 +1,4 @@ __construct($args); - } + public $shellName = null; + /** * Constructor * - * @param array $args the argv. + * 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 */ - function __construct($args = array()) { + public function __construct($args = array()) { set_time_limit(0); + $this->__initConstants(); $this->parseParams($args); $this->_initEnvironment(); $this->__buildPaths(); - $this->_stop($this->dispatch()); + $this->_stop($this->dispatch() === false ? 1 : 0); } + /** * Defines core configuration. * @@ -157,12 +162,12 @@ function __initConstants() { } require_once(CORE_PATH . 'cake' . DS . 'basics.php'); } + /** * Defines current working environment. * - * @access protected */ - function _initEnvironment() { + protected function _initEnvironment() { $this->stdin = fopen('php://stdin', 'r'); $this->stdout = fopen('php://stdout', 'w'); $this->stderr = fopen('php://stderr', 'w'); @@ -193,6 +198,7 @@ function _initEnvironment() { $this->shiftArgs(); } + /** * Builds the shell paths. * @@ -201,23 +207,19 @@ function _initEnvironment() { */ function __buildPaths() { $paths = array(); - $pluginPaths = Configure::read('pluginPaths'); if (!class_exists('Folder')) { require LIBS . 'folder.php'; } - - foreach ($pluginPaths as $pluginPath) { - $Folder = new Folder($pluginPath); - list($plugins,) = $Folder->read(false, true); - foreach ((array)$plugins as $plugin) { - $path = $pluginPath . Inflector::underscore($plugin) . DS . 'vendors' . DS . 'shells' . DS; - if (file_exists($path)) { - $paths[] = $path; - } + $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(Configure::read('vendorPaths')); + $vendorPaths = array_values(App::path('vendors')); foreach ($vendorPaths as $vendorPath) { $path = rtrim($vendorPath, DS) . DS . 'shells' . DS; if (file_exists($path)) { @@ -225,8 +227,9 @@ function __buildPaths() { } } - $this->shellPaths = array_values(array_unique(array_merge($paths, Configure::read('shellPaths')))); + $this->shellPaths = array_values(array_unique(array_merge($paths, App::path('shells')))); } + /** * Initializes the environment and loads the Cake core. * @@ -239,6 +242,9 @@ function __bootstrap() { 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', @@ -262,129 +268,155 @@ function __bootstrap() { Configure::getInstance(file_exists(CONFIGS . 'bootstrap.php')); if (!file_exists(APP_PATH . 'config' . DS . 'core.php')) { - include_once CORE_PATH . 'cake' . DS . 'console' . DS . 'libs' . DS . 'templates' . DS . 'skel' . DS . 'config' . DS . 'core.php'; - Configure::buildPaths(array()); + include_once CORE_PATH . 'cake' . DS . 'console' . DS . 'templates' . DS . 'skel' . DS . 'config' . DS . 'core.php'; + App::build(); } - Configure::write('debug', 1); return true; } + /** - * Dispatches a CLI request + * Clear the console * - * @access public + * @return void */ - function dispatch() { - if (isset($this->args[0])) { - $plugin = null; - $shell = $this->args[0]; - if (strpos($shell, '.') !== false) { - list($plugin, $shell) = explode('.', $this->args[0]); + public function clear() { + if (empty($this->params['noclear'])) { + if ( DS === '/') { + passthru('clear'); + } else { + passthru('cls'); } + } + } - $this->shell = $shell; - $this->shiftArgs(); - $this->shellName = Inflector::camelize($this->shell); - $this->shellClass = $this->shellName . 'Shell'; +/** + * Dispatches a CLI request + * + * @return boolean + */ + public function dispatch() { + $arg = $this->shiftArgs(); - if ($this->shell === 'help') { - $this->help(); - } else { - $loaded = false; - foreach ($this->shellPaths as $path) { - $this->shellPath = $path . $this->shell . '.php'; - - $isPlugin = ($plugin && strpos($path, DS . $plugin . DS . 'vendors' . DS . 'shells' . DS) !== false); - if (($isPlugin && file_exists($this->shellPath)) || (!$plugin && file_exists($this->shellPath))) { - $loaded = true; - break; - } - } + if (!$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'; - if ($loaded) { - if (!class_exists('Shell')) { - require CONSOLE_LIBS . 'shell.php'; - } - require $this->shellPath; - if (class_exists($this->shellClass)) { - $command = null; - if (isset($this->args[0])) { - $command = $this->args[0]; - } - $this->shellCommand = Inflector::variable($command); - $shell = new $this->shellClass($this); - - if (strtolower(get_parent_class($shell)) == 'shell') { - $shell->initialize(); - $shell->loadTasks(); - - foreach ($shell->taskNames as $task) { - if (strtolower(get_parent_class($shell)) == 'shell') { - $shell->{$task}->initialize(); - $shell->{$task}->loadTasks(); - } - } - - $task = Inflector::camelize($command); - if (in_array($task, $shell->taskNames)) { - $this->shiftArgs(); - $shell->{$task}->startup(); - if (isset($this->args[0]) && $this->args[0] == 'help') { - if (method_exists($shell->{$task}, 'help')) { - $shell->{$task}->help(); - $this->_stop(); - } else { - $this->help(); - } - } - return $shell->{$task}->execute(); - } - } + $arg = null; - $classMethods = get_class_methods($shell); + if (isset($this->args[0])) { + $arg = $this->args[0]; + $this->shellCommand = Inflector::variable($arg); + } - $privateMethod = $missingCommand = false; - if ((in_array($command, $classMethods) || in_array(strtolower($command), $classMethods)) && strpos($command, '_', 0) === 0) { - $privateMethod = true; - } + $Shell = $this->_getShell($plugin); - if (!in_array($command, $classMethods) && !in_array(strtolower($command), $classMethods)) { - $missingCommand = true; - } + if (!$Shell) { + $title = sprintf(__('Error: Class %s could not be loaded.'), $this->shellClass); + $this->stderr($title . "\n"); + return false; + } - $protectedCommands = array( - 'initialize','in','out','err','hr', - 'createfile', 'isdir','copydir','object','tostring', - 'requestaction','log','cakeerror', 'shelldispatcher', - '__initconstants','__initenvironment','__construct', - 'dispatch','__bootstrap','getinput','stdout','stderr','parseparams','shiftargs' - ); + $methods = array(); - if (in_array(strtolower($command), $protectedCommands)) { - $missingCommand = true; - } + if (is_a($Shell, 'Shell')) { + $Shell->initialize(); + $Shell->loadTasks(); - if ($missingCommand && method_exists($shell, 'main')) { - $shell->startup(); - return $shell->main(); - } elseif (!$privateMethod && method_exists($shell, $command)) { - $this->shiftArgs(); - $shell->startup(); - return $shell->{$command}(); - } else { - $this->stderr("Unknown {$this->shellName} command '$command'.\nFor usage, try 'cake {$this->shell} help'.\n\n"); - } + 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->stderr('Class '.$this->shellClass.' could not be loaded'); + $this->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(); } - } else { - $this->help(); } + + $title = sprintf(__('Error: Unknown %1$s command %2$s.'), $this->shellName, $arg); + $message = sprintf(__('For usage try `cake %s help`'), $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 + */ + 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. * @@ -392,9 +424,8 @@ function dispatch() { * @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) { + public function getInput($prompt, $options = null, $default = null) { if (!is_array($options)) { $printOptions = ''; } else { @@ -418,36 +449,37 @@ function getInput($prompt, $options = null, $default = null) { } return $result; } + /** * Outputs to the stdout filehandle. * * @param string $string String to output. * @param boolean $newline If true, the outputs gets an added newline. - * @access public + * @return integer Returns the number of bytes output to stdout. */ - function stdout($string, $newline = true) { + public function stdout($string, $newline = true) { if ($newline) { - fwrite($this->stdout, $string . "\n"); + return fwrite($this->stdout, $string . "\n"); } else { - fwrite($this->stdout, $string); + 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, 'Error: '. $string); + public function stderr($string) { + fwrite($this->stderr, $string); } + /** * Parses command line options * * @param array $params Parameters to parse - * @access public */ - function parseParams($params) { + 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)); @@ -484,8 +516,9 @@ function parseParams($params) { $this->params = array_merge($this->params, $params); } + /** - * Helper for recursively paraing params + * Helper for recursively parsing params * * @return array params * @access private @@ -515,26 +548,22 @@ function __parseParams($params) { } } } + /** * Removes first argument and shifts other arguments up * - * @return boolean False if there are no arguments - * @access public + * @return mixed Null if there are no arguments otherwise the shifted argument */ - function shiftArgs() { - if (empty($this->args)) { - return false; - } - unset($this->args[0]); - $this->args = array_values($this->args); - return true; + public function shiftArgs() { + return array_shift($this->args); } + /** * Shows console help * - * @access public */ - function help() { + public function help() { + $this->clear(); $this->stdout("\nWelcome to CakePHP v" . Configure::version() . " Console"); $this->stdout("---------------------------------------------------------------"); $this->stdout("Current Paths:"); @@ -549,40 +578,69 @@ function help() { $this->stdout("Example: -app relative/path/to/myapp or -app /absolute/path/to/myapp"); $this->stdout("\nAvailable Shells:"); - $_shells = array(); - + $shellList = array(); foreach ($this->shellPaths as $path) { - if (is_dir($path)) { - $shells = Configure::listObjects('file', $path); - $path = str_replace(CAKE_CORE_INCLUDE_PATH . DS . 'cake' . DS, 'CORE' . DS, $path); - $path = str_replace(APP, 'APP' . DS, $path); - $path = str_replace(ROOT, 'ROOT', $path); - $path = rtrim($path, DS); - $this->stdout("\n " . $path . ":"); - if (empty($shells)) { - $this->stdout("\t - none"); - } else { - sort($shells); - foreach ($shells as $shell) { - if ($shell !== 'shell.php') { - $this->stdout("\t " . str_replace('.php', '', $shell)); - } - } + 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'"); - $this->_stop(); } + /** * Stop execution of the current script * * @param $status see http://php.net/exit for values * @return void - * @access protected */ - function _stop($status = 0) { + protected function _stop($status = 0) { exit($status); } } diff --git a/cake/console/error.php b/cake/console/error.php index 9c88a40b8..bd64e1c1f 100644 --- a/cake/console/error.php +++ b/cake/console/error.php @@ -1,29 +1,23 @@ stdout = fopen('php://stdout', 'w'); $this->stderr = fopen('php://stderr', 'w'); - if (Configure::read() > 0 || $method == 'error') { - call_user_func_array(array(&$this, $method), $messages); - } else { - call_user_func_array(array(&$this, 'error404'), $messages); - } + 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) { + public function error($params) { extract($params, EXTR_OVERWRITE); $this->stderr($code . $name . $message."\n"); $this->_stop(); } + /** * Convenience method to display a 404 page. * * @param array $params Parameters (url, message) - * @access public */ - function error404($params) { + 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->error(array( + 'code' => '404', + 'name' => 'Not found', + 'message' => sprintf(__('The requested address %s was not found on this server.'), $url, $message) + )); $this->_stop(); } + /** * Renders the Missing Controller web page. * * @param array $params Parameters (className) - * @access public */ - function missingController($params) { + public function missingController($params) { extract($params, EXTR_OVERWRITE); $controllerName = str_replace('Controller', '', $className); - $this->stderr(sprintf(__("Missing Controller '%s'", true), $controllerName)); + $this->stderr(sprintf(__("Missing Controller '%s'"), $controllerName)); $this->_stop(); } + /** * Renders the Missing Action web page. * * @param array $params Parameters (action, className) - * @access public */ - function missingAction($params) { + public function missingAction($params) { extract($params, EXTR_OVERWRITE); - $this->stderr(sprintf(__("Missing Method '%s' in '%s'", true), $action, $className)); + $this->stderr(sprintf(__("Missing Method '%s' in '%s'"), $action, $className)); $this->_stop(); } + /** * Renders the Private Action web page. * * @param array $params Parameters (action, className) - * @access public */ - function privateAction($params) { + public function privateAction($params) { extract($params, EXTR_OVERWRITE); - $this->stderr(sprintf(__("Trying to access private method '%s' in '%s'", true), $action, $className)); + $this->stderr(sprintf(__("Trying to access private method '%s' in '%s'"), $action, $className)); $this->_stop(); } + /** * Renders the Missing Table web page. * * @param array $params Parameters (table, className) - * @access public */ - function missingTable($params) { + public function missingTable($params) { extract($params, EXTR_OVERWRITE); - $this->stderr(sprintf(__("Missing database table '%s' for model '%s'", true), $table, $className)); + $this->stderr(sprintf(__("Missing database table '%s' for model '%s'"), $table, $className)); $this->_stop(); } + /** * Renders the Missing Database web page. * * @param array $params Parameters - * @access public */ - function missingDatabase($params = array()) { - $this->stderr(__("Missing Database", true)); + public function missingDatabase($params = array()) { + $this->stderr(__('Missing Database')); $this->_stop(); } + /** * Renders the Missing View web page. * * @param array $params Parameters (file, action, className) - * @access public */ - function missingView($params) { + public function missingView($params) { extract($params, EXTR_OVERWRITE); - $this->stderr(sprintf(__("Missing View '%s' for '%s' in '%s'", true), $file, $action, $className)); + $this->stderr(sprintf(__("Missing View '%s' for '%s' in '%s'"), $file, $action, $className)); $this->_stop(); } + /** * Renders the Missing Layout web page. * * @param array $params Parameters (file) - * @access public */ - function missingLayout($params) { + public function missingLayout($params) { extract($params, EXTR_OVERWRITE); - $this->stderr(sprintf(__("Missing Layout '%s'", true), $file)); + $this->stderr(sprintf(__("Missing Layout '%s'"), $file)); $this->_stop(); } + /** * Renders the Database Connection web page. * * @param array $params Parameters - * @access public */ - function missingConnection($params) { + public function missingConnection($params) { extract($params, EXTR_OVERWRITE); - $this->stderr(__("Missing Database Connection. Try 'cake bake'", true)); + $this->stderr(__("Missing Database Connection. Try 'cake bake'")); $this->_stop(); } + /** * Renders the Missing Helper file web page. * * @param array $params Parameters (file, helper) - * @access public */ - function missingHelperFile($params) { + public function missingHelperFile($params) { extract($params, EXTR_OVERWRITE); - $this->stderr(sprintf(__("Missing Helper file '%s' for '%s'", true), $file, Inflector::camelize($helper))); + $this->stderr(sprintf(__("Missing Helper file '%s' for '%s'"), $file, Inflector::camelize($helper))); $this->_stop(); } + /** * Renders the Missing Helper class web page. * * @param array $params Parameters (file, helper) - * @access public */ - function missingHelperClass($params) { + public function missingHelperClass($params) { extract($params, EXTR_OVERWRITE); - $this->stderr(sprintf(__("Missing Helper class '%s' in '%s'", true), Inflector::camelize($helper), $file)); + $this->stderr(sprintf(__("Missing Helper class '%s' in '%s'"), Inflector::camelize($helper), $file)); $this->_stop(); } + /** * Renders the Missing Component file web page. * * @param array $params Parameters (file, component) - * @access public */ - function missingComponentFile($params) { + public function missingComponentFile($params) { extract($params, EXTR_OVERWRITE); - $this->stderr(sprintf(__("Missing Component file '%s' for '%s'", true), $file, Inflector::camelize($component))); + $this->stderr(sprintf(__("Missing Component file '%s' for '%s'"), $file, Inflector::camelize($component))); $this->_stop(); } + /** * Renders the Missing Component class web page. * * @param array $params Parameters (file, component) - * @access public */ - function missingComponentClass($params) { + public function missingComponentClass($params) { extract($params, EXTR_OVERWRITE); - $this->stderr(sprintf(__("Missing Component class '%s' in '%s'", true), Inflector::camelize($component), $file)); + $this->stderr(sprintf(__("Missing Component class '%s' in '%s'"), Inflector::camelize($component), $file)); $this->_stop(); } + /** * Renders the Missing Model class web page. * * @param array $params Parameters (className) - * @access public */ - function missingModel($params) { + public function missingModel($params) { extract($params, EXTR_OVERWRITE); - $this->stderr(sprintf(__("Missing model '%s'", true), $className)); + $this->stderr(sprintf(__("Missing model '%s'"), $className)); $this->_stop(); } + /** * Outputs to the stdout filehandle. * * @param string $string String to output. * @param boolean $newline If true, the outputs gets an added newline. - * @access public */ - function stdout($string, $newline = true) { + 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) { + public function stderr($string) { fwrite($this->stderr, "Error: ". $string . "\n"); } } diff --git a/cake/console/libs/acl.php b/cake/console/libs/acl.php index 5a7873da8..722bc937d 100644 --- a/cake/console/libs/acl.php +++ b/cake/console/libs/acl.php @@ -1,31 +1,25 @@ dataSource = 'default'; - - if (isset($this->params['datasource'])) { - $this->dataSource = $this->params['datasource']; + 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 .= __('Error: Your current Cake configuration is set to') . "\n"; + $out .= __('an ACL implementation other than DB. Please change') . "\n"; + $out .= __('your core config to reflect your decision to use') . "\n"; + $out .= __('DbAcl before attempting to use this script') . ".\n"; $out .= "--------------------------------------------------\n"; - $out .= sprintf(__("Current ACL Classname: %s", true), Configure::read('Acl.classname')) . "\n"; + $out .= sprintf(__('Current ACL Classname: %s'), Configure::read('Acl.classname')) . "\n"; $out .= "--------------------------------------------------\n"; $this->err($out); $this->_stop(); @@ -88,26 +84,26 @@ function startup() { 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->out(__('Your database configuration was not found. Take a moment to create one.'), true); $this->args = null; return $this->DbConfig->execute(); } require_once (CONFIGS.'database.php'); if (!in_array($this->command, array('initdb'))) { - $this->Acl = new AclComponent(); + $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"; + public function main() { + $out = __('Available ACL commands:') . "\n"; $out .= "\t - create\n"; $out .= "\t - delete\n"; $out .= "\t - setParent\n"; @@ -119,208 +115,231 @@ function main() { $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 '", true); + $out .= __("For help, run the 'help' command. For help on a specific command, run 'help '"); $this->out($out); } + /** * Creates an ARO/ACO node * - * @access public */ - function create() { - + public function create() { $this->_checkArgs(3, 'create'); $this->checkNodeType(); extract($this->__dataVars()); $class = ucfirst($this->args[0]); - $object = new $class(); - - if (preg_match('/^([\w]+)\.(.*)$/', $this->args[1], $matches) && count($matches) == 3) { - $parent = array( - 'model' => $matches[1], - 'foreign_key' => $matches[2], - ); - } else { - $parent = $this->args[1]; - } + $parent = $this->parseIdentifier($this->args[1]); if (!empty($parent) && $parent != '/' && $parent != 'root') { - @$parent = $object->node($parent); - if (empty($parent)) { - $this->err(sprintf(__('Could not find parent node using reference "%s"', true), $this->args[1])); - return; - } else { - $parent = Set::extract($parent, "0.{$class}.id"); - } + $parent = $this->_getNodeId($class, $parent); } else { $parent = null; } - if (preg_match('/^([\w]+)\.(.*)$/', $this->args[2], $matches) && count($matches) == 3) { - $data = array( - 'model' => $matches[1], - 'foreign_key' => $matches[2], - ); - } else { - if (!($this->args[2] == '/')) { - $data = array('alias' => $this->args[2]); - } else { - $this->error(__('/ can not be used as an alias!', true), __('\t/ is the root, please supply a sub alias', true)); - } + $data = $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!'), __("\t/ is the root, please supply a sub alias")); } $data['parent_id'] = $parent; - $object->create(); - - if ($object->save($data)) { - $this->out(sprintf(__("New %s '%s' created.\n", true), $class, $this->args[2]), true); + $this->Acl->{$class}->create(); + if ($this->Acl->{$class}->save($data)) { + $this->out(sprintf(__("New %s '%s' created.\n"), $class, $this->args[2]), true); } else { - $this->err(sprintf(__("There was a problem creating a new %s '%s'.", true), $class, $this->args[2])); + $this->err(sprintf(__("There was a problem creating a new %s '%s'."), $class, $this->args[2])); } } + /** * Delete an ARO/ACO node. * - * @access public */ - function delete() { + public function delete() { $this->_checkArgs(2, 'delete'); $this->checkNodeType(); extract($this->__dataVars()); - if (!$this->Acl->{$class}->delete($this->args[1])) { - $this->error(__("Node Not Deleted", true), sprintf(__("There was an error deleting the %s. Check that the node exists", true), $class) . ".\n"); + + $identifier = $this->parseIdentifier($this->args[1]); + $nodeId = $this->_getNodeId($class, $identifier); + + if (!$this->Acl->{$class}->delete($nodeId)) { + $this->error(__('Node Not Deleted'), sprintf(__('There was an error deleting the %s. Check that the node exists'), $class) . ".\n"); } - $this->out(sprintf(__("%s deleted", true), $class) . ".\n", true); + $this->out(sprintf(__('%s deleted'), $class) . ".\n", true); } /** * Set parent for an ARO/ACO node. * - * @access public */ - function setParent() { + 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->args[1], - 'parent_id' => $this->args[2] + '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); + $this->out(__('Error in setting new parent. Please make sure the parent node exists, and is not a descendant of the node specified.'), true); } else { - $this->out(sprintf(__("Node parent set to %s", true), $this->args[2]) . "\n", true); + $this->out(sprintf(__('Node parent set to %s'), $this->args[2]) . "\n", true); } } + /** * Get path to specified ARO/ACO node. * - * @access public */ - function getPath() { + public function getPath() { $this->_checkArgs(2, 'getPath'); $this->checkNodeType(); extract($this->__dataVars()); - $id = ife(is_numeric($this->args[1]), intval($this->args[1]), $this->args[1]); + $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->error( + sprintf(__("Supplied Node '%s' not found"), $this->args[1]), + __('No tree returned.') + ); } + $this->out(__('Path:')); + $this->hr(); for ($i = 0; $i < count($nodes); $i++) { - $this->out(str_repeat(' ', $i) . "[" . $nodes[$i][$class]['id'] . "]" . $nodes[$i][$class]['alias'] . "\n"); + $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 + */ + 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() { + public function check() { $this->_checkArgs(3, 'check'); extract($this->__getParams()); if ($this->Acl->check($aro, $aco, $action)) { - $this->out(sprintf(__("%s is allowed.", true), $aro), true); + $this->out(sprintf(__('%s is allowed.'), $aroName), true); } else { - $this->out(sprintf(__("%s is not allowed.", true), $aro), true); + $this->out(sprintf(__('%s is not allowed.'), $aroName), true); } } + /** * Grant permission for a given ARO to a given ACO. * - * @access public */ - function grant() { + public function grant() { $this->_checkArgs(3, 'grant'); extract($this->__getParams()); if ($this->Acl->allow($aro, $aco, $action)) { - $this->out(__("Permission granted.", true), true); + $this->out(__('Permission granted.'), true); } else { - $this->out(__("Permission was not granted.", true), true); + $this->out(__('Permission was not granted.'), true); } } + /** * Deny access for an ARO to an ACO. * - * @access public */ - function deny() { + public function deny() { $this->_checkArgs(3, 'deny'); extract($this->__getParams()); if ($this->Acl->deny($aro, $aco, $action)) { - $this->out(__("Permission denied.", true), true); + $this->out(__('Permission denied.'), true); } else { - $this->out(__("Permission was not denied.", true), true); + $this->out(__('Permission was not denied.'), true); } } + /** * Set an ARO to inhermit permission to an ACO. * - * @access public */ - function inherit() { + public function inherit() { $this->_checkArgs(3, 'inherit'); extract($this->__getParams()); if ($this->Acl->inherit($aro, $aco, $action)) { - $this->out(__("Permission inherited.", true), true); + $this->out(__('Permission inherited.'), true); } else { - $this->out(__("Permission was not inherited.", true), true); + $this->out(__('Permission was not inherited.'), true); } } + /** * Show a specific ARO/ACO node. * - * @access public */ - function view() { + public function view() { $this->_checkArgs(1, 'view'); $this->checkNodeType(); extract($this->__dataVars()); - if (isset($this->args[1]) && !is_null($this->args[1])) { - $key = ife(is_numeric($this->args[1]), $secondary_id, 'alias'); - $conditions = array($class . '.' . $key => $this->args[1]); + + 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 { - $conditions = null; + $nodes = $this->Acl->{$class}->find('all', array('order' => $class . '.lft ASC')); } - $nodes = $this->Acl->{$class}->find('all', array('conditions' => $conditions, 'order' => 'lft ASC')); + if (empty($nodes)) { if (isset($this->args[1])) { - $this->error(sprintf(__("%s not found", true), $this->args[1]), __("No tree returned.", true)); + $this->error(sprintf(__('%s not found'), $this->args[1]), __('No tree returned.')); } elseif (isset($this->args[0])) { - $this->error(sprintf(__("%s not found", true), $this->args[0]), __("No tree returned.", true)); + $this->error(sprintf(__('%s not found'), $this->args[0]), __('No tree returned.')); } } $this->out($class . " tree:"); $this->hr(); + $stack = array(); $last = null; + foreach ($nodes as $n) { $stack[] = $n; if (!empty($last)) { @@ -334,90 +353,104 @@ function view() { } } } - $last = $n[$class]['rght']; - $count = count($stack); - $indent = str_repeat(' ', $count); - if ($n[$class]['alias']) { - $this->out($indent . "[" . $n[$class]['id'] . "]" . $n[$class]['alias']."\n"); - } else { - $this->out($indent . "[" . $n[$class]['id'] . "]" . $n[$class]['model'] . '.' . $n[$class]['foreign_key'] . "\n"); - } + $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', 'run', 'create', 'DbAcl'); + public function initdb() { + $this->Dispatch->args = array('schema', 'create', 'DbAcl'); $this->Dispatch->dispatch(); } + /** * Show help screen. * - * @access public */ - function help() { - $head = __("Usage: cake acl ...", true) . "\n"; + public function help() { + $head = "-----------------------------------------------\n"; + $head .= __('Usage: cake acl ...') . "\n"; $head .= "-----------------------------------------------\n"; - $head .= __("Commands:", true) . "\n\n"; + $head .= __('Commands:') . "\n"; $commands = array( - 'create' => "\tcreate aro|aco \n" . - "\t\t" . __("Creates a new ACL object under the parent specified by , an id/alias.", true) . "\n" . - "\t\t" . __("The and references can be in one of the following formats:", true) . "\n" . - "\t\t\t- " . __(". - The node will be bound to a specific record of the given model", true) . "\n" . - "\t\t\t- " . __(" - The node will be given a string alias (or path, in the case of ),", true) . "\n" . - "\t\t\t " . __("i.e. 'John'. When used with , this takes the form of an alias path,", true) . "\n" . - "\t\t\t " . __("i.e. //.", true) . "\n" . - "\t\t" . __("To add a node at the root level, enter 'root' or '/' as the parameter.", true) . "\n", - - 'delete' => "\tdelete aro|aco \n" . - "\t\t" . __("Deletes the ACL object with the given reference (see 'create' for info on node references).", true) . "\n", - - 'setparent' => "\tsetParent aro|aco \n" . - "\t\t" . __("Moves the ACL object specified by beneath the parent ACL object specified by .", true) . "\n" . - "\t\t" . __("To identify the node and parent, use the row id.", true) . "\n", - - 'getpath' => "\tgetPath aro|aco \n" . - "\t\t" . __("Returns the path to the ACL object specified by . This command", true) . "\n" . - "\t\t" . __("is useful in determining the inhertiance of permissions for a certain", true) . "\n" . - "\t\t" . __("object in the tree.", true) . "\n" . - "\t\t" . __("For more detailed parameter usage info, see help for the 'create' command.", true) . "\n", - - 'check' => "\tcheck [] " . __("or", true) . " all\n" . - "\t\t" . __("Use this command to check ACL permissions.", true) . "\n" . - "\t\t" . __("For more detailed parameter usage info, see help for the 'create' command.", true) . "\n", - - 'grant' => "\tgrant [] " . __("or", true) . " all\n" . - "\t\t" . __("Use this command to grant ACL permissions. Once executed, the ARO", true) . "\n" . - "\t\t" . __("specified (and its children, if any) will have ALLOW access to the", true) . "\n" . - "\t\t" . __("specified ACO action (and the ACO's children, if any).", true) . "\n" . - "\t\t" . __("For more detailed parameter usage info, see help for the 'create' command.", true) . "\n", - - 'deny' => "\tdeny []" . __("or", true) . " all\n" . - "\t\t" . __("Use this command to deny ACL permissions. Once executed, the ARO", true) . "\n" . - "\t\t" . __("specified (and its children, if any) will have DENY access to the", true) . "\n" . - "\t\t" . __("specified ACO action (and the ACO's children, if any).", true) . "\n" . - "\t\t" . __("For more detailed parameter usage info, see help for the 'create' command.", true) . "\n", - - 'inherit' => "\tinherit []" . __("or", true) . " all\n" . - "\t\t" . __("Use this command to force a child ARO object to inherit its", true) . "\n" . - "\t\t" . __("permissions settings from its parent.", true) . "\n" . - "\t\t" . __("For more detailed parameter usage info, see help for the 'create' command.", true) . "\n", - - 'view' => "\tview aro|aco []\n" . - "\t\t" . __("The view command will return the ARO or ACO tree. The optional", true) . "\n" . - "\t\t" . __("id/alias parameter allows you to return only a portion of the requested tree.", true) . "\n" . - "\t\t" . __("For more detailed parameter usage info, see help for the 'create' command.", true) . "\n", - - 'initdb' => "\tinitdb\n". - "\t\t" . __("Uses this command : cake schema run create DbAcl", true) . "\n", - - 'help' => "\thelp []\n" . - "\t\t" . __("Displays this help message, or a message on a specific command.", true) . "\n" + 'create' => "create aro|aco \n" . + "\t" . __("Creates a new ACL object under the parent") . "\n" . + "\t" . __("specified by , an id/alias.") . "\n" . + "\t" . __("The and references can be") . "\n" . + "\t" . __("in one of the following formats:") . "\n\n" . + "\t\t- " . __(". - The node will be bound to a") . "\n" . + "\t\t" . __("specific record of the given model.") . "\n\n" . + "\t\t- " . __(" - The node will be given a string alias,") . "\n" . + "\t\t" . __(" (or path, in the case of )") . "\n" . + "\t\t " . __("i.e. 'John'. When used with ,") . "\n" . + "\t\t" . __("this takes the form of an alias path,") . "\n" . + "\t\t " . __("i.e. //.") . "\n\n" . + "\t" . __("To add a node at the root level,") . "\n" . + "\t" . __("enter 'root' or '/' as the parameter.") . "\n", + + 'delete' => "delete aro|aco \n" . + "\t" . __("Deletes the ACL object with the given reference") . "\n" . + "\t" . __("For more detailed parameter usage info,") . "\n" . + "\t" . __("see help for the 'create' command."), + + 'setparent' => "setParent aro|aco \n" . + "\t" . __("Moves the ACL object specified by beneath") . "\n" . + "\t" . __("the parent ACL object specified by .") . "\n" . + "\t" . __("For more detailed parameter usage info,") . "\n" . + "\t" . __("see help for the 'create' command."), + + 'getpath' => "getPath aro|aco \n" . + "\t" . __("Returns the path to the ACL object specified by . This command") . "\n" . + "\t" . __("is useful in determining the inhertiance of permissions for a certain") . "\n" . + "\t" . __("object in the tree.") . "\n" . + "\t" . __("For more detailed parameter usage info,") . "\n" . + "\t" . __("see help for the 'create' command."), + + 'check' => "check [] " . __("or") . " all\n" . + "\t" . __("Use this command to check ACL permissions.") . "\n" . + "\t" . __("For more detailed parameter usage info,") . "\n" . + "\t" . __("see help for the 'create' command."), + + 'grant' => "grant [] " . __("or") . " all\n" . + "\t" . __("Use this command to grant ACL permissions. Once executed, the ARO") . "\n" . + "\t" . __("specified (and its children, if any) will have ALLOW access to the") . "\n" . + "\t" . __("specified ACO action (and the ACO's children, if any).") . "\n" . + "\t" . __("For more detailed parameter usage info,") . "\n" . + "\t" . __("see help for the 'create' command."), + + 'deny' => "deny []" . __("or") . " all\n" . + "\t" . __("Use this command to deny ACL permissions. Once executed, the ARO") . "\n" . + "\t" . __("specified (and its children, if any) will have DENY access to the") . "\n" . + "\t" . __("specified ACO action (and the ACO's children, if any).") . "\n" . + "\t" . __("For more detailed parameter usage info,") . "\n" . + "\t" . __("see help for the 'create' command."), + + 'inherit' => "inherit []" . __("or") . " all\n" . + "\t" . __("Use this command to force a child ARO object to inherit its") . "\n" . + "\t" . __("permissions settings from its parent.") . "\n" . + "\t" . __("For more detailed parameter usage info,") . "\n" . + "\t" . __("see help for the 'create' command."), + + 'view' => "view aro|aco []\n" . + "\t" . __("The view command will return the ARO or ACO tree.") . "\n" . + "\t" . __("The optional node parameter allows you to return") . "\n" . + "\t" . __("only a portion of the requested tree.") . "\n" . + "\t" . __("For more detailed parameter usage info,") . "\n" . + "\t" . __("see help for the 'create' command."), + + 'initdb' => "initdb\n". + "\t" . __("Uses this command : cake schema run create DbAcl"), + + 'help' => "help []\n" . + "\t" . __("Displays this help message, or a message on a specific command.") ); $this->out($head); @@ -425,46 +458,83 @@ function help() { foreach ($commands as $cmd) { $this->out("{$cmd}\n\n"); } - } elseif (isset($commands[low($this->args[0])])) { - $this->out($commands[low($this->args[0])] . "\n\n"); + } 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])); + $this->out(sprintf(__("Command '%s' not found"), $this->args[0])); } } + /** * Check that first argument specifies a valid Node type (ARO/ACO) * - * @access public */ - function checkNodeType() { + public function checkNodeType() { if (!isset($this->args[0])) { return false; } if ($this->args[0] != 'aco' && $this->args[0] != 'aro') { - $this->error(sprintf(__("Missing/Unknown node type: '%s'", true), $this->args[1]), __('Please specify which ACL object type you wish to create.', true)); + $this->error(sprintf(__("Missing/Unknown node type: '%s'"), $this->args[0]), __('Please specify which ACL object type you wish to create. Either "aro" or "aco"')); } } + /** * Checks that given node exists * * @param string $type Node type (ARO/ACO) * @param integer $id Node id * @return boolean Success - * @access public */ - function nodeExists() { + public function nodeExists() { if (!$this->checkNodeType() && !isset($this->args[1])) { return false; } extract($this->__dataVars($this->args[0])); - $key = (ife(is_numeric($this->args[1]), $secondary_id, 'alias')); + $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)); + $this->error(sprintf(__('%s not found'), $this->args[1]), __('No tree returned.')); } 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"'), $identifier)); + } + return Set::extract($node, "0.{$class}.id"); + } + /** * get params for standard Acl methods * @@ -472,23 +542,17 @@ function nodeExists() { * @access private */ function __getParams() { - $aro = ife(is_numeric($this->args[0]), intval($this->args[0]), $this->args[0]); - $aco = ife(is_numeric($this->args[1]), intval($this->args[1]), $this->args[1]); + $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) && preg_match('/^([\w]+)\.(.*)$/', $aro, $matches)) { - $aro = array( - 'model' => $matches[1], - 'foreign_key' => $matches[2], - ); + if (is_string($aro)) { + $aro = $this->parseIdentifier($aro); } - - if (is_string($aco) && preg_match('/^([\w]+)\.(.*)$/', $aco, $matches)) { - $aco = array( - 'model' => $matches[1], - 'foreign_key' => $matches[2], - ); + if (is_string($aco)) { + $aco = $this->parseIdentifier($aco); } - $action = null; if (isset($this->args[2])) { $action = $this->args[2]; @@ -496,7 +560,7 @@ function __getParams() { $action = '*'; } } - return compact('aro', 'aco', 'action'); + return compact('aro', 'aco', 'action', 'aroName', 'acoName'); } /** @@ -512,7 +576,7 @@ function __dataVars($type = null) { } $vars = array(); $class = ucwords($type); - $vars['secondary_id'] = ife(strtolower($class) == 'aro', 'foreign_key', 'object_id'); + $vars['secondary_id'] = (strtolower($class) == 'aro') ? 'foreign_key' : 'object_id'; $vars['data_name'] = $type; $vars['table_name'] = $type . 's'; $vars['class'] = $class; diff --git a/cake/console/libs/api.php b/cake/console/libs/api.php index 87aefc10a..43c26a967 100644 --- a/cake/console/libs/api.php +++ b/cake/console/libs/api.php @@ -1,5 +1,4 @@ paths = array_merge($this->paths, array( 'behavior' => LIBS . 'model' . DS . 'behaviors' . DS, 'cache' => LIBS . 'cache' . DS, @@ -56,12 +52,12 @@ function initialize () { 'core' => LIBS )); } + /** * Override main() to handle action * - * @access public */ - function main() { + public function main() { if (empty($this->args)) { return $this->help(); } @@ -82,7 +78,7 @@ function main() { $class = Inflector::camelize($file); } - $objects = Configure::listObjects('class', $path); + $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)) { @@ -91,7 +87,7 @@ function main() { } } else { - $this->err(sprintf(__("%s not found", true), $class)); + $this->err(sprintf(__('%s not found'), $class)); $this->_stop(); } @@ -100,7 +96,7 @@ function main() { 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->err(sprintf(__('%s::%s() could not be found'), $class, $this->params['m'])); $this->_stop(); } $method = $parsed[$this->params['m']]; @@ -117,9 +113,9 @@ function main() { $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'))) { + while ($number = strtolower($this->in(__('Select a number to see the more information about a specific method. q to quit. l to list.'), null, 'q'))) { if ($number === 'q') { - $this->out(__('Done', true)); + $this->out(__('Done')); $this->_stop(); } @@ -142,9 +138,8 @@ function main() { /** * Show help for this shell. * - * @access public */ - function help() { + public function help() { $head = "Usage: cake api [] [-m ]\n"; $head .= "-----------------------------------------------\n"; $head .= "Parameters:\n\n"; @@ -169,8 +164,8 @@ function help() { foreach ($commands as $cmd) { $this->out("{$cmd}\n\n"); } - } elseif (isset($commands[low($this->args[1])])) { - $this->out($commands[low($this->args[1])] . "\n\n"); + } 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"); } @@ -190,7 +185,7 @@ function __parseClass($path) { $File = new File($path); if (!$File->exists()) { - $this->err(sprintf(__("%s could not be found", true), $File->name)); + $this->err(sprintf(__('%s could not be found'), $File->name)); $this->_stop(); } diff --git a/cake/console/libs/bake.php b/cake/console/libs/bake.php index f891ca2a4..b3aabcd54 100644 --- a/cake/console/libs/bake.php +++ b/cake/console/libs/bake.php @@ -1,5 +1,4 @@ command); if (isset($this->{$task}) && !in_array($task, array('Project', 'DbConfig'))) { - $path = Inflector::underscore(Inflector::pluralize($this->command)); - $this->{$task}->path = $this->params['working'] . DS . $path . DS; - if (!is_dir($this->{$task}->path)) { - $this->err(sprintf(__("%s directory could not be found.\nBe sure you have created %s", true), $task, $this->{$task}->path)); - $this->_stop(); + 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() { + public function main() { 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->out(__('Your database configuration was not found. Take a moment to create one.')); $this->args = null; return $this->DbConfig->execute(); } @@ -82,9 +87,11 @@ function main() { $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', 'Q'))); + $classToBake = strtoupper($this->in(__('What would you like to Bake?'), array('D', 'M', 'V', 'C', 'P', 'F', 'T', 'Q'))); switch ($classToBake) { case 'D': $this->DbConfig->execute(); @@ -101,37 +108,47 @@ function main() { 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, or C.', true)); + $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.')); } $this->hr(); $this->main(); } + /** * Quickly bake the MVC * - * @access public */ - function all() { - $ds = 'default'; + public function all() { $this->hr(); $this->out('Bake All'); $this->hr(); - if (isset($this->params['connection'])) { - $ds = $this->params['connection']; + if (!isset($this->params['connection']) && empty($this->connection)) { + $this->connection = $this->DbConfig->getConfig(); } if (empty($this->args)) { - $name = $this->Model->getName($ds); + $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]; - $this->Model->listAll($ds, false); } $modelExists = false; @@ -140,15 +157,16 @@ function all() { $object = new $model(); $modelExists = true; } else { - App::import('Model'); - $object = new Model(array('name' => $name, 'ds' => $ds)); + 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)); + $this->out(sprintf(__('%s Model was baked.'), $model)); if ($this->_checkUnitTest()) { + $this->Model->bakeFixture($model); $this->Model->bakeTest($model); } $modelExists = true; @@ -157,7 +175,7 @@ function all() { 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)); + $this->out(sprintf(__('%s Controller was baked.'), $name)); if ($this->_checkUnitTest()) { $this->Controller->bakeTest($controller); } @@ -165,15 +183,12 @@ function all() { if (App::import('Controller', $controller)) { $this->View->args = array($controller); $this->View->execute(); + $this->out(sprintf(__('%s Views were baked.'), $name)); } $this->out(__('Bake All complete')); array_shift($this->args); } else { - $this->err(__('Bake All could not continue without a valid model', true)); - } - - if (empty($this->args)) { - $this->all(); + $this->err(__('Bake All could not continue without a valid model')); } $this->_stop(); } @@ -181,9 +196,8 @@ function all() { /** * Displays help contents * - * @access public */ - function help() { + public function help() { $this->out('CakePHP Bake:'); $this->hr(); $this->out('The Bake script generates controllers, views and models for your application.'); @@ -204,8 +218,9 @@ function help() { $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(""); - + $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(); } } ?> \ No newline at end of file diff --git a/cake/console/libs/console.php b/cake/console/libs/console.php index 19b075092..c610fb585 100644 --- a/cake/console/libs/console.php +++ b/cake/console/libs/console.php @@ -1,68 +1,65 @@ Dispatcher = new Dispatcher(); - $this->models = Configure::listObjects('model'); + $this->models = App::objects('model'); App::import('Model', $this->models); foreach ($this->models as $model) { - $class = Inflector::camelize(r('.php', '', $model)); + $class = Inflector::camelize(str_replace('.php', '', $model)); $this->models[$model] = $class; $this->{$class} =& new $class(); } @@ -72,22 +69,76 @@ function initialize() { foreach ($this->models as $model) { $this->out(" - {$model}"); } - $this->__loadRoutes(); + $this->_loadRoutes(); } + /** * Prints the help message * - * @access public */ - function help() { - $this->main('help'); + 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 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 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 "; + $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) { + public function main($command = null) { while (true) { if (empty($command)) { $command = trim($this->in('')); @@ -95,44 +146,7 @@ function main($command = null) { switch ($command) { case 'help': - $this->out('Console help:'); - $this->out('-------------'); - $this->out('The interactive console is a tool for testing parts of your app before you commit code'); - $this->out(''); - $this->out('Model testing:'); - $this->out('To test model results, use the name of your model without a leading $'); - $this->out('e.g. Foo->find("all")'); - $this->out(''); - $this->out('To dynamically set associations, you can do the following:'); - $this->out("\tModelA bind ModelB"); - $this->out("where the supported assocations are hasOne, hasMany, belongsTo, hasAndBelongsToMany"); - $this->out(''); - $this->out('To dynamically remove associations, you can do the following:'); - $this->out("\t ModelA unbind ModelB"); - $this->out("where the supported associations are the same as above"); - $this->out(''); - $this->out("To save a new field in a model, you can do the following:"); - $this->out("\tModelA->save(array('foo' => 'bar', 'baz' => 0))"); - $this->out("where you are passing a hash of data to be saved in the format"); - $this->out("of field => value pairs"); - $this->out(''); - $this->out("To get column information for a model, use the following:"); - $this->out("\tModelA columns"); - $this->out("which returns a list of columns and their type"); - $this->out(''); - $this->out('Route testing:'); - $this->out('To test URLs against your app\'s route configuration, type:'); - $this->out("\tRoute "); - $this->out("where url is the path to your your action plus any query parameters, minus the"); - $this->out("application's base path"); - $this->out(''); - $this->out('To reload your routes config (config/routes.php), do the following:'); - $this->out("\tRoutes reload"); - $this->out(''); - $this->out(''); - $this->out('To show all connected routes, do the following:'); - $this->out("\tRoutes show"); - $this->out(''); + $this->help(); break; case 'quit': case 'exit': @@ -155,7 +169,7 @@ function main($command = null) { $association = $tmp[2]; $modelB = $tmp[3]; - if ($this->__isValidModel($modelA) && $this->__isValidModel($modelB) && in_array($association, $this->associations)) { + 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 { @@ -182,7 +196,7 @@ function main($command = null) { } } - if ($this->__isValidModel($modelA) && $this->__isValidModel($modelB) && in_array($association, $this->associations) && $validCurrentAssociation) { + 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 { @@ -197,7 +211,7 @@ function main($command = null) { // Do we have a valid model? list($modelToCheck, $tmp) = explode('->', $command); - if ($this->__isValidModel($modelToCheck)) { + if ($this->_isValidModel($modelToCheck)) { $findCommand = "\$data = \$this->$command;"; @eval($findCommand); @@ -213,7 +227,7 @@ function main($command = null) { $this->out("\t$field2: $value2"); } - $this->out(""); + $this->out(); } else { $this->out("\t$field: $value"); } @@ -228,7 +242,7 @@ function main($command = null) { $this->out("\t$field2: $value2"); } - $this->out(""); + $this->out(); } else { $this->out("\t$field: $value"); } @@ -249,7 +263,7 @@ function main($command = null) { $command = str_replace($this->badCommandChars, "", $command); list($modelToSave, $tmp) = explode("->", $command); - if ($this->__isValidModel($modelToSave)) { + 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); @@ -261,7 +275,7 @@ function main($command = null) { case (preg_match("/^(\w+) columns/", $command, $tmp) == true): $modelToCheck = strip_tags(str_replace($this->badCommandChars, "", $tmp[1])); - if ($this->__isValidModel($modelToCheck)) { + if ($this->_isValidModel($modelToCheck)) { // Get the column info for this model $fieldsCommand = "\$data = \$this->{$modelToCheck}->getColumnTypes();"; @eval($fieldsCommand); @@ -277,7 +291,7 @@ function main($command = null) { break; case (preg_match("/^routes\s+reload/i", $command, $tmp) == true): $router =& Router::getInstance(); - if (!$this->__loadRoutes()) { + 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; @@ -286,7 +300,12 @@ function main($command = null) { break; case (preg_match("/^routes\s+show/i", $command, $tmp) == true): $router =& Router::getInstance(); - $this->out(join("\n", Set::extract($router->routes, '{n}.0'))); + $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)); @@ -298,24 +317,24 @@ function main($command = null) { $command = ''; } } + /** * Tells if the specified model is included in the list of available models * * @param string $modelToCheck * @return boolean true if is an available model, false otherwise - * @access private */ - function __isValidModel($modelToCheck) { + 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 private */ - function __loadRoutes() { + protected function _loadRoutes() { $router =& Router::getInstance(); $router->reload(); @@ -329,10 +348,10 @@ function __loadRoutes() { foreach (array_keys($router->getNamedExpressions()) as $var) { unset(${$var}); } - for ($i = 0; $i < count($router->routes); $i++) { - $router->compile($i); + for ($i = 0, $len = count($router->routes); $i < $len; $i++) { + $router->routes[$i]->compile(); } return true; } } -?> +?> \ No newline at end of file diff --git a/cake/console/libs/i18n.php b/cake/console/libs/i18n.php index f0e23b91b..c01bd9e2f 100644 --- a/cake/console/libs/i18n.php +++ b/cake/console/libs/i18n.php @@ -1,29 +1,23 @@ _welcome(); if (isset($this->params['datasource'])) { $this->dataSource = $this->params['datasource']; @@ -58,25 +54,25 @@ function startup() { 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->out(__('Your database configuration was not found. Take a moment to create one.'), true); return $this->DbConfig->execute(); } } } + /** * Override main() for help message hook * - * @access public */ - function main() { - $this->out(__('I18n Shell', true)); + public function main() { + $this->out(__('I18n Shell')); $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)); + $this->out(__('[E]xtract POT file from sources')); + $this->out(__('[I]nitialize i18n database table')); + $this->out(__('[H]elp')); + $this->out(__('[Q]uit')); - $choice = strtolower($this->in(__('What would you like to do?', true), array('E', 'I', 'H', 'Q'))); + $choice = strtolower($this->in(__('What would you like to do?'), array('E', 'I', 'H', 'Q'))); switch ($choice) { case 'e': $this->Extract->execute(); @@ -91,36 +87,36 @@ function main() { 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->out(__('You have made an invalid selection. Please choose a command to execute by entering E, I, H, or Q.')); } $this->hr(); $this->main(); } + /** * Initialize I18N database. * - * @access public */ - function initdb() { - $this->Dispatch->args = array('schema', 'run', 'create', 'i18n'); + public function initdb() { + $this->Dispatch->args = array('schema', 'create', 'i18n'); $this->Dispatch->dispatch(); } + /** * Show help screen. * - * @access public */ - function help() { + public function help() { $this->hr(); - $this->out(__('I18n Shell:', true)); + $this->out(__('I18n Shell:')); $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->out(__('I18n Shell initializes i18n database table for your application')); + $this->out(__('and generates .pot file(s) with translations.')); $this->hr(); - $this->out(__('usage:', true)); + $this->out(__('usage:')); $this->out(' cake i18n help'); $this->out(' cake i18n initdb [-datasource custom]'); - $this->out(''); + $this->out(); $this->hr(); $this->Extract->help(); diff --git a/cake/console/libs/schema.php b/cake/console/libs/schema.php index 36974eb51..eb5ed6650 100644 --- a/cake/console/libs/schema.php +++ b/cake/console/libs/schema.php @@ -7,65 +7,70 @@ * * PHP versions 4 and 5 * - * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org) - * Copyright 2005-2009, Cake Software Foundation, Inc. (http://www.cakefoundation.org) + * CakePHP(tm) : Rapid Development Framework (http://cakephp.org) + * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org) * * Licensed under The MIT License * Redistributions of files must retain the above copyright notice. * - * @filesource - * @copyright Copyright 2005-2009, Cake Software Foundation, Inc. (http://www.cakefoundation.org) - * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @copyright Copyright 2005-2010, 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 http://www.opensource.org/licenses/mit-license.php The MIT License + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ -App::import('File'); -App::import('Model', 'Schema'); +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/734/Schema-management-and-migrations + * @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; + private $__dry = null; + /** * Override initialize * - * @access public */ - function initialize() { + public function initialize() { $this->_welcome(); $this->out('Cake Schema Shell'); $this->hr(); } + /** * Override startup * - * @access public */ - function startup() { - $name = null; + public function startup() { + $name = $file = $path = $connection = $plugin = null; if (!empty($this->params['name'])) { $name = $this->params['name']; - $this->params['file'] = Inflector::underscore($name); + } elseif (!empty($this->args[0])) { + $name = $this->params['name'] = $this->args[0]; } - $path = null; - if (!empty($this->params['path'])) { - $path = $this->params['path']; + if (strpos($name, '.')) { + list($this->params['plugin'], $splitName) = pluginSplit($name); + $name = $this->params['name'] = $splitName; + } + + if ($name) { + $this->params['file'] = Inflector::underscore($name); } - $file = null; if (empty($this->params['file'])) { $this->params['file'] = 'schema.php'; } @@ -74,45 +79,51 @@ function startup() { } $file = $this->params['file']; - $connection = null; + if (!empty($this->params['path'])) { + $path = $this->params['path']; + } + if (!empty($this->params['connection'])) { $connection = $this->params['connection']; } - - $this->Schema =& new CakeSchema(compact('name', 'path', 'file', 'connection')); + if (!empty($this->params['plugin'])) { + $plugin = $this->params['plugin']; + } + $this->Schema =& new CakeSchema(compact('name', 'path', 'file', 'connection', 'plugin')); } + /** * Override main * - * @access public */ - function main() { + public function main() { $this->help(); } + /** * Read and output contents of schema object * path to read as second arg * - * @access public */ - function view() { + public function view() { $File = new File($this->Schema->path . DS . $this->params['file']); if ($File->exists()) { $this->out($File->read()); $this->_stop(); } else { - $this->err(__('Schema could not be found', true)); + $file = $this->Schema->path . DS . $this->params['file']; + $this->err(sprintf(__('Schema file (%s) could not be found.'), $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)); + public function generate() { + $this->out(__('Generating Schema...')); $options = array(); if (isset($this->params['f'])) { $options = array('models' => false); @@ -166,111 +177,120 @@ function generate() { } if ($this->Schema->write($content)) { - $this->out(sprintf(__('Schema file: %s generated', true), $content['file'])); + $this->out(sprintf(__('Schema file: %s generated'), $content['file'])); $this->_stop(); } else { - $this->err(__('Schema file: %s generated', true)); + $this->err(__('Schema file: %s generated')); $this->_stop(); } } + /** * Dump Schema object to sql file - * if first arg == write, file will be written to sql file - * or it will output sql + * 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() { + public function dump() { $write = false; $Schema = $this->Schema->load(); if (!$Schema) { - $this->err(__('Schema could not be loaded', true)); + $this->err(__('Schema could not be loaded')); $this->_stop(); } - if (!empty($this->args[0])) { - if ($this->args[0] == 'write') { + if (isset($this->params['write'])) { + if ($this->params['write'] == 1) { $write = Inflector::underscore($this->Schema->name); } else { - $write = $this->args[0]; + $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'; } - $File = new File($this->Schema->path . DS . $write, true); + 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->out(sprintf(__('SQL dump file created in %s'), $File->pwd())); $this->_stop(); } else { - $this->err(__('SQL dump could not be created', true)); + $this->err(__('SQL dump could not be created')); $this->_stop(); } } $this->out($contents); return $contents; } + /** - * Run database commands: create, update + * Run database create commands. Alias for run create. * - * @access public + * @return void */ - function run() { - if (!isset($this->args[0])) { - $this->err(__('Command not found', true)); - $this->_stop(); - } - - $command = $this->args[0]; + function create() { + list($Schema, $table) = $this->_loadSchema(); + $this->__create($Schema, $table); + } - $this->Dispatch->shiftArgs(); +/** + * Run database create commands. Alias for run create. + * + * @return void + */ + function update() { + list($Schema, $table) = $this->_loadSchema(); + $this->__update($Schema, $table); + } - $name = null; - if (isset($this->args[0])) { - $name = $this->args[0]; - } +/** + * 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)); + $this->out(__('Performing a dry run.')); } - $options = array('name' => $name); + $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); + $Schema =& $this->Schema->load($options); if (!$Schema) { - $this->err(sprintf(__('%s could not be loaded', true), $this->Schema->file)); + $this->err(sprintf(__('%s could not be loaded'), $this->Schema->path . DS . $this->Schema->file)); $this->_stop(); } - $table = null; if (isset($this->args[1])) { $table = $this->args[1]; } - - switch ($command) { - case 'create': - $this->__create($Schema, $table); - break; - case 'update': - $this->__update($Schema, $table); - break; - default: - $this->err(__('Command not found', true)); - $this->_stop(); - } + return array(&$Schema, $table); } + /** * Create database from Schema object * Should be called via the run method @@ -292,28 +312,28 @@ function __create(&$Schema, $table = null) { $create[$table] = $db->createSchema($Schema, $table); } if (empty($drop) || empty($create)) { - $this->out(__('Schema is up to date.', true)); + $this->out(__('Schema is up to date.')); $this->_stop(); } - $this->out("\n" . __('The following table(s) will be dropped.', true)); + $this->out("\n" . __('The following table(s) will be dropped.')); $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)); + if ('y' == $this->in(__('Are you sure you want to drop the table(s)?'), array('y', 'n'), 'n')) { + $this->out(__('Dropping table(s).')); $this->__run($drop, 'drop', $Schema); } - $this->out("\n" . __('The following table(s) will be created.', true)); + $this->out("\n" . __('The following table(s) will be created.')); $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)); + if ('y' == $this->in(__('Are you sure you want to create the table(s)?'), array('y', 'n'), 'y')) { + $this->out(__('Creating table(s).')); $this->__run($create, 'create', $Schema); } - - $this->out(__('End create.', true)); + $this->out(__('End create.')); } + /** * Update database with Schema object * Should be called via the run method @@ -323,7 +343,7 @@ function __create(&$Schema, $table = null) { function __update(&$Schema, $table = null) { $db =& ConnectionManager::getDataSource($this->Schema->connection); - $this->out(__('Comparing Database to Schema...', true)); + $this->out(__('Comparing Database to Schema...')); $options = array(); if (isset($this->params['f'])) { $options['models'] = false; @@ -342,20 +362,21 @@ function __update(&$Schema, $table = null) { } if (empty($contents)) { - $this->out(__('Schema is up to date.', true)); + $this->out(__('Schema is up to date.')); $this->_stop(); } - $this->out("\n" . __('The following statements will run.', true)); + $this->out("\n" . __('The following statements will run.')); $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)); + if ('y' == $this->in(__('Are you sure you want to alter the tables?'), array('y', 'n'), 'n')) { + $this->out(); + $this->out(__('Updating Database...')); $this->__run($contents, 'update', $Schema); } - $this->out(__('End update.', true)); + $this->out(__('End update.')); } + /** * Runs sql from __create() or __update() * @@ -363,19 +384,18 @@ function __update(&$Schema, $table = null) { */ function __run($contents, $event, &$Schema) { if (empty($contents)) { - $this->err(__('Sql could not be run', true)); + $this->err(__('Sql could not be run')); return; } Configure::write('debug', 2); $db =& ConnectionManager::getDataSource($this->Schema->connection); - $db->fullDebug = true; foreach ($contents as $table => $sql) { if (empty($sql)) { - $this->out(sprintf(__('%s is up to date.', true), $table)); + $this->out(sprintf(__('%s is up to date.'), $table)); } else { if ($this->__dry === true) { - $this->out(sprintf(__('Dry run for %s :', true), $table)); + $this->out(sprintf(__('Dry run for %s :'), $table)); $this->out($sql); } else { if (!$Schema->before(array($event => $table))) { @@ -391,39 +411,86 @@ function __run($contents, $event, &$Schema) { if (!empty($error)) { $this->out($error); } else { - $this->out(sprintf(__('%s updated.', true), $table)); + $this->out(sprintf(__('%s updated.'), $table)); } } } } } + /** * Displays help contents * - * @access public */ - function help() { - $this->out("The Schema Shell generates a schema object from"); - $this->out("the database and updates the database from the schema."); - $this->hr(); - $this->out("Usage: cake schema ..."); - $this->hr(); - $this->out('Params:'); - $this->out("\n\t-connection \n\t\tset db config . uses 'default' if none is specified"); - $this->out("\n\t-path \n\t\tpath to read and write schema.php.\n\t\tdefault path: ". $this->Schema->path); - $this->out("\n\t-name \n\t\tclassname to use."); - $this->out("\n\t-file \n\t\tfile to read and write.\n\t\tdefault file: ". $this->Schema->file); - $this->out("\n\t-s \n\t\tsnapshot to use for run."); - $this->out("\n\t-dry\n\t\tPerform a dry run on 'run' commands.\n\t\tQueries will be output to window instead of executed."); - $this->out("\n\t-f\n\t\tforce 'generate' to create a new schema."); - $this->out('Commands:'); - $this->out("\n\tschema help\n\t\tshows this help message."); - $this->out("\n\tschema view\n\t\tread and output contents of schema file"); - $this->out("\n\tschema generate\n\t\treads from 'connection' writes to 'path'\n\t\tTo force generation of all tables into the schema, use the -f param.\n\t\tUse 'schema generate snapshot ' to generate snapshots\n\t\twhich you can use with the -s parameter in the other operations."); - $this->out("\n\tschema dump \n\t\tDump database sql based on schema file to . \n\t\tIf is write, schema dump will be written to a file\n\t\tthat has the same name as the app directory."); - $this->out("\n\tschema run create \n\t\tDrop and create tables based on schema file\n\t\toptional arg for selecting schema name\n\t\toptional
arg for creating only one table\n\t\tpass the -s param with a number to use a snapshot\n\t\tTo see the changes, perform a dry run with the -dry param"); - $this->out("\n\tschema run update
\n\t\talter tables based on schema file\n\t\toptional arg for selecting schema name.\n\t\toptional
arg for altering only one table.\n\t\tTo use a snapshot, pass the -s param with the snapshot number\n\t\tTo see the changes, perform a dry run with the -dry param"); - $this->out(""); + public function help() { + $help = << ... +--------------------------------------------------------------- +Params: + -connection + set db config . uses 'default' if none is specified + + -path + path to read and write schema.php. + default path: {$this->Schema->path} + + -name + Classname to use. If is Plugin.className, it will + set the plugin and name params. + + -file + file to read and write. + default file: {$this->Schema->file} + + -s + snapshot 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 + 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 ' to generate snapshots + which you can use with the -s parameter in the other operations. + + schema dump + 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
+ Drop and create tables based on schema file + optional
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
+ Alter the tables based on schema file. Optional
+ 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`. +TEXT; + $this->out($help); $this->_stop(); } } diff --git a/cake/console/libs/shell.php b/cake/console/libs/shell.php index eed6bff8e..e3fe0ee03 100644 --- a/cake/console/libs/shell.php +++ b/cake/console/libs/shell.php @@ -1,29 +1,23 @@ 'command'); + foreach ($vars as $key => $var) { if (is_string($key)) { $this->{$var} =& $dispatch->{$key}; @@ -159,64 +168,66 @@ function __construct(&$dispatch) { $this->Dispatch =& $dispatch; } + /** * Initializes the Shell * acts as constructor for subclasses * allows configuration of tasks prior to shell execution * - * @access public */ - function initialize() { + public function initialize() { $this->_loadModels(); } + /** * Starts up the the Shell * allows for checking and configuring prior to command or main execution * can be overriden in subclasses * - * @access public */ - function startup() { + public function startup() { $this->_welcome(); } + /** * Displays a header for the shell * - * @access protected */ - function _welcome() { - $this->out("\nWelcome to CakePHP v" . Configure::version() . " Console"); - $this->out("---------------------------------------------------------------"); + 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() { + 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'); + $this->err('Database config could not be loaded.'); + $this->out('Run `bake` to create the database configuration.'); return false; } + /** - * if var $uses = true + * if public $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 + * if public $uses is an array of models will load those models * * @return bool - * @access protected */ - function _loadModels() { + protected function _loadModels() { if ($this->uses === null || $this->uses === false) { return; } @@ -236,11 +247,7 @@ function _loadModels() { $this->modelClass = $modelClassName; foreach ($uses as $modelClass) { - $plugin = null; - if (strpos($modelClass, '.') !== false) { - list($plugin, $modelClass) = explode('.', $modelClass); - $plugin = $plugin . '.'; - } + list($plugin, $modelClass) = pluginSplit($modelClass, true); if (PHP5) { $this->{$modelClass} = ClassRegistry::init($plugin . $modelClass); } else { @@ -251,13 +258,13 @@ function _loadModels() { } return false; } + /** - * Loads tasks defined in var $tasks + * Loads tasks defined in public $tasks * * @return bool - * @access public */ - function loadTasks() { + public function loadTasks() { if ($this->tasks === null || $this->tasks === false || $this->tasks === true || empty($this->tasks)) { return true; } @@ -273,19 +280,23 @@ function loadTasks() { if (!class_exists($taskClass)) { foreach ($this->Dispatch->shellPaths as $path) { - $taskPath = $path . 'tasks' . DS . $task.'.php'; + $taskPath = $path . 'tasks' . DS . $task . '.php'; if (file_exists($taskPath)) { require_once $taskPath; break; } } } - if (ClassRegistry::isKeySet($taskClass)) { + $taskClassCheck = $taskClass; + if (!PHP5) { + $taskClassCheck = strtolower($taskClass); + } + if (ClassRegistry::isKeySet($taskClassCheck)) { $this->taskNames[] = $taskName; if (!PHP5) { - $this->{$taskName} =& ClassRegistry::getObject($taskClass); + $this->{$taskName} =& ClassRegistry::getObject($taskClassCheck); } else { - $this->{$taskName} = ClassRegistry::getObject($taskClass); + $this->{$taskName} = ClassRegistry::getObject($taskClassCheck); } } else { $this->taskNames[] = $taskName; @@ -297,13 +308,14 @@ function loadTasks() { } if (!isset($this->{$taskName})) { - $this->err("Task '" . $taskName . "' could not be loaded"); + $this->err("Task `{$taskName}` could not be loaded"); $this->_stop(); } } return true; } + /** * Prompts the user for input, and returns it. * @@ -311,9 +323,8 @@ function loadTasks() { * @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) { + public function in($prompt, $options = null, $default = null) { if (!$this->interactive) { return $default; } @@ -337,279 +348,276 @@ function in($prompt, $options = null, $default = null) { return $in; } } + /** - * Outputs to the stdout filehandle. + * Outputs a single or multiple messages to stdout. If no parameters + * are passed outputs just a newline. * - * @param string $string String to output. - * @param boolean $newline If true, the outputs gets an added newline. - * @access public + * @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. */ - function out($string, $newline = true) { - if (is_array($string)) { - $str = ''; - foreach ($string as $message) { - $str .= $message ."\n"; - } - $string = $str; + public function out($message = null, $newlines = 1) { + if (is_array($message)) { + $message = implode($this->nl(), $message); } - return $this->Dispatch->stdout($string, $newline); + return $this->Dispatch->stdout($message . $this->nl($newlines), false); } + /** - * Outputs to the stderr filehandle. + * Outputs a single or multiple error messages to stderr. If no parameters + * are passed outputs just a newline. * - * @param string $string Error text to output. - * @access public + * @param mixed $message A string or a an array of strings to output + * @param integer $newlines Number of newlines to append */ - function err($string) { - if (is_array($string)) { - $str = ''; - foreach ($string as $message) { - $str .= $message ."\n"; - } - $string = $str; + public function err($message = null, $newlines = 1) { + if (is_array($message)) { + $message = implode($this->nl(), $message); } - return $this->Dispatch->stderr($string."\n"); + $this->Dispatch->stderr($message . $this->nl($newlines)); } + /** - * Outputs a series of minus characters to the standard output, acts as a visual separator. + * Returns a single or multiple linefeeds sequences. * - * @param boolean $newline If true, the outputs gets an added newline. + * @param integer $multiplier Number of times the linefeed sequence should be repeated * @access public + * @return string */ - function hr($newline = false) { - if ($newline) { - $this->out("\n"); - } + 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 + */ + public function hr($newlines = 0) { + $this->out(null, $newlines); $this->out('---------------------------------------------------------------'); - if ($newline) { - $this->out("\n"); - } + $this->out(null, $newlines); } + /** - * Displays a formatted error message and exits the application + * Displays a formatted error message + * and exits the application with status code 1 * - * @param string $title Title of the error message - * @param string $msg Error message - * @access public + * @param string $title Title of the error + * @param string $message An optional error message */ - function error($title, $msg) { - $out = "$title\n"; - $out .= "$msg\n"; - $out .= "\n"; - $this->err($out); - $this->_stop(); + public function error($title, $message = null) { + $this->err(sprintf(__('Error: %s'), $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) { + protected function _checkArgs($expectedNum, $command = null) { if (!$command) { $command = $this->command; } if (count($this->args) < $expectedNum) { - $this->error("Wrong number of parameters: ".count($this->args), "Expected: {$expectedNum}\nPlease type 'cake {$this->shell} help' for help on usage of the {$this->name} {$command}"); + $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) { + public function createFile($path, $contents) { $path = str_replace(DS . DS, DS, $path); - $this->out("\n" . sprintf(__("Creating file %s", true), $path)); + + $this->out(); + $this->out(sprintf(__('Creating file %s'), $path)); + if (is_file($path) && $this->interactive === true) { - $key = $this->in(__("File exists, overwrite?", true). " {$path}", array('y', 'n', 'q'), 'n'); + $prompt = sprintf(__('File `%s` exists, overwrite?'), $path); + $key = $this->in($prompt, array('y', 'n', 'q'), 'n'); + if (strtolower($key) == 'q') { - $this->out(__("Quitting.", true) ."\n"); - exit; + $this->out(__('Quitting.'), 2); + $this->_stop(); } elseif (strtolower($key) != 'y') { - $this->out(__("Skip", true) ." {$path}\n"); + $this->out(sprintf(__('Skip `%s`'), $path), 2); return false; } } if (!class_exists('File')) { - uses('file'); + require LIBS . 'file.php'; } if ($File = new File($path, true)) { $data = $File->prepare($contents); $File->write($data); - $this->out(__("Wrote", true) ." {$path}"); + $this->out(sprintf(__('Wrote `%s`'), $path)); return true; } else { - $this->err(__("Error! Could not write to", true)." {$path}.\n"); + $this->err(sprintf(__('Could not write to `%s`.'), $path), 2); return false; } } + /** * Outputs usage text on the standard output. Implement it in subclasses. * - * @access public */ - function help() { + public function help() { if ($this->command != null) { - $this->err("Unknown {$this->name} command '$this->command'.\nFor usage, try 'cake {$this->shell} help'.\n\n"); + $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() { + protected function _checkUnitTest() { if (App::import('vendor', 'simpletest' . DS . 'simpletest')) { return true; } - $unitTest = $this->in('SimpleTest is not installed. Do you want to bake unit test files anyway?', array('y','n'), 'y'); + $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("\nYou can download SimpleTest from http://simpletest.org", true); + $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) { + public function shortPath($file) { $shortPath = str_replace(ROOT, null, $file); $shortPath = str_replace('..' . DS, '', $shortPath); return str_replace(DS . DS, DS, $shortPath); } -/** - * Checks for Configure::read('Routing.admin') and forces user to input it if not enabled - * - * @return string Admin route to use - * @access public - */ - function getAdmin() { - $admin = ''; - $cakeAdmin = null; - $adminRoute = Configure::read('Routing.admin'); - if (!empty($adminRoute)) { - $cakeAdmin = $adminRoute . '_'; - } else { - $this->out('You need to enable Configure::write(\'Routing.admin\',\'admin\') in /app/config/core.php to use admin routing.'); - $this->out('What would you like the admin route to be?'); - $this->out('Example: www.example.com/admin/controller'); - while ($admin == '') { - $admin = $this->in("What would you like the admin route to be?", null, 'admin'); - } - if ($this->Project->cakeAdmin($admin) !== true) { - $this->out('Unable to write to /app/config/core.php.'); - $this->out('You need to enable Configure::write(\'Routing.admin\',\'admin\') in /app/config/core.php to use admin routing.'); - $this->_stop(); - } else { - $cakeAdmin = $admin . '_'; - } - } - return $cakeAdmin; - } + /** * Creates the proper controller path for the specified controller class name * * @param string $name Controller class name * @return string Path to controller - * @access protected */ - function _controllerPath($name) { + 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) { + 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) { + protected function _modelName($name) { return Inflector::camelize(Inflector::singularize($name)); } + /** * Creates the proper singular model key for associations * * @param string $name Controller class name * @return string Singular model key - * @access protected */ - function _modelKey($name) { - return Inflector::underscore(Inflector::singularize($name)).'_id'; + protected function _modelKey($name) { + return Inflector::underscore(Inflector::singularize($name)) . '_id'; } + /** * Creates the proper model name from a foreign key * * @param string $key Foreign key * @return string Model name - * @access protected */ - function _modelNameFromKey($key) { - $name = str_replace('_id', '',$key); - return Inflector::camelize($name); + 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) { + 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) { + 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) { + protected function _singularHumanName($name) { return Inflector::humanize(Inflector::underscore(Inflector::singularize($name))); } + /** * Creates the plural human name used in views * * @param string $name Controller name * @return string Plural human name - * @access protected */ - function _pluralHumanName($name) { - return Inflector::humanize(Inflector::underscore(Inflector::pluralize($name))); + 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); } } ?> \ No newline at end of file diff --git a/cake/console/libs/tasks/bake.php b/cake/console/libs/tasks/bake.php new file mode 100644 index 000000000..08ca80728 --- /dev/null +++ b/cake/console/libs/tasks/bake.php @@ -0,0 +1,59 @@ +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; + } +} \ No newline at end of file diff --git a/cake/console/libs/tasks/controller.php b/cake/console/libs/tasks/controller.php index 736522a84..2207147f0 100644 --- a/cake/console/libs/tasks/controller.php +++ b/cake/console/libs/tasks/controller.php @@ -1,93 +1,95 @@ args)) { - $this->__interactive(); + $this->_interactive(); } if (isset($this->args[0])) { - $controller = Inflector::camelize($this->args[0]); - $actions = null; - if (isset($this->args[1]) && $this->args[1] == 'scaffold') { - $this->out('Baking scaffold for ' . $controller); + 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 ') . $controller); $actions = $this->bakeActions($controller); - } else { - $actions = 'scaffold'; + } elseif (!empty($this->args[1]) && $this->args[1] == 'admin') { + $admin = $this->Project->getPrefix(); + if ($admin) { + $this->out(sprintf(__('Adding %s methods'), $admin)); + $actions = $this->bakeActions($controller, $admin); + } } - if ((isset($this->args[1]) && $this->args[1] == 'admin') || (isset($this->args[2]) && $this->args[2] == 'admin')) { - if ($admin = $this->getAdmin()) { - $this->out('Adding ' . Configure::read('Routing.admin') .' methods'); - if ($actions == 'scaffold') { - $actions = $this->bakeActions($controller, $admin); - } else { - $actions .= $this->bakeActions($controller, $admin); - } + + if (!empty($this->args[2]) && $this->args[2] == 'admin') { + $admin = $this->Project->getPrefix(); + if ($admin) { + $this->out(sprintf(__('Adding %s methods'), $admin)); + $actions .= "\n" . $this->bakeActions($controller, $admin); } } + if ($this->bake($controller, $actions)) { if ($this->_checkUnitTest()) { $this->bakeTest($controller); @@ -95,144 +97,168 @@ function execute() { } } } + +/** + * Bake All the controllers at once. Will only bake controllers for models that exist. + * + * @return void + */ + public 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 + * @return void */ - function __interactive($controllerName = false) { - if (!$controllerName) { - $this->interactive = true; - $this->hr(); - $this->out(sprintf("Bake Controller\nPath: %s", $this->path)); - $this->hr(); - $actions = ''; - $uses = array(); - $helpers = array(); - $components = array(); - $wannaUseSession = 'y'; - $wannaDoAdmin = 'n'; - $wannaUseScaffold = 'n'; - $wannaDoScaffolding = 'y'; - $controllerName = $this->getName(); + protected function _interactive() { + $this->interactive = true; + $this->hr(); + $this->out(sprintf(__("Bake Controller\nPath: %s"), $this->path)); + $this->hr(); + + if (empty($this->connection)) { + $this->connection = $this->DbConfig->getConfig(); } + + $controllerName = $this->getName(); $this->hr(); - $this->out("Baking {$controllerName}Controller"); + $this->out(sprintf(__('Baking %sController'), $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); + $question[] = __("Would you like to build your controller interactively?"); if (file_exists($this->path . $controllerFile .'_controller.php')) { - $question[] = sprintf(__("Warning: Choosing no will overwrite the %sController.", true), $controllerName); + $question[] = sprintf(__("Warning: Choosing no will overwrite the %sController."), $controllerName); } - $doItInteractive = $this->in(join("\n", $question), array('y','n'), 'y'); + $doItInteractive = $this->in(implode("\n", $question), array('y','n'), 'y'); - if (strtolower($doItInteractive) == 'y' || strtolower($doItInteractive) == 'yes') { + if (strtolower($doItInteractive) == 'y') { $this->interactive = true; + $useDynamicScaffold = $this->in( + __("Would you like to use dynamic scaffolding?"), array('y','n'), 'n' + ); - $wannaUseScaffold = $this->in(__("Would you like to use scaffolding?", true), array('y','n'), 'n'); - - if (strtolower($wannaUseScaffold) == 'n' || strtolower($wannaUseScaffold) == 'no') { - - $wannaDoScaffolding = $this->in(__("Would you like to include some basic class methods (index(), add(), view(), edit())?", true), array('y','n'), 'n'); - - if (strtolower($wannaDoScaffolding) == 'y' || strtolower($wannaDoScaffolding) == 'yes') { - $wannaDoAdmin = $this->in(__("Would you like to create the methods for admin routing?", true), array('y','n'), 'n'); - } - - $wannaDoHelpers = $this->in(__("Would you like this controller to use other helpers besides HtmlHelper and FormHelper?", true), array('y','n'), 'n'); - - if (strtolower($wannaDoHelpers) == 'y' || strtolower($wannaDoHelpers) == 'yes') { - $helpersList = $this->in(__("Please provide a comma separated list of the other helper names you'd like to use.\nExample: 'Ajax, Javascript, Time'", true)); - $helpersListTrimmed = str_replace(' ', '', $helpersList); - $helpers = explode(',', $helpersListTrimmed); - } - $wannaDoComponents = $this->in(__("Would you like this controller to use any components?", true), array('y','n'), 'n'); + if (strtolower($useDynamicScaffold) == 'y') { + $wannaBakeCrud = 'n'; + $actions = 'scaffold'; + } else { + list($wannaBakeCrud, $wannaBakeAdminCrud) = $this->_askAboutMethods(); - if (strtolower($wannaDoComponents) == 'y' || strtolower($wannaDoComponents) == 'yes') { - $componentsList = $this->in(__("Please provide a comma separated list of the component names you'd like to use.\nExample: 'Acl, Security, RequestHandler'", true)); - $componentsListTrimmed = str_replace(' ', '', $componentsList); - $components = explode(',', $componentsListTrimmed); - } + $helpers = $this->doHelpers(); + $components = $this->doComponents(); - $wannaUseSession = $this->in(__("Would you like to use Sessions?", true), array('y','n'), 'y'); - } else { - $wannaDoScaffolding = 'n'; + $wannaUseSession = $this->in( + __("Would you like to use Session flash messages?"), array('y','n'), 'y' + ); } } else { - $wannaDoScaffolding = $this->in(__("Would you like to include some basic class methods (index(), add(), view(), edit())?", true), array('y','n'), 'y'); - - if (strtolower($wannaDoScaffolding) == 'y' || strtolower($wannaDoScaffolding) == 'yes') { - $wannaDoAdmin = $this->in(__("Would you like to create the methods for admin routing?", true), array('y','n'), 'y'); - } + list($wannaBakeCrud, $wannaBakeCrud) = $this->_askAboutMethods(); } - $admin = false; - if ((strtolower($wannaDoAdmin) == 'y' || strtolower($wannaDoAdmin) == 'yes')) { - $admin = $this->getAdmin(); + if (strtolower($wannaBakeCrud) == 'y') { + $actions = $this->bakeActions($controllerName, null, strtolower($wannaUseSession) == 'y'); } - - if (strtolower($wannaDoScaffolding) == 'y' || strtolower($wannaDoScaffolding) == 'yes') { - $actions = $this->bakeActions($controllerName, null, in_array(strtolower($wannaUseSession), array('y', 'yes'))); - if ($admin) { - $actions .= $this->bakeActions($controllerName, $admin, in_array(strtolower($wannaUseSession), array('y', 'yes'))); - } + if (strtolower($wannaBakeAdminCrud) == 'y') { + $admin = $this->Project->getPrefix(); + $actions .= $this->bakeActions($controllerName, $admin, strtolower($wannaUseSession) == 'y'); } if ($this->interactive === true) { - $this->out(''); - $this->hr(); - $this->out('The following controller will be created:'); - $this->hr(); - $this->out("Controller Name: $controllerName"); - - if (strtolower($wannaUseScaffold) == 'y' || strtolower($wannaUseScaffold) == 'yes') { - $this->out(" var \$scaffold;"); - $actions = 'scaffold'; - } - - if (count($helpers)) { - $this->out("Helpers: ", false); - - foreach ($helpers as $help) { - if ($help != $helpers[count($helpers) - 1]) { - $this->out(ucfirst($help) . ", ", false); - } else { - $this->out(ucfirst($help)); - } - } - } - - if (count($components)) { - $this->out("Components: ", false); + $this->confirmController($controllerName, $useDynamicScaffold, $helpers, $components); + $looksGood = $this->in(__('Look okay?'), array('y','n'), 'y'); - foreach ($components as $comp) { - if ($comp != $components[count($components) - 1]) { - $this->out(ucfirst($comp) . ", ", false); - } else { - $this->out(ucfirst($comp)); - } - } - } - $this->hr(); - $looksGood = $this->in(__('Look okay?', true), array('y','n'), 'y'); - - if (strtolower($looksGood) == 'y' || strtolower($looksGood) == 'yes') { - $baked = $this->bake($controllerName, $actions, $helpers, $components, $uses); + if (strtolower($looksGood) == 'y') { + $baked = $this->bake($controllerName, $actions, $helpers, $components); if ($baked && $this->_checkUnitTest()) { $this->bakeTest($controllerName); } - } else { - $this->__interactive($controllerName); } } else { - $baked = $this->bake($controllerName, $actions, $helpers, $components, $uses); + $baked = $this->bake($controllerName, $actions, $helpers, $components); if ($baked && $this->_checkUnitTest()) { $this->bakeTest($controllerName); } } } + +/** + * Confirm a to be baked controller with the user + * + * @return void + */ + public function confirmController($controllerName, $useDynamicScaffold, $helpers, $components) { + $this->out(); + $this->hr(); + $this->out(__('The following controller will be created:')); + $this->hr(); + $this->out(sprintf(__("Controller Name:\n\t%s"), $controllerName)); + + if (strtolower($useDynamicScaffold) == 'y') { + $this->out("var \$scaffold;"); + } + + $properties = array( + 'helpers' => __('Helpers:'), + 'components' => __('Components:'), + ); + + 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 + */ + protected function _askAboutMethods() { + $wannaBakeCrud = $this->in( + __("Would you like to create some basic class methods \n(index(), add(), view(), edit())?"), + array('y','n'), 'n' + ); + $wannaBakeAdminCrud = $this->in( + __("Would you like to create the basic class methods for admin routing?"), + array('y','n'), 'n' + ); + return array($wannaBakeCrud, $wannaBakeAdminCrud); + } + /** * Bake scaffold actions * @@ -242,156 +268,29 @@ function __interactive($controllerName = false) { * @return string Baked actions * @access private */ - function bakeActions($controllerName, $admin = null, $wannaUseSession = true) { + public function bakeActions($controllerName, $admin = null, $wannaUseSession = true) { $currentModelName = $modelImport = $this->_modelName($controllerName); if ($this->plugin) { $modelImport = $this->plugin . '.' . $modelImport; } if (!App::import('Model', $modelImport)) { - $this->err(__('You must have a model for this class to build scaffold methods. Please try again.', true)); - exit; + $this->err(__('You must have a model for this class to build basic methods. Please try again.')); + $this->_stop(); } - $actions = null; - $modelObj =& new $currentModelName(); + + $modelObj =& ClassRegistry::init($currentModelName); $controllerPath = $this->_controllerPath($controllerName); $pluralName = $this->_pluralName($currentModelName); $singularName = Inflector::variable($currentModelName); - $singularHumanName = Inflector::humanize($currentModelName); - $pluralHumanName = Inflector::humanize($controllerName); - $actions .= "\n"; - $actions .= "\tfunction {$admin}index() {\n"; - $actions .= "\t\t\$this->{$currentModelName}->recursive = 0;\n"; - $actions .= "\t\t\$this->set('{$pluralName}', \$this->paginate());\n"; - $actions .= "\t}\n"; - $actions .= "\n"; - $actions .= "\tfunction {$admin}view(\$id = null) {\n"; - $actions .= "\t\tif (!\$id) {\n"; - if ($wannaUseSession) { - $actions .= "\t\t\t\$this->Session->setFlash(__('Invalid {$singularHumanName}.', true));\n"; - $actions .= "\t\t\t\$this->redirect(array('action'=>'index'));\n"; - } else { - $actions .= "\t\t\t\$this->flash(__('Invalid {$singularHumanName}', true), array('action'=>'index'));\n"; - } - $actions .= "\t\t}\n"; - $actions .= "\t\t\$this->set('" . $singularName . "', \$this->{$currentModelName}->read(null, \$id));\n"; - $actions .= "\t}\n"; - $actions .= "\n"; - - /* ADD ACTION */ - $compact = array(); - $actions .= "\tfunction {$admin}add() {\n"; - $actions .= "\t\tif (!empty(\$this->data)) {\n"; - $actions .= "\t\t\t\$this->{$currentModelName}->create();\n"; - $actions .= "\t\t\tif (\$this->{$currentModelName}->save(\$this->data)) {\n"; - if ($wannaUseSession) { - $actions .= "\t\t\t\t\$this->Session->setFlash(__('The " . $singularHumanName . " has been saved', true));\n"; - $actions .= "\t\t\t\t\$this->redirect(array('action'=>'index'));\n"; - } else { - $actions .= "\t\t\t\t\$this->flash(__('{$currentModelName} saved.', true), array('action'=>'index'));\n"; - } - $actions .= "\t\t\t} else {\n"; - if ($wannaUseSession) { - $actions .= "\t\t\t\t\$this->Session->setFlash(__('The {$singularHumanName} could not be saved. Please, try again.', true));\n"; - } - $actions .= "\t\t\t}\n"; - $actions .= "\t\t}\n"; - foreach ($modelObj->hasAndBelongsToMany as $associationName => $relation) { - if (!empty($associationName)) { - $habtmModelName = $this->_modelName($associationName); - $habtmSingularName = $this->_singularName($associationName); - $habtmPluralName = $this->_pluralName($associationName); - $actions .= "\t\t\${$habtmPluralName} = \$this->{$currentModelName}->{$habtmModelName}->find('list');\n"; - $compact[] = "'{$habtmPluralName}'"; - } - } - foreach ($modelObj->belongsTo as $associationName => $relation) { - if (!empty($associationName)) { - $belongsToModelName = $this->_modelName($associationName); - $belongsToPluralName = $this->_pluralName($associationName); - $actions .= "\t\t\${$belongsToPluralName} = \$this->{$currentModelName}->{$belongsToModelName}->find('list');\n"; - $compact[] = "'{$belongsToPluralName}'"; - } - } - if (!empty($compact)) { - $actions .= "\t\t\$this->set(compact(" . join(', ', $compact) . "));\n"; - } - $actions .= "\t}\n"; - $actions .= "\n"; - - /* EDIT ACTION */ - $compact = array(); - $actions .= "\tfunction {$admin}edit(\$id = null) {\n"; - $actions .= "\t\tif (!\$id && empty(\$this->data)) {\n"; - if ($wannaUseSession) { - $actions .= "\t\t\t\$this->Session->setFlash(__('Invalid {$singularHumanName}', true));\n"; - $actions .= "\t\t\t\$this->redirect(array('action'=>'index'));\n"; - } else { - $actions .= "\t\t\t\$this->flash(__('Invalid {$singularHumanName}', true), array('action'=>'index'));\n"; - } - $actions .= "\t\t}\n"; - $actions .= "\t\tif (!empty(\$this->data)) {\n"; - $actions .= "\t\t\tif (\$this->{$currentModelName}->save(\$this->data)) {\n"; - if ($wannaUseSession) { - $actions .= "\t\t\t\t\$this->Session->setFlash(__('The " . $singularHumanName . " has been saved', true));\n"; - $actions .= "\t\t\t\t\$this->redirect(array('action'=>'index'));\n"; - } else { - $actions .= "\t\t\t\t\$this->flash(__('The " . $singularHumanName . " has been saved.', true), array('action'=>'index'));\n"; - } - $actions .= "\t\t\t} else {\n"; - if ($wannaUseSession) { - $actions .= "\t\t\t\t\$this->Session->setFlash(__('The {$singularHumanName} could not be saved. Please, try again.', true));\n"; - } - $actions .= "\t\t\t}\n"; - $actions .= "\t\t}\n"; - $actions .= "\t\tif (empty(\$this->data)) {\n"; - $actions .= "\t\t\t\$this->data = \$this->{$currentModelName}->read(null, \$id);\n"; - $actions .= "\t\t}\n"; - - foreach ($modelObj->hasAndBelongsToMany as $associationName => $relation) { - if (!empty($associationName)) { - $habtmModelName = $this->_modelName($associationName); - $habtmSingularName = $this->_singularName($associationName); - $habtmPluralName = $this->_pluralName($associationName); - $actions .= "\t\t\${$habtmPluralName} = \$this->{$currentModelName}->{$habtmModelName}->find('list');\n"; - $compact[] = "'{$habtmPluralName}'"; - } - } - foreach ($modelObj->belongsTo as $associationName => $relation) { - if (!empty($associationName)) { - $belongsToModelName = $this->_modelName($associationName); - $belongsToPluralName = $this->_pluralName($associationName); - $actions .= "\t\t\${$belongsToPluralName} = \$this->{$currentModelName}->{$belongsToModelName}->find('list');\n"; - $compact[] = "'{$belongsToPluralName}'"; - } - } - if (!empty($compact)) { - $actions .= "\t\t\$this->set(compact(" . join(',', $compact) . "));\n"; - } - $actions .= "\t}\n"; - $actions .= "\n"; - $actions .= "\tfunction {$admin}delete(\$id = null) {\n"; - $actions .= "\t\tif (!\$id) {\n"; - if ($wannaUseSession) { - $actions .= "\t\t\t\$this->Session->setFlash(__('Invalid id for {$singularHumanName}', true));\n"; - $actions .= "\t\t\t\$this->redirect(array('action'=>'index'));\n"; - } else { - $actions .= "\t\t\t\$this->flash(__('Invalid {$singularHumanName}', true), array('action'=>'index'));\n"; - } - $actions .= "\t\t}\n"; - $actions .= "\t\tif (\$this->{$currentModelName}->del(\$id)) {\n"; - if ($wannaUseSession) { - $actions .= "\t\t\t\$this->Session->setFlash(__('{$singularHumanName} deleted', true));\n"; - $actions .= "\t\t\t\$this->redirect(array('action'=>'index'));\n"; - } else { - $actions .= "\t\t\t\$this->flash(__('{$singularHumanName} deleted', true), array('action'=>'index'));\n"; - } - $actions .= "\t\t}\n"; - $actions .= "\t}\n"; - $actions .= "\n"; + $singularHumanName = $this->_singularHumanName($currentModelName); + $pluralHumanName = $this->_pluralName($controllerName); + + $this->Template->set(compact('admin', 'controllerPath', 'pluralName', 'singularName', 'singularHumanName', + 'pluralHumanName', 'modelObj', 'wannaUseSession', 'currentModelName')); + $actions = $this->Template->generate('actions', 'controller_actions'); return $actions; } - /** * Assembles and writes a Controller file * @@ -401,151 +300,123 @@ function bakeActions($controllerName, $admin = null, $wannaUseSession = true) { * @param array $components Components to use in controller * @param array $uses Models to use in controller * @return string Baked controller - * @access private */ - function bake($controllerName, $actions = '', $helpers = null, $components = null, $uses = null) { - $out = "plugin}AppController {\n\n"; - $out .= "\tvar \$name = '$controllerName';\n"; - - if (strtolower($actions) == 'scaffold') { - $out .= "\tvar \$scaffold;\n"; - } else { - if (count($uses)) { - $out .= "\tvar \$uses = array('" . $this->_modelName($controllerName) . "', "; - - foreach ($uses as $use) { - if ($use != $uses[count($uses) - 1]) { - $out .= "'" . $this->_modelName($use) . "', "; - } else { - $out .= "'" . $this->_modelName($use) . "'"; - } - } - $out .= ");\n"; - } + public function bake($controllerName, $actions = '', $helpers = null, $components = null) { + $isScaffold = ($actions === 'scaffold') ? true : false; - $out .= "\tvar \$helpers = array('Html', 'Form'"; - if (count($helpers)) { - foreach ($helpers as $help) { - $out .= ", '" . Inflector::camelize($help) . "'"; - } - } - $out .= ");\n"; + $this->Template->set('plugin', Inflector::camelize($this->plugin)); + $this->Template->set(compact('controllerName', 'actions', 'helpers', 'components', 'isScaffold')); + $contents = $this->Template->generate('classes', 'controller'); - if (count($components)) { - $out .= "\tvar \$components = array("; - - foreach ($components as $comp) { - if ($comp != $components[count($components) - 1]) { - $out .= "'" . Inflector::camelize($comp) . "', "; - } else { - $out .= "'" . Inflector::camelize($comp) . "'"; - } - } - $out .= ");\n"; - } - $out .= $actions; + $path = $this->getPath(); + $filename = $path . $this->_controllerPath($controllerName) . '_controller.php'; + if ($this->createFile($filename, $contents)) { + return $contents; } - $out .= "}\n"; - $out .= "?>"; - $filename = $this->path . $this->_controllerPath($controllerName) . '_controller.php'; - return $this->createFile($filename, $out); + return false; } + /** * Assembles and writes a unit test file * * @param string $className Controller class name * @return string Baked test - * @access private */ - function bakeTest($className) { - $import = $className; - if ($this->plugin) { - $import = $this->plugin . '.' . $className; - } - $out = "App::import('Controller', '$import');\n\n"; - $out .= "class Test{$className} extends {$className}Controller {\n"; - $out .= "\tvar \$autoRender = false;\n}\n\n"; - $out .= "class {$className}ControllerTest extends CakeTestCase {\n"; - $out .= "\tvar \${$className} = null;\n\n"; - $out .= "\tfunction startTest() {\n\t\t\$this->{$className} = new Test{$className}();"; - $out .= "\n\t\t\$this->{$className}->constructClasses();\n\t}\n\n"; - $out .= "\tfunction test{$className}ControllerInstance() {\n"; - $out .= "\t\t\$this->assertTrue(is_a(\$this->{$className}, '{$className}Controller'));\n\t}\n\n"; - $out .= "\tfunction endTest() {\n\t\tunset(\$this->{$className});\n\t}\n}\n"; - - $path = CONTROLLER_TESTS; - if (isset($this->plugin)) { - $pluginPath = 'plugins' . DS . Inflector::underscore($this->plugin) . DS; - $path = APP . $pluginPath . 'tests' . DS . 'cases' . DS . 'controllers' . DS; - } + public 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. + */ + public function doHelpers() { + return $this->_doPropertyChoices( + __("Would you like this controller to use other helpers\nbesides HtmlHelper and FormHelper?"), + __("Please provide a comma separated list of the other\nhelper names you'd like to use.\nExample: 'Ajax, Javascript, Time'") + ); + } - $filename = Inflector::underscore($className).'_controller.test.php'; - $this->out("\nBaking unit test for $className..."); +/** + * Interact with the user and get a list of additional components + * + * @return array Components the user wants to use. + */ + public function doComponents() { + return $this->_doPropertyChoices( + __("Would you like this controller to use any components?"), + __("Please provide a comma separated list of the component names you'd like to use.\nExample: 'Acl, Security, RequestHandler'") + ); + } - $header = '$Id'; - $content = ""; - return $this->createFile($path . $filename, $content); +/** + * 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. + */ + protected 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 models or controllers from database + * 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 = 'default') { - $db =& ConnectionManager::getDataSource($useDbConfig); - $usePrefix = empty($db->config['prefix']) ? '' : $db->config['prefix']; - if ($usePrefix) { - $tables = array(); - foreach ($db->listSources() as $table) { - if (!strncmp($table, $usePrefix, strlen($usePrefix))) { - $tables[] = substr($table, strlen($usePrefix)); - } + 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:')); + $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]); } - } else { - $tables = $db->listSources(); - } - - if (empty($tables)) { - $this->err(__('Your database does not have any tables.', true)); - $this->_stop(); - } - - $this->__tables = $tables; - $this->out('Possible Controllers based on your current database:'); - $this->_controllerNames = array(); - $count = count($tables); - for ($i = 0; $i < $count; $i++) { - $this->_controllerNames[] = $this->_controllerName($this->_modelName($tables[$i])); - $this->out($i + 1 . ". " . $this->_controllerNames[$i]); + return $this->_controllerNames; } - 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 = 'default'; - $controllers = $this->listAll($useDbConfig, 'Controllers'); + public function getName($useDbConfig = null) { + $controllers = $this->listAll($useDbConfig); $enteredController = ''; while ($enteredController == '') { - $enteredController = $this->in(__("Enter a number from the list above, type in the name of another controller, or 'q' to exit", true), null, 'q'); + $enteredController = $this->in(__("Enter a number from the list above,\ntype in the name of another controller, or 'q' to exit"), null, 'q'); if ($enteredController === 'q') { - $this->out(__("Exit", true)); + $this->out(__('Exit')); $this->_stop(); } if ($enteredController == '' || intval($enteredController) > count($controllers)) { - $this->out(__('Error:', true)); - $this->out(__("The Controller name you supplied was empty, or the number \nyou selected was not an option. Please try again.", true)); + $this->err(__("The Controller name you supplied was empty,\nor the number you selected was not an option. Please try again.")); $enteredController = ''; } } @@ -555,25 +426,46 @@ function getName() { } else { $controllerName = Inflector::camelize($enteredController); } - return $controllerName; } + /** * Displays help contents * - * @access public */ - function help() { + public function help() { $this->hr(); $this->out("Usage: cake bake controller ..."); $this->hr(); + $this->out('Arguments:'); + $this->out(); + $this->out(""); + $this->out("\tName of the controller to bake. Can use Plugin.name"); + $this->out("\tas a shortcut for plugin baking."); + $this->out(); $this->out('Commands:'); - $this->out("\n\tcontroller \n\t\tbakes controller with var \$scaffold"); - $this->out("\n\tcontroller scaffold\n\t\tbakes controller with scaffold actions.\n\t\t(index, view, add, edit, delete)"); - $this->out("\n\tcontroller scaffold admin\n\t\tbakes a controller with scaffold actions for both public and Configure::read('Routing.admin')"); - $this->out("\n\tcontroller admin\n\t\tbakes a controller with scaffold actions only for Configure::read('Routing.admin')"); - $this->out(""); + $this->out(); + $this->out("controller "); + $this->out("\tbakes controller with var \$scaffold"); + $this->out(); + $this->out("controller public"); + $this->out("\tbakes controller with basic crud actions"); + $this->out("\t(index, view, add, edit, delete)"); + $this->out(); + $this->out("controller 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 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(); } } -?> +?> \ No newline at end of file diff --git a/cake/console/libs/tasks/db_config.php b/cake/console/libs/tasks/db_config.php index d2f05393b..7dda92627 100644 --- a/cake/console/libs/tasks/db_config.php +++ b/cake/console/libs/tasks/db_config.php @@ -1,32 +1,23 @@ '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 + */ + public $databaseClassName = 'DATABASE_CONFIG'; + /** * initialization callback * * @var string - * @access public */ - function initialize() { + public function initialize() { $this->path = $this->params['working'] . DS . 'config' . DS; } + /** * Execution method always used for tasks * - * @access public */ - function execute() { + public function execute() { if (empty($this->args)) { - $this->__interactive(); + $this->_interactive(); $this->_stop(); } } + /** * Interactive interface * - * @access private + * @return void */ - function __interactive() { + protected function _interactive() { $this->hr(); $this->out('Database Configuration:'); $this->hr(); @@ -92,44 +94,36 @@ function __interactive() { 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)) { + } else if (preg_match('/^[^a-z_]/i', $name)) { $name = ''; $this->out('The name must start with an unaccented latin character or an underscore'); } } - $driver = ''; - while ($driver == '') { - $driver = $this->in('Driver:', array('db2', 'firebird', 'mssql', 'mysql', 'mysqli', 'odbc', 'oracle', 'postgres', 'sqlite', 'sybase'), 'mysql'); - } - $persistent = ''; - - while ($persistent == '') { - $persistent = $this->in('Persistent Connection?', array('y', 'n'), 'n'); - } + $driver = $this->in('Driver:', array('db2', 'firebird', 'mssql', 'mysql', 'mysqli', 'odbc', 'oracle', 'postgres', 'sqlite', 'sybase'), 'mysql'); - if (low($persistent) == 'n') { + $persistent = $this->in('Persistent Connection?', array('y', 'n'), 'n'); + if (strtolower($persistent) == 'n') { $persistent = 'false'; } else { $persistent = 'true'; } - $host = ''; + $host = ''; while ($host == '') { $host = $this->in('Database Host:', null, 'localhost'); } - $port = ''; + $port = ''; while ($port == '') { $port = $this->in('Port?', null, 'n'); } - if (low($port) == 'n') { + if (strtolower($port) == 'n') { $port = null; } - $login = ''; + $login = ''; while ($login == '') { $login = $this->in('User:', null, 'root'); } @@ -141,56 +135,52 @@ function __interactive() { if ($password == '') { $blank = $this->in('The password you supplied was empty. Use an empty password?', array('y', 'n'), 'n'); - if ($blank == 'y') - { + if ($blank == 'y') { $blankPassword = true; } } } - $database = ''; + $database = ''; while ($database == '') { $database = $this->in('Database Name:', null, 'cake'); } - $prefix = ''; + $prefix = ''; while ($prefix == '') { $prefix = $this->in('Table Prefix?', null, 'n'); } - - if (low($prefix) == 'n') { + if (strtolower($prefix) == 'n') { $prefix = null; } - $encoding = ''; + $encoding = ''; while ($encoding == '') { $encoding = $this->in('Table encoding?', null, 'n'); } - - if (low($encoding) == 'n') { + if (strtolower($encoding) == 'n') { $encoding = null; } - $schema = ''; + $schema = ''; if ($driver == 'postgres') { while ($schema == '') { $schema = $this->in('Table schema?', null, 'n'); } } - - if (low($schema) == '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(); + $this->_interactive(); } $dbConfigs[] = $config; $doneYet = $this->in('Do you wish to add another database configuration?', null, 'n'); - if (low($doneYet == 'n')) { + if (strtolower($doneYet == 'n')) { $done = true; } } @@ -199,16 +189,16 @@ function __interactive() { 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) { + private function __verify($config) { $config = array_merge($this->__defaultConfig, $config); extract($config); - $this->out(''); + $this->out(); $this->hr(); $this->out('The following database configuration will be created:'); $this->hr(); @@ -240,19 +230,19 @@ function __verify($config) { $this->hr(); $looksGood = $this->in('Look okay?', array('y', 'n'), 'y'); - if (strtolower($looksGood) == 'y' || strtolower($looksGood) == 'yes') { + 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) { + public function bake($configs) { if (!is_dir($this->path)) { $this->err($this->path . ' not found'); return false; @@ -262,7 +252,8 @@ function bake($configs) { $oldConfigs = array(); if (file_exists($filename)) { - $db = new DATABASE_CONFIG; + config('database'); + $db = new $this->databaseClassName; $temp = get_class_vars(get_class($db)); foreach ($temp as $configName => $info) { @@ -346,8 +337,29 @@ function bake($configs) { $out .= "}\n"; $out .= "?>"; - $filename = $this->path.'database.php'; + $filename = $this->path . 'database.php'; return $this->createFile($filename, $out); } + +/** + * Get a user specified Connection name + * + * @return void + */ + public 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') .':', $connections, 'default'); + } + return $useDbConfig; + } } ?> \ No newline at end of file diff --git a/cake/console/libs/tasks/extract.php b/cake/console/libs/tasks/extract.php index 7918e8cd7..2e5866859 100644 --- a/cake/console/libs/tasks/extract.php +++ b/cake/console/libs/tasks/extract.php @@ -1,255 +1,227 @@ params['files']) && !is_array($this->params['files'])) { - $this->files = explode(',', $this->params['files']); + $this->__files = explode(',', $this->params['files']); } - if (isset($this->params['path'])) { - $this->path = $this->params['path']; + if (isset($this->params['paths'])) { + $this->__paths = explode(',', $this->params['paths']); } else { - $response = ''; - while ($response == '') { - $response = $this->in("What is the full path you would like to extract?\nExample: " . $this->params['root'] . DS . "myapp\n[Q]uit", null, $this->params['working']); + $defaultPath = $this->params['working']; + $message = sprintf(__("What is the full path you would like to extract?\nExample: %s\n[Q]uit [D]one"), $this->params['root'] . DS . 'myapp'); + while (true) { + $response = $this->in($message, null, $defaultPath); if (strtoupper($response) === 'Q') { - $this->out('Extract Aborted'); + $this->out(__('Extract Aborted')); $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.')); } + $this->out(); } - - if (is_dir($response)) { - $this->path = $response; - } else { - $this->err('The directory path you supplied was not found. Please try again.'); - $this->execute(); - } - } - - if (isset($this->params['debug'])) { - $this->path = ROOT; - $this->files = array(__FILE__); } if (isset($this->params['output'])) { $this->__output = $this->params['output']; } else { - $response = ''; - while ($response == '') { - $response = $this->in("What is the full path you would like to output?\nExample: " . $this->path . DS . "locale\n[Q]uit", null, $this->path . DS . "locale"); + $message = sprintf(__("What is the full path you would like to output?\nExample: %s\n[Q]uit"), $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'); + $this->out(__('Extract Aborted')); $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.')); } + $this->out(); } + } - if (is_dir($response)) { - $this->__output = $response . DS; - } else { - $this->err('The directory path you supplied was not found. Please try again.'); - $this->execute(); - } + if (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?')), array('y', 'n'), 'n'); + $this->__merge = strtolower($response) === 'y'; } - if (empty($this->files)) { - $this->files = $this->__searchDirectory(); + 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(__('Path: ', true). $this->path); - $this->out(__('Output Directory: ', true). $this->__output); + $this->out(); + $this->out(); + $this->out(__('Extracting...')); $this->hr(); - - $response = ''; - $filename = ''; - while ($response == '') { - $response = $this->in(__('Would you like to merge all translations into one file?', true), array('y','n'), 'y'); - if (strtolower($response) == 'n') { - $this->__oneFile = false; - } else { - while ($filename == '') { - $filename = $this->in(__('What should we name this file?', true), null, $this->__filename); - if ($filename == '') { - $this->out(__('The filesname you supplied was empty. Please try again.', true)); - } - } - $this->__filename = $filename; - } + $this->out(__('Paths:')); + foreach ($this->__paths as $path) { + $this->out(' ' . $path); } + $this->out(__('Output Directory: ') . $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.')); } + /** * Show help options * - * @access public + * @return void */ - function help() { - $this->out(__('CakePHP Language String Extraction:', true)); + public function help() { + $this->out(__('CakePHP Language String Extraction:')); $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->out(__('The Extract script generates .pot file(s) with translations')); + $this->out(__('By default the .pot file(s) will be place in the locale directory of -app')); + $this->out(__('By default -app is ROOT/app')); $this->hr(); - $this->out(__('usage: cake i18n extract [command] [path...]', true)); - $this->out(''); - $this->out(__('commands:', true)); - $this->out(__(' -app [path...]: directory where your application is located', true)); - $this->out(__(' -root [path...]: path to install', true)); - $this->out(__(' -core [path...]: path to cake directory', true)); - $this->out(__(' -path [path...]: Full path to directory to extract strings', true)); - $this->out(__(' -output [path...]: Full path to output directory', true)); - $this->out(__(' -files: [comma separated list of files, full path to file is needed]', true)); - $this->out(__(' cake i18n extract help: Shows this help message.', true)); - $this->out(__(' -debug: Perform self test.', true)); - $this->out(''); + $this->out(__('Usage: cake i18n extract ...')); + $this->out(); + $this->out(__('Params:')); + $this->out(__(' -app [path...]: directory where your application is located')); + $this->out(__(' -root [path...]: path to install')); + $this->out(__(' -core [path...]: path to cake directory')); + $this->out(__(' -paths [comma separated list of paths, full path is needed]')); + $this->out(__(' -merge [yes|no]: Merge all domains strings into the default.pot file')); + $this->out(__(' -output [path...]: Full path to output directory')); + $this->out(__(' -files: [comma separated list of files, full path to file is needed]')); + $this->out(); + $this->out(__('Commands:')); + $this->out(__(' cake i18n extract help: Shows this help message.')); + $this->out(); } + /** * Extract tokens out of all files to be processed * + * @return void * @access private */ function __extractTokens() { - foreach ($this->files as $file) { + foreach ($this->__files as $file) { $this->__file = $file; - $this->out(sprintf(__('Processing %s...', true), $file)); + $this->out(sprintf(__('Processing %s...'), $file)); $code = file_get_contents($file); - - $this->__findVersion($code, $file); $allTokens = token_get_all($code); $this->__tokens = array(); $lineNumber = 1; @@ -263,72 +235,35 @@ function __extractTokens() { } if (is_array($token)) { - $lineNumber += count(split("\n", $token[1])) - 1; + $lineNumber += count(explode("\n", $token[1])) - 1; } else { - $lineNumber += count(split("\n", $token)) - 1; + $lineNumber += count(explode("\n", $token)) - 1; } } unset($allTokens); - $this->basic(); - $this->basic('__c'); - $this->extended(); - $this->extended('__dc', 2); - $this->extended('__n', 0, true); - $this->extended('__dn', 2, true); - $this->extended('__dcn', 4, true); + $this->__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')); } - $this->__buildFiles(); - $this->__writeFiles(); - $this->out('Done.'); } -/** - * Will parse __(), __c() functions - * - * @param string $functionName Function name that indicates translatable string (e.g: '__') - * @access public - */ - function basic($functionName = '__') { - $count = 0; - $tokenCount = count($this->__tokens); - while (($tokenCount - $count) > 3) { - list($countToken, $parenthesis, $middle, $right) = array($this->__tokens[$count], $this->__tokens[$count + 1], $this->__tokens[$count + 2], $this->__tokens[$count + 3]); - if (!is_array($countToken)) { - $count++; - continue; - } - - list($type, $string, $line) = $countToken; - if (($type == T_STRING) && ($string == $functionName) && ($parenthesis == '(')) { - - if (in_array($right, array(')', ',')) - && (is_array($middle) && ($middle[0] == T_CONSTANT_ENCAPSED_STRING))) { - - if ($this->__oneFile === true) { - $this->__strings[$this->__formatString($middle[1])][$this->__file][] = $line; - } else { - $this->__strings[$this->__file][$this->__formatString($middle[1])][] = $line; - } - } else { - $this->__markerError($this->__file, $line, $functionName, $count); - } - } - $count++; - } - } /** - * Will parse __d(), __dc(), __n(), __dn(), __dcn() + * Parse tokens * * @param string $functionName Function name that indicates translatable string (e.g: '__') - * @param integer $shift Number of parameters to shift to find translateable string - * @param boolean $plural Set to true if function supports plural format, false otherwise - * @access public + * @param array $map Array containing what variables it will find (e.g: domain, singular, plural) + * @return void + * @access private */ - function extended($functionName = '__d', $shift = 0, $plural = false) { + function __parse($functionName, $map) { $count = 0; $tokenCount = count($this->__tokens); - while (($tokenCount - $count) > 7) { + while (($tokenCount - $count) > 1) { list($countToken, $firstParenthesis) = array($this->__tokens[$count], $this->__tokens[$count + 1]); if (!is_array($countToken)) { $count++; @@ -349,52 +284,25 @@ function extended($functionName = '__d', $shift = 0, $plural = false) { $position++; } - if ($plural) { - $end = $position + $shift + 7; - - if ($this->__tokens[$position + $shift + 5] === ')') { - $end = $position + $shift + 5; - } - - if (empty($shift)) { - list($singular, $firstComma, $plural, $seoncdComma, $endParenthesis) = array($this->__tokens[$position], $this->__tokens[$position + 1], $this->__tokens[$position + 2], $this->__tokens[$position + 3], $this->__tokens[$end]); - $condition = ($seoncdComma == ','); - } else { - list($domain, $firstComma, $singular, $seoncdComma, $plural, $comma3, $endParenthesis) = array($this->__tokens[$position], $this->__tokens[$position + 1], $this->__tokens[$position + 2], $this->__tokens[$position + 3], $this->__tokens[$position + 4], $this->__tokens[$position + 5], $this->__tokens[$end]); - $condition = ($comma3 == ','); - } - $condition = $condition && - (is_array($singular) && ($singular[0] == T_CONSTANT_ENCAPSED_STRING)) && - (is_array($plural) && ($plural[0] == T_CONSTANT_ENCAPSED_STRING)); - } else { - if ($this->__tokens[$position + $shift + 5] === ')') { - $comma = $this->__tokens[$position + $shift + 3]; - $end = $position + $shift + 5; - } else { - $comma = null; - $end = $position + $shift + 3; + $mapCount = count($map); + $strings = array(); + while (count($strings) < $mapCount && ($this->__tokens[$position] == ',' || $this->__tokens[$position][0] == T_CONSTANT_ENCAPSED_STRING)) { + if ($this->__tokens[$position][0] == T_CONSTANT_ENCAPSED_STRING) { + $strings[] = $this->__tokens[$position][1]; } - - list($domain, $firstComma, $text, $seoncdComma, $endParenthesis) = array($this->__tokens[$position], $this->__tokens[$position + 1], $this->__tokens[$position + 2], $comma, $this->__tokens[$end]); - $condition = ($seoncdComma == ',' || $seoncdComma === null) && - (is_array($domain) && ($domain[0] == T_CONSTANT_ENCAPSED_STRING)) && - (is_array($text) && ($text[0] == T_CONSTANT_ENCAPSED_STRING)); + $position++; } - if (($endParenthesis == ')') && $condition) { - if ($this->__oneFile === true) { - if ($plural) { - $this->__strings[$this->__formatString($singular[1]) . "\0" . $this->__formatString($plural[1])][$this->__file][] = $line; - } else { - $this->__strings[$this->__formatString($text[1])][$this->__file][] = $line; - } - } else { - if ($plural) { - $this->__strings[$this->__file][$this->__formatString($singular[1]) . "\0" . $this->__formatString($plural[1])][] = $line; - } else { - $this->__strings[$this->__file][$this->__formatString($text[1])][] = $line; - } + if ($mapCount == count($strings)) { + extract(array_combine($map, $strings)); + if (!isset($domain)) { + $domain = '\'default\''; } + $string = $this->__formatString($singular); + if (isset($plural)) { + $string .= "\0" . $this->__formatString($plural); + } + $this->__strings[$this->__formatString($domain)][$string][$this->__file][] = $line; } else { $this->__markerError($this->__file, $line, $functionName, $count); } @@ -402,169 +310,95 @@ function extended($functionName = '__d', $shift = 0, $plural = false) { $count++; } } + /** * Build the translate template file contents out of obtained strings * + * @return void * @access private */ function __buildFiles() { - foreach ($this->__strings as $str => $fileInfo) { - $output = ''; - $occured = $fileList = array(); - - if ($this->__oneFile === true) { - foreach ($fileInfo as $file => $lines) { - $occured[] = "$file:" . join(';', $lines); - - if (isset($this->__fileVersions[$file])) { - $fileList[] = $this->__fileVersions[$file]; - } + foreach ($this->__strings as $domain => $strings) { + foreach ($strings as $string => $files) { + $occurrences = array(); + foreach ($files as $file => $lines) { + $occurrences[] = $file . ':' . implode(';', $lines); } - $occurances = join("\n#: ", $occured); - $occurances = str_replace($this->path, '', $occurances); - $output = "#: $occurances\n"; - $filename = $this->__filename; - - if (strpos($str, "\0") === false) { - $output .= "msgid \"$str\"\n"; - $output .= "msgstr \"\"\n"; + $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", $str); - $output .= "msgid \"$singular\"\n"; - $output .= "msgid_plural \"$plural\"\n"; - $output .= "msgstr[0] \"\"\n"; - $output .= "msgstr[1] \"\"\n"; + list($singular, $plural) = explode("\0", $string); + $sentence = "msgid \"{$singular}\"\n"; + $sentence .= "msgid_plural \"{$plural}\"\n"; + $sentence .= "msgstr[0] \"\"\n"; + $sentence .= "msgstr[1] \"\"\n\n"; } - $output .= "\n"; - } else { - foreach ($fileInfo as $file => $lines) { - $filename = $str; - $occured = array("$str:" . join(';', $lines)); - if (isset($this->__fileVersions[$str])) { - $fileList[] = $this->__fileVersions[$str]; - } - $occurances = join("\n#: ", $occured); - $occurances = str_replace($this->path, '', $occurances); - $output .= "#: $occurances\n"; - - if (strpos($file, "\0") === false) { - $output .= "msgid \"$file\"\n"; - $output .= "msgstr \"\"\n"; - } else { - list($singular, $plural) = explode("\0", $file); - $output .= "msgid \"$singular\"\n"; - $output .= "msgid_plural \"$plural\"\n"; - $output .= "msgstr[0] \"\"\n"; - $output .= "msgstr[1] \"\"\n"; - } - $output .= "\n"; + $this->__store($domain, $header, $sentence); + if ($domain != 'default' && $this->__merge) { + $this->__store('default', $header, $sentence); } } - $this->__store($filename, $output, $fileList); } } + /** * Prepare a file to be stored * - * @param string $file Filename - * @param string $input What to store - * @param array $fileList File list - * @param integer $get Set to 1 to get files to store, false to set - * @return mixed If $get == 1, files to store, otherwise void + * @return void * @access private */ - function __store($file = 0, $input = 0, $fileList = array(), $get = 0) { - static $storage = array(); - - if (!$get) { - if (isset($storage[$file])) { - $storage[$file][1] = array_unique(array_merge($storage[$file][1], $fileList)); - $storage[$file][] = $input; - } else { - $storage[$file] = array(); - $storage[$file][0] = $this->__writeHeader(); - $storage[$file][1] = $fileList; - $storage[$file][2] = $input; - } + 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 { - return $storage; + $this->__storage[$domain][$sentence] .= $header; } } + /** * Write the files that need to be stored * + * @return void * @access private */ function __writeFiles() { - $output = $this->__store(0, 0, array(), 1); - $output = $this->__mergeFiles($output); - - foreach ($output as $file => $content) { - $tmp = str_replace(array($this->path, '.php','.ctp','.thtml', '.inc','.tpl' ), '', $file); - $tmp = str_replace(DS, '.', $tmp); - $file = str_replace('.', '-', $tmp) .'.pot'; - $fileList = $content[1]; - - unset($content[1]); - - $fileList = str_replace(array($this->path), '', $fileList); - - if (count($fileList) > 1) { - $fileList = "Generated from files:\n# " . join("\n# ", $fileList); - } elseif (count($fileList) == 1) { - $fileList = 'Generated from file: ' . join('', $fileList); - } else { - $fileList = 'No version information was available in the source files.'; + $overwriteAll = false; + foreach ($this->__storage as $domain => $sentences) { + $output = $this->__writeHeader(); + foreach ($sentences as $sentence => $header) { + $output .= $header . $sentence; } - if (is_file($this->__output . $file)) { - $response = ''; - while ($response == '') { - $response = $this->in("\n\nError: ".$file . ' already exists in this location. Overwrite?', array('y','n', 'q'), 'n'); - if (strtoupper($response) === 'Q') { - $this->out('Extract Aborted'); - $this->_stop(); - } elseif (strtoupper($response) === 'N') { - $response = ''; - while ($response == '') { - $response = $this->in("What would you like to name this file?\nExample: new_" . $file, null, "new_" . $file); - $file = $response; - } + $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'), $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"), 'new_' . $filename), null, 'new_' . $filename); + $File = new File($this->__output . $response); + $filename = $response; } + } elseif (strtoupper($response) === 'A') { + $overwriteAll = true; } } - $fp = fopen($this->__output . $file, 'w'); - fwrite($fp, str_replace('--VERSIONS--', $fileList, join('', $content))); - fclose($fp); + $File->write($output); + $File->close(); } } -/** - * Merge output files - * - * @param array $output Output to merge - * @return array Merged output - * @access private - */ - function __mergeFiles($output) { - foreach ($output as $file => $content) { - if (count($content) <= 1 && $file != $this->__filename) { - @$output[$this->__filename][1] = array_unique(array_merge($output[$this->__filename][1], $content[1])); - if (!isset($output[$this->__filename][0])) { - $output[$this->__filename][0] = $content[0]; - } - unset($content[0]); - unset($content[1]); - - foreach ($content as $msgid) { - $output[$this->__filename][] = $msgid; - } - unset($output[$file]); - } - } - return $output; - } /** * Build the translation template header * @@ -574,7 +408,6 @@ function __mergeFiles($output) { function __writeHeader() { $output = "# LANGUAGE translation of CakePHP Application\n"; $output .= "# Copyright YEAR NAME \n"; - $output .= "# --VERSIONS--\n"; $output .= "#\n"; $output .= "#, fuzzy\n"; $output .= "msgid \"\"\n"; @@ -590,20 +423,7 @@ function __writeHeader() { $output .= "\"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\\n\"\n\n"; return $output; } -/** - * Find the version number of a file looking for SVN commands - * - * @param string $code Source code of file - * @param string $file File - * @access private - */ - function __findVersion($code, $file) { - $header = '$Id' . ':'; - if (preg_match('/\\' . $header . ' [\\w.]* ([\\d]*)/', $code, $versionInfo)) { - $version = str_replace(ROOT, '', 'Revision: ' . $versionInfo[1] . ' ' .$file); - $this->__fileVersions[$file] = $version; - } - } + /** * Format a string to be added as a translateable string * @@ -622,6 +442,7 @@ function __formatString($string) { $string = str_replace("\r\n", "\n", $string); return addcslashes($string, "\0..\37\\\""); } + /** * Indicate an invalid marker on a processed file * @@ -629,10 +450,11 @@ function __formatString($string) { * @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("Invalid marker content in $file:$line\n* $marker(", true); + $this->out(sprintf(__("Invalid marker content in %s:%s\n* %s("), $file, $line, $marker), true); $count += 2; $tokenCount = count($this->__tokens); $parenthesis = 1; @@ -654,32 +476,19 @@ function __markerError($file, $line, $marker, $count) { } $this->out("\n", true); } + /** - * Search the specified path for files that may contain translateable strings + * Search files that may contain translateable strings * - * @param string $path Path (or set to null to use current) - * @return array Files + * @return void * @access private */ - function __searchDirectory($path = null) { - if ($path === null) { - $path = $this->path .DS; - } - $files = glob("$path*.{php,ctp,thtml,inc,tpl}", GLOB_BRACE); - $dirs = glob("$path*", GLOB_ONLYDIR); - - $files = $files ? $files : array(); - $dirs = $dirs ? $dirs : array(); - - foreach ($dirs as $dir) { - if (!preg_match("!(^|.+/)(CVS|.svn)$!", $dir)) { - $files = array_merge($files, $this->__searchDirectory("$dir" . DS)); - if (($id = array_search($dir . DS . 'extract.php', $files)) !== FALSE) { - unset($files[$id]); - } - } + function __searchFiles() { + foreach ($this->__paths as $path) { + $Folder = new Folder($path); + $files = $Folder->findRecursive('.*\.(php|ctp|thtml|inc|tpl)', true); + $this->__files += $files; } - return $files; } } ?> \ No newline at end of file diff --git a/cake/console/libs/tasks/fixture.php b/cake/console/libs/tasks/fixture.php new file mode 100644 index 000000000..58d5c668e --- /dev/null +++ b/cake/console/libs/tasks/fixture.php @@ -0,0 +1,413 @@ +path = $this->params['working'] . DS . 'tests' . DS . 'fixtures' . DS; + } + +/** + * Execution method always used for tasks + * Handles dispatching to interactive, named, or all processess. + * + * @return void + */ + 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. + * + * @return void + */ + public 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 + * + * @return void + */ + protected 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. + */ + public function importOptions($modelName) { + $options = array(); + $doSchema = $this->in(__('Would you like to import schema for this fixture?'), array('y', 'n'), 'n'); + if ($doSchema == 'y') { + $options['schema'] = $modelName; + } + $doRecords = $this->in(__('Would you like to use record importing for this fixture?'), 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?"), $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 public $import + * @return string Baked fixture content + */ + public function bake($model, $useTable = false, $importOptions = array()) { + if (!class_exists('CakeSchema')) { + App::import('Model', 'CakeSchema', false); + } + $table = $schema = $records = $import = $modelImport = $recordImport = null; + if (!$useTable) { + $useTable = Inflector::tableize($model); + } elseif ($useTable != Inflector::tableize($model)) { + $table = $useTable; + } + + if (!empty($importOptions)) { + if (isset($importOptions['schema'])) { + $modelImport = "'model' => '{$importOptions['schema']}'"; + } + if (isset($importOptions['records'])) { + $recordImport = "'records' => true"; + } + if ($modelImport && $recordImport) { + $modelImport .= ', '; + } + if (!empty($modelImport) || !empty($recordImport)) { + $import = sprintf("array(%s%s)", $modelImport, $recordImport); + } + } + + $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. + */ + 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 string Path for the fixtures + */ + public 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 + */ + 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. + */ + 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); + } + } + $insert = "'$insert'"; + break; + case 'timestamp': + $ts = time(); + $insert = "'$ts'"; + break; + case 'datetime': + $ts = date('Y-m-d H:i:s'); + $insert = "'$ts'"; + break; + case 'date': + $ts = date('Y-m-d'); + $insert = "'$ts'"; + break; + case 'time': + $ts = date('H:i:s'); + $insert = "'$ts'"; + break; + case 'boolean': + $insert = 1; + break; + case 'text': + $insert = "'Lorem ipsum dolor sit amet, aliquet feugiat."; + $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. + */ + protected function _makeRecordString($records) { + $out = "array(\n"; + foreach ($records as $record) { + $values = array(); + foreach ($record as $field => $value) { + $values[] = "\t\t\t'$field' => $value"; + } + $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. + */ + 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"); + 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) { + $row[$field] = $db->value($value, $schema[$field]['type']); + } + $out[] = $row; + } + return $out; + } + +/** + * Displays help contents + * + */ + public function help() { + $this->hr(); + $this->out("Usage: cake bake fixture "); + $this->hr(); + $this->out('Arguments:'); + $this->out(); + $this->out(""); + $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 \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 /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(); + } +} +?> \ No newline at end of file diff --git a/cake/console/libs/tasks/model.php b/cake/console/libs/tasks/model.php index 779d088f8..d0013364b 100644 --- a/cake/console/libs/tasks/model.php +++ b/cake/console/libs/tasks/model.php @@ -1,225 +1,315 @@ args)) { - $this->__interactive(); + $this->_interactive(); } if (!empty($this->args[0])) { - $model = Inflector::camelize($this->args[0]); - if ($this->bake($model)) { + $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 + */ + public 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'), $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 + */ + protected 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. + */ + public 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'); + } + $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() { + protected function _interactive() { $this->hr(); $this->out(sprintf("Bake Model\nPath: %s", $this->path)); $this->hr(); $this->interactive = true; - $useTable = null; $primaryKey = 'id'; - $validate = array(); - $associations = array('belongsTo'=> array(), 'hasOne'=> array(), 'hasMany' => array(), 'hasAndBelongsToMany'=> array()); - - $useDbConfig = 'default'; - $configs = get_class_vars('DATABASE_CONFIG'); - - if (!is_array($configs)) { - return $this->DbConfig->execute(); - } - - $connections = array_keys($configs); - if (count($connections) > 1) { - $useDbConfig = $this->in(__('Use Database Config', true) .':', $connections, 'default'); - } - - $currentModelName = $this->getName($useDbConfig); - $db =& ConnectionManager::getDataSource($useDbConfig); - $useTable = Inflector::tableize($currentModelName); - $fullTableName = $db->fullTableName($useTable, false); - $tableIsGood = false; - - if (array_search($useTable, $this->__tables) === false) { - $this->out(''); - $this->out(sprintf(__("Given your model named '%s', Cake would expect a database table named %s", true), $currentModelName, $fullTableName)); - $tableIsGood = $this->in(__('Do you want to use this table?', true), array('y','n'), 'y'); - } - - if (strtolower($tableIsGood) == 'n' || strtolower($tableIsGood) == 'no') { - $useTable = $this->in(__('What is the name of the table (enter "null" to use NO table)?', true)); - } + $validate = $associations = array(); - while ($tableIsGood == false && strtolower($useTable) != 'null') { - if (is_array($this->__tables) && !in_array($useTable, $this->__tables)) { - $fullTableName = $db->fullTableName($useTable, false); - $this->out($fullTableName . ' does not exist.'); - $useTable = $this->in(__('What is the name of the table (enter "null" to use NO table)?', true)); - $tableIsGood = false; - } else { - $tableIsGood = true; - } + 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); - $wannaDoValidation = $this->in(__('Would you like to supply validation criteria for the fields in your model?', true), array('y','n'), 'y'); - - if (in_array($useTable, $this->__tables)) { - App::import('Model'); - $tempModel = new Model(array('name' => $currentModelName, 'table' => $useTable, 'ds' => $useDbConfig)); - - $fields = $tempModel->schema(); + if (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)) { - foreach ($fields as $name => $field) { - if (isset($field['key']) && $field['key'] == 'primary') { - break; - } - } - $primaryKey = $this->in(__('What is the primaryKey?', true), null, $name); + $primaryKey = $this->findPrimaryKey($fields); } + } else { + $this->err(sprintf(__('Table %s does not exist, cannot bake a model without a table.'), $useTable)); + $this->_stop(); + return false; + } + $displayField = $tempModel->hasField(array('name', 'title')); + if (!$displayField) { + $displayField = $this->findDisplayField($tempModel->schema()); } - if (array_search($useTable, $this->__tables) !== false && (strtolower($wannaDoValidation) == 'y' || strtolower($wannaDoValidation) == 'yes')) { + $prompt = __("Would you like to supply validation criteria \nfor the fields in your model?"); + $wannaDoValidation = $this->in($prompt, array('y','n'), 'y'); + if (array_search($useTable, $this->_tables) !== false && strtolower($wannaDoValidation) == 'y') { $validate = $this->doValidation($tempModel); } - $wannaDoAssoc = $this->in(__('Would you like to define model associations (hasMany, hasOne, belongsTo, etc.)?', true), array('y','n'), 'y'); - if ((strtolower($wannaDoAssoc) == 'y' || strtolower($wannaDoAssoc) == 'yes')) { + $prompt = __("Would you like to define model associations\n(hasMany, hasOne, belongsTo, etc.)?"); + $wannaDoAssoc = $this->in($prompt, array('y','n'), 'y'); + if (strtolower($wannaDoAssoc) == 'y') { $associations = $this->doAssociations($tempModel); } - $this->out(''); + $this->out(); $this->hr(); - $this->out(__('The following Model will be created:', true)); + $this->out(__('The following Model will be created:')); $this->hr(); $this->out("Name: " . $currentModelName); - if ($useDbConfig !== 'default') { - $this->out("DB Config: " . $useDbConfig); + if ($this->connection !== 'default') { + $this->out(sprintf(__("DB Config: %s"), $this->connection)); } if ($fullTableName !== Inflector::tableize($currentModelName)) { - $this->out("DB Table: " . $fullTableName); + $this->out(sprintf(__('DB Table: %s'), $fullTableName)); } if ($primaryKey != 'id') { - $this->out("Primary Key: " . $primaryKey); + $this->out(sprintf(__('Primary Key: %s'), $primaryKey)); } if (!empty($validate)) { - $this->out("Validation: " . print_r($validate, true)); + $this->out(sprintf(__('Validation: %s'), print_r($validate, true))); } if (!empty($associations)) { - $this->out("Associations:"); - - if (!empty($associations['belongsTo'])) { - for ($i = 0; $i < count($associations['belongsTo']); $i++) { - $this->out(" $currentModelName belongsTo {$associations['belongsTo'][$i]['alias']}"); - } + $this->out(__('Associations:')); + $assocKeys = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany'); + foreach ($assocKeys as $assocKey) { + $this->_printAssociation($currentModelName, $assocKey, $associations); } + } - if (!empty($associations['hasOne'])) { - for ($i = 0; $i < count($associations['hasOne']); $i++) { - $this->out(" $currentModelName hasOne {$associations['hasOne'][$i]['alias']}"); - } - } + $this->hr(); + $looksGood = $this->in(__('Look okay?'), array('y','n'), 'y'); - if (!empty($associations['hasMany'])) { - for ($i = 0; $i < count($associations['hasMany']); $i++) { - $this->out(" $currentModelName hasMany {$associations['hasMany'][$i]['alias']}"); + 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; + } + } - if (!empty($associations['hasAndBelongsToMany'])) { - for ($i = 0; $i < count($associations['hasAndBelongsToMany']); $i++) { - $this->out(" $currentModelName hasAndBelongsToMany {$associations['hasAndBelongsToMany'][$i]['alias']}"); - } +/** + * 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. + * @return void + */ + protected 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); } } - $this->hr(); - $looksGood = $this->in(__('Look okay?', true), array('y','n'), 'y'); + } - if (strtolower($looksGood) == 'y' || strtolower($looksGood) == 'yes') { - if ($this->bake($currentModelName, $associations, $validate, $primaryKey, $useTable, $useDbConfig)) { - if ($this->_checkUnitTest()) { - $this->bakeTest($currentModelName, $useTable, $associations); - } +/** + * 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. + */ + public function findPrimaryKey($fields) { + foreach ($fields as $name => $field) { + if (isset($field['key']) && $field['key'] == 'primary') { + break; } - } else { + } + return $this->in(__('What is the primaryKey?'), 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 + */ + public function findDisplayField($fields) { + $fieldNames = array_keys($fields); + $prompt = __("A displayField could not be automatically detected\nwould you like to choose one?"); + $continue = $this->in($prompt, array('y', 'n')); + if (strtolower($continue) == 'n') { return false; } + $prompt = __('Choose a field from the options above:'); + $choice = $this->inOptions($fieldNames, $prompt); + return $fieldNames[$choice]; } + /** - * Handles associations + * Handles Generation and user interaction for creating validation. * - * @param object $model - * @param boolean $interactive - * @return array $validate - * @access public + * @param object $model Model to have validations generated for. + * @return array $validate Array of user selected validations. */ - function doValidation(&$model, $interactive = true) { + public function doValidation(&$model) { if (!is_object($model)) { return false; } @@ -228,67 +318,119 @@ function doValidation(&$model, $interactive = true) { 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; + } - $options = array(); - +/** + * Populate the _validations array + * + * @return void + */ + public function initValidations() { + $options = $choices = array(); if (class_exists('Validation')) { - $parent = get_class_methods(get_parent_class('Validation')); - $options = array_diff(get_class_methods('Validation'), $parent); + $options = get_class_methods('Validation'); } - - foreach ($fields as $fieldName => $field) { - $prompt = 'Field: ' . $fieldName . "\n"; - $prompt .= 'Type: ' . $field['type'] . "\n"; - $prompt .= '---------------------------------------------------------------'."\n"; - $prompt .= 'Please select one of the following validation options:'."\n"; - $prompt .= '---------------------------------------------------------------'."\n"; - - sort($options); - - $skip = 1; - foreach ($options as $key => $option) { - if ($option{0} != '_' && strtolower($option) != 'getinstance') { - $prompt .= "{$skip} - {$option}\n"; - $choices[$skip] = strtolower($option); - $skip++; - } + sort($options); + $default = 1; + foreach ($options as $key => $option) { + if ($option{0} != '_') { + $choices[$default] = strtolower($option); + $default++; } + } + $this->_validations = $choices; + return $choices; + } - $methods = array_flip($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. + */ + public 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'), $fieldName)); + $this->out(sprintf(__('Type: %s'), $metaData['type'])); + $this->hr(); + $this->out(__('Please select one of the following validation options:')); + $this->hr(); + } - $prompt .= "{$skip} - Do not do any validation on this field.\n"; - $prompt .= "... or enter in a valid regex validation string.\n"; + $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"), $defaultChoice); + $prompt .= __("... or enter in a valid regex validation string.\n"); - $guess = $skip; - if ($field['null'] != 1 && $fieldName != $model->primaryKey && !in_array($fieldName, array('created', 'modified', 'updated'))) { + $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 ($field['type'] == 'string') { + } elseif ($metaData['type'] == 'string') { $guess = $methods['notempty']; - } elseif ($field['type'] == 'integer') { - $guess = $methods['numeric']; - } elseif ($field['type'] == 'boolean') { + } elseif ($metaData['type'] == 'integer') { $guess = $methods['numeric']; - } elseif ($field['type'] == 'datetime') { + } elseif ($metaData['type'] == 'boolean') { + $guess = $methods['boolean']; + } elseif ($metaData['type'] == 'date') { $guess = $methods['date']; + } elseif ($metaData['type'] == 'time') { + $guess = $methods['time']; } } - if ($interactive === true) { - $this->out(''); + 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")); + continue; + } + if (!isset($this->_validations[$choice]) && is_numeric($choice)) { + $this->out(__('Please make a valid selection.')); + continue; + } + $alreadyChosen[] = $choice; } else { $choice = $guess; } - if ($choice != $skip) { - if (is_numeric($choice) && isset($choices[$choice])) { - $validate[$fieldName] = $choices[$choice]; + + 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[$fieldName] = $choice; + $validate[$validatorName] = $choice; } } + if ($this->interactive == true && $choice != $defaultChoice) { + $anotherValidator = $this->in(__('Would you like to add another validation rule?'), array('y', 'n'), 'n'); + } else { + $anotherValidator = 'n'; + } } return $validate; } @@ -297,458 +439,393 @@ function doValidation(&$model, $interactive = true) { * Handles associations * * @param object $model - * @param boolean $interactive * @return array $assocaitons - * @access public */ - function doAssociations(&$model, $interactive = true) { - + public function doAssociations(&$model) { if (!is_object($model)) { return false; } - $this->out(__('One moment while the associations are detected.', true)); - - $fields = $model->schema(); + if ($this->interactive === true) { + $this->out(__('One moment while the associations are detected.')); + } + $fields = $model->schema(true); if (empty($fields)) { return false; } - $primaryKey = $model->primaryKey; - $foreignKey = $this->_modelKey($model->name); + if (empty($this->_tables)) { + $this->_tables = $this->getAllTables(); + } - $associations = array('belongsTo' => array(), 'hasMany' => array(), 'hasOne'=> array(), 'hasAndBelongsToMany' => array()); + $associations = array( + 'belongsTo' => array(), 'hasMany' => array(), 'hasOne'=> array(), 'hasAndBelongsToMany' => array() + ); $possibleKeys = array(); - //Look for belongsTo - $i = 0; + $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.')); + } else { + $this->out(__('Please confirm the following associations:')); + $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. + */ + public function findBelongsTo(&$model, $associations) { + $fields = $model->schema(true); foreach ($fields as $fieldName => $field) { $offset = strpos($fieldName, '_id'); - if ($fieldName != $model->primaryKey && $offset !== false) { + if ($fieldName != $model->primaryKey && $fieldName != 'parent_id' && $offset !== false) { $tmpModelName = $this->_modelNameFromKey($fieldName); - $associations['belongsTo'][$i]['alias'] = $tmpModelName; - $associations['belongsTo'][$i]['className'] = $tmpModelName; - $associations['belongsTo'][$i]['foreignKey'] = $fieldName; - $i++; + $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, + ); } } - //Look for hasOne and hasMany and hasAndBelongsToMany - $i = $j = 0; - - foreach ($this->__tables as $otherTable) { - App::import('Model'); - $tmpModelName = $this->_modelName($otherTable); - $tempOtherModel = & new Model(array('name' => $tmpModelName, 'table' => $otherTable, 'ds' => $model->useDbConfig)); - $modelFieldsTemp = $tempOtherModel->schema(); + return $associations; + } - $offset = strpos($otherTable, $model->table . '_'); - $otherOffset = strpos($otherTable, '_' . $model->table); +/** + * 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. + */ + public 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) { - if ($field['type'] == 'integer' || $field['type'] == 'string') { - $possibleKeys[$otherTable][] = $fieldName; + $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 ($fieldName != $model->primaryKey && $fieldName == $foreignKey && $offset === false && $otherOffset === false) { - $associations['hasOne'][$j]['alias'] = $tempOtherModel->name; - $associations['hasOne'][$j]['className'] = $tempOtherModel->name; - $associations['hasOne'][$j]['foreignKey'] = $fieldName; - - $associations['hasMany'][$j]['alias'] = $tempOtherModel->name; - $associations['hasMany'][$j]['className'] = $tempOtherModel->name; - $associations['hasMany'][$j]['foreignKey'] = $fieldName; - $j++; + if ($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. + */ + public 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 . '_'); - $tmpModelName = $this->_modelName(substr($otherTable, $offset)); - $associations['hasAndBelongsToMany'][$i]['alias'] = $tmpModelName; - $associations['hasAndBelongsToMany'][$i]['className'] = $tmpModelName; - $associations['hasAndBelongsToMany'][$i]['foreignKey'] = $foreignKey; - $associations['hasAndBelongsToMany'][$i]['associationForeignKey'] = $this->_modelKey($tmpModelName); - $associations['hasAndBelongsToMany'][$i]['joinTable'] = $otherTable; - $i++; + $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 + ); } - - if ($otherOffset !== false) { - $tmpModelName = $this->_modelName(substr($otherTable, 0, $otherOffset)); - $associations['hasAndBelongsToMany'][$i]['alias'] = $tmpModelName; - $associations['hasAndBelongsToMany'][$i]['className'] = $tmpModelName; - $associations['hasAndBelongsToMany'][$i]['foreignKey'] = $foreignKey; - $associations['hasAndBelongsToMany'][$i]['associationForeignKey'] = $this->_modelKey($tmpModelName); - $associations['hasAndBelongsToMany'][$i]['joinTable'] = $otherTable; - $i++; - } - } - - if ($interactive !== true) { - unset($associations['hasOne']); } + return $associations; + } - if ($interactive === true) { - $this->hr(); - if (empty($associations)) { - $this->out(__('None found.', true)); - } else { - $this->out(__('Please confirm the following associations:', true)); - $this->hr(); - foreach ($associations as $type => $settings) { - if (!empty($associations[$type])) { - $count = count($associations[$type]); - $response = 'y'; - for ($i = 0; $i < $count; $i++) { - $prompt = "{$model->name} {$type} {$associations[$type][$i]['alias']}"; - $response = $this->in("{$prompt}?", array('y','n'), 'y'); - - if ('n' == strtolower($response) || 'no' == strtolower($response)) { - unset($associations[$type][$i]); - } else { - if ($model->name === $associations[$type][$i]['alias']) { - if ($type === 'belongsTo') { - $alias = 'Parent' . $associations[$type][$i]['alias']; - } - if ($type === 'hasOne' || $type === 'hasMany') { - $alias = 'Child' . $associations[$type][$i]['alias']; - } - - $alternateAlias = $this->in(sprintf(__('This is a self join. Use %s as the alias', true), $alias), array('y', 'n'), 'y'); - - if ('n' == strtolower($alternateAlias) || 'no' == strtolower($alternateAlias)) { - $associations[$type][$i]['alias'] = $this->in(__('Specify an alternate alias.', true)); - } else { - $associations[$type][$i]['alias'] = $alias; - } - } - } - } - $associations[$type] = array_merge($associations[$type]); +/** + * 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 + */ + public 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; + } - $wannaDoMoreAssoc = $this->in(__('Would you like to define some additional model associations?', true), array('y','n'), 'n'); +/** + * 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. + */ + public function doMoreAssociations($model, $associations) { + $prompt = __('Would you like to define some additional model associations?'); + $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?')); + $assocType = intval($this->inOptions($assocs, __('Enter a number'))); + + $this->out(__("For the following options be very careful to match your setup exactly.\nAny spelling mistakes will cause errors.")); + $this->hr(); - while ((strtolower($wannaDoMoreAssoc) == 'y' || strtolower($wannaDoMoreAssoc) == 'yes')) { - $assocs = array(1 => 'belongsTo', 2 => 'hasOne', 3 => 'hasMany', 4 => 'hasAndBelongsToMany'); - $bad = true; - while ($bad) { - $this->out(__('What is the association type?', true)); - $prompt = "1. belongsTo\n"; - $prompt .= "2. hasOne\n"; - $prompt .= "3. hasMany\n"; - $prompt .= "4. hasAndBelongsToMany\n"; - $assocType = intval($this->in($prompt, null, __("Enter a number", true))); + $alias = $this->in(__('What is the alias for this association?')); + $className = $this->in(sprintf(__('What className will %s use?'), $alias), null, $alias ); + $suggestedForeignKey = null; - if (intval($assocType) < 1 || intval($assocType) > 4) { - $this->out(__('The selection you entered was invalid. Please enter a number between 1 and 4.', true)); + 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 { - $bad = false; + $showKeys = null; } - } - $this->out(__('For the following options be very careful to match your setup exactly. Any spelling mistakes will cause errors.', true)); - $this->hr(); - $alias = $this->in(__('What is the alias for this association?', true)); - $className = $this->in(sprintf(__('What className will %s use?', true), $alias), null, $alias ); - $suggestedForeignKey = null; - if ($assocType == '1') { - $showKeys = $possibleKeys[$model->table]; - $suggestedForeignKey = $this->_modelKey($alias); } else { - $otherTable = Inflector::tableize($className); - if (in_array($otherTable, $this->__tables)) { - if ($assocType < '4') { - $showKeys = $possibleKeys[$otherTable]; - } else { - $showKeys = null; - } - } else { - $otherTable = $this->in(__('What is the table for this model?', true)); - $showKeys = $possibleKeys[$otherTable]; - } - $suggestedForeignKey = $this->_modelKey($model->name); - } - if (!empty($showKeys)) { - $this->out(__('A helpful List of possible keys', true)); - for ($i = 0; $i < count($showKeys); $i++) { - $this->out($i + 1 . ". " . $showKeys[$i]); - } - $foreignKey = $this->in(__('What is the foreignKey?', true), null, __("Enter a number", true)); - if (intval($foreignKey) > 0 && intval($foreignKey) <= $i ) { - $foreignKey = $showKeys[intval($foreignKey) - 1]; - } - } - if (!isset($foreignKey)) { - $foreignKey = $this->in(__('What is the foreignKey? Specify your own.', true), null, $suggestedForeignKey); - } - if ($assocType == '4') { - $associationForeignKey = $this->in(__('What is the associationForeignKey?', true), null, $this->_modelKey($model->name)); - $joinTable = $this->in(__('What is the joinTable?', true)); + $otherTable = $this->in(__('What is the table for this model?')); + $showKeys = $possibleKeys[$otherTable]; } - $associations[$assocs[$assocType]] = array_values((array)$associations[$assocs[$assocType]]); - $count = count($associations[$assocs[$assocType]]); - $i = ($count > 0) ? $count : 0; - $associations[$assocs[$assocType]][$i]['alias'] = $alias; - $associations[$assocs[$assocType]][$i]['className'] = $className; - $associations[$assocs[$assocType]][$i]['foreignKey'] = $foreignKey; - if ($assocType == '4') { - $associations[$assocs[$assocType]][$i]['associationForeignKey'] = $associationForeignKey; - $associations[$assocs[$assocType]][$i]['joinTable'] = $joinTable; - } - $wannaDoMoreAssoc = $this->in(__('Define another association?', true), array('y','n'), 'y'); + $suggestedForeignKey = $this->_modelKey($model->name); + } + if (!empty($showKeys)) { + $this->out(__('A helpful List of possible keys')); + $foreignKey = $this->inOptions($showKeys, __('What is the foreignKey?')); + $foreignKey = $showKeys[intval($foreignKey)]; } + if (!isset($foreignKey)) { + $foreignKey = $this->in(__('What is the foreignKey? Specify your own.'), null, $suggestedForeignKey); + } + if ($assocType == 3) { + $associationForeignKey = $this->in(__('What is the associationForeignKey?'), null, $this->_modelKey($model->name)); + $joinTable = $this->in(__('What is the joinTable?')); + } + $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?'), array('y','n'), 'y'); } return $associations; } + /** - * Assembles and writes a Model file. + * Finds all possible keys to use on custom associations. * - * @param mixed $name Model name or object - * @param mixed $associations if array and $name is not an object assume Model associations array otherwise boolean interactive - * @param array $validate Validation rules - * @param string $primaryKey Primary key to use - * @param string $useTable Table to use - * @param string $useDbConfig Database configuration setting to use - * @access private + * @return array array of tables and possible keys */ - function bake($name, $associations = array(), $validate = array(), $primaryKey = 'id', $useTable = null, $useDbConfig = 'default') { - - if (is_object($name)) { - if (!is_array($associations)) { - $associations = $this->doAssociations($name, $associations); - $validate = $this->doValidation($name, $associations); - } - $primaryKey = $name->primaryKey; - $useTable = $name->table; - $useDbConfig = $name->useDbConfig; - $name = $name->name; - } - - $out = "plugin}AppModel {\n\n"; - $out .= "\tvar \$name = '{$name}';\n"; - - if ($useDbConfig !== 'default') { - $out .= "\tvar \$useDbConfig = '$useDbConfig';\n"; - } - - if (($useTable && $useTable !== Inflector::tableize($name)) || $useTable === false) { - $table = "'$useTable'"; - if (!$useTable) { - $table = 'false'; - } - $out .= "\tvar \$useTable = $table;\n"; - } - - if ($primaryKey !== 'id') { - $out .= "\tvar \$primaryKey = '$primaryKey';\n"; - } - - $validateCount = count($validate); - if (is_array($validate) && $validateCount > 0) { - $out .= "\tvar \$validate = array(\n"; - $keys = array_keys($validate); - for ($i = 0; $i < $validateCount; $i++) { - $val = "'" . $validate[$keys[$i]] . "'"; - $out .= "\t\t'" . $keys[$i] . "' => array({$val})"; - if ($i + 1 < $validateCount) { - $out .= ","; + protected 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; } - $out .= "\n"; } - $out .= "\t);\n"; } - $out .= "\n"; + return $possible; + } - if (!empty($associations)) { - if (!empty($associations['belongsTo']) || !empty($associations['hasOne']) || !empty($associations['hasMany']) || !empty($associations['hasAndBelongsToMany'])) { - $out.= "\t//The Associations below have been created with all possible keys, those that are not needed can be removed\n"; - } - - if (!empty($associations['belongsTo'])) { - $out .= "\tvar \$belongsTo = array(\n"; - $belongsToCount = count($associations['belongsTo']); - - for ($i = 0; $i < $belongsToCount; $i++) { - $out .= "\t\t'{$associations['belongsTo'][$i]['alias']}' => array(\n"; - $out .= "\t\t\t'className' => '{$associations['belongsTo'][$i]['className']}',\n"; - $out .= "\t\t\t'foreignKey' => '{$associations['belongsTo'][$i]['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 < $belongsToCount) { - $out .= ","; - } - $out .= "\n"; +/** + * 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. + */ + public 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); - } - $out .= "\t);\n\n"; - } - - if (!empty($associations['hasOne'])) { - $out .= "\tvar \$hasOne = array(\n"; - $hasOneCount = count($associations['hasOne']); - - for ($i = 0; $i < $hasOneCount; $i++) { - $out .= "\t\t'{$associations['hasOne'][$i]['alias']}' => array(\n"; - $out .= "\t\t\t'className' => '{$associations['hasOne'][$i]['className']}',\n"; - $out .= "\t\t\t'foreignKey' => '{$associations['hasOne'][$i]['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)"; - if ($i + 1 < $hasOneCount) { - $out .= ","; - } - $out .= "\n"; + $this->Template->set($data); + $this->Template->set('plugin', Inflector::camelize($this->plugin)); + $out = $this->Template->generate('classes', 'model'); - } - $out .= "\t);\n\n"; - } - - if (!empty($associations['hasMany'])) { - $out .= "\tvar \$hasMany = array(\n"; - $hasManyCount = count($associations['hasMany']); - - for ($i = 0; $i < $hasManyCount; $i++) { - $out .= "\t\t'{$associations['hasMany'][$i]['alias']}' => array(\n"; - $out .= "\t\t\t'className' => '{$associations['hasMany'][$i]['className']}',\n"; - $out .= "\t\t\t'foreignKey' => '{$associations['hasMany'][$i]['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 < $hasManyCount) { - $out .= ","; - } - $out .= "\n"; - } - $out .= "\t);\n\n"; - } - - if (!empty($associations['hasAndBelongsToMany'])) { - $out .= "\tvar \$hasAndBelongsToMany = array(\n"; - $hasAndBelongsToManyCount = count($associations['hasAndBelongsToMany']); - - for ($i = 0; $i < $hasAndBelongsToManyCount; $i++) { - $out .= "\t\t'{$associations['hasAndBelongsToMany'][$i]['alias']}' => array(\n"; - $out .= "\t\t\t'className' => '{$associations['hasAndBelongsToMany'][$i]['className']}',\n"; - $out .= "\t\t\t'joinTable' => '{$associations['hasAndBelongsToMany'][$i]['joinTable']}',\n"; - $out .= "\t\t\t'foreignKey' => '{$associations['hasAndBelongsToMany'][$i]['foreignKey']}',\n"; - $out .= "\t\t\t'associationForeignKey' => '{$associations['hasAndBelongsToMany'][$i]['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 < $hasAndBelongsToManyCount) { - $out .= ","; - } - $out .= "\n"; - } - $out .= "\t);\n\n"; - } - } - $out .= "}\n"; - $out .= "?>"; - $filename = $this->path . Inflector::underscore($name) . '.php'; + $path = $this->getPath(); + $filename = $path . Inflector::underscore($name) . '.php'; $this->out("\nBaking model class for $name..."); - return $this->createFile($filename, $out); + $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, $useTable = null, $associations = array()) { - $results = $this->fixture($className, $useTable); + public 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 + */ + public function listAll($useDbConfig = null) { + $this->_tables = $this->getAllTables($useDbConfig); - if ($results) { - $fixtureInc = 'app'; - if ($this->plugin) { - $fixtureInc = 'plugin.'.Inflector::underscore($this->plugin); + if ($this->interactive === true) { + $this->out(__('Possible Models based on your current database:')); + $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; + } - $fixture[] = "'{$fixtureInc}." . Inflector::underscore($className) ."'"; +/** + * 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 string Table name + */ + public function getTable($modelName, $useDbConfig = null) { + if (!isset($useDbConfig)) { + $useDbConfig = $this->connection; + } + App::import('Model', 'ConnectionManager', false); - if (!empty($associations)) { - $assoc[] = Set::extract($associations, 'belongsTo.{n}.className'); - $assoc[] = Set::extract($associations, 'hasOne.{n}.className'); - $assoc[] = Set::extract($associations, 'hasMany.{n}.className'); - foreach ($assoc as $key => $value) { - if (is_array($value)) { - foreach ($value as $class) { - $fixture[] = "'{$fixtureInc}." . Inflector::underscore($class) ."'"; - } - } - } - } - $fixture = join(", ", $fixture); - - $import = $className; - if (isset($this->plugin)) { - $import = $this->plugin . '.' . $className; - } - - $out = "App::import('Model', '$import');\n\n"; - $out .= "class {$className}TestCase extends CakeTestCase {\n"; - $out .= "\tvar \${$className} = null;\n"; - $out .= "\tvar \$fixtures = array($fixture);\n\n"; - $out .= "\tfunction startTest() {\n"; - $out .= "\t\t\$this->{$className} =& ClassRegistry::init('{$className}');\n"; - $out .= "\t}\n\n"; - $out .= "\tfunction test{$className}Instance() {\n"; - $out .= "\t\t\$this->assertTrue(is_a(\$this->{$className}, '{$className}'));\n"; - $out .= "\t}\n\n"; - $out .= "\tfunction test{$className}Find() {\n"; - $out .= "\t\t\$this->{$className}->recursive = -1;\n"; - $out .= "\t\t\$results = \$this->{$className}->find('first');\n\t\t\$this->assertTrue(!empty(\$results));\n\n"; - $out .= "\t\t\$expected = array('$className' => array(\n$results\n\t\t));\n"; - $out .= "\t\t\$this->assertEqual(\$results, \$expected);\n"; - $out .= "\t}\n"; - $out .= "}\n"; - - $path = MODEL_TESTS; - if (isset($this->plugin)) { - $pluginPath = 'plugins' . DS . Inflector::underscore($this->plugin) . DS; - $path = APP . $pluginPath . 'tests' . DS . 'cases' . DS . 'models' . DS; - } - - $filename = Inflector::underscore($className).'.test.php'; - $this->out("\nBaking unit test for $className..."); - - $header = '$Id'; - $content = ""; - return $this->createFile($path . $filename, $content); - } - return 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'"), $modelName, $fullTableName)); + $tableIsGood = $this->in(__('Do you want to use this table?'), array('y','n'), 'y'); + } + if (strtolower($tableIsGood) == 'n') { + $useTable = $this->in(__('What is the name of the table?')); + } + return $useTable; } + /** - * outputs the a list of possible models or controllers from database + * Get an Array of all the tables in the supplied connection + * will halt the script if no tables are found. * - * @param string $useDbConfig Database configuration name - * @access public + * @param string $useDbConfig Connection name to scan. + * @return array Array of tables in the database. */ - function listAll($useDbConfig = 'default', $interactive = true) { + public 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) { - $tables = array(); foreach ($db->listSources() as $table) { if (!strncmp($table, $usePrefix, strlen($usePrefix))) { $tables[] = substr($table, strlen($usePrefix)); @@ -758,181 +835,84 @@ function listAll($useDbConfig = 'default', $interactive = true) { $tables = $db->listSources(); } if (empty($tables)) { - $this->err(__('Your database does not have any tables.', true)); + $this->err(__('Your database does not have any tables.')); $this->_stop(); } - - $this->__tables = $tables; - - if ($interactive === true) { - $this->out(__('Possible Models based on your current database:', true)); - $this->_modelNames = array(); - $count = count($tables); - for ($i = 0; $i < $count; $i++) { - $this->_modelNames[] = $this->_modelName($tables[$i]); - $this->out($i + 1 . ". " . $this->_modelNames[$i]); - } - } + 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) { + public function getName($useDbConfig = null) { $this->listAll($useDbConfig); $enteredModel = ''; while ($enteredModel == '') { - $enteredModel = $this->in(__("Enter a number from the list above, type in the name of another model, or 'q' to exit", true), null, 'q'); + $enteredModel = $this->in(__("Enter a number from the list above,\ntype in the name of another model, or 'q' to exit"), null, 'q'); if ($enteredModel === 'q') { - $this->out(__("Exit", true)); + $this->out(__('Exit')); $this->_stop(); } if ($enteredModel == '' || intval($enteredModel) > count($this->_modelNames)) { - $this->err(__("The model name you supplied was empty, or the number you selected was not an option. Please try again.", true)); + $this->err(__("The model name you supplied was empty,\nor the number you selected was not an option. Please try again.")); $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() { + public function help() { $this->hr(); $this->out("Usage: cake bake model "); $this->hr(); + $this->out('Arguments:'); + $this->out(); + $this->out(""); + $this->out("\tName of the model to bake. Can use Plugin.name"); + $this->out("\tas a shortcut for plugin baking."); + $this->out(); $this->out('Commands:'); - $this->out("\n\tmodel\n\t\tbakes model in interactive mode."); - $this->out("\n\tmodel \n\t\tbakes model file with no associations or validation"); - $this->out(""); + $this->out(); + $this->out("model"); + $this->out("\tbakes model in interactive mode."); + $this->out(); + $this->out("model "); + $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(); } + /** - * Builds the tests fixtures for the model and create the file + * Interact with FixtureTask to automatically bake fixtures when baking models. * - * @param string $model the name of the model - * @param string $useTable table name - * @return array $records, used in ModelTask::bakeTest() to create $expected - * @todo move this to a task + * @param string $className Name of class to bake fixture for + * @param string $useTable Optional table name for fixture to use. + * @return void + * @see FixtureTask::bake */ - function fixture($model, $useTable = null) { - if (!class_exists('CakeSchema')) { - App::import('Model', 'Schema'); - } - $out = "\nclass {$model}Fixture extends CakeTestFixture {\n"; - $out .= "\tvar \$name = '$model';\n"; - - if (!$useTable) { - $useTable = Inflector::tableize($model); - } else { - $out .= "\tvar \$table = '$useTable';\n"; - } - $schema = new CakeSchema(); - $data = $schema->read(array('models' => false)); - - if (!isset($data['tables'][$useTable])) { - return false; - } - $tables[$model] = $data['tables'][$useTable]; - - foreach ($tables as $table => $fields) { - if (!is_numeric($table) && $table !== 'missing') { - $out .= "\tvar \$fields = array(\n"; - $records = array(); - if (is_array($fields)) { - $cols = array(); - foreach ($fields as $field => $value) { - if ($field != 'indexes') { - if (is_string($value)) { - $type = $value; - $value = array('type'=> $type); - } - $col = "\t\t'{$field}' => array('type'=>'" . $value['type'] . "', "; - - switch ($value['type']) { - case 'integer': - $insert = 1; - break; - case 'string'; - $insert = "Lorem ipsum dolor sit amet"; - if (!empty($value['length'])) { - $insert = substr($insert, 0, (int)$value['length'] - 2); - } - $insert = "'$insert'"; - break; - case 'datetime': - $ts = date('Y-m-d H:i:s'); - $insert = "'$ts'"; - break; - case 'date': - $ts = date('Y-m-d'); - $insert = "'$ts'"; - break; - case 'time': - $ts = date('H:i:s'); - $insert = "'$ts'"; - break; - case 'boolean': - $insert = 1; - break; - case 'text': - $insert = - "'Lorem ipsum dolor sit amet, aliquet feugiat. Convallis morbi fringilla gravida,"; - $insert .= "phasellus feugiat dapibus velit nunc, pulvinar eget sollicitudin venenatis cum nullam,"; - $insert .= "vivamus ut a sed, mollitia lectus. Nulla vestibulum massa neque ut et, id hendrerit sit,"; - $insert .= "feugiat in taciti enim proin nibh, tempor dignissim, rhoncus duis vestibulum nunc mattis convallis.'"; - break; - } - $records[] = "\t\t'$field' => $insert"; - unset($value['type']); - $col .= join(', ', $schema->__values($value)); - } else { - $col = "\t\t'indexes' => array("; - $props = array(); - foreach ((array)$value as $key => $index) { - $props[] = "'{$key}' => array(" . join(', ', $schema->__values($index)) . ")"; - } - $col .= join(', ', $props); - } - $col .= ")"; - $cols[] = $col; - } - $out .= join(",\n", $cols); - } - $out .= "\n\t);\n"; - } - } - $records = join(",\n", $records); - $out .= "\tvar \$records = array(array(\n$records\n\t));\n"; - $out .= "}\n"; - $path = TESTS . DS . 'fixtures' . DS; - if (isset($this->plugin)) { - $pluginPath = 'plugins' . DS . Inflector::underscore($this->plugin) . DS; - $path = APP . $pluginPath . 'tests' . DS . 'fixtures' . DS; - } - $filename = Inflector::underscore($model) . '_fixture.php'; - $header = '$Id'; - $content = ""; - $this->out("\nBaking test fixture for $model..."); - if ($this->createFile($path . $filename, $content)) { - return str_replace("\t\t", "\t\t\t", $records); - } - return false; + public 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); } } ?> \ No newline at end of file diff --git a/cake/console/libs/tasks/plugin.php b/cake/console/libs/tasks/plugin.php index a23181322..255d3385f 100644 --- a/cake/console/libs/tasks/plugin.php +++ b/cake/console/libs/tasks/plugin.php @@ -1,32 +1,23 @@ path = APP . 'plugins' . DS; } + /** * Execution method always used for tasks * * @return void */ - function execute() { + public function execute() { if (empty($this->params['skel'])) { $this->params['skel'] = ''; - if (is_dir(CAKE_CORE_INCLUDE_PATH.DS.'cake'.DS.'console'.DS.'libs'.DS.'templates'.DS.'skel') === true) { - $this->params['skel'] = CAKE_CORE_INCLUDE_PATH.DS.'cake'.DS.'console'.DS.'libs'.DS.'templates'.DS.'skel'; + 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 = Inflector::underscore($plugin) . DS; + $pluginPath = $this->_pluginPath($plugin); $this->Dispatch->shiftArgs(); - if (is_dir($this->path . $pluginPath)) { - $this->out(sprintf('Plugin: %s', $plugin)); - $this->out(sprintf('Path: %s', $this->path . $pluginPath)); - $this->hr(); + if (is_dir($pluginPath)) { + $this->out(sprintf(__('Plugin: %s'), $plugin)); + $this->out(sprintf(__('Path: %s'), $pluginPath)); } elseif (isset($this->args[0])) { - $this->err(sprintf('%s in path %s not found.', $plugin, $this->path . $pluginPath)); + $this->err(sprintf(__('%s in path %s not found.'), $plugin, $pluginPath)); $this->_stop(); } else { - $this->__interactive($plugin); + $this->_interactive($plugin); } + } else { + return $this->_interactive(); } if (isset($this->args[0])) { @@ -90,13 +85,13 @@ function execute() { $this->Dispatch->shiftArgs(); if (in_array($task, $this->tasks)) { $this->{$task}->plugin = $plugin; - $this->{$task}->path = $this->path . $pluginPath . Inflector::underscore(Inflector::pluralize($task)) . DS; + $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->err(sprintf(__("%s directory could not be found.\nBe sure you have created %s"), $task, $this->{$task}->path)); } $this->{$task}->loadTasks(); - $this->{$task}->execute(); + return $this->{$task}->execute(); } } } @@ -107,13 +102,13 @@ function execute() { * @access private * @return void */ - function __interactive($plugin = null) { + protected function _interactive($plugin = null) { while ($plugin === null) { - $plugin = $this->in(__('Enter the name of the plugin in CamelCase format', true)); + $plugin = $this->in(__('Enter the name of the plugin in CamelCase format')); } if (!$this->bake($plugin)) { - $this->err(sprintf(__("An error occured trying to bake: %s in %s", true), $plugin, $this->path . $pluginPath)); + $this->err(sprintf(__("An error occured trying to bake: %s in %s"), $plugin, $this->path . Inflector::underscore($pluginPath))); } } @@ -124,29 +119,50 @@ function __interactive($plugin = null) { * @access public * @return bool */ - function bake($plugin) { - + public function bake($plugin) { $pluginPath = Inflector::underscore($plugin); + $pathOptions = App::path('plugins'); + if (count($pathOptions) > 1) { + $this->findPath($pathOptions); + } $this->hr(); - $this->out("Plugin Name: $plugin"); - $this->out("Plugin Directory: {$this->path}{$pluginPath}"); + $this->out(sprintf(__("Plugin Name: %s"), $plugin)); + $this->out(sprintf(__("Plugin Directory: %s"), $this->path . $pluginPath)); $this->hr(); - - $looksGood = $this->in('Look okay?', array('y', 'n', 'q'), 'y'); - - if (strtolower($looksGood) == 'y' || strtolower($looksGood) == 'yes') { - $verbose = $this->in(__('Do you want verbose output?', true), array('y', 'n'), 'n'); - - $Folder = new Folder($this->path . $pluginPath); - $directories = array('models' . DS . 'behaviors', 'controllers' . DS . 'components', 'views' . DS . 'helpers'); + $looksGood = $this->in(__('Look okay?'), array('y', 'n', 'q'), 'y'); + + if (strtolower($looksGood) == 'y') { + $verbose = $this->in(__('Do you want verbose output?'), 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) { - $Folder->create($this->path . $pluginPath . DS . $directory); + $dirPath = $this->path . $pluginPath . DS . $directory; + $Folder->create($dirPath); + $File =& new File($dirPath . DS . 'empty', true); } - if (strtolower($verbose) == 'y' || strtolower($verbose) == 'yes') { + if (strtolower($verbose) == 'y') { foreach ($Folder->messages() as $message) { $this->out($message); } @@ -174,28 +190,57 @@ function bake($plugin) { $this->createFile($this->path . $pluginPath . DS . $modelFileName, $out); $this->hr(); - $this->out(sprintf(__("Created: %s in %s", true), $plugin, $this->path . $pluginPath)); + $this->out(sprintf(__('Created: %s in %s'), $plugin, $this->path . $pluginPath)); $this->hr(); } return true; } + +/** + * find and change $this->path to the user selection + * + * @return string plugin path + */ + public 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.'); + $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() { + public function help() { $this->hr(); $this->out("Usage: cake bake plugin ..."); $this->hr(); $this->out('Commands:'); - $this->out("\n\tplugin \n\t\tbakes plugin directory structure"); - $this->out("\n\tplugin model\n\t\tbakes model. Run 'cake bake model help' for more info."); - $this->out("\n\tplugin controller\n\t\tbakes controller. Run 'cake bake controller help' for more info."); - $this->out("\n\tplugin view\n\t\tbakes view. Run 'cake bake view help' for more info."); - $this->out(""); + $this->out(); + $this->out("plugin "); + $this->out("\tbakes plugin directory structure"); + $this->out(); + $this->out("plugin model"); + $this->out("\tbakes model. Run 'cake bake model help' for more info."); + $this->out(); + $this->out("plugin controller"); + $this->out("\tbakes controller. Run 'cake bake controller help' for more info."); + $this->out(); + $this->out("plugin view"); + $this->out("\tbakes view. Run 'cake bake view help' for more info."); + $this->out(); $this->_stop(); } } diff --git a/cake/console/libs/tasks/project.php b/cake/console/libs/tasks/project.php index f7a0c3021..36ae4de1d 100644 --- a/cake/console/libs/tasks/project.php +++ b/cake/console/libs/tasks/project.php @@ -1,32 +1,23 @@ args[0])) { $project = $this->args[0]; - $this->Dispatch->shiftArgs(); } } @@ -56,19 +53,22 @@ function execute($project = null) { if (empty($this->params['skel'])) { $this->params['skel'] = ''; - if (is_dir(CAKE_CORE_INCLUDE_PATH.DS.'cake'.DS.'console'.DS.'libs'.DS.'templates'.DS.'skel') === true) { - $this->params['skel'] = CAKE_CORE_INCLUDE_PATH.DS.'cake'.DS.'console'.DS.'libs'.DS.'templates'.DS.'skel'; + if (is_dir(CAKE . 'console' . DS . 'templates' . DS . 'skel') === true) { + $this->params['skel'] = CAKE . 'console' . DS . 'templates' . DS . 'skel'; } } while (!$project) { - $project = $this->in("What is the full path for this app including the app directory name?\nExample: ".$this->params['working'] . DS . "myapp", null, $this->params['working'] . DS . 'myapp'); + $prompt = __("What is the full path for this app including the app directory name?\n Example:"); + $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')) { - $response = $this->in('A project already exists in this location: '.$project.' Overwrite?', array('y','n'), 'n'); + $prompt = sprintf(__('A project already exists in this location: %s Overwrite?'), $project); + $response = $this->in($prompt, array('y','n'), 'n'); if (strtolower($response) === 'n') { $response = $project = false; } @@ -78,29 +78,35 @@ function execute($project = null) { if ($this->bake($project)) { $path = Folder::slashTerm($project); if ($this->createHome($path)) { - $this->out(__('Welcome page created', true)); + $this->out(__('Welcome page created')); } else { - $this->out(__('The Welcome page was NOT created', true)); + $this->out(__('The Welcome page was NOT created')); } if ($this->securitySalt($path) === true ) { - $this->out(__('Random hash key created for \'Security.salt\'', true)); + $this->out(__('Random hash key created for \'Security.salt\'')); } else { - $this->err(sprintf(__('Unable to generate random hash for \'Security.salt\', you should change it in %s', true), CONFIGS . 'core.php')); + $this->err(sprintf(__('Unable to generate random hash for \'Security.salt\', you should change it in %s'), CONFIGS . 'core.php')); + } + + if ($this->securityCipherSeed($path) === true ) { + $this->out(__('Random seed created for \'Security.cipherSeed\'')); + } else { + $this->err(sprintf(__('Unable to generate random seed for \'Security.cipherSeed\', you should change it in %s'), 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)); + $this->out(sprintf(__('CAKE_CORE_INCLUDE_PATH set to %s in webroot/index.php'), CAKE_CORE_INCLUDE_PATH)); + $this->out(sprintf(__('CAKE_CORE_INCLUDE_PATH set to %s in webroot/test.php'), CAKE_CORE_INCLUDE_PATH)); + $this->out(__('Remember to check these value after moving to production server')); } 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')); + $this->err(sprintf(__('Unable to set CAKE_CORE_INCLUDE_PATH, you should change it in %s'), $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->err(sprintf(__('Could not set permissions on %s'), $path . DS .'tmp')); + $this->out(sprintf(__('chmod -R 0777 %s'), $path . DS .'tmp')); } $this->params['working'] = $path; @@ -108,6 +114,7 @@ function execute($project = null) { 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 @@ -123,80 +130,83 @@ 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)); + $this->out(__('The directory path you supplied was empty. Please try again.')); } else { while (is_dir($skel) === false) { - $skel = $this->in(__('Directory path does not exist please choose another:', true)); + $skel = $this->in(__('Directory path does not exist please choose another:')); } } } $app = basename($path); - $this->out('Bake Project'); - $this->out("Skel Directory: $skel"); - $this->out("Will be copied to: {$path}"); + $this->out(__('Bake Project')); + $this->out(__('Skel Directory: ') . $skel); + $this->out(__('Will be copied to: ') . $path); $this->hr(); - $looksGood = $this->in('Look okay?', array('y', 'n', 'q'), 'y'); + $looksGood = $this->in(__('Look okay?'), array('y', 'n', 'q'), 'y'); - if (strtolower($looksGood) == 'y' || strtolower($looksGood) == 'yes') { - $verbose = $this->in(__('Do you want verbose output?', true), array('y', 'n'), 'n'); + if (strtolower($looksGood) == 'y') { + $verbose = $this->in(__('Do you want verbose output?'), 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->out(sprintf(__('Created: %s in %s'), $app, $path)); $this->hr(); } else { - $this->err(" '" . $app . "' could not be created properly"); + $this->err(sprintf(__(" '%s' could not be created properly"), $app)); return false; } - if (strtolower($verbose) == 'y' || strtolower($verbose) == 'yes') { + if (strtolower($verbose) == 'y') { foreach ($Folder->messages() as $message) { $this->out($message); } } return true; - } elseif (strtolower($looksGood) == 'q' || strtolower($looksGood) == 'quit') { - $this->out('Bake Aborted.'); + } elseif (strtolower($looksGood) == 'q') { + $this->out(__('Bake Aborted.')); } else { $this->execute(false); return false; } } + /** * Writes a file with a default home page to the project. * * @param string $dir Path to project * @return boolean Success - * @access public */ - function createHome($dir) { + public function createHome($dir) { $app = basename($dir); $path = $dir . 'views' . DS . 'pages' . DS; - include(CAKE_CORE_INCLUDE_PATH.DS.'cake'.DS.'console'.DS.'libs'.DS.'templates'.DS.'views'.DS.'home.ctp'); + $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) { + public function securitySalt($path) { $File =& new File($path . 'config' . DS . 'core.php'); $contents = $File->read(); if (preg_match('/([\\t\\x20]*Configure::write\\(\\\'Security.salt\\\',[\\t\\x20\'A-z0-9]*\\);)/', $contents, $match)) { if (!class_exists('Security')) { - uses('Security'); + require LIBS . 'security.php'; } $string = Security::generateAuthKey(); $result = str_replace($match[0], "\t" . 'Configure::write(\'Security.salt\', \''.$string.'\');', $contents); @@ -207,19 +217,43 @@ function securitySalt($path) { } return false; } + + /** + * Generates and writes 'Security.cipherSeed' + * + * @param string $path Project path + * @return boolean Success + */ + 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) { + public function corePath($path) { if (dirname($path) !== CAKE_CORE_INCLUDE_PATH) { $File =& new File($path . 'webroot' . DS . 'index.php'); $contents = $File->read(); if (preg_match('/([\\t\\x20]*define\\(\\\'CAKE_CORE_INCLUDE_PATH\\\',[\\t\\x20\'A-z0-9]*\\);)/', $contents, $match)) { - $result = str_replace($match[0], "\t\tdefine('CAKE_CORE_INCLUDE_PATH', '" . CAKE_CORE_INCLUDE_PATH . "');", $contents); + $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; } @@ -230,7 +264,7 @@ function corePath($path) { $File =& new File($path . 'webroot' . DS . 'test.php'); $contents = $File->read(); if (preg_match('/([\\t\\x20]*define\\(\\\'CAKE_CORE_INCLUDE_PATH\\\',[\\t\\x20\'A-z0-9]*\\);)/', $contents, $match)) { - $result = str_replace($match[0], "\t\tdefine('CAKE_CORE_INCLUDE_PATH', '" . CAKE_CORE_INCLUDE_PATH . "');", $contents); + $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; } @@ -240,20 +274,21 @@ function corePath($path) { return true; } } + /** - * Enables Configure::read('Routing.admin') in /app/config/core.php + * 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) { - $File =& new File(CONFIGS . 'core.php'); + 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.admin\',[\\t\\x20\'a-z]*\\);)%', $contents, $match)) { - $result = str_replace($match[0], "\t" . 'Configure::write(\'Routing.admin\', \''.$name.'\');', $contents); + 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.admin', $name); + Configure::write('Routing.prefixes', array($name)); return true; } else { return false; @@ -262,19 +297,66 @@ function cakeAdmin($name) { return false; } } + +/** + * Checks for Configure::read('Routing.prefixes') and forces user to input it if not enabled + * + * @return string Admin route to use + */ + 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')); + } + $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.'), $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?')); + $this->out(__('Example: www.example.com/admin/controller')); + while ($admin == '') { + $admin = $this->in(__('Enter a routing prefix:'), null, 'admin'); + } + if ($this->cakeAdmin($admin) !== true) { + $this->out(__('Unable to write to /app/config/core.php.')); + $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() { + public function help() { $this->hr(); $this->out("Usage: cake bake project "); $this->hr(); $this->out('Commands:'); - $this->out("\n\tproject \n\t\tbakes app directory structure.\n\t\tif begins with '/' path is absolute."); - $this->out(""); + $this->out(); + $this->out("project "); + $this->out("\tbakes app directory structure."); + $this->out("\tif begins with '/' path is absolute."); + $this->out(); $this->_stop(); } diff --git a/cake/console/libs/tasks/template.php b/cake/console/libs/tasks/template.php new file mode 100644 index 000000000..5c32e19a3 --- /dev/null +++ b/cake/console/libs/tasks/template.php @@ -0,0 +1,212 @@ + $path + * + * @var array + */ + public $templatePaths = array(); + +/** + * Initialize callback. Setup paths for the template task. + * + * @access public + * @return void + */ + public 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. + */ + protected function _findThemes() { + $paths = App::path('shells'); + $core = array_pop($paths); + $core = str_replace('libs' . DS, '', $core); + $paths[] = $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; + } + + // 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 + */ + 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; + } + + foreach ($data as $name => $value) { + $this->templateVars[$name] = $value; + } + } + +/** + * 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 + */ + public 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. + */ + public 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.')); + $this->out(__('Please choose the template set you wish to use:')); + $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?'), 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. + */ + public 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'), $filename)); + return false; + } +} +?> \ No newline at end of file diff --git a/cake/console/libs/tasks/test.php b/cake/console/libs/tasks/test.php index 416d3bd22..a1765c584 100644 --- a/cake/console/libs/tasks/test.php +++ b/cake/console/libs/tasks/test.php @@ -1,203 +1,441 @@ args)) { - $this->__interactive(); + $this->_interactive(); } if (count($this->args) == 1) { - $this->__interactive($this->args[0]); + $this->_interactive($this->args[0]); } if (count($this->args) > 1) { - $class = Inflector::underscore($this->args[0]); - if ($this->bake($class, $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($class = null) { + protected function _interactive($type = null) { + $this->interactive = true; $this->hr(); - $this->out(sprintf("Bake Tests\nPath: %s", $this->path)); + $this->out(__('Bake Tests')); + $this->out(sprintf(__('Path: %s'), $this->path)); $this->hr(); - $key = null; - $options = array('Behavior', 'Helper', 'Component', 'Model', 'Controller'); - - if ($class !== null) { - $class = Inflector::camelize($class); - if (in_array($class, $options)) { - $key = array_search($class); + $selection = null; + if ($type) { + $type = Inflector::camelize($type); + if (!in_array($type, $this->classTypes)) { + unset($type); } } + if (!$type) { + $type = $this->getObjectType(); + } + $className = $this->getClassName($type); + return $this->bake($type, $className); + } - while ($class == null) { - $cases = array(); - $this->hr(); - $this->out("Select a class:"); - $this->hr(); +/** + * 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 + */ + public function bake($type, $className) { + if ($this->typeCanDetectFixtures($type) && $this->isLoadableClass($type, $className)) { + $this->out(__('Bake is detecting possible fixtures..')); + $testSubject =& $this->buildTestSubject($type, $className); + $this->generateFixtureList($testSubject); + } elseif ($this->interactive) { + $this->getUserFixtures(); + } + $fullClassName = $this->getRealClassName($type, $className); - $keys = array(); - foreach ($options as $key => $option) { - $this->out(++$key . '. ' . $option); - $keys[] = $key; - } - $keys[] = 'q'; + $methods = array(); + if (class_exists($fullClassName)) { + $methods = $this->getTestableMethods($fullClassName); + } + $mock = $this->hasMockClass($type, $fullClassName); + $construction = $this->generateConstructor($type, $fullClassName); - $key = $this->in(__("Enter the class to test or (q)uit", true), $keys, 'q'); + $plugin = null; + if ($this->plugin) { + $plugin = $this->plugin . '.'; + } - if ($key != 'q') { - if (isset($options[--$key])) { - $class = $options[$key]; - } + $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'); - if ($class) { - $name = $this->in(__("Enter the name for the test or (q)uit", true), null, 'q'); - if ($name !== 'q') { - $case = null; - while ($case !== 'q') { - $case = $this->in(__("Enter a test case or (q)uit", true), null, 'q'); - if ($case !== 'q') { - $cases[] = $case; - } - } - if ($this->bake($class, $name, $cases)) { - $this->out(__("Test baked\n", true)); - $type = null; - } - $class = null; - } - } - } else { - $this->_stop(); - } + $filename = $this->testCaseFileName($type, $className); + $made = $this->createFile($filename, $out); + if ($made) { + return $out; } + return false; } + /** - * Writes File + * Interact with the user and get their chosen type. Can exit the script. * - * @access public + * @return string Users chosen type. */ - function bake($class, $name = null, $cases = array()) { - if (!$name) { - return false; + public function getObjectType() { + $this->hr(); + $this->out(__('Select an object type:')); + $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'), $keys, 'q'); + if ($selection == 'q') { + return $this->_stop(); + } + return $this->classTypes[$selection - 1]; + } - if (!is_array($cases)) { - $cases = array($cases); +/** + * 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. + */ + public function getClassName($objectType) { + $options = App::objects(strtolower($objectType)); + $this->out(sprintf(__('Choose a %s class'), $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')); + 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 + */ + public function typeCanDetectFixtures($type) { + $type = strtolower($type); + return ($type == 'controller' || $type == 'model'); + } - if (strpos($this->path, $class) === false) { - $this->filePath = $this->path . 'cases' . DS . Inflector::tableize($class) . DS; +/** + * 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 + */ + 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. + */ + 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; + } - $class = Inflector::classify($class); - $name = Inflector::classify($name); +/** + * 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 + */ + public function getRealClassName($type, $class) { + if (strtolower($type) == 'model') { + return $class; + } + return $class . $type; + } - $import = $name; - if (isset($this->plugin)) { - $import = $this->plugin . '.' . $name; +/** + * 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. + */ + 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; + } } - $extras = $this->__extras($class); - $out = "App::import('$class', '$import');\n"; - if ($class == 'Model') { - $class = null; + 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. + */ + public function generateFixtureList(&$subject) { + $this->_fixtures = array(); + if (is_a($subject, 'Model')) { + $this->_processModel($subject); + } elseif (is_a($subject, 'Controller')) { + $this->_processController($subject); } - $out .= "class Test{$name} extends {$name}{$class} {\n"; - $out .= "{$extras}"; - $out .= "}\n\n"; - $out .= "class {$name}{$class}Test extends CakeTestCase {\n"; - $out .= "\n\tfunction startTest() {"; - $out .= "\n\t\t\$this->{$name} = new Test{$name}();"; - $out .= "\n\t}\n"; - $out .= "\n\tfunction test{$name}Instance() {\n"; - $out .= "\t\t\$this->assertTrue(is_a(\$this->{$name}, '{$name}{$class}'));\n\t}\n"; - foreach ($cases as $case) { - $case = Inflector::classify($case); - $out .= "\n\tfunction test{$case}() {\n\n\t}\n"; + 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 + */ + 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}); + } + } } - $out .= "}\n"; + } - $this->out("Baking unit test for $name..."); - $this->out($out); - $ok = $this->in(__('Is this correct?', true), array('y', 'n'), 'y'); - if ($ok == 'n') { - return false; +/** + * 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 + */ + 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}); } + } - $header = '$Id'; - $content = ""; - return $this->createFile($this->filePath . Inflector::underscore($name) . '.test.php', $content); +/** + * 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 + */ + 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; } + /** - * Handles the extra stuff needed + * Interact with the user to get additional fixtures they want to use. * - * @access private + * @return array Array of fixtures the user wants to add. + */ + public function getUserFixtures() { + $proceed = $this->in(__('Bake could not detect fixtures, would you like to add some?'), 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'")); + $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 + */ + 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. */ - function __extras($class) { - $extras = null; - switch ($class) { - case 'Model': - $extras = "\n\tvar \$cacheSources = false;"; - $extras .= "\n\tvar \$useDbConfig = 'test_suite';\n"; - break; + public function generateConstructor($type, $fullClassName) { + $type = strtolower($type); + if ($type == 'model') { + return "ClassRegistry::init('$fullClassName');\n"; } - return $extras; + 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. + */ + 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 + */ + public function help() { + $this->hr(); + $this->out("Usage: cake bake test "); + $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 Can be any of the following 'controller', 'model', 'helper',\n\t'component', 'behavior'."); + $this->out("\t 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(); } } ?> \ No newline at end of file diff --git a/cake/console/libs/tasks/view.php b/cake/console/libs/tasks/view.php index cd60df611..70053ffd8 100644 --- a/cake/console/libs/tasks/view.php +++ b/cake/console/libs/tasks/view.php @@ -1,226 +1,259 @@ args)) { - $this->__interactive(); + $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); - if (isset($this->args[0])) { - $controller = $action = $alias = null; - $this->controllerName = Inflector::camelize($this->args[0]); - $this->controllerPath = Inflector::underscore($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[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(); - if (isset($this->args[2])) { - $action = $this->args[2]; + foreach ($methods as $method) { + $content = $this->getContent($method, $vars); + if ($content) { + $this->bake($method, $content); } + } + } - if (!$action) { - $action = $this->template; +/** + * Get a list of actions that can / should have views baked for them. + * + * @return array Array of action names that should be baked + */ + protected 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; + } - if (in_array($action, $this->scaffoldActions)) { - $this->bake($action, true); - } elseif ($action) { - $this->bake($action, true); - } else { +/** + * Bake All views for All controllers. + * + * @return void + */ + public 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 ($vars) { - - $methods = array_diff( - array_map('strtolower', get_class_methods($this->controllerName . 'Controller')), - array_map('strtolower', get_class_methods('appcontroller')) - ); - if (empty($methods)) { - $methods = $this->scaffoldActions; - } - $adminDelete = null; - - $adminRoute = Configure::read('Routing.admin'); - if (!empty($adminRoute)) { - $adminDelete = $adminRoute.'_delete'; - } - foreach ($methods as $method) { - if ($method{0} != '_' && !in_array($method, array('delete', $adminDelete))) { - $content = $this->getContent($method, $vars); - $this->bake($method, $content); - } - } + if (!$actions) { + $actions = $this->_methodsToBake(); } + $this->bakeActions($actions, $vars); } } } + /** * Handles interactive baking * - * @access private */ - function __interactive() { + protected function _interactive() { $this->hr(); $this->out(sprintf("Bake View\nPath: %s", $this->path)); $this->hr(); - $wannaDoAdmin = 'n'; - $wannaDoScaffold = 'y'; - $this->interactive = false; + $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)); - $interactive = $this->in("Would you like bake to build your views interactively?\nWarning: Choosing no will overwrite {$this->controllerName} views if it exist.", array('y','n'), 'y'); + $prompt = sprintf(__("Would you like bake to build your views interactively?\nWarning: Choosing no will overwrite %s views if it exist."), $this->controllerName); + $interactive = $this->in($prompt, array('y', 'n'), 'n'); - if (strtolower($interactive) == 'y' || strtolower($interactive) == 'yes') { - $this->interactive = true; - $wannaDoScaffold = $this->in("Would you like to create some scaffolded views (index, add, view, edit) for this controller?\nNOTE: Before doing so, you'll need to create your controller and model classes (including associated models).", array('y','n'), 'n'); + if (strtolower($interactive) == 'n') { + $this->interactive = false; } - if (strtolower($wannaDoScaffold) == 'y' || strtolower($wannaDoScaffold) == 'yes') { - $wannaDoAdmin = $this->in("Would you like to create the views for admin routing?", array('y','n'), 'y'); - } - $admin = 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)."); + $wannaDoScaffold = $this->in($prompt, array('y','n'), 'y'); - if ((strtolower($wannaDoAdmin) == 'y' || strtolower($wannaDoAdmin) == 'yes')) { - $admin = $this->getAdmin(); - } + $wannaDoAdmin = $this->in(__("Would you like to create the views for admin routing?"), array('y','n'), 'n'); - if (strtolower($wannaDoScaffold) == 'y' || strtolower($wannaDoScaffold) == 'yes') { - $actions = $this->scaffoldActions; - if ($admin) { - foreach ($actions as $action) { - $actions[] = $admin . $action; - } - } + if (strtolower($wannaDoScaffold) == 'y' || strtolower($wannaDoAdmin) == 'y') { $vars = $this->__loadController(); - if ($vars) { - foreach ($actions as $action) { - $content = $this->getContent($action, $vars); - $this->bake($action, $content); - } + if (strtolower($wannaDoScaffold) == 'y') { + $actions = $this->scaffoldActions; + $this->bakeActions($actions, $vars); } - $this->hr(); - $this->out(''); - $this->out('View Scaffolding Complete.'."\n"); - } else { - $action = ''; - while ($action == '') { - $action = $this->in('Action Name? (use camelCased function name)'); - if ($action == '') { - $this->out('The action name you supplied was empty. Please try again.'); + 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->out(''); - $this->hr(); - $this->out('The following view will be created:'); $this->hr(); - $this->out("Controller Name: {$this->controllerName}"); - $this->out("Action Name: {$action}"); - $this->out("Path: ".$this->params['app'] . DS . $this->controllerPath . DS . Inflector::underscore($action) . ".ctp"); - $this->hr(); - $looksGood = $this->in('Look okay?', array('y','n'), 'y'); - if (strtolower($looksGood) == 'y' || strtolower($looksGood) == 'yes') { - $this->bake($action); - $this->_stop(); - } else { - $this->out('Bake Aborted.'); - } + $this->out(); + $this->out(__("View Scaffolding Complete.\n")); + } else { + $this->customAction(); } } + /** * Loads Controller and sets variables for the template * Available template variables @@ -231,9 +264,9 @@ function __interactive() { * @return array Returns an variables to be made available to a view template * @access private */ - function __loadController() { + private function __loadController() { if (!$this->controllerName) { - $this->err(__('Controller not found', true)); + $this->err(__('Controller not found')); } $import = $this->controllerName; @@ -243,133 +276,193 @@ function __loadController() { 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->err(sprintf(__("The file '%s' could not be found.\nIn order to bake a view, you'll need to first create the controller."), $file)); $this->_stop(); } $controllerClassName = $this->controllerName . 'Controller'; - $controllerObj = & new $controllerClassName(); + $controllerObj =& new $controllerClassName(); + $controllerObj->plugin = $this->plugin; $controllerObj->constructClasses(); $modelClass = $controllerObj->modelClass; - $modelObj =& ClassRegistry::getObject($controllerObj->modelKey); + $modelObj =& $controllerObj->{$controllerObj->modelClass}; if ($modelObj) { $primaryKey = $modelObj->primaryKey; $displayField = $modelObj->displayField; $singularVar = Inflector::variable($modelClass); - $pluralVar = Inflector::variable($this->controllerName); - $singularHumanName = Inflector::humanize($modelClass); - $pluralHumanName = Inflector::humanize($this->controllerName); - $schema = $modelObj->schema(); + $singularHumanName = $this->_singularHumanName($modelClass); + $schema = $modelObj->schema(true); $fields = array_keys($schema); $associations = $this->__associations($modelObj); } else { - $primaryKey = null; - $displayField = null; + $primaryKey = $displayField = null; $singularVar = Inflector::variable(Inflector::singularize($this->controllerName)); - $pluralVar = Inflector::variable($this->controllerName); - $singularHumanName = Inflector::humanize(Inflector::singularize($this->controllerName)); - $pluralHumanName = Inflector::humanize($this->controllerName); - $fields = array(); - $schema = array(); - $associations = array(); + $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 + */ + public 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 + */ + public function customAction() { + $action = ''; + while ($action == '') { + $action = $this->in(__('Action Name? (use lowercase_underscored function name)')); + if ($action == '') { + $this->out(__('The action name you supplied was empty. Please try again.')); + } + } + $this->out(); + $this->hr(); + $this->out(__('The following view will be created:')); + $this->hr(); + $this->out(sprintf(__('Controller Name: %s'), $this->controllerName)); + $this->out(sprintf(__('Action Name: %s'), $action)); + $this->out(sprintf(__('Path: %s'), $this->params['app'] . DS . $this->controllerPath . DS . Inflector::underscore($action) . ".ctp")); + $this->hr(); + $looksGood = $this->in(__('Look okay?'), array('y','n'), 'y'); + if (strtolower($looksGood) == 'y') { + $this->bake($action); + $this->_stop(); + } else { + $this->out(__('Bake Aborted.')); + } + } + /** * 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 = '') { + public function bake($action, $content = '') { if ($content === true) { - $content = $this->getContent(); - } - $filename = $this->path . $this->controllerPath . DS . Inflector::underscore($action) . '.ctp'; - $Folder =& new Folder($this->path . $this->controllerPath, true); - $errors = $Folder->errors(); - if (empty($errors)) { - $path = $Folder->slashTerm($Folder->pwd()); - return $this->createFile($filename, $content); - } else { - foreach ($errors as $error) { - $this->err($error); - } + $content = $this->getContent($action); } - 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 $template file to use + * @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($template = null, $vars = null) { - if (!$template) { - $template = $this->template; + public function getContent($action, $vars = null) { + if (!$vars) { + $vars = $this->__loadController(); } - $action = $template; - $adminRoute = Configure::read('Routing.admin'); - if (!empty($adminRoute) && strpos($template, $adminRoute) !== false) { - $template = str_replace($adminRoute.'_', '', $template); + $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); } - if (in_array($template, array('add', 'edit'))) { - $action = $template; - $template = 'form'; + return false; + } + +/** + * Gets the template name based on the action name + * + * @param string $action name + * @return string template name + */ + public function getTemplate($action) { + if ($action != $this->template && in_array($action, $this->noTemplateActions)) { + return false; } - $loaded = false; - foreach ($this->Dispatch->shellPaths as $path) { - $templatePath = $path . 'templates' . DS . 'views' . DS .Inflector::underscore($template).'.ctp'; - if (file_exists($templatePath) && is_file($templatePath)) { - $loaded = true; - break; + if (!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 (!$vars) { - $vars = $this->__loadController(); - } - if ($loaded) { - extract($vars); - ob_start(); - ob_implicit_flush(0); - include($templatePath); - $content = ob_get_clean(); - return $content; + if (in_array($template, array('add', 'edit'))) { + $template = 'form'; + } elseif (preg_match('@(_add|_edit)$@', $template)) { + $template = str_replace(array('_add', '_edit'), '_form', $template); } - $this->hr(); - $this->err(sprintf(__('Template for %s could not be found', true), $template)); - return false; + return $template; } + /** * Displays help contents * - * @access public */ - function help() { + public function help() { $this->hr(); $this->out("Usage: cake bake view ..."); $this->hr(); + $this->out('Arguments:'); + $this->out(); + $this->out(""); + $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(""); + $this->out("\tName of the action view to bake"); + $this->out(); $this->out('Commands:'); - $this->out("\n\tview \n\t\twill read the given controller for methods\n\t\tand bake corresponding views.\n\t\tIf var scaffold is found it will bake the scaffolded actions\n\t\t(index,view,add,edit)"); - $this->out("\n\tview \n\t\twill bake a template. core templates: (index, add, edit, view)"); - $this->out("\n\tview