a grand renaming so that the most significant portion of the name comes first

This commit is contained in:
Dan Buch
2012-03-03 21:45:20 -05:00
parent c8b8078175
commit f4f448926d
1300 changed files with 0 additions and 234 deletions

View File

@@ -0,0 +1,60 @@
<?php
/**
* Base class for Bake Tasks.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc.
*
* 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 CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.console.libs.tasks
* @since CakePHP(tm) v 1.3
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
class BakeTask extends Shell {
/**
* Name of plugin
*
* @var string
* @access public
*/
var $plugin = null;
/**
* The db connection being used for baking
*
* @var string
* @access public
*/
var $connection = null;
/**
* Flag for interactive mode
*
* @var boolean
*/
var $interactive = false;
/**
* Gets the path for output. Checks the plugin property
* and returns the correct path.
*
* @return string Path to output.
* @access public
*/
function getPath() {
$path = $this->path;
if (isset($this->plugin)) {
$name = substr($this->name, 0, strlen($this->name) - 4);
$path = $this->_pluginPath($this->plugin) . Inflector::pluralize(Inflector::underscore($name)) . DS;
}
return $path;
}
}

View File

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

View File

@@ -0,0 +1,368 @@
<?php
/**
* The DbConfig Task handles creating and updating the database.php
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-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 CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.console.libs.tasks
* @since CakePHP(tm) v 1.2
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Task class for creating and updating the database configuration file.
*
* @package cake
* @subpackage cake.cake.console.libs.tasks
*/
class DbConfigTask extends Shell {
/**
* path to CONFIG directory
*
* @var string
* @access public
*/
var $path = null;
/**
* Default configuration settings to use
*
* @var array
* @access private
*/
var $__defaultConfig = array(
'name' => 'default', 'driver'=> 'mysql', 'persistent'=> 'false', 'host'=> 'localhost',
'login'=> 'root', 'password'=> 'password', 'database'=> 'project_name',
'schema'=> null, 'prefix'=> null, 'encoding' => null, 'port' => null
);
/**
* String name of the database config class name.
* Used for testing.
*
* @var string
*/
var $databaseClassName = 'DATABASE_CONFIG';
/**
* initialization callback
*
* @var string
* @access public
*/
function initialize() {
$this->path = $this->params['working'] . DS . 'config' . DS;
}
/**
* Execution method always used for tasks
*
* @access public
*/
function execute() {
if (empty($this->args)) {
$this->__interactive();
$this->_stop();
}
}
/**
* Interactive interface
*
* @access private
*/
function __interactive() {
$this->hr();
$this->out('Database Configuration:');
$this->hr();
$done = false;
$dbConfigs = array();
while ($done == false) {
$name = '';
while ($name == '') {
$name = $this->in("Name:", null, 'default');
if (preg_match('/[^a-z0-9_]/i', $name)) {
$name = '';
$this->out('The name may only contain unaccented latin characters, numbers or underscores');
} else if (preg_match('/^[^a-z_]/i', $name)) {
$name = '';
$this->out('The name must start with an unaccented latin character or an underscore');
}
}
$driver = $this->in('Driver:', array('db2', 'firebird', 'mssql', 'mysql', 'mysqli', 'odbc', 'oracle', 'postgres', 'sqlite', 'sybase'), 'mysql');
$persistent = $this->in('Persistent Connection?', array('y', 'n'), 'n');
if (strtolower($persistent) == 'n') {
$persistent = 'false';
} else {
$persistent = 'true';
}
$host = '';
while ($host == '') {
$host = $this->in('Database Host:', null, 'localhost');
}
$port = '';
while ($port == '') {
$port = $this->in('Port?', null, 'n');
}
if (strtolower($port) == 'n') {
$port = null;
}
$login = '';
while ($login == '') {
$login = $this->in('User:', null, 'root');
}
$password = '';
$blankPassword = false;
while ($password == '' && $blankPassword == false) {
$password = $this->in('Password:');
if ($password == '') {
$blank = $this->in('The password you supplied was empty. Use an empty password?', array('y', 'n'), 'n');
if ($blank == 'y') {
$blankPassword = true;
}
}
}
$database = '';
while ($database == '') {
$database = $this->in('Database Name:', null, 'cake');
}
$prefix = '';
while ($prefix == '') {
$prefix = $this->in('Table Prefix?', null, 'n');
}
if (strtolower($prefix) == 'n') {
$prefix = null;
}
$encoding = '';
while ($encoding == '') {
$encoding = $this->in('Table encoding?', null, 'n');
}
if (strtolower($encoding) == 'n') {
$encoding = null;
}
$schema = '';
if ($driver == 'postgres') {
while ($schema == '') {
$schema = $this->in('Table schema?', null, 'n');
}
}
if (strtolower($schema) == 'n') {
$schema = null;
}
$config = compact('name', 'driver', 'persistent', 'host', 'login', 'password', 'database', 'prefix', 'encoding', 'port', 'schema');
while ($this->__verify($config) == false) {
$this->__interactive();
}
$dbConfigs[] = $config;
$doneYet = $this->in('Do you wish to add another database configuration?', null, 'n');
if (strtolower($doneYet == 'n')) {
$done = true;
}
}
$this->bake($dbConfigs);
config('database');
return true;
}
/**
* Output verification message and bake if it looks good
*
* @return boolean True if user says it looks good, false otherwise
* @access private
*/
function __verify($config) {
$config = array_merge($this->__defaultConfig, $config);
extract($config);
$this->out();
$this->hr();
$this->out('The following database configuration will be created:');
$this->hr();
$this->out("Name: $name");
$this->out("Driver: $driver");
$this->out("Persistent: $persistent");
$this->out("Host: $host");
if ($port) {
$this->out("Port: $port");
}
$this->out("User: $login");
$this->out("Pass: " . str_repeat('*', strlen($password)));
$this->out("Database: $database");
if ($prefix) {
$this->out("Table prefix: $prefix");
}
if ($schema) {
$this->out("Schema: $schema");
}
if ($encoding) {
$this->out("Encoding: $encoding");
}
$this->hr();
$looksGood = $this->in('Look okay?', array('y', 'n'), 'y');
if (strtolower($looksGood) == 'y') {
return $config;
}
return false;
}
/**
* Assembles and writes database.php
*
* @param array $configs Configuration settings to use
* @return boolean Success
* @access public
*/
function bake($configs) {
if (!is_dir($this->path)) {
$this->err($this->path . ' not found');
return false;
}
$filename = $this->path . 'database.php';
$oldConfigs = array();
if (file_exists($filename)) {
config('database');
$db = new $this->databaseClassName;
$temp = get_class_vars(get_class($db));
foreach ($temp as $configName => $info) {
$info = array_merge($this->__defaultConfig, $info);
if (!isset($info['schema'])) {
$info['schema'] = null;
}
if (!isset($info['encoding'])) {
$info['encoding'] = null;
}
if (!isset($info['port'])) {
$info['port'] = null;
}
if ($info['persistent'] === false) {
$info['persistent'] = 'false';
} else {
$info['persistent'] = ($info['persistent'] == true) ? 'true' : 'false';
}
$oldConfigs[] = array(
'name' => $configName,
'driver' => $info['driver'],
'persistent' => $info['persistent'],
'host' => $info['host'],
'port' => $info['port'],
'login' => $info['login'],
'password' => $info['password'],
'database' => $info['database'],
'prefix' => $info['prefix'],
'schema' => $info['schema'],
'encoding' => $info['encoding']
);
}
}
foreach ($oldConfigs as $key => $oldConfig) {
foreach ($configs as $key1 => $config) {
if ($oldConfig['name'] == $config['name']) {
unset($oldConfigs[$key]);
}
}
}
$configs = array_merge($oldConfigs, $configs);
$out = "<?php\n";
$out .= "class DATABASE_CONFIG {\n\n";
foreach ($configs as $config) {
$config = array_merge($this->__defaultConfig, $config);
extract($config);
$out .= "\tvar \${$name} = array(\n";
$out .= "\t\t'driver' => '{$driver}',\n";
$out .= "\t\t'persistent' => {$persistent},\n";
$out .= "\t\t'host' => '{$host}',\n";
if ($port) {
$out .= "\t\t'port' => {$port},\n";
}
$out .= "\t\t'login' => '{$login}',\n";
$out .= "\t\t'password' => '{$password}',\n";
$out .= "\t\t'database' => '{$database}',\n";
if ($schema) {
$out .= "\t\t'schema' => '{$schema}',\n";
}
if ($prefix) {
$out .= "\t\t'prefix' => '{$prefix}',\n";
}
if ($encoding) {
$out .= "\t\t'encoding' => '{$encoding}'\n";
}
$out .= "\t);\n";
}
$out .= "}\n";
$out .= "?>";
$filename = $this->path . 'database.php';
return $this->createFile($filename, $out);
}
/**
* Get a user specified Connection name
*
* @return void
*/
function getConfig() {
App::import('Model', 'ConnectionManager', false);
$useDbConfig = 'default';
$configs = get_class_vars($this->databaseClassName);
if (!is_array($configs)) {
return $this->execute();
}
$connections = array_keys($configs);
if (count($connections) > 1) {
$useDbConfig = $this->in(__('Use Database Config', true) .':', $connections, 'default');
}
return $useDbConfig;
}
}

View File

@@ -0,0 +1,494 @@
<?php
/**
* Language string extractor
*
* PHP versions 4 and 5
*
* 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 CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.console.libs
* @since CakePHP(tm) v 1.2.0.5012
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Language string extractor
*
* @package cake
* @subpackage cake.cake.console.libs.tasks
*/
class ExtractTask extends Shell {
/**
* Paths to use when looking for strings
*
* @var string
* @access private
*/
var $__paths = array();
/**
* Files from where to extract
*
* @var array
* @access private
*/
var $__files = array();
/**
* Merge all domains string into the default.pot file
*
* @var boolean
* @access private
*/
var $__merge = false;
/**
* Current file being processed
*
* @var string
* @access private
*/
var $__file = null;
/**
* Contains all content waiting to be write
*
* @var string
* @access private
*/
var $__storage = array();
/**
* Extracted tokens
*
* @var array
* @access private
*/
var $__tokens = array();
/**
* Extracted strings
*
* @var array
* @access private
*/
var $__strings = array();
/**
* Destination path
*
* @var string
* @access private
*/
var $__output = null;
/**
* Execution method always used for tasks
*
* @return void
* @access private
*/
function execute() {
if (isset($this->params['files']) && !is_array($this->params['files'])) {
$this->__files = explode(',', $this->params['files']);
}
if (isset($this->params['paths'])) {
$this->__paths = explode(',', $this->params['paths']);
} else {
$defaultPath = $this->params['working'];
$message = sprintf(__("What is the full path you would like to extract?\nExample: %s\n[Q]uit [D]one", true), $this->params['root'] . DS . 'myapp');
while (true) {
$response = $this->in($message, null, $defaultPath);
if (strtoupper($response) === 'Q') {
$this->out(__('Extract Aborted', true));
$this->_stop();
} elseif (strtoupper($response) === 'D') {
$this->out();
break;
} elseif (is_dir($response)) {
$this->__paths[] = $response;
$defaultPath = 'D';
} else {
$this->err(__('The directory path you supplied was not found. Please try again.', true));
}
$this->out();
}
}
if (isset($this->params['output'])) {
$this->__output = $this->params['output'];
} else {
$message = sprintf(__("What is the full path you would like to output?\nExample: %s\n[Q]uit", true), $this->__paths[0] . DS . 'locale');
while (true) {
$response = $this->in($message, null, $this->__paths[0] . DS . 'locale');
if (strtoupper($response) === 'Q') {
$this->out(__('Extract Aborted', true));
$this->_stop();
} elseif (is_dir($response)) {
$this->__output = $response . DS;
break;
} else {
$this->err(__('The directory path you supplied was not found. Please try again.', true));
}
$this->out();
}
}
if (isset($this->params['merge'])) {
$this->__merge = !(strtolower($this->params['merge']) === 'no');
} else {
$this->out();
$response = $this->in(sprintf(__('Would you like to merge all domains strings into the default.pot file?', true)), array('y', 'n'), 'n');
$this->__merge = strtolower($response) === 'y';
}
if (empty($this->__files)) {
$this->__searchFiles();
}
$this->__extract();
}
/**
* Extract text
*
* @return void
* @access private
*/
function __extract() {
$this->out();
$this->out();
$this->out(__('Extracting...', true));
$this->hr();
$this->out(__('Paths:', true));
foreach ($this->__paths as $path) {
$this->out(' ' . $path);
}
$this->out(__('Output Directory: ', true) . $this->__output);
$this->hr();
$this->__extractTokens();
$this->__buildFiles();
$this->__writeFiles();
$this->__paths = $this->__files = $this->__storage = array();
$this->__strings = $this->__tokens = array();
$this->out();
$this->out(__('Done.', true));
}
/**
* Show help options
*
* @return void
* @access public
*/
function help() {
$this->out(__('CakePHP Language String Extraction:', true));
$this->hr();
$this->out(__('The Extract script generates .pot file(s) with translations', true));
$this->out(__('By default the .pot file(s) will be place in the locale directory of -app', true));
$this->out(__('By default -app is ROOT/app', true));
$this->hr();
$this->out(__('Usage: cake i18n extract <command> <param1> <param2>...', true));
$this->out();
$this->out(__('Params:', true));
$this->out(__(' -app [path...]: directory where your application is located', true));
$this->out(__(' -root [path...]: path to install', true));
$this->out(__(' -core [path...]: path to cake directory', true));
$this->out(__(' -paths [comma separated list of paths, full path is needed]', true));
$this->out(__(' -merge [yes|no]: Merge all domains strings into the default.pot file', true));
$this->out(__(' -output [path...]: Full path to output directory', true));
$this->out(__(' -files: [comma separated list of files, full path to file is needed]', true));
$this->out();
$this->out(__('Commands:', true));
$this->out(__(' cake i18n extract help: Shows this help message.', true));
$this->out();
}
/**
* Extract tokens out of all files to be processed
*
* @return void
* @access private
*/
function __extractTokens() {
foreach ($this->__files as $file) {
$this->__file = $file;
$this->out(sprintf(__('Processing %s...', true), $file));
$code = file_get_contents($file);
$allTokens = token_get_all($code);
$this->__tokens = array();
$lineNumber = 1;
foreach ($allTokens as $token) {
if ((!is_array($token)) || (($token[0] != T_WHITESPACE) && ($token[0] != T_INLINE_HTML))) {
if (is_array($token)) {
$token[] = $lineNumber;
}
$this->__tokens[] = $token;
}
if (is_array($token)) {
$lineNumber += count(explode("\n", $token[1])) - 1;
} else {
$lineNumber += count(explode("\n", $token)) - 1;
}
}
unset($allTokens);
$this->__parse('__', array('singular'));
$this->__parse('__n', array('singular', 'plural'));
$this->__parse('__d', array('domain', 'singular'));
$this->__parse('__c', array('singular'));
$this->__parse('__dc', array('domain', 'singular'));
$this->__parse('__dn', array('domain', 'singular', 'plural'));
$this->__parse('__dcn', array('domain', 'singular', 'plural'));
}
}
/**
* Parse tokens
*
* @param string $functionName Function name that indicates translatable string (e.g: '__')
* @param array $map Array containing what variables it will find (e.g: domain, singular, plural)
* @return void
* @access private
*/
function __parse($functionName, $map) {
$count = 0;
$tokenCount = count($this->__tokens);
while (($tokenCount - $count) > 1) {
list($countToken, $firstParenthesis) = array($this->__tokens[$count], $this->__tokens[$count + 1]);
if (!is_array($countToken)) {
$count++;
continue;
}
list($type, $string, $line) = $countToken;
if (($type == T_STRING) && ($string == $functionName) && ($firstParenthesis == '(')) {
$position = $count;
$depth = 0;
while ($depth == 0) {
if ($this->__tokens[$position] == '(') {
$depth++;
} elseif ($this->__tokens[$position] == ')') {
$depth--;
}
$position++;
}
$mapCount = count($map);
$strings = 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];
}
$position++;
}
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);
}
}
$count++;
}
}
/**
* Build the translate template file contents out of obtained strings
*
* @return void
* @access private
*/
function __buildFiles() {
foreach ($this->__strings as $domain => $strings) {
foreach ($strings as $string => $files) {
$occurrences = array();
foreach ($files as $file => $lines) {
$occurrences[] = $file . ':' . implode(';', $lines);
}
$occurrences = implode("\n#: ", $occurrences);
$header = '#: ' . str_replace($this->__paths, '', $occurrences) . "\n";
if (strpos($string, "\0") === false) {
$sentence = "msgid \"{$string}\"\n";
$sentence .= "msgstr \"\"\n\n";
} else {
list($singular, $plural) = explode("\0", $string);
$sentence = "msgid \"{$singular}\"\n";
$sentence .= "msgid_plural \"{$plural}\"\n";
$sentence .= "msgstr[0] \"\"\n";
$sentence .= "msgstr[1] \"\"\n\n";
}
$this->__store($domain, $header, $sentence);
if ($domain != 'default' && $this->__merge) {
$this->__store('default', $header, $sentence);
}
}
}
}
/**
* Prepare a file to be stored
*
* @return void
* @access private
*/
function __store($domain, $header, $sentence) {
if (!isset($this->__storage[$domain])) {
$this->__storage[$domain] = array();
}
if (!isset($this->__storage[$domain][$sentence])) {
$this->__storage[$domain][$sentence] = $header;
} else {
$this->__storage[$domain][$sentence] .= $header;
}
}
/**
* Write the files that need to be stored
*
* @return void
* @access private
*/
function __writeFiles() {
$overwriteAll = false;
foreach ($this->__storage as $domain => $sentences) {
$output = $this->__writeHeader();
foreach ($sentences as $sentence => $header) {
$output .= $header . $sentence;
}
$filename = $domain . '.pot';
$File = new File($this->__output . $filename);
$response = '';
while ($overwriteAll === false && $File->exists() && strtoupper($response) !== 'Y') {
$this->out();
$response = $this->in(sprintf(__('Error: %s already exists in this location. Overwrite? [Y]es, [N]o, [A]ll', true), $filename), array('y', 'n', 'a'), 'y');
if (strtoupper($response) === 'N') {
$response = '';
while ($response == '') {
$response = $this->in(sprintf(__("What would you like to name this file?\nExample: %s", true), 'new_' . $filename), null, 'new_' . $filename);
$File = new File($this->__output . $response);
$filename = $response;
}
} elseif (strtoupper($response) === 'A') {
$overwriteAll = true;
}
}
$File->write($output);
$File->close();
}
}
/**
* Build the translation template header
*
* @return string Translation template header
* @access private
*/
function __writeHeader() {
$output = "# LANGUAGE translation of CakePHP Application\n";
$output .= "# Copyright YEAR NAME <EMAIL@ADDRESS>\n";
$output .= "#\n";
$output .= "#, fuzzy\n";
$output .= "msgid \"\"\n";
$output .= "msgstr \"\"\n";
$output .= "\"Project-Id-Version: PROJECT VERSION\\n\"\n";
$output .= "\"POT-Creation-Date: " . date("Y-m-d H:iO") . "\\n\"\n";
$output .= "\"PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\\n\"\n";
$output .= "\"Last-Translator: NAME <EMAIL@ADDRESS>\\n\"\n";
$output .= "\"Language-Team: LANGUAGE <EMAIL@ADDRESS>\\n\"\n";
$output .= "\"MIME-Version: 1.0\\n\"\n";
$output .= "\"Content-Type: text/plain; charset=utf-8\\n\"\n";
$output .= "\"Content-Transfer-Encoding: 8bit\\n\"\n";
$output .= "\"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\\n\"\n\n";
return $output;
}
/**
* Format a string to be added as a translateable string
*
* @param string $string String to format
* @return string Formatted string
* @access private
*/
function __formatString($string) {
$quote = substr($string, 0, 1);
$string = substr($string, 1, -1);
if ($quote == '"') {
$string = stripcslashes($string);
} else {
$string = strtr($string, array("\\'" => "'", "\\\\" => "\\"));
}
$string = str_replace("\r\n", "\n", $string);
return addcslashes($string, "\0..\37\\\"");
}
/**
* Indicate an invalid marker on a processed file
*
* @param string $file File where invalid marker resides
* @param integer $line Line number
* @param string $marker Marker found
* @param integer $count Count
* @return void
* @access private
*/
function __markerError($file, $line, $marker, $count) {
$this->out(sprintf(__("Invalid marker content in %s:%s\n* %s(", true), $file, $line, $marker), true);
$count += 2;
$tokenCount = count($this->__tokens);
$parenthesis = 1;
while ((($tokenCount - $count) > 0) && $parenthesis) {
if (is_array($this->__tokens[$count])) {
$this->out($this->__tokens[$count][1], false);
} else {
$this->out($this->__tokens[$count], false);
if ($this->__tokens[$count] == '(') {
$parenthesis++;
}
if ($this->__tokens[$count] == ')') {
$parenthesis--;
}
}
$count++;
}
$this->out("\n", true);
}
/**
* Search files that may contain translateable strings
*
* @return void
* @access private
*/
function __searchFiles() {
foreach ($this->__paths as $path) {
$Folder = new Folder($path);
$files = $Folder->findRecursive('.*\.(php|ctp|thtml|inc|tpl)', true);
$this->__files = array_merge($this->__files, $files);
}
}
}

View File

@@ -0,0 +1,422 @@
<?php
/**
* The FixtureTask handles creating and updating fixture files.
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2010, Cake Software Foundation, Inc.
*
* 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 CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.console.libs.tasks
* @since CakePHP(tm) v 1.3
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
include_once dirname(__FILE__) . DS . 'bake.php';
/**
* Task class for creating and updating fixtures files.
*
* @package cake
* @subpackage cake.cake.console.libs.tasks
*/
class FixtureTask extends BakeTask {
/**
* Tasks to be loaded by this Task
*
* @var array
* @access public
*/
var $tasks = array('DbConfig', 'Model', 'Template');
/**
* path to fixtures directory
*
* @var string
* @access public
*/
var $path = null;
/**
* Schema instance
*
* @var object
* @access protected
*/
var $_Schema = null;
/**
* Override initialize
*
* @access public
*/
function __construct(&$dispatch) {
parent::__construct($dispatch);
$this->path = $this->params['working'] . DS . 'tests' . DS . 'fixtures' . DS;
}
/**
* Execution method always used for tasks
* Handles dispatching to interactive, named, or all processess.
*
* @access public
*/
function execute() {
if (empty($this->args)) {
$this->__interactive();
}
if (isset($this->args[0])) {
$this->interactive = false;
if (!isset($this->connection)) {
$this->connection = 'default';
}
if (strtolower($this->args[0]) == 'all') {
return $this->all();
}
$model = $this->_modelName($this->args[0]);
$this->bake($model);
}
}
/**
* Bake All the Fixtures at once. Will only bake fixtures for models that exist.
*
* @access public
* @return void
*/
function all() {
$this->interactive = false;
$this->Model->interactive = false;
$tables = $this->Model->listAll($this->connection, false);
foreach ($tables as $table) {
$model = $this->_modelName($table);
$this->bake($model);
}
}
/**
* Interactive baking function
*
* @access private
*/
function __interactive() {
$this->DbConfig->interactive = $this->Model->interactive = $this->interactive = true;
$this->hr();
$this->out(sprintf("Bake Fixture\nPath: %s", $this->path));
$this->hr();
$useDbConfig = $this->connection;
if (!isset($this->connection)) {
$this->connection = $this->DbConfig->getConfig();
}
$modelName = $this->Model->getName($this->connection);
$useTable = $this->Model->getTable($modelName, $this->connection);
$importOptions = $this->importOptions($modelName);
$this->bake($modelName, $useTable, $importOptions);
}
/**
* Interacts with the User to setup an array of import options. For a fixture.
*
* @param string $modelName Name of model you are dealing with.
* @return array Array of import options.
* @access public
*/
function importOptions($modelName) {
$options = array();
$doSchema = $this->in(__('Would you like to import schema for this fixture?', true), array('y', 'n'), 'n');
if ($doSchema == 'y') {
$options['schema'] = $modelName;
}
$doRecords = $this->in(__('Would you like to use record importing for this fixture?', true), array('y', 'n'), 'n');
if ($doRecords == 'y') {
$options['records'] = true;
}
if ($doRecords == 'n') {
$prompt = sprintf(__("Would you like to build this fixture with data from %s's table?", true), $modelName);
$fromTable = $this->in($prompt, array('y', 'n'), 'n');
if (strtolower($fromTable) == 'y') {
$options['fromTable'] = true;
}
}
return $options;
}
/**
* Assembles and writes a Fixture file
*
* @param string $model Name of model to bake.
* @param string $useTable Name of table to use.
* @param array $importOptions Options for var $import
* @return string Baked fixture content
* @access public
*/
function bake($model, $useTable = false, $importOptions = array()) {
if (!class_exists('CakeSchema')) {
App::import('Model', 'CakeSchema', false);
}
$table = $schema = $records = $import = $modelImport = $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.
* @access public
*/
function generateFixtureFile($model, $otherVars) {
$defaults = array('table' => null, 'schema' => null, 'records' => null, 'import' => null, 'fields' => null);
$vars = array_merge($defaults, $otherVars);
$path = $this->getPath();
$filename = Inflector::underscore($model) . '_fixture.php';
$this->Template->set('model', $model);
$this->Template->set($vars);
$content = $this->Template->generate('classes', 'fixture');
$this->out("\nBaking test fixture for $model...");
$this->createFile($path . $filename, $content);
return $content;
}
/**
* Get the path to the fixtures.
*
* @return void
*/
function getPath() {
$path = $this->path;
if (isset($this->plugin)) {
$path = $this->_pluginPath($this->plugin) . 'tests' . DS . 'fixtures' . DS;
}
return $path;
}
/**
* Generates a string representation of a schema.
*
* @param array $table Table schema array
* @return string fields definitions
* @access protected
*/
function _generateSchema($tableInfo) {
$schema = $this->_Schema->generateTable('f', $tableInfo);
return substr($schema, 10, -2);
}
/**
* Generate String representation of Records
*
* @param array $table Table schema array
* @return array Array of records to use in the fixture.
* @access protected
*/
function _generateRecords($tableInfo, $recordCount = 1) {
$records = array();
for ($i = 0; $i < $recordCount; $i++) {
$record = array();
foreach ($tableInfo as $field => $fieldInfo) {
if (empty($fieldInfo['type'])) {
continue;
}
switch ($fieldInfo['type']) {
case 'integer':
case 'float':
$insert = $i + 1;
break;
case 'string':
case 'binary':
$isPrimaryUuid = (
isset($fieldInfo['key']) && strtolower($fieldInfo['key']) == 'primary' &&
isset($fieldInfo['length']) && $fieldInfo['length'] == 36
);
if ($isPrimaryUuid) {
$insert = String::uuid();
} else {
$insert = "Lorem ipsum dolor sit amet";
if (!empty($fieldInfo['length'])) {
$insert = substr($insert, 0, (int)$fieldInfo['length'] - 2);
}
}
$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.
* @access 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.
* @access protected
*/
function _getRecordsFromTable($modelName, $useTable = null) {
if ($this->interactive) {
$condition = null;
$prompt = __("Please provide a SQL fragment to use as conditions\nExample: WHERE 1=1 LIMIT 10", true);
while (!$condition) {
$condition = $this->in($prompt, null, 'WHERE 1=1 LIMIT 10');
}
} else {
$condition = 'WHERE 1=1 LIMIT ' . (isset($this->params['count']) ? $this->params['count'] : 10);
}
App::import('Model', 'Model', false);
$modelObject =& new Model(array('name' => $modelName, 'table' => $useTable, 'ds' => $this->connection));
$records = $modelObject->find('all', array(
'conditions' => $condition,
'recursive' => -1
));
$db =& ConnectionManager::getDataSource($modelObject->useDbConfig);
$schema = $modelObject->schema(true);
$out = array();
foreach ($records as $record) {
$row = array();
foreach ($record[$modelObject->alias] as $field => $value) {
$row[$field] = $db->value($value, $schema[$field]['type']);
}
$out[] = $row;
}
return $out;
}
/**
* Displays help contents
*
* @access public
*/
function help() {
$this->hr();
$this->out("Usage: cake bake fixture <arg1> <params>");
$this->hr();
$this->out('Arguments:');
$this->out();
$this->out("<name>");
$this->out("\tName of the fixture to bake. Can use Plugin.name");
$this->out("\tas a shortcut for plugin baking.");
$this->out();
$this->out('Commands:');
$this->out("\nfixture <name>\n\tbakes fixture with specified name.");
$this->out("\nfixture all\n\tbakes all fixtures.");
$this->out();
$this->out('Parameters:');
$this->out("\t-count When using generated data, the number of records to include in the fixture(s).");
$this->out("\t-connection Which database configuration to use for baking.");
$this->out("\t-plugin CamelCased name of plugin to bake fixtures for.");
$this->out("\t-records Used with -count and <name>/all commands to pull [n] records from the live tables");
$this->out("\t Where [n] is either -count or the default of 10.");
$this->out();
$this->_stop();
}
}

View File

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

View File

@@ -0,0 +1,247 @@
<?php
/**
* The Plugin Task handles creating an empty plugin, ready to be used
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-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 CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.console.libs.tasks
* @since CakePHP(tm) v 1.2
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Task class for creating a plugin
*
* @package cake
* @subpackage cake.cake.console.libs.tasks
*/
class PluginTask extends Shell {
/**
* Tasks
*
*/
var $tasks = array('Model', 'Controller', 'View');
/**
* path to CONTROLLERS directory
*
* @var array
* @access public
*/
var $path = null;
/**
* initialize
*
* @return void
*/
function initialize() {
$this->path = APP . 'plugins' . DS;
}
/**
* Execution method always used for tasks
*
* @return void
*/
function execute() {
if (empty($this->params['skel'])) {
$this->params['skel'] = '';
if (is_dir(CAKE_CORE_INCLUDE_PATH . DS . CAKE . 'console' . DS . 'templates' . DS . 'skel') === true) {
$this->params['skel'] = CAKE_CORE_INCLUDE_PATH . DS . CAKE . 'console' . DS . 'templates' . DS . 'skel';
}
}
$plugin = null;
if (isset($this->args[0])) {
$plugin = Inflector::camelize($this->args[0]);
$pluginPath = $this->_pluginPath($plugin);
$this->Dispatch->shiftArgs();
if (is_dir($pluginPath)) {
$this->out(sprintf(__('Plugin: %s', true), $plugin));
$this->out(sprintf(__('Path: %s', true), $pluginPath));
} elseif (isset($this->args[0])) {
$this->err(sprintf(__('%s in path %s not found.', true), $plugin, $pluginPath));
$this->_stop();
} else {
$this->__interactive($plugin);
}
} else {
return $this->__interactive();
}
if (isset($this->args[0])) {
$task = Inflector::classify($this->args[0]);
$this->Dispatch->shiftArgs();
if (in_array($task, $this->tasks)) {
$this->{$task}->plugin = $plugin;
$this->{$task}->path = $pluginPath . Inflector::underscore(Inflector::pluralize($task)) . DS;
if (!is_dir($this->{$task}->path)) {
$this->err(sprintf(__("%s directory could not be found.\nBe sure you have created %s", true), $task, $this->{$task}->path));
}
$this->{$task}->loadTasks();
return $this->{$task}->execute();
}
}
}
/**
* Interactive interface
*
* @access private
* @return void
*/
function __interactive($plugin = null) {
while ($plugin === null) {
$plugin = $this->in(__('Enter the name of the plugin in CamelCase format', true));
}
if (!$this->bake($plugin)) {
$this->err(sprintf(__("An error occured trying to bake: %s in %s", true), $plugin, $this->path . Inflector::underscore($pluginPath)));
}
}
/**
* Bake the plugin, create directories and files
*
* @params $plugin name of the plugin in CamelCased format
* @access public
* @return bool
*/
function bake($plugin) {
$pluginPath = Inflector::underscore($plugin);
$pathOptions = App::path('plugins');
if (count($pathOptions) > 1) {
$this->findPath($pathOptions);
}
$this->hr();
$this->out(sprintf(__("Plugin Name: %s", true), $plugin));
$this->out(sprintf(__("Plugin Directory: %s", true), $this->path . $pluginPath));
$this->hr();
$looksGood = $this->in(__('Look okay?', true), array('y', 'n', 'q'), 'y');
if (strtolower($looksGood) == 'y') {
$verbose = $this->in(__('Do you want verbose output?', true), array('y', 'n'), 'n');
$Folder =& new Folder($this->path . $pluginPath);
$directories = array(
'config' . DS . 'schema',
'models' . DS . 'behaviors',
'models' . DS . 'datasources',
'controllers' . DS . 'components',
'libs',
'views' . DS . 'helpers',
'tests' . DS . 'cases' . DS . 'components',
'tests' . DS . 'cases' . DS . 'helpers',
'tests' . DS . 'cases' . DS . 'behaviors',
'tests' . DS . 'cases' . DS . 'controllers',
'tests' . DS . 'cases' . DS . 'models',
'tests' . DS . 'groups',
'tests' . DS . 'fixtures',
'vendors',
'vendors' . DS . 'shells' . DS . 'tasks',
'webroot'
);
foreach ($directories as $directory) {
$dirPath = $this->path . $pluginPath . DS . $directory;
$Folder->create($dirPath);
$File =& new File($dirPath . DS . 'empty', true);
}
if (strtolower($verbose) == 'y') {
foreach ($Folder->messages() as $message) {
$this->out($message);
}
}
$errors = $Folder->errors();
if (!empty($errors)) {
return false;
}
$controllerFileName = $pluginPath . '_app_controller.php';
$out = "<?php\n\n";
$out .= "class {$plugin}AppController extends AppController {\n\n";
$out .= "}\n\n";
$out .= "?>";
$this->createFile($this->path . $pluginPath. DS . $controllerFileName, $out);
$modelFileName = $pluginPath . '_app_model.php';
$out = "<?php\n\n";
$out .= "class {$plugin}AppModel extends AppModel {\n\n";
$out .= "}\n\n";
$out .= "?>";
$this->createFile($this->path . $pluginPath . DS . $modelFileName, $out);
$this->hr();
$this->out(sprintf(__("Created: %s in %s", true), $plugin, $this->path . $pluginPath));
$this->hr();
}
return true;
}
/**
* find and change $this->path to the user selection
*
* @return void
*/
function findPath($pathOptions) {
$valid = false;
$max = count($pathOptions);
while (!$valid) {
foreach ($pathOptions as $i => $option) {
$this->out($i + 1 .'. ' . $option);
}
$prompt = __('Choose a plugin path from the paths above.', true);
$choice = $this->in($prompt);
if (intval($choice) > 0 && intval($choice) <= $max) {
$valid = true;
}
}
$this->path = $pathOptions[$choice - 1];
}
/**
* Help
*
* @return void
* @access public
*/
function help() {
$this->hr();
$this->out("Usage: cake bake plugin <arg1> <arg2>...");
$this->hr();
$this->out('Commands:');
$this->out();
$this->out("plugin <name>");
$this->out("\tbakes plugin directory structure");
$this->out();
$this->out("plugin <name> model");
$this->out("\tbakes model. Run 'cake bake model help' for more info.");
$this->out();
$this->out("plugin <name> controller");
$this->out("\tbakes controller. Run 'cake bake controller help' for more info.");
$this->out();
$this->out("plugin <name> view");
$this->out("\tbakes view. Run 'cake bake view help' for more info.");
$this->out();
$this->_stop();
}
}

View File

@@ -0,0 +1,371 @@
<?php
/**
* The Project Task handles creating the base application
*
*
* PHP versions 4 and 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-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 CakePHP(tm) Project
* @package cake
* @subpackage cake.cake.console.bake
* @since CakePHP(tm) v 1.2
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Task class for creating new project apps and plugins
*
* @package cake
* @subpackage cake.cake.console.libs.tasks
*/
class ProjectTask extends Shell {
/**
* configs path (used in testing).
*
* @var string
*/
var $configPath = null;
/**
* Checks that given project path does not already exist, and
* finds the app directory in it. Then it calls bake() with that information.
*
* @param string $project Project path
* @access public
*/
function execute($project = null) {
if ($project === null) {
if (isset($this->args[0])) {
$project = $this->args[0];
}
}
if ($project) {
$this->Dispatch->parseParams(array('-app', $project));
$project = $this->params['working'];
}
if (empty($this->params['skel'])) {
$this->params['skel'] = '';
if (is_dir(CAKE . 'console' . DS . 'templates' . DS . 'skel') === true) {
$this->params['skel'] = CAKE . 'console' . DS . 'templates' . DS . 'skel';
}
}
while (!$project) {
$prompt = __("What is the full path for this app including the app directory name?\n Example:", true);
$default = $this->params['working'] . DS . 'myapp';
$project = $this->in($prompt . $default, null, $default);
}
if ($project) {
$response = false;
while ($response == false && is_dir($project) === true && file_exists($project . 'config' . 'core.php')) {
$prompt = sprintf(__('A project already exists in this location: %s Overwrite?', true), $project);
$response = $this->in($prompt, array('y','n'), 'n');
if (strtolower($response) === 'n') {
$response = $project = false;
}
}
}
if ($this->bake($project)) {
$path = Folder::slashTerm($project);
if ($this->createHome($path)) {
$this->out(__('Welcome page created', true));
} else {
$this->out(__('The Welcome page was NOT created', true));
}
if ($this->securitySalt($path) === true ) {
$this->out(__('Random hash key created for \'Security.salt\'', true));
} else {
$this->err(sprintf(__('Unable to generate random hash for \'Security.salt\', you should change it in %s', true), CONFIGS . 'core.php'));
}
if ($this->securityCipherSeed($path) === true ) {
$this->out(__('Random seed created for \'Security.cipherSeed\'', true));
} else {
$this->err(sprintf(__('Unable to generate random seed for \'Security.cipherSeed\', you should change it in %s', true), CONFIGS . 'core.php'));
}
$corePath = $this->corePath($path);
if ($corePath === true ) {
$this->out(sprintf(__('CAKE_CORE_INCLUDE_PATH set to %s in webroot/index.php', true), CAKE_CORE_INCLUDE_PATH));
$this->out(sprintf(__('CAKE_CORE_INCLUDE_PATH set to %s in webroot/test.php', true), CAKE_CORE_INCLUDE_PATH));
$this->out(__('Remember to check these value after moving to production server', true));
} elseif ($corePath === false) {
$this->err(sprintf(__('Unable to set CAKE_CORE_INCLUDE_PATH, you should change it in %s', true), $path . 'webroot' .DS .'index.php'));
}
$Folder = new Folder($path);
if (!$Folder->chmod($path . 'tmp', 0777)) {
$this->err(sprintf(__('Could not set permissions on %s', true), $path . DS .'tmp'));
$this->out(sprintf(__('chmod -R 0777 %s', true), $path . DS .'tmp'));
}
$this->params['working'] = $path;
$this->params['app'] = basename($path);
return true;
}
}
/**
* Looks for a skeleton template of a Cake application,
* and if not found asks the user for a path. When there is a path
* this method will make a deep copy of the skeleton to the project directory.
* A default home page will be added, and the tmp file storage will be chmod'ed to 0777.
*
* @param string $path Project path
* @param string $skel Path to copy from
* @param string $skip array of directories to skip when copying
* @access private
*/
function bake($path, $skel = null, $skip = array('empty')) {
if (!$skel) {
$skel = $this->params['skel'];
}
while (!$skel) {
$skel = $this->in(sprintf(__("What is the path to the directory layout you wish to copy?\nExample: %s"), APP, null, ROOT . DS . 'myapp' . DS));
if ($skel == '') {
$this->out(__('The directory path you supplied was empty. Please try again.', true));
} else {
while (is_dir($skel) === false) {
$skel = $this->in(__('Directory path does not exist please choose another:', true));
}
}
}
$app = basename($path);
$this->out(__('Bake Project', true));
$this->out(__("Skel Directory: ", true) . $skel);
$this->out(__("Will be copied to: ", true) . $path);
$this->hr();
$looksGood = $this->in(__('Look okay?', true), array('y', 'n', 'q'), 'y');
if (strtolower($looksGood) == 'y') {
$verbose = $this->in(__('Do you want verbose output?', true), array('y', 'n'), 'n');
$Folder = new Folder($skel);
if (!empty($this->params['empty'])) {
$skip = array();
}
if ($Folder->copy(array('to' => $path, 'skip' => $skip))) {
$this->hr();
$this->out(sprintf(__("Created: %s in %s", true), $app, $path));
$this->hr();
} else {
$this->err(sprintf(__(" '%s' could not be created properly", true), $app));
return false;
}
if (strtolower($verbose) == 'y') {
foreach ($Folder->messages() as $message) {
$this->out($message);
}
}
return true;
} elseif (strtolower($looksGood) == 'q') {
$this->out(__('Bake Aborted.', true));
} else {
$this->execute(false);
return false;
}
}
/**
* Writes a file with a default home page to the project.
*
* @param string $dir Path to project
* @return boolean Success
* @access public
*/
function createHome($dir) {
$app = basename($dir);
$path = $dir . 'views' . DS . 'pages' . DS;
$source = CAKE . 'console' . DS . 'templates' . DS .'default' . DS . 'views' . DS . 'home.ctp';
include($source);
return $this->createFile($path.'home.ctp', $output);
}
/**
* Generates and writes 'Security.salt'
*
* @param string $path Project path
* @return boolean Success
* @access public
*/
function securitySalt($path) {
$File =& new File($path . 'config' . DS . 'core.php');
$contents = $File->read();
if (preg_match('/([\\t\\x20]*Configure::write\\(\\\'Security.salt\\\',[\\t\\x20\'A-z0-9]*\\);)/', $contents, $match)) {
if (!class_exists('Security')) {
require LIBS . 'security.php';
}
$string = Security::generateAuthKey();
$result = str_replace($match[0], "\t" . 'Configure::write(\'Security.salt\', \''.$string.'\');', $contents);
if ($File->write($result)) {
return true;
}
return false;
}
return false;
}
/**
* Generates and writes 'Security.cipherSeed'
*
* @param string $path Project path
* @return boolean Success
* @access public
*/
function securityCipherSeed($path) {
$File =& new File($path . 'config' . DS . 'core.php');
$contents = $File->read();
if (preg_match('/([\\t\\x20]*Configure::write\\(\\\'Security.cipherSeed\\\',[\\t\\x20\'A-z0-9]*\\);)/', $contents, $match)) {
if (!class_exists('Security')) {
require LIBS . 'security.php';
}
$string = substr(bin2hex(Security::generateAuthKey()), 0, 30);
$result = str_replace($match[0], "\t" . 'Configure::write(\'Security.cipherSeed\', \''.$string.'\');', $contents);
if ($File->write($result)) {
return true;
}
return false;
}
return false;
}
/**
* Generates and writes CAKE_CORE_INCLUDE_PATH
*
* @param string $path Project path
* @return boolean Success
* @access public
*/
function corePath($path) {
if (dirname($path) !== CAKE_CORE_INCLUDE_PATH) {
$File =& new File($path . 'webroot' . DS . 'index.php');
$contents = $File->read();
if (preg_match('/([\\t\\x20]*define\\(\\\'CAKE_CORE_INCLUDE_PATH\\\',[\\t\\x20\'A-z0-9]*\\);)/', $contents, $match)) {
$root = strpos(CAKE_CORE_INCLUDE_PATH, '/') === 0 ? " DS . '" : "'";
$result = str_replace($match[0], "\t\tdefine('CAKE_CORE_INCLUDE_PATH', " . $root . str_replace(DS, "' . DS . '", trim(CAKE_CORE_INCLUDE_PATH, DS)) . "');", $contents);
if (!$File->write($result)) {
return false;
}
} else {
return false;
}
$File =& new File($path . 'webroot' . DS . 'test.php');
$contents = $File->read();
if (preg_match('/([\\t\\x20]*define\\(\\\'CAKE_CORE_INCLUDE_PATH\\\',[\\t\\x20\'A-z0-9]*\\);)/', $contents, $match)) {
$result = str_replace($match[0], "\t\tdefine('CAKE_CORE_INCLUDE_PATH', " . $root . str_replace(DS, "' . DS . '", trim(CAKE_CORE_INCLUDE_PATH, DS)) . "');", $contents);
if (!$File->write($result)) {
return false;
}
} else {
return false;
}
return true;
}
}
/**
* Enables Configure::read('Routing.prefixes') in /app/config/core.php
*
* @param string $name Name to use as admin routing
* @return boolean Success
* @access public
*/
function cakeAdmin($name) {
$path = (empty($this->configPath)) ? CONFIGS : $this->configPath;
$File =& new File($path . 'core.php');
$contents = $File->read();
if (preg_match('%([/\\t\\x20]*Configure::write\(\'Routing.prefixes\',[\\t\\x20\'a-z,\)\(]*\\);)%', $contents, $match)) {
$result = str_replace($match[0], "\t" . 'Configure::write(\'Routing.prefixes\', array(\''.$name.'\'));', $contents);
if ($File->write($result)) {
Configure::write('Routing.prefixes', array($name));
return true;
} else {
return false;
}
} else {
return false;
}
}
/**
* Checks for Configure::read('Routing.prefixes') and forces user to input it if not enabled
*
* @return string Admin route to use
* @access public
*/
function getPrefix() {
$admin = '';
$prefixes = Configure::read('Routing.prefixes');
if (!empty($prefixes)) {
if (count($prefixes) == 1) {
return $prefixes[0] . '_';
}
if ($this->interactive) {
$this->out();
$this->out(__('You have more than one routing prefix configured', true));
}
$options = array();
foreach ($prefixes as $i => $prefix) {
$options[] = $i + 1;
if ($this->interactive) {
$this->out($i + 1 . '. ' . $prefix);
}
}
$selection = $this->in(__('Please choose a prefix to bake with.', true), $options, 1);
return $prefixes[$selection - 1] . '_';
}
if ($this->interactive) {
$this->hr();
$this->out('You need to enable Configure::write(\'Routing.prefixes\',array(\'admin\')) in /app/config/core.php to use prefix routing.');
$this->out(__('What would you like the prefix route to be?', true));
$this->out(__('Example: www.example.com/admin/controller', true));
while ($admin == '') {
$admin = $this->in(__("Enter a routing prefix:", true), null, 'admin');
}
if ($this->cakeAdmin($admin) !== true) {
$this->out(__('Unable to write to /app/config/core.php.', true));
$this->out('You need to enable Configure::write(\'Routing.prefixes\',array(\'admin\')) in /app/config/core.php to use prefix routing.');
$this->_stop();
}
return $admin . '_';
}
return '';
}
/**
* Help
*
* @return void
* @access public
*/
function help() {
$this->hr();
$this->out("Usage: cake bake project <arg1>");
$this->hr();
$this->out('Commands:');
$this->out();
$this->out("project <name>");
$this->out("\tbakes app directory structure.");
$this->out("\tif <name> begins with '/' path is absolute.");
$this->out();
$this->_stop();
}
}

View File

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

View File

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

View File

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