2021-04-28 20:10:26 +02:00

537 lines
15 KiB
Plaintext

<?php
/*************************************
* SPDX-FileCopyrightText: 2009-2020 Vtenext S.r.l. <info@vtenext.com>
* SPDX-License-Identifier: AGPL-3.0-only
************************************/
/* crmv@64542 */
/*@ DEL
This is a template file for the ModuleMaker
There are some special values in this file:
1. Any comment starting with /*@ is treated as a special comment and the next characters
on that line specifies a command:
DEL: remove all the comment until the closing tag (which has the @ sign as well)
REPLACE: the comment is replaced with a standard comment, with some other text inside
2. All the variables that begin with $TPL_ are replaced with other values/variables
@*/
/* Automatic installation script */
/* This file must be placed in the VTEROOT/storage/custom_modules folder */
// security check
if (php_sapi_name() != "cli" && !defined('MODULEMAKERSCRIPT')) {
throw new UninstallException("You are not allowed to invoke this script in this way!");
}
$configInc = '../../config.inc.php';
if (!is_readable($configInc)) throw new UninstallException("File $configInc not found. Ensure this file is in the storage/custom_modules folder");
/* Includes */
require($configInc);
chdir($root_directory);
require('vteversion.php'); // crmv@181168
require_once('include/utils/utils.php');
require_once('modules/Update/Update.php');
require_once('vtlib/Vtecrm/Utils.php');
require_once('vtlib/Vtecrm/Field.php');
require_once('vtlib/Vtecrm/Module.php');
/* Globals */
global $adb, $table_prefix, $vtlib_Utils_Log, $current_user;
$vtlib_Utils_Log = true;
// missing current_user, when invoking from CLI
if (!isset($current_user)) {
$current_user = CRMEntity::getInstance('Users');
$current_user->id = 1;
}
/* Script class */
$MMS = new ModuleMakerScriptUninstall();
/* Basic variables */
$module_name = $TPL_MODULENAME;
$module_dest_dir = "modules/$module_name";
/* Module variables */
// other fields to remove
$module_other_fields = $TPL_OTHERFIELDS;
// relations NtoN to remove
$module_relations = $TPL_RELATIONS;
// relations 1toN to existing fields to remove
$module_addrelations = $TPL_ADDRELATIONS;
// labels to remove
$labels = $TPL_LANGUAGES;
// ----------------- SCRIPT START -------------------
// check destination directory
if (!is_writable('modules')) {
throw new InstallException("The modules folder is not writable, please fix the permissions");
}
// instance to the module
$newModule = Vtecrm_Module::getInstance($module_name);
// remove other labels
if (is_array($labels)) {
foreach ($labels as $module=>$modlang) {
foreach ($modlang as $lang=>$translist) {
foreach ($translist as $label=>$translabel) {
SDK::deleteLanguageEntry($module, $lang, $label);
}
}
}
}
// relations NtoN (couple of related lists)
if (is_array($module_relations)) {
foreach ($module_relations as $rel) {
$relinst = Vtecrm_Module::getInstance($rel['module']);
if ($relinst) {
if (!empty($rel['function'])) {
$func = $rel['function'];
} elseif ($rel['module'] == 'Messages') {
$func = 'get_messages_list';
} elseif ($rel['module'] == 'Documents') {
$func = 'get_attachments';
} else {
$func = 'get_related_list';
}
// check the inverse relation function
if (!empty($rel['reverse_function'])) {
$func2 = $rel['reverse_function'];
} elseif ($rel['module'] == 'Documents') {
$func2 = 'get_documents_dependents_list';
} else {
$func2 = $func;
}
$relinst->unsetRelatedList($newModule, $module_name, $func2);
if ($newModule) $newModule->unsetRelatedList($relinst, $rel['module'], $func);
}
}
}
// relations 1toN with fields not to remove
if (is_array($module_addrelations)) {
foreach ($module_addrelations as $rel) {
$relinst = Vtecrm_Module::getInstance($rel['module']);
if ($relinst) {
$label = $rel['module'];
if (!empty($rel['function'])) {
$func = $rel['function'];
} elseif ($rel['module'] == 'Calendar') {
$func = 'get_activities';
} else {
$func = 'get_dependents_list';
}
if ($func == 'get_activities') {
$label = 'Activities';
}
if ($newModule) $newModule->unsetRelatedList($relinst, $label, $func);
if ($rel['field']) {
$field = @Vtecrm_Field::getInstance($rel['field'], $relinst);
if ($field) {
$field->unsetRelatedModules(array($module_name));
}
}
}
}
}
// remove other modules fields
if (is_array($module_other_fields)) {
foreach ($module_other_fields as $field) {
$name = $field['name'];
$mod = $field['module'];
$modInst = Vtecrm_Module::getInstance($mod);
if (empty($modInst)) {
$MMS->warn("The module $mod was not found while removing the field $name");
continue;
}
$field = @Vtecrm_Field::getInstance($name, $modInst);
if ($field) {
$field->delete();
}
}
}
// now delete all
$r = $MMS->deleteModuleData($module_name);
if (!$r) throw new UninstallException("Error during module removal");
$r = $MMS->deleteModuleFiles($module_name);
if (!$r) throw new UninstallException("Error during module files removal");
// and just to be sure, regenerate the tabdata
create_tab_data_file();
// crmv@104956
// clear some caches
if (class_exists('Cache')) {
$cache = Cache::getInstance('getQuickCreateModules');
if ($cache) $cache->clear();
}
// crmv@104956e
// ----------------------------------- CLASSES ---------------------------------------
class UninstallException extends Exception { }
// some useful functions for the uninstall
class ModuleMakerScriptUninstall {
public $enableLog = true;
protected static $dbtables = array(); // crmv@167431
public function warn($s) {
return $this->log('[WARNING] '.$s);
}
public function log($s) {
if ($this->enableLog) echo $s."\n";
}
/**
* Remove ALL the record and metadata for the specified custom module
*/
public function deleteModuleData($modname) {
global $adb, $table_prefix;
// crmv@108984
$module = Vtecrm_Module::getInstance($modname);
if ($module) {
$tabid = $module->id;
} else {
$tabid = getTabid($modname);
}
// crmv@69568
// remove some accessory data
$this->deleteReports($modname);
$this->deleteWorkflows($modname);
$this->deletePicklists($modname);
// crmv@69568e
// remove the module tables
$this->dropModuleTables($modname);
// delete module with vtlib
if ($module) $module->__delete();
// crmv@108984e
// removes all the rows with references to this module
if ($tabid > 0) {
$this->deleteRowsWithColumnValue('tabid', $tabid, true);
}
$this->deleteRowsWithColumnValue('entitytype', $modname, false);
$this->deleteRowsWithColumnValue('entity_type', $modname, false);
$this->deleteRowsWithColumnValue('setype', $modname, false);
$this->deleteRowsWithColumnValue('module', $modname, false);
$this->deleteRowsWithColumnValue('relmodule', $modname, false);
$this->deleteRowsWithColumnValue('semodule', $modname, false);
// crmv@80117
if (Vtecrm_Utils::CheckTable($table_prefix.'_fieldformulas')) {
$adb->pquery("DELETE FROM {$table_prefix}_fieldformulas WHERE modulename = ?", array($modname));
}
// crmv@80117e
//crmv@85638 - these shouldn't be necessary, but sometimes the deleteRowsWithColumnValue doesn't work (?)
if ($tabid > 0) {
$adb->pquery("DELETE FROM {$table_prefix}_profile2standardperm WHERE tabid = ?", array($tabid));
$adb->pquery("DELETE FROM {$table_prefix}_org_share_action2tab WHERE tabid = ?", array($tabid));
}
//crmv@85638e
// TODO: maybe there are other tables/columns?
return true;
}
// crmv@69568
//crmv@95610
// delete all the workflows with this primary module
protected function deleteWorkflows($modname) {
global $adb, $table_prefix;
require_once("modules/com_workflow/VTWorkflowApplication.inc");
require_once("modules/com_workflow/VTWorkflowManager.inc");
require_once("modules/com_workflow/VTWorkflowUtils.php");
$this->log('Deleting all workflows...');
$res = $adb->pquery("SELECT w.workflow_id
FROM com_{$table_prefix}_workflows w
WHERE w.module_name = ?", array($modname));
if ($res && $adb->num_rows($res) > 0) {
$wm = new VTWorkflowManager($adb);
while ($row = $adb->FetchByAssoc($res, -1, false)) {
$id = intval($row['workflow_id']);
if ($id > 0) $wm->delete($id);
}
}
}
//crmv@95610e
// crmv@99794
// delete all the reports with this primary module
protected function deleteReports($modname) {
global $adb, $table_prefix;
require_once('modules/Reports/Reports.php');
$reports = Reports::getInstance();
$this->log('Deleting all reports...');
$res = $adb->pquery("SELECT r.reportid
FROM {$table_prefix}_report r
INNER JOIN {$table_prefix}_reportconfig rc ON rc.reportid = r.reportid
WHERE rc.module = ?", array($modname));
if ($res && $adb->num_rows($res) > 0) {
while ($row = $adb->FetchByAssoc($res, -1, false)) {
$id = intval($row['reportid']);
if ($id > 0) $reports->deleteReport($id);
}
}
}
// crmv@99794e
protected function deletePicklists($modname) {
global $adb, $table_prefix;
$this->log('Dropping all picklist values and tables...');
// search for picklist fields and remove their tables
$res = $adb->pquery("SELECT fieldid, fieldname, p.picklistid
FROM {$table_prefix}_field f
INNER JOIN {$table_prefix}_tab t ON t.tabid = f.tabid
LEFT JOIN {$table_prefix}_picklist p ON p.name = f.fieldname
WHERE t.name = ? AND f.uitype IN (?,?,?,?) ", array($modname, 15, 16, 1015, 300));
if ($res && $adb->num_rows($res) > 0) {
while ($row = $adb->FetchByAssoc($res, -1, false)) {
$fieldid = $row['fieldid'];
$pickid = intval($row['picklistid']);
// delete the values
if ($pickid > 0) {
$adb->pquery("DELETE FROM {$table_prefix}_picklist WHERE picklistid = ?", array($pickid));
$adb->pquery("DELETE FROM {$table_prefix}_role2picklist WHERE picklistid = ?", array($pickid));
}
// drop the tables
$tab = $table_prefix.'_'.$row['fieldname'];
if (Vtecrm_Utils::CheckTable($tab)) {
$dropq = $adb->datadict->DropTableSQL($tab);
$adb->datadict->ExecuteSQLArray($dropq);
}
$tab_seq = $table_prefix.'_'.$row['fieldname'].'_seq';
if (Vtecrm_Utils::CheckTable($tab_seq)) {
$dropq = $adb->datadict->DropTableSQL($tab_seq);
$adb->datadict->ExecuteSQLArray($dropq);
}
}
}
}
// crmv@69568e
protected function dropModuleTables($modname) {
global $adb, $table_prefix;
$this->log('Dropping module tables...');
$tables = array();
@include_once("modules/$modname/{$modname}.php");
if (class_exists($modname)) {
$instance = CRMEntity::getInstance($modname);
if ($instance) {
$tables[] = $instance->table_name;
$tables[] = $instance->customFieldTable[0];
}
}
$tables = array_filter($tables);
if (count($tables) > 0) {
foreach ($tables as $modTable) {
if (Vtecrm_Utils::CheckTable($modTable)) {
$dropq = $adb->datadict->DropTableSQL($modTable);
$adb->datadict->ExecuteSQLArray($dropq);
}
}
}
return true;
}
/**
* Delete all the files for the specified custom module.
*/
public function deleteModuleFiles($modname) {
global $adb, $table_prefix;
$this->log('Removing module files...');
$this->rrmdir("modules/$modname");
$this->rrmdir("cron/modules/$modname");
$this->rrmdir("Smarty/templates/modules/$modname");
return true;
}
protected function rrmdir($dir) {
if (is_dir($dir)) {
$objects = scandir($dir);
foreach ($objects as $object) {
if ($object != "." && $object != "..") {
$subpath = $dir.DIRECTORY_SEPARATOR.$object;
if (filetype($subpath) == "dir") $this->rrmdir($subpath); else unlink($subpath);
}
}
reset($objects);
rmdir($dir);
} elseif (is_file($dir)) {
unlink($dir);
}
}
protected function deleteRowsWithColumnValue($column, $value, $fuzzy = false) {
global $adb, $table_prefix;
$list = $this->getTablesWithColumn($column, $fuzzy);
if ($list && is_array($list)) {
foreach ($list as $table) {
if ($fuzzy) {
list($tablename, $columnname) = explode('.', $table, 2);
} else {
$tablename = $table;
$columnname = $column;
}
$adb->format_columns($columnname);
$q = "DELETE FROM $tablename WHERE $columnname = ?";
$adb->pquery($q, array($value));
}
}
return true;
}
// use only alphanumeric names for the column
protected function getTablesWithColumn($column, $fuzzy = false) {
global $adb, $dbconfig;
$dbname = $dbconfig['db_name'];
$params = array();
// crmv@167431
// crmv@73504
if ($adb->isMysql()) {
// use a faster method
$list = array();
$tables = $this->getMysqlTables();
foreach ($tables as $tablename=>$tablefields) {
if ($fuzzy) {
foreach ($tablefields as $colfield) {
if (stripos($colfield,$column) !== false) $list[] = $tablename.'.'.$colfield;
}
} else {
if (in_array($column,$tablefields)) $list[] = $tablename;
}
}
return $list;
} elseif ($adb->isPostgres()) {
// crmv@167431e
// crmv@140072
if ($fuzzy) {
$query = "SELECT table_name, column_name FROM information_schema.columns INNER JOIN information_schema.tables USING (table_schema,table_name) WHERE table_schema = '$dbname' AND column_name LIKE '%$column%' AND table_type = 'BASE TABLE'";
} else {
$query = "SELECT table_name FROM information_schema.columns INNER JOIN information_schema.tables USING (table_schema,table_name) WHERE table_schema = '$dbname' AND column_name = ? AND table_type = 'BASE TABLE'";
$params = array($column);
}
// crmv@140072e
} elseif ($adb->isMssql()) {
if ($fuzzy) {
$query = "SELECT table_name, column_name FROM information_schema.columns WHERE table_catalog = '$dbname' AND table_schema = 'dbo' AND column_name LIKE '%$column%'";
} else {
$query = "SELECT table_name FROM information_schema.columns WHERE table_catalog = '$dbname' AND table_schema = 'dbo' AND column_name = ?";
$params = array($column);
}
// crmv@73504e
} elseif ($adb->isOracle()) {
if ($fuzzy) {
$query =
"SELECT object_name AS table_name, user_tab_cols.column_name
FROM user_objects
INNER JOIN user_tab_cols ON user_objects.object_name = user_tab_cols.table_name
WHERE user_objects.object_type = 'TABLE' AND user_tab_cols.column_name LIKE '%$column%'";
} else {
$query =
"SELECT object_name AS table_name
FROM user_objects
INNER JOIN user_tab_cols ON user_objects.object_name = user_tab_cols.table_name
WHERE user_objects.object_type = 'TABLE' AND user_tab_cols.column_name = ?";
$params = array(strtoupper($column));
}
} else {
// database type not supported
return false;
}
$list = array();
$res = $adb->pquery($query, $params);
if ($res && $adb->num_rows($res) > 0) {
while ($row = $adb->FetchByAssoc($res,-1, false)) {
if ($fuzzy) {
$col = $row['table_name'].'.'.$row['column_name'];
} else {
$col = $row['table_name'];
}
$list[] = $col;
}
}
return $list;
}
// crmv@167431
protected function getMysqlTables(){
global $adb;
if (empty(self::$dbtables)){
$res = $adb->query("SHOW TABLES");
if ($res && $adb->num_rows($res) > 0) {
while ($row = $adb->fetch_array_no_html($res)) {
$table = $row[0];
//get column definition
$tablefields = array();
$res1 = $adb->query("DESCRIBE `{$table}`"); // crmv@205759
if ($res1 && $adb->num_rows($res1) > 0) {
while ($row1 = $adb->fetch_array_no_html($res1)) {
$tablefields[]=$row1[0];
}
self::$dbtables[$table] = $tablefields;
}
}
}
}
return self::$dbtables;
}
// crmv@167431e
}