Re-implemented CLI to provide help messages.

This commit is contained in:
Alexandre Rosenfeld
2011-01-27 15:04:37 +00:00
parent 5923296736
commit 976e6789d7
6 changed files with 554 additions and 207 deletions

View File

@@ -1,9 +1,9 @@
<?php <?php
/** /**
* commands.php * cli.php
* *
* ProcessMaker Open Source Edition * ProcessMaker Open Source Edition
* Copyright (C) 2010 Colosa Inc. * Copyright (C) 2011 Colosa Inc.
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as * it under the terms of the GNU Affero General Public License as
@@ -21,6 +21,7 @@
* For more information, contact Colosa Inc, 2566 Le Jeune Rd., * For more information, contact Colosa Inc, 2566 Le Jeune Rd.,
* Coral Gables, FL, 33134, USA, or email info@colosa.com. * Coral Gables, FL, 33134, USA, or email info@colosa.com.
* *
* @author Alexandre Rosenfeld <alexandre@colosa.com>
*/ */
/* Windows supports both / and \ as path separators, so use the Unix separator /* Windows supports both / and \ as path separators, so use the Unix separator
@@ -43,19 +44,30 @@
require_once( PATH_THIRDPARTY . 'pake/pakeGetopt.class.php'); require_once( PATH_THIRDPARTY . 'pake/pakeGetopt.class.php');
require_once( PATH_CORE . 'config/environments.php'); require_once( PATH_CORE . 'config/environments.php');
G::LoadClass("cli");
/* Hide notice, otherwise we get a lot of messages */ /* Hide notice, otherwise we get a lot of messages */
error_reporting(E_ALL ^ E_NOTICE); error_reporting(E_ALL ^ E_NOTICE);
ini_set('display_errors', 1);
// register tasks // trap -V before pake
$dir = PATH_HOME . 'engine/bin/tasks'; if (in_array('-v', $argv) || in_array('-V', $argv) || in_array('--version', $argv))
$tasks = pakeFinder::type('file')->name( 'cli*.php' )->in($dir); {
printf("ProcessMaker version %s\n", pakeColor::colorize(trim(file_get_contents( PATH_GULLIVER . 'VERSION')), 'INFO'));
foreach ($tasks as $task) { exit(0);
include_once($task);
} }
// run task // register tasks
pakeApp::get_instance()->run(null, null, false); //TODO: include plugins
$directories = array(PATH_HOME . 'engine/bin/tasks');
foreach ($directories as $dir) {
foreach (glob("$dir/*.php") as $filename) {
include_once($filename);
}
}
CLI::run();
exit(0); exit(0);

View File

@@ -1,5 +1,27 @@
<?php <?php
/** /**
* cliCommon.php
*
* ProcessMaker Open Source Edition
* Copyright (C) 2011 Colosa Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* For more information, contact Colosa Inc, 2566 Le Jeune Rd.,
* Coral Gables, FL, 33134, USA, or email info@colosa.com.
*
* @author Alexandre Rosenfeld <alexandre@colosa.com>
* @package workflow-engine-bin-tasks * @package workflow-engine-bin-tasks
**/ **/
@@ -12,130 +34,17 @@ if(sizeof($output) == 3) {
} }
/** /**
* From http://www.php.net/manual/en/function.getopt.php#83414 * Returns workspace objects from an array of workspace names.
* Parses $args for parameters and assigns them to an array.
* *
* Supports: * @param array $args an array of workspace names
* -e * @param bool $includeAll if true and no workspace is specified in args,
* -e <value> * returns all available workspaces
* --long-param * @return array of workspace objects
* --long-param=<value>
* --long-param <value>
* <value>
*
* @param array $noopt List of parameters without values
*/ */
function parse_args($args, $noopt = array()) {
$result = array();
while (list($tmp, $p) = each($args)) {
if ($p{0} == '-') {
$pname = substr($p, 1);
$value = true;
if ($pname{0} == '-') {
// long-opt (--<param>)
$pname = substr($pname, 1);
if (strpos($p, '=') !== false) {
// value specified inline (--<param>=<value>)
list($pname, $value) = explode('=', substr($p, 2), 2);
}
}
// check if next parameter is a descriptor or a value
$nextparm = current($args);
if (!in_array($pname, $noopt) && $value === true && $nextparm !== false && $nextparm{0} != '-') list($tmp, $value) = each($args);
$result[$pname] = $value;
} else {
// param doesn't belong to any option
$result[] = $p;
}
}
return $result;
}
function info($message) {
return pakeColor::colorize($message, "INFO");
}
function error($message) {
return pakeColor::colorize($message, "ERROR");
}
function prompt($message) {
echo "$message";
$handle = fopen ("php://stdin","r");
$line = fgets($handle);
return $line;
}
function question($message) {
$input = strtolower(prompt("$message [Y/n] "));
return (array_search(trim($input), array("y", "")) !== false);
}
function logging($message, $filename = NULL) {
static $log_file = NULL;
if (isset($filename)) {
$log_file = fopen($filename, "a");
fwrite($log_file, " -- " . date("c") . " " . $message . " --\n");
} else {
if (isset($log_file))
fwrite($log_file, $message);
echo $message;
}
}
function progress($done, $total, $size=30) {
static $start_time;
// if we go over our bound, just ignore it
if($done > $total) return;
if(empty($start_time)) $start_time=time();
$now = time();
$perc=(double)($done/$total);
$bar=floor($perc*$size);
$status_bar="\r[";
$status_bar.=str_repeat("=", $bar);
if($bar<$size){
$status_bar.=">";
$status_bar.=str_repeat(" ", $size-$bar);
} else {
$status_bar.="=";
}
$disp=number_format($perc*100, 0);
$status_bar.="] $disp% $done/$total";
$rate = ($now-$start_time)/$done;
$left = $total - $done;
$eta = round($rate * $left, 2);
$elapsed = $now - $start_time;
$status_bar.= " remaining: ".number_format($eta)." sec. elapsed: ".number_format($elapsed)." sec.";
echo "$status_bar ";
flush();
// when done, send a newline
if($done == $total) {
echo "\n";
}
}
function get_workspaces_from_args($args, $includeAll = true) { function get_workspaces_from_args($args, $includeAll = true) {
$opts = parse_args($args);
$workspaces = array(); $workspaces = array();
foreach ($opts as $opt => $arg) { foreach ($args as $arg) {
if (is_int($opt)) { $workspaces[] = new workspaceTools($arg);
$workspaces[] = new workspaceTools($arg);
}
} }
if (empty($workspaces) && $includeAll) { if (empty($workspaces) && $includeAll) {
$workspaces = System::listWorkspaces(); $workspaces = System::listWorkspaces();

View File

@@ -1,60 +1,109 @@
<?php <?php
/** /**
* cliUpgrade.php
*
* ProcessMaker Open Source Edition
* Copyright (C) 2011 Colosa Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* For more information, contact Colosa Inc, 2566 Le Jeune Rd.,
* Coral Gables, FL, 33134, USA, or email info@colosa.com.
*
* @author Alexandre Rosenfeld <alexandre@colosa.com>
* @package workflow-engine-bin-tasks * @package workflow-engine-bin-tasks
**/ **/
G::LoadClass("system"); G::LoadClass("system");
G::LoadClass("wsTools"); G::LoadClass("wsTools");
pake_task("upgrade"); CLI::taskName('upgrade');
CLI::taskDescription(<<<EOT
Upgrade workspaces.
This command should be run after ProcessMaker files are upgraded so that all
workspaces are upgraded to the current version.
EOT
);
CLI::taskRun(run_upgrade);
/**
* A versoni of rm_dir which does not exits on error.
*
* @param string $filename directory or file to remove
* @param bool $filesOnly either to remove the containing directory as well or not
*/
function rm_dir($filename, $filesOnly = false) {
if (is_file($filename)) {
@unlink($filename) or CLI::message(CLI::error("Could not remove file $filename")."\n");;
} else {
foreach(glob("$filename/*") as $f) {
rm_dir($f);
}
if (!$filesOnly)
@rmdir($filename) or CLI::message(CLI::error("Could not remove directory $filename")."\n");
}
}
function run_upgrade($command, $args) { function run_upgrade($command, $args) {
logging("UPGRADE", PROCESSMAKER_PATH . "upgrade.log"); CLI::logging("UPGRADE", PROCESSMAKER_PATH . "upgrade.log");
logging("Checking files integrity...\n"); CLI::logging("Checking files integrity...\n");
$checksum = System::verifyChecksum(); $checksum = System::verifyChecksum();
if ($checksum === false) { if ($checksum === false) {
logging(error("checksum.txt not found, integrity check is not possible") . "\n"); CLI::logging(CLI::error("checksum.txt not found, integrity check is not possible") . "\n");
if (!question("Integrity check failed, do you want to continue the upgrade?")) { if (!CLI::question("Integrity check failed, do you want to continue the upgrade?")) {
logging("Upgrade failed\n"); CLI::logging("Upgrade failed\n");
die(); die();
} }
} else { } else {
if (!empty($checksum['missing'])) { if (!empty($checksum['missing'])) {
logging(error("The following files were not found in the installation:")."\n"); CLI::logging(CLI::error("The following files were not found in the installation:")."\n");
foreach($checksum['missing'] as $missing) { foreach($checksum['missing'] as $missing) {
logging(" $missing\n"); CLI::logging(" $missing\n");
} }
} }
if (!empty($checksum['diff'])) { if (!empty($checksum['diff'])) {
logging(error("The following files have modifications:")."\n"); CLI::logging(CLI::error("The following files have modifications:")."\n");
foreach($checksum['diff'] as $diff) { foreach($checksum['diff'] as $diff) {
logging(" $diff\n"); CLI::logging(" $diff\n");
} }
} }
if (!(empty($checksum['missing']) || empty($checksum['diff']))) { if (!(empty($checksum['missing']) || empty($checksum['diff']))) {
if (!question("Integrity check failed, do you want to continue the upgrade?")) { if (!CLI::question("Integrity check failed, do you want to continue the upgrade?")) {
logging("Upgrade failed\n"); CLI::logging("Upgrade failed\n");
die(); die();
} }
} }
} }
logging("Clearing cache...\n"); CLI::logging("Clearing cache...\n");
if(defined('PATH_C')) if(defined('PATH_C'))
G::rm_dir(PATH_C); rm_dir(PATH_C, true);
$workspaces = get_workspaces_from_args($args); $workspaces = get_workspaces_from_args($args);
$count = count($workspaces); $count = count($workspaces);
$first = true; $first = true;
foreach ($workspaces as $index => $workspace) { foreach ($workspaces as $index => $workspace) {
try { try {
logging("Upgrading workspaces ($index/$count): " . info($workspace->name) . "\n"); CLI::logging("Upgrading workspaces ($index/$count): " . CLI::info($workspace->name) . "\n");
$workspace->upgrade($first); $workspace->upgrade($first);
$workspace->close(); $workspace->close();
$first = false; $first = false;
} catch (Exception $e) { } catch (Exception $e) {
logging("Errors upgrading workspace " . info($workspace->name) . ": " . error($e->getMessage()) . "\n"); CLI::logging("Errors upgrading workspace " . CLI::info($workspace->name) . ": " . CLI::error($e->getMessage()) . "\n");
} }
} }
logging("Upgrade successful\n"); CLI::logging("Upgrade successful\n");
} }
?> ?>

View File

@@ -1,38 +1,160 @@
<?php <?php
/** /**
* cliWorkspaces.php
*
* ProcessMaker Open Source Edition
* Copyright (C) 2011 Colosa Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* For more information, contact Colosa Inc, 2566 Le Jeune Rd.,
* Coral Gables, FL, 33134, USA, or email info@colosa.com.
*
* @author Alexandre Rosenfeld <alexandre@colosa.com>
* @package workflow-engine-bin-tasks * @package workflow-engine-bin-tasks
**/ **/
pake_task('info'); CLI::taskName('info');
CLI::taskDescription(<<<EOT
Print information about the current system and specified workspaces.
EOT
);
CLI::taskArg('workspace-name', true, true);
CLI::taskRun(run_info);
pake_task('workspace-upgrade'); CLI::taskName('workspace-backup');
CLI::taskDescription(<<<EOT
Backup the specified workspace to an archive.
pake_task('workspace-backup'); BACKUP-NAME is the backup filename. If it contains slashes, it will be
pake_task('workspace-restore'); treated as a filename, either absolute or relative. Otherwise it will be
treated as a filename inside the backups directory in the 'shared' folder.
If no BACKUP-NAME is specified, it will use the workspace name as the
filename.
pake_task('translation-upgrade'); A backup archive will contain all information about the specified workspace
pake_task('cacheview-upgrade'); so that it can be restored later. A database dump will be created and all
the workspace files will be included in the archive.
EOT
);
CLI::taskArg('workspace', false);
CLI::taskArg('backup-file', true);
CLI::taskRun(run_workspace_backup);
pake_task('database-upgrade'); CLI::taskName('workspace-restore');
pake_task('database-check'); CLI::taskDescription(<<<EOT
Restore a workspace from a backup.
pake_task('plugins-database-upgrade'); BACKUP-NAME is the backup filename. If it contains slashes, it will be
treated as a filename, either absolute or relative. Otherwise it will be
treated as a filename inside the backups directory in the 'shared' folder.
pake_task('database-export'); If WORKSPACE is specified, it will be used as the workspace to restore to,
pake_task('database-import'); otherwise the workspace will be restored using the same name as before.
EOT
);
CLI::taskArg('backup-file', false);
CLI::taskArg('workspace', true);
CLI::taskRun(run_workspace_restore);
pake_task('drafts-clean'); CLI::taskName('cacheview-repair');
CLI::taskDescription(<<<EOT
Create and populate APP_CACHE_VIEW
function run_info($command, $args) { You can specify as many workspaces as you want and if no workspace is
specified, it will run the upgrade on all available workspaces.
In order to improve the performance, ProcessMaker includes a cache of cases
in the table APP_CACHE_VIEW. This table must be in sync with the database
to present the right information in the inbox. This command will create the
table and populate it with the right information. You only need to use this
command after upgrading or if the inbox is out of sync.
EOT
);
CLI::taskArg('workspace', true, true);
CLI::taskRun(run_cacheview_upgrade);
CLI::taskName('database-upgrade');
CLI::taskDescription(<<<EOT
Repair the database schema to the latest version
You can specify as many workspaces as you want and if no workspace is
specified, it will run the upgrade on all available workspaces.
This command will read the system schema and attempt to modify the workspaces
tables to this new schema. This is useful after an upgrade when the workspace
database is not upgraded or when the database does not contain the right
schema.
EOT
);
CLI::taskArg('workspace', true, true);
CLI::taskRun(run_database_upgrade);
CLI::taskName('plugins-database-upgrade');
CLI::taskDescription(<<<EOT
Repair the database schema to the latest version
You can specify as many workspaces as you want and if no workspace is
specified, it will run the upgrade on all available workspaces.
The same as database-upgrade but works with schemas provided by plugins.
This is useful if there are installed plugins that include database schemas.
EOT
);
CLI::taskArg('workspace', true, true);
CLI::taskRun(run_plugins_database_upgrade);
CLI::taskName('workspace-upgrade');
CLI::taskDescription(<<<EOT
Upgrade the workspace(s) specified.
If no workspace is specified, the command will be run in all workspaces. You
can specify more then one workspace.
This command is a shortcut to execute all upgrade commands in this workspace.
Upgrading a workspace will make it corresponds to this version of
ProcessMaker. Use this command to upgrade workspaces individually, otherwise
use the upgrade command to upgrade the entire system.
EOT
);
CLI::taskArg('workspace-name', true, true);
CLI::taskRun(run_workspace_upgrade);
CLI::taskName('translation-repair');
CLI::taskDescription(<<<EOT
Upgrade translations for the specified workspace(s).
If no workspace is specified, the command will be run in all workspaces. You
can specify more then one workspace.
This command will go through each language installed in ProcessMaker and
update this workspace translations to match the installed translations.
EOT
);
CLI::taskArg('workspace-name', true, true);
CLI::taskRun(run_translation_upgrade);
function run_info($args, $opts) {
$workspaces = get_workspaces_from_args($args, false); $workspaces = get_workspaces_from_args($args, false);
System::printSysInfo(); workspaceTools::printSysInfo();
foreach ($workspaces as $workspace) { foreach ($workspaces as $workspace) {
echo "\n"; echo "\n";
$workspace->printMetadata(false); $workspace->printMetadata(false);
} }
} }
function run_workspace_upgrade($command, $args) { function run_workspace_upgrade($args, $opts) {
$workspaces = get_workspaces_from_args($args); $workspaces = get_workspaces_from_args($args);
foreach ($workspaces as $workspace) { foreach ($workspaces as $workspace) {
try { try {
@@ -43,7 +165,7 @@ function run_workspace_upgrade($command, $args) {
} }
} }
function run_translation_upgrade($command, $args) { function run_translation_upgrade($args, $opts) {
$workspaces = get_workspaces_from_args($args); $workspaces = get_workspaces_from_args($args);
$updateXml = true; $updateXml = true;
foreach ($workspaces as $workspace) { foreach ($workspaces as $workspace) {
@@ -57,7 +179,7 @@ function run_translation_upgrade($command, $args) {
} }
} }
function run_cacheview_upgrade($command, $args) { function run_cacheview_upgrade($args, $opts) {
$workspaces = get_workspaces_from_args($args); $workspaces = get_workspaces_from_args($args);
$updateXml = true; $updateXml = true;
foreach ($workspaces as $workspace) { foreach ($workspaces as $workspace) {
@@ -70,34 +192,34 @@ function run_cacheview_upgrade($command, $args) {
} }
} }
function run_plugins_database_upgrade($command, $args) { function run_plugins_database_upgrade($args, $opts) {
$workspaces = get_workspaces_from_args($args); $workspaces = get_workspaces_from_args($args);
foreach ($workspaces as $workspace) { foreach ($workspaces as $workspace) {
try { try {
logging("Upgrading plugins database for " . info($workspace->name) . "\n"); CLI::logging("Upgrading plugins database for " . info($workspace->name) . "\n");
$workspace->upgradePluginsDatabase(); $workspace->upgradePluginsDatabase();
} catch (Exception $e) { } catch (Exception $e) {
logging("Errors upgrading plugins database: " . error($e->getMessage())); CLI::logging("Errors upgrading plugins database: " . error($e->getMessage()));
} }
} }
} }
function run_database_export($command, $args) { function run_database_export($args, $opts) {
if (count($args) < 2) if (count($args) < 2)
throw new Exception ("Please provide a workspace name and a directory for export"); throw new Exception ("Please provide a workspace name and a directory for export");
$workspace = new workspaceTools($args[0]); $workspace = new workspaceTools($args[0]);
$workspace->exportDatabase($args[1]); $workspace->exportDatabase($args[1]);
} }
function run_database_import($command, $args) { function run_database_import($args, $opts) {
throw new Exception("Not implemented"); throw new Exception("Not implemented");
} }
function run_database_upgrade($task, $args) { function run_database_upgrade($args, $opts) {
database_upgrade("upgrade", $args); database_upgrade("upgrade", $args);
} }
function run_database_check($task, $args) { function run_database_check($args, $opts) {
database_upgrade("check", $args); database_upgrade("check", $args);
} }
@@ -136,7 +258,7 @@ function delete_app_from_table($con, $tableName, $appUid, $col="APP_UID") {
$rs = $stmt->executeQuery($sql, ResultSet::FETCHMODE_NUM); $rs = $stmt->executeQuery($sql, ResultSet::FETCHMODE_NUM);
} }
function run_drafts_clean($task, $args) { function run_drafts_clean($args, $opts) {
echo "Cleaning drafts\n"; echo "Cleaning drafts\n";
if (count($args) < 1) if (count($args) < 1)
@@ -215,16 +337,25 @@ function run_drafts_clean($task, $args) {
echo "\n"; echo "\n";
} }
function run_workspace_backup($task, $args) { function run_workspace_backup($args, $opts) {
$workspace = new workspaceTools($args[0]); $workspaces = array();
if (isset($args[1])) if (sizeof($args) > 2) {
$filename = $args[1]; $filename = array_pop($args);
else foreach ($args as $arg) {
$filename = PATH_DATA . "backups/" . $workspace->name . ".tar"; $workspaces[] = new workspaceTools($arg);
$workspace->backup($filename); }
} else if (sizeof($args) > 0) {
$workspaces[] = new workspaceTools($args[0]);
if (sizeof($args) == 2)
$filename = $args[1];
else
$filename = $workspace->name . ".tar.gz";
}
foreach ($workspaces as $workspace)
$workspace->backup($filename);
} }
function run_workspace_restore($task, $args) { function run_workspace_restore($args, $opts) {
//$workspace = new workspaceTools($args[0]); //$workspace = new workspaceTools($args[0]);
workspaceTools::restore($args[0], $args[1]); workspaceTools::restore($args[0], $args[1]);
} }

View File

@@ -0,0 +1,245 @@
<?php
/**
* class.cli.php
*
* ProcessMaker Open Source Edition
* Copyright (C) 2011 Colosa Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* For more information, contact Colosa Inc, 2566 Le Jeune Rd.,
* Coral Gables, FL, 33134, USA, or email info@colosa.com.
*
* @author Alexandre Rosenfeld <alexandre@colosa.com>
*/
class CLI {
public static $tasks = array();
public static $currentTask = NULL;
/**
* Adds a new task defined by it's name. All other task functions will
* remember the current task defined here.
*
* @param string $name name of the task, used in the command-line
*/
public static function taskName($name) {
self::$currentTask = $name;
self::$tasks[$name] = array(
'name' => $name,
'description' => NULL,
'args' => array(),
'function' => NULL,
'opt' => array('short' => '', 'long' => array())
);
}
/**
* Adds a description to the current task. The description should contain a
* one-line description of the command and a few lines of text with more
* information.
*
* @param string $description task description
*/
public static function taskDescription($description) {
assert(self::$currentTask !== NULL);
self::$tasks[self::$currentTask]["description"] = $description;
}
/**
* Adds an argument to the current task. The options will affect how it is
* displayed in the help command. Optional will put [] in the argument and
* multiple will put ... in the end. Arguments are displayed together with
* the task name in the help command.
*
* @param string $name argument name
*/
public static function taskArg($name, $optional = true, $multiple = false) {
assert(self::$currentTask !== NULL);
self::$tasks[self::$currentTask]["args"][$name] = array(
'optional' => $optional,
'multiple' => $multiple
);
}
/**
* Defines short and long options as used by getopt to the current task.
*
* @param string $short short options
* @param array $long long options
*/
public static function taskOpt($short, $long = array()) {
assert(self::$currentTask !== NULL);
self::$tasks[self::$currentTask]["opt"] = array('short' => $short, 'long' => $long);
}
/**
* Defines the function to run for the current task.
*
* @param callback $function function to run
*/
public static function taskRun($function) {
assert(self::$currentTask !== NULL);
self::$tasks[self::$currentTask]["function"] = $function;
}
/**
* Displays the help instructions.
*
* @param array $args if defined, the task name should be argument 0
* @param array $opts options as returned by getopt
*/
public static function help($args, $opts) {
global $argv;
$scriptName = $argv[0];
$taskName = $args[0];
if (!$taskName) {
echo "usage: $scriptName <task> [options] [args]\n";
echo "Type '$scriptName help <task-name>' for help on a specific task\n";
echo "\nAvailable tasks:\n";
$tasks = array();
ksort(self::$tasks);
foreach (self::$tasks as $name => $data) {
$description = explode("\n", $data['description']);
$tasks[] = " $name";
}
$tasks = join("\n", $tasks);
echo $tasks . "\n\n";
} else {
$valid_args = array();
foreach(self::$tasks[$taskName]['args'] as $arg => $data) {
$arg = strtoupper($arg);
if ($data['multiple'])
$arg = "$arg...";
if ($data['optional'])
$arg = "[$arg]";
$valid_args[] = $arg;
}
$valid_args = join(" ", $valid_args);
$description = explode("\n", self::$tasks[$taskName]['description']);
$taskDescription = trim(array_shift($description));
$description = trim(implode("\n", $description));
$message = <<< EOT
$taskName: {$taskDescription}
Usage: $scriptName $taskName $valid_args
$description
EOT;
if ($valid_options) {
$message .= <<< EOT
Options:
$valid_options
EOT;
}
echo $message . "\n";
}
}
/**
* Run the CLI task, which will check which command is specified and run it.
*/
public static function run() {
CLI::taskName("help");
CLI::taskRun(array('self', 'help'));
global $argv;
$args = $argv;
$cliname = array_shift($args);
$taskName = array_shift($args);
while ($taskName{0} == '-')
$taskName = array_shift($args);
if (!$taskName) {
self::help();
return;
}
$taskData = NULL;
foreach (self::$tasks as $name => $data) {
if (strcasecmp($name, $taskName) === 0) {
$taskData = $data;
break;
}
}
G::LoadThirdParty('pear/Console', 'Getopt');
$short = "h" . $taskData['opt']['short'];
$long = array_merge(array("help"), $taskData['opt']['long']);
list($options, $arguments) = Console_GetOpt::getopt2($args, $short, $long);
call_user_func($taskData['function'], $arguments, $options);
die();
}
/**
* Returns an information colorized version of the message.
*
* @param string $message the message to colorize
*/
public static function info($message) {
return pakeColor::colorize($message, "INFO");
}
/**
* Returns an error colorized version of the message.
*
* @param string $message the message to colorize
*/
public static function error($message) {
return pakeColor::colorize($message, "ERROR");
}
/**
* Prompt the user for information.
*
* @param string $message the message to display
* @return string the text typed by the user
*/
public static function prompt($message) {
echo "$message";
$handle = fopen ("php://stdin","r");
$line = fgets($handle);
return $line;
}
/**
* Ask a question of yes or no.
*
* @param string $message the message to display
* @return bool true if the user choosed no, false otherwise
*/
public static function question($message) {
$input = strtolower(self::prompt("$message [Y/n] "));
return (array_search(trim($input), array("y", "")) !== false);
}
/**
* Display a message to the user. If filename is specified, it will setup
* a logging file where all messages will be recorded.
*
* @param string $message the message to display
* @param string $filename the log file to write messages
*/
public static function logging($message, $filename = NULL) {
static $log_file = NULL;
if (isset($filename)) {
$log_file = fopen($filename, "a");
fwrite($log_file, " -- " . date("c") . " " . $message . " --\n");
} else {
if (isset($log_file))
fwrite($log_file, $message);
echo $message;
}
}
}
?>

View File

@@ -8,6 +8,7 @@
*/ */
G::LoadSystem('dbMaintenance'); G::LoadSystem('dbMaintenance');
G::LoadClass("cli");
class workspaceTools { class workspaceTools {
var $name = NULL; var $name = NULL;
@@ -51,11 +52,11 @@ class workspaceTools {
* @param bool $first true if this is the first workspace to be upgrade * @param bool $first true if this is the first workspace to be upgrade
*/ */
public function upgrade($first = false) { public function upgrade($first = false) {
logging("> Updating database...\n"); CLI::logging("> Updating database...\n");
$this->upgradeDatabase(); $this->upgradeDatabase();
logging("> Updating translations...\n"); CLI::logging("> Updating translations...\n");
$this->upgradeTranslation($first); $this->upgradeTranslation($first);
logging("> Updating cache view...\n"); CLI::logging("> Updating cache view...\n");
$this->upgradeCacheView(); $this->upgradeCacheView();
} }
@@ -135,7 +136,7 @@ class workspaceTools {
$this->resetDBNames = $resetDBNames; $this->resetDBNames = $resetDBNames;
$this->resetDBDiff = array(); $this->resetDBDiff = array();
logging("Resetting db info\n"); CLI::logging("Resetting db info\n");
if (!$this->workspaceExists()) if (!$this->workspaceExists())
throw new Exception("Could not find db.php in the restore"); throw new Exception("Could not find db.php in the restore");
$sDbFile = file_get_contents($this->dbPath); $sDbFile = file_get_contents($this->dbPath);
@@ -258,7 +259,7 @@ class workspaceTools {
G::LoadThirdParty('pear/json', 'class.json'); G::LoadThirdParty('pear/json', 'class.json');
$languages = new languages(); $languages = new languages();
foreach (System::listPoFiles() as $poFile) { foreach (System::listPoFiles() as $poFile) {
logging("Updating language ".basename($poFile)."\n"); CLI::logging("Updating language ".basename($poFile)."\n");
$languages->importLanguage($poFile, $updateXml); $languages->importLanguage($poFile, $updateXml);
} }
} }
@@ -389,13 +390,13 @@ class workspaceTools {
$currentUserIsSuper = true; $currentUserIsSuper = true;
} }
logging("Creating table"); CLI::logging("Creating table");
//now check if table APPCACHEVIEW exists, and it have correct number of fields, etc. //now check if table APPCACHEVIEW exists, and it have correct number of fields, etc.
if (!$checkOnly) { if (!$checkOnly) {
$res = $appCache->checkAppCacheView(); $res = $appCache->checkAppCacheView();
} }
logging(", triggers"); CLI::logging(", triggers");
//now check if we have the triggers installed //now check if we have the triggers installed
$triggers = array(); $triggers = array();
$triggers[] = $appCache->triggerAppDelegationInsert($lang, $checkOnly); $triggers[] = $appCache->triggerAppDelegationInsert($lang, $checkOnly);
@@ -404,12 +405,12 @@ class workspaceTools {
$triggers[] = $appCache->triggerApplicationDelete($lang, $checkOnly); $triggers[] = $appCache->triggerApplicationDelete($lang, $checkOnly);
if (!$checkOnly) { if (!$checkOnly) {
logging(", filling cache view"); CLI::logging(", filling cache view");
//build using the method in AppCacheView Class //build using the method in AppCacheView Class
$res = $appCache->fillAppCacheView($lang); $res = $appCache->fillAppCacheView($lang);
logging("."); CLI::logging(".");
} }
logging("\n"); CLI::logging("\n");
//set status in config table //set status in config table
$confParams = Array( $confParams = Array(
'LANG' => $lang, 'LANG' => $lang,
@@ -432,7 +433,7 @@ class workspaceTools {
foreach (System::getPlugins() as $pluginName) { foreach (System::getPlugins() as $pluginName) {
$pluginSchema = System::getPluginSchema($pluginName); $pluginSchema = System::getPluginSchema($pluginName);
if ($pluginSchema !== false) { if ($pluginSchema !== false) {
logging("Updating plugin " . info($pluginName) . "\n"); CLI::logging("Updating plugin " . CLI::info($pluginName) . "\n");
$this->upgradeSchema($pluginSchema); $this->upgradeSchema($pluginSchema);
} }
} }
@@ -483,7 +484,7 @@ class workspaceTools {
$oDataBase->logQuery ( count ($changes ) ); $oDataBase->logQuery ( count ($changes ) );
logging( "" . count($changes['tablesToAdd']) . " tables to add"); CLI::logging( "" . count($changes['tablesToAdd']) . " tables to add");
foreach ($changes['tablesToAdd'] as $sTable => $aColumns) { foreach ($changes['tablesToAdd'] as $sTable => $aColumns) {
$oDataBase->executeQuery($oDataBase->generateCreateTableSQL($sTable, $aColumns)); $oDataBase->executeQuery($oDataBase->generateCreateTableSQL($sTable, $aColumns));
if (isset($changes['tablesToAdd'][$sTable]['INDEXES'])) { if (isset($changes['tablesToAdd'][$sTable]['INDEXES'])) {
@@ -493,7 +494,7 @@ class workspaceTools {
} }
} }
logging(", " . count($changes['tablesToAlter']) . " tables to alter"); CLI::logging(", " . count($changes['tablesToAlter']) . " tables to alter");
foreach ($changes['tablesToAlter'] as $sTable => $aActions) { foreach ($changes['tablesToAlter'] as $sTable => $aActions) {
foreach ($aActions as $sAction => $aAction) { foreach ($aActions as $sAction => $aAction) {
foreach ($aAction as $sColumn => $vData) { foreach ($aAction as $sColumn => $vData) {
@@ -512,21 +513,21 @@ class workspaceTools {
} }
} }
logging(", " . count($changes['tablesWithNewIndex']) . " indexes to add"); CLI::logging(", " . count($changes['tablesWithNewIndex']) . " indexes to add");
foreach ($changes['tablesWithNewIndex'] as $sTable => $aIndexes) { foreach ($changes['tablesWithNewIndex'] as $sTable => $aIndexes) {
foreach ($aIndexes as $sIndexName => $aIndexFields ) { foreach ($aIndexes as $sIndexName => $aIndexFields ) {
$oDataBase->executeQuery($oDataBase->generateAddKeysSQL($sTable, $sIndexName, $aIndexFields )); $oDataBase->executeQuery($oDataBase->generateAddKeysSQL($sTable, $sIndexName, $aIndexFields ));
} }
} }
logging(", " . count($changes['tablesWithNewIndex']) . " indexes to alter"); CLI::logging(", " . count($changes['tablesWithNewIndex']) . " indexes to alter");
foreach ($changes['tablesToAlterIndex'] as $sTable => $aIndexes) { foreach ($changes['tablesToAlterIndex'] as $sTable => $aIndexes) {
foreach ($aIndexes as $sIndexName => $aIndexFields ) { foreach ($aIndexes as $sIndexName => $aIndexFields ) {
$oDataBase->executeQuery($oDataBase->generateDropKeySQL($sTable, $sIndexName )); $oDataBase->executeQuery($oDataBase->generateDropKeySQL($sTable, $sIndexName ));
$oDataBase->executeQuery($oDataBase->generateAddKeysSQL($sTable, $sIndexName, $aIndexFields )); $oDataBase->executeQuery($oDataBase->generateAddKeysSQL($sTable, $sIndexName, $aIndexFields ));
} }
} }
logging("\n"); CLI::logging("\n");
$this->closeDatabase(); $this->closeDatabase();
return true; return true;
} }
@@ -601,7 +602,7 @@ class workspaceTools {
foreach ($info as $k => $v) { foreach ($info as $k => $v) {
if (is_numeric($k)) $k = ""; if (is_numeric($k)) $k = "";
logging(sprintf("%20s %s\n", $k, pakeColor::colorize($v, 'INFO'))); CLI::logging(sprintf("%20s %s\n", $k, pakeColor::colorize($v, 'INFO')));
} }
} }
@@ -615,7 +616,7 @@ class workspaceTools {
if ($printSysInfo) { if ($printSysInfo) {
workspaceTools::printSysInfo (); workspaceTools::printSysInfo ();
logging("\n"); CLI::logging("\n");
} }
$wfDsn = $fields['DB_ADAPTER'] . '://' . $fields['DB_USER'] . ':' . $fields['DB_PASS'] . '@' . $fields['DB_HOST'] . '/' . $fields['DB_NAME']; $wfDsn = $fields['DB_ADAPTER'] . '://' . $fields['DB_USER'] . ':' . $fields['DB_PASS'] . '@' . $fields['DB_HOST'] . '/' . $fields['DB_NAME'];
@@ -633,7 +634,7 @@ class workspaceTools {
foreach ($info as $k => $v) { foreach ($info as $k => $v) {
if (is_numeric($k)) $k = ""; if (is_numeric($k)) $k = "";
logging(sprintf("%20s %s\n", $k, pakeColor::colorize($v, 'INFO'))); CLI::logging(sprintf("%20s %s\n", $k, pakeColor::colorize($v, 'INFO')));
} }
} }
@@ -652,7 +653,7 @@ class workspaceTools {
$dbInfo = $this->getDBCredentials($db); $dbInfo = $this->getDBCredentials($db);
$oDbMaintainer = new DataBaseMaintenance($dbInfo["host"], $dbInfo["user"], $oDbMaintainer = new DataBaseMaintenance($dbInfo["host"], $dbInfo["user"],
$dbInfo["pass"]); $dbInfo["pass"]);
logging("Saving database {$dbInfo["name"]}\n"); CLI::logging("Saving database {$dbInfo["name"]}\n");
$oDbMaintainer->connect($dbInfo["name"]); $oDbMaintainer->connect($dbInfo["name"]);
$oDbMaintainer->lockTables(); $oDbMaintainer->lockTables();
$oDbMaintainer->setTempDir($path . "/"); $oDbMaintainer->setTempDir($path . "/");
@@ -716,7 +717,7 @@ class workspaceTools {
unlink($tempDirectory); unlink($tempDirectory);
mkdir($tempDirectory); mkdir($tempDirectory);
$metadata = $this->getMetadata(); $metadata = $this->getMetadata();
logging("Backing up database...\n"); CLI::logging("Backing up database...\n");
$metadata["databases"] = $this->exportDatabase($tempDirectory); $metadata["databases"] = $this->exportDatabase($tempDirectory);
$metadata["directories"] = array("{$this->name}.files"); $metadata["directories"] = array("{$this->name}.files");
$metadata["version"] = 1; $metadata["version"] = 1;
@@ -727,9 +728,9 @@ class workspaceTools {
file_put_contents($metaFilename, file_put_contents($metaFilename,
str_replace(array(",", "{", "}"), array(",\n ", "{\n ", "\n}\n"), str_replace(array(",", "{", "}"), array(",\n ", "{\n ", "\n}\n"),
G::json_encode($metadata))); G::json_encode($metadata)));
logging("Copying database to backup...\n"); CLI::logging("Copying database to backup...\n");
$this->addToBackup($backup, $tempDirectory, $tempDirectory); $this->addToBackup($backup, $tempDirectory, $tempDirectory);
logging("Copying files to backup...\n"); CLI::logging("Copying files to backup...\n");
$this->addToBackup($backup, $this->path, $this->path, "{$this->name}.files"); $this->addToBackup($backup, $this->path, $this->path, "{$this->name}.files");
//Remove leftovers. //Remove leftovers.
@@ -764,7 +765,7 @@ class workspaceTools {
if ($result === false) if ($result === false)
throw new Exception("Unable to drop user: " . mysql_error ()); throw new Exception("Unable to drop user: " . mysql_error ());
} }
logging("Creating user $username for $hostname\n"); CLI::logging("Creating user $username for $hostname\n");
$result = mysql_query("CREATE USER '$username'@'$hostname' IDENTIFIED BY '$password'"); $result = mysql_query("CREATE USER '$username'@'$hostname' IDENTIFIED BY '$password'");
if ($result === false) if ($result === false)
throw new Exception("Unable to create user: " . mysql_error ()); throw new Exception("Unable to create user: " . mysql_error ());
@@ -859,13 +860,13 @@ class workspaceTools {
$workspace = new workspaceTools($workspaceName); $workspace = new workspaceTools($workspaceName);
if ($workspace->workspaceExists()) if ($workspace->workspaceExists())
// throw new Exception("Workspace exists"); // throw new Exception("Workspace exists");
logging("Workspace $workspaceName already exists, overwriting!\n"); CLI::logging("Workspace $workspaceName already exists, overwriting!\n");
if (file_exists($workspace->path)) if (file_exists($workspace->path))
G::rm_dir($workspace->path); G::rm_dir($workspace->path);
foreach ($metadata->directories as $dir) { foreach ($metadata->directories as $dir) {
logging("Restoring directory '$dir'\n"); CLI::logging("Restoring directory '$dir'\n");
if (!rename("$tempDirectory/$dir", $workspace->path)) { if (!rename("$tempDirectory/$dir", $workspace->path)) {
throw new Exception("There was an error copying the backup files ($tempDirectory/$dir) to the workspace directory {$workspace->path}.\n"); throw new Exception("There was an error copying the backup files ($tempDirectory/$dir) to the workspace directory {$workspace->path}.\n");
@@ -879,7 +880,7 @@ class workspaceTools {
foreach ($metadata->databases as $db) { foreach ($metadata->databases as $db) {
$dbName = $newDBNames[$db->name]; $dbName = $newDBNames[$db->name];
logging("Restoring database {$db->name} to $dbName\n"); CLI::logging("Restoring database {$db->name} to $dbName\n");
$workspace->executeSQLScript($dbName, "$tempDirectory/{$db->name}.sql"); $workspace->executeSQLScript($dbName, "$tempDirectory/{$db->name}.sql");
$workspace->createDBUser($dbName, $db->pass, "localhost", $dbName); $workspace->createDBUser($dbName, $db->pass, "localhost", $dbName);
$workspace->createDBUser($dbName, $db->pass, "%", $dbName); $workspace->createDBUser($dbName, $db->pass, "%", $dbName);