custom_fields->addField * 2. installing relationships - calls createTableParams to build the relationship table, and createRelationshipMeta to add the relationship to the relationship table * 3. rebuilding the relationships - at almost the last step in install(), calls modules/Administration/RebuildRelationship.php * 4. repair indices - uses "modules/Administration/RepairIndex.php"; */ require_once('include/utils/progress_bar_utils.php'); require_once('ModuleInstall/ModuleScanner.php'); define('DISABLED_PATH', 'Disabled'); class ModuleInstaller{ var $modules = array(); var $silent = false; var $base_dir = ''; var $modulesInPackage = array(); public $disabled_path = DISABLED_PATH; public $id_name; function ModuleInstaller(){ $this->ms = new ModuleScanner(); $this->modules = get_module_dir_list(); $this->db = & DBManagerFactory::getInstance(); include("ModuleInstall/extensions.php"); $this->extensions = $extensions; } /* * ModuleInstaller->install includes the manifest.php from the base directory it has been given. If it has been asked to do an upgrade it checks to see if there is * an upgrade_manifest defined in the manifest; if not it errors. It then adds the bean into the custom/Extension/application/Ext/Include/.php - sets beanList, beanFiles * and moduleList - and then calls ModuleInstaller->merge_files('Ext/Include', 'modules.ext.php', '', true) to merge the individual module files into a combined file * /custom/Extension/application/Ext/Include/modules.ext.php (which now contains a list of all $beanList, $beanFiles and $moduleList for all extension modules) - * this file modules.ext.php is included at the end of modules.php. * * Finally it runs over a list of defined tasks; then install_beans, then install_custom_fields, then clear the Vardefs, run a RepairAndClear, then finally call rebuild_relationships. */ function install($base_dir, $is_upgrade = false, $previous_version = ''){ if(defined('TEMPLATE_URL'))SugarTemplateUtilities::disableCache(); if ((defined('MODULE_INSTALLER_PACKAGE_SCAN') && MODULE_INSTALLER_PACKAGE_SCAN) || !empty($GLOBALS['sugar_config']['moduleInstaller']['packageScan'])) { $this->ms->scanPackage($base_dir); if($this->ms->hasIssues()){ $this->ms->displayIssues(); sugar_cleanup(true); } } // workaround for bug 45812 - refresh vardefs cache before unpacking to avoid partial vardefs in cache global $beanList; foreach ($this->modules as $module_name) { if (!empty($beanList[$module_name])) { $objectName = BeanFactory::getObjectName($module_name); VardefManager::loadVardef($module_name, $objectName); } } global $app_strings, $mod_strings; $this->base_dir = $base_dir; $total_steps = 5; //minimum number of steps with no tasks $current_step = 0; $tasks = array( 'pre_execute', 'install_copy', 'install_extensions', 'install_images', 'install_dcactions', 'install_dashlets', 'install_connectors', 'install_layoutfields', 'install_relationships', 'enable_manifest_logichooks', 'post_execute', 'reset_opcodes', ); $total_steps += count($tasks); if(file_exists($this->base_dir . '/manifest.php')){ if(!$this->silent){ $current_step++; display_progress_bar('install', $current_step, $total_steps); echo ''; } if(!$this->silent){ $current_step++; update_progress_bar('install', $current_step, $total_steps); echo ''; } $selectedActions = array( 'clearTpls', 'clearJsFiles', 'clearDashlets', 'clearVardefs', 'clearJsLangFiles', 'rebuildAuditTables', 'repairDatabase', ); VardefManager::clearVardef(); global $beanList, $beanFiles, $moduleList; if (file_exists('custom/application/Ext/Include/modules.ext.php')) { include('custom/application/Ext/Include/modules.ext.php'); } require_once("modules/Administration/upgrade_custom_relationships.php"); upgrade_custom_relationships($this->installed_modules); $this->rebuild_all(true); require_once('modules/Administration/QuickRepairAndRebuild.php'); $rac = new RepairAndClear(); $rac->repairAndClearAll($selectedActions, $this->installed_modules,true, false); $this->rebuild_relationships(); UpdateSystemTabs('Add',$this->tab_modules); //Clear out all the langauge cache files. clearAllJsAndJsLangFilesWithoutOutput(); $cache_key = 'app_list_strings.'.$GLOBALS['current_language']; sugar_cache_clear($cache_key ); sugar_cache_reset(); //clear the unified_search_module.php file require_once('modules/Home/UnifiedSearchAdvanced.php'); UnifiedSearchAdvanced::unlinkUnifiedSearchModulesFile(); $this->log('
' . translate('LBL_MI_COMPLETE') . ''); }else{ die("No \$installdefs Defined In $this->base_dir/manifest.php"); } } function install_user_prefs($module, $hide_from_user=false){ UserPreference::updateAllUserPrefs('display_tabs', $module, '', true, !$hide_from_user); UserPreference::updateAllUserPrefs('hide_tabs', $module, '', true, $hide_from_user); UserPreference::updateAllUserPrefs('remove_tabs', $module, '', true, $hide_from_user); } function uninstall_user_prefs($module){ UserPreference::updateAllUserPrefs('display_tabs', $module, '', true, true); UserPreference::updateAllUserPrefs('hide_tabs', $module, '', true, true); UserPreference::updateAllUserPrefs('remove_tabs', $module, '', true, true); } function pre_execute(){ require_once($this->base_dir . '/manifest.php'); if(isset($this->installdefs['pre_execute']) && is_array($this->installdefs['pre_execute'])){ foreach($this->installdefs['pre_execute'] as $includefile){ require_once(str_replace('', $this->base_dir, $includefile)); } } } function post_execute(){ require_once($this->base_dir . '/manifest.php'); if(isset($this->installdefs['post_execute']) && is_array($this->installdefs['post_execute'])){ foreach($this->installdefs['post_execute'] as $includefile){ require_once(str_replace('', $this->base_dir, $includefile)); } } } function pre_uninstall(){ require_once($this->base_dir . '/manifest.php'); if(isset($this->installdefs['pre_uninstall']) && is_array($this->installdefs['pre_uninstall'])){ foreach($this->installdefs['pre_uninstall'] as $includefile){ require_once(str_replace('', $this->base_dir, $includefile)); } } } function post_uninstall(){ require_once($this->base_dir . '/manifest.php'); if(isset($this->installdefs['post_uninstall']) && is_array($this->installdefs['post_uninstall'])){ foreach($this->installdefs['post_uninstall'] as $includefile){ require_once(str_replace('', $this->base_dir, $includefile)); } } } /* * ModuleInstaller->install_copy gets the copy section of installdefs in the manifest and calls copy_path to copy each path (file or directory) to its final location * (specified as from and to in the manifest), replacing by the base_dir value passed in to install. */ function install_copy(){ if(isset($this->installdefs['copy'])){ /* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:22:11 PM */ $backup_path = clean_path( remove_file_extension(urldecode($_REQUEST['install_file']))."-restore" ); /* END - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */ foreach($this->installdefs['copy'] as $cp){ $GLOBALS['log']->debug("Copying ..." . $cp['from']. " to " .$cp['to'] ); /* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:22:11 PM */ //$this->copy_path($cp['from'], $cp['to']); $this->copy_path($cp['from'], $cp['to'], $backup_path); /* END - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */ } //here we should get the module list again as we could have copied something to the modules dir $this->modules = get_module_dir_list(); } } function uninstall_copy(){ if(!empty($this->installdefs['copy'])){ foreach($this->installdefs['copy'] as $cp){ $cp['to'] = clean_path(str_replace('', $this->base_dir, $cp['to'])); $cp['from'] = clean_path(str_replace('', $this->base_dir, $cp['from'])); $GLOBALS['log']->debug('Unlink ' . $cp['to']); /* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:22:11 PM */ //rmdir_recursive($cp['to']); $backup_path = clean_path( remove_file_extension(urldecode(hashToFile($_REQUEST['install_file'])))."-restore/".$cp['to'] ); $this->uninstall_new_files($cp, $backup_path); $this->copy_path($backup_path, $cp['to'], $backup_path, true); /* END - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */ } $backup_path = clean_path( remove_file_extension(urldecode(hashToFile($_REQUEST['install_file'])))."-restore"); if(file_exists($backup_path)) rmdir_recursive($backup_path); } } /** * Removes any files that were added by the loaded module. If the files already existed prior to install * it will be handled by copy_path with the uninstall parameter. * */ function uninstall_new_files($cp, $backup_path){ $zip_files = $this->dir_get_files($cp['from'],$cp['from']); $backup_files = $this->dir_get_files($backup_path, $backup_path); foreach($zip_files as $k=>$v){ //if it's not a backup then it is probably a new file but we'll check that it is not in the md5.files first if(!isset($backup_files[$k])){ $to = $cp['to'] . $k; //if it's not a sugar file then we remove it otherwise we can't restor it if(!$this->ms->sugarFileExists($to)){ $GLOBALS['log']->debug('ModuleInstaller[uninstall_new_file] deleting file ' . $to); if(file_exists($to)) { unlink($to); } }else{ $GLOBALS['log']->fatal('ModuleInstaller[uninstall_new_file] Could not remove file ' . $to . ' as no backup file was found to restore to'); } } } //lets check if the directory is empty if it is we will delete it as well $files_remaining = $this->dir_file_count($cp['to']); if(file_exists($cp['to']) && $files_remaining == 0){ $GLOBALS['log']->debug('ModuleInstaller[uninstall_new_file] deleting directory ' . $cp['to']); rmdir_recursive($cp['to']); } } /** * Get directory where module's extensions go * @param string $module Module name */ public function getExtDir($module) { if($module == 'application') { return "custom/Extension/application/Ext"; } else { return "custom/Extension/modules/$module/Ext"; } } /** * Install file(s) into Ext/ part * @param string $section Name of the install file section * @param string $extname Name in Ext directory * @param string $module This extension belongs to a specific module */ public function installExt($section, $extname, $module = '') { if(isset($this->installdefs[$section])){ $this->log(sprintf(translate("LBL_MI_IN_EXT"), $section)); foreach($this->installdefs[$section] as $item){ if(isset($item['from'])) { $from = str_replace('', $this->base_dir, $item['from']); } else { $from = ''; } if(!empty($module)) { $item['to_module'] = $module; } $GLOBALS['log']->debug("Installing section $section from $from for " .$item['to_module'] ); if($item['to_module'] == 'application') { $path = "custom/Extension/application/Ext/$extname"; } else { $path = "custom/Extension/modules/{$item['to_module']}/Ext/$extname"; } if(!file_exists($path)){ mkdir_recursive($path, true); } if(isset($item["name"])) { $target = $item["name"]; } else if (!empty($from)){ $target = basename($from, ".php"); } else { $target = $this->id_name; } if(!empty($from)) { copy_recursive($from , "$path/$target.php"); } } } } /** * Uninstall file(s) into Ext/ part * @param string $section Name of the install file section * @param string $extname Name in Ext directory * @param string $module This extension belongs to a specific module */ public function uninstallExt($section, $extname, $module = '') { if(isset($this->installdefs[$section])){ $this->log(sprintf(translate("LBL_MI_UN_EXT"), $section)); foreach($this->installdefs[$section] as $item){ if(isset($item['from'])) { $from = str_replace('', $this->base_dir, $item['from']); } else { $from = ''; } if(!empty($module)) { $item['to_module'] = $module; } $GLOBALS['log']->debug("Uninstalling section $section from $from for " .$item['to_module'] ); if($item['to_module'] == 'application') { $path = "custom/Extension/application/Ext/$extname"; } else { $path = "custom/Extension/modules/{$item['to_module']}/Ext/$extname"; } if(isset($item["name"])) { $target = $item["name"]; } else if (!empty($from)){ $target = basename($from, ".php"); } else { $target = $this->id_name; } $disabled_path = $path.'/'.DISABLED_PATH; if (file_exists("$path/$target.php")) { rmdir_recursive("$path/$target.php"); } else if (file_exists("$disabled_path/$target.php")) { rmdir_recursive("$disabled_path/$target.php"); } else if (!empty($from) && file_exists($path . '/'. basename($from))) { rmdir_recursive( $path . '/'. basename($from)); } else if (!empty($from) && file_exists($disabled_path . '/'. basename($from))) { rmdir_recursive( $disabled_path . '/'. basename($from)); } } } } /** * Rebuild generic extension * @param string $ext Extension directory * @param string $filename Target filename */ public function rebuildExt($ext, $filename) { $this->log(translate('LBL_MI_REBUILDING') . " $ext..."); $this->merge_files("Ext/$ext/", $filename); } /** * Disable generic extension * @param string $section Install file section name * @param string $extname Extension directory * @param string $module This extension belongs to a specific module */ public function disableExt($section, $extname, $module = '') { if(isset($this->installdefs[$section])) { foreach($this->installdefs[$section] as $item) { if(isset($item['from'])) { $from = str_replace('', $this->base_dir, $item['from']); } else { $from = ''; } if(!empty($module)) { $item['to_module'] = $module; } $GLOBALS['log']->debug("Disabling $extname ... from $from for " .$item['to_module']); if($item['to_module'] == 'application') { $path = "custom/Extension/application/Ext/$extname"; } else { $path = "custom/Extension/modules/{$item['to_module']}/Ext/$extname"; } if(isset($item["name"])) { $target = $item["name"]; } else if (!empty($from)){ $target = basename($from, ".php"); }else { $target = $this->id_name; } $disabled_path = $path.'/'.DISABLED_PATH; if (file_exists("$path/$target.php")) { mkdir_recursive($disabled_path, true); rename("$path/$target.php", "$disabled_path/$target.php"); } else if (!empty($from) && file_exists($path . '/'. basename($from))) { mkdir_recursive($disabled_path, true); rename( $path . '/'. basename($from), $disabled_path.'/'. basename($from)); } } } } /** * Enable generic extension * @param string $section Install file section name * @param string $extname Extension directory * @param string $module This extension belongs to a specific module */ public function enableExt($section, $extname, $module = '') { if(isset($this->installdefs[$section])) { foreach($this->installdefs[$section] as $item) { if(isset($item['from'])) { $from = str_replace('', $this->base_dir, $item['from']); } else { $from = ''; } if(!empty($module)) { $item['to_module'] = $module; } $GLOBALS['log']->debug("Enabling $extname ... from $from for " .$item['to_module']); if($item['to_module'] == 'application') { $path = "custom/Extension/application/Ext/$extname"; } else { $path = "custom/Extension/modules/{$item['to_module']}/Ext/$extname"; } if(isset($item["name"])) { $target = $item["name"]; } else if (!empty($from)){ $target = basename($from, ".php"); } else { $target = $this->id_name; } if(!file_exists($path)) { mkdir_recursive($path, true); } $disabled_path = $path.'/'.DISABLED_PATH; if (file_exists("$disabled_path/$target.php")) { rename("$disabled_path/$target.php", "$path/$target.php"); } if (!empty($from) && file_exists($disabled_path . '/'. basename($from))) { rename($disabled_path.'/'. basename($from), $path . '/'. basename($from)); } } } } /** * Method removes module from global search configurations * * return bool */ public function uninstall_global_search() { if (empty($this->installdefs['beans'])) { return true; } if (is_file('custom/modules/unified_search_modules_display.php') == false) { return true; } $user = new User(); $users = get_user_array(); $unified_search_modules_display = array(); require('custom/modules/unified_search_modules_display.php'); foreach($this->installdefs['beans'] as $beanDefs) { if (array_key_exists($beanDefs['module'], $unified_search_modules_display) == false) { continue; } unset($unified_search_modules_display[$beanDefs['module']]); foreach($users as $userId => $userName) { if (empty($userId)) { continue; } $user->retrieve($userId); $prefs = $user->getPreference('globalSearch', 'search'); if (array_key_exists($beanDefs['module'], $prefs) == false) { continue; } unset($prefs[$beanDefs['module']]); $user->setPreference('globalSearch', $prefs, 0, 'search'); $user->savePreferencesToDB(); } } if (write_array_to_file("unified_search_modules_display", $unified_search_modules_display, 'custom/modules/unified_search_modules_display.php') == false) { global $app_strings; $msg = string_format($app_strings['ERR_FILE_WRITE'], array('custom/modules/unified_search_modules_display.php')); $GLOBALS['log']->error($msg); throw new Exception($msg); return false; } return true; } /** * Method enables module in global search configurations by disabled_module_visible key * * return bool */ public function enable_global_search() { if (empty($this->installdefs['beans'])) { return true; } if (is_file('custom/modules/unified_search_modules_display.php') == false) { return true; } $unified_search_modules_display = array(); require('custom/modules/unified_search_modules_display.php'); foreach($this->installdefs['beans'] as $beanDefs) { if (array_key_exists($beanDefs['module'], $unified_search_modules_display) == false) { continue; } if (isset($unified_search_modules_display[$beanDefs['module']]['disabled_module_visible']) == false) { continue; } $unified_search_modules_display[$beanDefs['module']]['visible'] = $unified_search_modules_display[$beanDefs['module']]['disabled_module_visible']; unset($unified_search_modules_display[$beanDefs['module']]['disabled_module_visible']); } if (write_array_to_file("unified_search_modules_display", $unified_search_modules_display, 'custom/modules/unified_search_modules_display.php') == false) { global $app_strings; $msg = string_format($app_strings['ERR_FILE_WRITE'], array('custom/modules/unified_search_modules_display.php')); $GLOBALS['log']->error($msg); throw new Exception($msg); return false; } return true; } /** * Method disables module in global search configurations by disabled_module_visible key * * return bool */ public function disable_global_search() { if (empty($this->installdefs['beans'])) { return true; } if (is_file('custom/modules/unified_search_modules_display.php') == false) { return true; } $unified_search_modules_display = array(); require('custom/modules/unified_search_modules_display.php'); foreach($this->installdefs['beans'] as $beanDefs) { if (array_key_exists($beanDefs['module'], $unified_search_modules_display) == false) { continue; } if (isset($unified_search_modules_display[$beanDefs['module']]['visible']) == false) { continue; } $unified_search_modules_display[$beanDefs['module']]['disabled_module_visible'] = $unified_search_modules_display[$beanDefs['module']]['visible']; $unified_search_modules_display[$beanDefs['module']]['visible'] = false; } if (write_array_to_file("unified_search_modules_display", $unified_search_modules_display, 'custom/modules/unified_search_modules_display.php') == false) { global $app_strings; $msg = string_format($app_strings['ERR_FILE_WRITE'], array('custom/modules/unified_search_modules_display.php')); $GLOBALS['log']->error($msg); throw new Exception($msg); return false; } return true; } public function install_extensions() { foreach($this->extensions as $extname => $ext) { $install = "install_$extname"; if(method_exists($this, $install)) { // non-standard function $this->$install(); } else { if(!empty($ext["section"])) { $module = isset($ext['module'])?$ext['module']:''; $this->installExt($ext["section"], $ext["extdir"], $module); } } } $this->rebuild_extensions(); } public function uninstall_extensions() { foreach($this->extensions as $extname => $ext) { $func = "uninstall_$extname"; if(method_exists($this, $func)) { // non-standard function $this->$func(); } else { if(!empty($ext["section"])) { $module = isset($ext['module'])?$ext['module']:''; $this->uninstallExt($ext["section"], $ext["extdir"], $module); } } } $this->rebuild_extensions(); } public function rebuild_extensions() { foreach($this->extensions as $extname => $ext) { $func = "rebuild_$extname"; if(method_exists($this, $func)) { // non-standard function $this->$func(); } else { $this->rebuildExt($ext["extdir"], $ext["file"]); } } } public function disable_extensions() { foreach($this->extensions as $extname => $ext) { $func = "disable_$extname"; if(method_exists($this, $func)) { // non-standard install $this->$func(); } else { if(!empty($ext["section"])) { $module = isset($ext['module'])?$ext['module']:''; $this->disableExt($ext["section"], $ext["extdir"], $module); } } } $this->rebuild_extensions(); } public function enable_extensions() { foreach($this->extensions as $extname => $ext) { $func = "enable_$extname"; if(method_exists($this, $func)) { // non-standard install $this->$func(); } else { if(!empty($ext["section"])) { $module = isset($ext['module'])?$ext['module']:''; $this->enableExt($ext["section"], $ext["extdir"], $module); } } } $this->rebuild_extensions(); } function install_dashlets() { if(isset($this->installdefs['dashlets'])){ foreach($this->installdefs['dashlets'] as $cp){ $this->log(translate('LBL_MI_IN_DASHLETS') . $cp['name']); $cp['from'] = str_replace('', $this->base_dir, $cp['from']); $path = 'custom/modules/Home/Dashlets/' . $cp['name'] . '/'; $GLOBALS['log']->debug("Installing Dashlet " . $cp['name'] . "..." . $cp['from'] ); if(!file_exists($path)){ mkdir_recursive($path, true); } copy_recursive($cp['from'] , $path); } include('modules/Administration/RebuildDashlets.php'); } } function uninstall_dashlets(){ if(isset($this->installdefs['dashlets'])){ foreach($this->installdefs['dashlets'] as $cp){ $this->log(translate('LBL_MI_UN_DASHLETS') . $cp['name']); $path = 'custom/modules/Home/Dashlets/' . $cp['name']; $GLOBALS['log']->debug('Unlink ' .$path); if (file_exists($path)) rmdir_recursive($path); } include('modules/Administration/RebuildDashlets.php'); } } function install_images(){ if(isset($this->installdefs['image_dir'])){ $this->log( translate('LBL_MI_IN_IMAGES') ); $this->copy_path($this->installdefs['image_dir'] , 'custom/themes'); } } function install_dcactions(){ if(isset($this->installdefs['dcaction'])){ $this->log(translate('LBL_MI_IN_MENUS')); foreach($this->installdefs['dcaction'] as $action){ $action['from'] = str_replace('', $this->base_dir, $action['from']); $GLOBALS['log']->debug("Installing DCActions ..." . $action['from']); $path = 'custom/Extension/application/Ext/DashletContainer/Containers'; if(!file_exists($path)){ mkdir_recursive($path, true); } copy_recursive($action['from'] , $path . '/'. $this->id_name . '.php'); } $this->rebuild_dashletcontainers(); } } function uninstall_dcactions(){ if(isset($this->installdefs['dcaction'])){ $this->log(translate('LBL_MI_UN_MENUS')); foreach($this->installdefs['dcaction'] as $action){ $action['from'] = str_replace('', $this->base_dir, $action['from']); $GLOBALS['log']->debug("Uninstalling DCActions ..." . $action['from'] ); $path = 'custom/Extension/application/Ext/DashletContainer/Containers'; if (sugar_is_file($path . '/'. $this->id_name . '.php', 'w')) { rmdir_recursive( $path . '/'. $this->id_name . '.php'); } else if (sugar_is_file($path . '/'. DISABLED_PATH . '/'. $this->id_name . '.php', 'w')) { rmdir_recursive( $path . '/'. DISABLED_PATH . '/'. $this->id_name . '.php'); } } $this->rebuild_dashletcontainers(); } } function install_connectors(){ if(isset($this->installdefs['connectors'])){ foreach($this->installdefs['connectors'] as $cp){ $this->log(translate('LBL_MI_IN_CONNECTORS') . $cp['name']); $dir = str_replace('_','/',$cp['name']); $cp['connector'] = str_replace('', $this->base_dir, $cp['connector']); $source_path = 'custom/modules/Connectors/connectors/sources/' . $dir. '/'; $GLOBALS['log']->debug("Installing Connector " . $cp['name'] . "..." . $cp['connector'] ); if(!file_exists($source_path)){ mkdir_recursive($source_path, true); } copy_recursive($cp['connector'] , $source_path); //Install optional formatter code if it is specified if(!empty($cp['formatter'])) { $cp['formatter'] = str_replace('', $this->base_dir, $cp['formatter']); $formatter_path = 'custom/modules/Connectors/connectors/formatters/' . $dir. '/'; if(!file_exists($formatter_path)){ mkdir_recursive($formatter_path, true); } copy_recursive($cp['formatter'] , $formatter_path); } } require_once('include/connectors/utils/ConnectorUtils.php'); ConnectorUtils::installSource($cp['name']); } } function uninstall_connectors(){ if(isset($this->installdefs['connectors'])){ foreach($this->installdefs['connectors'] as $cp){ $this->log(translate('LBL_MI_UN_CONNECTORS') . $cp['name']); $dir = str_replace('_','/',$cp['name']); $source_path = 'custom/modules/Connectors/connectors/sources/' . $dir; $formatter_path = 'custom/modules/Connectors/connectors/formatters/' . $dir; $GLOBALS['log']->debug('Unlink ' .$source_path); rmdir_recursive($source_path); rmdir_recursive($formatter_path); } require_once('include/connectors/utils/ConnectorUtils.php'); //ConnectorUtils::getConnectors(true); ConnectorUtils::uninstallSource($cp['name']); } } function install_vardef($from, $to_module) { $GLOBALS['log']->debug("Installing Vardefs ..." . $from . " for " .$to_module); $path = 'custom/Extension/modules/' . $to_module. '/Ext/Vardefs'; if($to_module == 'application'){ $path ='custom/Extension/' . $to_module. '/Ext/Vardefs'; } if(!file_exists($path)){ mkdir_recursive($path, true); } copy_recursive($from , $path.'/'. basename($from)); } function install_layoutdef($from, $to_module){ $GLOBALS['log']->debug("Installing Layout Defs ..." . $from . " for " .$to_module); $path = 'custom/Extension/modules/' . $to_module. '/Ext/Layoutdefs'; if($to_module == 'application'){ $path ='custom/Extension/' . $to_module. '/Ext/Layoutdefs'; } if(!file_exists($path)){ mkdir_recursive($path, true); } copy_recursive($from , $path.'/'. basename($from)); } // Non-standard - needs special rebuild call function install_languages() { $languages = array(); if(isset($this->installdefs['language'])) { $this->log(translate('LBL_MI_IN_LANG') ); foreach($this->installdefs['language'] as $packs) { $modules[]=$packs['to_module']; $languages[$packs['language']] = $packs['language']; $packs['from'] = str_replace('', $this->base_dir, $packs['from']); $GLOBALS['log']->debug("Installing Language Pack ..." . $packs['from'] . " for " .$packs['to_module']); $path = $this->getInstallLanguagesPath($packs); if (!file_exists(dirname($path))) { mkdir_recursive(dirname($path), true); } copy_recursive($packs['from'], $path); } $this->rebuild_languages($languages, $modules); } } /** * Function return path to file where store label * * @param $packs * @return string */ protected function getInstallLanguagesPath($packs) { $path = 'custom/Extension/modules/' . $packs['to_module']. '/Ext/Language'; if($packs['to_module'] == 'application'){ $path ='custom/Extension/' . $packs['to_module']. '/Ext/Language'; } $path .= '/'.$packs['language'].'.'. $this->id_name . '.php'; return $path; } // Non-standard, needs special rebuild function uninstall_languages(){ $languages = array(); if(isset($this->installdefs['language'])){ $this->log(translate('LBL_MI_UN_LANG') ); foreach($this->installdefs['language'] as $packs){ $modules[]=$packs['to_module']; $languages[$packs['language']] = $packs['language']; $packs['from'] = str_replace('', $this->base_dir, $packs['from']); $GLOBALS['log']->debug("Uninstalling Language Pack ..." . $packs['from'] . " for " .$packs['to_module']); $path = 'custom/Extension/modules/' . $packs['to_module']. '/Ext/Language'; if($packs['to_module'] == 'application'){ $path ='custom/Extension/' . $packs['to_module']. '/Ext/Language'; } if (sugar_is_file($path.'/'.$packs['language'].'.'. $this->id_name . '.php', 'w')) { rmdir_recursive( $path.'/'.$packs['language'].'.'. $this->id_name . '.php'); } else if (sugar_is_file($path.'/'.DISABLED_PATH.'/'.$packs['language'].'.'. $this->id_name . '.php', 'w')) { rmdir_recursive($path.'/'.DISABLED_PATH.'/'.$packs['language'].'.'. $this->id_name . '.php', 'w'); } } $this->rebuild_languages($languages, $modules); } } // Non-standard, needs special rebuild public function disable_languages() { if(isset($this->installdefs['language'])) { $languages = $modules = array(); foreach($this->installdefs['language'] as $item) { $from = str_replace('', $this->base_dir, $item['from']); $GLOBALS['log']->debug("Disabling Language {$item['language']}... from $from for " .$item['to_module']); $modules[]=$item['to_module']; $languages[$item['language']] = $item['language']; if($item['to_module'] == 'application') { $path = "custom/Extension/application/Ext/Language"; } else { $path = "custom/Extension/modules/{$item['to_module']}/Ext/Language"; } if(isset($item["name"])) { $target = $item["name"]; } else { $target = $this->id_name; } $target = "{$item['language']}.$target"; $disabled_path = $path.'/'.DISABLED_PATH; if (file_exists("$path/$target.php")) { mkdir_recursive($disabled_path, true); rename("$path/$target.php", "$disabled_path/$target.php"); } else if (file_exists($path . '/'. basename($from))) { mkdir_recursive($disabled_path, true); rename( $path . '/'. basename($from), $disabled_path.'/'. basename($from)); } } $this->rebuild_languages($languages, $modules); } } // Non-standard, needs special rebuild public function enable_languages() { if(isset($this->installdefs['language'])) { foreach($this->installdefs['language'] as $item) { $from = str_replace('', $this->base_dir, $item['from']); $GLOBALS['log']->debug("Enabling Language {$item['language']}... from $from for " .$item['to_module']); $modules[]=$item['to_module']; $languages[$item['language']] = $item['language']; if(!empty($module)) { $item['to_module'] = $module; } if($item['to_module'] == 'application') { $path = "custom/Extension/application/Ext/Language"; } else { $path = "custom/Extension/modules/{$item['to_module']}/Ext/Language"; } if(isset($item["name"])) { $target = $item["name"]; } else { $target = $this->id_name; } $target = "{$item['language']}.$target"; if(!file_exists($path)) { mkdir_recursive($path, true); } $disabled_path = $path.'/'.DISABLED_PATH; if (file_exists("$disabled_path/$target.php")) { rename("$disabled_path/$target.php", "$path/$target.php"); } if (file_exists($disabled_path . '/'. basename($from))) { rename($disabled_path.'/'. basename($from), $path . '/'. basename($from)); } } $this->rebuild_languages($languages, $modules); } } // Functions for adding and removing logic hooks from uploaded files // Since one class/file can be used by multiple logic hooks, I'm not going to touch the file labeled in the logic_hook entry /* The module hook definition should look like this: $installdefs = array( ... blah blah ... 'logic_hooks' => array( array('module' => 'Accounts', 'hook' => 'after_save', 'order' => 99, 'description' => 'Account sample logic hook', 'file' => 'modules/Sample/sample_account_logic_hook_file.php', 'class' => 'SampleLogicClass', 'function' => 'accountAfterSave', ), ), ... blah blah ... ); */ function enable_manifest_logichooks() { if(empty($this->installdefs['logic_hooks']) || !is_array($this->installdefs['logic_hooks'])) { return; } foreach($this->installdefs['logic_hooks'] as $hook ) { check_logic_hook_file($hook['module'], $hook['hook'], array($hook['order'], $hook['description'], $hook['file'], $hook['class'], $hook['function'])); } } function disable_manifest_logichooks() { if(empty($this->installdefs['logic_hooks']) || !is_array($this->installdefs['logic_hooks'])) { return; } foreach($this->installdefs['logic_hooks'] as $hook ) { remove_logic_hook($hook['module'], $hook['hook'], array($hook['order'], $hook['description'], $hook['file'], $hook['class'], $hook['function'])); } } /** * Check labels inside label files and remove them * * @param $basePath - path to files with labels * @param array $labelDefinitions - format like output from AbstractRelationship buildLabels() */ public function uninstallLabels($basePath, $labelDefinitions) { foreach ($labelDefinitions as $definition) { $filename = $basePath . "{$definition['module']}.php"; if (!file_exists($filename)) { continue; } $uninstalLabes = $this->getLabelsToUninstall($labelDefinitions); $this->uninstallLabel($uninstalLabes, $definition, $filename); } } /** * Check labels inside label file and remove them * * @param $uninstalLabes * @param $definition * @param $filename */ protected function uninstallLabel($uninstalLabes, $definition, $filename) { $app_list_strings = array(); $mod_strings = array(); $stringsName = $definition['module'] == 'application' ? 'app_list_strings' : 'mod_strings'; include($filename); if ('app_list_strings' == $stringsName) { $strings = $app_list_strings; } else { $strings = $mod_strings; } foreach ($uninstalLabes AS $label) { if (isset($strings[$label])) { unset($strings[$label]); } } if (count($strings)) { $this->saveContentToFile($filename, $stringsName, $strings); } else { unlink($filename); } } /** * Save labels that not need be uninstalled at this case * * @param $filename * @param $stringsName * @param $strings */ protected function saveContentToFile($filename, $stringsName, $strings) { $fileContent = " $val) { $fileContent .= override_value_to_string_recursive2($stringsName, $key, $val); } sugar_file_put_contents($filename, $fileContent); } /** * Uninstall extend labels * * @param $labelDefinitions */ public function uninstallExtLabels($labelDefinitions) { foreach ($labelDefinitions as $definition) { if (!isset($GLOBALS['sugar_config']['languages']) || !is_array($GLOBALS['sugar_config']['languages'])) { continue; } foreach (array_keys($GLOBALS['sugar_config']['languages']) AS $language) { $pathDef = array( 'language' => $language, 'to_module' => $definition['module'] ); $path = $this->getInstallLanguagesPath($pathDef); if (file_exists($path)) { unlink($path); } } } } /** * Returns the names of the label(key 'system_label') from a multi-dimensional array $labelDefinitions * * @param $labelDefinitions * @return array of labels */ protected function getLabelsToUninstall($labelDefinitions) { $labels = array(); foreach($labelDefinitions AS $definition){ $labels[] = $definition['system_label']; } return $labels; } /* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */ function copy_path($from, $to, $backup_path='', $uninstall=false){ //function copy_path($from, $to){ /* END - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */ $to = str_replace('', $this->base_dir, $to); if(!$uninstall) { $from = str_replace('', $this->base_dir, $from); $GLOBALS['log']->debug('Copy ' . $from); } else { $from = str_replace('', $backup_path, $from); //$GLOBALS['log']->debug('Restore ' . $from); } $from = clean_path($from); $to = clean_path($to); $dir = dirname($to); //there are cases where if we need to create a directory in the root directory if($dir == '.' && is_dir($from)){ $dir = $to; } if(!sugar_is_dir($dir, 'instance')) mkdir_recursive($dir, true); /* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */ if(empty($backup_path)) { /* END - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */ if(!copy_recursive($from, $to)){ die('Failed to copy ' . $from. ' ' . $to); } /* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */ } elseif(!$this->copy_recursive_with_backup($from, $to, $backup_path, $uninstall)){ die('Failed to copy ' . $from. ' to ' . $to); } /* END - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */ } function install_custom_fields($fields){ global $beanList, $beanFiles; include('include/modules.php'); require_once('modules/DynamicFields/FieldCases.php'); foreach($fields as $field){ $installed = false; if(isset($beanList[ $field['module']])){ $class = $beanList[ $field['module']]; if(!isset($field['ext4']))$field['ext4'] = ''; if(!isset($field['mass_update']))$field['mass_update'] = 0; if(!isset($field['duplicate_merge']))$field['duplicate_merge'] = 0; if(!isset($field['help']))$field['help'] = ''; //Merge contents of the sugar field extension if we copied one over if (file_exists("custom/Extension/modules/{$field['module']}/Ext/Vardefs/sugarfield_{$field['name']}.php")) { $dictionary = array(); include ("custom/Extension/modules/{$field['module']}/Ext/Vardefs/sugarfield_{$field['name']}.php"); $obj = BeanFactory::getObjectName($field['module']); if (!empty($dictionary[$obj]['fields'][$field['name']])) { $field = array_merge($dictionary[$obj]['fields'][$field['name']], $field); } } if(file_exists($beanFiles[$class])){ require_once($beanFiles[$class]); $mod = new $class(); $installed = true; $fieldObject = get_widget($field['type']); $fieldObject->populateFromRow($field); $mod->custom_fields->use_existing_labels = true; $mod->custom_fields->addFieldObject($fieldObject); } } if(!$installed){ $GLOBALS['log']->debug('Could not install custom field ' . $field['name'] . ' for module ' . $field['module'] . ': Module does not exist'); } } } function uninstall_custom_fields($fields){ global $beanList, $beanFiles; require_once('modules/DynamicFields/DynamicField.php'); $dyField = new DynamicField(); foreach($fields as $field){ $class = $beanList[ $field['module']]; if(file_exists($beanFiles[$class])){ require_once($beanFiles[$class]); $mod = new $class(); $dyField->bean = $mod; $dyField->module = $field['module']; $dyField->deleteField($field['name']); } } } /* * ModuleInstaller->install_relationships calls install_relationship for every file included in the module package that defines a relationship, and then * writes a custom/Extension/application/Ext/TableDictionary/$module.php file containing an include_once for every relationship metadata file passed to install_relationship. * Next it calls install_vardef and install_layoutdef. Finally, it rebuilds the vardefs and layoutdefs (by calling merge_files as usual), and then calls merge_files to merge * everything in 'Ext/TableDictionary/' into 'tabledictionary.ext.php' */ function install_relationships () { if (isset ( $this->installdefs [ 'relationships' ] )) { $this->log ( translate ( 'LBL_MI_IN_RELATIONSHIPS' ) ) ; $str = "installdefs [ 'relationships' ] as $key => $relationship ) { $filename = basename ( $relationship [ 'meta_data' ] ) ; $this->copy_path ( $relationship [ 'meta_data' ], 'custom/metadata/' . $filename ) ; $this->install_relationship ( 'custom/metadata/' . $filename ) ; $save_table_dictionary = true ; if (! empty ( $relationship [ 'module_vardefs' ] )) { $relationship [ 'module_vardefs' ] = str_replace ( '', $this->base_dir, $relationship [ 'module_vardefs' ] ) ; $this->install_vardef ( $relationship [ 'module_vardefs' ], $relationship [ 'module' ] ) ; } if (! empty ( $relationship [ 'module_layoutdefs' ] )) { $relationship [ 'module_layoutdefs' ] = str_replace ( '', $this->base_dir, $relationship [ 'module_layoutdefs' ] ) ; $this->install_layoutdef ( $relationship [ 'module_layoutdefs' ], $relationship [ 'module' ] ) ; } $relName = strpos($filename, "MetaData") !== false ? substr($filename, 0, strlen($filename) - 12) : $filename; $out = sugar_fopen ( "custom/Extension/application/Ext/TableDictionary/$relName.php", 'w' ) ; fwrite ( $out, $str . "include('custom/metadata/$filename');\n\n?>" ) ; fclose ( $out ) ; } Relationship::delete_cache(); $this->rebuild_vardefs () ; $this->rebuild_layoutdefs () ; if ($save_table_dictionary) { $this->rebuild_tabledictionary () ; } require_once("data/Relationships/RelationshipFactory.php"); SugarRelationshipFactory::deleteCache(); } } /* * Install_relationship obtains a set of relationship definitions from the filename passed in as a parameter. * For each definition it calls db->createTableParams to build the relationships table if it does not exist, * and SugarBean::createRelationshipMeta to add the relationship into the 'relationships' table. */ function install_relationship($file) { $_REQUEST['moduleInstaller'] = true; if(!file_exists($file)) { $GLOBALS['log']->debug( 'File does not exists : '.$file); return; } include($file); $rel_dictionary = $dictionary; foreach ($rel_dictionary as $rel_name => $rel_data) { $table = ''; // table is actually optional // check if we have a table definition - not all relationships require a join table if ( isset( $rel_data[ 'table' ] ) ) { $table = $rel_data[ 'table' ]; if(!$this->db->tableExists($table)) { $this->db->createTableParams($table, $rel_data[ 'fields' ], $rel_data[ 'indices' ]); } } if(!$this->silent) $GLOBALS['log']->debug("Processing relationship meta for ". $rel_name."..."); SugarBean::createRelationshipMeta($rel_name, $this->db,$table,$rel_dictionary,''); Relationship::delete_cache(); if(!$this->silent) $GLOBALS['log']->debug( 'done
'); } } function install_layoutfields() { if (!empty ( $this->installdefs [ 'layoutfields' ] )) { foreach ( $this->installdefs [ 'layoutfields' ] as $fieldSet ) { if (!empty($fieldSet['additional_fields'])) { $this->addFieldsToLayout($fieldSet['additional_fields']); } } } } function uninstall_layoutfields() { if (!empty ( $this->installdefs [ 'layoutfields' ] )) { foreach ( $this->installdefs [ 'layoutfields' ] as $fieldSet ) { if (!empty($fieldSet['additional_fields'])) { $this->removeFieldsFromLayout($fieldSet['additional_fields']); } } } } function uninstall_relationship($file, $rel_dictionary = null){ if ($rel_dictionary == null) { if(!file_exists($file)){ $GLOBALS['log']->debug( 'File does not exists : '.$file); return; } include($file); $rel_dictionary = $dictionary; } foreach ($rel_dictionary as $rel_name => $rel_data) { if (!empty($rel_data['table'])){ $table = $rel_data['table']; } else{ $table = ' One-to-Many '; } if ($this->db->tableExists($table) && isset($GLOBALS['mi_remove_tables']) && $GLOBALS['mi_remove_tables']) { SugarBean::removeRelationshipMeta($rel_name, $this->db,$table,$rel_dictionary,''); $this->db->dropTableName($table); if(!$this->silent) $this->log( translate('LBL_MI_UN_RELATIONSHIPS_DROP') . $table); } //Delete Layout defs // check to see if we have any vardef or layoutdef entries to remove - must have a relationship['module'] parameter if we do if (!isset($rel_data[ 'module' ])) $mods = array( $rel_data['relationships'][$rel_name]['lhs_module'], $rel_data['relationships'][$rel_name]['rhs_module'], ); else $mods = array($rel_data[ 'module' ]); $filename = "$rel_name.php"; foreach($mods as $mod) { if ($mod != 'application' ) { $basepath = "custom/Extension/modules/$mod/Ext/"; } else { $basepath = "custom/Extension/application/Ext/"; } foreach (array($filename , "custom" . $filename, $rel_name ."_". $mod. ".php") as $fn) { //remove any vardefs $path = $basepath . "Vardefs/$fn" ; if (file_exists( $path )) rmdir_recursive( $path ); //remove any layoutdefs $path = $basepath . "Layoutdefs/$fn" ; if( file_exists( $path )) { rmdir_recursive( $path ); } $path = $basepath . "WirelessLayoutdefs/$fn"; if (file_exists($path)) { rmdir_recursive($path); } } $relationships_path = 'custom/Extension/modules/relationships/'; $relationships_dirs = array( 'layoutdefs', 'vardefs', 'wirelesslayoutdefs' ); foreach ($relationships_dirs as $relationship_dir) { $realtionship_file_path = $relationships_path . $relationship_dir . "/{$rel_name}_{$mod}.php"; if (file_exists($realtionship_file_path)) { rmdir_recursive($realtionship_file_path); } } if (file_exists($relationships_path . "relationships/{$rel_name}MetaData.php")) { rmdir_recursive($relationships_path . "relationships/{$rel_name}MetaData.php"); } } foreach (array($filename , "custom" . $filename, $rel_name ."_". $mod. ".php") as $fn) { // remove the table dictionary extension if ( file_exists("custom/Extension/application/Ext/TableDictionary/$fn")) unlink("custom/Extension/application/Ext/TableDictionary/$fn"); if (file_exists("custom/metadata/{$rel_name}MetaData.php")) unlink( "custom/metadata/{$rel_name}MetaData.php" ); } } } function uninstall_relationships($include_studio_relationships = false){ $relationships = array(); //Find and remove studio created relationships. global $beanList, $beanFiles, $dictionary; //Load up the custom relationship definitions. if(file_exists('custom/application/Ext/TableDictionary/tabledictionary.ext.php')){ include('custom/application/Ext/TableDictionary/tabledictionary.ext.php'); } //Find all the relatioships/relate fields involving this module. $rels_to_remove = array(); foreach($beanList as $mod => $bean) { //Some modules like cases have a bean name that doesn't match the object name $bean = BeanFactory::getObjectName($mod); VardefManager::loadVardef($mod, $bean); //We can skip modules that are in this package as they will be removed anyhow if (!in_array($mod, $this->modulesInPackage) && !empty($dictionary[$bean]) && !empty($dictionary[$bean]['fields'])) { $field_defs = $dictionary[$bean]['fields']; foreach($field_defs as $field => $def) { //Weed out most fields first if (isset ($def['type'])) { //Custom relationships created in the relationship editor if ($def['type'] == "link" && !empty($def['relationship']) && !empty($dictionary[$def['relationship']])) { $rel_name = $def['relationship']; $rel_def = $dictionary[$rel_name]['relationships'][$rel_name]; //Check against mods to be removed. foreach($this->modulesInPackage as $removed_mod) { if ($rel_def['lhs_module'] == $removed_mod || $rel_def['rhs_module'] == $removed_mod ) { $dictionary[$rel_name]['from_studio'] = true; $relationships[$rel_name] = $dictionary[$rel_name]; } } } //Custom "relate" fields created in studio also need to be removed if ($def['type'] == 'relate' && isset($def['module'])) { foreach($this->modulesInPackage as $removed_mod) { if ($def['module'] == $removed_mod) { require_once 'modules/ModuleBuilder/Module/StudioModule.php' ; $studioMod = new StudioModule ( $mod ); $studioMod->removeFieldFromLayouts( $field ); if (isset($def['custom_module'])) { require_once ('modules/DynamicFields/DynamicField.php') ; require_once ($beanFiles [ $bean ]) ; $seed = new $bean ( ) ; $df = new DynamicField ( $mod ) ; $df->setup ( $seed ) ; //Need to load the entire field_meta_data for some field types $field_obj = $df->getFieldWidget($mod, $field); $field_obj->delete ( $df ) ; } } } } } } } } $this->uninstall_relationship(null, $relationships); if(isset($this->installdefs['relationships'])) { $relationships = $this->installdefs['relationships']; $this->log(translate('LBL_MI_UN_RELATIONSHIPS') ); foreach($relationships as $relationship) { // remove the metadata entry $filename = basename ( $relationship['meta_data'] ); $pathname = (file_exists("custom/metadata/$filename")) ? "custom/metadata/$filename" : "metadata/$filename" ; if(isset($GLOBALS['mi_remove_tables']) && $GLOBALS['mi_remove_tables']) $this->uninstall_relationship( $pathname ); if (file_exists($pathname)) unlink( $pathname ); } } if (file_exists("custom/Extension/application/Ext/TableDictionary/{$this->id_name}.php")) unlink("custom/Extension/application/Ext/TableDictionary/{$this->id_name}.php"); Relationship::delete_cache(); $this->rebuild_tabledictionary(); } function uninstall($base_dir){ if(defined('TEMPLATE_URL'))SugarTemplateUtilities::disableCache(); global $app_strings; $total_steps = 5; //min steps with no tasks $current_step = 0; $this->base_dir = $base_dir; $tasks = array( 'pre_uninstall', 'uninstall_relationships', 'uninstall_copy', 'uninstall_dcactions', 'uninstall_dashlets', 'uninstall_connectors', 'uninstall_layoutfields', 'uninstall_extensions', 'uninstall_global_search', 'disable_manifest_logichooks', 'post_uninstall', ); $total_steps += count($tasks); //now the real number of steps if(file_exists($this->base_dir . '/manifest.php')){ if(!$this->silent){ $current_step++; display_progress_bar('install', $current_step, $total_steps); echo ''; } //since we are passing $silent = true to rebuildAll() in that method it will set $this->silent = true, so //we need to save the setting to set it back after rebuildAll() completes. $silentBak = $this->silent; $this->rebuild_all(true); $this->silent = $silentBak; //#27877, If the request from MB redeploy a custom module , we will not remove the ACL actions for this package. if( !isset($_REQUEST['action']) || $_REQUEST['action']!='DeployPackage' ){ $this->remove_acl_actions(); } //end if(!$this->silent){ $current_step++; update_progress_bar('install', $current_step, $total_steps); echo ''; } UpdateSystemTabs('Restore',$installed_modules); //clear the unified_search_module.php file require_once('modules/Home/UnifiedSearchAdvanced.php'); UnifiedSearchAdvanced::unlinkUnifiedSearchModulesFile(); $this->log('
' . translate('LBL_MI_COMPLETE') . ''); if(!$this->silent){ update_progress_bar('install', $total_steps, $total_steps); } }else{ die("No manifest.php Defined In $this->base_dir/manifest.php"); } } function rebuild_languages($languages = array(), $modules="") { foreach($languages as $language=>$value){ $this->log(translate('LBL_MI_REBUILDING') . " Language...$language"); $this->merge_files('Ext/Language/', $language.'.lang.ext.php', $language); if($modules!=""){ foreach($modules as $module){ LanguageManager::clearLanguageCache($module, $language); } } } sugar_cache_reset(); } function rebuild_vardefs() { $this->rebuildExt("Vardefs", 'vardefs.ext.php'); sugar_cache_reset(); } function rebuild_dashletcontainers(){ $this->log(translate('LBL_MI_REBUILDING') . " DC Actions..."); $this->merge_files('Ext/DashletContainer/Containers/', 'dcactions.ext.php'); } function rebuild_tabledictionary() { $this->rebuildExt("TableDictionary", 'tabledictionary.ext.php'); } function rebuild_relationships() { if(!$this->silent) echo translate('LBL_MI_REBUILDING') . ' Relationships'; $_REQUEST['silent'] = true; global $beanFiles; include('include/modules.php'); include("modules/Administration/RebuildRelationship.php"); } function remove_acl_actions() { global $beanFiles, $beanList, $current_user; include('include/modules.php'); include("modules/ACL/remove_actions.php"); } /** * Wrapper call to modules/Administration/RepairIndex.php */ function repair_indices() { global $current_user,$beanFiles,$dictionary; $this->log(translate('LBL_MI_REPAIR_INDICES')); $_REQUEST['silent'] = true; // local var flagging echo'd output in repair script $_REQUEST['mode'] = 'execute'; // flag to just go ahead and run the script include("modules/Administration/RepairIndex.php"); } /** * Rebuilds the extension files found in custom/Extension * @param boolean $silent */ function rebuild_all($silent=false){ if(defined('TEMPLATE_URL'))SugarTemplateUtilities::disableCache(); $this->silent=$silent; global $sugar_config; //Check for new module extensions $this->rebuild_modules(); $this->rebuild_languages($sugar_config['languages']); $this->rebuild_extensions(); $this->rebuild_dashletcontainers(); $this->rebuild_relationships(); $this->rebuild_tabledictionary(); $this->reset_opcodes(); sugar_cache_reset(); } /* * ModuleInstaller->merge_files runs over the list of all modules already installed in /modules. For each $module it reads the contents of every file in * custom/Extension/modules/$module/ (_override files last) and concatenates them to custom/modules/$module//. * Then it does the same thing in custom/Extension/application/, concatenating those files and copying the result to custom/application// */ function merge_files($path, $name, $filter = '', $application = false){ if(!$application){ $GLOBALS['log']->debug( get_class($this)."->merge_files() : merging module files in custom/Extension/modules//$path to custom/modules//$path$name"); foreach($this->modules as $module){ //$GLOBALS['log']->debug("Merging Files for: ".$module); //$GLOBALS['log']->debug("Merging Files for path: ".$path); $extension = "read()){ if((empty($filter) || substr_count($entry, $filter) > 0) && is_file($module_install.'/'.$entry) && $entry != '.' && $entry != '..' && strtolower(substr($entry, -4)) == ".php") { if (substr($entry, 0, 9) == '_override') { $override[] = $entry; } else { $file = file_get_contents($module_install . '/' . $entry); $GLOBALS['log']->debug(get_class($this)."->merge_files(): found {$module_install}{$entry}") ; $extension .= "\n". str_replace(array('', '', '"; if($shouldSave){ if(!file_exists("custom/$extpath")) { mkdir_recursive("custom/$extpath", true); } $out = sugar_fopen("custom/$extpath/$name", 'w'); fwrite($out,$extension); fclose($out); }else{ if(file_exists("custom/$extpath/$name")){ unlink("custom/$extpath/$name"); } } } } $GLOBALS['log']->debug("Merging application files for $name in $path"); //Now the application stuff $extension = "read()){ $shouldSave = true; if((empty($filter) || substr_count($entry, $filter) > 0) && is_file($module_install.'/'.$entry) && $entry != '.' && $entry != '..' && strtolower(substr($entry, -4)) == ".php") { $file = file_get_contents($module_install . '/' . $entry); $extension .= "\n". str_replace(array('', '"; if($shouldSave){ if(!file_exists("custom/$extpath")){ mkdir_recursive("custom/$extpath", true); } $out = sugar_fopen("custom/$extpath/$name", 'w'); fwrite($out,$extension); fclose($out); }else{ if(file_exists("custom/$extpath/$name")){ unlink("custom/$extpath/$name"); } } } function install_modules() { $this->installed_modules = array(); $this->tab_modules = array(); if(isset($this->installdefs['beans'])){ $str = "installdefs['beans'] as $bean){ if(!empty($bean['module']) && !empty($bean['class']) && !empty($bean['path'])){ $module = $bean['module']; $class = $bean['class']; $path = $bean['path']; $str .= "\$beanList['$module'] = '$class';\n"; $str .= "\$beanFiles['$class'] = '$path';\n"; if($bean['tab']){ $str .= "\$moduleList[] = '$module';\n"; $this->install_user_prefs($module, empty($bean['hide_by_default'])); $this->tab_modules[] = $module; }else{ $str .= "\$modules_exempt_from_availability_check['$module'] = '$module';\n"; $str .= "\$report_include_modules['$module'] = '$module';\n"; $str .= "\$modInvisList[] = '$module';\n"; } $this->installed_modules[] = $module; }else{ $errors[] = 'Bean array not well defined.'; $this->abort($errors); } } $str.= "\n?>"; if(!file_exists("custom/Extension/application/Ext/Include")){ mkdir_recursive("custom/Extension/application/Ext/Include", true); } file_put_contents("custom/Extension/application/Ext/Include/{$this->id_name}.php", $str); } } /* * ModuleInstaller->install_beans runs through the list of beans given, instantiates each bean, calls bean->create_tables, and then calls SugarBean::createRelationshipMeta for the * bean/module. */ function install_beans($beans){ include('include/modules.php'); foreach($beans as $bean){ $this->log( translate('LBL_MI_IN_BEAN') . " $bean"); if(isset($beanList[$bean])){ $class = $beanList[$bean]; if(file_exists($beanFiles[$class])){ require_once($beanFiles[$class]); $mod = new $class(); //#30273 if(is_subclass_of($mod, 'SugarBean') && $mod->disable_vardefs == false ){ $GLOBALS['log']->debug( "Creating Tables Bean : $bean"); $mod->create_tables(); SugarBean::createRelationshipMeta($mod->getObjectName(), $mod->db,$mod->table_name,'',$mod->module_dir); } }else{ $GLOBALS['log']->debug( "File Does Not Exist:" . $beanFiles[$class] ); } } } } function uninstall_beans($beans){ include('include/modules.php'); foreach($beans as $bean){ $this->log( translate('LBL_MI_UN_BEAN') . " $bean"); if(isset($beanList[$bean])){ $class = $beanList[$bean]; if(file_exists($beanFiles[$class])){ require_once($beanFiles[$class]); $mod = new $class(); if(is_subclass_of($mod, 'SugarBean')){ $GLOBALS['log']->debug( "Drop Tables : $bean"); if(isset($GLOBALS['mi_remove_tables']) && $GLOBALS['mi_remove_tables']) $mod->drop_tables(); } }else{ $GLOBALS['log']->debug( "File Does Not Exist:" . $beanFiles[$class] ); } } } } /** * Remove any customizations made within Studio while the module was installed. */ function uninstall_customizations($beans){ foreach($beans as $bean){ $dirs = array( 'custom/modules/' . $bean, 'custom/Extension/modules/' . $bean, 'custom/working/modules/' . $bean ); foreach($dirs as $dir) { if(is_dir($dir)){ rmdir_recursive($dir); } } } } function log($str){ $GLOBALS['log']->debug('ModuleInstaller:'. $str); if(!$this->silent){ echo $str . '
'; } } /* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:15:18 PM */ function copy_recursive_with_backup( $source, $dest, $backup_path, $uninstall=false ) { if(is_file($source)) { if($uninstall) { $GLOBALS['log']->debug("Restoring ... " . $source. " to " .$dest ); if(copy( $source, $dest)) { if(is_writable($dest)) sugar_touch( $dest, filemtime($source) ); return(unlink($source)); } else { $GLOBALS['log']->debug( "Can't restore file: " . $source ); return true; } } else { if(file_exists($dest)) { $rest = clean_path($backup_path."/$dest"); if( !is_dir(dirname($rest)) ) mkdir_recursive(dirname($rest), true); $GLOBALS['log']->debug("Backup ... " . $dest. " to " .$rest ); if(copy( $dest, $rest)) { if(is_writable($rest)) sugar_touch( $rest, filemtime($dest) ); } else { $GLOBALS['log']->debug( "Can't backup file: " . $dest ); } } return( copy( $source, $dest ) ); } } elseif(!is_dir($source)) { if($uninstall) { if(is_file($dest)) return(unlink($dest)); else { //don't do anything we already cleaned up the files using uninstall_new_files return true; } } else return false; } if( !is_dir($dest) && !$uninstall){ sugar_mkdir( $dest ); } $status = true; $d = dir( $source ); while( $f = $d->read() ){ if( $f == "." || $f == ".." ){ continue; } $status &= $this->copy_recursive_with_backup( "$source/$f", "$dest/$f", $backup_path, $uninstall ); } $d->close(); return( $status ); } private function dir_get_files($path, $base_path){ $files = array(); if(!is_dir($path))return $files; $d = dir($path); while ($e = $d->read()){ //ignore invisible files . .. ._MACOSX if(substr($e, 0, 1) == '.')continue; if(is_file($path . '/' . $e))$files[str_replace($base_path , '', $path . '/' . $e)] = str_replace($base_path , '', $path . '/' . $e); if(is_dir($path . '/' . $e))$files = array_merge($files, $this->dir_get_files($path . '/' . $e, $base_path)); } $d->close(); return $files; } private function dir_file_count($path){ //if its a file then it has at least 1 file in the directory if(is_file($path)) return 1; if(!is_dir($path)) return 0; $d = dir($path); $count = 0; while ($e = $d->read()){ //ignore invisible files . .. ._MACOSX if(substr($e, 0, 1) == '.')continue; if(is_file($path . '/' . $e))$count++; if(is_dir($path . '/' . $e))$count += $this->dir_file_count($path . '/' . $e); } $d->close(); return $count; } /* END - RESTORE POINT - by MR. MILK August 31, 2005 02:15:34 PM */ /** * Static function which allows a module developer to abort their progress, pass in an array of errors and * redirect back to the main module loader page * * @param errors an array of error messages which will be displayed on the * main module loader page once it is loaded. */ function abort($errors = array()){ //set the errors onto the session so we can display them one the moduler loader page loads $_SESSION['MODULEINSTALLER_ERRORS'] = $errors; echo ''; die(); //header('Location: index.php?module=Administration&action=UpgradeWizard&view=module'); } /** * Return the set of errors stored in the SESSION * * @return an array of errors */ function getErrors(){ if(!empty($_SESSION['MODULEINSTALLER_ERRORS'])){ $errors = $_SESSION['MODULEINSTALLER_ERRORS']; unset($_SESSION['MODULEINSTALLER_ERRORS']); return $errors; } else return null; } /* * Add any fields to the DetailView and EditView of the appropriate modules * Only add into deployed modules, as addFieldsToUndeployedLayouts has done this already for undeployed modules (and the admin might have edited the layouts already) * @param array $layoutAdditions An array of module => fieldname * return null */ function addFieldsToLayout($layoutAdditions) { require_once 'modules/ModuleBuilder/parsers/views/GridLayoutMetaDataParser.php' ; // these modules either lack editviews/detailviews or use custom mechanisms for the editview/detailview. // In either case, we don't want to attempt to add a relate field to them // would be better if GridLayoutMetaDataParser could handle this gracefully, so we don't have to maintain this list here $invalidModules = array ( 'emails' , 'kbdocuments' ) ; foreach ( $layoutAdditions as $deployedModuleName => $fieldName ) { if ( ! in_array( strtolower ( $deployedModuleName ) , $invalidModules ) ) { foreach ( array ( MB_EDITVIEW , MB_DETAILVIEW ) as $view ) { $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . ": adding $fieldName to $view layout for module $deployedModuleName" ) ; $parser = new GridLayoutMetaDataParser ( $view, $deployedModuleName ) ; $parser->addField ( array ( 'name' => $fieldName ) ) ; $parser->handleSave ( false ) ; } } } } function removeFieldsFromLayout($layoutAdditions) { require_once 'modules/ModuleBuilder/parsers/views/GridLayoutMetaDataParser.php' ; // these modules either lack editviews/detailviews or use custom mechanisms for the editview/detailview. // In either case, we don't want to attempt to add a relate field to them // would be better if GridLayoutMetaDataParser could handle this gracefully, so we don't have to maintain this list here $invalidModules = array ( 'emails' , 'kbdocuments' ) ; foreach ( $layoutAdditions as $deployedModuleName => $fieldName ) { if ( ! in_array( strtolower ( $deployedModuleName ) , $invalidModules ) ) { foreach ( array ( MB_EDITVIEW , MB_DETAILVIEW ) as $view ) { $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . ": adding $fieldName to $view layout for module $deployedModuleName" ) ; $parser = new GridLayoutMetaDataParser ( $view, $deployedModuleName ) ; $parser->removeField ( $fieldName ) ; $parser->handleSave ( false ) ; } } } } /////////////////// //********** DISABLE/ENABLE FUNCTIONS /////////////////// function enable($base_dir, $is_upgrade = false, $previous_version = ''){ global $app_strings; $this->base_dir = $base_dir; $total_steps = 3; //minimum number of steps with no tasks $current_step = 0; $tasks = array( 'enable_copy', 'enable_dashlets', 'enable_relationships', 'enable_extensions', 'enable_global_search', 'enable_manifest_logichooks', 'reset_opcodes', ); $total_steps += count($tasks); if(file_exists($this->base_dir . '/manifest.php')){ if(!$this->silent){ $current_step++; display_progress_bar('install', $current_step, $total_steps); echo ''; } UpdateSystemTabs('Add',$installed_modules); $GLOBALS['log']->debug('Complete'); }else{ die("No \$installdefs Defined In $this->base_dir/manifest.php"); } } function disable($base_dir){ global $app_strings; $total_steps = 3; //min steps with no tasks $current_step = 0; $this->base_dir = $base_dir; $tasks = array( 'disable_copy', 'disable_dashlets', 'disable_relationships', 'disable_extensions', 'disable_global_search', 'disable_manifest_logichooks', 'reset_opcodes', ); $total_steps += count($tasks); //now the real number of steps if(file_exists($this->base_dir . '/manifest.php')){ if(!$this->silent){ $current_step++; display_progress_bar('install', $current_step, $total_steps); echo ''; } UpdateSystemTabs('Restore',$installed_modules); }else{ die("No manifest.php Defined In $this->base_dir/manifest.php"); } } function enable_vardef($to_module) { $this->enableExt("vardefs", "Vardefs", $to_module); } function enable_layoutdef($to_module) { $this->enableExt("layoutdefs", "Layoutdefs", $to_module); } function enable_relationships(){ if(isset($this->installdefs['relationships'])){ $str = "installdefs['relationships'] as $relationship){ $filename =basename($relationship['meta_data']); $save_table_dictionary = true; $str .= "include_once('metadata/$filename');\n"; if (empty($relationship['module'])) continue; if(!empty($relationship['module_vardefs'])){ $this->enable_vardef($relationship['module']); } if(!empty($relationship['module_layoutdefs'])){ $this->enable_layoutdef($relationship['module']); } } $this->rebuild_vardefs(); $this->rebuild_layoutdefs(); if($save_table_dictionary){ if(!file_exists("custom/Extension/application/Ext/TableDictionary")){ mkdir_recursive("custom/Extension/application/Ext/TableDictionary", true); } if (file_exists("custom/Extension/application/Ext/TableDictionary/".DISABLED_PATH."/$this->id_name.php")) rename("custom/Extension/application/Ext/TableDictionary/".DISABLED_PATH."/$this->id_name.php", "custom/Extension/application/Ext/TableDictionary/$this->id_name.php"); $this->rebuild_tabledictionary(); } } } function disable_relationships($action = 'disable'){ if(isset($this->installdefs['relationships'])){ foreach($this->installdefs['relationships'] as $relationship){ $filename = basename($relationship['meta_data']); $relName = substr($filename, -12) == "MetaData.php" ? substr($filename,0,strlen($filename) - 12) : ""; if (empty($relationship['module']) && empty($relName)) continue; //remove the vardefs if (empty($relName)) $path = 'custom/Extension/modules/' . $relationship['module']. '/Ext/Vardefs'; if(!empty($relationship['module']) && $relationship['module'] == 'application'){ $path ='custom/Extension/' . $relationship['module']. '/Ext/Vardefs'; } if(!empty($relationship['module_vardefs']) && file_exists($path . '/'. $this->id_name . '.php')){ mkdir_recursive($path . '/'.DISABLED_PATH, true); rename( $path . '/'. $this->id_name . '.php', $path . '/'.DISABLED_PATH.'/'. $this->id_name . '.php'); } //remove the layoutdefs if ( !empty($relationship['module']) ) { $path = 'custom/Extension/modules/' . $relationship['module']. '/Ext/Layoutdefs'; if($relationship['module'] == 'application'){ $path ='custom/Extension/' . $relationship['module']. '/Ext/Layoutdefs'; } } if(!empty($relationship['module_layoutdefs']) && file_exists($path . '/'. $this->id_name . '.php')){ mkdir_recursive($path . '/'.DISABLED_PATH, true); rename( $path . '/'. $this->id_name . '.php', $path . '/'.DISABLED_PATH.'/'. $this->id_name . '.php'); } } if(file_exists("custom/Extension/application/Ext/TableDictionary/$this->id_name.php")){ mkdir_recursive("custom/Extension/application/Ext/TableDictionary/".DISABLED_PATH, true); rename("custom/Extension/application/Ext/TableDictionary/$this->id_name.php", "custom/Extension/application/Ext/TableDictionary/".DISABLED_PATH."/$this->id_name.php"); } $this->rebuild_tabledictionary(); $this->rebuild_vardefs(); $this->rebuild_layoutdefs(); } } function enable_dashlets(){ if(isset($this->installdefs['dashlets'])){ foreach($this->installdefs['dashlets'] as $cp){ $cp['from'] = str_replace('', $this->base_dir, $cp['from']); $path = 'custom/modules/Home/Dashlets/' . $cp['name'] . '/'; $disabled_path = 'custom/modules/Home/'.DISABLED_PATH.'Dashlets/' . $cp['name']; $GLOBALS['log']->debug("Enabling Dashlet " . $cp['name'] . "..." . $cp['from'] ); if (file_exists($disabled_path)) { rename($disabled_path, $path); } } include('modules/Administration/RebuildDashlets.php'); } } function disable_dashlets(){ if(isset($this->installdefs['dashlets'])){ foreach($this->installdefs['dashlets'] as $cp){ $path = 'custom/modules/Home/Dashlets/' . $cp['name']; $disabled_path = 'custom/modules/Home/'.DISABLED_PATH.'Dashlets/' . $cp['name']; $GLOBALS['log']->debug('Disabling ' .$path); if (file_exists($path)) { mkdir_recursive('custom/modules/Home/'.DISABLED_PATH.'Dashlets/', true); rename( $path, $disabled_path); } } include('modules/Administration/RebuildDashlets.php'); } } function enable_copy(){ //copy files back onto file system. first perform md5 check to determine if anything has been modified //here we should just go through the files in the -restore directory and copy those back if(isset($GLOBALS['mi_overwrite_files']) && $GLOBALS['mi_overwrite_files']){ if(!empty($this->installdefs['copy'])){ foreach($this->installdefs['copy'] as $cp){ $cp['to'] = clean_path(str_replace('', $this->base_dir, $cp['to'])); $backup_path = clean_path( remove_file_extension(urldecode(hashToFile($_REQUEST['install_file'])))."-restore/".$cp['to'] ); //check if this file exists in the -restore directory if(file_exists($backup_path)){ //since the file exists, then we want do an md5 of the install version and the file system version //if(is_file($backup_path) && md5_file($backup_path) == md5_file($cp['to'])){ //since the files are the same then we can safely move back from the -restore //directory into the file system $GLOBALS['log']->debug("ENABLE COPY:: FROM: ".$cp['from']. " TO: ".$cp['to']); $this->copy_path($cp['from'], $cp['to']); /*}else{ //since they are not equal then we need to prompt the user }*/ }//fi }//rof }//fi }//fi } function disable_copy(){ //when we disable we want to copy the -restore files back into the file system //but we should check the version in the module install against the version on the file system //if they match then we can copy the file back, but otherwise we should ask the user. // $GLOBALS['log']->debug('ModuleInstaller.php->disable_copy()'); if(isset($GLOBALS['mi_overwrite_files']) && $GLOBALS['mi_overwrite_files']){ // $GLOBALS['log']->debug('ModuleInstaller.php->disable_copy():mi_overwrite_files=true'); if(!empty($this->installdefs['copy'])){ // $GLOBALS['log']->debug('ModuleInstaller.php->disable_copy(): installdefs not empty'); foreach($this->installdefs['copy'] as $cp){ $cp['to'] = clean_path(str_replace('', $this->base_dir, $cp['to'])); $backup_path = clean_path( remove_file_extension(urldecode(hashToFile($_REQUEST['install_file'])))."-restore/".$cp['to'] ); // bug 16966 tyoung - replaced missing assignment to $backup_path //check if this file exists in the -restore directory // $GLOBALS['log']->debug("ModuleInstaller.php->disable_copy(): backup_path=".$backup_path); if(file_exists($backup_path)){ //since the file exists, then we want do an md5 of the install version and the file system version $from = str_replace('', $this->base_dir, $cp['from']); //if(is_file($from) && md5_file($from) == md5_file($cp['to'])){ //since the files are the same then we can safely move back from the -restore //directory into the file system $GLOBALS['log']->debug("DISABLE COPY:: FROM: ".$backup_path. " TO: ".$cp['to']); $this->copy_path($backup_path, $cp['to']); /*}else{ //since they are not equal then we need to prompt the user }*/ }//fi }//rof }//fi }//fi } public function reset_opcodes() { /* Bug 39354 - added function_exists check. Not optimal fix, but safe nonetheless. * This is for the upgrade to 6.1 from pre 6.1, since the utils files haven't been updated to 6.1 when this is called, * but this file has been updated to 6.1 */ if(function_exists('sugar_clean_opcodes')){ sugar_clean_opcodes(); } } /** * BC implementation to provide specific calls to extensions */ public function __call($name, $args) { $nameparts = explode('_', $name); // name is something_something if(count($nameparts) == 2 && isset($this->extensions[$nameparts[1]])) { $ext = $this->extensions[$nameparts[1]]; switch($nameparts[0]) { case 'enable': return $this->enableExt($ext['section'], $ext['extdir']); case 'disable': return $this->disableExt($ext['section'], $ext['extdir']); case 'install': return $this->installExt($ext['section'], $ext['extdir']); case 'uninstall': return $this->uninstallExt($ext['section'], $ext['extdir']); case 'rebuild': return $this->rebuildExt($ext['extdir'], $ext['file']); } } sugar_die("Unknown method ModuleInstaller::$name called"); } } function UpdateSystemTabs($action, $installed_modules){ require_once("modules/MySettings/TabController.php"); $controller = new TabController(); $isSystemTabsInDB = $controller->is_system_tabs_in_db(); if ($isSystemTabsInDB && !empty($installed_modules)) { global $moduleList; switch ($action) { case 'Restore' : $currentTabs = $controller->get_system_tabs(); foreach ($installed_modules as $module) { if(in_array($module, $currentTabs)){ unset($currentTabs[$module]); } } $controller->set_system_tabs($currentTabs);; break; case 'Add' : $currentTabs = $controller->get_system_tabs(); foreach ($installed_modules as $module) { if(!in_array($module, $currentTabs)){ $currentTabs[$module] = $module; } } $controller->set_system_tabs($currentTabs); default: break; } } }