mirror of
https://github.com/brmlab/brmbiolab_sklad.git
synced 2025-10-29 06:24:01 +01:00
Initial commit
This commit is contained in:
commit
3b93da31de
1004 changed files with 265840 additions and 0 deletions
619
lib/Cake/Console/Command/AclShell.php
Normal file
619
lib/Cake/Console/Command/AclShell.php
Normal file
|
|
@ -0,0 +1,619 @@
|
|||
<?php
|
||||
/**
|
||||
* Acl Shell provides Acl access in the CLI environment
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since CakePHP(tm) v 1.2.0.5012
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('AppShell', 'Console/Command');
|
||||
App::uses('Controller', 'Controller');
|
||||
App::uses('ComponentCollection', 'Controller');
|
||||
App::uses('AclComponent', 'Controller/Component');
|
||||
App::uses('DbAcl', 'Model');
|
||||
App::uses('Hash', 'Utility');
|
||||
|
||||
/**
|
||||
* Shell for ACL management. This console is known to have issues with zend.ze1_compatibility_mode
|
||||
* being enabled. Be sure to turn it off when using this shell.
|
||||
*
|
||||
* @package Cake.Console.Command
|
||||
*/
|
||||
class AclShell extends AppShell {
|
||||
|
||||
/**
|
||||
* Contains instance of AclComponent
|
||||
*
|
||||
* @var AclComponent
|
||||
*/
|
||||
public $Acl;
|
||||
|
||||
/**
|
||||
* Contains arguments parsed from the command line.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $args;
|
||||
|
||||
/**
|
||||
* Contains database source to use
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $connection = 'default';
|
||||
|
||||
/**
|
||||
* Contains tasks to load and instantiate
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $tasks = array('DbConfig');
|
||||
|
||||
/**
|
||||
* Override startup of the Shell
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function startup() {
|
||||
parent::startup();
|
||||
if (isset($this->params['connection'])) {
|
||||
$this->connection = $this->params['connection'];
|
||||
}
|
||||
|
||||
$class = Configure::read('Acl.classname');
|
||||
list($plugin, $class) = pluginSplit($class, true);
|
||||
App::uses($class, $plugin . 'Controller/Component/Acl');
|
||||
if (!in_array($class, array('DbAcl', 'DB_ACL')) && !is_subclass_of($class, 'DbAcl')) {
|
||||
$out = "--------------------------------------------------\n";
|
||||
$out .= __d('cake_console', 'Error: Your current CakePHP configuration is set to an ACL implementation other than DB.') . "\n";
|
||||
$out .= __d('cake_console', 'Please change your core config to reflect your decision to use DbAcl before attempting to use this script') . "\n";
|
||||
$out .= "--------------------------------------------------\n";
|
||||
$out .= __d('cake_console', 'Current ACL Classname: %s', $class) . "\n";
|
||||
$out .= "--------------------------------------------------\n";
|
||||
$this->err($out);
|
||||
return $this->_stop();
|
||||
}
|
||||
|
||||
if ($this->command) {
|
||||
if (!config('database')) {
|
||||
$this->out(__d('cake_console', 'Your database configuration was not found. Take a moment to create one.'));
|
||||
$this->args = null;
|
||||
return $this->DbConfig->execute();
|
||||
}
|
||||
require_once APP . 'Config' . DS . 'database.php';
|
||||
|
||||
if (!in_array($this->command, array('initdb'))) {
|
||||
$collection = new ComponentCollection();
|
||||
$this->Acl = new AclComponent($collection);
|
||||
$controller = new Controller();
|
||||
$this->Acl->startup($controller);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override main() for help message hook
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function main() {
|
||||
$this->out($this->OptionParser->help());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ARO/ACO node
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function create() {
|
||||
extract($this->_dataVars());
|
||||
|
||||
$class = ucfirst($this->args[0]);
|
||||
$parent = $this->parseIdentifier($this->args[1]);
|
||||
|
||||
if (!empty($parent) && $parent !== '/' && $parent !== 'root') {
|
||||
$parent = $this->_getNodeId($class, $parent);
|
||||
} else {
|
||||
$parent = null;
|
||||
}
|
||||
|
||||
$data = $this->parseIdentifier($this->args[2]);
|
||||
if (is_string($data) && $data !== '/') {
|
||||
$data = array('alias' => $data);
|
||||
} elseif (is_string($data)) {
|
||||
$this->error(__d('cake_console', '/ can not be used as an alias!') . __d('cake_console', " / is the root, please supply a sub alias"));
|
||||
}
|
||||
|
||||
$data['parent_id'] = $parent;
|
||||
$this->Acl->{$class}->create();
|
||||
if ($this->Acl->{$class}->save($data)) {
|
||||
$this->out(__d('cake_console', "<success>New %s</success> '%s' created.", $class, $this->args[2]), 2);
|
||||
} else {
|
||||
$this->err(__d('cake_console', "There was a problem creating a new %s '%s'.", $class, $this->args[2]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an ARO/ACO node. Note there may be (as a result of poor configuration)
|
||||
* multiple records with the same logical identifier. All are deleted.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete() {
|
||||
extract($this->_dataVars());
|
||||
|
||||
$identifier = $this->parseIdentifier($this->args[1]);
|
||||
if (is_string($identifier)) {
|
||||
$identifier = array('alias' => $identifier);
|
||||
}
|
||||
|
||||
if ($this->Acl->{$class}->find('all', array('conditions' => $identifier))) {
|
||||
if (!$this->Acl->{$class}->deleteAll($identifier)) {
|
||||
$this->error(__d('cake_console', 'Node Not Deleted. ') . __d('cake_console', 'There was an error deleting the %s.', $class) . "\n");
|
||||
}
|
||||
$this->out(__d('cake_console', '<success>%s deleted.</success>', $class), 2);
|
||||
} else {
|
||||
$this->error(__d('cake_console', 'Node Not Deleted. ') . __d('cake_console', 'There was an error deleting the %s. Node does not exist.', $class) . "\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set parent for an ARO/ACO node.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setParent() {
|
||||
extract($this->_dataVars());
|
||||
$target = $this->parseIdentifier($this->args[1]);
|
||||
$parent = $this->parseIdentifier($this->args[2]);
|
||||
|
||||
$data = array(
|
||||
$class => array(
|
||||
'id' => $this->_getNodeId($class, $target),
|
||||
'parent_id' => $this->_getNodeId($class, $parent)
|
||||
)
|
||||
);
|
||||
$this->Acl->{$class}->create();
|
||||
if (!$this->Acl->{$class}->save($data)) {
|
||||
$this->out(__d('cake_console', 'Error in setting new parent. Please make sure the parent node exists, and is not a descendant of the node specified.'));
|
||||
} else {
|
||||
$this->out(__d('cake_console', 'Node parent set to %s', $this->args[2]) . "\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get path to specified ARO/ACO node.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getPath() {
|
||||
extract($this->_dataVars());
|
||||
$identifier = $this->parseIdentifier($this->args[1]);
|
||||
|
||||
$id = $this->_getNodeId($class, $identifier);
|
||||
$nodes = $this->Acl->{$class}->getPath($id);
|
||||
|
||||
if (empty($nodes)) {
|
||||
$this->error(
|
||||
__d('cake_console', "Supplied Node '%s' not found", $this->args[1]),
|
||||
__d('cake_console', 'No tree returned.')
|
||||
);
|
||||
}
|
||||
$this->out(__d('cake_console', 'Path:'));
|
||||
$this->hr();
|
||||
for ($i = 0, $len = count($nodes); $i < $len; $i++) {
|
||||
$this->_outputNode($class, $nodes[$i], $i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs a single node, Either using the alias or Model.key
|
||||
*
|
||||
* @param string $class Class name that is being used.
|
||||
* @param array $node Array of node information.
|
||||
* @param int $indent indent level.
|
||||
* @return void
|
||||
*/
|
||||
protected function _outputNode($class, $node, $indent) {
|
||||
$indent = str_repeat(' ', $indent);
|
||||
$data = $node[$class];
|
||||
if ($data['alias']) {
|
||||
$this->out($indent . "[" . $data['id'] . "] " . $data['alias']);
|
||||
} else {
|
||||
$this->out($indent . "[" . $data['id'] . "] " . $data['model'] . '.' . $data['foreign_key']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check permission for a given ARO to a given ACO.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function check() {
|
||||
extract($this->_getParams());
|
||||
|
||||
if ($this->Acl->check($aro, $aco, $action)) {
|
||||
$this->out(__d('cake_console', '%s is <success>allowed</success>.', $aroName));
|
||||
} else {
|
||||
$this->out(__d('cake_console', '%s is <error>not allowed</error>.', $aroName));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Grant permission for a given ARO to a given ACO.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function grant() {
|
||||
extract($this->_getParams());
|
||||
|
||||
if ($this->Acl->allow($aro, $aco, $action)) {
|
||||
$this->out(__d('cake_console', 'Permission <success>granted</success>.'));
|
||||
} else {
|
||||
$this->out(__d('cake_console', 'Permission was <error>not granted</error>.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deny access for an ARO to an ACO.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function deny() {
|
||||
extract($this->_getParams());
|
||||
|
||||
if ($this->Acl->deny($aro, $aco, $action)) {
|
||||
$this->out(__d('cake_console', 'Permission denied.'));
|
||||
} else {
|
||||
$this->out(__d('cake_console', 'Permission was not denied.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an ARO to inherit permission to an ACO.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function inherit() {
|
||||
extract($this->_getParams());
|
||||
|
||||
if ($this->Acl->inherit($aro, $aco, $action)) {
|
||||
$this->out(__d('cake_console', 'Permission inherited.'));
|
||||
} else {
|
||||
$this->out(__d('cake_console', 'Permission was not inherited.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a specific ARO/ACO node.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function view() {
|
||||
extract($this->_dataVars());
|
||||
|
||||
if (isset($this->args[1])) {
|
||||
$identity = $this->parseIdentifier($this->args[1]);
|
||||
|
||||
$topNode = $this->Acl->{$class}->find('first', array(
|
||||
'conditions' => array($class . '.id' => $this->_getNodeId($class, $identity))
|
||||
));
|
||||
|
||||
$nodes = $this->Acl->{$class}->find('all', array(
|
||||
'conditions' => array(
|
||||
$class . '.lft >=' => $topNode[$class]['lft'],
|
||||
$class . '.lft <=' => $topNode[$class]['rght']
|
||||
),
|
||||
'order' => $class . '.lft ASC'
|
||||
));
|
||||
} else {
|
||||
$nodes = $this->Acl->{$class}->find('all', array('order' => $class . '.lft ASC'));
|
||||
}
|
||||
|
||||
if (empty($nodes)) {
|
||||
if (isset($this->args[1])) {
|
||||
$this->error(__d('cake_console', '%s not found', $this->args[1]), __d('cake_console', 'No tree returned.'));
|
||||
} elseif (isset($this->args[0])) {
|
||||
$this->error(__d('cake_console', '%s not found', $this->args[0]), __d('cake_console', 'No tree returned.'));
|
||||
}
|
||||
}
|
||||
$this->out($class . ' tree:');
|
||||
$this->hr();
|
||||
|
||||
$stack = array();
|
||||
$last = null;
|
||||
|
||||
foreach ($nodes as $n) {
|
||||
$stack[] = $n;
|
||||
if (!empty($last)) {
|
||||
$end = end($stack);
|
||||
if ($end[$class]['rght'] > $last) {
|
||||
foreach ($stack as $k => $v) {
|
||||
$end = end($stack);
|
||||
if ($v[$class]['rght'] < $end[$class]['rght']) {
|
||||
unset($stack[$k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$last = $n[$class]['rght'];
|
||||
$count = count($stack);
|
||||
|
||||
$this->_outputNode($class, $n, $count);
|
||||
}
|
||||
$this->hr();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize ACL database.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function initdb() {
|
||||
return $this->dispatchShell('schema create DbAcl');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser() {
|
||||
$parser = parent::getOptionParser();
|
||||
|
||||
$type = array(
|
||||
'choices' => array('aro', 'aco'),
|
||||
'required' => true,
|
||||
'help' => __d('cake_console', 'Type of node to create.')
|
||||
);
|
||||
|
||||
$parser->description(
|
||||
__d('cake_console', 'A console tool for managing the DbAcl')
|
||||
)->addSubcommand('create', array(
|
||||
'help' => __d('cake_console', 'Create a new ACL node'),
|
||||
'parser' => array(
|
||||
'description' => __d('cake_console', 'Creates a new ACL object <node> under the parent'),
|
||||
'epilog' => __d('cake_console', 'You can use `root` as the parent when creating nodes to create top level nodes.'),
|
||||
'arguments' => array(
|
||||
'type' => $type,
|
||||
'parent' => array(
|
||||
'help' => __d('cake_console', 'The node selector for the parent.'),
|
||||
'required' => true
|
||||
),
|
||||
'alias' => array(
|
||||
'help' => __d('cake_console', 'The alias to use for the newly created node.'),
|
||||
'required' => true
|
||||
)
|
||||
)
|
||||
)
|
||||
))->addSubcommand('delete', array(
|
||||
'help' => __d('cake_console', 'Deletes the ACL object with the given <node> reference'),
|
||||
'parser' => array(
|
||||
'description' => __d('cake_console', 'Delete an ACL node.'),
|
||||
'arguments' => array(
|
||||
'type' => $type,
|
||||
'node' => array(
|
||||
'help' => __d('cake_console', 'The node identifier to delete.'),
|
||||
'required' => true,
|
||||
)
|
||||
)
|
||||
)
|
||||
))->addSubcommand('setparent', array(
|
||||
'help' => __d('cake_console', 'Moves the ACL node under a new parent.'),
|
||||
'parser' => array(
|
||||
'description' => __d('cake_console', 'Moves the ACL object specified by <node> beneath <parent>'),
|
||||
'arguments' => array(
|
||||
'type' => $type,
|
||||
'node' => array(
|
||||
'help' => __d('cake_console', 'The node to move'),
|
||||
'required' => true,
|
||||
),
|
||||
'parent' => array(
|
||||
'help' => __d('cake_console', 'The new parent for <node>.'),
|
||||
'required' => true
|
||||
)
|
||||
)
|
||||
)
|
||||
))->addSubcommand('getpath', array(
|
||||
'help' => __d('cake_console', 'Print out the path to an ACL node.'),
|
||||
'parser' => array(
|
||||
'description' => array(
|
||||
__d('cake_console', "Returns the path to the ACL object specified by <node>."),
|
||||
__d('cake_console', "This command is useful in determining the inheritance of permissions for a certain object in the tree.")
|
||||
),
|
||||
'arguments' => array(
|
||||
'type' => $type,
|
||||
'node' => array(
|
||||
'help' => __d('cake_console', 'The node to get the path of'),
|
||||
'required' => true,
|
||||
)
|
||||
)
|
||||
)
|
||||
))->addSubcommand('check', array(
|
||||
'help' => __d('cake_console', 'Check the permissions between an ACO and ARO.'),
|
||||
'parser' => array(
|
||||
'description' => array(
|
||||
__d('cake_console', 'Use this command to check ACL permissions.')
|
||||
),
|
||||
'arguments' => array(
|
||||
'aro' => array('help' => __d('cake_console', 'ARO to check.'), 'required' => true),
|
||||
'aco' => array('help' => __d('cake_console', 'ACO to check.'), 'required' => true),
|
||||
'action' => array('help' => __d('cake_console', 'Action to check'), 'default' => 'all')
|
||||
)
|
||||
)
|
||||
))->addSubcommand('grant', array(
|
||||
'help' => __d('cake_console', 'Grant an ARO permissions to an ACO.'),
|
||||
'parser' => array(
|
||||
'description' => array(
|
||||
__d('cake_console', 'Use this command to grant ACL permissions. Once executed, the ARO specified (and its children, if any) will have ALLOW access to the specified ACO action (and the ACO\'s children, if any).')
|
||||
),
|
||||
'arguments' => array(
|
||||
'aro' => array('help' => __d('cake_console', 'ARO to grant permission to.'), 'required' => true),
|
||||
'aco' => array('help' => __d('cake_console', 'ACO to grant access to.'), 'required' => true),
|
||||
'action' => array('help' => __d('cake_console', 'Action to grant'), 'default' => 'all')
|
||||
)
|
||||
)
|
||||
))->addSubcommand('deny', array(
|
||||
'help' => __d('cake_console', 'Deny an ARO permissions to an ACO.'),
|
||||
'parser' => array(
|
||||
'description' => array(
|
||||
__d('cake_console', 'Use this command to deny ACL permissions. Once executed, the ARO specified (and its children, if any) will have DENY access to the specified ACO action (and the ACO\'s children, if any).')
|
||||
),
|
||||
'arguments' => array(
|
||||
'aro' => array('help' => __d('cake_console', 'ARO to deny.'), 'required' => true),
|
||||
'aco' => array('help' => __d('cake_console', 'ACO to deny.'), 'required' => true),
|
||||
'action' => array('help' => __d('cake_console', 'Action to deny'), 'default' => 'all')
|
||||
)
|
||||
)
|
||||
))->addSubcommand('inherit', array(
|
||||
'help' => __d('cake_console', 'Inherit an ARO\'s parent permissions.'),
|
||||
'parser' => array(
|
||||
'description' => array(
|
||||
__d('cake_console', "Use this command to force a child ARO object to inherit its permissions settings from its parent.")
|
||||
),
|
||||
'arguments' => array(
|
||||
'aro' => array('help' => __d('cake_console', 'ARO to have permissions inherit.'), 'required' => true),
|
||||
'aco' => array('help' => __d('cake_console', 'ACO to inherit permissions on.'), 'required' => true),
|
||||
'action' => array('help' => __d('cake_console', 'Action to inherit'), 'default' => 'all')
|
||||
)
|
||||
)
|
||||
))->addSubcommand('view', array(
|
||||
'help' => __d('cake_console', 'View a tree or a single node\'s subtree.'),
|
||||
'parser' => array(
|
||||
'description' => array(
|
||||
__d('cake_console', "The view command will return the ARO or ACO tree."),
|
||||
__d('cake_console', "The optional node parameter allows you to return"),
|
||||
__d('cake_console', "only a portion of the requested tree.")
|
||||
),
|
||||
'arguments' => array(
|
||||
'type' => $type,
|
||||
'node' => array('help' => __d('cake_console', 'The optional node to view the subtree of.'))
|
||||
)
|
||||
)
|
||||
))->addSubcommand('initdb', array(
|
||||
'help' => __d('cake_console', 'Initialize the DbAcl tables. Uses this command : cake schema create DbAcl')
|
||||
))->epilog(array(
|
||||
'Node and parent arguments can be in one of the following formats:',
|
||||
'',
|
||||
' - <model>.<id> - The node will be bound to a specific record of the given model.',
|
||||
'',
|
||||
' - <alias> - The node will be given a string alias (or path, in the case of <parent>)',
|
||||
" i.e. 'John'. When used with <parent>, this takes the form of an alias path,",
|
||||
" i.e. <group>/<subgroup>/<parent>.",
|
||||
'',
|
||||
"To add a node at the root level, enter 'root' or '/' as the <parent> parameter."
|
||||
));
|
||||
|
||||
return $parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that given node exists
|
||||
*
|
||||
* @return bool Success
|
||||
*/
|
||||
public function nodeExists() {
|
||||
if (!isset($this->args[0]) || !isset($this->args[1])) {
|
||||
return false;
|
||||
}
|
||||
$dataVars = $this->_dataVars($this->args[0]);
|
||||
extract($dataVars);
|
||||
$key = is_numeric($this->args[1]) ? $dataVars['secondary_id'] : 'alias';
|
||||
$conditions = array($class . '.' . $key => $this->args[1]);
|
||||
$possibility = $this->Acl->{$class}->find('all', compact('conditions'));
|
||||
if (empty($possibility)) {
|
||||
$this->error(__d('cake_console', '%s not found', $this->args[1]), __d('cake_console', 'No tree returned.'));
|
||||
}
|
||||
return $possibility;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an identifier into Model.foreignKey or an alias.
|
||||
* Takes an identifier determines its type and returns the result as used by other methods.
|
||||
*
|
||||
* @param string $identifier Identifier to parse
|
||||
* @return mixed a string for aliases, and an array for model.foreignKey
|
||||
*/
|
||||
public function parseIdentifier($identifier) {
|
||||
if (preg_match('/^([\w]+)\.(.*)$/', $identifier, $matches)) {
|
||||
return array(
|
||||
'model' => $matches[1],
|
||||
'foreign_key' => $matches[2],
|
||||
);
|
||||
}
|
||||
return $identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the node for a given identifier. $identifier can either be a string alias
|
||||
* or an array of properties to use in AcoNode::node()
|
||||
*
|
||||
* @param string $class Class type you want (Aro/Aco)
|
||||
* @param string|array $identifier A mixed identifier for finding the node.
|
||||
* @return int Integer of NodeId. Will trigger an error if nothing is found.
|
||||
*/
|
||||
protected function _getNodeId($class, $identifier) {
|
||||
$node = $this->Acl->{$class}->node($identifier);
|
||||
if (empty($node)) {
|
||||
if (is_array($identifier)) {
|
||||
$identifier = var_export($identifier, true);
|
||||
}
|
||||
$this->error(__d('cake_console', 'Could not find node using reference "%s"', $identifier));
|
||||
return;
|
||||
}
|
||||
return Hash::get($node, "0.{$class}.id");
|
||||
}
|
||||
|
||||
/**
|
||||
* get params for standard Acl methods
|
||||
*
|
||||
* @return array aro, aco, action
|
||||
*/
|
||||
protected function _getParams() {
|
||||
$aro = is_numeric($this->args[0]) ? intval($this->args[0]) : $this->args[0];
|
||||
$aco = is_numeric($this->args[1]) ? intval($this->args[1]) : $this->args[1];
|
||||
$aroName = $aro;
|
||||
$acoName = $aco;
|
||||
|
||||
if (is_string($aro)) {
|
||||
$aro = $this->parseIdentifier($aro);
|
||||
}
|
||||
if (is_string($aco)) {
|
||||
$aco = $this->parseIdentifier($aco);
|
||||
}
|
||||
$action = '*';
|
||||
if (isset($this->args[2]) && !in_array($this->args[2], array('', 'all'))) {
|
||||
$action = $this->args[2];
|
||||
}
|
||||
return compact('aro', 'aco', 'action', 'aroName', 'acoName');
|
||||
}
|
||||
|
||||
/**
|
||||
* Build data parameters based on node type
|
||||
*
|
||||
* @param string $type Node type (ARO/ACO)
|
||||
* @return array Variables
|
||||
*/
|
||||
protected function _dataVars($type = null) {
|
||||
if (!$type) {
|
||||
$type = $this->args[0];
|
||||
}
|
||||
$vars = array();
|
||||
$class = ucwords($type);
|
||||
$vars['secondary_id'] = (strtolower($class) === 'aro') ? 'foreign_key' : 'object_id';
|
||||
$vars['data_name'] = $type;
|
||||
$vars['table_name'] = $type . 's';
|
||||
$vars['class'] = $class;
|
||||
return $vars;
|
||||
}
|
||||
|
||||
}
|
||||
242
lib/Cake/Console/Command/ApiShell.php
Normal file
242
lib/Cake/Console/Command/ApiShell.php
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
<?php
|
||||
/**
|
||||
* API shell to get CakePHP core method signatures.
|
||||
*
|
||||
* Implementation of a Cake Shell to show CakePHP core method signatures.
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since CakePHP(tm) v 1.2.0.5012
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('AppShell', 'Console/Command');
|
||||
App::uses('File', 'Utility');
|
||||
|
||||
/**
|
||||
* API shell to show method signatures of CakePHP core classes.
|
||||
*
|
||||
* Implementation of a Cake Shell to show CakePHP core method signatures.
|
||||
*
|
||||
* @package Cake.Console.Command
|
||||
*/
|
||||
class ApiShell extends AppShell {
|
||||
|
||||
/**
|
||||
* Map between short name for paths and real paths.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $paths = array();
|
||||
|
||||
/**
|
||||
* Override initialize of the Shell
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function initialize() {
|
||||
$this->paths = array_merge($this->paths, array(
|
||||
'behavior' => CAKE . 'Model' . DS . 'Behavior' . DS,
|
||||
'cache' => CAKE . 'Cache' . DS,
|
||||
'controller' => CAKE . 'Controller' . DS,
|
||||
'component' => CAKE . 'Controller' . DS . 'Component' . DS,
|
||||
'helper' => CAKE . 'View' . DS . 'Helper' . DS,
|
||||
'model' => CAKE . 'Model' . DS,
|
||||
'view' => CAKE . 'View' . DS,
|
||||
'core' => CAKE
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Override main() to handle action
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function main() {
|
||||
if (empty($this->args)) {
|
||||
return $this->out($this->OptionParser->help());
|
||||
}
|
||||
|
||||
$type = strtolower($this->args[0]);
|
||||
|
||||
if (isset($this->paths[$type])) {
|
||||
$path = $this->paths[$type];
|
||||
} else {
|
||||
$path = $this->paths['core'];
|
||||
}
|
||||
|
||||
$count = count($this->args);
|
||||
if ($count > 1) {
|
||||
$file = Inflector::underscore($this->args[1]);
|
||||
$class = Inflector::camelize($this->args[1]);
|
||||
} elseif ($count) {
|
||||
$file = $type;
|
||||
$class = Inflector::camelize($type);
|
||||
}
|
||||
$objects = App::objects('class', $path);
|
||||
if (in_array($class, $objects)) {
|
||||
if (in_array($type, array('behavior', 'component', 'helper')) && $type !== $file) {
|
||||
if (!preg_match('/' . Inflector::camelize($type) . '$/', $class)) {
|
||||
$class .= Inflector::camelize($type);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
$this->error(__d('cake_console', '%s not found', $class));
|
||||
}
|
||||
|
||||
$parsed = $this->_parseClass($path . $class . '.php', $class);
|
||||
|
||||
if (!empty($parsed)) {
|
||||
if (isset($this->params['method'])) {
|
||||
if (!isset($parsed[$this->params['method']])) {
|
||||
$this->err(__d('cake_console', '%s::%s() could not be found', $class, $this->params['method']));
|
||||
return $this->_stop();
|
||||
}
|
||||
$method = $parsed[$this->params['method']];
|
||||
$this->out($class . '::' . $method['method'] . $method['parameters']);
|
||||
$this->hr();
|
||||
$this->out($method['comment'], true);
|
||||
} else {
|
||||
$this->out(ucwords($class));
|
||||
$this->hr();
|
||||
$i = 0;
|
||||
foreach ($parsed as $method) {
|
||||
$list[] = ++$i . ". " . $method['method'] . $method['parameters'];
|
||||
}
|
||||
$this->out($list);
|
||||
|
||||
$methods = array_keys($parsed);
|
||||
while ($number = strtolower($this->in(__d('cake_console', 'Select a number to see the more information about a specific method. q to quit. l to list.'), null, 'q'))) {
|
||||
if ($number === 'q') {
|
||||
$this->out(__d('cake_console', 'Done'));
|
||||
return $this->_stop();
|
||||
}
|
||||
|
||||
if ($number === 'l') {
|
||||
$this->out($list);
|
||||
}
|
||||
|
||||
if (isset($methods[--$number])) {
|
||||
$method = $parsed[$methods[$number]];
|
||||
$this->hr();
|
||||
$this->out($class . '::' . $method['method'] . $method['parameters']);
|
||||
$this->hr();
|
||||
$this->out($method['comment'], true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser() {
|
||||
$parser = parent::getOptionParser();
|
||||
|
||||
$parser->description(
|
||||
__d('cake_console', 'Lookup doc block comments for classes in CakePHP.')
|
||||
)->addArgument('type', array(
|
||||
'help' => __d('cake_console', 'Either a full path or type of class (model, behavior, controller, component, view, helper)')
|
||||
))->addArgument('className', array(
|
||||
'help' => __d('cake_console', 'A CakePHP core class name (e.g: Component, HtmlHelper).')
|
||||
))->addOption('method', array(
|
||||
'short' => 'm',
|
||||
'help' => __d('cake_console', 'The specific method you want help on.')
|
||||
));
|
||||
|
||||
return $parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show help for this shell.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function help() {
|
||||
$head = "Usage: cake api [<type>] <className> [-m <method>]\n";
|
||||
$head .= "-----------------------------------------------\n";
|
||||
$head .= "Parameters:\n\n";
|
||||
|
||||
$commands = array(
|
||||
'path' => "\t<type>\n" .
|
||||
"\t\tEither a full path or type of class (model, behavior, controller, component, view, helper).\n" .
|
||||
"\t\tAvailable values:\n\n" .
|
||||
"\t\tbehavior\tLook for class in CakePHP behavior path\n" .
|
||||
"\t\tcache\tLook for class in CakePHP cache path\n" .
|
||||
"\t\tcontroller\tLook for class in CakePHP controller path\n" .
|
||||
"\t\tcomponent\tLook for class in CakePHP component path\n" .
|
||||
"\t\thelper\tLook for class in CakePHP helper path\n" .
|
||||
"\t\tmodel\tLook for class in CakePHP model path\n" .
|
||||
"\t\tview\tLook for class in CakePHP view path\n",
|
||||
'className' => "\t<className>\n" .
|
||||
"\t\tA CakePHP core class name (e.g: Component, HtmlHelper).\n"
|
||||
);
|
||||
|
||||
$this->out($head);
|
||||
if (!isset($this->args[1])) {
|
||||
foreach ($commands as $cmd) {
|
||||
$this->out("{$cmd}\n\n");
|
||||
}
|
||||
} elseif (isset($commands[strtolower($this->args[1])])) {
|
||||
$this->out($commands[strtolower($this->args[1])] . "\n\n");
|
||||
} else {
|
||||
$this->out(__d('cake_console', 'Command %s not found', $this->args[1]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a given class (located on given file) and get public methods and their
|
||||
* signatures.
|
||||
*
|
||||
* @param string $path File path
|
||||
* @param string $class Class name
|
||||
* @return array Methods and signatures indexed by method name
|
||||
*/
|
||||
protected function _parseClass($path, $class) {
|
||||
$parsed = array();
|
||||
|
||||
if (!class_exists($class)) {
|
||||
if (!include_once $path) {
|
||||
$this->err(__d('cake_console', '%s could not be found', $path));
|
||||
}
|
||||
}
|
||||
|
||||
$reflection = new ReflectionClass($class);
|
||||
|
||||
foreach ($reflection->getMethods() as $method) {
|
||||
if (!$method->isPublic() || strpos($method->getName(), '_') === 0) {
|
||||
continue;
|
||||
}
|
||||
if ($method->getDeclaringClass()->getName() != $class) {
|
||||
continue;
|
||||
}
|
||||
$args = array();
|
||||
foreach ($method->getParameters() as $param) {
|
||||
$paramString = '$' . $param->getName();
|
||||
if ($param->isDefaultValueAvailable()) {
|
||||
$paramString .= ' = ' . str_replace("\n", '', var_export($param->getDefaultValue(), true));
|
||||
}
|
||||
$args[] = $paramString;
|
||||
}
|
||||
$parsed[$method->getName()] = array(
|
||||
'comment' => str_replace(array('/*', '*/', '*'), '', $method->getDocComment()),
|
||||
'method' => $method->getName(),
|
||||
'parameters' => '(' . implode(', ', $args) . ')'
|
||||
);
|
||||
}
|
||||
ksort($parsed);
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
}
|
||||
30
lib/Cake/Console/Command/AppShell.php
Normal file
30
lib/Cake/Console/Command/AppShell.php
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
/**
|
||||
* AppShell file
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since CakePHP(tm) v 2.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('Shell', 'Console');
|
||||
|
||||
/**
|
||||
* Application Shell
|
||||
*
|
||||
* Add your application-wide methods in the class below, your shells
|
||||
* will inherit them.
|
||||
*
|
||||
* @package app.Console.Command
|
||||
*/
|
||||
class AppShell extends Shell {
|
||||
|
||||
}
|
||||
255
lib/Cake/Console/Command/BakeShell.php
Normal file
255
lib/Cake/Console/Command/BakeShell.php
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
<?php
|
||||
/**
|
||||
* Command-line code generation utility to automate programmer chores.
|
||||
*
|
||||
* Bake is CakePHP's code generation script, which can help you kickstart
|
||||
* application development by writing fully functional skeleton controllers,
|
||||
* models, and views. Going further, Bake can also write Unit Tests for you.
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since CakePHP(tm) v 1.2.0.5012
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('AppShell', 'Console/Command');
|
||||
App::uses('Model', 'Model');
|
||||
|
||||
/**
|
||||
* Command-line code generation utility to automate programmer chores.
|
||||
*
|
||||
* Bake is CakePHP's code generation script, which can help you kickstart
|
||||
* application development by writing fully functional skeleton controllers,
|
||||
* models, and views. Going further, Bake can also write Unit Tests for you.
|
||||
*
|
||||
* @package Cake.Console.Command
|
||||
* @link http://book.cakephp.org/2.0/en/console-and-shells/code-generation-with-bake.html
|
||||
*/
|
||||
class BakeShell extends AppShell {
|
||||
|
||||
/**
|
||||
* Contains tasks to load and instantiate
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $tasks = array('Project', 'DbConfig', 'Model', 'Controller', 'View', 'Plugin', 'Fixture', 'Test');
|
||||
|
||||
/**
|
||||
* The connection being used.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $connection = 'default';
|
||||
|
||||
/**
|
||||
* Assign $this->connection to the active task if a connection param is set.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function startup() {
|
||||
parent::startup();
|
||||
Configure::write('debug', 2);
|
||||
Configure::write('Cache.disable', 1);
|
||||
|
||||
$task = Inflector::classify($this->command);
|
||||
if (isset($this->{$task}) && !in_array($task, array('Project', 'DbConfig'))) {
|
||||
if (isset($this->params['connection'])) {
|
||||
$this->{$task}->connection = $this->params['connection'];
|
||||
}
|
||||
}
|
||||
if (isset($this->params['connection'])) {
|
||||
$this->connection = $this->params['connection'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override main() to handle action
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function main() {
|
||||
if (!is_dir($this->DbConfig->path)) {
|
||||
$path = $this->Project->execute();
|
||||
if (!empty($path)) {
|
||||
$this->DbConfig->path = $path . 'Config' . DS;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!config('database')) {
|
||||
$this->out(__d('cake_console', 'Your database configuration was not found. Take a moment to create one.'));
|
||||
$this->args = null;
|
||||
return $this->DbConfig->execute();
|
||||
}
|
||||
$this->out(__d('cake_console', 'Interactive Bake Shell'));
|
||||
$this->hr();
|
||||
$this->out(__d('cake_console', '[D]atabase Configuration'));
|
||||
$this->out(__d('cake_console', '[M]odel'));
|
||||
$this->out(__d('cake_console', '[V]iew'));
|
||||
$this->out(__d('cake_console', '[C]ontroller'));
|
||||
$this->out(__d('cake_console', '[P]roject'));
|
||||
$this->out(__d('cake_console', '[F]ixture'));
|
||||
$this->out(__d('cake_console', '[T]est case'));
|
||||
$this->out(__d('cake_console', '[Q]uit'));
|
||||
|
||||
$classToBake = strtoupper($this->in(__d('cake_console', 'What would you like to Bake?'), array('D', 'M', 'V', 'C', 'P', 'F', 'T', 'Q')));
|
||||
switch ($classToBake) {
|
||||
case 'D':
|
||||
$this->DbConfig->execute();
|
||||
break;
|
||||
case 'M':
|
||||
$this->Model->execute();
|
||||
break;
|
||||
case 'V':
|
||||
$this->View->execute();
|
||||
break;
|
||||
case 'C':
|
||||
$this->Controller->execute();
|
||||
break;
|
||||
case 'P':
|
||||
$this->Project->execute();
|
||||
break;
|
||||
case 'F':
|
||||
$this->Fixture->execute();
|
||||
break;
|
||||
case 'T':
|
||||
$this->Test->execute();
|
||||
break;
|
||||
case 'Q':
|
||||
return $this->_stop();
|
||||
default:
|
||||
$this->out(__d('cake_console', 'You have made an invalid selection. Please choose a type of class to Bake by entering D, M, V, F, T, or C.'));
|
||||
}
|
||||
$this->hr();
|
||||
$this->main();
|
||||
}
|
||||
|
||||
/**
|
||||
* Quickly bake the MVC
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function all() {
|
||||
$this->out('Bake All');
|
||||
$this->hr();
|
||||
|
||||
if (!isset($this->params['connection']) && empty($this->connection)) {
|
||||
$this->connection = $this->DbConfig->getConfig();
|
||||
}
|
||||
|
||||
if (empty($this->args)) {
|
||||
$this->Model->interactive = true;
|
||||
$name = $this->Model->getName($this->connection);
|
||||
}
|
||||
|
||||
foreach (array('Model', 'Controller', 'View') as $task) {
|
||||
$this->{$task}->connection = $this->connection;
|
||||
$this->{$task}->interactive = false;
|
||||
}
|
||||
|
||||
if (!empty($this->args[0])) {
|
||||
$name = $this->args[0];
|
||||
}
|
||||
|
||||
$modelExists = false;
|
||||
$model = $this->_modelName($name);
|
||||
|
||||
App::uses('AppModel', 'Model');
|
||||
App::uses($model, 'Model');
|
||||
if (class_exists($model)) {
|
||||
$object = new $model();
|
||||
$modelExists = true;
|
||||
} else {
|
||||
$object = new Model(array('name' => $name, 'ds' => $this->connection));
|
||||
}
|
||||
|
||||
$modelBaked = $this->Model->bake($object, false);
|
||||
|
||||
if ($modelBaked && $modelExists === false) {
|
||||
if ($this->_checkUnitTest()) {
|
||||
$this->Model->bakeFixture($model);
|
||||
$this->Model->bakeTest($model);
|
||||
}
|
||||
$modelExists = true;
|
||||
}
|
||||
|
||||
if ($modelExists === true) {
|
||||
$controller = $this->_controllerName($name);
|
||||
if ($this->Controller->bake($controller, $this->Controller->bakeActions($controller))) {
|
||||
if ($this->_checkUnitTest()) {
|
||||
$this->Controller->bakeTest($controller);
|
||||
}
|
||||
}
|
||||
App::uses($controller . 'Controller', 'Controller');
|
||||
if (class_exists($controller . 'Controller')) {
|
||||
$this->View->args = array($name);
|
||||
$this->View->execute();
|
||||
}
|
||||
$this->out('', 1, Shell::QUIET);
|
||||
$this->out(__d('cake_console', '<success>Bake All complete</success>'), 1, Shell::QUIET);
|
||||
array_shift($this->args);
|
||||
} else {
|
||||
$this->error(__d('cake_console', 'Bake All could not continue without a valid model'));
|
||||
}
|
||||
return $this->_stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser() {
|
||||
$parser = parent::getOptionParser();
|
||||
|
||||
$parser->description(
|
||||
__d('cake_console', 'The Bake script generates controllers, views and models for your application.' .
|
||||
' If run with no command line arguments, Bake guides the user through the class creation process.' .
|
||||
' You can customize the generation process by telling Bake where different parts of your application are using command line arguments.')
|
||||
)->addSubcommand('all', array(
|
||||
'help' => __d('cake_console', 'Bake a complete MVC. optional <name> of a Model')
|
||||
))->addSubcommand('project', array(
|
||||
'help' => __d('cake_console', 'Bake a new app folder in the path supplied or in current directory if no path is specified'),
|
||||
'parser' => $this->Project->getOptionParser()
|
||||
))->addSubcommand('plugin', array(
|
||||
'help' => __d('cake_console', 'Bake a new plugin folder in the path supplied or in current directory if no path is specified.'),
|
||||
'parser' => $this->Plugin->getOptionParser()
|
||||
))->addSubcommand('db_config', array(
|
||||
'help' => __d('cake_console', 'Bake a database.php file in config directory.'),
|
||||
'parser' => $this->DbConfig->getOptionParser()
|
||||
))->addSubcommand('model', array(
|
||||
'help' => __d('cake_console', 'Bake a model.'),
|
||||
'parser' => $this->Model->getOptionParser()
|
||||
))->addSubcommand('view', array(
|
||||
'help' => __d('cake_console', 'Bake views for controllers.'),
|
||||
'parser' => $this->View->getOptionParser()
|
||||
))->addSubcommand('controller', array(
|
||||
'help' => __d('cake_console', 'Bake a controller.'),
|
||||
'parser' => $this->Controller->getOptionParser()
|
||||
))->addSubcommand('fixture', array(
|
||||
'help' => __d('cake_console', 'Bake a fixture.'),
|
||||
'parser' => $this->Fixture->getOptionParser()
|
||||
))->addSubcommand('test', array(
|
||||
'help' => __d('cake_console', 'Bake a unit test.'),
|
||||
'parser' => $this->Test->getOptionParser()
|
||||
))->addOption('connection', array(
|
||||
'help' => __d('cake_console', 'Database connection to use in conjunction with `bake all`.'),
|
||||
'short' => 'c',
|
||||
'default' => 'default'
|
||||
))->addOption('theme', array(
|
||||
'short' => 't',
|
||||
'help' => __d('cake_console', 'Theme to use when baking code.')
|
||||
));
|
||||
|
||||
return $parser;
|
||||
}
|
||||
|
||||
}
|
||||
143
lib/Cake/Console/Command/CommandListShell.php
Normal file
143
lib/Cake/Console/Command/CommandListShell.php
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP Project
|
||||
* @package Cake.Console.Command
|
||||
* @since CakePHP v 2.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('AppShell', 'Console/Command');
|
||||
App::uses('Inflector', 'Utility');
|
||||
|
||||
/**
|
||||
* Shows a list of commands available from the console.
|
||||
*
|
||||
* @package Cake.Console.Command
|
||||
*/
|
||||
class CommandListShell extends AppShell {
|
||||
|
||||
/**
|
||||
* Contains tasks to load and instantiate
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $tasks = array('Command');
|
||||
|
||||
/**
|
||||
* startup
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function startup() {
|
||||
if (empty($this->params['xml'])) {
|
||||
parent::startup();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function Prints out the list of shells.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function main() {
|
||||
if (empty($this->params['xml'])) {
|
||||
$this->out(__d('cake_console', "<info>Current Paths:</info>"), 2);
|
||||
$this->out(" -app: " . APP_DIR);
|
||||
$this->out(" -working: " . rtrim(APP, DS));
|
||||
$this->out(" -root: " . rtrim(ROOT, DS));
|
||||
$this->out(" -core: " . rtrim(CORE_PATH, DS));
|
||||
$this->out("");
|
||||
$this->out(__d('cake_console', "<info>Changing Paths:</info>"), 2);
|
||||
$this->out(__d('cake_console', "Your working path should be the same as your application path. To change your path use the '-app' param."));
|
||||
$this->out(__d('cake_console', "Example: %s or %s", '-app relative/path/to/myapp', '-app /absolute/path/to/myapp'), 2);
|
||||
|
||||
$this->out(__d('cake_console', "<info>Available Shells:</info>"), 2);
|
||||
}
|
||||
|
||||
$shellList = $this->Command->getShellList();
|
||||
if (empty($shellList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($this->params['xml'])) {
|
||||
$this->_asText($shellList);
|
||||
} else {
|
||||
$this->_asXml($shellList);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output text.
|
||||
*
|
||||
* @param array $shellList The shell list.
|
||||
* @return void
|
||||
*/
|
||||
protected function _asText($shellList) {
|
||||
foreach ($shellList as $plugin => $commands) {
|
||||
sort($commands);
|
||||
$this->out(sprintf('[<info>%s</info>] %s', $plugin, implode(', ', $commands)));
|
||||
$this->out();
|
||||
}
|
||||
|
||||
$this->out(__d('cake_console', "To run an app or core command, type <info>cake shell_name [args]</info>"));
|
||||
$this->out(__d('cake_console', "To run a plugin command, type <info>cake Plugin.shell_name [args]</info>"));
|
||||
$this->out(__d('cake_console', "To get help on a specific command, type <info>cake shell_name --help</info>"), 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Output as XML
|
||||
*
|
||||
* @param array $shellList The shell list.
|
||||
* @return void
|
||||
*/
|
||||
protected function _asXml($shellList) {
|
||||
$plugins = CakePlugin::loaded();
|
||||
$shells = new SimpleXmlElement('<shells></shells>');
|
||||
foreach ($shellList as $plugin => $commands) {
|
||||
foreach ($commands as $command) {
|
||||
$callable = $command;
|
||||
if (in_array($plugin, $plugins)) {
|
||||
$callable = Inflector::camelize($plugin) . '.' . $command;
|
||||
}
|
||||
|
||||
$shell = $shells->addChild('shell');
|
||||
$shell->addAttribute('name', $command);
|
||||
$shell->addAttribute('call_as', $callable);
|
||||
$shell->addAttribute('provider', $plugin);
|
||||
$shell->addAttribute('help', $callable . ' -h');
|
||||
}
|
||||
}
|
||||
$this->stdout->outputAs(ConsoleOutput::RAW);
|
||||
$this->out($shells->saveXml());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser() {
|
||||
$parser = parent::getOptionParser();
|
||||
|
||||
$parser->description(
|
||||
__d('cake_console', 'Get the list of available shells for this CakePHP application.')
|
||||
)->addOption('sort', array(
|
||||
'help' => __d('cake_console', 'Does nothing (deprecated)'),
|
||||
'boolean' => true
|
||||
))->addOption('xml', array(
|
||||
'help' => __d('cake_console', 'Get the listing as XML.'),
|
||||
'boolean' => true
|
||||
));
|
||||
|
||||
return $parser;
|
||||
}
|
||||
|
||||
}
|
||||
155
lib/Cake/Console/Command/CompletionShell.php
Normal file
155
lib/Cake/Console/Command/CompletionShell.php
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP Project
|
||||
* @package Cake.Console.Command
|
||||
* @since CakePHP v 2.5
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('AppShell', 'Console/Command');
|
||||
|
||||
/**
|
||||
* Provide command completion shells such as bash.
|
||||
*
|
||||
* @package Cake.Console.Command
|
||||
*/
|
||||
class CompletionShell extends AppShell {
|
||||
|
||||
/**
|
||||
* Contains tasks to load and instantiate
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $tasks = array('Command');
|
||||
|
||||
/**
|
||||
* Echo no header by overriding the startup method
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function startup() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Not called by the autocomplete shell - this is for curious users
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function main() {
|
||||
return $this->out($this->getOptionParser()->help());
|
||||
}
|
||||
|
||||
/**
|
||||
* list commands
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function commands() {
|
||||
$options = $this->Command->commands();
|
||||
return $this->_output($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* list options for the named command
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function options() {
|
||||
$commandName = '';
|
||||
if (!empty($this->args[0])) {
|
||||
$commandName = $this->args[0];
|
||||
}
|
||||
$options = $this->Command->options($commandName);
|
||||
|
||||
return $this->_output($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* list subcommands for the named command
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function subCommands() {
|
||||
if (!$this->args) {
|
||||
return $this->_output();
|
||||
}
|
||||
|
||||
$options = $this->Command->subCommands($this->args[0]);
|
||||
return $this->_output($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Guess autocomplete from the whole argument string
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function fuzzy() {
|
||||
return $this->_output();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser() {
|
||||
$parser = parent::getOptionParser();
|
||||
|
||||
$parser->description(
|
||||
__d('cake_console', 'Used by shells like bash to autocomplete command name, options and arguments')
|
||||
)->addSubcommand('commands', array(
|
||||
'help' => __d('cake_console', 'Output a list of available commands'),
|
||||
'parser' => array(
|
||||
'description' => __d('cake_console', 'List all availables'),
|
||||
'arguments' => array(
|
||||
)
|
||||
)
|
||||
))->addSubcommand('subcommands', array(
|
||||
'help' => __d('cake_console', 'Output a list of available subcommands'),
|
||||
'parser' => array(
|
||||
'description' => __d('cake_console', 'List subcommands for a command'),
|
||||
'arguments' => array(
|
||||
'command' => array(
|
||||
'help' => __d('cake_console', 'The command name'),
|
||||
'required' => true,
|
||||
)
|
||||
)
|
||||
)
|
||||
))->addSubcommand('options', array(
|
||||
'help' => __d('cake_console', 'Output a list of available options'),
|
||||
'parser' => array(
|
||||
'description' => __d('cake_console', 'List options'),
|
||||
'arguments' => array(
|
||||
'command' => array(
|
||||
'help' => __d('cake_console', 'The command name'),
|
||||
'required' => false,
|
||||
)
|
||||
)
|
||||
)
|
||||
))->epilog(
|
||||
__d('cake_console', 'This command is not intended to be called manually')
|
||||
);
|
||||
|
||||
return $parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit results as a string, space delimited
|
||||
*
|
||||
* @param array $options The options to output
|
||||
* @return void
|
||||
*/
|
||||
protected function _output($options = array()) {
|
||||
if ($options) {
|
||||
return $this->out(implode($options, ' '));
|
||||
}
|
||||
}
|
||||
}
|
||||
514
lib/Cake/Console/Command/ConsoleShell.php
Normal file
514
lib/Cake/Console/Command/ConsoleShell.php
Normal file
|
|
@ -0,0 +1,514 @@
|
|||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since CakePHP(tm) v 1.2.0.5012
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('AppShell', 'Console/Command');
|
||||
|
||||
/**
|
||||
* Provides a very basic 'interactive' console for CakePHP apps.
|
||||
*
|
||||
* @package Cake.Console.Command
|
||||
* @deprecated Deprecated since version 2.4, will be removed in 3.0
|
||||
*/
|
||||
class ConsoleShell extends AppShell {
|
||||
|
||||
/**
|
||||
* Available binding types
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $associations = array('hasOne', 'hasMany', 'belongsTo', 'hasAndBelongsToMany');
|
||||
|
||||
/**
|
||||
* Chars that describe invalid commands
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $badCommandChars = array('$', ';');
|
||||
|
||||
/**
|
||||
* Available models
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $models = array();
|
||||
|
||||
/**
|
||||
* _finished
|
||||
*
|
||||
* This shell is perpetual, setting this property to true exits the process
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $_finished = false;
|
||||
|
||||
/**
|
||||
* _methodPatterns
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_methodPatterns = array(
|
||||
'help' => '/^(help|\?)/',
|
||||
'_exit' => '/^(quit|exit)/',
|
||||
'_models' => '/^models/i',
|
||||
'_bind' => '/^(\w+) bind (\w+) (\w+)/',
|
||||
'_unbind' => '/^(\w+) unbind (\w+) (\w+)/',
|
||||
'_find' => '/.+->find/',
|
||||
'_save' => '/.+->save/',
|
||||
'_columns' => '/^(\w+) columns/',
|
||||
'_routesReload' => '/^routes\s+reload/i',
|
||||
'_routesShow' => '/^routes\s+show/i',
|
||||
'_routeToString' => '/^route\s+(\(.*\))$/i',
|
||||
'_routeToArray' => '/^route\s+(.*)$/i',
|
||||
);
|
||||
|
||||
/**
|
||||
* Override startup of the Shell
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function startup() {
|
||||
App::uses('Dispatcher', 'Routing');
|
||||
$this->Dispatcher = new Dispatcher();
|
||||
$this->models = App::objects('Model');
|
||||
|
||||
foreach ($this->models as $model) {
|
||||
$class = $model;
|
||||
App::uses($class, 'Model');
|
||||
$this->{$class} = new $class();
|
||||
}
|
||||
$this->out(__d('cake_console', 'Model classes:'));
|
||||
$this->hr();
|
||||
|
||||
foreach ($this->models as $model) {
|
||||
$this->out(" - {$model}");
|
||||
}
|
||||
|
||||
if (!$this->_loadRoutes()) {
|
||||
$message = __d(
|
||||
'cake_console',
|
||||
'There was an error loading the routes config. Please check that the file exists and contains no errors.'
|
||||
);
|
||||
$this->err($message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser() {
|
||||
$parser = parent::getOptionParser();
|
||||
|
||||
$parser->description(array(
|
||||
'The interactive console is a tool for testing parts of your',
|
||||
'app before you write code.',
|
||||
'',
|
||||
'See below for a list of supported commands.'
|
||||
))->epilog(array(
|
||||
'<info>Model testing</info>',
|
||||
'',
|
||||
'To test model results, use the name of your model without a leading $',
|
||||
'e.g. Foo->find("all")',
|
||||
"",
|
||||
'To dynamically set associations, you can do the following:',
|
||||
'',
|
||||
"\tModelA bind <association> ModelB",
|
||||
'',
|
||||
"where the supported associations are hasOne, hasMany, belongsTo, hasAndBelongsToMany",
|
||||
"",
|
||||
'To dynamically remove associations, you can do the following:',
|
||||
'',
|
||||
"\t ModelA unbind <association> ModelB",
|
||||
'',
|
||||
"where the supported associations are the same as above",
|
||||
"",
|
||||
"To save a new field in a model, you can do the following:",
|
||||
'',
|
||||
"\tModelA->save(array('foo' => 'bar', 'baz' => 0))",
|
||||
'',
|
||||
"where you are passing a hash of data to be saved in the format",
|
||||
"of field => value pairs",
|
||||
"",
|
||||
"To get column information for a model, use the following:",
|
||||
'',
|
||||
"\tModelA columns",
|
||||
'',
|
||||
"which returns a list of columns and their type",
|
||||
"",
|
||||
'<info>Route testing</info>',
|
||||
"",
|
||||
'To test URLs against your app\'s route configuration, type:',
|
||||
"",
|
||||
"\tRoute <url>",
|
||||
"",
|
||||
"where url is the path to your your action plus any query parameters,",
|
||||
"minus the application's base path. For example:",
|
||||
"",
|
||||
"\tRoute /posts/view/1",
|
||||
"",
|
||||
"will return something like the following:",
|
||||
"",
|
||||
"\tarray(",
|
||||
"\t [...]",
|
||||
"\t 'controller' => 'posts',",
|
||||
"\t 'action' => 'view',",
|
||||
"\t [...]",
|
||||
"\t)",
|
||||
"",
|
||||
'Alternatively, you can use simple array syntax to test reverse',
|
||||
'To reload your routes config (Config/routes.php), do the following:',
|
||||
"",
|
||||
"\tRoutes reload",
|
||||
"",
|
||||
'To show all connected routes, do the following:',
|
||||
'',
|
||||
"\tRoutes show",
|
||||
));
|
||||
|
||||
return $parser;
|
||||
}
|
||||
/**
|
||||
* Prints the help message
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function help() {
|
||||
$optionParser = $this->getOptionParser();
|
||||
$this->out($optionParser->epilog());
|
||||
}
|
||||
|
||||
/**
|
||||
* Override main() to handle action
|
||||
*
|
||||
* @param string $command The command to run.
|
||||
* @return void
|
||||
*/
|
||||
public function main($command = null) {
|
||||
$this->_finished = false;
|
||||
while (!$this->_finished) {
|
||||
if (empty($command)) {
|
||||
$command = trim($this->in(''));
|
||||
}
|
||||
|
||||
$method = $this->_method($command);
|
||||
|
||||
if ($method) {
|
||||
$this->$method($command);
|
||||
} else {
|
||||
$this->out(__d('cake_console', "Invalid command"));
|
||||
$this->out();
|
||||
}
|
||||
$command = '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the method to process the current command
|
||||
*
|
||||
* @param string $command The command to run.
|
||||
* @return string or false
|
||||
*/
|
||||
protected function _method($command) {
|
||||
foreach ($this->_methodPatterns as $method => $pattern) {
|
||||
if (preg_match($pattern, $command)) {
|
||||
return $method;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the finiished property so that the loop in main method ends
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _exit() {
|
||||
$this->_finished = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* List all models
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _models() {
|
||||
$this->out(__d('cake_console', 'Model classes:'));
|
||||
$this->hr();
|
||||
foreach ($this->models as $model) {
|
||||
$this->out(" - {$model}");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind an association
|
||||
*
|
||||
* @param mixed $command The command to run.
|
||||
* @return void
|
||||
*/
|
||||
protected function _bind($command) {
|
||||
preg_match($this->_methodPatterns[__FUNCTION__], $command, $tmp);
|
||||
|
||||
foreach ($tmp as $data) {
|
||||
$data = strip_tags($data);
|
||||
$data = str_replace($this->badCommandChars, "", $data);
|
||||
}
|
||||
|
||||
$modelA = $tmp[1];
|
||||
$association = $tmp[2];
|
||||
$modelB = $tmp[3];
|
||||
|
||||
if ($this->_isValidModel($modelA) && $this->_isValidModel($modelB) && in_array($association, $this->associations)) {
|
||||
$this->{$modelA}->bindModel(array($association => array($modelB => array('className' => $modelB))), false);
|
||||
$this->out(__d('cake_console', "Created %s association between %s and %s",
|
||||
$association, $modelA, $modelB));
|
||||
} else {
|
||||
$this->out(__d('cake_console', "Please verify you are using valid models and association types"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unbind an association
|
||||
*
|
||||
* @param mixed $command The command to run.
|
||||
* @return void
|
||||
*/
|
||||
protected function _unbind($command) {
|
||||
preg_match($this->_methodPatterns[__FUNCTION__], $command, $tmp);
|
||||
|
||||
foreach ($tmp as $data) {
|
||||
$data = strip_tags($data);
|
||||
$data = str_replace($this->badCommandChars, "", $data);
|
||||
}
|
||||
|
||||
$modelA = $tmp[1];
|
||||
$association = $tmp[2];
|
||||
$modelB = $tmp[3];
|
||||
|
||||
// Verify that there is actually an association to unbind
|
||||
$currentAssociations = $this->{$modelA}->getAssociated();
|
||||
$validCurrentAssociation = false;
|
||||
|
||||
foreach ($currentAssociations as $model => $currentAssociation) {
|
||||
if ($model === $modelB && $association === $currentAssociation) {
|
||||
$validCurrentAssociation = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->_isValidModel($modelA) && $this->_isValidModel($modelB) && in_array($association, $this->associations) && $validCurrentAssociation) {
|
||||
$this->{$modelA}->unbindModel(array($association => array($modelB)));
|
||||
$this->out(__d('cake_console', "Removed %s association between %s and %s",
|
||||
$association, $modelA, $modelB));
|
||||
} else {
|
||||
$this->out(__d('cake_console', "Please verify you are using valid models, valid current association, and valid association types"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a find
|
||||
*
|
||||
* @param mixed $command The command to run.
|
||||
* @return void
|
||||
*/
|
||||
protected function _find($command) {
|
||||
$command = strip_tags($command);
|
||||
$command = str_replace($this->badCommandChars, "", $command);
|
||||
|
||||
// Do we have a valid model?
|
||||
list($modelToCheck) = explode('->', $command);
|
||||
|
||||
if ($this->_isValidModel($modelToCheck)) {
|
||||
$findCommand = "\$data = \$this->$command;";
|
||||
//@codingStandardsIgnoreStart
|
||||
@eval($findCommand);
|
||||
//@codingStandardsIgnoreEnd
|
||||
|
||||
if (is_array($data)) {
|
||||
foreach ($data as $idx => $results) {
|
||||
if (is_numeric($idx)) { // findAll() output
|
||||
foreach ($results as $modelName => $result) {
|
||||
$this->out("$modelName");
|
||||
|
||||
foreach ($result as $field => $value) {
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $field2 => $value2) {
|
||||
$this->out("\t$field2: $value2");
|
||||
}
|
||||
|
||||
$this->out();
|
||||
} else {
|
||||
$this->out("\t$field: $value");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { // find() output
|
||||
$this->out($idx);
|
||||
|
||||
foreach ($results as $field => $value) {
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $field2 => $value2) {
|
||||
$this->out("\t$field2: $value2");
|
||||
}
|
||||
|
||||
$this->out();
|
||||
} else {
|
||||
$this->out("\t$field: $value");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->out();
|
||||
$this->out(__d('cake_console', "No result set found"));
|
||||
}
|
||||
} else {
|
||||
$this->out(__d('cake_console', "%s is not a valid model", $modelToCheck));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a record
|
||||
*
|
||||
* @param mixed $command The command to run.
|
||||
* @return void
|
||||
*/
|
||||
protected function _save($command) {
|
||||
// Validate the model we're trying to save here
|
||||
$command = strip_tags($command);
|
||||
$command = str_replace($this->badCommandChars, "", $command);
|
||||
list($modelToSave) = explode("->", $command);
|
||||
|
||||
if ($this->_isValidModel($modelToSave)) {
|
||||
// Extract the array of data we are trying to build
|
||||
list(, $data) = explode("->save", $command);
|
||||
$data = preg_replace('/^\(*(array)?\(*(.+?)\)*$/i', '\\2', $data);
|
||||
$saveCommand = "\$this->{$modelToSave}->save(array('{$modelToSave}' => array({$data})));";
|
||||
//@codingStandardsIgnoreStart
|
||||
@eval($saveCommand);
|
||||
//@codingStandardsIgnoreEnd
|
||||
$this->out(__d('cake_console', 'Saved record for %s', $modelToSave));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the columns for a model
|
||||
*
|
||||
* @param mixed $command The command to run.
|
||||
* @return void
|
||||
*/
|
||||
protected function _columns($command) {
|
||||
preg_match($this->_methodPatterns[__FUNCTION__], $command, $tmp);
|
||||
|
||||
$modelToCheck = strip_tags(str_replace($this->badCommandChars, "", $tmp[1]));
|
||||
|
||||
if ($this->_isValidModel($modelToCheck)) {
|
||||
// Get the column info for this model
|
||||
$fieldsCommand = "\$data = \$this->{$modelToCheck}->getColumnTypes();";
|
||||
//@codingStandardsIgnoreStart
|
||||
@eval($fieldsCommand);
|
||||
//@codingStandardsIgnoreEnd
|
||||
|
||||
if (is_array($data)) {
|
||||
foreach ($data as $field => $type) {
|
||||
$this->out("\t{$field}: {$type}");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->out(__d('cake_console', "Please verify that you selected a valid model"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload route definitions
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _routesReload() {
|
||||
if (!$this->_loadRoutes()) {
|
||||
return $this->err(__d('cake_console', "There was an error loading the routes config. Please check that the file exists and is free of parse errors."));
|
||||
}
|
||||
$this->out(__d('cake_console', "Routes configuration reloaded, %d routes connected", count(Router::$routes)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show all routes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _routesShow() {
|
||||
$this->out(print_r(Hash::combine(Router::$routes, '{n}.template', '{n}.defaults'), true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an array URL and show the equivalent URL as a string
|
||||
*
|
||||
* @param mixed $command The command to run.
|
||||
* @return void
|
||||
*/
|
||||
protected function _routeToString($command) {
|
||||
preg_match($this->_methodPatterns[__FUNCTION__], $command, $tmp);
|
||||
|
||||
//@codingStandardsIgnoreStart
|
||||
if ($url = eval('return array' . $tmp[1] . ';')) {
|
||||
//@codingStandardsIgnoreEnd
|
||||
$this->out(Router::url($url));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a string URL and show as an array
|
||||
*
|
||||
* @param mixed $command The command to run.
|
||||
* @return void
|
||||
*/
|
||||
protected function _routeToArray($command) {
|
||||
preg_match($this->_methodPatterns[__FUNCTION__], $command, $tmp);
|
||||
|
||||
$this->out(var_export(Router::parse($tmp[1]), true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if the specified model is included in the list of available models
|
||||
*
|
||||
* @param string $modelToCheck The model to check.
|
||||
* @return bool true if is an available model, false otherwise
|
||||
*/
|
||||
protected function _isValidModel($modelToCheck) {
|
||||
return in_array($modelToCheck, $this->models);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the routes configuration from app/Config/routes.php, and compiles
|
||||
* all routes found
|
||||
*
|
||||
* @return bool True if config reload was a success, otherwise false
|
||||
*/
|
||||
protected function _loadRoutes() {
|
||||
Router::reload();
|
||||
extract(Router::getNamedExpressions());
|
||||
|
||||
//@codingStandardsIgnoreStart
|
||||
if (!@include APP . 'Config' . DS . 'routes.php') {
|
||||
//@codingStandardsIgnoreEnd
|
||||
return false;
|
||||
}
|
||||
CakePlugin::routes();
|
||||
|
||||
Router::parse('/');
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
122
lib/Cake/Console/Command/I18nShell.php
Normal file
122
lib/Cake/Console/Command/I18nShell.php
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
<?php
|
||||
/**
|
||||
* Internationalization Management Shell
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since CakePHP(tm) v 1.2.0.5669
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('AppShell', 'Console/Command');
|
||||
|
||||
/**
|
||||
* Shell for I18N management.
|
||||
*
|
||||
* @package Cake.Console.Command
|
||||
*/
|
||||
class I18nShell extends AppShell {
|
||||
|
||||
/**
|
||||
* Contains database source to use
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $dataSource = 'default';
|
||||
|
||||
/**
|
||||
* Contains tasks to load and instantiate
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $tasks = array('DbConfig', 'Extract');
|
||||
|
||||
/**
|
||||
* Override startup of the Shell
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function startup() {
|
||||
$this->_welcome();
|
||||
if (isset($this->params['datasource'])) {
|
||||
$this->dataSource = $this->params['datasource'];
|
||||
}
|
||||
|
||||
if ($this->command && !in_array($this->command, array('help'))) {
|
||||
if (!config('database')) {
|
||||
$this->out(__d('cake_console', 'Your database configuration was not found. Take a moment to create one.'));
|
||||
return $this->DbConfig->execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override main() for help message hook
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function main() {
|
||||
$this->out(__d('cake_console', '<info>I18n Shell</info>'));
|
||||
$this->hr();
|
||||
$this->out(__d('cake_console', '[E]xtract POT file from sources'));
|
||||
$this->out(__d('cake_console', '[I]nitialize i18n database table'));
|
||||
$this->out(__d('cake_console', '[H]elp'));
|
||||
$this->out(__d('cake_console', '[Q]uit'));
|
||||
|
||||
$choice = strtolower($this->in(__d('cake_console', 'What would you like to do?'), array('E', 'I', 'H', 'Q')));
|
||||
switch ($choice) {
|
||||
case 'e':
|
||||
$this->Extract->execute();
|
||||
break;
|
||||
case 'i':
|
||||
$this->initdb();
|
||||
break;
|
||||
case 'h':
|
||||
$this->out($this->OptionParser->help());
|
||||
break;
|
||||
case 'q':
|
||||
return $this->_stop();
|
||||
default:
|
||||
$this->out(__d('cake_console', 'You have made an invalid selection. Please choose a command to execute by entering E, I, H, or Q.'));
|
||||
}
|
||||
$this->hr();
|
||||
$this->main();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize I18N database.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function initdb() {
|
||||
$this->dispatchShell('schema create i18n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser() {
|
||||
$parser = parent::getOptionParser();
|
||||
|
||||
$parser->description(
|
||||
__d('cake_console', 'I18n Shell initializes i18n database table for your application and generates .pot files(s) with translations.')
|
||||
)->addSubcommand('initdb', array(
|
||||
'help' => __d('cake_console', 'Initialize the i18n table.')
|
||||
))->addSubcommand('extract', array(
|
||||
'help' => __d('cake_console', 'Extract the po translations from your application'),
|
||||
'parser' => $this->Extract->getOptionParser()
|
||||
));
|
||||
|
||||
return $parser;
|
||||
}
|
||||
|
||||
}
|
||||
577
lib/Cake/Console/Command/SchemaShell.php
Normal file
577
lib/Cake/Console/Command/SchemaShell.php
Normal file
|
|
@ -0,0 +1,577 @@
|
|||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since CakePHP(tm) v 1.2.0.5550
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('AppShell', 'Console/Command');
|
||||
App::uses('File', 'Utility');
|
||||
App::uses('Folder', 'Utility');
|
||||
App::uses('CakeSchema', 'Model');
|
||||
|
||||
/**
|
||||
* Schema is a command-line database management utility for automating programmer chores.
|
||||
*
|
||||
* Schema is CakePHP's database management utility. This helps you maintain versions of
|
||||
* of your database.
|
||||
*
|
||||
* @package Cake.Console.Command
|
||||
* @link http://book.cakephp.org/2.0/en/console-and-shells/schema-management-and-migrations.html
|
||||
*/
|
||||
class SchemaShell extends AppShell {
|
||||
|
||||
/**
|
||||
* Schema class being used.
|
||||
*
|
||||
* @var CakeSchema
|
||||
*/
|
||||
public $Schema;
|
||||
|
||||
/**
|
||||
* is this a dry run?
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $_dry = null;
|
||||
|
||||
/**
|
||||
* Override startup
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function startup() {
|
||||
$this->_welcome();
|
||||
$this->out('Cake Schema Shell');
|
||||
$this->hr();
|
||||
|
||||
Configure::write('Cache.disable', 1);
|
||||
|
||||
$name = $path = $connection = $plugin = null;
|
||||
if (!empty($this->params['name'])) {
|
||||
$name = $this->params['name'];
|
||||
} elseif (!empty($this->args[0]) && $this->args[0] !== 'snapshot') {
|
||||
$name = $this->params['name'] = $this->args[0];
|
||||
}
|
||||
|
||||
if (strpos($name, '.')) {
|
||||
list($this->params['plugin'], $splitName) = pluginSplit($name);
|
||||
$name = $this->params['name'] = $splitName;
|
||||
}
|
||||
|
||||
$defaultFile = 'schema.php';
|
||||
if (empty($this->params['file'])) {
|
||||
$this->params['file'] = $defaultFile;
|
||||
}
|
||||
if ($name && $this->params['file'] === $defaultFile) {
|
||||
$this->params['file'] = Inflector::underscore($name);
|
||||
}
|
||||
if (strpos($this->params['file'], '.php') === false) {
|
||||
$this->params['file'] .= '.php';
|
||||
}
|
||||
$file = $this->params['file'];
|
||||
|
||||
if (!empty($this->params['path'])) {
|
||||
$path = $this->params['path'];
|
||||
}
|
||||
|
||||
if (!empty($this->params['connection'])) {
|
||||
$connection = $this->params['connection'];
|
||||
}
|
||||
if (!empty($this->params['plugin'])) {
|
||||
$plugin = $this->params['plugin'];
|
||||
if (empty($name)) {
|
||||
$name = $plugin;
|
||||
}
|
||||
}
|
||||
$name = Inflector::classify($name);
|
||||
$this->Schema = new CakeSchema(compact('name', 'path', 'file', 'connection', 'plugin'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and output contents of schema object
|
||||
* path to read as second arg
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function view() {
|
||||
$File = new File($this->Schema->path . DS . $this->params['file']);
|
||||
if ($File->exists()) {
|
||||
$this->out($File->read());
|
||||
return $this->_stop();
|
||||
}
|
||||
$file = $this->Schema->path . DS . $this->params['file'];
|
||||
$this->err(__d('cake_console', 'Schema file (%s) could not be found.', $file));
|
||||
return $this->_stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read database and Write schema object
|
||||
* accepts a connection as first arg or path to save as second arg
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function generate() {
|
||||
$this->out(__d('cake_console', 'Generating Schema...'));
|
||||
$options = array();
|
||||
if ($this->params['force']) {
|
||||
$options['models'] = false;
|
||||
} elseif (!empty($this->params['models'])) {
|
||||
$options['models'] = String::tokenize($this->params['models']);
|
||||
}
|
||||
|
||||
$snapshot = false;
|
||||
if (isset($this->args[0]) && $this->args[0] === 'snapshot') {
|
||||
$snapshot = true;
|
||||
}
|
||||
|
||||
if (!$snapshot && file_exists($this->Schema->path . DS . $this->params['file'])) {
|
||||
$snapshot = true;
|
||||
$prompt = __d('cake_console', "Schema file exists.\n [O]verwrite\n [S]napshot\n [Q]uit\nWould you like to do?");
|
||||
$result = strtolower($this->in($prompt, array('o', 's', 'q'), 's'));
|
||||
if ($result === 'q') {
|
||||
return $this->_stop();
|
||||
}
|
||||
if ($result === 'o') {
|
||||
$snapshot = false;
|
||||
}
|
||||
}
|
||||
|
||||
$cacheDisable = Configure::read('Cache.disable');
|
||||
Configure::write('Cache.disable', true);
|
||||
|
||||
$content = $this->Schema->read($options);
|
||||
$content['file'] = $this->params['file'];
|
||||
|
||||
Configure::write('Cache.disable', $cacheDisable);
|
||||
|
||||
if (!empty($this->params['exclude']) && !empty($content)) {
|
||||
$excluded = String::tokenize($this->params['exclude']);
|
||||
foreach ($excluded as $table) {
|
||||
unset($content['tables'][$table]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($snapshot === true) {
|
||||
$fileName = rtrim($this->params['file'], '.php');
|
||||
$Folder = new Folder($this->Schema->path);
|
||||
$result = $Folder->read();
|
||||
|
||||
$numToUse = false;
|
||||
if (isset($this->params['snapshot'])) {
|
||||
$numToUse = $this->params['snapshot'];
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
if (!empty($result[1])) {
|
||||
foreach ($result[1] as $file) {
|
||||
if (preg_match('/' . preg_quote($fileName) . '(?:[_\d]*)?\.php$/', $file)) {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($numToUse !== false) {
|
||||
if ($numToUse > $count) {
|
||||
$count = $numToUse;
|
||||
}
|
||||
}
|
||||
|
||||
$content['file'] = $fileName . '_' . $count . '.php';
|
||||
}
|
||||
|
||||
if ($this->Schema->write($content)) {
|
||||
$this->out(__d('cake_console', 'Schema file: %s generated', $content['file']));
|
||||
return $this->_stop();
|
||||
}
|
||||
$this->err(__d('cake_console', 'Schema file: %s generated'));
|
||||
return $this->_stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump Schema object to sql file
|
||||
* Use the `write` param to enable and control SQL file output location.
|
||||
* Simply using -write will write the sql file to the same dir as the schema file.
|
||||
* If -write contains a full path name the file will be saved there. If -write only
|
||||
* contains no DS, that will be used as the file name, in the same dir as the schema file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function dump() {
|
||||
$write = false;
|
||||
$Schema = $this->Schema->load();
|
||||
if (!$Schema) {
|
||||
$this->err(__d('cake_console', 'Schema could not be loaded'));
|
||||
return $this->_stop();
|
||||
}
|
||||
if (!empty($this->params['write'])) {
|
||||
if ($this->params['write'] == 1) {
|
||||
$write = Inflector::underscore($this->Schema->name);
|
||||
} else {
|
||||
$write = $this->params['write'];
|
||||
}
|
||||
}
|
||||
$db = ConnectionManager::getDataSource($this->Schema->connection);
|
||||
$contents = "\n\n" . $db->dropSchema($Schema) . "\n\n" . $db->createSchema($Schema);
|
||||
|
||||
if ($write) {
|
||||
if (strpos($write, '.sql') === false) {
|
||||
$write .= '.sql';
|
||||
}
|
||||
if (strpos($write, DS) !== false) {
|
||||
$File = new File($write, true);
|
||||
} else {
|
||||
$File = new File($this->Schema->path . DS . $write, true);
|
||||
}
|
||||
|
||||
if ($File->write($contents)) {
|
||||
$this->out(__d('cake_console', 'SQL dump file created in %s', $File->pwd()));
|
||||
return $this->_stop();
|
||||
}
|
||||
$this->err(__d('cake_console', 'SQL dump could not be created'));
|
||||
return $this->_stop();
|
||||
}
|
||||
$this->out($contents);
|
||||
return $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run database create commands. Alias for run create.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function create() {
|
||||
list($Schema, $table) = $this->_loadSchema();
|
||||
$this->_create($Schema, $table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run database create commands. Alias for run create.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function update() {
|
||||
list($Schema, $table) = $this->_loadSchema();
|
||||
$this->_update($Schema, $table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the Schema objects for database operations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _loadSchema() {
|
||||
$name = $plugin = null;
|
||||
if (!empty($this->params['name'])) {
|
||||
$name = $this->params['name'];
|
||||
}
|
||||
if (!empty($this->params['plugin'])) {
|
||||
$plugin = $this->params['plugin'];
|
||||
}
|
||||
|
||||
if (!empty($this->params['dry'])) {
|
||||
$this->_dry = true;
|
||||
$this->out(__d('cake_console', 'Performing a dry run.'));
|
||||
}
|
||||
|
||||
$options = array(
|
||||
'name' => $name,
|
||||
'plugin' => $plugin,
|
||||
'connection' => $this->params['connection'],
|
||||
);
|
||||
if (!empty($this->params['snapshot'])) {
|
||||
$fileName = rtrim($this->Schema->file, '.php');
|
||||
$options['file'] = $fileName . '_' . $this->params['snapshot'] . '.php';
|
||||
}
|
||||
|
||||
$Schema = $this->Schema->load($options);
|
||||
|
||||
if (!$Schema) {
|
||||
$this->err(__d('cake_console', 'The chosen schema could not be loaded. Attempted to load:'));
|
||||
$this->err(__d('cake_console', 'File: %s', $this->Schema->path . DS . $this->Schema->file));
|
||||
$this->err(__d('cake_console', 'Name: %s', $this->Schema->name));
|
||||
return $this->_stop();
|
||||
}
|
||||
$table = null;
|
||||
if (isset($this->args[1])) {
|
||||
$table = $this->args[1];
|
||||
}
|
||||
return array(&$Schema, $table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create database from Schema object
|
||||
* Should be called via the run method
|
||||
*
|
||||
* @param CakeSchema $Schema The schema instance to create.
|
||||
* @param string $table The table name.
|
||||
* @return void
|
||||
*/
|
||||
protected function _create(CakeSchema $Schema, $table = null) {
|
||||
$db = ConnectionManager::getDataSource($this->Schema->connection);
|
||||
|
||||
$drop = $create = array();
|
||||
|
||||
if (!$table) {
|
||||
foreach ($Schema->tables as $table => $fields) {
|
||||
$drop[$table] = $db->dropSchema($Schema, $table);
|
||||
$create[$table] = $db->createSchema($Schema, $table);
|
||||
}
|
||||
} elseif (isset($Schema->tables[$table])) {
|
||||
$drop[$table] = $db->dropSchema($Schema, $table);
|
||||
$create[$table] = $db->createSchema($Schema, $table);
|
||||
}
|
||||
if (empty($drop) || empty($create)) {
|
||||
$this->out(__d('cake_console', 'Schema is up to date.'));
|
||||
return $this->_stop();
|
||||
}
|
||||
|
||||
$this->out("\n" . __d('cake_console', 'The following table(s) will be dropped.'));
|
||||
$this->out(array_keys($drop));
|
||||
|
||||
if (
|
||||
!empty($this->params['yes']) ||
|
||||
$this->in(__d('cake_console', 'Are you sure you want to drop the table(s)?'), array('y', 'n'), 'n') === 'y'
|
||||
) {
|
||||
$this->out(__d('cake_console', 'Dropping table(s).'));
|
||||
$this->_run($drop, 'drop', $Schema);
|
||||
}
|
||||
|
||||
$this->out("\n" . __d('cake_console', 'The following table(s) will be created.'));
|
||||
$this->out(array_keys($create));
|
||||
|
||||
if (
|
||||
!empty($this->params['yes']) ||
|
||||
$this->in(__d('cake_console', 'Are you sure you want to create the table(s)?'), array('y', 'n'), 'y') === 'y'
|
||||
) {
|
||||
$this->out(__d('cake_console', 'Creating table(s).'));
|
||||
$this->_run($create, 'create', $Schema);
|
||||
}
|
||||
$this->out(__d('cake_console', 'End create.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update database with Schema object
|
||||
* Should be called via the run method
|
||||
*
|
||||
* @param CakeSchema &$Schema The schema instance
|
||||
* @param string $table The table name.
|
||||
* @return void
|
||||
*/
|
||||
protected function _update(&$Schema, $table = null) {
|
||||
$db = ConnectionManager::getDataSource($this->Schema->connection);
|
||||
|
||||
$this->out(__d('cake_console', 'Comparing Database to Schema...'));
|
||||
$options = array();
|
||||
if (isset($this->params['force'])) {
|
||||
$options['models'] = false;
|
||||
}
|
||||
$Old = $this->Schema->read($options);
|
||||
$compare = $this->Schema->compare($Old, $Schema);
|
||||
|
||||
$contents = array();
|
||||
|
||||
if (empty($table)) {
|
||||
foreach ($compare as $table => $changes) {
|
||||
if (isset($compare[$table]['create'])) {
|
||||
$contents[$table] = $db->createSchema($Schema, $table);
|
||||
} else {
|
||||
$contents[$table] = $db->alterSchema(array($table => $compare[$table]), $table);
|
||||
}
|
||||
}
|
||||
} elseif (isset($compare[$table])) {
|
||||
if (isset($compare[$table]['create'])) {
|
||||
$contents[$table] = $db->createSchema($Schema, $table);
|
||||
} else {
|
||||
$contents[$table] = $db->alterSchema(array($table => $compare[$table]), $table);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($contents)) {
|
||||
$this->out(__d('cake_console', 'Schema is up to date.'));
|
||||
return $this->_stop();
|
||||
}
|
||||
|
||||
$this->out("\n" . __d('cake_console', 'The following statements will run.'));
|
||||
$this->out(array_map('trim', $contents));
|
||||
if (
|
||||
!empty($this->params['yes']) ||
|
||||
$this->in(__d('cake_console', 'Are you sure you want to alter the tables?'), array('y', 'n'), 'n') === 'y'
|
||||
) {
|
||||
$this->out();
|
||||
$this->out(__d('cake_console', 'Updating Database...'));
|
||||
$this->_run($contents, 'update', $Schema);
|
||||
}
|
||||
|
||||
$this->out(__d('cake_console', 'End update.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs sql from _create() or _update()
|
||||
*
|
||||
* @param array $contents The contents to execute.
|
||||
* @param string $event The event to fire
|
||||
* @param CakeSchema $Schema The schema instance.
|
||||
* @return void
|
||||
*/
|
||||
protected function _run($contents, $event, CakeSchema $Schema) {
|
||||
if (empty($contents)) {
|
||||
$this->err(__d('cake_console', 'Sql could not be run'));
|
||||
return;
|
||||
}
|
||||
Configure::write('debug', 2);
|
||||
$db = ConnectionManager::getDataSource($this->Schema->connection);
|
||||
|
||||
foreach ($contents as $table => $sql) {
|
||||
if (empty($sql)) {
|
||||
$this->out(__d('cake_console', '%s is up to date.', $table));
|
||||
} else {
|
||||
if ($this->_dry === true) {
|
||||
$this->out(__d('cake_console', 'Dry run for %s :', $table));
|
||||
$this->out($sql);
|
||||
} else {
|
||||
if (!$Schema->before(array($event => $table))) {
|
||||
return false;
|
||||
}
|
||||
$error = null;
|
||||
try {
|
||||
$db->execute($sql);
|
||||
} catch (PDOException $e) {
|
||||
$error = $table . ': ' . $e->getMessage();
|
||||
}
|
||||
|
||||
$Schema->after(array($event => $table, 'errors' => $error));
|
||||
|
||||
if (!empty($error)) {
|
||||
$this->err($error);
|
||||
} else {
|
||||
$this->out(__d('cake_console', '%s updated.', $table));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser() {
|
||||
$parser = parent::getOptionParser();
|
||||
|
||||
$plugin = array(
|
||||
'short' => 'p',
|
||||
'help' => __d('cake_console', 'The plugin to use.'),
|
||||
);
|
||||
$connection = array(
|
||||
'short' => 'c',
|
||||
'help' => __d('cake_console', 'Set the db config to use.'),
|
||||
'default' => 'default'
|
||||
);
|
||||
$path = array(
|
||||
'help' => __d('cake_console', 'Path to read and write schema.php'),
|
||||
'default' => APP . 'Config' . DS . 'Schema'
|
||||
);
|
||||
$file = array(
|
||||
'help' => __d('cake_console', 'File name to read and write.'),
|
||||
'default' => 'schema.php'
|
||||
);
|
||||
$name = array(
|
||||
'help' => __d('cake_console',
|
||||
'Classname to use. If its Plugin.class, both name and plugin options will be set.'
|
||||
)
|
||||
);
|
||||
$snapshot = array(
|
||||
'short' => 's',
|
||||
'help' => __d('cake_console', 'Snapshot number to use/make.')
|
||||
);
|
||||
$models = array(
|
||||
'short' => 'm',
|
||||
'help' => __d('cake_console', 'Specify models as comma separated list.'),
|
||||
);
|
||||
$dry = array(
|
||||
'help' => __d('cake_console',
|
||||
'Perform a dry run on create and update commands. Queries will be output instead of run.'
|
||||
),
|
||||
'boolean' => true
|
||||
);
|
||||
$force = array(
|
||||
'short' => 'f',
|
||||
'help' => __d('cake_console', 'Force "generate" to create a new schema'),
|
||||
'boolean' => true
|
||||
);
|
||||
$write = array(
|
||||
'help' => __d('cake_console', 'Write the dumped SQL to a file.')
|
||||
);
|
||||
$exclude = array(
|
||||
'help' => __d('cake_console', 'Tables to exclude as comma separated list.')
|
||||
);
|
||||
$yes = array(
|
||||
'short' => 'y',
|
||||
'help' => __d('cake_console', 'Do not prompt for confirmation. Be careful!'),
|
||||
'boolean' => true
|
||||
);
|
||||
|
||||
$parser->description(
|
||||
__d('cake_console', 'The Schema Shell generates a schema object from the database and updates the database from the schema.')
|
||||
)->addSubcommand('view', array(
|
||||
'help' => __d('cake_console', 'Read and output the contents of a schema file'),
|
||||
'parser' => array(
|
||||
'options' => compact('plugin', 'path', 'file', 'name', 'connection'),
|
||||
'arguments' => compact('name')
|
||||
)
|
||||
))->addSubcommand('generate', array(
|
||||
'help' => __d('cake_console', 'Reads from --connection and writes to --path. Generate snapshots with -s'),
|
||||
'parser' => array(
|
||||
'options' => compact('plugin', 'path', 'file', 'name', 'connection', 'snapshot', 'force', 'models', 'exclude'),
|
||||
'arguments' => array(
|
||||
'snapshot' => array('help' => __d('cake_console', 'Generate a snapshot.'))
|
||||
)
|
||||
)
|
||||
))->addSubcommand('dump', array(
|
||||
'help' => __d('cake_console', 'Dump database SQL based on a schema file to stdout.'),
|
||||
'parser' => array(
|
||||
'options' => compact('plugin', 'path', 'file', 'name', 'connection', 'write'),
|
||||
'arguments' => compact('name')
|
||||
)
|
||||
))->addSubcommand('create', array(
|
||||
'help' => __d('cake_console', 'Drop and create tables based on the schema file.'),
|
||||
'parser' => array(
|
||||
'options' => compact('plugin', 'path', 'file', 'name', 'connection', 'dry', 'snapshot', 'yes'),
|
||||
'args' => array(
|
||||
'name' => array(
|
||||
'help' => __d('cake_console', 'Name of schema to use.')
|
||||
),
|
||||
'table' => array(
|
||||
'help' => __d('cake_console', 'Only create the specified table.')
|
||||
)
|
||||
)
|
||||
)
|
||||
))->addSubcommand('update', array(
|
||||
'help' => __d('cake_console', 'Alter the tables based on the schema file.'),
|
||||
'parser' => array(
|
||||
'options' => compact('plugin', 'path', 'file', 'name', 'connection', 'dry', 'snapshot', 'force', 'yes'),
|
||||
'args' => array(
|
||||
'name' => array(
|
||||
'help' => __d('cake_console', 'Name of schema to use.')
|
||||
),
|
||||
'table' => array(
|
||||
'help' => __d('cake_console', 'Only create the specified table.')
|
||||
)
|
||||
)
|
||||
)
|
||||
));
|
||||
|
||||
return $parser;
|
||||
}
|
||||
|
||||
}
|
||||
167
lib/Cake/Console/Command/ServerShell.php
Normal file
167
lib/Cake/Console/Command/ServerShell.php
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
<?php
|
||||
/**
|
||||
* built-in Server Shell
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since CakePHP(tm) v 2.3.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('AppShell', 'Console/Command');
|
||||
|
||||
/**
|
||||
* built-in Server Shell
|
||||
*
|
||||
* @package Cake.Console.Command
|
||||
*/
|
||||
class ServerShell extends AppShell {
|
||||
|
||||
/**
|
||||
* Default ServerHost
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const DEFAULT_HOST = 'localhost';
|
||||
|
||||
/**
|
||||
* Default ListenPort
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const DEFAULT_PORT = 80;
|
||||
|
||||
/**
|
||||
* server host
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_host = null;
|
||||
|
||||
/**
|
||||
* listen port
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_port = null;
|
||||
|
||||
/**
|
||||
* document root
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_documentRoot = null;
|
||||
|
||||
/**
|
||||
* Override initialize of the Shell
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function initialize() {
|
||||
$this->_host = self::DEFAULT_HOST;
|
||||
$this->_port = self::DEFAULT_PORT;
|
||||
$this->_documentRoot = WWW_ROOT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts up the Shell and displays the welcome message.
|
||||
* Allows for checking and configuring prior to command or main execution
|
||||
*
|
||||
* Override this method if you want to remove the welcome information,
|
||||
* or otherwise modify the pre-command flow.
|
||||
*
|
||||
* @return void
|
||||
* @link http://book.cakephp.org/2.0/en/console-and-shells.html#Shell::startup
|
||||
*/
|
||||
public function startup() {
|
||||
if (!empty($this->params['host'])) {
|
||||
$this->_host = $this->params['host'];
|
||||
}
|
||||
if (!empty($this->params['port'])) {
|
||||
$this->_port = $this->params['port'];
|
||||
}
|
||||
if (!empty($this->params['document_root'])) {
|
||||
$this->_documentRoot = $this->params['document_root'];
|
||||
}
|
||||
|
||||
// for windows
|
||||
if (substr($this->_documentRoot, -1, 1) === DIRECTORY_SEPARATOR) {
|
||||
$this->_documentRoot = substr($this->_documentRoot, 0, strlen($this->_documentRoot) - 1);
|
||||
}
|
||||
if (preg_match("/^([a-z]:)[\\\]+(.+)$/i", $this->_documentRoot, $m)) {
|
||||
$this->_documentRoot = $m[1] . '\\' . $m[2];
|
||||
}
|
||||
|
||||
parent::startup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a header for the shell
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _welcome() {
|
||||
$this->out();
|
||||
$this->out(__d('cake_console', '<info>Welcome to CakePHP %s Console</info>', 'v' . Configure::version()));
|
||||
$this->hr();
|
||||
$this->out(__d('cake_console', 'App : %s', APP_DIR));
|
||||
$this->out(__d('cake_console', 'Path: %s', APP));
|
||||
$this->out(__d('cake_console', 'DocumentRoot: %s', $this->_documentRoot));
|
||||
$this->hr();
|
||||
}
|
||||
|
||||
/**
|
||||
* Override main() to handle action
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function main() {
|
||||
if (version_compare(PHP_VERSION, '5.4.0') < 0) {
|
||||
$this->out(__d('cake_console', '<warning>This command is available on %s or above</warning>', 'PHP5.4'));
|
||||
return;
|
||||
}
|
||||
|
||||
$command = sprintf("php -S %s:%d -t %s %s",
|
||||
$this->_host,
|
||||
$this->_port,
|
||||
escapeshellarg($this->_documentRoot),
|
||||
escapeshellarg($this->_documentRoot . '/index.php')
|
||||
);
|
||||
|
||||
$port = ($this->_port == self::DEFAULT_PORT) ? '' : ':' . $this->_port;
|
||||
$this->out(__d('cake_console', 'built-in server is running in http://%s%s/', $this->_host, $port));
|
||||
system($command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser() {
|
||||
$parser = parent::getOptionParser();
|
||||
|
||||
$parser->description(array(
|
||||
__d('cake_console', 'PHP Built-in Server for CakePHP'),
|
||||
__d('cake_console', '<warning>[WARN] Don\'t use this at the production environment</warning>')
|
||||
))->addOption('host', array(
|
||||
'short' => 'H',
|
||||
'help' => __d('cake_console', 'ServerHost')
|
||||
))->addOption('port', array(
|
||||
'short' => 'p',
|
||||
'help' => __d('cake_console', 'ListenPort')
|
||||
))->addOption('document_root', array(
|
||||
'short' => 'd',
|
||||
'help' => __d('cake_console', 'DocumentRoot')
|
||||
));
|
||||
|
||||
return $parser;
|
||||
}
|
||||
}
|
||||
92
lib/Cake/Console/Command/Task/BakeTask.php
Normal file
92
lib/Cake/Console/Command/Task/BakeTask.php
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
/**
|
||||
* Base class for Bake Tasks.
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since CakePHP(tm) v 1.3
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('AppShell', 'Console/Command');
|
||||
|
||||
/**
|
||||
* Base class for Bake Tasks.
|
||||
*
|
||||
* @package Cake.Console.Command.Task
|
||||
*/
|
||||
class BakeTask extends AppShell {
|
||||
|
||||
/**
|
||||
* Name of plugin
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $plugin = null;
|
||||
|
||||
/**
|
||||
* The db connection being used for baking
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $connection = null;
|
||||
|
||||
/**
|
||||
* Flag for interactive mode
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $interactive = false;
|
||||
|
||||
/**
|
||||
* Disable caching and enable debug for baking.
|
||||
* This forces the most current database schema to be used.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function startup() {
|
||||
Configure::write('debug', 2);
|
||||
Configure::write('Cache.disable', 1);
|
||||
parent::startup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the path for output. Checks the plugin property
|
||||
* and returns the correct path.
|
||||
*
|
||||
* @return string Path to output.
|
||||
*/
|
||||
public function getPath() {
|
||||
$path = $this->path;
|
||||
if (isset($this->plugin)) {
|
||||
$path = $this->_pluginPath($this->plugin) . $this->name . DS;
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base execute method parses some parameters and sets some properties on the bake tasks.
|
||||
* call when overriding execute()
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute() {
|
||||
foreach ($this->args as $i => $arg) {
|
||||
if (strpos($arg, '.')) {
|
||||
list($this->params['plugin'], $this->args[$i]) = pluginSplit($arg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isset($this->params['plugin'])) {
|
||||
$this->plugin = $this->params['plugin'];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
183
lib/Cake/Console/Command/Task/CommandTask.php
Normal file
183
lib/Cake/Console/Command/Task/CommandTask.php
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since CakePHP(tm) v 2.5
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('AppShell', 'Console/Command');
|
||||
|
||||
/**
|
||||
* Base class for Shell Command reflection.
|
||||
*
|
||||
* @package Cake.Console.Command.Task
|
||||
*/
|
||||
class CommandTask extends AppShell {
|
||||
|
||||
/**
|
||||
* Gets the shell command listing.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getShellList() {
|
||||
$skipFiles = array('AppShell');
|
||||
|
||||
$plugins = CakePlugin::loaded();
|
||||
$shellList = array_fill_keys($plugins, null) + array('CORE' => null, 'app' => null);
|
||||
|
||||
$corePath = App::core('Console/Command');
|
||||
$shells = App::objects('file', $corePath[0]);
|
||||
$shells = array_diff($shells, $skipFiles);
|
||||
$this->_appendShells('CORE', $shells, $shellList);
|
||||
|
||||
$appShells = App::objects('Console/Command', null, false);
|
||||
$appShells = array_diff($appShells, $shells, $skipFiles);
|
||||
$this->_appendShells('app', $appShells, $shellList);
|
||||
|
||||
foreach ($plugins as $plugin) {
|
||||
$pluginShells = App::objects($plugin . '.Console/Command');
|
||||
$this->_appendShells($plugin, $pluginShells, $shellList);
|
||||
}
|
||||
|
||||
return array_filter($shellList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the provided paths for shells, and append them into $shellList
|
||||
*
|
||||
* @param string $type The type of object.
|
||||
* @param array $shells The shell name.
|
||||
* @param array &$shellList List of shells.
|
||||
* @return void
|
||||
*/
|
||||
protected function _appendShells($type, $shells, &$shellList) {
|
||||
foreach ($shells as $shell) {
|
||||
$shellList[$type][] = Inflector::underscore(str_replace('Shell', '', $shell));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of all commands
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function commands() {
|
||||
$shellList = $this->getShellList();
|
||||
|
||||
$options = array();
|
||||
foreach ($shellList as $type => $commands) {
|
||||
$prefix = '';
|
||||
if (!in_array(strtolower($type), array('app', 'core'))) {
|
||||
$prefix = $type . '.';
|
||||
}
|
||||
|
||||
foreach ($commands as $shell) {
|
||||
$options[] = $prefix . $shell;
|
||||
}
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of subcommands for a given command
|
||||
*
|
||||
* @param string $commandName The command you want subcommands from.
|
||||
* @return array
|
||||
*/
|
||||
public function subCommands($commandName) {
|
||||
$Shell = $this->getShell($commandName);
|
||||
|
||||
if (!$Shell) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$taskMap = TaskCollection::normalizeObjectArray((array)$Shell->tasks);
|
||||
$return = array_keys($taskMap);
|
||||
$return = array_map('Inflector::underscore', $return);
|
||||
|
||||
$ShellReflection = new ReflectionClass('AppShell');
|
||||
$shellMethods = $ShellReflection->getMethods(ReflectionMethod::IS_PUBLIC);
|
||||
$shellMethodNames = array('main', 'help');
|
||||
foreach ($shellMethods as $method) {
|
||||
$shellMethodNames[] = $method->getName();
|
||||
}
|
||||
|
||||
$Reflection = new ReflectionClass($Shell);
|
||||
$methods = $Reflection->getMethods(ReflectionMethod::IS_PUBLIC);
|
||||
$methodNames = array();
|
||||
foreach ($methods as $method) {
|
||||
$methodNames[] = $method->getName();
|
||||
}
|
||||
|
||||
$return += array_diff($methodNames, $shellMethodNames);
|
||||
sort($return);
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Shell instance for the given command
|
||||
*
|
||||
* @param mixed $commandName The command you want.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getShell($commandName) {
|
||||
list($pluginDot, $name) = pluginSplit($commandName, true);
|
||||
|
||||
if (in_array(strtolower($pluginDot), array('app.', 'core.'))) {
|
||||
$commandName = $name;
|
||||
$pluginDot = '';
|
||||
}
|
||||
|
||||
if (!in_array($commandName, $this->commands())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$name = Inflector::camelize($name);
|
||||
$pluginDot = Inflector::camelize($pluginDot);
|
||||
$class = $name . 'Shell';
|
||||
App::uses($class, $pluginDot . 'Console/Command');
|
||||
|
||||
$Shell = new $class();
|
||||
$Shell->plugin = trim($pluginDot, '.');
|
||||
$Shell->initialize();
|
||||
|
||||
return $Shell;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Shell instance for the given command
|
||||
*
|
||||
* @param mixed $commandName The command to get options for.
|
||||
* @return array
|
||||
*/
|
||||
public function options($commandName) {
|
||||
$Shell = $this->getShell($commandName);
|
||||
if (!$Shell) {
|
||||
$parser = new ConsoleOptionParser();
|
||||
} else {
|
||||
$parser = $Shell->getOptionParser();
|
||||
}
|
||||
|
||||
$options = array();
|
||||
$array = $parser->options();
|
||||
foreach ($array as $name => $obj) {
|
||||
$options[] = "--$name";
|
||||
$short = $obj->short();
|
||||
if ($short) {
|
||||
$options[] = "-$short";
|
||||
}
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
}
|
||||
508
lib/Cake/Console/Command/Task/ControllerTask.php
Normal file
508
lib/Cake/Console/Command/Task/ControllerTask.php
Normal file
|
|
@ -0,0 +1,508 @@
|
|||
<?php
|
||||
/**
|
||||
* The ControllerTask handles creating and updating controller files.
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since CakePHP(tm) v 1.2
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('AppShell', 'Console/Command');
|
||||
App::uses('BakeTask', 'Console/Command/Task');
|
||||
App::uses('AppModel', 'Model');
|
||||
|
||||
/**
|
||||
* Task class for creating and updating controller files.
|
||||
*
|
||||
* @package Cake.Console.Command.Task
|
||||
*/
|
||||
class ControllerTask extends BakeTask {
|
||||
|
||||
/**
|
||||
* Tasks to be loaded by this Task
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $tasks = array('Model', 'Test', 'Template', 'DbConfig', 'Project');
|
||||
|
||||
/**
|
||||
* path to Controller directory
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $path = null;
|
||||
|
||||
/**
|
||||
* Override initialize
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function initialize() {
|
||||
$this->path = current(App::path('Controller'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Execution method always used for tasks
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute() {
|
||||
parent::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 = '';
|
||||
|
||||
if (!empty($this->params['public'])) {
|
||||
$this->out(__d('cake_console', 'Baking basic crud methods for ') . $controller);
|
||||
$actions .= $this->bakeActions($controller);
|
||||
}
|
||||
if (!empty($this->params['admin'])) {
|
||||
$admin = $this->Project->getPrefix();
|
||||
if ($admin) {
|
||||
$this->out(__d('cake_console', 'Adding %s methods', $admin));
|
||||
$actions .= "\n" . $this->bakeActions($controller, $admin);
|
||||
}
|
||||
}
|
||||
if (empty($actions)) {
|
||||
$actions = 'scaffold';
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function all() {
|
||||
$this->interactive = false;
|
||||
$this->listAll($this->connection, false);
|
||||
ClassRegistry::config('Model', array('ds' => $this->connection));
|
||||
$unitTestExists = $this->_checkUnitTest();
|
||||
|
||||
$admin = false;
|
||||
if (!empty($this->params['admin'])) {
|
||||
$admin = $this->Project->getPrefix();
|
||||
}
|
||||
|
||||
$controllersCreated = 0;
|
||||
foreach ($this->__tables as $table) {
|
||||
$model = $this->_modelName($table);
|
||||
$controller = $this->_controllerName($model);
|
||||
App::uses($model, 'Model');
|
||||
if (class_exists($model)) {
|
||||
$actions = $this->bakeActions($controller);
|
||||
if ($admin) {
|
||||
$this->out(__d('cake_console', 'Adding %s methods', $admin));
|
||||
$actions .= "\n" . $this->bakeActions($controller, $admin);
|
||||
}
|
||||
if ($this->bake($controller, $actions) && $unitTestExists) {
|
||||
$this->bakeTest($controller);
|
||||
}
|
||||
$controllersCreated++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$controllersCreated) {
|
||||
$this->out(__d('cake_console', 'No Controllers were baked, Models need to exist before Controllers can be baked.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interactive
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _interactive() {
|
||||
$this->interactive = true;
|
||||
$this->hr();
|
||||
$this->out(__d('cake_console', "Bake Controller\nPath: %s", $this->getPath()));
|
||||
$this->hr();
|
||||
|
||||
if (empty($this->connection)) {
|
||||
$this->connection = $this->DbConfig->getConfig();
|
||||
}
|
||||
|
||||
$controllerName = $this->getName();
|
||||
$this->hr();
|
||||
$this->out(__d('cake_console', 'Baking %sController', $controllerName));
|
||||
$this->hr();
|
||||
|
||||
$helpers = $components = array();
|
||||
$actions = '';
|
||||
$wannaUseSession = 'y';
|
||||
$wannaBakeAdminCrud = 'n';
|
||||
$useDynamicScaffold = 'n';
|
||||
$wannaBakeCrud = 'y';
|
||||
|
||||
$question[] = __d('cake_console', "Would you like to build your controller interactively?");
|
||||
if (file_exists($this->path . $controllerName . 'Controller.php')) {
|
||||
$question[] = __d('cake_console', "Warning: Choosing no will overwrite the %sController.", $controllerName);
|
||||
}
|
||||
$doItInteractive = $this->in(implode("\n", $question), array('y', 'n'), 'y');
|
||||
|
||||
if (strtolower($doItInteractive) === 'y') {
|
||||
$this->interactive = true;
|
||||
$useDynamicScaffold = $this->in(
|
||||
__d('cake_console', "Would you like to use dynamic scaffolding?"), 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(
|
||||
__d('cake_console', "Would you like to use Session flash messages?"), array('y', 'n'), 'y'
|
||||
);
|
||||
|
||||
if (strtolower($wannaUseSession) === 'y') {
|
||||
array_push($components, 'Session');
|
||||
}
|
||||
array_unique($components);
|
||||
}
|
||||
} 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(__d('cake_console', 'Look okay?'), 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
|
||||
*
|
||||
* @param string $controllerName The name of the controller.
|
||||
* @param string $useDynamicScaffold Whether or not to use dynamic scaffolds.
|
||||
* @param array $helpers The list of helpers to include.
|
||||
* @param array $components The list of components to include.
|
||||
* @return void
|
||||
*/
|
||||
public function confirmController($controllerName, $useDynamicScaffold, $helpers, $components) {
|
||||
$this->out();
|
||||
$this->hr();
|
||||
$this->out(__d('cake_console', 'The following controller will be created:'));
|
||||
$this->hr();
|
||||
$this->out(__d('cake_console', "Controller Name:\n\t%s", $controllerName));
|
||||
|
||||
if (strtolower($useDynamicScaffold) === 'y') {
|
||||
$this->out("public \$scaffold;");
|
||||
}
|
||||
|
||||
$properties = array(
|
||||
'helpers' => __d('cake_console', 'Helpers:'),
|
||||
'components' => __d('cake_console', 'Components:'),
|
||||
);
|
||||
|
||||
foreach ($properties as $var => $title) {
|
||||
if (count($$var)) {
|
||||
$output = '';
|
||||
$length = count($$var);
|
||||
foreach ($$var as $i => $propElement) {
|
||||
if ($i != $length - 1) {
|
||||
$output .= ucfirst($propElement) . ', ';
|
||||
} else {
|
||||
$output .= ucfirst($propElement);
|
||||
}
|
||||
}
|
||||
$this->out($title . "\n\t" . $output);
|
||||
}
|
||||
}
|
||||
$this->hr();
|
||||
}
|
||||
|
||||
/**
|
||||
* Interact with the user and ask about which methods (admin or regular they want to bake)
|
||||
*
|
||||
* @return array Array containing (bakeRegular, bakeAdmin) answers
|
||||
*/
|
||||
protected function _askAboutMethods() {
|
||||
$wannaBakeCrud = $this->in(
|
||||
__d('cake_console', "Would you like to create some basic class methods \n(index(), add(), view(), edit())?"),
|
||||
array('y', 'n'), 'n'
|
||||
);
|
||||
$wannaBakeAdminCrud = $this->in(
|
||||
__d('cake_console', "Would you like to create the basic class methods for admin routing?"),
|
||||
array('y', 'n'), 'n'
|
||||
);
|
||||
return array($wannaBakeCrud, $wannaBakeAdminCrud);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bake scaffold actions
|
||||
*
|
||||
* @param string $controllerName Controller name
|
||||
* @param string $admin Admin route to use
|
||||
* @param bool $wannaUseSession Set to true to use sessions, false otherwise
|
||||
* @return string Baked actions
|
||||
*/
|
||||
public function bakeActions($controllerName, $admin = null, $wannaUseSession = true) {
|
||||
$currentModelName = $modelImport = $this->_modelName($controllerName);
|
||||
$plugin = $this->plugin;
|
||||
if ($plugin) {
|
||||
$plugin .= '.';
|
||||
}
|
||||
App::uses($modelImport, $plugin . 'Model');
|
||||
if (!class_exists($modelImport)) {
|
||||
$this->err(__d('cake_console', 'You must have a model for this class to build basic methods. Please try again.'));
|
||||
return $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);
|
||||
$displayField = $modelObj->displayField;
|
||||
$primaryKey = $modelObj->primaryKey;
|
||||
|
||||
$this->Template->set(compact(
|
||||
'plugin', 'admin', 'controllerPath', 'pluralName', 'singularName',
|
||||
'singularHumanName', 'pluralHumanName', 'modelObj', 'wannaUseSession', 'currentModelName',
|
||||
'displayField', 'primaryKey'
|
||||
));
|
||||
$actions = $this->Template->generate('actions', 'controller_actions');
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles and writes a Controller file
|
||||
*
|
||||
* @param string $controllerName Controller name already pluralized and correctly cased.
|
||||
* @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
|
||||
* @return string Baked controller
|
||||
*/
|
||||
public function bake($controllerName, $actions = '', $helpers = null, $components = null) {
|
||||
$this->out("\n" . __d('cake_console', 'Baking controller class for %s...', $controllerName), 1, Shell::QUIET);
|
||||
|
||||
$isScaffold = ($actions === 'scaffold') ? true : false;
|
||||
|
||||
$this->Template->set(array(
|
||||
'plugin' => $this->plugin,
|
||||
'pluginPath' => empty($this->plugin) ? '' : $this->plugin . '.'
|
||||
));
|
||||
|
||||
if (!in_array('Paginator', (array)$components)) {
|
||||
$components[] = 'Paginator';
|
||||
}
|
||||
|
||||
$this->Template->set(compact('controllerName', 'actions', 'helpers', 'components', 'isScaffold'));
|
||||
$contents = $this->Template->generate('classes', 'controller');
|
||||
|
||||
$path = $this->getPath();
|
||||
$filename = $path . $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
|
||||
*/
|
||||
public function bakeTest($className) {
|
||||
$this->Test->plugin = $this->plugin;
|
||||
$this->Test->connection = $this->connection;
|
||||
$this->Test->interactive = $this->interactive;
|
||||
return $this->Test->bake('Controller', $className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interact with the user and get a list of additional helpers
|
||||
*
|
||||
* @return array Helpers that the user wants to use.
|
||||
*/
|
||||
public function doHelpers() {
|
||||
return $this->_doPropertyChoices(
|
||||
__d('cake_console', "Would you like this controller to use other helpers\nbesides HtmlHelper and FormHelper?"),
|
||||
__d('cake_console', "Please provide a comma separated list of the other\nhelper names you'd like to use.\nExample: 'Text, Js, Time'")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interact with the user and get a list of additional components
|
||||
*
|
||||
* @return array Components the user wants to use.
|
||||
*/
|
||||
public function doComponents() {
|
||||
$components = array('Paginator');
|
||||
return array_merge($components, $this->_doPropertyChoices(
|
||||
__d('cake_console', "Would you like this controller to use other components\nbesides PaginatorComponent?"),
|
||||
__d('cake_console', "Please provide a comma separated list of the component names you'd like to use.\nExample: 'Acl, Security, RequestHandler'")
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Common code for property choice handling.
|
||||
*
|
||||
* @param string $prompt A yes/no question to precede the list
|
||||
* @param string $example A question for a comma separated list, with examples.
|
||||
* @return array Array of values for property.
|
||||
*/
|
||||
protected function _doPropertyChoices($prompt, $example) {
|
||||
$proceed = $this->in($prompt, array('y', 'n'), 'n');
|
||||
$property = array();
|
||||
if (strtolower($proceed) === 'y') {
|
||||
$propertyList = $this->in($example);
|
||||
$propertyListTrimmed = str_replace(' ', '', $propertyList);
|
||||
$property = explode(',', $propertyListTrimmed);
|
||||
}
|
||||
return array_filter($property);
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs and gets the list of possible controllers from database
|
||||
*
|
||||
* @param string $useDbConfig Database configuration name
|
||||
* @return array Set of controllers
|
||||
*/
|
||||
public function listAll($useDbConfig = null) {
|
||||
if ($useDbConfig === null) {
|
||||
$useDbConfig = $this->connection;
|
||||
}
|
||||
$this->__tables = $this->Model->getAllTables($useDbConfig);
|
||||
|
||||
if ($this->interactive) {
|
||||
$this->out(__d('cake_console', 'Possible Controllers based on your current database:'));
|
||||
$this->hr();
|
||||
$this->_controllerNames = array();
|
||||
$count = count($this->__tables);
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$this->_controllerNames[] = $this->_controllerName($this->_modelName($this->__tables[$i]));
|
||||
$this->out(sprintf("%2d. %s", $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
|
||||
*/
|
||||
public function getName($useDbConfig = null) {
|
||||
$controllers = $this->listAll($useDbConfig);
|
||||
$enteredController = '';
|
||||
|
||||
while (!$enteredController) {
|
||||
$enteredController = $this->in(__d('cake_console', "Enter a number from the list above,\ntype in the name of another controller, or 'q' to exit"), null, 'q');
|
||||
if ($enteredController === 'q') {
|
||||
$this->out(__d('cake_console', 'Exit'));
|
||||
return $this->_stop();
|
||||
}
|
||||
|
||||
if (!$enteredController || intval($enteredController) > count($controllers)) {
|
||||
$this->err(__d('cake_console', "The Controller name you supplied was empty,\nor the number you selected was not an option. Please try again."));
|
||||
$enteredController = '';
|
||||
}
|
||||
}
|
||||
|
||||
if (intval($enteredController) > 0 && intval($enteredController) <= count($controllers)) {
|
||||
$controllerName = $controllers[intval($enteredController) - 1];
|
||||
} else {
|
||||
$controllerName = Inflector::camelize($enteredController);
|
||||
}
|
||||
return $controllerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser() {
|
||||
$parser = parent::getOptionParser();
|
||||
|
||||
$parser->description(
|
||||
__d('cake_console', 'Bake a controller for a model. Using options you can bake public, admin or both.'
|
||||
))->addArgument('name', array(
|
||||
'help' => __d('cake_console', 'Name of the controller to bake. Can use Plugin.name to bake controllers into plugins.')
|
||||
))->addOption('public', array(
|
||||
'help' => __d('cake_console', 'Bake a controller with basic crud actions (index, view, add, edit, delete).'),
|
||||
'boolean' => true
|
||||
))->addOption('admin', array(
|
||||
'help' => __d('cake_console', 'Bake a controller with crud actions for one of the Routing.prefixes.'),
|
||||
'boolean' => true
|
||||
))->addOption('plugin', array(
|
||||
'short' => 'p',
|
||||
'help' => __d('cake_console', 'Plugin to bake the controller into.')
|
||||
))->addOption('connection', array(
|
||||
'short' => 'c',
|
||||
'help' => __d('cake_console', 'The connection the controller\'s model is on.')
|
||||
))->addOption('theme', array(
|
||||
'short' => 't',
|
||||
'help' => __d('cake_console', 'Theme to use when baking code.')
|
||||
))->addOption('force', array(
|
||||
'short' => 'f',
|
||||
'help' => __d('cake_console', 'Force overwriting existing files without prompting.')
|
||||
))->addSubcommand('all', array(
|
||||
'help' => __d('cake_console', 'Bake all controllers with CRUD methods.')
|
||||
))->epilog(
|
||||
__d('cake_console', 'Omitting all arguments and options will enter into an interactive mode.')
|
||||
);
|
||||
|
||||
return $parser;
|
||||
}
|
||||
|
||||
}
|
||||
385
lib/Cake/Console/Command/Task/DbConfigTask.php
Normal file
385
lib/Cake/Console/Command/Task/DbConfigTask.php
Normal file
|
|
@ -0,0 +1,385 @@
|
|||
<?php
|
||||
/**
|
||||
* The DbConfig Task handles creating and updating the database.php
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since CakePHP(tm) v 1.2
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('AppShell', 'Console/Command');
|
||||
|
||||
/**
|
||||
* Task class for creating and updating the database configuration file.
|
||||
*
|
||||
* @package Cake.Console.Command.Task
|
||||
*/
|
||||
class DbConfigTask extends AppShell {
|
||||
|
||||
/**
|
||||
* path to CONFIG directory
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $path = null;
|
||||
|
||||
/**
|
||||
* Default configuration settings to use
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_defaultConfig = array(
|
||||
'name' => 'default',
|
||||
'datasource' => 'Database/Mysql',
|
||||
'persistent' => 'false',
|
||||
'host' => 'localhost',
|
||||
'login' => 'root',
|
||||
'password' => 'password',
|
||||
'database' => 'project_name',
|
||||
'schema' => null,
|
||||
'prefix' => null,
|
||||
'encoding' => null,
|
||||
'port' => null
|
||||
);
|
||||
|
||||
/**
|
||||
* String name of the database config class name.
|
||||
* Used for testing.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $databaseClassName = 'DATABASE_CONFIG';
|
||||
|
||||
/**
|
||||
* initialization callback
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function initialize() {
|
||||
$this->path = APP . 'Config' . DS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execution method always used for tasks
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute() {
|
||||
if (empty($this->args)) {
|
||||
$this->_interactive();
|
||||
return $this->_stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interactive interface
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _interactive() {
|
||||
$this->hr();
|
||||
$this->out(__d('cake_console', 'Database Configuration:'));
|
||||
$this->hr();
|
||||
$done = false;
|
||||
$dbConfigs = array();
|
||||
|
||||
while (!$done) {
|
||||
$name = '';
|
||||
|
||||
while (!$name) {
|
||||
$name = $this->in(__d('cake_console', "Name:"), null, 'default');
|
||||
if (preg_match('/[^a-z0-9_]/i', $name)) {
|
||||
$name = '';
|
||||
$this->out(__d('cake_console', 'The name may only contain unaccented latin characters, numbers or underscores'));
|
||||
} elseif (preg_match('/^[^a-z_]/i', $name)) {
|
||||
$name = '';
|
||||
$this->out(__d('cake_console', 'The name must start with an unaccented latin character or an underscore'));
|
||||
}
|
||||
}
|
||||
|
||||
$datasource = $this->in(__d('cake_console', 'Datasource:'), array('Mysql', 'Postgres', 'Sqlite', 'Sqlserver'), 'Mysql');
|
||||
|
||||
$persistent = $this->in(__d('cake_console', 'Persistent Connection?'), array('y', 'n'), 'n');
|
||||
if (strtolower($persistent) === 'n') {
|
||||
$persistent = 'false';
|
||||
} else {
|
||||
$persistent = 'true';
|
||||
}
|
||||
|
||||
$host = '';
|
||||
while (!$host) {
|
||||
$host = $this->in(__d('cake_console', 'Database Host:'), null, 'localhost');
|
||||
}
|
||||
|
||||
$port = '';
|
||||
while (!$port) {
|
||||
$port = $this->in(__d('cake_console', 'Port?'), null, 'n');
|
||||
}
|
||||
|
||||
if (strtolower($port) === 'n') {
|
||||
$port = null;
|
||||
}
|
||||
|
||||
$login = '';
|
||||
while (!$login) {
|
||||
$login = $this->in(__d('cake_console', 'User:'), null, 'root');
|
||||
}
|
||||
$password = '';
|
||||
$blankPassword = false;
|
||||
|
||||
while (!$password && !$blankPassword) {
|
||||
$password = $this->in(__d('cake_console', 'Password:'));
|
||||
|
||||
if (!$password) {
|
||||
$blank = $this->in(__d('cake_console', '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(__d('cake_console', 'Database Name:'), null, 'cake');
|
||||
}
|
||||
|
||||
$prefix = '';
|
||||
while (!$prefix) {
|
||||
$prefix = $this->in(__d('cake_console', 'Table Prefix?'), null, 'n');
|
||||
}
|
||||
if (strtolower($prefix) === 'n') {
|
||||
$prefix = null;
|
||||
}
|
||||
|
||||
$encoding = '';
|
||||
while (!$encoding) {
|
||||
$encoding = $this->in(__d('cake_console', 'Table encoding?'), null, 'n');
|
||||
}
|
||||
if (strtolower($encoding) === 'n') {
|
||||
$encoding = null;
|
||||
}
|
||||
|
||||
$schema = '';
|
||||
if ($datasource === 'postgres') {
|
||||
while (!$schema) {
|
||||
$schema = $this->in(__d('cake_console', 'Table schema?'), null, 'n');
|
||||
}
|
||||
}
|
||||
if (strtolower($schema) === 'n') {
|
||||
$schema = null;
|
||||
}
|
||||
|
||||
$config = compact('name', 'datasource', 'persistent', 'host', 'login', 'password', 'database', 'prefix', 'encoding', 'port', 'schema');
|
||||
|
||||
while (!$this->_verify($config)) {
|
||||
$this->_interactive();
|
||||
}
|
||||
|
||||
$dbConfigs[] = $config;
|
||||
$doneYet = $this->in(__d('cake_console', '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
|
||||
*
|
||||
* @param array $config The config data.
|
||||
* @return bool True if user says it looks good, false otherwise
|
||||
*/
|
||||
protected function _verify($config) {
|
||||
$config += $this->_defaultConfig;
|
||||
extract($config);
|
||||
$this->out();
|
||||
$this->hr();
|
||||
$this->out(__d('cake_console', 'The following database configuration will be created:'));
|
||||
$this->hr();
|
||||
$this->out(__d('cake_console', "Name: %s", $name));
|
||||
$this->out(__d('cake_console', "Datasource: %s", $datasource));
|
||||
$this->out(__d('cake_console', "Persistent: %s", $persistent));
|
||||
$this->out(__d('cake_console', "Host: %s", $host));
|
||||
|
||||
if ($port) {
|
||||
$this->out(__d('cake_console', "Port: %s", $port));
|
||||
}
|
||||
|
||||
$this->out(__d('cake_console', "User: %s", $login));
|
||||
$this->out(__d('cake_console', "Pass: %s", str_repeat('*', strlen($password))));
|
||||
$this->out(__d('cake_console', "Database: %s", $database));
|
||||
|
||||
if ($prefix) {
|
||||
$this->out(__d('cake_console', "Table prefix: %s", $prefix));
|
||||
}
|
||||
|
||||
if ($schema) {
|
||||
$this->out(__d('cake_console', "Schema: %s", $schema));
|
||||
}
|
||||
|
||||
if ($encoding) {
|
||||
$this->out(__d('cake_console', "Encoding: %s", $encoding));
|
||||
}
|
||||
|
||||
$this->hr();
|
||||
$looksGood = $this->in(__d('cake_console', '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 bool Success
|
||||
*/
|
||||
public function bake($configs) {
|
||||
if (!is_dir($this->path)) {
|
||||
$this->err(__d('cake_console', '%s not found', $this->path));
|
||||
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 += $this->_defaultConfig;
|
||||
|
||||
if (!isset($info['schema'])) {
|
||||
$info['schema'] = null;
|
||||
}
|
||||
if (!isset($info['encoding'])) {
|
||||
$info['encoding'] = null;
|
||||
}
|
||||
if (!isset($info['port'])) {
|
||||
$info['port'] = null;
|
||||
}
|
||||
|
||||
$info['persistent'] = var_export((bool)$info['persistent'], true);
|
||||
|
||||
$oldConfigs[] = array(
|
||||
'name' => $configName,
|
||||
'datasource' => $info['datasource'],
|
||||
'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 $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 += $this->_defaultConfig;
|
||||
extract($config);
|
||||
|
||||
if (strpos($datasource, 'Database/') === false) {
|
||||
$datasource = "Database/{$datasource}";
|
||||
}
|
||||
$out .= "\tpublic \${$name} = array(\n";
|
||||
$out .= "\t\t'datasource' => '{$datasource}',\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";
|
||||
$filename = $this->path . 'database.php';
|
||||
return $this->createFile($filename, $out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a user specified Connection name
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getConfig() {
|
||||
App::uses('ConnectionManager', 'Model');
|
||||
$configs = ConnectionManager::enumConnectionObjects();
|
||||
|
||||
$useDbConfig = key($configs);
|
||||
if (!is_array($configs) || empty($configs)) {
|
||||
return $this->execute();
|
||||
}
|
||||
$connections = array_keys($configs);
|
||||
|
||||
if (count($connections) > 1) {
|
||||
$useDbConfig = $this->in(__d('cake_console', 'Use Database Config') . ':', $connections, $useDbConfig);
|
||||
}
|
||||
return $useDbConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser() {
|
||||
$parser = parent::getOptionParser();
|
||||
|
||||
$parser->description(
|
||||
__d('cake_console', 'Bake new database configuration settings.')
|
||||
);
|
||||
|
||||
return $parser;
|
||||
}
|
||||
|
||||
}
|
||||
811
lib/Cake/Console/Command/Task/ExtractTask.php
Normal file
811
lib/Cake/Console/Command/Task/ExtractTask.php
Normal file
|
|
@ -0,0 +1,811 @@
|
|||
<?php
|
||||
/**
|
||||
* Language string extractor
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since CakePHP(tm) v 1.2.0.5012
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('AppShell', 'Console/Command');
|
||||
App::uses('File', 'Utility');
|
||||
App::uses('Folder', 'Utility');
|
||||
App::uses('Hash', 'Utility');
|
||||
|
||||
/**
|
||||
* Language string extractor
|
||||
*
|
||||
* @package Cake.Console.Command.Task
|
||||
*/
|
||||
class ExtractTask extends AppShell {
|
||||
|
||||
/**
|
||||
* Paths to use when looking for strings
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_paths = array();
|
||||
|
||||
/**
|
||||
* Files from where to extract
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_files = array();
|
||||
|
||||
/**
|
||||
* Merge all domain and category strings into the default.pot file
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $_merge = false;
|
||||
|
||||
/**
|
||||
* Current file being processed
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_file = null;
|
||||
|
||||
/**
|
||||
* Contains all content waiting to be write
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_storage = array();
|
||||
|
||||
/**
|
||||
* Extracted tokens
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_tokens = array();
|
||||
|
||||
/**
|
||||
* Extracted strings indexed by category and domain.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_translations = array();
|
||||
|
||||
/**
|
||||
* Destination path
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_output = null;
|
||||
|
||||
/**
|
||||
* An array of directories to exclude.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_exclude = array();
|
||||
|
||||
/**
|
||||
* Holds whether this call should extract model validation messages
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $_extractValidation = true;
|
||||
|
||||
/**
|
||||
* Holds the validation string domain to use for validation messages when extracting
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $_validationDomain = 'default';
|
||||
|
||||
/**
|
||||
* Holds whether this call should extract the CakePHP Lib messages
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $_extractCore = false;
|
||||
|
||||
/**
|
||||
* Method to interact with the User and get path selections.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _getPaths() {
|
||||
$defaultPath = APP;
|
||||
while (true) {
|
||||
$currentPaths = count($this->_paths) > 0 ? $this->_paths : array('None');
|
||||
$message = __d(
|
||||
'cake_console',
|
||||
"Current paths: %s\nWhat is the path you would like to extract?\n[Q]uit [D]one",
|
||||
implode(', ', $currentPaths)
|
||||
);
|
||||
$response = $this->in($message, null, $defaultPath);
|
||||
if (strtoupper($response) === 'Q') {
|
||||
$this->err(__d('cake_console', 'Extract Aborted'));
|
||||
return $this->_stop();
|
||||
} elseif (strtoupper($response) === 'D' && count($this->_paths)) {
|
||||
$this->out();
|
||||
return;
|
||||
} elseif (strtoupper($response) === 'D') {
|
||||
$this->err(__d('cake_console', '<warning>No directories selected.</warning> Please choose a directory.'));
|
||||
} elseif (is_dir($response)) {
|
||||
$this->_paths[] = $response;
|
||||
$defaultPath = 'D';
|
||||
} else {
|
||||
$this->err(__d('cake_console', 'The directory path you supplied was not found. Please try again.'));
|
||||
}
|
||||
$this->out();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execution method always used for tasks
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute() {
|
||||
if (!empty($this->params['exclude'])) {
|
||||
$this->_exclude = explode(',', $this->params['exclude']);
|
||||
}
|
||||
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']);
|
||||
} elseif (isset($this->params['plugin'])) {
|
||||
$plugin = Inflector::camelize($this->params['plugin']);
|
||||
if (!CakePlugin::loaded($plugin)) {
|
||||
CakePlugin::load($plugin);
|
||||
}
|
||||
$this->_paths = array(CakePlugin::path($plugin));
|
||||
$this->params['plugin'] = $plugin;
|
||||
} else {
|
||||
$this->_getPaths();
|
||||
}
|
||||
|
||||
if (isset($this->params['extract-core'])) {
|
||||
$this->_extractCore = !(strtolower($this->params['extract-core']) === 'no');
|
||||
} else {
|
||||
$response = $this->in(__d('cake_console', 'Would you like to extract the messages from the CakePHP core?'), array('y', 'n'), 'n');
|
||||
$this->_extractCore = strtolower($response) === 'y';
|
||||
}
|
||||
|
||||
if (!empty($this->params['exclude-plugins']) && $this->_isExtractingApp()) {
|
||||
$this->_exclude = array_merge($this->_exclude, App::path('plugins'));
|
||||
}
|
||||
|
||||
if (!empty($this->params['ignore-model-validation']) || (!$this->_isExtractingApp() && empty($plugin))) {
|
||||
$this->_extractValidation = false;
|
||||
}
|
||||
if (!empty($this->params['validation-domain'])) {
|
||||
$this->_validationDomain = $this->params['validation-domain'];
|
||||
}
|
||||
|
||||
if ($this->_extractCore) {
|
||||
$this->_paths[] = CAKE;
|
||||
$this->_exclude = array_merge($this->_exclude, array(
|
||||
CAKE . 'Test',
|
||||
CAKE . 'Console' . DS . 'Templates'
|
||||
));
|
||||
}
|
||||
|
||||
if (isset($this->params['output'])) {
|
||||
$this->_output = $this->params['output'];
|
||||
} elseif (isset($this->params['plugin'])) {
|
||||
$this->_output = $this->_paths[0] . DS . 'Locale';
|
||||
} else {
|
||||
$message = __d('cake_console', "What is the path you would like to output?\n[Q]uit", $this->_paths[0] . DS . 'Locale');
|
||||
while (true) {
|
||||
$response = $this->in($message, null, rtrim($this->_paths[0], DS) . DS . 'Locale');
|
||||
if (strtoupper($response) === 'Q') {
|
||||
$this->err(__d('cake_console', 'Extract Aborted'));
|
||||
return $this->_stop();
|
||||
} elseif ($this->_isPathUsable($response)) {
|
||||
$this->_output = $response . DS;
|
||||
break;
|
||||
} else {
|
||||
$this->err(__d('cake_console', 'The directory path you supplied was not found. Please try again.'));
|
||||
}
|
||||
$this->out();
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->params['merge'])) {
|
||||
$this->_merge = !(strtolower($this->params['merge']) === 'no');
|
||||
} else {
|
||||
$this->out();
|
||||
$response = $this->in(__d('cake_console', 'Would you like to merge all domain and category strings into the default.pot file?'), array('y', 'n'), 'n');
|
||||
$this->_merge = strtolower($response) === 'y';
|
||||
}
|
||||
|
||||
if (empty($this->_files)) {
|
||||
$this->_searchFiles();
|
||||
}
|
||||
|
||||
$this->_output = rtrim($this->_output, DS) . DS;
|
||||
if (!$this->_isPathUsable($this->_output)) {
|
||||
$this->err(__d('cake_console', 'The output directory %s was not found or writable.', $this->_output));
|
||||
return $this->_stop();
|
||||
}
|
||||
|
||||
$this->_extract();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a translation to the internal translations property
|
||||
*
|
||||
* Takes care of duplicate translations
|
||||
*
|
||||
* @param string $category The category
|
||||
* @param string $domain The domain
|
||||
* @param string $msgid The message string
|
||||
* @param array $details The file and line references
|
||||
* @return void
|
||||
*/
|
||||
protected function _addTranslation($category, $domain, $msgid, $details = array()) {
|
||||
if (empty($this->_translations[$category][$domain][$msgid])) {
|
||||
$this->_translations[$category][$domain][$msgid] = array(
|
||||
'msgid_plural' => false
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($details['msgid_plural'])) {
|
||||
$this->_translations[$category][$domain][$msgid]['msgid_plural'] = $details['msgid_plural'];
|
||||
}
|
||||
|
||||
if (isset($details['file'])) {
|
||||
$line = 0;
|
||||
if (isset($details['line'])) {
|
||||
$line = $details['line'];
|
||||
}
|
||||
$this->_translations[$category][$domain][$msgid]['references'][$details['file']][] = $line;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract text
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _extract() {
|
||||
$this->out();
|
||||
$this->out();
|
||||
$this->out(__d('cake_console', 'Extracting...'));
|
||||
$this->hr();
|
||||
$this->out(__d('cake_console', 'Paths:'));
|
||||
foreach ($this->_paths as $path) {
|
||||
$this->out(' ' . $path);
|
||||
}
|
||||
$this->out(__d('cake_console', 'Output Directory: ') . $this->_output);
|
||||
$this->hr();
|
||||
$this->_extractTokens();
|
||||
$this->_extractValidationMessages();
|
||||
$this->_buildFiles();
|
||||
$this->_writeFiles();
|
||||
$this->_paths = $this->_files = $this->_storage = array();
|
||||
$this->_translations = $this->_tokens = array();
|
||||
$this->_extractValidation = true;
|
||||
$this->out();
|
||||
$this->out(__d('cake_console', 'Done.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser() {
|
||||
$parser = parent::getOptionParser();
|
||||
|
||||
$parser->description(
|
||||
__d('cake_console', 'CakePHP Language String Extraction:')
|
||||
)->addOption('app', array(
|
||||
'help' => __d('cake_console', 'Directory where your application is located.')
|
||||
))->addOption('paths', array(
|
||||
'help' => __d('cake_console', 'Comma separated list of paths.')
|
||||
))->addOption('merge', array(
|
||||
'help' => __d('cake_console', 'Merge all domain and category strings into the default.po file.'),
|
||||
'choices' => array('yes', 'no')
|
||||
))->addOption('output', array(
|
||||
'help' => __d('cake_console', 'Full path to output directory.')
|
||||
))->addOption('files', array(
|
||||
'help' => __d('cake_console', 'Comma separated list of files.')
|
||||
))->addOption('exclude-plugins', array(
|
||||
'boolean' => true,
|
||||
'default' => true,
|
||||
'help' => __d('cake_console', 'Ignores all files in plugins if this command is run inside from the same app directory.')
|
||||
))->addOption('plugin', array(
|
||||
'help' => __d('cake_console', 'Extracts tokens only from the plugin specified and puts the result in the plugin\'s Locale directory.')
|
||||
))->addOption('ignore-model-validation', array(
|
||||
'boolean' => true,
|
||||
'default' => false,
|
||||
'help' => __d('cake_console', 'Ignores validation messages in the $validate property.' .
|
||||
' If this flag is not set and the command is run from the same app directory,' .
|
||||
' all messages in model validation rules will be extracted as tokens.'
|
||||
)
|
||||
))->addOption('validation-domain', array(
|
||||
'help' => __d('cake_console', 'If set to a value, the localization domain to be used for model validation messages.')
|
||||
))->addOption('exclude', array(
|
||||
'help' => __d('cake_console', 'Comma separated list of directories to exclude.' .
|
||||
' Any path containing a path segment with the provided values will be skipped. E.g. test,vendors'
|
||||
)
|
||||
))->addOption('overwrite', array(
|
||||
'boolean' => true,
|
||||
'default' => false,
|
||||
'help' => __d('cake_console', 'Always overwrite existing .pot files.')
|
||||
))->addOption('extract-core', array(
|
||||
'help' => __d('cake_console', 'Extract messages from the CakePHP core libs.'),
|
||||
'choices' => array('yes', 'no')
|
||||
));
|
||||
|
||||
return $parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract tokens out of all files to be processed
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _extractTokens() {
|
||||
foreach ($this->_files as $file) {
|
||||
$this->_file = $file;
|
||||
$this->out(__d('cake_console', 'Processing %s...', $file));
|
||||
|
||||
$code = file_get_contents($file);
|
||||
$allTokens = token_get_all($code);
|
||||
|
||||
$this->_tokens = array();
|
||||
foreach ($allTokens as $token) {
|
||||
if (!is_array($token) || ($token[0] != T_WHITESPACE && $token[0] != T_INLINE_HTML)) {
|
||||
$this->_tokens[] = $token;
|
||||
}
|
||||
}
|
||||
unset($allTokens);
|
||||
$this->_parse('__', array('singular'));
|
||||
$this->_parse('__n', array('singular', 'plural'));
|
||||
$this->_parse('__d', array('domain', 'singular'));
|
||||
$this->_parse('__c', array('singular', 'category'));
|
||||
$this->_parse('__dc', array('domain', 'singular', 'category'));
|
||||
$this->_parse('__dn', array('domain', 'singular', 'plural'));
|
||||
$this->_parse('__dcn', array('domain', 'singular', 'plural', 'count', 'category'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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: category, domain, singular, plural)
|
||||
* @return void
|
||||
*/
|
||||
protected function _parse($functionName, $map) {
|
||||
$count = 0;
|
||||
$categories = array('LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME', 'LC_MESSAGES');
|
||||
$tokenCount = count($this->_tokens);
|
||||
|
||||
while (($tokenCount - $count) > 1) {
|
||||
$countToken = $this->_tokens[$count];
|
||||
$firstParenthesis = $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) {
|
||||
if ($this->_tokens[$position] === '(') {
|
||||
$depth++;
|
||||
} elseif ($this->_tokens[$position] === ')') {
|
||||
$depth--;
|
||||
}
|
||||
$position++;
|
||||
}
|
||||
|
||||
$mapCount = count($map);
|
||||
$strings = $this->_getStrings($position, $mapCount);
|
||||
|
||||
if ($mapCount === count($strings)) {
|
||||
extract(array_combine($map, $strings));
|
||||
$category = isset($category) ? $category : 6;
|
||||
$category = intval($category);
|
||||
$categoryName = $categories[$category];
|
||||
$domain = isset($domain) ? $domain : 'default';
|
||||
$details = array(
|
||||
'file' => $this->_file,
|
||||
'line' => $line,
|
||||
);
|
||||
if (isset($plural)) {
|
||||
$details['msgid_plural'] = $plural;
|
||||
}
|
||||
$this->_addTranslation($categoryName, $domain, $singular, $details);
|
||||
} else {
|
||||
$this->_markerError($this->_file, $line, $functionName, $count);
|
||||
}
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks for models in the application and extracts the validation messages
|
||||
* to be added to the translation map
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _extractValidationMessages() {
|
||||
if (!$this->_extractValidation) {
|
||||
return;
|
||||
}
|
||||
|
||||
$plugins = array(null);
|
||||
if (empty($this->params['exclude-plugins'])) {
|
||||
$plugins = array_merge($plugins, App::objects('plugin', null, false));
|
||||
}
|
||||
foreach ($plugins as $plugin) {
|
||||
$this->_extractPluginValidationMessages($plugin);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract validation messages from application or plugin models
|
||||
*
|
||||
* @param string $plugin Plugin name or `null` to process application models
|
||||
* @return void
|
||||
*/
|
||||
protected function _extractPluginValidationMessages($plugin = null) {
|
||||
App::uses('AppModel', 'Model');
|
||||
if (!empty($plugin)) {
|
||||
if (!CakePlugin::loaded($plugin)) {
|
||||
return;
|
||||
}
|
||||
App::uses($plugin . 'AppModel', $plugin . '.Model');
|
||||
$plugin = $plugin . '.';
|
||||
}
|
||||
$models = App::objects($plugin . 'Model', null, false);
|
||||
|
||||
foreach ($models as $model) {
|
||||
App::uses($model, $plugin . 'Model');
|
||||
$reflection = new ReflectionClass($model);
|
||||
if (!$reflection->isSubClassOf('Model')) {
|
||||
continue;
|
||||
}
|
||||
$properties = $reflection->getDefaultProperties();
|
||||
$validate = $properties['validate'];
|
||||
if (empty($validate)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$file = $reflection->getFileName();
|
||||
$domain = $this->_validationDomain;
|
||||
if (!empty($properties['validationDomain'])) {
|
||||
$domain = $properties['validationDomain'];
|
||||
}
|
||||
foreach ($validate as $field => $rules) {
|
||||
$this->_processValidationRules($field, $rules, $file, $domain);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a validation rule for a field and looks for a message to be added
|
||||
* to the translation map
|
||||
*
|
||||
* @param string $field the name of the field that is being processed
|
||||
* @param array $rules the set of validation rules for the field
|
||||
* @param string $file the file name where this validation rule was found
|
||||
* @param string $domain default domain to bind the validations to
|
||||
* @param string $category the translation category
|
||||
* @return void
|
||||
*/
|
||||
protected function _processValidationRules($field, $rules, $file, $domain, $category = 'LC_MESSAGES') {
|
||||
if (!is_array($rules)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$dims = Hash::dimensions($rules);
|
||||
if ($dims === 1 || ($dims === 2 && isset($rules['message']))) {
|
||||
$rules = array($rules);
|
||||
}
|
||||
|
||||
foreach ($rules as $rule => $validateProp) {
|
||||
$msgid = null;
|
||||
if (isset($validateProp['message'])) {
|
||||
if (is_array($validateProp['message'])) {
|
||||
$msgid = $validateProp['message'][0];
|
||||
} else {
|
||||
$msgid = $validateProp['message'];
|
||||
}
|
||||
} elseif (is_string($rule)) {
|
||||
$msgid = $rule;
|
||||
}
|
||||
if ($msgid) {
|
||||
$msgid = $this->_formatString(sprintf("'%s'", $msgid));
|
||||
$details = array(
|
||||
'file' => $file,
|
||||
'line' => 'validation for field ' . $field
|
||||
);
|
||||
$this->_addTranslation($category, $domain, $msgid, $details);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the translate template file contents out of obtained strings
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _buildFiles() {
|
||||
$paths = $this->_paths;
|
||||
$paths[] = realpath(APP) . DS;
|
||||
foreach ($this->_translations as $category => $domains) {
|
||||
foreach ($domains as $domain => $translations) {
|
||||
foreach ($translations as $msgid => $details) {
|
||||
$plural = $details['msgid_plural'];
|
||||
$files = $details['references'];
|
||||
$occurrences = array();
|
||||
foreach ($files as $file => $lines) {
|
||||
$lines = array_unique($lines);
|
||||
$occurrences[] = $file . ':' . implode(';', $lines);
|
||||
}
|
||||
$occurrences = implode("\n#: ", $occurrences);
|
||||
$header = '#: ' . str_replace(DS, '/', str_replace($paths, '', $occurrences)) . "\n";
|
||||
|
||||
if ($plural === false) {
|
||||
$sentence = "msgid \"{$msgid}\"\n";
|
||||
$sentence .= "msgstr \"\"\n\n";
|
||||
} else {
|
||||
$sentence = "msgid \"{$msgid}\"\n";
|
||||
$sentence .= "msgid_plural \"{$plural}\"\n";
|
||||
$sentence .= "msgstr[0] \"\"\n";
|
||||
$sentence .= "msgstr[1] \"\"\n\n";
|
||||
}
|
||||
|
||||
$this->_store($category, $domain, $header, $sentence);
|
||||
if (($category !== 'LC_MESSAGES' || $domain !== 'default') && $this->_merge) {
|
||||
$this->_store('LC_MESSAGES', 'default', $header, $sentence);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a file to be stored
|
||||
*
|
||||
* @param string $category The category
|
||||
* @param string $domain The domain
|
||||
* @param string $header The header content.
|
||||
* @param string $sentence The sentence to store.
|
||||
* @return void
|
||||
*/
|
||||
protected function _store($category, $domain, $header, $sentence) {
|
||||
if (!isset($this->_storage[$category])) {
|
||||
$this->_storage[$category] = array();
|
||||
}
|
||||
if (!isset($this->_storage[$category][$domain])) {
|
||||
$this->_storage[$category][$domain] = array();
|
||||
}
|
||||
if (!isset($this->_storage[$category][$domain][$sentence])) {
|
||||
$this->_storage[$category][$domain][$sentence] = $header;
|
||||
} else {
|
||||
$this->_storage[$category][$domain][$sentence] .= $header;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the files that need to be stored
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _writeFiles() {
|
||||
$overwriteAll = false;
|
||||
if (!empty($this->params['overwrite'])) {
|
||||
$overwriteAll = true;
|
||||
}
|
||||
foreach ($this->_storage as $category => $domains) {
|
||||
foreach ($domains as $domain => $sentences) {
|
||||
$output = $this->_writeHeader();
|
||||
foreach ($sentences as $sentence => $header) {
|
||||
$output .= $header . $sentence;
|
||||
}
|
||||
|
||||
$filename = $domain . '.pot';
|
||||
if ($category === 'LC_MESSAGES') {
|
||||
$File = new File($this->_output . $filename);
|
||||
} else {
|
||||
new Folder($this->_output . $category, true);
|
||||
$File = new File($this->_output . $category . DS . $filename);
|
||||
}
|
||||
$response = '';
|
||||
while ($overwriteAll === false && $File->exists() && strtoupper($response) !== 'Y') {
|
||||
$this->out();
|
||||
$response = $this->in(
|
||||
__d('cake_console', 'Error: %s already exists in this location. Overwrite? [Y]es, [N]o, [A]ll', $filename),
|
||||
array('y', 'n', 'a'),
|
||||
'y'
|
||||
);
|
||||
if (strtoupper($response) === 'N') {
|
||||
$response = '';
|
||||
while (!$response) {
|
||||
$response = $this->in(__d('cake_console', "What would you like to name this file?"), 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
|
||||
*/
|
||||
protected 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the strings from the position forward
|
||||
*
|
||||
* @param int &$position Actual position on tokens array
|
||||
* @param int $target Number of strings to extract
|
||||
* @return array Strings extracted
|
||||
*/
|
||||
protected function _getStrings(&$position, $target) {
|
||||
$strings = array();
|
||||
$count = count($strings);
|
||||
while ($count < $target && ($this->_tokens[$position] === ',' || $this->_tokens[$position][0] == T_CONSTANT_ENCAPSED_STRING || $this->_tokens[$position][0] == T_LNUMBER)) {
|
||||
$count = count($strings);
|
||||
if ($this->_tokens[$position][0] == T_CONSTANT_ENCAPSED_STRING && $this->_tokens[$position + 1] === '.') {
|
||||
$string = '';
|
||||
while ($this->_tokens[$position][0] == T_CONSTANT_ENCAPSED_STRING || $this->_tokens[$position] === '.') {
|
||||
if ($this->_tokens[$position][0] == T_CONSTANT_ENCAPSED_STRING) {
|
||||
$string .= $this->_formatString($this->_tokens[$position][1]);
|
||||
}
|
||||
$position++;
|
||||
}
|
||||
$strings[] = $string;
|
||||
} elseif ($this->_tokens[$position][0] == T_CONSTANT_ENCAPSED_STRING) {
|
||||
$strings[] = $this->_formatString($this->_tokens[$position][1]);
|
||||
} elseif ($this->_tokens[$position][0] == T_LNUMBER) {
|
||||
$strings[] = $this->_tokens[$position][1];
|
||||
}
|
||||
$position++;
|
||||
}
|
||||
return $strings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a string to be added as a translatable string
|
||||
*
|
||||
* @param string $string String to format
|
||||
* @return string Formatted string
|
||||
*/
|
||||
protected 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 int $line Line number
|
||||
* @param string $marker Marker found
|
||||
* @param int $count Count
|
||||
* @return void
|
||||
*/
|
||||
protected function _markerError($file, $line, $marker, $count) {
|
||||
$this->err(__d('cake_console', "Invalid marker content in %s:%s\n* %s(", $file, $line, $marker));
|
||||
$count += 2;
|
||||
$tokenCount = count($this->_tokens);
|
||||
$parenthesis = 1;
|
||||
|
||||
while ((($tokenCount - $count) > 0) && $parenthesis) {
|
||||
if (is_array($this->_tokens[$count])) {
|
||||
$this->err($this->_tokens[$count][1], false);
|
||||
} else {
|
||||
$this->err($this->_tokens[$count], false);
|
||||
if ($this->_tokens[$count] === '(') {
|
||||
$parenthesis++;
|
||||
}
|
||||
|
||||
if ($this->_tokens[$count] === ')') {
|
||||
$parenthesis--;
|
||||
}
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
$this->err("\n", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search files that may contain translatable strings
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _searchFiles() {
|
||||
$pattern = false;
|
||||
if (!empty($this->_exclude)) {
|
||||
$exclude = array();
|
||||
foreach ($this->_exclude as $e) {
|
||||
if (DS !== '\\' && $e[0] !== DS) {
|
||||
$e = DS . $e;
|
||||
}
|
||||
$exclude[] = preg_quote($e, '/');
|
||||
}
|
||||
$pattern = '/' . implode('|', $exclude) . '/';
|
||||
}
|
||||
foreach ($this->_paths as $path) {
|
||||
$Folder = new Folder($path);
|
||||
$files = $Folder->findRecursive('.*\.(php|ctp|thtml|inc|tpl)', true);
|
||||
if (!empty($pattern)) {
|
||||
foreach ($files as $i => $file) {
|
||||
if (preg_match($pattern, $file)) {
|
||||
unset($files[$i]);
|
||||
}
|
||||
}
|
||||
$files = array_values($files);
|
||||
}
|
||||
$this->_files = array_merge($this->_files, $files);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this execution is meant to extract string only from directories in folder represented by the
|
||||
* APP constant, i.e. this task is extracting strings from same application.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function _isExtractingApp() {
|
||||
return $this->_paths === array(APP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not a given path is usable for writing.
|
||||
*
|
||||
* @param string $path Path to folder
|
||||
* @return bool true if it exists and is writable, false otherwise
|
||||
*/
|
||||
protected function _isPathUsable($path) {
|
||||
return is_dir($path) && is_writable($path);
|
||||
}
|
||||
}
|
||||
451
lib/Cake/Console/Command/Task/FixtureTask.php
Normal file
451
lib/Cake/Console/Command/Task/FixtureTask.php
Normal file
|
|
@ -0,0 +1,451 @@
|
|||
<?php
|
||||
/**
|
||||
* The FixtureTask handles creating and updating fixture files.
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since CakePHP(tm) v 1.3
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('AppShell', 'Console/Command');
|
||||
App::uses('BakeTask', 'Console/Command/Task');
|
||||
App::uses('Model', 'Model');
|
||||
|
||||
/**
|
||||
* Task class for creating and updating fixtures files.
|
||||
*
|
||||
* @package Cake.Console.Command.Task
|
||||
*/
|
||||
class FixtureTask extends BakeTask {
|
||||
|
||||
/**
|
||||
* Tasks to be loaded by this Task
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $tasks = array('DbConfig', 'Model', 'Template');
|
||||
|
||||
/**
|
||||
* path to fixtures directory
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $path = null;
|
||||
|
||||
/**
|
||||
* Schema instance
|
||||
*
|
||||
* @var CakeSchema
|
||||
*/
|
||||
protected $_Schema = null;
|
||||
|
||||
/**
|
||||
* Override initialize
|
||||
*
|
||||
* @param ConsoleOutput $stdout A ConsoleOutput object for stdout.
|
||||
* @param ConsoleOutput $stderr A ConsoleOutput object for stderr.
|
||||
* @param ConsoleInput $stdin A ConsoleInput object for stdin.
|
||||
*/
|
||||
public function __construct($stdout = null, $stderr = null, $stdin = null) {
|
||||
parent::__construct($stdout, $stderr, $stdin);
|
||||
$this->path = APP . 'Test' . DS . 'Fixture' . DS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser() {
|
||||
$parser = parent::getOptionParser();
|
||||
|
||||
$parser->description(
|
||||
__d('cake_console', 'Generate fixtures for use with the test suite. You can use `bake fixture all` to bake all fixtures.')
|
||||
)->addArgument('name', array(
|
||||
'help' => __d('cake_console', 'Name of the fixture to bake. Can use Plugin.name to bake plugin fixtures.')
|
||||
))->addOption('count', array(
|
||||
'help' => __d('cake_console', 'When using generated data, the number of records to include in the fixture(s).'),
|
||||
'short' => 'n',
|
||||
'default' => 10
|
||||
))->addOption('connection', array(
|
||||
'help' => __d('cake_console', 'Which database configuration to use for baking.'),
|
||||
'short' => 'c',
|
||||
'default' => 'default'
|
||||
))->addOption('plugin', array(
|
||||
'help' => __d('cake_console', 'CamelCased name of the plugin to bake fixtures for.'),
|
||||
'short' => 'p'
|
||||
))->addOption('schema', array(
|
||||
'help' => __d('cake_console', 'Importing schema for fixtures rather than hardcoding it.'),
|
||||
'short' => 's',
|
||||
'boolean' => true
|
||||
))->addOption('theme', array(
|
||||
'short' => 't',
|
||||
'help' => __d('cake_console', 'Theme to use when baking code.')
|
||||
))->addOption('force', array(
|
||||
'short' => 'f',
|
||||
'help' => __d('cake_console', 'Force overwriting existing files without prompting.')
|
||||
))->addOption('records', array(
|
||||
'help' => __d('cake_console', 'Used with --count and <name>/all commands to pull [n] records from the live tables, ' .
|
||||
'where [n] is either --count or the default of 10.'),
|
||||
'short' => 'r',
|
||||
'boolean' => true
|
||||
))->epilog(
|
||||
__d('cake_console', 'Omitting all arguments and options will enter into an interactive mode.')
|
||||
);
|
||||
|
||||
return $parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execution method always used for tasks
|
||||
* Handles dispatching to interactive, named, or all processes.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute() {
|
||||
parent::execute();
|
||||
if (empty($this->args)) {
|
||||
$this->_interactive();
|
||||
}
|
||||
|
||||
if (isset($this->args[0])) {
|
||||
$this->interactive = false;
|
||||
if (!isset($this->connection)) {
|
||||
$this->connection = 'default';
|
||||
}
|
||||
if (strtolower($this->args[0]) === 'all') {
|
||||
return $this->all();
|
||||
}
|
||||
$model = $this->_modelName($this->args[0]);
|
||||
$this->bake($model);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bake All the Fixtures at once. Will only bake fixtures for models that exist.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function all() {
|
||||
$this->interactive = false;
|
||||
$this->Model->interactive = false;
|
||||
$tables = $this->Model->listAll($this->connection, false);
|
||||
|
||||
foreach ($tables as $table) {
|
||||
$model = $this->_modelName($table);
|
||||
$importOptions = array();
|
||||
if (!empty($this->params['schema'])) {
|
||||
$importOptions['schema'] = $model;
|
||||
}
|
||||
$this->bake($model, false, $importOptions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interactive baking function
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _interactive() {
|
||||
$this->DbConfig->interactive = $this->Model->interactive = $this->interactive = true;
|
||||
$this->hr();
|
||||
$this->out(__d('cake_console', "Bake Fixture\nPath: %s", $this->getPath()));
|
||||
$this->hr();
|
||||
|
||||
if (!isset($this->connection)) {
|
||||
$this->connection = $this->DbConfig->getConfig();
|
||||
}
|
||||
$modelName = $this->Model->getName($this->connection);
|
||||
$useTable = $this->Model->getTable($modelName, $this->connection);
|
||||
$importOptions = $this->importOptions($modelName);
|
||||
$this->bake($modelName, $useTable, $importOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interacts with the User to setup an array of import options. For a fixture.
|
||||
*
|
||||
* @param string $modelName Name of model you are dealing with.
|
||||
* @return array Array of import options.
|
||||
*/
|
||||
public function importOptions($modelName) {
|
||||
$options = array();
|
||||
|
||||
if (!empty($this->params['schema'])) {
|
||||
$options['schema'] = $modelName;
|
||||
} else {
|
||||
$doSchema = $this->in(__d('cake_console', 'Would you like to import schema for this fixture?'), array('y', 'n'), 'n');
|
||||
if ($doSchema === 'y') {
|
||||
$options['schema'] = $modelName;
|
||||
}
|
||||
}
|
||||
if (!empty($this->params['records'])) {
|
||||
$doRecords = 'y';
|
||||
} else {
|
||||
$doRecords = $this->in(__d('cake_console', 'Would you like to use record importing for this fixture?'), array('y', 'n'), 'n');
|
||||
}
|
||||
if ($doRecords === 'y') {
|
||||
$options['records'] = true;
|
||||
}
|
||||
if ($doRecords === 'n') {
|
||||
$prompt = __d('cake_console', "Would you like to build this fixture with data from %s's table?", $modelName);
|
||||
$fromTable = $this->in($prompt, array('y', 'n'), 'n');
|
||||
if (strtolower($fromTable) === 'y') {
|
||||
$options['fromTable'] = true;
|
||||
}
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles and writes a Fixture file
|
||||
*
|
||||
* @param string $model Name of model to bake.
|
||||
* @param string $useTable Name of table to use.
|
||||
* @param array $importOptions Options for public $import
|
||||
* @return string Baked fixture content
|
||||
*/
|
||||
public function bake($model, $useTable = false, $importOptions = array()) {
|
||||
App::uses('CakeSchema', 'Model');
|
||||
$table = $schema = $records = $import = $modelImport = null;
|
||||
$importBits = array();
|
||||
|
||||
if (!$useTable) {
|
||||
$useTable = Inflector::tableize($model);
|
||||
} elseif ($useTable != Inflector::tableize($model)) {
|
||||
$table = $useTable;
|
||||
}
|
||||
|
||||
if (!empty($importOptions)) {
|
||||
if (isset($importOptions['schema'])) {
|
||||
$modelImport = true;
|
||||
$importBits[] = "'model' => '{$importOptions['schema']}'";
|
||||
}
|
||||
if (isset($importOptions['records'])) {
|
||||
$importBits[] = "'records' => true";
|
||||
}
|
||||
if ($this->connection !== 'default') {
|
||||
$importBits[] .= "'connection' => '{$this->connection}'";
|
||||
}
|
||||
if (!empty($importBits)) {
|
||||
$import = sprintf("array(%s)", implode(', ', $importBits));
|
||||
}
|
||||
}
|
||||
|
||||
$this->_Schema = new CakeSchema();
|
||||
$data = $this->_Schema->read(array('models' => false, 'connection' => $this->connection));
|
||||
if (!isset($data['tables'][$useTable])) {
|
||||
$this->error('Could not find your selected table ' . $useTable);
|
||||
return false;
|
||||
}
|
||||
|
||||
$tableInfo = $data['tables'][$useTable];
|
||||
if ($modelImport === null) {
|
||||
$schema = $this->_generateSchema($tableInfo);
|
||||
}
|
||||
|
||||
if (empty($importOptions['records']) && !isset($importOptions['fromTable'])) {
|
||||
$recordCount = 1;
|
||||
if (isset($this->params['count'])) {
|
||||
$recordCount = $this->params['count'];
|
||||
}
|
||||
$records = $this->_makeRecordString($this->_generateRecords($tableInfo, $recordCount));
|
||||
}
|
||||
if (!empty($this->params['records']) || isset($importOptions['fromTable'])) {
|
||||
$records = $this->_makeRecordString($this->_getRecordsFromTable($model, $useTable));
|
||||
}
|
||||
$out = $this->generateFixtureFile($model, compact('records', 'table', 'schema', 'import'));
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the fixture file, and write to disk
|
||||
*
|
||||
* @param string $model name of the model being generated
|
||||
* @param string $otherVars Contents of the fixture file.
|
||||
* @return string Content saved into fixture file.
|
||||
*/
|
||||
public function generateFixtureFile($model, $otherVars) {
|
||||
$defaults = array('table' => null, 'schema' => null, 'records' => null, 'import' => null, 'fields' => null);
|
||||
$vars = array_merge($defaults, $otherVars);
|
||||
|
||||
$path = $this->getPath();
|
||||
$filename = Inflector::camelize($model) . 'Fixture.php';
|
||||
|
||||
$this->Template->set('model', $model);
|
||||
$this->Template->set($vars);
|
||||
$content = $this->Template->generate('classes', 'fixture');
|
||||
|
||||
$this->out("\n" . __d('cake_console', 'Baking test fixture for %s...', $model), 1, Shell::QUIET);
|
||||
$this->createFile($path . $filename, $content);
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the fixtures.
|
||||
*
|
||||
* @return string Path for the fixtures
|
||||
*/
|
||||
public function getPath() {
|
||||
$path = $this->path;
|
||||
if (isset($this->plugin)) {
|
||||
$path = $this->_pluginPath($this->plugin) . 'Test' . DS . 'Fixture' . DS;
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a string representation of a schema.
|
||||
*
|
||||
* @param array $tableInfo Table schema array
|
||||
* @return string fields definitions
|
||||
*/
|
||||
protected function _generateSchema($tableInfo) {
|
||||
$schema = trim($this->_Schema->generateTable('f', $tableInfo), "\n");
|
||||
return substr($schema, 13, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate String representation of Records
|
||||
*
|
||||
* @param array $tableInfo Table schema array
|
||||
* @param int $recordCount The number of records to generate.
|
||||
* @return array Array of records to use in the fixture.
|
||||
*/
|
||||
protected function _generateRecords($tableInfo, $recordCount = 1) {
|
||||
$records = array();
|
||||
for ($i = 0; $i < $recordCount; $i++) {
|
||||
$record = array();
|
||||
foreach ($tableInfo as $field => $fieldInfo) {
|
||||
if (empty($fieldInfo['type'])) {
|
||||
continue;
|
||||
}
|
||||
$insert = '';
|
||||
switch ($fieldInfo['type']) {
|
||||
case 'integer':
|
||||
case 'float':
|
||||
$insert = $i + 1;
|
||||
break;
|
||||
case 'string':
|
||||
case 'binary':
|
||||
$isPrimaryUuid = (
|
||||
isset($fieldInfo['key']) && strtolower($fieldInfo['key']) === 'primary' &&
|
||||
isset($fieldInfo['length']) && $fieldInfo['length'] == 36
|
||||
);
|
||||
if ($isPrimaryUuid) {
|
||||
$insert = String::uuid();
|
||||
} else {
|
||||
$insert = "Lorem ipsum dolor sit amet";
|
||||
if (!empty($fieldInfo['length'])) {
|
||||
$insert = substr($insert, 0, (int)$fieldInfo['length'] - 2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'timestamp':
|
||||
$insert = time();
|
||||
break;
|
||||
case 'datetime':
|
||||
$insert = date('Y-m-d H:i:s');
|
||||
break;
|
||||
case 'date':
|
||||
$insert = date('Y-m-d');
|
||||
break;
|
||||
case 'time':
|
||||
$insert = date('H:i:s');
|
||||
break;
|
||||
case 'boolean':
|
||||
$insert = 1;
|
||||
break;
|
||||
case 'text':
|
||||
$insert = "Lorem ipsum dolor sit amet, aliquet feugiat.";
|
||||
$insert .= " Convallis morbi fringilla gravida,";
|
||||
$insert .= " phasellus feugiat dapibus velit nunc, pulvinar eget sollicitudin";
|
||||
$insert .= " venenatis cum nullam, vivamus ut a sed, mollitia lectus. Nulla";
|
||||
$insert .= " vestibulum massa neque ut et, id hendrerit sit,";
|
||||
$insert .= " feugiat in taciti enim proin nibh, tempor dignissim, rhoncus";
|
||||
$insert .= " duis vestibulum nunc mattis convallis.";
|
||||
break;
|
||||
}
|
||||
$record[$field] = $insert;
|
||||
}
|
||||
$records[] = $record;
|
||||
}
|
||||
return $records;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a $records array into a a string.
|
||||
*
|
||||
* @param array $records Array of records to be converted to string
|
||||
* @return string A string value of the $records array.
|
||||
*/
|
||||
protected function _makeRecordString($records) {
|
||||
$out = "array(\n";
|
||||
foreach ($records as $record) {
|
||||
$values = array();
|
||||
foreach ($record as $field => $value) {
|
||||
$val = var_export($value, true);
|
||||
if ($val === 'NULL') {
|
||||
$val = 'null';
|
||||
}
|
||||
$values[] = "\t\t\t'$field' => $val";
|
||||
}
|
||||
$out .= "\t\tarray(\n";
|
||||
$out .= implode(",\n", $values);
|
||||
$out .= "\n\t\t),\n";
|
||||
}
|
||||
$out .= "\t)";
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interact with the user to get a custom SQL condition and use that to extract data
|
||||
* to build a fixture.
|
||||
*
|
||||
* @param string $modelName name of the model to take records from.
|
||||
* @param string $useTable Name of table to use.
|
||||
* @return array Array of records.
|
||||
*/
|
||||
protected function _getRecordsFromTable($modelName, $useTable = null) {
|
||||
if ($this->interactive) {
|
||||
$condition = null;
|
||||
$prompt = __d('cake_console', "Please provide a SQL fragment to use as conditions\nExample: WHERE 1=1");
|
||||
while (!$condition) {
|
||||
$condition = $this->in($prompt, null, 'WHERE 1=1');
|
||||
}
|
||||
$prompt = __d('cake_console', "How many records do you want to import?");
|
||||
$recordCount = $this->in($prompt, null, 10);
|
||||
} else {
|
||||
$condition = 'WHERE 1=1';
|
||||
$recordCount = (isset($this->params['count']) ? $this->params['count'] : 10);
|
||||
}
|
||||
$modelObject = new Model(array('name' => $modelName, 'table' => $useTable, 'ds' => $this->connection));
|
||||
$records = $modelObject->find('all', array(
|
||||
'conditions' => $condition,
|
||||
'recursive' => -1,
|
||||
'limit' => $recordCount
|
||||
));
|
||||
|
||||
$schema = $modelObject->schema(true);
|
||||
$out = array();
|
||||
foreach ($records as $record) {
|
||||
$row = array();
|
||||
foreach ($record[$modelObject->alias] as $field => $value) {
|
||||
if ($schema[$field]['type'] === 'boolean') {
|
||||
$value = (int)(bool)$value;
|
||||
}
|
||||
$row[$field] = $value;
|
||||
}
|
||||
$out[] = $row;
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
}
|
||||
1049
lib/Cake/Console/Command/Task/ModelTask.php
Normal file
1049
lib/Cake/Console/Command/Task/ModelTask.php
Normal file
File diff suppressed because it is too large
Load diff
231
lib/Cake/Console/Command/Task/PluginTask.php
Normal file
231
lib/Cake/Console/Command/Task/PluginTask.php
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
<?php
|
||||
/**
|
||||
* The Plugin Task handles creating an empty plugin, ready to be used
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since CakePHP(tm) v 1.2
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('AppShell', 'Console/Command');
|
||||
App::uses('File', 'Utility');
|
||||
App::uses('Folder', 'Utility');
|
||||
|
||||
/**
|
||||
* The Plugin Task handles creating an empty plugin, ready to be used
|
||||
*
|
||||
* @package Cake.Console.Command.Task
|
||||
*/
|
||||
class PluginTask extends AppShell {
|
||||
|
||||
/**
|
||||
* path to plugins directory
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $path = null;
|
||||
|
||||
/**
|
||||
* Path to the bootstrap file. Changed in tests.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $bootstrap = null;
|
||||
|
||||
/**
|
||||
* initialize
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function initialize() {
|
||||
$this->path = current(App::path('plugins'));
|
||||
$this->bootstrap = APP . 'Config' . DS . 'bootstrap.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Execution method always used for tasks
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute() {
|
||||
if (isset($this->args[0])) {
|
||||
$plugin = Inflector::camelize($this->args[0]);
|
||||
$pluginPath = $this->_pluginPath($plugin);
|
||||
if (is_dir($pluginPath)) {
|
||||
$this->out(__d('cake_console', 'Plugin: %s already exists, no action taken', $plugin));
|
||||
$this->out(__d('cake_console', 'Path: %s', $pluginPath));
|
||||
return false;
|
||||
}
|
||||
$this->_interactive($plugin);
|
||||
} else {
|
||||
return $this->_interactive();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interactive interface
|
||||
*
|
||||
* @param string $plugin The plugin name.
|
||||
* @return void
|
||||
*/
|
||||
protected function _interactive($plugin = null) {
|
||||
while ($plugin === null) {
|
||||
$plugin = $this->in(__d('cake_console', 'Enter the name of the plugin in CamelCase format'));
|
||||
}
|
||||
|
||||
if (!$this->bake($plugin)) {
|
||||
$this->error(__d('cake_console', "An error occurred trying to bake: %s in %s", $plugin, $this->path . $plugin));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bake the plugin, create directories and files
|
||||
*
|
||||
* @param string $plugin Name of the plugin in CamelCased format
|
||||
* @return bool
|
||||
*/
|
||||
public function bake($plugin) {
|
||||
$pathOptions = App::path('plugins');
|
||||
if (count($pathOptions) > 1) {
|
||||
$this->findPath($pathOptions);
|
||||
}
|
||||
$this->hr();
|
||||
$this->out(__d('cake_console', "<info>Plugin Name:</info> %s", $plugin));
|
||||
$this->out(__d('cake_console', "<info>Plugin Directory:</info> %s", $this->path . $plugin));
|
||||
$this->hr();
|
||||
|
||||
$looksGood = $this->in(__d('cake_console', 'Look okay?'), array('y', 'n', 'q'), 'y');
|
||||
|
||||
if (strtolower($looksGood) === 'y') {
|
||||
$Folder = new Folder($this->path . $plugin);
|
||||
$directories = array(
|
||||
'Config' . DS . 'Schema',
|
||||
'Model' . DS . 'Behavior',
|
||||
'Model' . DS . 'Datasource',
|
||||
'Console' . DS . 'Command' . DS . 'Task',
|
||||
'Controller' . DS . 'Component',
|
||||
'Lib',
|
||||
'View' . DS . 'Helper',
|
||||
'Test' . DS . 'Case' . DS . 'Controller' . DS . 'Component',
|
||||
'Test' . DS . 'Case' . DS . 'View' . DS . 'Helper',
|
||||
'Test' . DS . 'Case' . DS . 'Model' . DS . 'Behavior',
|
||||
'Test' . DS . 'Fixture',
|
||||
'Vendor',
|
||||
'webroot'
|
||||
);
|
||||
|
||||
foreach ($directories as $directory) {
|
||||
$dirPath = $this->path . $plugin . DS . $directory;
|
||||
$Folder->create($dirPath);
|
||||
new File($dirPath . DS . 'empty', true);
|
||||
}
|
||||
|
||||
foreach ($Folder->messages() as $message) {
|
||||
$this->out($message, 1, Shell::VERBOSE);
|
||||
}
|
||||
|
||||
$errors = $Folder->errors();
|
||||
if (!empty($errors)) {
|
||||
foreach ($errors as $message) {
|
||||
$this->error($message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$controllerFileName = $plugin . 'AppController.php';
|
||||
|
||||
$out = "<?php\n\n";
|
||||
$out .= "App::uses('AppController', 'Controller');\n\n";
|
||||
$out .= "class {$plugin}AppController extends AppController {\n\n";
|
||||
$out .= "}\n";
|
||||
$this->createFile($this->path . $plugin . DS . 'Controller' . DS . $controllerFileName, $out);
|
||||
|
||||
$modelFileName = $plugin . 'AppModel.php';
|
||||
|
||||
$out = "<?php\n\n";
|
||||
$out .= "App::uses('AppModel', 'Model');\n\n";
|
||||
$out .= "class {$plugin}AppModel extends AppModel {\n\n";
|
||||
$out .= "}\n";
|
||||
$this->createFile($this->path . $plugin . DS . 'Model' . DS . $modelFileName, $out);
|
||||
|
||||
$this->_modifyBootstrap($plugin);
|
||||
|
||||
$this->hr();
|
||||
$this->out(__d('cake_console', '<success>Created:</success> %s in %s', $plugin, $this->path . $plugin), 2);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the app's bootstrap.php file.
|
||||
*
|
||||
* @param string $plugin Name of plugin
|
||||
* @return void
|
||||
*/
|
||||
protected function _modifyBootstrap($plugin) {
|
||||
$bootstrap = new File($this->bootstrap, false);
|
||||
$contents = $bootstrap->read();
|
||||
if (!preg_match("@\n\s*CakePlugin::loadAll@", $contents)) {
|
||||
$bootstrap->append("\nCakePlugin::load('$plugin', array('bootstrap' => false, 'routes' => false));\n");
|
||||
$this->out('');
|
||||
$this->out(__d('cake_dev', '%s modified', $this->bootstrap));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* find and change $this->path to the user selection
|
||||
*
|
||||
* @param array $pathOptions The list of paths to look in.
|
||||
* @return void
|
||||
*/
|
||||
public function findPath($pathOptions) {
|
||||
$valid = false;
|
||||
foreach ($pathOptions as $i => $path) {
|
||||
if (!is_dir($path)) {
|
||||
unset($pathOptions[$i]);
|
||||
}
|
||||
}
|
||||
$pathOptions = array_values($pathOptions);
|
||||
|
||||
$max = count($pathOptions);
|
||||
while (!$valid) {
|
||||
foreach ($pathOptions as $i => $option) {
|
||||
$this->out($i + 1 . '. ' . $option);
|
||||
}
|
||||
$prompt = __d('cake_console', 'Choose a plugin path from the paths above.');
|
||||
$choice = $this->in($prompt, null, 1);
|
||||
if (intval($choice) > 0 && intval($choice) <= $max) {
|
||||
$valid = true;
|
||||
}
|
||||
}
|
||||
$this->path = $pathOptions[$choice - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser() {
|
||||
$parser = parent::getOptionParser();
|
||||
|
||||
$parser->description(
|
||||
__d('cake_console', 'Create the directory structure, AppModel and AppController classes for a new plugin. ' .
|
||||
'Can create plugins in any of your bootstrapped plugin paths.')
|
||||
)->addArgument('name', array(
|
||||
'help' => __d('cake_console', 'CamelCased name of the plugin to create.')
|
||||
));
|
||||
|
||||
return $parser;
|
||||
}
|
||||
|
||||
}
|
||||
448
lib/Cake/Console/Command/Task/ProjectTask.php
Normal file
448
lib/Cake/Console/Command/Task/ProjectTask.php
Normal file
|
|
@ -0,0 +1,448 @@
|
|||
<?php
|
||||
/**
|
||||
* The Project Task handles creating the base application
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since CakePHP(tm) v 1.2
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('AppShell', 'Console/Command');
|
||||
App::uses('File', 'Utility');
|
||||
App::uses('Folder', 'Utility');
|
||||
App::uses('String', 'Utility');
|
||||
App::uses('Security', 'Utility');
|
||||
|
||||
/**
|
||||
* Task class for creating new project apps and plugins
|
||||
*
|
||||
* @package Cake.Console.Command.Task
|
||||
*/
|
||||
class ProjectTask extends AppShell {
|
||||
|
||||
/**
|
||||
* configs path (used in testing).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $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.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function execute() {
|
||||
$project = null;
|
||||
if (isset($this->args[0])) {
|
||||
$project = $this->args[0];
|
||||
} else {
|
||||
$appContents = array_diff(scandir(APP), array('.', '..'));
|
||||
if (empty($appContents)) {
|
||||
$suggestedPath = rtrim(APP, DS);
|
||||
} else {
|
||||
$suggestedPath = APP . 'myapp';
|
||||
}
|
||||
}
|
||||
|
||||
while (!$project) {
|
||||
$prompt = __d('cake_console', "What is the path to the project you want to bake?");
|
||||
$project = $this->in($prompt, null, $suggestedPath);
|
||||
}
|
||||
|
||||
if ($project && !Folder::isAbsolute($project) && isset($_SERVER['PWD'])) {
|
||||
$project = $_SERVER['PWD'] . DS . $project;
|
||||
}
|
||||
|
||||
$response = false;
|
||||
while (!$response && is_dir($project) === true && file_exists($project . 'Config' . 'core.php')) {
|
||||
$prompt = __d('cake_console', '<warning>A project already exists in this location:</warning> %s Overwrite?', $project);
|
||||
$response = $this->in($prompt, array('y', 'n'), 'n');
|
||||
if (strtolower($response) === 'n') {
|
||||
$response = $project = false;
|
||||
}
|
||||
}
|
||||
|
||||
$success = true;
|
||||
if ($this->bake($project)) {
|
||||
$path = Folder::slashTerm($project);
|
||||
|
||||
if ($this->securitySalt($path) === true) {
|
||||
$this->out(__d('cake_console', ' * Random hash key created for \'Security.salt\''));
|
||||
} else {
|
||||
$this->err(__d('cake_console', 'Unable to generate random hash for \'Security.salt\', you should change it in %s', APP . 'Config' . DS . 'core.php'));
|
||||
$success = false;
|
||||
}
|
||||
|
||||
if ($this->securityCipherSeed($path) === true) {
|
||||
$this->out(__d('cake_console', ' * Random seed created for \'Security.cipherSeed\''));
|
||||
} else {
|
||||
$this->err(__d('cake_console', 'Unable to generate random seed for \'Security.cipherSeed\', you should change it in %s', APP . 'Config' . DS . 'core.php'));
|
||||
$success = false;
|
||||
}
|
||||
|
||||
if ($this->cachePrefix($path)) {
|
||||
$this->out(__d('cake_console', ' * Cache prefix set'));
|
||||
} else {
|
||||
$this->err(__d('cake_console', 'The cache prefix was <error>NOT</error> set'));
|
||||
$success = false;
|
||||
}
|
||||
|
||||
if ($this->consolePath($path) === true) {
|
||||
$this->out(__d('cake_console', ' * app/Console/cake.php path set.'));
|
||||
} else {
|
||||
$this->err(__d('cake_console', 'Unable to set console path for app/Console.'));
|
||||
$success = false;
|
||||
}
|
||||
|
||||
$hardCode = false;
|
||||
if ($this->cakeOnIncludePath()) {
|
||||
$this->out(__d('cake_console', '<info>CakePHP is on your `include_path`. CAKE_CORE_INCLUDE_PATH will be set, but commented out.</info>'));
|
||||
} else {
|
||||
$this->out(__d('cake_console', '<warning>CakePHP is not on your `include_path`, CAKE_CORE_INCLUDE_PATH will be hard coded.</warning>'));
|
||||
$this->out(__d('cake_console', 'You can fix this by adding CakePHP to your `include_path`.'));
|
||||
$hardCode = true;
|
||||
}
|
||||
$success = $this->corePath($path, $hardCode) === true;
|
||||
if ($success) {
|
||||
$this->out(__d('cake_console', ' * CAKE_CORE_INCLUDE_PATH set to %s in %s', CAKE_CORE_INCLUDE_PATH, 'webroot/index.php'));
|
||||
$this->out(__d('cake_console', ' * CAKE_CORE_INCLUDE_PATH set to %s in %s', CAKE_CORE_INCLUDE_PATH, 'webroot/test.php'));
|
||||
} else {
|
||||
$this->err(__d('cake_console', 'Unable to set CAKE_CORE_INCLUDE_PATH, you should change it in %s', $path . 'webroot' . DS . 'index.php'));
|
||||
$success = false;
|
||||
}
|
||||
if ($success && $hardCode) {
|
||||
$this->out(__d('cake_console', ' * <warning>Remember to check these values after moving to production server</warning>'));
|
||||
}
|
||||
|
||||
$Folder = new Folder($path);
|
||||
if (!$Folder->chmod($path . 'tmp', 0777)) {
|
||||
$this->err(__d('cake_console', 'Could not set permissions on %s', $path . DS . 'tmp'));
|
||||
$this->out('chmod -R 0777 ' . $path . DS . 'tmp');
|
||||
$success = false;
|
||||
}
|
||||
if ($success) {
|
||||
$this->out(__d('cake_console', '<success>Project baked successfully!</success>'));
|
||||
} else {
|
||||
$this->out(__d('cake_console', 'Project baked but with <warning>some issues.</warning>.'));
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks PHP's include_path for CakePHP.
|
||||
*
|
||||
* @return bool Indicates whether or not CakePHP exists on include_path
|
||||
*/
|
||||
public function cakeOnIncludePath() {
|
||||
$paths = explode(PATH_SEPARATOR, ini_get('include_path'));
|
||||
foreach ($paths as $path) {
|
||||
if (file_exists($path . DS . 'Cake' . DS . 'bootstrap.php')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param string $path Project path
|
||||
* @param string $skel Path to copy from
|
||||
* @param string $skip array of directories to skip when copying
|
||||
* @return mixed
|
||||
*/
|
||||
public function bake($path, $skel = null, $skip = array('empty')) {
|
||||
if (!$skel && !empty($this->params['skel'])) {
|
||||
$skel = $this->params['skel'];
|
||||
}
|
||||
while (!$skel) {
|
||||
$skel = $this->in(
|
||||
__d('cake_console', "What is the path to the directory layout you wish to copy?"),
|
||||
null,
|
||||
CAKE . 'Console' . DS . 'Templates' . DS . 'skel'
|
||||
);
|
||||
if (!$skel) {
|
||||
$this->err(__d('cake_console', 'The directory path you supplied was empty. Please try again.'));
|
||||
} else {
|
||||
while (is_dir($skel) === false) {
|
||||
$skel = $this->in(
|
||||
__d('cake_console', 'Directory path does not exist please choose another:'),
|
||||
null,
|
||||
CAKE . 'Console' . DS . 'Templates' . DS . 'skel'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$app = basename($path);
|
||||
|
||||
$this->out(__d('cake_console', '<info>Skel Directory</info>: ') . $skel);
|
||||
$this->out(__d('cake_console', '<info>Will be copied to</info>: ') . $path);
|
||||
$this->hr();
|
||||
|
||||
$looksGood = $this->in(__d('cake_console', 'Look okay?'), array('y', 'n', 'q'), 'y');
|
||||
|
||||
switch (strtolower($looksGood)) {
|
||||
case 'y':
|
||||
$Folder = new Folder($skel);
|
||||
if (!empty($this->params['empty'])) {
|
||||
$skip = array();
|
||||
}
|
||||
|
||||
if ($Folder->copy(array('to' => $path, 'skip' => $skip))) {
|
||||
$this->hr();
|
||||
$this->out(__d('cake_console', '<success>Created:</success> %s in %s', $app, $path));
|
||||
$this->hr();
|
||||
} else {
|
||||
$this->err(__d('cake_console', "<error>Could not create</error> '%s' properly.", $app));
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($Folder->messages() as $message) {
|
||||
$this->out(String::wrap(' * ' . $message), 1, Shell::VERBOSE);
|
||||
}
|
||||
|
||||
return true;
|
||||
case 'n':
|
||||
unset($this->args[0]);
|
||||
$this->execute();
|
||||
return false;
|
||||
case 'q':
|
||||
$this->out(__d('cake_console', '<error>Bake Aborted.</error>'));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the correct path to the CakePHP libs that are generating the project
|
||||
* and points app/console/cake.php to the right place
|
||||
*
|
||||
* @param string $path Project path.
|
||||
* @return bool success
|
||||
*/
|
||||
public function consolePath($path) {
|
||||
$File = new File($path . 'Console' . DS . 'cake.php');
|
||||
$contents = $File->read();
|
||||
if (preg_match('/(__CAKE_PATH__)/', $contents, $match)) {
|
||||
$root = strpos(CAKE_CORE_INCLUDE_PATH, '/') === 0 ? " \$ds . '" : "'";
|
||||
$replacement = $root . str_replace(DS, "' . \$ds . '", trim(CAKE_CORE_INCLUDE_PATH, DS)) . "'";
|
||||
$result = str_replace($match[0], $replacement, $contents);
|
||||
if ($File->write($result)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates and writes 'Security.salt'
|
||||
*
|
||||
* @param string $path Project path
|
||||
* @return bool Success
|
||||
*/
|
||||
public function securitySalt($path) {
|
||||
$File = new File($path . 'Config' . DS . 'core.php');
|
||||
$contents = $File->read();
|
||||
if (preg_match('/([\s]*Configure::write\(\'Security.salt\',[\s\'A-z0-9]*\);)/', $contents, $match)) {
|
||||
$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 bool Success
|
||||
*/
|
||||
public function securityCipherSeed($path) {
|
||||
$File = new File($path . 'Config' . DS . 'core.php');
|
||||
$contents = $File->read();
|
||||
if (preg_match('/([\s]*Configure::write\(\'Security.cipherSeed\',[\s\'A-z0-9]*\);)/', $contents, $match)) {
|
||||
App::uses('Security', 'Utility');
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes cache prefix using app's name
|
||||
*
|
||||
* @param string $dir Path to project
|
||||
* @return bool Success
|
||||
*/
|
||||
public function cachePrefix($dir) {
|
||||
$app = basename($dir);
|
||||
$File = new File($dir . 'Config' . DS . 'core.php');
|
||||
$contents = $File->read();
|
||||
if (preg_match('/(\$prefix = \'myapp_\';)/', $contents, $match)) {
|
||||
$result = str_replace($match[0], '$prefix = \'' . $app . '_\';', $contents);
|
||||
return $File->write($result);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates and writes CAKE_CORE_INCLUDE_PATH
|
||||
*
|
||||
* @param string $path Project path
|
||||
* @param bool $hardCode Whether or not define calls should be hardcoded.
|
||||
* @return bool Success
|
||||
*/
|
||||
public function corePath($path, $hardCode = true) {
|
||||
if (dirname($path) !== CAKE_CORE_INCLUDE_PATH) {
|
||||
$filename = $path . 'webroot' . DS . 'index.php';
|
||||
if (!$this->_replaceCorePath($filename, $hardCode)) {
|
||||
return false;
|
||||
}
|
||||
$filename = $path . 'webroot' . DS . 'test.php';
|
||||
if (!$this->_replaceCorePath($filename, $hardCode)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the __CAKE_PATH__ placeholder in the template files.
|
||||
*
|
||||
* @param string $filename The filename to operate on.
|
||||
* @param bool $hardCode Whether or not the define should be uncommented.
|
||||
* @return bool Success
|
||||
*/
|
||||
protected function _replaceCorePath($filename, $hardCode) {
|
||||
$contents = file_get_contents($filename);
|
||||
|
||||
$root = strpos(CAKE_CORE_INCLUDE_PATH, '/') === 0 ? " DS . '" : "'";
|
||||
$corePath = $root . str_replace(DS, "' . DS . '", trim(CAKE_CORE_INCLUDE_PATH, DS)) . "'";
|
||||
|
||||
$result = str_replace('__CAKE_PATH__', $corePath, $contents, $count);
|
||||
if ($hardCode) {
|
||||
$result = str_replace('//define(\'CAKE_CORE', 'define(\'CAKE_CORE', $result);
|
||||
}
|
||||
if (!file_put_contents($filename, $result)) {
|
||||
return false;
|
||||
}
|
||||
return (bool)$count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables Configure::read('Routing.prefixes') in /app/Config/core.php
|
||||
*
|
||||
* @param string $name Name to use as admin routing
|
||||
* @return bool Success
|
||||
*/
|
||||
public function cakeAdmin($name) {
|
||||
$path = (empty($this->configPath)) ? APP . 'Config' . DS : $this->configPath;
|
||||
$File = new File($path . 'core.php');
|
||||
$contents = $File->read();
|
||||
if (preg_match('%(\s*[/]*Configure::write\(\'Routing.prefixes\',[\s\'a-z,\)\(]*\);)%', $contents, $match)) {
|
||||
$result = str_replace($match[0], "\n" . 'Configure::write(\'Routing.prefixes\', array(\'' . $name . '\'));', $contents);
|
||||
if ($File->write($result)) {
|
||||
Configure::write('Routing.prefixes', array($name));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for Configure::read('Routing.prefixes') and forces user to input it if not enabled
|
||||
*
|
||||
* @return string Admin route to use
|
||||
*/
|
||||
public function getPrefix() {
|
||||
$admin = '';
|
||||
$prefixes = Configure::read('Routing.prefixes');
|
||||
if (!empty($prefixes)) {
|
||||
if (count($prefixes) === 1) {
|
||||
return $prefixes[0] . '_';
|
||||
}
|
||||
if ($this->interactive) {
|
||||
$this->out();
|
||||
$this->out(__d('cake_console', 'You have more than one routing prefix configured'));
|
||||
}
|
||||
$options = array();
|
||||
foreach ($prefixes as $i => $prefix) {
|
||||
$options[] = $i + 1;
|
||||
if ($this->interactive) {
|
||||
$this->out($i + 1 . '. ' . $prefix);
|
||||
}
|
||||
}
|
||||
$selection = $this->in(__d('cake_console', 'Please choose a prefix to bake with.'), $options, 1);
|
||||
return $prefixes[$selection - 1] . '_';
|
||||
}
|
||||
if ($this->interactive) {
|
||||
$this->hr();
|
||||
$this->out(__d('cake_console', 'You need to enable %s in %s to use prefix routing.',
|
||||
'Configure::write(\'Routing.prefixes\', array(\'admin\'))',
|
||||
'/app/Config/core.php'));
|
||||
$this->out(__d('cake_console', 'What would you like the prefix route to be?'));
|
||||
$this->out(__d('cake_console', 'Example: %s', 'www.example.com/admin/controller'));
|
||||
while (!$admin) {
|
||||
$admin = $this->in(__d('cake_console', 'Enter a routing prefix:'), null, 'admin');
|
||||
}
|
||||
if ($this->cakeAdmin($admin) !== true) {
|
||||
$this->out(__d('cake_console', '<error>Unable to write to</error> %s.', '/app/Config/core.php'));
|
||||
$this->out(__d('cake_console', 'You need to enable %s in %s to use prefix routing.',
|
||||
'Configure::write(\'Routing.prefixes\', array(\'admin\'))',
|
||||
'/app/Config/core.php'));
|
||||
return $this->_stop();
|
||||
}
|
||||
return $admin . '_';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser() {
|
||||
$parser = parent::getOptionParser();
|
||||
|
||||
$parser->description(
|
||||
__d('cake_console', 'Generate a new CakePHP project skeleton.')
|
||||
)->addArgument('name', array(
|
||||
'help' => __d('cake_console', 'Application directory to make, if it starts with "/" the path is absolute.')
|
||||
))->addOption('empty', array(
|
||||
'boolean' => true,
|
||||
'help' => __d('cake_console', 'Create empty files in each of the directories. Good if you are using git')
|
||||
))->addOption('theme', array(
|
||||
'short' => 't',
|
||||
'help' => __d('cake_console', 'Theme to use when baking code.')
|
||||
))->addOption('skel', array(
|
||||
'default' => current(App::core('Console')) . 'Templates' . DS . 'skel',
|
||||
'help' => __d('cake_console', 'The directory layout to use for the new application skeleton.' .
|
||||
' Defaults to cake/Console/Templates/skel of CakePHP used to create the project.')
|
||||
));
|
||||
|
||||
return $parser;
|
||||
}
|
||||
|
||||
}
|
||||
217
lib/Cake/Console/Command/Task/TemplateTask.php
Normal file
217
lib/Cake/Console/Command/Task/TemplateTask.php
Normal file
|
|
@ -0,0 +1,217 @@
|
|||
<?php
|
||||
/**
|
||||
* Template Task can generate templated output Used in other Tasks
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since CakePHP(tm) v 1.3
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('AppShell', 'Console/Command');
|
||||
App::uses('Folder', 'Utility');
|
||||
|
||||
/**
|
||||
* Template Task can generate templated output Used in other Tasks.
|
||||
* Acts like a simplified View class.
|
||||
*
|
||||
* @package Cake.Console.Command.Task
|
||||
*/
|
||||
class TemplateTask extends AppShell {
|
||||
|
||||
/**
|
||||
* variables to add to template scope
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $templateVars = array();
|
||||
|
||||
/**
|
||||
* Paths to look for templates on.
|
||||
* Contains a list of $theme => $path
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $templatePaths = array();
|
||||
|
||||
/**
|
||||
* Initialize callback. Setup paths for the template task.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function initialize() {
|
||||
$this->templatePaths = $this->_findThemes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the paths to all the installed shell themes in the app.
|
||||
*
|
||||
* Bake themes are directories not named `skel` inside a `Console/Templates` path.
|
||||
* They are listed in this order: app -> plugin -> default
|
||||
*
|
||||
* @return array Array of bake themes that are installed.
|
||||
*/
|
||||
protected function _findThemes() {
|
||||
$paths = App::path('Console');
|
||||
|
||||
$plugins = App::objects('plugin');
|
||||
foreach ($plugins as $plugin) {
|
||||
$paths[] = $this->_pluginPath($plugin) . 'Console' . DS;
|
||||
}
|
||||
|
||||
$core = current(App::core('Console'));
|
||||
$separator = DS === '/' ? '/' : '\\\\';
|
||||
$core = preg_replace('#shells' . $separator . '$#', '', $core);
|
||||
|
||||
$Folder = new Folder($core . 'Templates' . DS . 'default');
|
||||
|
||||
$contents = $Folder->read();
|
||||
$themeFolders = $contents[0];
|
||||
|
||||
$paths[] = $core;
|
||||
|
||||
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 string|array $one A string or an array of data.
|
||||
* @param string|array $two Value in case $one is a string (which then works as the key).
|
||||
* Unused if $one is an associative array, otherwise serves as the values to $one's keys.
|
||||
* @return void
|
||||
*/
|
||||
public function set($one, $two = null) {
|
||||
if (is_array($one)) {
|
||||
if (is_array($two)) {
|
||||
$data = array_combine($one, $two);
|
||||
} else {
|
||||
$data = $one;
|
||||
}
|
||||
} else {
|
||||
$data = array($one => $two);
|
||||
}
|
||||
|
||||
if (!$data) {
|
||||
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 array $vars Additional vars to set to template scope.
|
||||
* @return string contents of generated code template
|
||||
*/
|
||||
public function generate($directory, $filename, $vars = null) {
|
||||
if ($vars !== null) {
|
||||
$this->set($vars);
|
||||
}
|
||||
if (empty($this->templatePaths)) {
|
||||
$this->initialize();
|
||||
}
|
||||
$themePath = $this->getThemePath();
|
||||
$templateFile = $this->_findTemplate($themePath, $directory, $filename);
|
||||
if ($templateFile) {
|
||||
extract($this->templateVars);
|
||||
ob_start();
|
||||
ob_implicit_flush(0);
|
||||
include $templateFile;
|
||||
$content = ob_get_clean();
|
||||
return $content;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the theme name for the current operation.
|
||||
* If there is only one theme in $templatePaths it will be used.
|
||||
* If there is a -theme param in the cli args, it will be used.
|
||||
* If there is more than one installed theme user interaction will happen
|
||||
*
|
||||
* @return string returns the path to the selected theme.
|
||||
*/
|
||||
public function getThemePath() {
|
||||
if (count($this->templatePaths) === 1) {
|
||||
$paths = array_values($this->templatePaths);
|
||||
return $paths[0];
|
||||
}
|
||||
if (!empty($this->params['theme']) && isset($this->templatePaths[$this->params['theme']])) {
|
||||
return $this->templatePaths[$this->params['theme']];
|
||||
}
|
||||
|
||||
$this->hr();
|
||||
$this->out(__d('cake_console', 'You have more than one set of templates installed.'));
|
||||
$this->out(__d('cake_console', 'Please choose the template set you wish to use:'));
|
||||
$this->hr();
|
||||
|
||||
$i = 1;
|
||||
$indexedPaths = array();
|
||||
foreach ($this->templatePaths as $key => $path) {
|
||||
$this->out($i . '. ' . $key);
|
||||
$indexedPaths[$i] = $path;
|
||||
$i++;
|
||||
}
|
||||
$index = $this->in(__d('cake_console', 'Which bake theme would you like to use?'), range(1, $i - 1), 1);
|
||||
$themeNames = array_keys($this->templatePaths);
|
||||
$this->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.
|
||||
* @return string filename will exit program if template is not found.
|
||||
*/
|
||||
protected 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(__d('cake_console', 'Could not find template for %s', $filename));
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
582
lib/Cake/Console/Command/Task/TestTask.php
Normal file
582
lib/Cake/Console/Command/Task/TestTask.php
Normal file
|
|
@ -0,0 +1,582 @@
|
|||
<?php
|
||||
/**
|
||||
* The TestTask handles creating and updating test files.
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since CakePHP(tm) v 1.3
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('AppShell', 'Console/Command');
|
||||
App::uses('BakeTask', 'Console/Command/Task');
|
||||
App::uses('ClassRegistry', 'Utility');
|
||||
|
||||
/**
|
||||
* Task class for creating and updating test files.
|
||||
*
|
||||
* @package Cake.Console.Command.Task
|
||||
*/
|
||||
class TestTask extends BakeTask {
|
||||
|
||||
/**
|
||||
* path to TESTS directory
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $path = TESTS;
|
||||
|
||||
/**
|
||||
* Tasks used.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $tasks = array('Template');
|
||||
|
||||
/**
|
||||
* class types that methods can be generated for
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $classTypes = array(
|
||||
'Model' => 'Model',
|
||||
'Controller' => 'Controller',
|
||||
'Component' => 'Controller/Component',
|
||||
'Behavior' => 'Model/Behavior',
|
||||
'Helper' => 'View/Helper'
|
||||
);
|
||||
|
||||
/**
|
||||
* Mapping between packages, and their baseclass + package.
|
||||
* This is used to generate App::uses() call to autoload base
|
||||
* classes if a developer has forgotten to do so.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $baseTypes = array(
|
||||
'Model' => array('Model', 'Model'),
|
||||
'Behavior' => array('ModelBehavior', 'Model'),
|
||||
'Controller' => array('Controller', 'Controller'),
|
||||
'Component' => array('Component', 'Controller'),
|
||||
'Helper' => array('Helper', 'View')
|
||||
);
|
||||
|
||||
/**
|
||||
* Internal list of fixtures that have been added so far.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_fixtures = array();
|
||||
|
||||
/**
|
||||
* Execution method always used for tasks
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute() {
|
||||
parent::execute();
|
||||
$count = count($this->args);
|
||||
if (!$count) {
|
||||
$this->_interactive();
|
||||
}
|
||||
|
||||
if ($count === 1) {
|
||||
$this->_interactive($this->args[0]);
|
||||
}
|
||||
|
||||
if ($count > 1) {
|
||||
$type = Inflector::classify($this->args[0]);
|
||||
if ($this->bake($type, $this->args[1])) {
|
||||
$this->out('<success>Done</success>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles interactive baking
|
||||
*
|
||||
* @param string $type The type of object to bake a test for.
|
||||
* @return string|bool
|
||||
*/
|
||||
protected function _interactive($type = null) {
|
||||
$this->interactive = true;
|
||||
$this->hr();
|
||||
$this->out(__d('cake_console', 'Bake Tests'));
|
||||
$this->out(__d('cake_console', 'Path: %s', $this->getPath()));
|
||||
$this->hr();
|
||||
|
||||
if ($type) {
|
||||
$type = Inflector::camelize($type);
|
||||
if (!isset($this->classTypes[$type])) {
|
||||
$this->error(__d('cake_console', 'Incorrect type provided. Please choose one of %s', implode(', ', array_keys($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
|
||||
* @return string|bool
|
||||
*/
|
||||
public function bake($type, $className) {
|
||||
$plugin = null;
|
||||
if ($this->plugin) {
|
||||
$plugin = $this->plugin . '.';
|
||||
}
|
||||
|
||||
$realType = $this->mapType($type, $plugin);
|
||||
$fullClassName = $this->getRealClassName($type, $className);
|
||||
|
||||
if ($this->typeCanDetectFixtures($type) && $this->isLoadableClass($realType, $fullClassName)) {
|
||||
$this->out(__d('cake_console', 'Bake is detecting possible fixtures...'));
|
||||
$testSubject = $this->buildTestSubject($type, $className);
|
||||
$this->generateFixtureList($testSubject);
|
||||
} elseif ($this->interactive) {
|
||||
$this->getUserFixtures();
|
||||
}
|
||||
list($baseClass, $baseType) = $this->getBaseType($type);
|
||||
App::uses($baseClass, $baseType);
|
||||
App::uses($fullClassName, $realType);
|
||||
|
||||
$methods = array();
|
||||
if (class_exists($fullClassName)) {
|
||||
$methods = $this->getTestableMethods($fullClassName);
|
||||
}
|
||||
$mock = $this->hasMockClass($type, $fullClassName);
|
||||
list($preConstruct, $construction, $postConstruct) = $this->generateConstructor($type, $fullClassName, $plugin);
|
||||
$uses = $this->generateUses($type, $realType, $fullClassName);
|
||||
|
||||
$this->out("\n" . __d('cake_console', 'Baking test case for %s %s ...', $className, $type), 1, Shell::QUIET);
|
||||
|
||||
$this->Template->set('fixtures', $this->_fixtures);
|
||||
$this->Template->set('plugin', $plugin);
|
||||
$this->Template->set(compact(
|
||||
'className', 'methods', 'type', 'fullClassName', 'mock',
|
||||
'realType', 'preConstruct', 'postConstruct', 'construction',
|
||||
'uses'
|
||||
));
|
||||
$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.
|
||||
*/
|
||||
public function getObjectType() {
|
||||
$this->hr();
|
||||
$this->out(__d('cake_console', 'Select an object type:'));
|
||||
$this->hr();
|
||||
|
||||
$keys = array();
|
||||
$i = 0;
|
||||
foreach ($this->classTypes as $option => $package) {
|
||||
$this->out(++$i . '. ' . $option);
|
||||
$keys[] = $i;
|
||||
}
|
||||
$keys[] = 'q';
|
||||
$selection = $this->in(__d('cake_console', 'Enter the type of object to bake a test for or (q)uit'), $keys, 'q');
|
||||
if ($selection === 'q') {
|
||||
return $this->_stop();
|
||||
}
|
||||
$types = array_keys($this->classTypes);
|
||||
return $types[$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.
|
||||
*/
|
||||
public function getClassName($objectType) {
|
||||
$type = ucfirst(strtolower($objectType));
|
||||
$typeLength = strlen($type);
|
||||
$type = $this->classTypes[$type];
|
||||
if ($this->plugin) {
|
||||
$plugin = $this->plugin . '.';
|
||||
$options = App::objects($plugin . $type);
|
||||
} else {
|
||||
$options = App::objects($type);
|
||||
}
|
||||
$this->out(__d('cake_console', 'Choose a %s class', $objectType));
|
||||
$keys = array();
|
||||
foreach ($options as $key => $option) {
|
||||
$this->out(++$key . '. ' . $option);
|
||||
$keys[] = $key;
|
||||
}
|
||||
while (empty($selection)) {
|
||||
$selection = $this->in(__d('cake_console', 'Choose an existing class, or enter the name of a class that does not exist'));
|
||||
if (is_numeric($selection) && isset($options[$selection - 1])) {
|
||||
$selection = $options[$selection - 1];
|
||||
}
|
||||
if ($type !== 'Model') {
|
||||
$selection = substr($selection, 0, $typeLength * - 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
|
||||
* @return bool
|
||||
*/
|
||||
public function typeCanDetectFixtures($type) {
|
||||
$type = strtolower($type);
|
||||
return in_array($type, array('controller', 'model'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a class with the given package is loaded or can be loaded.
|
||||
*
|
||||
* @param string $package The package of object you are generating tests for eg. controller
|
||||
* @param string $class the Classname of the class the test is being generated for.
|
||||
* @return bool
|
||||
*/
|
||||
public function isLoadableClass($package, $class) {
|
||||
App::uses($class, $package);
|
||||
list($plugin, $ns) = pluginSplit($package);
|
||||
if ($plugin) {
|
||||
App::uses("{$plugin}AppController", $package);
|
||||
App::uses("{$plugin}AppModel", $package);
|
||||
App::uses("{$plugin}AppHelper", $package);
|
||||
}
|
||||
return class_exists($class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an instance of the class to be tested.
|
||||
* So that fixtures can be detected
|
||||
*
|
||||
* @param string $type The Type of object you are generating tests for eg. controller
|
||||
* @param string $class the Classname of the class the test is being generated for.
|
||||
* @return object And instance of the class that is going to be tested.
|
||||
*/
|
||||
public function buildTestSubject($type, $class) {
|
||||
ClassRegistry::flush();
|
||||
App::uses($class, $type);
|
||||
$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. If the class name is already
|
||||
* suffixed with the type, the type will not be duplicated.
|
||||
*
|
||||
* @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 class name
|
||||
*/
|
||||
public function getRealClassName($type, $class) {
|
||||
if (strtolower($type) === 'model' || empty($this->classTypes[$type])) {
|
||||
return $class;
|
||||
}
|
||||
|
||||
$position = strpos($class, $type);
|
||||
|
||||
if ($position !== false && (strlen($class) - $position) === strlen($type)) {
|
||||
return $class;
|
||||
}
|
||||
return $class . $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the types that TestTask uses to concrete types that App::uses can use.
|
||||
*
|
||||
* @param string $type The type of thing having a test generated.
|
||||
* @param string $plugin The plugin name.
|
||||
* @return string
|
||||
* @throws CakeException When invalid object types are requested.
|
||||
*/
|
||||
public function mapType($type, $plugin) {
|
||||
$type = ucfirst($type);
|
||||
if (empty($this->classTypes[$type])) {
|
||||
throw new CakeException(__d('cake_dev', 'Invalid object type.'));
|
||||
}
|
||||
$real = $this->classTypes[$type];
|
||||
if ($plugin) {
|
||||
$real = trim($plugin, '.') . '.' . $real;
|
||||
}
|
||||
return $real;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the base class and package name for a given type.
|
||||
*
|
||||
* @param string $type The type the class having a test
|
||||
* generated for is in.
|
||||
* @return array Array of (class, type)
|
||||
* @throws CakeException on invalid types.
|
||||
*/
|
||||
public function getBaseType($type) {
|
||||
if (empty($this->baseTypes[$type])) {
|
||||
throw new CakeException(__d('cake_dev', 'Invalid type name'));
|
||||
}
|
||||
return $this->baseTypes[$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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
public function generateFixtureList($subject) {
|
||||
$this->_fixtures = array();
|
||||
if ($subject instanceof Model) {
|
||||
$this->_processModel($subject);
|
||||
} elseif ($subject instanceof 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
|
||||
*/
|
||||
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') {
|
||||
if (!empty($subject->hasAndBelongsToMany[$alias]['with'])) {
|
||||
list(, $joinModel) = pluginSplit($subject->hasAndBelongsToMany[$alias]['with']);
|
||||
} else {
|
||||
$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
|
||||
*/
|
||||
protected function _processController($subject) {
|
||||
$subject->constructClasses();
|
||||
$models = array(Inflector::classify($subject->name));
|
||||
if (!empty($subject->uses)) {
|
||||
$models = $subject->uses;
|
||||
}
|
||||
foreach ($models as $model) {
|
||||
list(, $model) = pluginSplit($model);
|
||||
$this->_processModel($subject->{$model});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add class name to the fixture list.
|
||||
* Sets the app. or plugin.plugin_name. prefix.
|
||||
*
|
||||
* @param string $name Name of the Model class that a fixture might be required for.
|
||||
* @return void
|
||||
*/
|
||||
protected function _addFixture($name) {
|
||||
if ($this->plugin) {
|
||||
$prefix = 'plugin.' . Inflector::underscore($this->plugin) . '.';
|
||||
} else {
|
||||
$prefix = 'app.';
|
||||
}
|
||||
$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.
|
||||
*/
|
||||
public function getUserFixtures() {
|
||||
$proceed = $this->in(__d('cake_console', 'Bake could not detect fixtures, would you like to add some?'), array('y', 'n'), 'n');
|
||||
$fixtures = array();
|
||||
if (strtolower($proceed) === 'y') {
|
||||
$fixtureList = $this->in(__d('cake_console', "Please provide a comma separated list of the fixtures names you'd like to use.\nExample: 'app.comment, app.post, plugin.forums.post'"));
|
||||
$fixtureListTrimmed = str_replace(' ', '', $fixtureList);
|
||||
$fixtures = explode(',', $fixtureListTrimmed);
|
||||
}
|
||||
$this->_fixtures = array_merge($this->_fixtures, $fixtures);
|
||||
return $fixtures;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is a mock class required for this type of test?
|
||||
* Controllers require a mock class.
|
||||
*
|
||||
* @param string $type The type of object tests are being generated for eg. controller.
|
||||
* @return bool
|
||||
*/
|
||||
public function hasMockClass($type) {
|
||||
$type = strtolower($type);
|
||||
return $type === 'controller';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a constructor code snippet for the type and class name
|
||||
*
|
||||
* @param string $type The Type of object you are generating tests for eg. controller
|
||||
* @param string $fullClassName The Classname of the class the test is being generated for.
|
||||
* @param string $plugin The plugin name.
|
||||
* @return array Constructor snippets for the thing you are building.
|
||||
*/
|
||||
public function generateConstructor($type, $fullClassName, $plugin) {
|
||||
$type = strtolower($type);
|
||||
$pre = $construct = $post = '';
|
||||
if ($type === 'model') {
|
||||
$construct = "ClassRegistry::init('{$plugin}$fullClassName');\n";
|
||||
}
|
||||
if ($type === 'behavior') {
|
||||
$construct = "new $fullClassName();\n";
|
||||
}
|
||||
if ($type === 'helper') {
|
||||
$pre = "\$View = new View();\n";
|
||||
$construct = "new {$fullClassName}(\$View);\n";
|
||||
}
|
||||
if ($type === 'component') {
|
||||
$pre = "\$Collection = new ComponentCollection();\n";
|
||||
$construct = "new {$fullClassName}(\$Collection);\n";
|
||||
}
|
||||
return array($pre, $construct, $post);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the uses() calls for a type & class name
|
||||
*
|
||||
* @param string $type The Type of object you are generating tests for eg. controller
|
||||
* @param string $realType The package name for the class.
|
||||
* @param string $className The Classname of the class the test is being generated for.
|
||||
* @return array An array containing used classes
|
||||
*/
|
||||
public function generateUses($type, $realType, $className) {
|
||||
$uses = array();
|
||||
$type = strtolower($type);
|
||||
if ($type === 'component') {
|
||||
$uses[] = array('ComponentCollection', 'Controller');
|
||||
$uses[] = array('Component', 'Controller');
|
||||
}
|
||||
if ($type === 'helper') {
|
||||
$uses[] = array('View', 'View');
|
||||
$uses[] = array('Helper', 'View');
|
||||
}
|
||||
$uses[] = array($className, $realType);
|
||||
return $uses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the filename for the test case. resolve the suffixes for controllers
|
||||
* and get the plugin path if needed.
|
||||
*
|
||||
* @param string $type The Type of object you are generating tests for eg. controller
|
||||
* @param string $className the Classname of the class the test is being generated for.
|
||||
* @return string filename the test should be created on.
|
||||
*/
|
||||
public function testCaseFileName($type, $className) {
|
||||
$path = $this->getPath() . 'Case' . DS;
|
||||
$type = Inflector::camelize($type);
|
||||
if (isset($this->classTypes[$type])) {
|
||||
$path .= $this->classTypes[$type] . DS;
|
||||
}
|
||||
$className = $this->getRealClassName($type, $className);
|
||||
return str_replace('/', DS, $path) . Inflector::camelize($className) . 'Test.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser() {
|
||||
$parser = parent::getOptionParser();
|
||||
|
||||
$parser->description(
|
||||
__d('cake_console', 'Bake test case skeletons for classes.')
|
||||
)->addArgument('type', array(
|
||||
'help' => __d('cake_console', 'Type of class to bake, can be any of the following: controller, model, helper, component or behavior.'),
|
||||
'choices' => array(
|
||||
'Controller', 'controller',
|
||||
'Model', 'model',
|
||||
'Helper', 'helper',
|
||||
'Component', 'component',
|
||||
'Behavior', 'behavior'
|
||||
)
|
||||
))->addArgument('name', array(
|
||||
'help' => __d('cake_console', 'An existing class to bake tests for.')
|
||||
))->addOption('theme', array(
|
||||
'short' => 't',
|
||||
'help' => __d('cake_console', 'Theme to use when baking code.')
|
||||
))->addOption('plugin', array(
|
||||
'short' => 'p',
|
||||
'help' => __d('cake_console', 'CamelCased name of the plugin to bake tests for.')
|
||||
))->addOption('force', array(
|
||||
'short' => 'f',
|
||||
'help' => __d('cake_console', 'Force overwriting existing files without prompting.')
|
||||
))->epilog(
|
||||
__d('cake_console', 'Omitting all arguments and options will enter into an interactive mode.')
|
||||
);
|
||||
|
||||
return $parser;
|
||||
}
|
||||
|
||||
}
|
||||
477
lib/Cake/Console/Command/Task/ViewTask.php
Normal file
477
lib/Cake/Console/Command/Task/ViewTask.php
Normal file
|
|
@ -0,0 +1,477 @@
|
|||
<?php
|
||||
/**
|
||||
* The View Tasks handles creating and updating view files.
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since CakePHP(tm) v 1.2
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('AppShell', 'Console/Command');
|
||||
App::uses('Controller', 'Controller');
|
||||
App::uses('BakeTask', 'Console/Command/Task');
|
||||
|
||||
/**
|
||||
* Task class for creating and updating view files.
|
||||
*
|
||||
* @package Cake.Console.Command.Task
|
||||
*/
|
||||
class ViewTask extends BakeTask {
|
||||
|
||||
/**
|
||||
* Tasks to be loaded by this Task
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $tasks = array('Project', 'Controller', 'DbConfig', 'Template');
|
||||
|
||||
/**
|
||||
* path to View directory
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $path = null;
|
||||
|
||||
/**
|
||||
* Name of the controller being used
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $controllerName = null;
|
||||
|
||||
/**
|
||||
* The template file to use
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $template = null;
|
||||
|
||||
/**
|
||||
* Actions to use for scaffolding
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $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
|
||||
*/
|
||||
public $noTemplateActions = array('delete');
|
||||
|
||||
/**
|
||||
* Override initialize
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function initialize() {
|
||||
$this->path = current(App::path('View'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Execution method always used for tasks
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function execute() {
|
||||
parent::execute();
|
||||
if (empty($this->args)) {
|
||||
$this->_interactive();
|
||||
}
|
||||
if (empty($this->args[0])) {
|
||||
return;
|
||||
}
|
||||
if (!isset($this->connection)) {
|
||||
$this->connection = 'default';
|
||||
}
|
||||
$action = null;
|
||||
$this->controllerName = $this->_controllerName($this->args[0]);
|
||||
|
||||
$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
|
||||
*/
|
||||
protected function _methodsToBake() {
|
||||
$methods = array_diff(
|
||||
array_map('strtolower', get_class_methods($this->controllerName . 'Controller')),
|
||||
array_map('strtolower', get_class_methods('AppController'))
|
||||
);
|
||||
$scaffoldActions = false;
|
||||
if (empty($methods)) {
|
||||
$scaffoldActions = true;
|
||||
$methods = $this->scaffoldActions;
|
||||
}
|
||||
$adminRoute = $this->Project->getPrefix();
|
||||
foreach ($methods as $i => $method) {
|
||||
if ($adminRoute && !empty($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
|
||||
*/
|
||||
public function all() {
|
||||
$this->Controller->interactive = false;
|
||||
$tables = $this->Controller->listAll($this->connection, false);
|
||||
|
||||
$actions = null;
|
||||
if (isset($this->args[1])) {
|
||||
$actions = array($this->args[1]);
|
||||
}
|
||||
$this->interactive = false;
|
||||
foreach ($tables as $table) {
|
||||
$model = $this->_modelName($table);
|
||||
$this->controllerName = $this->_controllerName($model);
|
||||
App::uses($model, 'Model');
|
||||
if (class_exists($model)) {
|
||||
$vars = $this->_loadController();
|
||||
if (!$actions) {
|
||||
$actions = $this->_methodsToBake();
|
||||
}
|
||||
$this->bakeActions($actions, $vars);
|
||||
$actions = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles interactive baking
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _interactive() {
|
||||
$this->hr();
|
||||
$this->out(sprintf("Bake View\nPath: %s", $this->getPath()));
|
||||
$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();
|
||||
|
||||
$prompt = __d('cake_console', "Would you like bake to build your views interactively?\nWarning: Choosing no will overwrite %s views if it exist.", $this->controllerName);
|
||||
$interactive = $this->in($prompt, array('y', 'n'), 'n');
|
||||
|
||||
if (strtolower($interactive) === 'n') {
|
||||
$this->interactive = false;
|
||||
}
|
||||
|
||||
$prompt = __d('cake_console', "Would you like to create some CRUD views\n(index, add, view, edit) for this controller?\nNOTE: Before doing so, you'll need to create your controller\nand model classes (including associated models).");
|
||||
$wannaDoScaffold = $this->in($prompt, array('y', 'n'), 'y');
|
||||
|
||||
$wannaDoAdmin = $this->in(__d('cake_console', "Would you like to create the views for admin routing?"), 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(__d('cake_console', "View Scaffolding Complete.\n"));
|
||||
} 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
|
||||
*/
|
||||
protected function _loadController() {
|
||||
if (!$this->controllerName) {
|
||||
$this->err(__d('cake_console', 'Controller not found'));
|
||||
}
|
||||
|
||||
$plugin = null;
|
||||
if ($this->plugin) {
|
||||
$plugin = $this->plugin . '.';
|
||||
}
|
||||
|
||||
$controllerClassName = $this->controllerName . 'Controller';
|
||||
App::uses($controllerClassName, $plugin . 'Controller');
|
||||
if (!class_exists($controllerClassName)) {
|
||||
$file = $controllerClassName . '.php';
|
||||
$this->err(__d('cake_console', "The file '%s' could not be found.\nIn order to bake a view, you'll need to first create the controller.", $file));
|
||||
return $this->_stop();
|
||||
}
|
||||
$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.
|
||||
* @param array $vars The template variables.
|
||||
* @return void
|
||||
*/
|
||||
public function bakeActions($actions, $vars) {
|
||||
foreach ($actions as $action) {
|
||||
$content = $this->getContent($action, $vars);
|
||||
$this->bake($action, $content);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* handle creation of baking a custom action view file
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function customAction() {
|
||||
$action = '';
|
||||
while (!$action) {
|
||||
$action = $this->in(__d('cake_console', 'Action Name? (use lowercase_underscored function name)'));
|
||||
if (!$action) {
|
||||
$this->out(__d('cake_console', 'The action name you supplied was empty. Please try again.'));
|
||||
}
|
||||
}
|
||||
$this->out();
|
||||
$this->hr();
|
||||
$this->out(__d('cake_console', 'The following view will be created:'));
|
||||
$this->hr();
|
||||
$this->out(__d('cake_console', 'Controller Name: %s', $this->controllerName));
|
||||
$this->out(__d('cake_console', 'Action Name: %s', $action));
|
||||
$this->out(__d('cake_console', 'Path: %s', $this->getPath() . $this->controllerName . DS . Inflector::underscore($action) . ".ctp"));
|
||||
$this->hr();
|
||||
$looksGood = $this->in(__d('cake_console', 'Look okay?'), array('y', 'n'), 'y');
|
||||
if (strtolower($looksGood) === 'y') {
|
||||
$this->bake($action, ' ');
|
||||
return $this->_stop();
|
||||
}
|
||||
$this->out(__d('cake_console', 'Bake Aborted.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles and writes bakes the view file.
|
||||
*
|
||||
* @param string $action Action to bake
|
||||
* @param string $content Content to write
|
||||
* @return bool Success
|
||||
*/
|
||||
public function bake($action, $content = '') {
|
||||
if ($content === true) {
|
||||
$content = $this->getContent($action);
|
||||
}
|
||||
if (empty($content)) {
|
||||
return false;
|
||||
}
|
||||
$this->out("\n" . __d('cake_console', 'Baking `%s` view file...', $action), 1, Shell::QUIET);
|
||||
$path = $this->getPath();
|
||||
$filename = $path . $this->controllerName . 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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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;
|
||||
}
|
||||
$themePath = $this->Template->getThemePath();
|
||||
if (file_exists($themePath . 'views' . DS . $action . '.ctp')) {
|
||||
return $action;
|
||||
}
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser() {
|
||||
$parser = parent::getOptionParser();
|
||||
|
||||
$parser->description(
|
||||
__d('cake_console', 'Bake views for a controller, using built-in or custom templates.')
|
||||
)->addArgument('controller', array(
|
||||
'help' => __d('cake_console', 'Name of the controller views to bake. Can be Plugin.name as a shortcut for plugin baking.')
|
||||
))->addArgument('action', array(
|
||||
'help' => __d('cake_console', "Will bake a single action's file. core templates are (index, add, edit, view)")
|
||||
))->addArgument('alias', array(
|
||||
'help' => __d('cake_console', 'Will bake the template in <action> but create the filename after <alias>.')
|
||||
))->addOption('plugin', array(
|
||||
'short' => 'p',
|
||||
'help' => __d('cake_console', 'Plugin to bake the view into.')
|
||||
))->addOption('admin', array(
|
||||
'help' => __d('cake_console', 'Set to only bake views for a prefix in Routing.prefixes'),
|
||||
'boolean' => true
|
||||
))->addOption('theme', array(
|
||||
'short' => 't',
|
||||
'help' => __d('cake_console', 'Theme to use when baking code.')
|
||||
))->addOption('connection', array(
|
||||
'short' => 'c',
|
||||
'help' => __d('cake_console', 'The connection the connected model is on.')
|
||||
))->addOption('force', array(
|
||||
'short' => 'f',
|
||||
'help' => __d('cake_console', 'Force overwriting existing files without prompting.')
|
||||
))->addSubcommand('all', array(
|
||||
'help' => __d('cake_console', 'Bake all CRUD action views for all controllers. Requires models and controllers to exist.')
|
||||
))->epilog(
|
||||
__d('cake_console', 'Omitting all arguments and options will enter into an interactive mode.')
|
||||
);
|
||||
|
||||
return $parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns associations for controllers models.
|
||||
*
|
||||
* @param Model $model The Model instance.
|
||||
* @return array associations
|
||||
*/
|
||||
protected function _associations(Model $model) {
|
||||
$keys = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');
|
||||
$associations = array();
|
||||
|
||||
foreach ($keys as $type) {
|
||||
foreach ($model->{$type} as $assocKey => $assocData) {
|
||||
list(, $modelClass) = pluginSplit($assocData['className']);
|
||||
$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($modelClass));
|
||||
$associations[$type][$assocKey]['fields'] = array_keys($model->{$assocKey}->schema(true));
|
||||
}
|
||||
}
|
||||
return $associations;
|
||||
}
|
||||
|
||||
}
|
||||
432
lib/Cake/Console/Command/TestShell.php
Normal file
432
lib/Cake/Console/Command/TestShell.php
Normal file
|
|
@ -0,0 +1,432 @@
|
|||
<?php
|
||||
/**
|
||||
* Test Shell
|
||||
*
|
||||
* This Shell allows the running of test suites via the cake command line
|
||||
*
|
||||
* CakePHP(tm) Tests <http://book.cakephp.org/2.0/en/development/testing.html>
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://book.cakephp.org/2.0/en/development/testing.html
|
||||
* @since CakePHP(tm) v 1.2.0.4433
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('Shell', 'Console');
|
||||
App::uses('CakeTestSuiteDispatcher', 'TestSuite');
|
||||
App::uses('CakeTestSuiteCommand', 'TestSuite');
|
||||
App::uses('CakeTestLoader', 'TestSuite');
|
||||
|
||||
/**
|
||||
* Provides a CakePHP wrapper around PHPUnit.
|
||||
* Adds in CakePHP's fixtures and gives access to plugin, app and core test cases
|
||||
*
|
||||
* @package Cake.Console.Command
|
||||
*/
|
||||
class TestShell extends Shell {
|
||||
|
||||
/**
|
||||
* Dispatcher object for the run.
|
||||
*
|
||||
* @var CakeTestDispatcher
|
||||
*/
|
||||
protected $_dispatcher = null;
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser() {
|
||||
$parser = new ConsoleOptionParser($this->name);
|
||||
|
||||
$parser->description(
|
||||
__d('cake_console', 'The CakePHP Testsuite allows you to run test cases from the command line')
|
||||
)->addArgument('category', array(
|
||||
'help' => __d('cake_console', 'The category for the test, or test file, to test.'),
|
||||
'required' => false
|
||||
))->addArgument('file', array(
|
||||
'help' => __d('cake_console', 'The path to the file, or test file, to test.'),
|
||||
'required' => false
|
||||
))->addOption('log-junit', array(
|
||||
'help' => __d('cake_console', '<file> Log test execution in JUnit XML format to file.'),
|
||||
'default' => false
|
||||
))->addOption('log-json', array(
|
||||
'help' => __d('cake_console', '<file> Log test execution in JSON format to file.'),
|
||||
'default' => false
|
||||
))->addOption('log-tap', array(
|
||||
'help' => __d('cake_console', '<file> Log test execution in TAP format to file.'),
|
||||
'default' => false
|
||||
))->addOption('log-dbus', array(
|
||||
'help' => __d('cake_console', 'Log test execution to DBUS.'),
|
||||
'default' => false
|
||||
))->addOption('coverage-html', array(
|
||||
'help' => __d('cake_console', '<dir> Generate code coverage report in HTML format.'),
|
||||
'default' => false
|
||||
))->addOption('coverage-clover', array(
|
||||
'help' => __d('cake_console', '<file> Write code coverage data in Clover XML format.'),
|
||||
'default' => false
|
||||
))->addOption('testdox-html', array(
|
||||
'help' => __d('cake_console', '<file> Write agile documentation in HTML format to file.'),
|
||||
'default' => false
|
||||
))->addOption('testdox-text', array(
|
||||
'help' => __d('cake_console', '<file> Write agile documentation in Text format to file.'),
|
||||
'default' => false
|
||||
))->addOption('filter', array(
|
||||
'help' => __d('cake_console', '<pattern> Filter which tests to run.'),
|
||||
'default' => false
|
||||
))->addOption('group', array(
|
||||
'help' => __d('cake_console', '<name> Only runs tests from the specified group(s).'),
|
||||
'default' => false
|
||||
))->addOption('exclude-group', array(
|
||||
'help' => __d('cake_console', '<name> Exclude tests from the specified group(s).'),
|
||||
'default' => false
|
||||
))->addOption('list-groups', array(
|
||||
'help' => __d('cake_console', 'List available test groups.'),
|
||||
'boolean' => true
|
||||
))->addOption('loader', array(
|
||||
'help' => __d('cake_console', 'TestSuiteLoader implementation to use.'),
|
||||
'default' => false
|
||||
))->addOption('repeat', array(
|
||||
'help' => __d('cake_console', '<times> Runs the test(s) repeatedly.'),
|
||||
'default' => false
|
||||
))->addOption('tap', array(
|
||||
'help' => __d('cake_console', 'Report test execution progress in TAP format.'),
|
||||
'boolean' => true
|
||||
))->addOption('testdox', array(
|
||||
'help' => __d('cake_console', 'Report test execution progress in TestDox format.'),
|
||||
'default' => false,
|
||||
'boolean' => true
|
||||
))->addOption('no-colors', array(
|
||||
'help' => __d('cake_console', 'Do not use colors in output.'),
|
||||
'boolean' => true
|
||||
))->addOption('stderr', array(
|
||||
'help' => __d('cake_console', 'Write to STDERR instead of STDOUT.'),
|
||||
'boolean' => true
|
||||
))->addOption('stop-on-error', array(
|
||||
'help' => __d('cake_console', 'Stop execution upon first error or failure.'),
|
||||
'boolean' => true
|
||||
))->addOption('stop-on-failure', array(
|
||||
'help' => __d('cake_console', 'Stop execution upon first failure.'),
|
||||
'boolean' => true
|
||||
))->addOption('stop-on-skipped', array(
|
||||
'help' => __d('cake_console', 'Stop execution upon first skipped test.'),
|
||||
'boolean' => true
|
||||
))->addOption('stop-on-incomplete', array(
|
||||
'help' => __d('cake_console', 'Stop execution upon first incomplete test.'),
|
||||
'boolean' => true
|
||||
))->addOption('strict', array(
|
||||
'help' => __d('cake_console', 'Mark a test as incomplete if no assertions are made.'),
|
||||
'boolean' => true
|
||||
))->addOption('wait', array(
|
||||
'help' => __d('cake_console', 'Waits for a keystroke after each test.'),
|
||||
'boolean' => true
|
||||
))->addOption('process-isolation', array(
|
||||
'help' => __d('cake_console', 'Run each test in a separate PHP process.'),
|
||||
'boolean' => true
|
||||
))->addOption('no-globals-backup', array(
|
||||
'help' => __d('cake_console', 'Do not backup and restore $GLOBALS for each test.'),
|
||||
'boolean' => true
|
||||
))->addOption('static-backup', array(
|
||||
'help' => __d('cake_console', 'Backup and restore static attributes for each test.'),
|
||||
'boolean' => true
|
||||
))->addOption('syntax-check', array(
|
||||
'help' => __d('cake_console', 'Try to check source files for syntax errors.'),
|
||||
'boolean' => true
|
||||
))->addOption('bootstrap', array(
|
||||
'help' => __d('cake_console', '<file> A "bootstrap" PHP file that is run before the tests.'),
|
||||
'default' => false
|
||||
))->addOption('configuration', array(
|
||||
'help' => __d('cake_console', '<file> Read configuration from XML file.'),
|
||||
'default' => false
|
||||
))->addOption('no-configuration', array(
|
||||
'help' => __d('cake_console', 'Ignore default configuration file (phpunit.xml).'),
|
||||
'boolean' => true
|
||||
))->addOption('include-path', array(
|
||||
'help' => __d('cake_console', '<path(s)> Prepend PHP include_path with given path(s).'),
|
||||
'default' => false
|
||||
))->addOption('directive', array(
|
||||
'help' => __d('cake_console', 'key[=value] Sets a php.ini value.'),
|
||||
'default' => false
|
||||
))->addOption('fixture', array(
|
||||
'help' => __d('cake_console', 'Choose a custom fixture manager.')
|
||||
))->addOption('debug', array(
|
||||
'help' => __d('cake_console', 'More verbose output.')
|
||||
));
|
||||
|
||||
return $parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization method installs PHPUnit and loads all plugins
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function initialize() {
|
||||
$this->_dispatcher = new CakeTestSuiteDispatcher();
|
||||
$success = $this->_dispatcher->loadTestFramework();
|
||||
if (!$success) {
|
||||
throw new Exception(__d('cake_dev', 'Please install PHPUnit framework v3.7 <info>(http://www.phpunit.de)</info>'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the CLI options into an array CakeTestDispatcher can use.
|
||||
*
|
||||
* @return array Array of params for CakeTestDispatcher
|
||||
*/
|
||||
protected function _parseArgs() {
|
||||
if (empty($this->args)) {
|
||||
return;
|
||||
}
|
||||
$params = array(
|
||||
'core' => false,
|
||||
'app' => false,
|
||||
'plugin' => null,
|
||||
'output' => 'text',
|
||||
);
|
||||
|
||||
if (strpos($this->args[0], '.php')) {
|
||||
$category = $this->_mapFileToCategory($this->args[0]);
|
||||
$params['case'] = $this->_mapFileToCase($this->args[0], $category);
|
||||
} else {
|
||||
$category = $this->args[0];
|
||||
if (isset($this->args[1])) {
|
||||
$params['case'] = $this->args[1];
|
||||
}
|
||||
}
|
||||
|
||||
if ($category === 'core') {
|
||||
$params['core'] = true;
|
||||
} elseif ($category === 'app') {
|
||||
$params['app'] = true;
|
||||
} else {
|
||||
$params['plugin'] = $category;
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the options passed to the shell as options for the PHPUnit cli runner
|
||||
*
|
||||
* @return array Array of params for CakeTestDispatcher
|
||||
*/
|
||||
protected function _runnerOptions() {
|
||||
$options = array();
|
||||
$params = $this->params;
|
||||
unset($params['help']);
|
||||
|
||||
if (!empty($params['no-colors'])) {
|
||||
unset($params['no-colors'], $params['colors']);
|
||||
} else {
|
||||
$params['colors'] = true;
|
||||
}
|
||||
|
||||
foreach ($params as $param => $value) {
|
||||
if ($value === false) {
|
||||
continue;
|
||||
}
|
||||
$options[] = '--' . $param;
|
||||
if (is_string($value)) {
|
||||
$options[] = $value;
|
||||
}
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entry point to this shell
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function main() {
|
||||
$this->out(__d('cake_console', 'CakePHP Test Shell'));
|
||||
$this->hr();
|
||||
|
||||
$args = $this->_parseArgs();
|
||||
|
||||
if (empty($args['case'])) {
|
||||
return $this->available();
|
||||
}
|
||||
|
||||
$this->_run($args, $this->_runnerOptions());
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the test case from $runnerArgs
|
||||
*
|
||||
* @param array $runnerArgs list of arguments as obtained from _parseArgs()
|
||||
* @param array $options list of options as constructed by _runnerOptions()
|
||||
* @return void
|
||||
*/
|
||||
protected function _run($runnerArgs, $options = array()) {
|
||||
restore_error_handler();
|
||||
restore_error_handler();
|
||||
|
||||
$testCli = new CakeTestSuiteCommand('CakeTestLoader', $runnerArgs);
|
||||
$testCli->run($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a list of available test cases and gives the option to run one of them
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function available() {
|
||||
$params = $this->_parseArgs();
|
||||
$testCases = CakeTestLoader::generateTestList($params);
|
||||
$app = $params['app'];
|
||||
$plugin = $params['plugin'];
|
||||
|
||||
$title = "Core Test Cases:";
|
||||
$category = 'core';
|
||||
if ($app) {
|
||||
$title = "App Test Cases:";
|
||||
$category = 'app';
|
||||
} elseif ($plugin) {
|
||||
$title = Inflector::humanize($plugin) . " Test Cases:";
|
||||
$category = $plugin;
|
||||
}
|
||||
|
||||
if (empty($testCases)) {
|
||||
$this->out(__d('cake_console', "No test cases available \n\n"));
|
||||
return $this->out($this->OptionParser->help());
|
||||
}
|
||||
|
||||
$this->out($title);
|
||||
$i = 1;
|
||||
$cases = array();
|
||||
foreach ($testCases as $testCase) {
|
||||
$case = str_replace('Test.php', '', $testCase);
|
||||
$this->out("[$i] $case");
|
||||
$cases[$i] = $case;
|
||||
$i++;
|
||||
}
|
||||
|
||||
while ($choice = $this->in(__d('cake_console', 'What test case would you like to run?'), null, 'q')) {
|
||||
if (is_numeric($choice) && isset($cases[$choice])) {
|
||||
$this->args[0] = $category;
|
||||
$this->args[1] = $cases[$choice];
|
||||
$this->_run($this->_parseArgs(), $this->_runnerOptions());
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_string($choice) && in_array($choice, $cases)) {
|
||||
$this->args[0] = $category;
|
||||
$this->args[1] = $choice;
|
||||
$this->_run($this->_parseArgs(), $this->_runnerOptions());
|
||||
break;
|
||||
}
|
||||
|
||||
if ($choice === 'q') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the test case for the passed file. The file could itself be a test.
|
||||
*
|
||||
* @param string $file The file to map.
|
||||
* @param string $category The test file category.
|
||||
* @param bool $throwOnMissingFile Whether or not to throw an exception.
|
||||
* @return array array(type, case)
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function _mapFileToCase($file, $category, $throwOnMissingFile = true) {
|
||||
if (!$category || (substr($file, -4) !== '.php')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$_file = realpath($file);
|
||||
if ($_file) {
|
||||
$file = $_file;
|
||||
}
|
||||
|
||||
$testFile = $testCase = null;
|
||||
|
||||
if (preg_match('@Test[\\\/]@', $file)) {
|
||||
|
||||
if (substr($file, -8) === 'Test.php') {
|
||||
|
||||
$testCase = substr($file, 0, -8);
|
||||
$testCase = str_replace(DS, '/', $testCase);
|
||||
|
||||
if ($testCase = preg_replace('@.*Test\/Case\/@', '', $testCase)) {
|
||||
|
||||
if ($category === 'core') {
|
||||
$testCase = str_replace('lib/Cake', '', $testCase);
|
||||
}
|
||||
|
||||
return $testCase;
|
||||
}
|
||||
|
||||
throw new Exception(__d('cake_dev', 'Test case %s cannot be run via this shell', $testFile));
|
||||
}
|
||||
}
|
||||
|
||||
$file = substr($file, 0, -4);
|
||||
if ($category === 'core') {
|
||||
|
||||
$testCase = str_replace(DS, '/', $file);
|
||||
$testCase = preg_replace('@.*lib/Cake/@', '', $file);
|
||||
$testCase[0] = strtoupper($testCase[0]);
|
||||
$testFile = CAKE . 'Test/Case/' . $testCase . 'Test.php';
|
||||
|
||||
if (!file_exists($testFile) && $throwOnMissingFile) {
|
||||
throw new Exception(__d('cake_dev', 'Test case %s not found', $testFile));
|
||||
}
|
||||
|
||||
return $testCase;
|
||||
}
|
||||
|
||||
if ($category === 'app') {
|
||||
$testFile = str_replace(APP, APP . 'Test/Case/', $file) . 'Test.php';
|
||||
} else {
|
||||
$testFile = preg_replace(
|
||||
"@((?:plugins|Plugin)[\\/]{$category}[\\/])(.*)$@",
|
||||
'\1Test/Case/\2Test.php',
|
||||
$file
|
||||
);
|
||||
}
|
||||
|
||||
if (!file_exists($testFile) && $throwOnMissingFile) {
|
||||
throw new Exception(__d('cake_dev', 'Test case %s not found', $testFile));
|
||||
}
|
||||
|
||||
$testCase = substr($testFile, 0, -8);
|
||||
$testCase = str_replace(DS, '/', $testCase);
|
||||
$testCase = preg_replace('@.*Test/Case/@', '', $testCase);
|
||||
|
||||
return $testCase;
|
||||
}
|
||||
|
||||
/**
|
||||
* For the given file, what category of test is it? returns app, core or the name of the plugin
|
||||
*
|
||||
* @param string $file The file to map.
|
||||
* @return string
|
||||
*/
|
||||
protected function _mapFileToCategory($file) {
|
||||
$_file = realpath($file);
|
||||
if ($_file) {
|
||||
$file = $_file;
|
||||
}
|
||||
|
||||
$file = str_replace(DS, '/', $file);
|
||||
if (strpos($file, 'lib/Cake/') !== false) {
|
||||
return 'core';
|
||||
} elseif (preg_match('@(?:plugins|Plugin)/([^/]*)@', $file, $match)) {
|
||||
return $match[1];
|
||||
}
|
||||
return 'app';
|
||||
}
|
||||
|
||||
}
|
||||
100
lib/Cake/Console/Command/TestsuiteShell.php
Normal file
100
lib/Cake/Console/Command/TestsuiteShell.php
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
/**
|
||||
* Test Suite Shell
|
||||
*
|
||||
* This is a bc wrapper for the newer Test shell
|
||||
*
|
||||
* CakePHP(tm) Tests <http://book.cakephp.org/2.0/en/development/testing.html>
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://book.cakephp.org/2.0/en/development/testing.html
|
||||
* @since CakePHP(tm) v 1.2.0.4433
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('TestShell', 'Console/Command');
|
||||
App::uses('AppShell', 'Console/Command');
|
||||
App::uses('CakeTestSuiteDispatcher', 'TestSuite');
|
||||
App::uses('CakeTestSuiteCommand', 'TestSuite');
|
||||
App::uses('CakeTestLoader', 'TestSuite');
|
||||
|
||||
/**
|
||||
* Provides a CakePHP wrapper around PHPUnit.
|
||||
* Adds in CakePHP's fixtures and gives access to plugin, app and core test cases
|
||||
*
|
||||
* @package Cake.Console.Command
|
||||
*/
|
||||
class TestsuiteShell extends TestShell {
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser() {
|
||||
$parser = parent::getOptionParser();
|
||||
|
||||
$parser->description(array(
|
||||
__d('cake_console', 'The CakePHP Testsuite allows you to run test cases from the command line'),
|
||||
__d('cake_console', "<warning>This shell is for backwards-compatibility only</warning>\nuse the test shell instead")
|
||||
));
|
||||
|
||||
return $parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the CLI options into an array CakeTestDispatcher can use.
|
||||
*
|
||||
* @return array Array of params for CakeTestDispatcher
|
||||
*/
|
||||
protected function _parseArgs() {
|
||||
if (empty($this->args)) {
|
||||
return;
|
||||
}
|
||||
$params = array(
|
||||
'core' => false,
|
||||
'app' => false,
|
||||
'plugin' => null,
|
||||
'output' => 'text',
|
||||
);
|
||||
|
||||
$category = $this->args[0];
|
||||
|
||||
if ($category === 'core') {
|
||||
$params['core'] = true;
|
||||
} elseif ($category === 'app') {
|
||||
$params['app'] = true;
|
||||
} elseif ($category !== 'core') {
|
||||
$params['plugin'] = $category;
|
||||
}
|
||||
|
||||
if (isset($this->args[1])) {
|
||||
$params['case'] = $this->args[1];
|
||||
}
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entry point to this shell
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function main() {
|
||||
$this->out(__d('cake_console', 'CakePHP Test Shell'));
|
||||
$this->hr();
|
||||
|
||||
$args = $this->_parseArgs();
|
||||
|
||||
if (empty($args['case'])) {
|
||||
return $this->available();
|
||||
}
|
||||
|
||||
$this->_run($args, $this->_runnerOptions());
|
||||
}
|
||||
|
||||
}
|
||||
888
lib/Cake/Console/Command/UpgradeShell.php
Normal file
888
lib/Cake/Console/Command/UpgradeShell.php
Normal file
|
|
@ -0,0 +1,888 @@
|
|||
<?php
|
||||
/**
|
||||
* Upgrade Shell
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @package Cake.Console.Command
|
||||
* @since CakePHP(tm) v 2.0
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('AppShell', 'Console/Command');
|
||||
App::uses('Folder', 'Utility');
|
||||
App::uses('CakePlugin', 'Core');
|
||||
|
||||
/**
|
||||
* A shell class to help developers upgrade applications to CakePHP 2.0
|
||||
*
|
||||
* @package Cake.Console.Command
|
||||
*/
|
||||
class UpgradeShell extends AppShell {
|
||||
|
||||
/**
|
||||
* Files
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_files = array();
|
||||
|
||||
/**
|
||||
* Paths
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_paths = array();
|
||||
|
||||
/**
|
||||
* Map
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_map = array(
|
||||
'Controller' => 'Controller',
|
||||
'Component' => 'Controller/Component',
|
||||
'Model' => 'Model',
|
||||
'Behavior' => 'Model/Behavior',
|
||||
'Datasource' => 'Model/Datasource',
|
||||
'Dbo' => 'Model/Datasource/Database',
|
||||
'View' => 'View',
|
||||
'Helper' => 'View/Helper',
|
||||
'Shell' => 'Console/Command',
|
||||
'Task' => 'Console/Command/Task',
|
||||
'Case' => 'Test/Case',
|
||||
'Fixture' => 'Test/Fixture',
|
||||
'Error' => 'Lib/Error',
|
||||
);
|
||||
|
||||
/**
|
||||
* Shell startup, prints info message about dry run.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function startup() {
|
||||
parent::startup();
|
||||
if ($this->params['dry-run']) {
|
||||
$this->out(__d('cake_console', '<warning>Dry-run mode enabled!</warning>'), 1, Shell::QUIET);
|
||||
}
|
||||
if ($this->params['git'] && !is_dir('.git')) {
|
||||
$this->out(__d('cake_console', '<warning>No git repository detected!</warning>'), 1, Shell::QUIET);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run all upgrade steps one at a time
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function all() {
|
||||
foreach ($this->OptionParser->subcommands() as $command) {
|
||||
$name = $command->name();
|
||||
if ($name === 'all') {
|
||||
continue;
|
||||
}
|
||||
$this->out(__d('cake_console', 'Running %s', $name));
|
||||
$this->$name();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update tests.
|
||||
*
|
||||
* - Update tests class names to FooTest rather than FooTestCase.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function tests() {
|
||||
$this->_paths = array(APP . 'tests' . DS);
|
||||
if (!empty($this->params['plugin'])) {
|
||||
$this->_paths = array(CakePlugin::path($this->params['plugin']) . 'tests' . DS);
|
||||
}
|
||||
$patterns = array(
|
||||
array(
|
||||
'*TestCase extends CakeTestCase to *Test extends CakeTestCase',
|
||||
'/([a-zA-Z]*Test)Case extends CakeTestCase/',
|
||||
'\1 extends CakeTestCase'
|
||||
),
|
||||
);
|
||||
|
||||
$this->_filesRegexpUpdate($patterns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move files and folders to their new homes
|
||||
*
|
||||
* Moves folders containing files which cannot necessarily be auto-detected (libs and templates)
|
||||
* and then looks for all php files except vendors, and moves them to where Cake 2.0 expects
|
||||
* to find them.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function locations() {
|
||||
$cwd = getcwd();
|
||||
|
||||
if (!empty($this->params['plugin'])) {
|
||||
chdir(CakePlugin::path($this->params['plugin']));
|
||||
}
|
||||
|
||||
if (is_dir('plugins')) {
|
||||
$Folder = new Folder('plugins');
|
||||
list($plugins) = $Folder->read();
|
||||
foreach ($plugins as $plugin) {
|
||||
chdir($cwd . DS . 'plugins' . DS . $plugin);
|
||||
$this->out(__d('cake_console', 'Upgrading locations for plugin %s', $plugin));
|
||||
$this->locations();
|
||||
}
|
||||
$this->_files = array();
|
||||
chdir($cwd);
|
||||
$this->out(__d('cake_console', 'Upgrading locations for app directory'));
|
||||
}
|
||||
$moves = array(
|
||||
'config' => 'Config',
|
||||
'Config' . DS . 'schema' => 'Config' . DS . 'Schema',
|
||||
'libs' => 'Lib',
|
||||
'tests' => 'Test',
|
||||
'views' => 'View',
|
||||
'models' => 'Model',
|
||||
'Model' . DS . 'behaviors' => 'Model' . DS . 'Behavior',
|
||||
'Model' . DS . 'datasources' => 'Model' . DS . 'Datasource',
|
||||
'Test' . DS . 'cases' => 'Test' . DS . 'Case',
|
||||
'Test' . DS . 'fixtures' => 'Test' . DS . 'Fixture',
|
||||
'vendors' . DS . 'shells' . DS . 'templates' => 'Console' . DS . 'Templates',
|
||||
);
|
||||
foreach ($moves as $old => $new) {
|
||||
if (is_dir($old)) {
|
||||
$this->out(__d('cake_console', 'Moving %s to %s', $old, $new));
|
||||
if (!$this->params['dry-run']) {
|
||||
if ($this->params['git']) {
|
||||
exec('git mv -f ' . escapeshellarg($old) . ' ' . escapeshellarg($old . '__'));
|
||||
exec('git mv -f ' . escapeshellarg($old . '__') . ' ' . escapeshellarg($new));
|
||||
} else {
|
||||
$Folder = new Folder($old);
|
||||
$Folder->move($new);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->_moveViewFiles();
|
||||
$this->_moveAppClasses();
|
||||
|
||||
$sourceDirs = array(
|
||||
'.' => array('recursive' => false),
|
||||
'Console',
|
||||
'controllers',
|
||||
'Controller',
|
||||
'Lib' => array('checkFolder' => false),
|
||||
'models',
|
||||
'Model',
|
||||
'tests',
|
||||
'Test' => array('regex' => '@class (\S*Test) extends CakeTestCase@'),
|
||||
'views',
|
||||
'View',
|
||||
'vendors/shells',
|
||||
);
|
||||
|
||||
$defaultOptions = array(
|
||||
'recursive' => true,
|
||||
'checkFolder' => true,
|
||||
'regex' => '@class (\S*) .*(\s|\v)*{@i'
|
||||
);
|
||||
foreach ($sourceDirs as $dir => $options) {
|
||||
if (is_numeric($dir)) {
|
||||
$dir = $options;
|
||||
$options = array();
|
||||
}
|
||||
$options += $defaultOptions;
|
||||
$this->_movePhpFiles($dir, $options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update helpers.
|
||||
*
|
||||
* - Converts helpers usage to new format.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function helpers() {
|
||||
$this->_paths = array_diff(App::path('views'), App::core('views'));
|
||||
|
||||
if (!empty($this->params['plugin'])) {
|
||||
$this->_paths = array(CakePlugin::path($this->params['plugin']) . 'views' . DS);
|
||||
}
|
||||
|
||||
$patterns = array();
|
||||
App::build(array(
|
||||
'View/Helper' => App::core('View/Helper'),
|
||||
), App::APPEND);
|
||||
$helpers = App::objects('helper');
|
||||
$plugins = App::objects('plugin');
|
||||
$pluginHelpers = array();
|
||||
foreach ($plugins as $plugin) {
|
||||
CakePlugin::load($plugin);
|
||||
$pluginHelpers = array_merge(
|
||||
$pluginHelpers,
|
||||
App::objects('helper', CakePlugin::path($plugin) . DS . 'views' . DS . 'helpers' . DS, false)
|
||||
);
|
||||
}
|
||||
$helpers = array_merge($pluginHelpers, $helpers);
|
||||
foreach ($helpers as $helper) {
|
||||
$helper = preg_replace('/Helper$/', '', $helper);
|
||||
$oldHelper = $helper;
|
||||
$oldHelper{0} = strtolower($oldHelper{0});
|
||||
$patterns[] = array(
|
||||
"\${$oldHelper} to \$this->{$helper}",
|
||||
"/\\\${$oldHelper}->/",
|
||||
"\\\$this->{$helper}->"
|
||||
);
|
||||
}
|
||||
|
||||
$this->_filesRegexpUpdate($patterns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update i18n.
|
||||
*
|
||||
* - Removes extra true param.
|
||||
* - Add the echo to __*() calls that didn't need them before.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function i18n() {
|
||||
$this->_paths = array(
|
||||
APP
|
||||
);
|
||||
if (!empty($this->params['plugin'])) {
|
||||
$this->_paths = array(CakePlugin::path($this->params['plugin']));
|
||||
}
|
||||
|
||||
$patterns = array(
|
||||
array(
|
||||
'<?php __*(*) to <?php echo __*(*)',
|
||||
'/<\?php\s*(__[a-z]*\(.*?\))/',
|
||||
'<?php echo \1'
|
||||
),
|
||||
array(
|
||||
'<?php __*(*, true) to <?php echo __*()',
|
||||
'/<\?php\s*(__[a-z]*\(.*?)(,\s*true)(\))/',
|
||||
'<?php echo \1\3'
|
||||
),
|
||||
array('__*(*, true) to __*(*)', '/(__[a-z]*\(.*?)(,\s*true)(\))/', '\1\3')
|
||||
);
|
||||
|
||||
$this->_filesRegexpUpdate($patterns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrade the removed basics functions.
|
||||
*
|
||||
* - a(*) -> array(*)
|
||||
* - e(*) -> echo *
|
||||
* - ife(*, *, *) -> !empty(*) ? * : *
|
||||
* - a(*) -> array(*)
|
||||
* - r(*, *, *) -> str_replace(*, *, *)
|
||||
* - up(*) -> strtoupper(*)
|
||||
* - low(*, *, *) -> strtolower(*)
|
||||
* - getMicrotime() -> microtime(true)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function basics() {
|
||||
$this->_paths = array(
|
||||
APP
|
||||
);
|
||||
if (!empty($this->params['plugin'])) {
|
||||
$this->_paths = array(CakePlugin::path($this->params['plugin']));
|
||||
}
|
||||
$patterns = array(
|
||||
array(
|
||||
'a(*) -> array(*)',
|
||||
'/\ba\((.*)\)/',
|
||||
'array(\1)'
|
||||
),
|
||||
array(
|
||||
'e(*) -> echo *',
|
||||
'/\be\((.*)\)/',
|
||||
'echo \1'
|
||||
),
|
||||
array(
|
||||
'ife(*, *, *) -> !empty(*) ? * : *',
|
||||
'/ife\((.*), (.*), (.*)\)/',
|
||||
'!empty(\1) ? \2 : \3'
|
||||
),
|
||||
array(
|
||||
'r(*, *, *) -> str_replace(*, *, *)',
|
||||
'/\br\(/',
|
||||
'str_replace('
|
||||
),
|
||||
array(
|
||||
'up(*) -> strtoupper(*)',
|
||||
'/\bup\(/',
|
||||
'strtoupper('
|
||||
),
|
||||
array(
|
||||
'low(*) -> strtolower(*)',
|
||||
'/\blow\(/',
|
||||
'strtolower('
|
||||
),
|
||||
array(
|
||||
'getMicrotime() -> microtime(true)',
|
||||
'/getMicrotime\(\)/',
|
||||
'microtime(true)'
|
||||
),
|
||||
);
|
||||
$this->_filesRegexpUpdate($patterns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the properties moved to CakeRequest.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function request() {
|
||||
$views = array_diff(App::path('views'), App::core('views'));
|
||||
$controllers = array_diff(App::path('controllers'), App::core('controllers'), array(APP));
|
||||
$components = array_diff(App::path('components'), App::core('components'));
|
||||
|
||||
$this->_paths = array_merge($views, $controllers, $components);
|
||||
|
||||
if (!empty($this->params['plugin'])) {
|
||||
$pluginPath = CakePlugin::path($this->params['plugin']);
|
||||
$this->_paths = array(
|
||||
$pluginPath . 'controllers' . DS,
|
||||
$pluginPath . 'controllers' . DS . 'components' . DS,
|
||||
$pluginPath . 'views' . DS,
|
||||
);
|
||||
}
|
||||
$patterns = array(
|
||||
array(
|
||||
'$this->data -> $this->request->data',
|
||||
'/(\$this->data\b(?!\())/',
|
||||
'$this->request->data'
|
||||
),
|
||||
array(
|
||||
'$this->params -> $this->request->params',
|
||||
'/(\$this->params\b(?!\())/',
|
||||
'$this->request->params'
|
||||
),
|
||||
array(
|
||||
'$this->webroot -> $this->request->webroot',
|
||||
'/(\$this->webroot\b(?!\())/',
|
||||
'$this->request->webroot'
|
||||
),
|
||||
array(
|
||||
'$this->base -> $this->request->base',
|
||||
'/(\$this->base\b(?!\())/',
|
||||
'$this->request->base'
|
||||
),
|
||||
array(
|
||||
'$this->here -> $this->request->here',
|
||||
'/(\$this->here\b(?!\())/',
|
||||
'$this->request->here'
|
||||
),
|
||||
array(
|
||||
'$this->action -> $this->request->action',
|
||||
'/(\$this->action\b(?!\())/',
|
||||
'$this->request->action'
|
||||
),
|
||||
array(
|
||||
'$this->request->onlyAllow() -> $this->request->allowMethod()',
|
||||
'/\$this->request->onlyAllow\(/',
|
||||
'$this->request->allowMethod('
|
||||
)
|
||||
);
|
||||
$this->_filesRegexpUpdate($patterns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Configure::read() calls with no params.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function configure() {
|
||||
$this->_paths = array(
|
||||
APP
|
||||
);
|
||||
if (!empty($this->params['plugin'])) {
|
||||
$this->_paths = array(CakePlugin::path($this->params['plugin']));
|
||||
}
|
||||
$patterns = array(
|
||||
array(
|
||||
"Configure::read() -> Configure::read('debug')",
|
||||
'/Configure::read\(\)/',
|
||||
'Configure::read(\'debug\')'
|
||||
),
|
||||
);
|
||||
$this->_filesRegexpUpdate($patterns);
|
||||
}
|
||||
|
||||
/**
|
||||
* constants
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function constants() {
|
||||
$this->_paths = array(
|
||||
APP
|
||||
);
|
||||
if (!empty($this->params['plugin'])) {
|
||||
$this->_paths = array(CakePlugin::path($this->params['plugin']));
|
||||
}
|
||||
$patterns = array(
|
||||
array(
|
||||
"LIBS -> CAKE",
|
||||
'/\bLIBS\b/',
|
||||
'CAKE'
|
||||
),
|
||||
array(
|
||||
"CONFIGS -> APP . 'Config' . DS",
|
||||
'/\bCONFIGS\b/',
|
||||
'APP . \'Config\' . DS'
|
||||
),
|
||||
array(
|
||||
"CONTROLLERS -> APP . 'Controller' . DS",
|
||||
'/\bCONTROLLERS\b/',
|
||||
'APP . \'Controller\' . DS'
|
||||
),
|
||||
array(
|
||||
"COMPONENTS -> APP . 'Controller' . DS . 'Component' . DS",
|
||||
'/\bCOMPONENTS\b/',
|
||||
'APP . \'Controller\' . DS . \'Component\''
|
||||
),
|
||||
array(
|
||||
"MODELS -> APP . 'Model' . DS",
|
||||
'/\bMODELS\b/',
|
||||
'APP . \'Model\' . DS'
|
||||
),
|
||||
array(
|
||||
"BEHAVIORS -> APP . 'Model' . DS . 'Behavior' . DS",
|
||||
'/\bBEHAVIORS\b/',
|
||||
'APP . \'Model\' . DS . \'Behavior\' . DS'
|
||||
),
|
||||
array(
|
||||
"VIEWS -> APP . 'View' . DS",
|
||||
'/\bVIEWS\b/',
|
||||
'APP . \'View\' . DS'
|
||||
),
|
||||
array(
|
||||
"HELPERS -> APP . 'View' . DS . 'Helper' . DS",
|
||||
'/\bHELPERS\b/',
|
||||
'APP . \'View\' . DS . \'Helper\' . DS'
|
||||
),
|
||||
array(
|
||||
"LAYOUTS -> APP . 'View' . DS . 'Layouts' . DS",
|
||||
'/\bLAYOUTS\b/',
|
||||
'APP . \'View\' . DS . \'Layouts\' . DS'
|
||||
),
|
||||
array(
|
||||
"ELEMENTS -> APP . 'View' . DS . 'Elements' . DS",
|
||||
'/\bELEMENTS\b/',
|
||||
'APP . \'View\' . DS . \'Elements\' . DS'
|
||||
),
|
||||
array(
|
||||
"CONSOLE_LIBS -> CAKE . 'Console' . DS",
|
||||
'/\bCONSOLE_LIBS\b/',
|
||||
'CAKE . \'Console\' . DS'
|
||||
),
|
||||
array(
|
||||
"CAKE_TESTS_LIB -> CAKE . 'TestSuite' . DS",
|
||||
'/\bCAKE_TESTS_LIB\b/',
|
||||
'CAKE . \'TestSuite\' . DS'
|
||||
),
|
||||
array(
|
||||
"CAKE_TESTS -> CAKE . 'Test' . DS",
|
||||
'/\bCAKE_TESTS\b/',
|
||||
'CAKE . \'Test\' . DS'
|
||||
)
|
||||
);
|
||||
$this->_filesRegexpUpdate($patterns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update controller redirects.
|
||||
*
|
||||
* - Make redirect statements return early.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function controller_redirects() {
|
||||
$this->_paths = App::Path('Controller');
|
||||
if (!empty($this->params['plugin'])) {
|
||||
$this->_paths = App::Path('Controller', $this->params['plugin']);
|
||||
}
|
||||
$patterns = array(
|
||||
array(
|
||||
'$this->redirect() to return $this->redirect()',
|
||||
'/\t\$this-\>redirect\(/',
|
||||
"\t" . 'return $this->redirect('
|
||||
),
|
||||
);
|
||||
|
||||
$this->_filesRegexpUpdate($patterns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update components.
|
||||
*
|
||||
* - Make components that extend Object to extend Component.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function components() {
|
||||
$this->_paths = App::Path('Controller/Component');
|
||||
if (!empty($this->params['plugin'])) {
|
||||
$this->_paths = App::Path('Controller/Component', $this->params['plugin']);
|
||||
}
|
||||
$patterns = array(
|
||||
array(
|
||||
'*Component extends Object to *Component extends Component',
|
||||
'/([a-zA-Z]*Component extends) Object/',
|
||||
'\1 Component'
|
||||
),
|
||||
);
|
||||
|
||||
$this->_filesRegexpUpdate($patterns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace cakeError with built-in exceptions.
|
||||
* NOTE: this ignores calls where you've passed your own secondary parameters to cakeError().
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function exceptions() {
|
||||
$controllers = array_diff(App::path('controllers'), App::core('controllers'), array(APP));
|
||||
$components = array_diff(App::path('components'), App::core('components'));
|
||||
|
||||
$this->_paths = array_merge($controllers, $components);
|
||||
|
||||
if (!empty($this->params['plugin'])) {
|
||||
$pluginPath = CakePlugin::path($this->params['plugin']);
|
||||
$this->_paths = array(
|
||||
$pluginPath . 'controllers' . DS,
|
||||
$pluginPath . 'controllers' . DS . 'components' . DS,
|
||||
);
|
||||
}
|
||||
$patterns = array(
|
||||
array(
|
||||
'$this->cakeError("error400") -> throw new BadRequestException()',
|
||||
'/(\$this->cakeError\(["\']error400["\']\));/',
|
||||
'throw new BadRequestException();'
|
||||
),
|
||||
array(
|
||||
'$this->cakeError("error404") -> throw new NotFoundException()',
|
||||
'/(\$this->cakeError\(["\']error404["\']\));/',
|
||||
'throw new NotFoundException();'
|
||||
),
|
||||
array(
|
||||
'$this->cakeError("error500") -> throw new InternalErrorException()',
|
||||
'/(\$this->cakeError\(["\']error500["\']\));/',
|
||||
'throw new InternalErrorException();'
|
||||
),
|
||||
);
|
||||
$this->_filesRegexpUpdate($patterns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move application views files to where they now should be
|
||||
*
|
||||
* Find all view files in the folder and determine where cake expects the file to be
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _moveViewFiles() {
|
||||
if (!is_dir('View')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$dirs = scandir('View');
|
||||
foreach ($dirs as $old) {
|
||||
if (!is_dir('View' . DS . $old) || $old === '.' || $old === '..') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$new = 'View' . DS . Inflector::camelize($old);
|
||||
$old = 'View' . DS . $old;
|
||||
if ($new === $old) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->out(__d('cake_console', 'Moving %s to %s', $old, $new));
|
||||
if (!$this->params['dry-run']) {
|
||||
if ($this->params['git']) {
|
||||
exec('git mv -f ' . escapeshellarg($old) . ' ' . escapeshellarg($old . '__'));
|
||||
exec('git mv -f ' . escapeshellarg($old . '__') . ' ' . escapeshellarg($new));
|
||||
} else {
|
||||
$Folder = new Folder($old);
|
||||
$Folder->move($new);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the AppController, and AppModel classes.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _moveAppClasses() {
|
||||
$files = array(
|
||||
APP . 'app_controller.php' => APP . 'Controller' . DS . 'AppController.php',
|
||||
APP . 'controllers' . DS . 'app_controller.php' => APP . 'Controller' . DS . 'AppController.php',
|
||||
APP . 'app_model.php' => APP . 'Model' . DS . 'AppModel.php',
|
||||
APP . 'models' . DS . 'app_model.php' => APP . 'Model' . DS . 'AppModel.php',
|
||||
);
|
||||
foreach ($files as $old => $new) {
|
||||
if (file_exists($old)) {
|
||||
$this->out(__d('cake_console', 'Moving %s to %s', $old, $new));
|
||||
|
||||
if ($this->params['dry-run']) {
|
||||
continue;
|
||||
}
|
||||
if ($this->params['git']) {
|
||||
exec('git mv -f ' . escapeshellarg($old) . ' ' . escapeshellarg($old . '__'));
|
||||
exec('git mv -f ' . escapeshellarg($old . '__') . ' ' . escapeshellarg($new));
|
||||
} else {
|
||||
rename($old, $new);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move application php files to where they now should be
|
||||
*
|
||||
* Find all php files in the folder (honoring recursive) and determine where CakePHP expects the file to be
|
||||
* If the file is not exactly where CakePHP expects it - move it.
|
||||
*
|
||||
* @param string $path The path to move files in.
|
||||
* @param array $options array(recursive, checkFolder)
|
||||
* @return void
|
||||
*/
|
||||
protected function _movePhpFiles($path, $options) {
|
||||
if (!is_dir($path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$paths = $this->_paths;
|
||||
|
||||
$this->_paths = array($path);
|
||||
$this->_files = array();
|
||||
if ($options['recursive']) {
|
||||
$this->_findFiles('php');
|
||||
} else {
|
||||
$this->_files = scandir($path);
|
||||
foreach ($this->_files as $i => $file) {
|
||||
if (strlen($file) < 5 || substr($file, -4) !== '.php') {
|
||||
unset($this->_files[$i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$cwd = getcwd();
|
||||
foreach ($this->_files as &$file) {
|
||||
$file = $cwd . DS . $file;
|
||||
|
||||
$contents = file_get_contents($file);
|
||||
preg_match($options['regex'], $contents, $match);
|
||||
if (!$match) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$class = $match[1];
|
||||
|
||||
if (substr($class, 0, 3) === 'Dbo') {
|
||||
$type = 'Dbo';
|
||||
} else {
|
||||
preg_match('@([A-Z][^A-Z]*)$@', $class, $match);
|
||||
if ($match) {
|
||||
$type = $match[1];
|
||||
} else {
|
||||
$type = 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
preg_match('@^.*[\\\/]plugins[\\\/](.*?)[\\\/]@', $file, $match);
|
||||
$base = $cwd . DS;
|
||||
$plugin = false;
|
||||
if ($match) {
|
||||
$base = $match[0];
|
||||
$plugin = $match[1];
|
||||
}
|
||||
|
||||
if ($options['checkFolder'] && !empty($this->_map[$type])) {
|
||||
$folder = str_replace('/', DS, $this->_map[$type]);
|
||||
$new = $base . $folder . DS . $class . '.php';
|
||||
} else {
|
||||
$new = dirname($file) . DS . $class . '.php';
|
||||
}
|
||||
|
||||
if ($file === $new) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$dir = dirname($new);
|
||||
if (!is_dir($dir)) {
|
||||
new Folder($dir, true);
|
||||
}
|
||||
|
||||
$this->out(__d('cake_console', 'Moving %s to %s', $file, $new), 1, Shell::VERBOSE);
|
||||
if (!$this->params['dry-run']) {
|
||||
if ($this->params['git']) {
|
||||
exec('git mv -f ' . escapeshellarg($file) . ' ' . escapeshellarg($file . '__'));
|
||||
exec('git mv -f ' . escapeshellarg($file . '__') . ' ' . escapeshellarg($new));
|
||||
} else {
|
||||
rename($file, $new);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->_paths = $paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates files based on regular expressions.
|
||||
*
|
||||
* @param array $patterns Array of search and replacement patterns.
|
||||
* @return void
|
||||
*/
|
||||
protected function _filesRegexpUpdate($patterns) {
|
||||
$this->_findFiles($this->params['ext']);
|
||||
foreach ($this->_files as $file) {
|
||||
$this->out(__d('cake_console', 'Updating %s...', $file), 1, Shell::VERBOSE);
|
||||
$this->_updateFile($file, $patterns);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the paths and finds files based on extension.
|
||||
*
|
||||
* @param string $extensions The extensions to include. Defaults to none.
|
||||
* @return void
|
||||
*/
|
||||
protected function _findFiles($extensions = '') {
|
||||
$this->_files = array();
|
||||
foreach ($this->_paths as $path) {
|
||||
if (!is_dir($path)) {
|
||||
continue;
|
||||
}
|
||||
$Iterator = new RegexIterator(
|
||||
new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path)),
|
||||
'/^.+\.(' . $extensions . ')$/i',
|
||||
RegexIterator::MATCH
|
||||
);
|
||||
foreach ($Iterator as $file) {
|
||||
if ($file->isFile()) {
|
||||
$this->_files[] = $file->getPathname();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a single file.
|
||||
*
|
||||
* @param string $file The file to update
|
||||
* @param array $patterns The replacement patterns to run.
|
||||
* @return void
|
||||
*/
|
||||
protected function _updateFile($file, $patterns) {
|
||||
$contents = file_get_contents($file);
|
||||
|
||||
foreach ($patterns as $pattern) {
|
||||
$this->out(__d('cake_console', ' * Updating %s', $pattern[0]), 1, Shell::VERBOSE);
|
||||
$contents = preg_replace($pattern[1], $pattern[2], $contents);
|
||||
}
|
||||
|
||||
$this->out(__d('cake_console', 'Done updating %s', $file), 1);
|
||||
if (!$this->params['dry-run']) {
|
||||
file_put_contents($file, $contents);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser() {
|
||||
$parser = parent::getOptionParser();
|
||||
|
||||
$subcommandParser = array(
|
||||
'options' => array(
|
||||
'plugin' => array(
|
||||
'short' => 'p',
|
||||
'help' => __d('cake_console', 'The plugin to update. Only the specified plugin will be updated.')
|
||||
),
|
||||
'ext' => array(
|
||||
'short' => 'e',
|
||||
'help' => __d('cake_console', 'The extension(s) to search. A pipe delimited list, or a preg_match compatible subpattern'),
|
||||
'default' => 'php|ctp|thtml|inc|tpl'
|
||||
),
|
||||
'git' => array(
|
||||
'short' => 'g',
|
||||
'help' => __d('cake_console', 'Use git command for moving files around.'),
|
||||
'boolean' => true
|
||||
),
|
||||
'dry-run' => array(
|
||||
'short' => 'd',
|
||||
'help' => __d('cake_console', 'Dry run the update, no files will actually be modified.'),
|
||||
'boolean' => true
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$parser->description(
|
||||
__d('cake_console', "A tool to help automate upgrading an application or plugin " .
|
||||
"from CakePHP 1.3 to 2.0. Be sure to have a backup of your application before " .
|
||||
"running these commands."
|
||||
))->addSubcommand('all', array(
|
||||
'help' => __d('cake_console', 'Run all upgrade commands.'),
|
||||
'parser' => $subcommandParser
|
||||
))->addSubcommand('tests', array(
|
||||
'help' => __d('cake_console', 'Update tests class names to FooTest rather than FooTestCase.'),
|
||||
'parser' => $subcommandParser
|
||||
))->addSubcommand('locations', array(
|
||||
'help' => __d('cake_console', 'Move files and folders to their new homes.'),
|
||||
'parser' => $subcommandParser
|
||||
))->addSubcommand('i18n', array(
|
||||
'help' => __d('cake_console', 'Update the i18n translation method calls.'),
|
||||
'parser' => $subcommandParser
|
||||
))->addSubcommand('helpers', array(
|
||||
'help' => __d('cake_console', 'Update calls to helpers.'),
|
||||
'parser' => $subcommandParser
|
||||
))->addSubcommand('basics', array(
|
||||
'help' => __d('cake_console', 'Update removed basics functions to PHP native functions.'),
|
||||
'parser' => $subcommandParser
|
||||
))->addSubcommand('request', array(
|
||||
'help' => __d('cake_console', 'Update removed request access, and replace with $this->request.'),
|
||||
'parser' => $subcommandParser
|
||||
))->addSubcommand('configure', array(
|
||||
'help' => __d('cake_console', "Update Configure::read() to Configure::read('debug')"),
|
||||
'parser' => $subcommandParser
|
||||
))->addSubcommand('constants', array(
|
||||
'help' => __d('cake_console', "Replace Obsolete constants"),
|
||||
'parser' => $subcommandParser
|
||||
))->addSubcommand('controller_redirects', array(
|
||||
'help' => __d('cake_console', 'Return early on controller redirect calls.'),
|
||||
'parser' => $subcommandParser
|
||||
))->addSubcommand('components', array(
|
||||
'help' => __d('cake_console', 'Update components to extend Component class.'),
|
||||
'parser' => $subcommandParser
|
||||
))->addSubcommand('exceptions', array(
|
||||
'help' => __d('cake_console', 'Replace use of cakeError with exceptions.'),
|
||||
'parser' => $subcommandParser
|
||||
));
|
||||
|
||||
return $parser;
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue