mirror of
https://github.com/brmlab/brmbiolab_sklad.git
synced 2025-06-11 22:53:59 +02:00
Initial commit
This commit is contained in:
commit
3b93da31de
1004 changed files with 265840 additions and 0 deletions
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;
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue