- Added comprehensive AI Assistant system (aiassist/ directory): * Vector search and embedding capabilities * Typebot proxy integration * Elastic search functionality * Message classification and chat history * MCP proxy for external integrations - Implemented Court Status API (GetCourtStatus.php): * Real-time court document status checking * Integration with external court systems * Comprehensive error handling and logging - Enhanced S3 integration: * Improved file backup system with metadata * Batch processing capabilities * Enhanced error logging and recovery * Copy operations with URL fixing - Added Telegram contact creation API - Improved error logging across all modules - Enhanced callback system for AI responses - Extensive backup file storage with timestamps - Updated documentation and README files - File storage improvements: * Thousands of backup files with proper metadata * Fix operations for broken file references * Project-specific backup and recovery systems * Comprehensive file integrity checking Total: 26,461+ files added/modified including AWS SDK, vendor dependencies, and extensive backup system.
613 lines
18 KiB
PHP
613 lines
18 KiB
PHP
<?php
|
|
/*+**********************************************************************************
|
|
* The contents of this file are subject to the vtiger CRM Public License Version 1.0
|
|
* ("License"); You may not use this file except in compliance with the License
|
|
* The Original Code is: vtiger CRM Open Source
|
|
* The Initial Developer of the Original Code is vtiger.
|
|
* Portions created by vtiger are Copyright (C) vtiger.
|
|
* All Rights Reserved.
|
|
************************************************************************************/
|
|
|
|
chdir(dirname(__FILE__) . '/../..');
|
|
include_once 'vtlib/Vtiger/Module.php';
|
|
include_once 'vtlib/Vtiger/Package.php';
|
|
include_once 'includes/main/WebUI.php';
|
|
|
|
include_once 'include/Webservices/Utils.php';
|
|
|
|
class Vtiger_Tools_Console_Controller {
|
|
const PROMPT_ANY = 1;
|
|
const PROMPT_OPTIONAL = 2;
|
|
const PROMPT_NUMERIC = 3;
|
|
const PROMPT_ALPHANUMERIC = 4;
|
|
const PROMPT_NAME = 5;
|
|
const PROMPT_LABEL = 6;
|
|
const PROMPT_PATH = 7;
|
|
|
|
protected $interactive = true;
|
|
protected $arguments = array();
|
|
|
|
protected function __construct() { }
|
|
|
|
public function setArguments($args, $interactive) {
|
|
$this->arguments = $args;
|
|
$this->interactive = $interactive;
|
|
return $this;
|
|
}
|
|
|
|
protected function handle() {
|
|
global $argv;
|
|
$this->arguments = $argv;
|
|
|
|
// Discard the script name.
|
|
array_shift($this->arguments);
|
|
|
|
if ($this->arguments) {
|
|
$this->arguments = explode('=', $this->arguments[0]);
|
|
$this->interactive = false;
|
|
}
|
|
|
|
$this->welcome();
|
|
$this->options();
|
|
}
|
|
|
|
protected function welcome() {
|
|
if ($this->interactive) {
|
|
echo "Welcome to Vtiger CRM Creator.\n";
|
|
echo "This tool will enable you to get started with developing extensions with ease.\n";
|
|
echo "Have a good time. Press CTRL+C to \"quit\".\n";
|
|
}
|
|
}
|
|
|
|
protected function options() {
|
|
if ($this->interactive) {
|
|
echo "Choose the options below:\n";
|
|
echo "1. Create New Module.\n";
|
|
echo "2. Create New Layout.\n";
|
|
echo "3. Create New Language Pack.\n";
|
|
echo "4. Create Test Language Pack.\n";
|
|
echo "5. Import Module.\n";
|
|
echo "6. Update Module.\n";
|
|
echo "7. Remove Module.\n";
|
|
$option = $this->prompt("Enter your choice: ", self::PROMPT_NUMERIC);
|
|
} else {
|
|
$option = array_shift($this->arguments);
|
|
switch ($option) {
|
|
case '--import': $option = 5; break;
|
|
case '--update': $option = 6; break;
|
|
case '--remove': $option = 7; break;
|
|
}
|
|
}
|
|
|
|
try {
|
|
switch (intval($option)) {
|
|
case 1: $this->handleCreateModule(); break;
|
|
case 2: $this->handleCreateLayout(); break;
|
|
case 3: $this->handleCreateLanguage(); break;
|
|
case 4: $this->handleCreateTestLanguage(); break;
|
|
case 5: $this->handleImportModule(); break;
|
|
case 6: $this->handleUpdateModule(); break;
|
|
case 7: $this->handleRemoveModule(); break;
|
|
}
|
|
} catch (Exception $e) {
|
|
echo "ERROR: " .$e->getMessage() . "\n";
|
|
echo $e->getTraceAsString();
|
|
echo "\n";
|
|
}
|
|
}
|
|
|
|
protected function prompt($msg='', $type=self::PROMPT_ANY) {
|
|
do {
|
|
if ($msg) echo $msg;
|
|
$value = trim(fgets(STDIN));
|
|
|
|
if (!$value && $type == self::PROMPT_OPTIONAL) {
|
|
return $value;
|
|
|
|
} else if ($value) {
|
|
|
|
switch ($type) {
|
|
case self::PROMPT_NUMERIC:
|
|
if (is_numeric($value)) {
|
|
return $value;
|
|
}
|
|
break;
|
|
case self::PROMPT_ALPHANUMERIC:
|
|
if (!preg_match("/^[a-zA-Z0-9]+$/", $value)) {
|
|
return $value;
|
|
}
|
|
break;
|
|
case self::PROMPT_NAME:
|
|
if (!preg_match("/^[a-zA-Z][^a-zA-Z0-9_ ]*$/", $value)) {
|
|
return $value;
|
|
}
|
|
break;
|
|
case self::PROMPT_LABEL:
|
|
if (!preg_match("/^[a-zA-Z0-9_ ]+$/", $value)) {
|
|
return $value;
|
|
}
|
|
break;
|
|
case self::PROMPT_PATH:
|
|
if (!preg_match("/^[a-zA-Z0-9_:+.-\/\\\\]+$/", $value)) {
|
|
return $value;
|
|
}
|
|
default:
|
|
return $value;
|
|
}
|
|
}
|
|
} while (true);
|
|
}
|
|
|
|
protected function toAlphaNumeric($value) {
|
|
return preg_replace("/[^a-zA-Z0-9_]/", "", $value);
|
|
}
|
|
|
|
protected function findFiles($dir, $file_pattern, &$files) {
|
|
$items = glob($dir . '/*', GLOB_NOSORT);
|
|
foreach ($items as $item) {
|
|
if (is_file($item)) {
|
|
if (!$file_pattern || preg_match("/$file_pattern/", $item)) {
|
|
$files[] = $item;
|
|
}
|
|
} else if (is_dir($item) && ($dir != $item)) {
|
|
$this->findFiles($item, $file_pattern, $files);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Option Handlers
|
|
protected function handleCreateModule() {
|
|
$controller = new Vtiger_Tools_Console_ModuleController();
|
|
$controller->setArguments($this->arguments, $this->interactive)->handle();
|
|
}
|
|
|
|
protected function handleCreateLanguage() {
|
|
$controller = new Vtiger_Tools_Console_LanguageController();
|
|
$controller->setArguments($this->arguments, $this->interactive)->handle();
|
|
}
|
|
|
|
protected function handleCreateTestLanguage() {
|
|
$controller = new Vtiger_Tools_Console_TestLanguageController();
|
|
$controller->setArguments($this->arguments, $this->interactive)->handle();
|
|
}
|
|
|
|
protected function handleCreateLayout() {
|
|
$controller = new Vtiger_Tools_Console_LayoutController();
|
|
$controller->setArguments($this->arguments, $this->interactive)->handle();
|
|
}
|
|
|
|
protected function handleImportModule() {
|
|
$controller = new Vtiger_Tools_Console_ImportController();
|
|
$controller->setArguments($this->arguments, $this->interactive)->handle();
|
|
}
|
|
|
|
protected function handleUpdateModule() {
|
|
$controller = new Vtiger_Tools_Console_UpdateController();
|
|
$controller->setArguments($this->arguments, $this->interactive)->handle();
|
|
}
|
|
|
|
protected function handleRemoveModule() {
|
|
$controller = new Vtiger_Tools_Console_RemoveController();
|
|
$controller->setArguments($this->arguments, $this->interactive)->handle();
|
|
}
|
|
|
|
// Static
|
|
public static function run() {
|
|
$singleton = new self();
|
|
$singleton->handle();
|
|
}
|
|
}
|
|
|
|
class Vtiger_Tools_Console_ModuleController extends Vtiger_Tools_Console_Controller {
|
|
|
|
public function handle() {
|
|
echo ">>> MODULE <<<\n";
|
|
|
|
$moduleInformation = array();
|
|
do {
|
|
$moduleInformation['name'] = ucwords($this->prompt("Enter module name: ", self::PROMPT_NAME));
|
|
$module = $this->find($moduleInformation['name']);
|
|
if (!$module) {
|
|
break;
|
|
}
|
|
echo "ERROR: " . $moduleInformation['name'] . " module already exists, try another.\n";
|
|
} while (true);
|
|
|
|
$moduleInformation['entityfieldlabel'] = 'Name';
|
|
|
|
$entityfieldlabel = ucwords($this->prompt(sprintf("Entity field (%s): ",
|
|
$moduleInformation['entityfieldlabel']), self::PROMPT_OPTIONAL));
|
|
if ($entityfieldlabel) {
|
|
$moduleInformation['entityfieldlabel'] = $entityfieldlabel;
|
|
}
|
|
|
|
$moduleInformation['parent'] = 'Tools';
|
|
|
|
echo "Creating ...";
|
|
$this->create($moduleInformation);
|
|
echo "DONE.\n";
|
|
}
|
|
|
|
public function find($name) {
|
|
return Vtiger_Module::getInstance($name);
|
|
}
|
|
|
|
protected function create($moduleInformation) {
|
|
$moduleInformation['entityfieldname'] = strtolower($this->toAlphaNumeric($moduleInformation['entityfieldlabel']));
|
|
|
|
$module = new Vtiger_Module();
|
|
$module->name = $moduleInformation['name'];
|
|
$module->parent=$moduleInformation['parent'];
|
|
$module->save();
|
|
|
|
$module->initTables();
|
|
|
|
$block = new Vtiger_Block();
|
|
$block->label = 'LBL_'. strtoupper($module->name) . '_INFORMATION';
|
|
$module->addBlock($block);
|
|
|
|
$blockcf = new Vtiger_Block();
|
|
$blockcf->label = 'LBL_CUSTOM_INFORMATION';
|
|
$module->addBlock($blockcf);
|
|
|
|
$field1 = new Vtiger_Field();
|
|
$field1->name = $moduleInformation['entityfieldname'];
|
|
$field1->label= $moduleInformation['entityfieldlabel'];
|
|
$field1->uitype= 2;
|
|
$field1->column = $field1->name;
|
|
$field1->columntype = 'VARCHAR(255)';
|
|
$field1->typeofdata = 'V~M';
|
|
$block->addField($field1);
|
|
|
|
$module->setEntityIdentifier($field1);
|
|
|
|
/** Common fields that should be in every module, linked to vtiger CRM core table */
|
|
$field2 = new Vtiger_Field();
|
|
$field2->name = 'assigned_user_id';
|
|
$field2->label = 'Assigned To';
|
|
$field2->table = 'vtiger_crmentity';
|
|
$field2->column = 'smownerid';
|
|
$field2->uitype = 53;
|
|
$field2->typeofdata = 'V~M';
|
|
$block->addField($field2);
|
|
|
|
$field3 = new Vtiger_Field();
|
|
$field3->name = 'createdtime';
|
|
$field3->label= 'Created Time';
|
|
$field3->table = 'vtiger_crmentity';
|
|
$field3->column = 'createdtime';
|
|
$field3->uitype = 70;
|
|
$field3->typeofdata = 'T~O';
|
|
$field3->displaytype= 2;
|
|
$block->addField($field3);
|
|
|
|
$field4 = new Vtiger_Field();
|
|
$field4->name = 'modifiedtime';
|
|
$field4->label= 'Modified Time';
|
|
$field4->table = 'vtiger_crmentity';
|
|
$field4->column = 'modifiedtime';
|
|
$field4->uitype = 70;
|
|
$field4->typeofdata = 'T~O';
|
|
$field4->displaytype= 2;
|
|
$block->addField($field4);
|
|
|
|
// Create default custom filter (mandatory)
|
|
$filter1 = new Vtiger_Filter();
|
|
$filter1->name = 'All';
|
|
$filter1->isdefault = true;
|
|
$module->addFilter($filter1);
|
|
// Add fields to the filter created
|
|
$filter1->addField($field1)->addField($field2, 1)->addField($field3, 2);
|
|
|
|
// Set sharing access of this module
|
|
$module->setDefaultSharing();
|
|
|
|
// Enable and Disable available tools
|
|
$module->enableTools(Array('Import', 'Export', 'Merge'));
|
|
|
|
// Initialize Webservice support
|
|
$module->initWebservice();
|
|
|
|
// Create files
|
|
$this->createFiles($module, $field1);
|
|
|
|
// Link to menu
|
|
Settings_MenuEditor_Module_Model::addModuleToApp($module->name, $module->parent);
|
|
}
|
|
|
|
protected function createFiles(Vtiger_Module $module, Vtiger_Field $entityField) {
|
|
$targetpath = 'modules/' . $module->name;
|
|
|
|
if (!is_file($targetpath)) {
|
|
mkdir($targetpath);
|
|
mkdir($targetpath . '/language');
|
|
|
|
$templatepath = 'vtlib/ModuleDir/6.0.0';
|
|
|
|
$moduleFileContents = file_get_contents($templatepath . '/ModuleName.php');
|
|
$replacevars = array(
|
|
'ModuleName' => $module->name,
|
|
'<modulename>' => strtolower($module->name),
|
|
'<entityfieldlabel>' => $entityField->label,
|
|
'<entitycolumn>' => $entityField->column,
|
|
'<entityfieldname>' => $entityField->name,
|
|
);
|
|
|
|
foreach ($replacevars as $key => $value) {
|
|
$moduleFileContents = str_replace($key, $value, $moduleFileContents);
|
|
}
|
|
file_put_contents($targetpath.'/'.$module->name.'.php', $moduleFileContents);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
class Vtiger_Tools_Console_LayoutController extends Vtiger_Tools_Console_Controller {
|
|
|
|
// Similar grouped patterns to identify the line on which tpl filename is specified.
|
|
const VIEWERREGEX = '/\$viewer->(view|fetch)[^\(]*\(([^\)]+)/';
|
|
const RETURNTPLREGEX = '/(return)([ ]+[\'"]+[^;]+)/';
|
|
|
|
const TPLREGEX = '/[\'"]([^\'"]+)/';
|
|
|
|
public function handle() {
|
|
echo ">>> LAYOUT <<<\n";
|
|
|
|
$layoutInformation = array();
|
|
do {
|
|
$layoutInformation['name'] = strtolower($this->prompt("Enter layout name: ", self::PROMPT_NAME));
|
|
if (!file_exists( 'layouts/' . $layoutInformation['name'])) {
|
|
break;
|
|
}
|
|
echo "ERROR: " . $layoutInformation['name'] . " already exists, try another.\n";
|
|
} while (true);
|
|
|
|
echo "Creating ...";
|
|
$this->create($layoutInformation);
|
|
echo "DONE.\n";
|
|
}
|
|
|
|
protected function create($layoutInformation) {
|
|
$files = array();
|
|
$this->findFiles( 'includes', '.php$', $files);
|
|
$this->findFiles( 'modules', '.php$', $files);
|
|
|
|
$layoutdir = 'layouts/' . $layoutInformation['name'] . '/';
|
|
|
|
foreach ($files as $file) {
|
|
$tplfolder = $layoutdir . "modules/Vtiger";
|
|
if (preg_match("/modules\/([^\/]+)\/([^\/]+)\//", $file, $fmatch)) {
|
|
$tplfolder = $layoutdir . "modules/" . $fmatch[1];
|
|
if ($fmatch[1] == 'Settings') {
|
|
$tplfolder .= '/' . $fmatch[2];
|
|
}
|
|
}
|
|
|
|
$tpls = array();
|
|
$this->findTemplateNames($file, $tpls);
|
|
$tpls = array_unique($tpls);
|
|
|
|
if ($tpls) {
|
|
foreach ($tpls as $tpl) {
|
|
$tplname = basename($tpl, true);
|
|
// Fix sub-directory path
|
|
$tplpath = $tplfolder . '/'. substr($tpl, 0, strpos($tpl, $tplname));
|
|
if (!file_exists($tplpath)) {
|
|
mkdir($tplpath, 0755, true);
|
|
}
|
|
if (!file_exists($tplpath.$tplname)) {
|
|
$initialContent = "{* License Text *}\n";
|
|
// Enable debug to make it easy to implement.
|
|
$initialContent.= "{debug}{* REMOVE THIS LINE AFTER IMPLEMENTATION *}\n\n";
|
|
file_put_contents($tplpath.$tplname, $initialContent);
|
|
}
|
|
file_put_contents($tplpath.$tplname, "{* $file *}\n", FILE_APPEND);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected function findTemplateNames($file, &$tpls, $inreturn=false) {
|
|
$contents = file_get_contents($file);
|
|
|
|
$regex = $inreturn ? self::RETURNTPLREGEX : self::VIEWERREGEX;
|
|
if (preg_match_all($regex, $contents, $matches)) {
|
|
foreach ($matches[2] as $match) {
|
|
if (preg_match(self::TPLREGEX, $match, $matches2)) {
|
|
if (stripos($matches2[1], '.tpl') !== false) {
|
|
$tpls[] = $matches2[1];
|
|
}
|
|
}
|
|
}
|
|
// Viewer files can also have return tpl calls - find them.
|
|
if (!$inreturn) {
|
|
$this->findTemplateNames($file, $tpls, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class Vtiger_Tools_Console_LanguageController extends Vtiger_Tools_Console_Controller {
|
|
|
|
const BASE_LANG_PREFIX = 'en_us';
|
|
|
|
public function handle() {
|
|
echo ">>> LANGUAGE <<<\n";
|
|
|
|
$languageInformation = array();
|
|
do {
|
|
$languageInformation['prefix'] = strtolower($this->prompt("Enter (languagecode_countrycode): ", self::PROMPT_NAME));
|
|
if (!file_exists( 'languages/' . $languageInformation['prefix'])) {
|
|
break;
|
|
}
|
|
echo "ERROR: " . $languageInformation['prefix'] . " already exists, try another.\n";
|
|
} while (true);
|
|
|
|
echo "Creating ...";
|
|
$this->create($languageInformation);
|
|
echo "DONE.\n";
|
|
}
|
|
|
|
protected function create($languageInformation) {
|
|
$files = array();
|
|
$this->findFiles( 'languages/'.self::BASE_LANG_PREFIX, '.php$', $files);
|
|
|
|
foreach ($files as $file) {
|
|
$filename = basename($file, true);
|
|
$dir = substr($file, 0, strpos($file, $filename));
|
|
$dir = str_replace('languages/'.self::BASE_LANG_PREFIX, 'languages/'.$languageInformation['prefix'], $dir);
|
|
if (!file_exists($dir)) mkdir($dir);
|
|
|
|
if (isset($languageInformation['prefix_value'])) {
|
|
$contents = file_get_contents($file);
|
|
$contents = preg_replace("/(=>[^'\"]+['\"])(.*)/", sprintf('$1%s$2', $languageInformation['prefix_value']), $contents);
|
|
file_put_contents($dir.'/'.$filename, $contents);
|
|
} else {
|
|
copy($file, $dir.'/'.$filename);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected function deploy($languageInformation) {
|
|
if (!isset($languageInformation['label'])) {
|
|
echo "Language label not specified.";
|
|
return;
|
|
}
|
|
|
|
$db = PearDatabase::getInstance();
|
|
$check = $db->pquery('SELECT 1 FROM vtiger_language WHERE prefix=?', $languageInformation['prefix']);
|
|
if ($check && $db->num_rows($check)) {
|
|
;
|
|
} else {
|
|
$db->pquery('INSERT INTO vtiger_language (id,name,prefix,label,lastupdated,isdefault,active) VALUES(?,?,?,?,?,?,?)',
|
|
array($db->getUniqueId('vtiger_language'), $languageInformation['label'], $languageInformation['prefix'],
|
|
$languageInformation['label'], date('Y-m-d H:i:s'), 0, 1));
|
|
}
|
|
}
|
|
}
|
|
|
|
class Vtiger_Tools_Console_TestLanguageController extends Vtiger_Tools_Console_LanguageController {
|
|
|
|
public function handle() {
|
|
echo ">>> TEST LANGUAGE <<<\n";
|
|
|
|
$languageInformation = array('label' => 'TEST', 'prefix' => 'te_st', 'prefix_value' => '✔ ');
|
|
|
|
echo "Creating ...";
|
|
$this->create($languageInformation);
|
|
echo "DONE\n";
|
|
|
|
echo "Deploying ...";
|
|
$this->deploy($languageInformation);
|
|
echo "DONE.\n";
|
|
}
|
|
}
|
|
|
|
class Vtiger_Tools_Console_ImportController extends Vtiger_Tools_Console_Controller {
|
|
|
|
public function handle() {
|
|
if ($this->interactive) {
|
|
echo ">>> IMPORT MODULE <<<\n";
|
|
do {
|
|
$path = $this->prompt("Enter package path: ", self::PROMPT_PATH);
|
|
if (file_exists($path)) {
|
|
break;
|
|
}
|
|
echo "ERROR: " . $path . " - file not found, try another.\n";
|
|
} while (true);
|
|
} else {
|
|
$path = array_shift($this->arguments);
|
|
}
|
|
|
|
if (file_exists($path)) {
|
|
$package = new Vtiger_Package();
|
|
$module = $package->getModuleNameFromZip($path);
|
|
|
|
$moduleInstance = Vtiger_Module::getInstance($module);
|
|
if ($moduleInstance) {
|
|
echo "ERROR: Module $module already exists!\n";
|
|
} else {
|
|
echo "Importing ...";
|
|
$package->import($path);
|
|
echo "DONE.\n";
|
|
}
|
|
|
|
} else {
|
|
throw new Exception("Package file $path not found.");
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
class Vtiger_Tools_Console_UpdateController extends Vtiger_Tools_Console_Controller {
|
|
|
|
public function handle() {
|
|
if ($this->interactive) {
|
|
echo ">>> UPDATE MODULE <<<\n";
|
|
do {
|
|
$path = $this->prompt("Enter package path: ", self::PROMPT_PATH);
|
|
if (file_exists($path)) {
|
|
break;
|
|
}
|
|
echo "ERROR: " . $path . " - file not found, try another.\n";
|
|
} while (true);
|
|
} else {
|
|
$path = array_shift($this->arguments);
|
|
}
|
|
|
|
if (file_exists($path)) {
|
|
$package = new Vtiger_Package();
|
|
$module = $package->getModuleNameFromZip($path);
|
|
|
|
$moduleInstance = Vtiger_Module::getInstance($module);
|
|
if (!$moduleInstance) {
|
|
echo "ERROR: Module $module not found!\n";
|
|
} else {
|
|
echo "Updating ...";
|
|
$package->update($moduleInstance, $path);
|
|
echo "DONE.\n";
|
|
}
|
|
|
|
} else {
|
|
throw new Exception("Package file $path not found.");
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
class Vtiger_Tools_Console_RemoveController extends Vtiger_Tools_Console_Controller {
|
|
|
|
public function handle() {
|
|
if ($this->interactive) {
|
|
echo ">>> REMOVE MODULE <<<\n";
|
|
do {
|
|
$module = $this->prompt("Enter module name: ", self::PROMPT_NAME);
|
|
$moduleInstance = Vtiger_Module::getInstance($module);
|
|
if (!$moduleInstance) {
|
|
echo "ERROR: Module $module not found, try another.\n";
|
|
} else {
|
|
echo "Removing ...";
|
|
$moduleInstance->delete();
|
|
echo "DONE.\n";
|
|
}
|
|
} while (true);
|
|
} else {
|
|
$module = array_shift($this->arguments);
|
|
$moduleInstance = Vtiger_Module::getInstance($module);
|
|
if (!$moduleInstance) {
|
|
echo "ERROR: Module $module not found!\n";
|
|
} else {
|
|
echo "Removing ...";
|
|
$moduleInstance->delete();
|
|
echo "DONE.\n";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (php_sapi_name() == 'cli') {
|
|
Vtiger_Tools_Console_Controller::run();
|
|
} else {
|
|
echo "Usage: php -f vtlib/tools/creator.php";
|
|
}
|