2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4 * SugarCRM Community Edition is a customer relationship management program developed by
5 * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
7 * This program is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU Affero General Public License version 3 as published by the
9 * Free Software Foundation with the addition of the following permission added
10 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
19 * You should have received a copy of the GNU Affero General Public License along with
20 * this program; if not, see http://www.gnu.org/licenses or write to the Free
21 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
27 * The interactive user interfaces in modified source and object code versions
28 * of this program must display Appropriate Legal Notices, as required under
29 * Section 5 of the GNU Affero General Public License version 3.
31 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32 * these Appropriate Legal Notices must retain the display of the "Powered by
33 * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34 * technical reasons, the Appropriate Legal Notices must display the words
35 * "Powered by SugarCRM".
36 ********************************************************************************/
40 * ModuleInstaller - takes an installation package from files in the custom/Extension/X directories, and moves them into custom/X to install them.
41 * If a directory has multiple files they are concatenated together.
42 * Relevant directories (X) are Layoutdefs, Vardefs, Include (bean stuff), Language, TableDictionary (relationships)
44 * Installation steps that involve more than just copying files:
45 * 1. installing custom fields - calls bean->custom_fields->addField
46 * 2. installing relationships - calls createTableParams to build the relationship table, and createRelationshipMeta to add the relationship to the relationship table
47 * 3. rebuilding the relationships - at almost the last step in install(), calls modules/Administration/RebuildRelationship.php
48 * 4. repair indices - uses "modules/Administration/RepairIndex.php";
53 require_once('include/utils/progress_bar_utils.php');
55 require_once('ModuleInstall/ModuleScanner.php');
56 define('DISABLED_PATH', 'Disabled');
58 class ModuleInstaller{
59 var $modules = array();
62 var $modulesInPackage = array();
63 public $disabled_path = DISABLED_PATH;
65 function ModuleInstaller(){
66 $this->ms = new ModuleScanner();
67 $this->modules = get_module_dir_list();
68 $this->db = & DBManagerFactory::getInstance();
69 include("ModuleInstall/extensions.php");
70 $this->extensions = $extensions;
74 * 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
75 * an upgrade_manifest defined in the manifest; if not it errors. It then adds the bean into the custom/Extension/application/Ext/Include/<module>.php - sets beanList, beanFiles
76 * and moduleList - and then calls ModuleInstaller->merge_files('Ext/Include', 'modules.ext.php', '', true) to merge the individual module files into a combined file
77 * /custom/Extension/application/Ext/Include/modules.ext.php (which now contains a list of all $beanList, $beanFiles and $moduleList for all extension modules) -
78 * this file modules.ext.php is included at the end of modules.php.
80 * 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.
82 function install($base_dir, $is_upgrade = false, $previous_version = ''){
83 if(defined('TEMPLATE_URL'))SugarTemplateUtilities::disableCache();
84 if ((defined('MODULE_INSTALLER_PACKAGE_SCAN') && MODULE_INSTALLER_PACKAGE_SCAN)
85 || !empty($GLOBALS['sugar_config']['moduleInstaller']['packageScan'])) {
86 $this->ms->scanPackage($base_dir);
87 if($this->ms->hasIssues()){
88 $this->ms->displayIssues();
93 // workaround for bug 45812 - refresh vardefs cache before unpacking to avoid partial vardefs in cache
95 foreach ($this->modules as $module_name) {
96 if (!empty($beanList[$module_name])) {
97 $objectName = BeanFactory::getObjectName($module_name);
98 VardefManager::loadVardef($module_name, $objectName);
102 global $app_strings, $mod_strings;
103 $this->base_dir = $base_dir;
104 $total_steps = 5; //minimum number of steps with no tasks
109 'install_extensions',
113 'install_connectors',
114 'install_layoutfields',
115 'install_relationships',
116 'enable_manifest_logichooks',
121 $total_steps += count($tasks);
122 if(file_exists($this->base_dir . '/manifest.php')){
125 display_progress_bar('install', $current_step, $total_steps);
126 echo '<div id ="displayLoglink" ><a href="#" onclick="document.getElementById(\'displayLog\').style.display=\'\'">'
127 .$app_strings['LBL_DISPLAY_LOG'].'</a> </div><div id="displayLog" style="display:none">';
130 include($this->base_dir . '/manifest.php');
131 if($is_upgrade && !empty($previous_version)){
132 //check if the upgrade path exists
133 if(!empty($upgrade_manifest)){
134 if(!empty($upgrade_manifest['upgrade_paths'])){
135 if(!empty($upgrade_manifest['upgrade_paths'][$previous_version])){
136 $installdefs = $upgrade_manifest['upgrade_paths'][$previous_version];
138 $errors[] = 'No Upgrade Path Found in manifest.';
139 $this->abort($errors);
144 $this->id_name = $installdefs['id'];
145 $this->installdefs = $installdefs;
148 update_progress_bar('install', $current_step, $total_steps);
151 foreach($tasks as $task){
155 update_progress_bar('install', $current_step, $total_steps);
158 $this->install_beans($this->installed_modules);
161 update_progress_bar('install', $total_steps, $total_steps);
163 if(isset($installdefs['custom_fields'])){
164 $this->log(translate('LBL_MI_IN_CUSTOMFIELD'));
165 $this->install_custom_fields($installdefs['custom_fields']);
169 update_progress_bar('install', $current_step, $total_steps);
174 update_progress_bar('install', $current_step, $total_steps);
177 $selectedActions = array(
183 'rebuildAuditTables',
186 VardefManager::clearVardef();
187 global $beanList, $beanFiles, $moduleList;
188 if (file_exists('custom/application/Ext/Include/modules.ext.php'))
190 include('custom/application/Ext/Include/modules.ext.php');
192 require_once("modules/Administration/upgrade_custom_relationships.php");
193 upgrade_custom_relationships($this->installed_modules);
194 $this->rebuild_all(true);
195 require_once('modules/Administration/QuickRepairAndRebuild.php');
196 $rac = new RepairAndClear();
197 $rac->repairAndClearAll($selectedActions, $this->installed_modules,true, false);
198 $this->rebuild_relationships();
199 UpdateSystemTabs('Add',$this->tab_modules);
200 //Clear out all the langauge cache files.
201 clearAllJsAndJsLangFilesWithoutOutput();
202 $cache_key = 'app_list_strings.'.$GLOBALS['current_language'];
203 sugar_cache_clear($cache_key );
206 //clear the unified_search_module.php file
207 require_once('modules/Home/UnifiedSearchAdvanced.php');
208 UnifiedSearchAdvanced::unlinkUnifiedSearchModulesFile();
210 $this->log('<br><b>' . translate('LBL_MI_COMPLETE') . '</b>');
212 die("No \$installdefs Defined In $this->base_dir/manifest.php");
217 function install_user_prefs($module, $hide_from_user=false){
218 UserPreference::updateAllUserPrefs('display_tabs', $module, '', true, !$hide_from_user);
219 UserPreference::updateAllUserPrefs('hide_tabs', $module, '', true, $hide_from_user);
220 UserPreference::updateAllUserPrefs('remove_tabs', $module, '', true, $hide_from_user);
222 function uninstall_user_prefs($module){
223 UserPreference::updateAllUserPrefs('display_tabs', $module, '', true, true);
224 UserPreference::updateAllUserPrefs('hide_tabs', $module, '', true, true);
225 UserPreference::updateAllUserPrefs('remove_tabs', $module, '', true, true);
228 function pre_execute(){
229 require_once($this->base_dir . '/manifest.php');
230 if(isset($this->installdefs['pre_execute']) && is_array($this->installdefs['pre_execute'])){
231 foreach($this->installdefs['pre_execute'] as $includefile){
232 require_once(str_replace('<basepath>', $this->base_dir, $includefile));
237 function post_execute(){
238 require_once($this->base_dir . '/manifest.php');
239 if(isset($this->installdefs['post_execute']) && is_array($this->installdefs['post_execute'])){
240 foreach($this->installdefs['post_execute'] as $includefile){
241 require_once(str_replace('<basepath>', $this->base_dir, $includefile));
246 function pre_uninstall(){
247 require_once($this->base_dir . '/manifest.php');
248 if(isset($this->installdefs['pre_uninstall']) && is_array($this->installdefs['pre_uninstall'])){
249 foreach($this->installdefs['pre_uninstall'] as $includefile){
250 require_once(str_replace('<basepath>', $this->base_dir, $includefile));
255 function post_uninstall(){
256 require_once($this->base_dir . '/manifest.php');
257 if(isset($this->installdefs['post_uninstall']) && is_array($this->installdefs['post_uninstall'])){
258 foreach($this->installdefs['post_uninstall'] as $includefile){
259 require_once(str_replace('<basepath>', $this->base_dir, $includefile));
265 * 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
266 * (specified as from and to in the manifest), replacing <basepath> by the base_dir value passed in to install.
268 function install_copy(){
269 if(isset($this->installdefs['copy'])){
270 /* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:22:11 PM */
271 $backup_path = clean_path( remove_file_extension(urldecode($_REQUEST['install_file']))."-restore" );
272 /* END - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
273 foreach($this->installdefs['copy'] as $cp){
274 $GLOBALS['log']->debug("Copying ..." . $cp['from']. " to " .$cp['to'] );
275 /* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:22:11 PM */
276 //$this->copy_path($cp['from'], $cp['to']);
277 $this->copy_path($cp['from'], $cp['to'], $backup_path);
278 /* END - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
280 //here we should get the module list again as we could have copied something to the modules dir
281 $this->modules = get_module_dir_list();
284 function uninstall_copy(){
285 if(!empty($this->installdefs['copy'])){
286 foreach($this->installdefs['copy'] as $cp){
287 $cp['to'] = clean_path(str_replace('<basepath>', $this->base_dir, $cp['to']));
288 $cp['from'] = clean_path(str_replace('<basepath>', $this->base_dir, $cp['from']));
289 $GLOBALS['log']->debug('Unlink ' . $cp['to']);
290 /* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:22:11 PM */
291 //rmdir_recursive($cp['to']);
293 $backup_path = clean_path( remove_file_extension(urldecode(hashToFile($_REQUEST['install_file'])))."-restore/".$cp['to'] );
294 $this->uninstall_new_files($cp, $backup_path);
295 $this->copy_path($backup_path, $cp['to'], $backup_path, true);
296 /* END - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
298 $backup_path = clean_path( remove_file_extension(urldecode(hashToFile($_REQUEST['install_file'])))."-restore");
299 if(file_exists($backup_path))
300 rmdir_recursive($backup_path);
306 * Removes any files that were added by the loaded module. If the files already existed prior to install
307 * it will be handled by copy_path with the uninstall parameter.
310 function uninstall_new_files($cp, $backup_path){
311 $zip_files = $this->dir_get_files($cp['from'],$cp['from']);
312 $backup_files = $this->dir_get_files($backup_path, $backup_path);
313 foreach($zip_files as $k=>$v){
314 //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
315 if(!isset($backup_files[$k])){
316 $to = $cp['to'] . $k;
317 //if it's not a sugar file then we remove it otherwise we can't restor it
318 if(!$this->ms->sugarFileExists($to)){
319 $GLOBALS['log']->debug('ModuleInstaller[uninstall_new_file] deleting file ' . $to);
320 if(file_exists($to)) {
324 $GLOBALS['log']->fatal('ModuleInstaller[uninstall_new_file] Could not remove file ' . $to . ' as no backup file was found to restore to');
328 //lets check if the directory is empty if it is we will delete it as well
329 $files_remaining = $this->dir_file_count($cp['to']);
330 if(file_exists($cp['to']) && $files_remaining == 0){
331 $GLOBALS['log']->debug('ModuleInstaller[uninstall_new_file] deleting directory ' . $cp['to']);
332 rmdir_recursive($cp['to']);
338 * Get directory where module's extensions go
339 * @param string $module Module name
341 public function getExtDir($module)
343 if($module == 'application') {
344 return "custom/Extension/application/Ext";
346 return "custom/Extension/modules/$module/Ext";
351 * Install file(s) into Ext/ part
352 * @param string $section Name of the install file section
353 * @param string $extname Name in Ext directory
354 * @param string $module This extension belongs to a specific module
356 public function installExt($section, $extname, $module = '')
358 if(isset($this->installdefs[$section])){
359 $this->log(sprintf(translate("LBL_MI_IN_EXT"), $section));
360 foreach($this->installdefs[$section] as $item){
361 if(isset($item['from'])) {
362 $from = str_replace('<basepath>', $this->base_dir, $item['from']);
366 if(!empty($module)) {
367 $item['to_module'] = $module;
369 $GLOBALS['log']->debug("Installing section $section from $from for " .$item['to_module'] );
370 if($item['to_module'] == 'application') {
371 $path = "custom/Extension/application/Ext/$extname";
373 $path = "custom/Extension/modules/{$item['to_module']}/Ext/$extname";
375 if(!file_exists($path)){
376 mkdir_recursive($path, true);
378 if(isset($item["name"])) {
379 $target = $item["name"];
380 } else if (!empty($from)){
381 $target = basename($from, ".php");
383 $target = $this->id_name;
386 copy_recursive($from , "$path/$target.php");
393 * Uninstall file(s) into Ext/ part
394 * @param string $section Name of the install file section
395 * @param string $extname Name in Ext directory
396 * @param string $module This extension belongs to a specific module
398 public function uninstallExt($section, $extname, $module = '')
400 if(isset($this->installdefs[$section])){
401 $this->log(sprintf(translate("LBL_MI_UN_EXT"), $section));
402 foreach($this->installdefs[$section] as $item){
403 if(isset($item['from'])) {
404 $from = str_replace('<basepath>', $this->base_dir, $item['from']);
408 if(!empty($module)) {
409 $item['to_module'] = $module;
411 $GLOBALS['log']->debug("Uninstalling section $section from $from for " .$item['to_module'] );
412 if($item['to_module'] == 'application') {
413 $path = "custom/Extension/application/Ext/$extname";
415 $path = "custom/Extension/modules/{$item['to_module']}/Ext/$extname";
417 if(isset($item["name"])) {
418 $target = $item["name"];
419 } else if (!empty($from)){
420 $target = basename($from, ".php");
422 $target = $this->id_name;
424 $disabled_path = $path.'/'.DISABLED_PATH;
425 if (file_exists("$path/$target.php")) {
426 rmdir_recursive("$path/$target.php");
427 } else if (file_exists("$disabled_path/$target.php")) {
428 rmdir_recursive("$disabled_path/$target.php");
429 } else if (!empty($from) && file_exists($path . '/'. basename($from))) {
430 rmdir_recursive( $path . '/'. basename($from));
431 } else if (!empty($from) && file_exists($disabled_path . '/'. basename($from))) {
432 rmdir_recursive( $disabled_path . '/'. basename($from));
439 * Rebuild generic extension
440 * @param string $ext Extension directory
441 * @param string $filename Target filename
443 public function rebuildExt($ext, $filename)
445 $this->log(translate('LBL_MI_REBUILDING') . " $ext...");
446 $this->merge_files("Ext/$ext/", $filename);
450 * Disable generic extension
451 * @param string $section Install file section name
452 * @param string $extname Extension directory
453 * @param string $module This extension belongs to a specific module
455 public function disableExt($section, $extname, $module = '')
457 if(isset($this->installdefs[$section])) {
458 foreach($this->installdefs[$section] as $item) {
459 if(isset($item['from'])) {
460 $from = str_replace('<basepath>', $this->base_dir, $item['from']);
464 if(!empty($module)) {
465 $item['to_module'] = $module;
467 $GLOBALS['log']->debug("Disabling $extname ... from $from for " .$item['to_module']);
468 if($item['to_module'] == 'application') {
469 $path = "custom/Extension/application/Ext/$extname";
471 $path = "custom/Extension/modules/{$item['to_module']}/Ext/$extname";
473 if(isset($item["name"])) {
474 $target = $item["name"];
475 } else if (!empty($from)){
476 $target = basename($from, ".php");
478 $target = $this->id_name;
480 $disabled_path = $path.'/'.DISABLED_PATH;
481 if (file_exists("$path/$target.php")) {
482 mkdir_recursive($disabled_path, true);
483 rename("$path/$target.php", "$disabled_path/$target.php");
484 } else if (!empty($from) && file_exists($path . '/'. basename($from))) {
485 mkdir_recursive($disabled_path, true);
486 rename( $path . '/'. basename($from), $disabled_path.'/'. basename($from));
493 * Enable generic extension
494 * @param string $section Install file section name
495 * @param string $extname Extension directory
496 * @param string $module This extension belongs to a specific module
498 public function enableExt($section, $extname, $module = '')
500 if(isset($this->installdefs[$section])) {
501 foreach($this->installdefs[$section] as $item) {
502 if(isset($item['from'])) {
503 $from = str_replace('<basepath>', $this->base_dir, $item['from']);
507 if(!empty($module)) {
508 $item['to_module'] = $module;
510 $GLOBALS['log']->debug("Enabling $extname ... from $from for " .$item['to_module']);
512 if($item['to_module'] == 'application') {
513 $path = "custom/Extension/application/Ext/$extname";
515 $path = "custom/Extension/modules/{$item['to_module']}/Ext/$extname";
517 if(isset($item["name"])) {
518 $target = $item["name"];
519 } else if (!empty($from)){
520 $target = basename($from, ".php");
522 $target = $this->id_name;
524 if(!file_exists($path)) {
525 mkdir_recursive($path, true);
527 $disabled_path = $path.'/'.DISABLED_PATH;
528 if (file_exists("$disabled_path/$target.php")) {
529 rename("$disabled_path/$target.php", "$path/$target.php");
531 if (!empty($from) && file_exists($disabled_path . '/'. basename($from))) {
532 rename($disabled_path.'/'. basename($from), $path . '/'. basename($from));
539 * Method removes module from global search configurations
543 public function uninstall_global_search()
545 if (empty($this->installdefs['beans']))
550 if (is_file('custom/modules/unified_search_modules_display.php') == false)
556 $users = get_user_array();
557 $unified_search_modules_display = array();
558 require('custom/modules/unified_search_modules_display.php');
560 foreach($this->installdefs['beans'] as $beanDefs)
562 if (array_key_exists($beanDefs['module'], $unified_search_modules_display) == false)
566 unset($unified_search_modules_display[$beanDefs['module']]);
567 foreach($users as $userId => $userName)
573 $user->retrieve($userId);
574 $prefs = $user->getPreference('globalSearch', 'search');
575 if (array_key_exists($beanDefs['module'], $prefs) == false)
579 unset($prefs[$beanDefs['module']]);
580 $user->setPreference('globalSearch', $prefs, 0, 'search');
581 $user->savePreferencesToDB();
585 if (write_array_to_file("unified_search_modules_display", $unified_search_modules_display, 'custom/modules/unified_search_modules_display.php') == false)
588 $msg = string_format($app_strings['ERR_FILE_WRITE'], array('custom/modules/unified_search_modules_display.php'));
589 $GLOBALS['log']->error($msg);
590 throw new Exception($msg);
597 * Method enables module in global search configurations by disabled_module_visible key
601 public function enable_global_search()
603 if (empty($this->installdefs['beans']))
608 if (is_file('custom/modules/unified_search_modules_display.php') == false)
613 $unified_search_modules_display = array();
614 require('custom/modules/unified_search_modules_display.php');
616 foreach($this->installdefs['beans'] as $beanDefs)
618 if (array_key_exists($beanDefs['module'], $unified_search_modules_display) == false)
622 if (isset($unified_search_modules_display[$beanDefs['module']]['disabled_module_visible']) == false)
626 $unified_search_modules_display[$beanDefs['module']]['visible'] = $unified_search_modules_display[$beanDefs['module']]['disabled_module_visible'];
627 unset($unified_search_modules_display[$beanDefs['module']]['disabled_module_visible']);
630 if (write_array_to_file("unified_search_modules_display", $unified_search_modules_display, 'custom/modules/unified_search_modules_display.php') == false)
633 $msg = string_format($app_strings['ERR_FILE_WRITE'], array('custom/modules/unified_search_modules_display.php'));
634 $GLOBALS['log']->error($msg);
635 throw new Exception($msg);
642 * Method disables module in global search configurations by disabled_module_visible key
646 public function disable_global_search()
648 if (empty($this->installdefs['beans']))
653 if (is_file('custom/modules/unified_search_modules_display.php') == false)
658 $unified_search_modules_display = array();
659 require('custom/modules/unified_search_modules_display.php');
661 foreach($this->installdefs['beans'] as $beanDefs)
663 if (array_key_exists($beanDefs['module'], $unified_search_modules_display) == false)
667 if (isset($unified_search_modules_display[$beanDefs['module']]['visible']) == false)
671 $unified_search_modules_display[$beanDefs['module']]['disabled_module_visible'] = $unified_search_modules_display[$beanDefs['module']]['visible'];
672 $unified_search_modules_display[$beanDefs['module']]['visible'] = false;
675 if (write_array_to_file("unified_search_modules_display", $unified_search_modules_display, 'custom/modules/unified_search_modules_display.php') == false)
678 $msg = string_format($app_strings['ERR_FILE_WRITE'], array('custom/modules/unified_search_modules_display.php'));
679 $GLOBALS['log']->error($msg);
680 throw new Exception($msg);
686 public function install_extensions()
688 foreach($this->extensions as $extname => $ext) {
689 $install = "install_$extname";
690 if(method_exists($this, $install)) {
691 // non-standard function
694 if(!empty($ext["section"])) {
695 $module = isset($ext['module'])?$ext['module']:'';
696 $this->installExt($ext["section"], $ext["extdir"], $module);
700 $this->rebuild_extensions();
703 public function uninstall_extensions()
705 foreach($this->extensions as $extname => $ext) {
706 $func = "uninstall_$extname";
707 if(method_exists($this, $func)) {
708 // non-standard function
711 if(!empty($ext["section"])) {
712 $module = isset($ext['module'])?$ext['module']:'';
713 $this->uninstallExt($ext["section"], $ext["extdir"], $module);
717 $this->rebuild_extensions();
720 public function rebuild_extensions()
722 foreach($this->extensions as $extname => $ext) {
723 $func = "rebuild_$extname";
724 if(method_exists($this, $func)) {
725 // non-standard function
728 $this->rebuildExt($ext["extdir"], $ext["file"]);
733 public function disable_extensions()
735 foreach($this->extensions as $extname => $ext) {
736 $func = "disable_$extname";
737 if(method_exists($this, $func)) {
738 // non-standard install
741 if(!empty($ext["section"])) {
742 $module = isset($ext['module'])?$ext['module']:'';
743 $this->disableExt($ext["section"], $ext["extdir"], $module);
747 $this->rebuild_extensions();
750 public function enable_extensions()
752 foreach($this->extensions as $extname => $ext) {
753 $func = "enable_$extname";
754 if(method_exists($this, $func)) {
755 // non-standard install
758 if(!empty($ext["section"])) {
759 $module = isset($ext['module'])?$ext['module']:'';
760 $this->enableExt($ext["section"], $ext["extdir"], $module);
764 $this->rebuild_extensions();
767 function install_dashlets()
769 if(isset($this->installdefs['dashlets'])){
770 foreach($this->installdefs['dashlets'] as $cp){
771 $this->log(translate('LBL_MI_IN_DASHLETS') . $cp['name']);
772 $cp['from'] = str_replace('<basepath>', $this->base_dir, $cp['from']);
773 $path = 'custom/modules/Home/Dashlets/' . $cp['name'] . '/';
774 $GLOBALS['log']->debug("Installing Dashlet " . $cp['name'] . "..." . $cp['from'] );
775 if(!file_exists($path)){
776 mkdir_recursive($path, true);
778 copy_recursive($cp['from'] , $path);
780 include('modules/Administration/RebuildDashlets.php');
785 function uninstall_dashlets(){
786 if(isset($this->installdefs['dashlets'])){
787 foreach($this->installdefs['dashlets'] as $cp){
788 $this->log(translate('LBL_MI_UN_DASHLETS') . $cp['name']);
789 $path = 'custom/modules/Home/Dashlets/' . $cp['name'];
790 $GLOBALS['log']->debug('Unlink ' .$path);
791 if (file_exists($path))
792 rmdir_recursive($path);
794 include('modules/Administration/RebuildDashlets.php');
799 function install_images(){
800 if(isset($this->installdefs['image_dir'])){
801 $this->log( translate('LBL_MI_IN_IMAGES') );
802 $this->copy_path($this->installdefs['image_dir'] , 'custom/themes');
807 function install_dcactions(){
808 if(isset($this->installdefs['dcaction'])){
809 $this->log(translate('LBL_MI_IN_MENUS'));
810 foreach($this->installdefs['dcaction'] as $action){
811 $action['from'] = str_replace('<basepath>', $this->base_dir, $action['from']);
812 $GLOBALS['log']->debug("Installing DCActions ..." . $action['from']);
813 $path = 'custom/Extension/application/Ext/DashletContainer/Containers';
814 if(!file_exists($path)){
815 mkdir_recursive($path, true);
817 copy_recursive($action['from'] , $path . '/'. $this->id_name . '.php');
819 $this->rebuild_dashletcontainers();
823 function uninstall_dcactions(){
824 if(isset($this->installdefs['dcaction'])){
825 $this->log(translate('LBL_MI_UN_MENUS'));
826 foreach($this->installdefs['dcaction'] as $action){
827 $action['from'] = str_replace('<basepath>', $this->base_dir, $action['from']);
828 $GLOBALS['log']->debug("Uninstalling DCActions ..." . $action['from'] );
829 $path = 'custom/Extension/application/Ext/DashletContainer/Containers';
830 if (sugar_is_file($path . '/'. $this->id_name . '.php', 'w'))
832 rmdir_recursive( $path . '/'. $this->id_name . '.php');
834 else if (sugar_is_file($path . '/'. DISABLED_PATH . '/'. $this->id_name . '.php', 'w'))
836 rmdir_recursive( $path . '/'. DISABLED_PATH . '/'. $this->id_name . '.php');
839 $this->rebuild_dashletcontainers();
843 function install_connectors(){
844 if(isset($this->installdefs['connectors'])){
845 foreach($this->installdefs['connectors'] as $cp){
846 $this->log(translate('LBL_MI_IN_CONNECTORS') . $cp['name']);
847 $dir = str_replace('_','/',$cp['name']);
848 $cp['connector'] = str_replace('<basepath>', $this->base_dir, $cp['connector']);
849 $source_path = 'custom/modules/Connectors/connectors/sources/' . $dir. '/';
850 $GLOBALS['log']->debug("Installing Connector " . $cp['name'] . "..." . $cp['connector'] );
851 if(!file_exists($source_path)){
852 mkdir_recursive($source_path, true);
854 copy_recursive($cp['connector'] , $source_path);
856 //Install optional formatter code if it is specified
857 if(!empty($cp['formatter'])) {
858 $cp['formatter'] = str_replace('<basepath>', $this->base_dir, $cp['formatter']);
859 $formatter_path = 'custom/modules/Connectors/connectors/formatters/' . $dir. '/';
860 if(!file_exists($formatter_path)){
861 mkdir_recursive($formatter_path, true);
863 copy_recursive($cp['formatter'] , $formatter_path);
866 require_once('include/connectors/utils/ConnectorUtils.php');
867 ConnectorUtils::installSource($cp['name']);
871 function uninstall_connectors(){
872 if(isset($this->installdefs['connectors'])){
873 foreach($this->installdefs['connectors'] as $cp){
874 $this->log(translate('LBL_MI_UN_CONNECTORS') . $cp['name']);
875 $dir = str_replace('_','/',$cp['name']);
876 $source_path = 'custom/modules/Connectors/connectors/sources/' . $dir;
877 $formatter_path = 'custom/modules/Connectors/connectors/formatters/' . $dir;
878 $GLOBALS['log']->debug('Unlink ' .$source_path);
879 rmdir_recursive($source_path);
880 rmdir_recursive($formatter_path);
882 require_once('include/connectors/utils/ConnectorUtils.php');
883 //ConnectorUtils::getConnectors(true);
884 ConnectorUtils::uninstallSource($cp['name']);
888 function install_vardef($from, $to_module)
890 $GLOBALS['log']->debug("Installing Vardefs ..." . $from . " for " .$to_module);
891 $path = 'custom/Extension/modules/' . $to_module. '/Ext/Vardefs';
892 if($to_module == 'application'){
893 $path ='custom/Extension/' . $to_module. '/Ext/Vardefs';
895 if(!file_exists($path)){
896 mkdir_recursive($path, true);
898 copy_recursive($from , $path.'/'. basename($from));
901 function install_layoutdef($from, $to_module){
902 $GLOBALS['log']->debug("Installing Layout Defs ..." . $from . " for " .$to_module);
903 $path = 'custom/Extension/modules/' . $to_module. '/Ext/Layoutdefs';
904 if($to_module == 'application'){
905 $path ='custom/Extension/' . $to_module. '/Ext/Layoutdefs';
907 if(!file_exists($path)){
908 mkdir_recursive($path, true);
910 copy_recursive($from , $path.'/'. basename($from));
913 // Non-standard - needs special rebuild call
914 function install_languages()
916 $languages = array();
917 if(isset($this->installdefs['language']))
919 $this->log(translate('LBL_MI_IN_LANG') );
920 foreach($this->installdefs['language'] as $packs)
922 $modules[]=$packs['to_module'];
923 $languages[$packs['language']] = $packs['language'];
924 $packs['from'] = str_replace('<basepath>', $this->base_dir, $packs['from']);
925 $GLOBALS['log']->debug("Installing Language Pack ..." . $packs['from'] . " for " .$packs['to_module']);
926 $path = $this->getInstallLanguagesPath($packs);
927 if (!file_exists(dirname($path))) {
928 mkdir_recursive(dirname($path), true);
930 copy_recursive($packs['from'], $path);
933 $this->rebuild_languages($languages, $modules);
939 * Function return path to file where store label
944 protected function getInstallLanguagesPath($packs)
946 $path = 'custom/Extension/modules/' . $packs['to_module']. '/Ext/Language';
947 if($packs['to_module'] == 'application'){
948 $path ='custom/Extension/' . $packs['to_module']. '/Ext/Language';
950 $path .= '/'.$packs['language'].'.'. $this->id_name . '.php';
954 // Non-standard, needs special rebuild
955 function uninstall_languages(){
956 $languages = array();
957 if(isset($this->installdefs['language'])){
958 $this->log(translate('LBL_MI_UN_LANG') );
959 foreach($this->installdefs['language'] as $packs){
960 $modules[]=$packs['to_module'];
961 $languages[$packs['language']] = $packs['language'];
962 $packs['from'] = str_replace('<basepath>', $this->base_dir, $packs['from']);
963 $GLOBALS['log']->debug("Uninstalling Language Pack ..." . $packs['from'] . " for " .$packs['to_module']);
964 $path = 'custom/Extension/modules/' . $packs['to_module']. '/Ext/Language';
965 if($packs['to_module'] == 'application'){
966 $path ='custom/Extension/' . $packs['to_module']. '/Ext/Language';
968 if (sugar_is_file($path.'/'.$packs['language'].'.'. $this->id_name . '.php', 'w')) {
969 rmdir_recursive( $path.'/'.$packs['language'].'.'. $this->id_name . '.php');
970 } else if (sugar_is_file($path.'/'.DISABLED_PATH.'/'.$packs['language'].'.'. $this->id_name . '.php', 'w')) {
971 rmdir_recursive($path.'/'.DISABLED_PATH.'/'.$packs['language'].'.'. $this->id_name . '.php', 'w');
974 $this->rebuild_languages($languages, $modules);
979 // Non-standard, needs special rebuild
980 public function disable_languages()
982 if(isset($this->installdefs['language'])) {
983 $languages = $modules = array();
984 foreach($this->installdefs['language'] as $item) {
985 $from = str_replace('<basepath>', $this->base_dir, $item['from']);
986 $GLOBALS['log']->debug("Disabling Language {$item['language']}... from $from for " .$item['to_module']);
987 $modules[]=$item['to_module'];
988 $languages[$item['language']] = $item['language'];
989 if($item['to_module'] == 'application') {
990 $path = "custom/Extension/application/Ext/Language";
992 $path = "custom/Extension/modules/{$item['to_module']}/Ext/Language";
994 if(isset($item["name"])) {
995 $target = $item["name"];
997 $target = $this->id_name;
999 $target = "{$item['language']}.$target";
1001 $disabled_path = $path.'/'.DISABLED_PATH;
1002 if (file_exists("$path/$target.php")) {
1003 mkdir_recursive($disabled_path, true);
1004 rename("$path/$target.php", "$disabled_path/$target.php");
1005 } else if (file_exists($path . '/'. basename($from))) {
1006 mkdir_recursive($disabled_path, true);
1007 rename( $path . '/'. basename($from), $disabled_path.'/'. basename($from));
1010 $this->rebuild_languages($languages, $modules);
1014 // Non-standard, needs special rebuild
1015 public function enable_languages()
1017 if(isset($this->installdefs['language'])) {
1018 foreach($this->installdefs['language'] as $item) {
1019 $from = str_replace('<basepath>', $this->base_dir, $item['from']);
1020 $GLOBALS['log']->debug("Enabling Language {$item['language']}... from $from for " .$item['to_module']);
1021 $modules[]=$item['to_module'];
1022 $languages[$item['language']] = $item['language'];
1023 if(!empty($module)) {
1024 $item['to_module'] = $module;
1027 if($item['to_module'] == 'application') {
1028 $path = "custom/Extension/application/Ext/Language";
1030 $path = "custom/Extension/modules/{$item['to_module']}/Ext/Language";
1032 if(isset($item["name"])) {
1033 $target = $item["name"];
1035 $target = $this->id_name;
1037 $target = "{$item['language']}.$target";
1039 if(!file_exists($path)) {
1040 mkdir_recursive($path, true);
1042 $disabled_path = $path.'/'.DISABLED_PATH;
1043 if (file_exists("$disabled_path/$target.php")) {
1044 rename("$disabled_path/$target.php", "$path/$target.php");
1046 if (file_exists($disabled_path . '/'. basename($from))) {
1047 rename($disabled_path.'/'. basename($from), $path . '/'. basename($from));
1050 $this->rebuild_languages($languages, $modules);
1054 // Functions for adding and removing logic hooks from uploaded files
1055 // 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
1056 /* The module hook definition should look like this:
1057 $installdefs = array(
1059 'logic_hooks' => array(
1060 array('module' => 'Accounts',
1061 'hook' => 'after_save',
1063 'description' => 'Account sample logic hook',
1064 'file' => 'modules/Sample/sample_account_logic_hook_file.php',
1065 'class' => 'SampleLogicClass',
1066 'function' => 'accountAfterSave',
1072 function enable_manifest_logichooks() {
1073 if(empty($this->installdefs['logic_hooks']) || !is_array($this->installdefs['logic_hooks'])) {
1079 foreach($this->installdefs['logic_hooks'] as $hook ) {
1080 check_logic_hook_file($hook['module'], $hook['hook'], array($hook['order'], $hook['description'], $hook['file'], $hook['class'], $hook['function']));
1084 function disable_manifest_logichooks() {
1085 if(empty($this->installdefs['logic_hooks']) || !is_array($this->installdefs['logic_hooks'])) {
1089 foreach($this->installdefs['logic_hooks'] as $hook ) {
1090 remove_logic_hook($hook['module'], $hook['hook'], array($hook['order'], $hook['description'], $hook['file'], $hook['class'], $hook['function']));
1095 * Check labels inside label files and remove them
1097 * @param $basePath - path to files with labels
1098 * @param array $labelDefinitions - format like output from AbstractRelationship buildLabels()
1100 public function uninstallLabels($basePath, $labelDefinitions)
1103 foreach ($labelDefinitions as $definition) {
1105 $filename = $basePath . "{$definition['module']}.php";
1107 if (!file_exists($filename)) {
1111 $uninstalLabes = $this->getLabelsToUninstall($labelDefinitions);
1112 $this->uninstallLabel($uninstalLabes, $definition, $filename);
1118 * Check labels inside label file and remove them
1120 * @param $uninstalLabes
1121 * @param $definition
1124 protected function uninstallLabel($uninstalLabes, $definition, $filename)
1126 $app_list_strings = array();
1127 $mod_strings = array();
1128 $stringsName = $definition['module'] == 'application' ? 'app_list_strings' : 'mod_strings';
1131 if ('app_list_strings' == $stringsName) {
1132 $strings = $app_list_strings;
1134 $strings = $mod_strings;
1137 foreach ($uninstalLabes AS $label) {
1138 if (isset($strings[$label])) {
1139 unset($strings[$label]);
1143 if (count($strings)) {
1144 $this->saveContentToFile($filename, $stringsName, $strings);
1151 * Save labels that not need be uninstalled at this case
1154 * @param $stringsName
1157 protected function saveContentToFile($filename, $stringsName, $strings)
1159 $fileContent = "<?php\n//THIS FILE IS AUTO GENERATED, DO NOT MODIFY\n";
1160 foreach ($strings as $key => $val) {
1161 $fileContent .= override_value_to_string_recursive2($stringsName, $key, $val);
1163 sugar_file_put_contents($filename, $fileContent);
1167 * Uninstall extend labels
1169 * @param $labelDefinitions
1171 public function uninstallExtLabels($labelDefinitions)
1173 foreach ($labelDefinitions as $definition) {
1174 if (!isset($GLOBALS['sugar_config']['languages']) || !is_array($GLOBALS['sugar_config']['languages'])) {
1178 foreach (array_keys($GLOBALS['sugar_config']['languages']) AS $language) {
1180 'language' => $language,
1181 'to_module' => $definition['module']
1183 $path = $this->getInstallLanguagesPath($pathDef);
1184 if (file_exists($path)) {
1193 * Returns the names of the label(key 'system_label') from a multi-dimensional array $labelDefinitions
1195 * @param $labelDefinitions
1196 * @return array of labels
1198 protected function getLabelsToUninstall($labelDefinitions)
1201 foreach($labelDefinitions AS $definition){
1202 $labels[] = $definition['system_label'];
1207 /* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
1208 function copy_path($from, $to, $backup_path='', $uninstall=false){
1209 //function copy_path($from, $to){
1210 /* END - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
1211 $to = str_replace('<basepath>', $this->base_dir, $to);
1214 $from = str_replace('<basepath>', $this->base_dir, $from);
1215 $GLOBALS['log']->debug('Copy ' . $from);
1218 $from = str_replace('<basepath>', $backup_path, $from);
1219 //$GLOBALS['log']->debug('Restore ' . $from);
1221 $from = clean_path($from);
1222 $to = clean_path($to);
1224 $dir = dirname($to);
1225 //there are cases where if we need to create a directory in the root directory
1226 if($dir == '.' && is_dir($from)){
1229 if(!sugar_is_dir($dir, 'instance'))
1230 mkdir_recursive($dir, true);
1231 /* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
1232 if(empty($backup_path)) {
1233 /* END - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
1234 if(!copy_recursive($from, $to)){
1235 die('Failed to copy ' . $from. ' ' . $to);
1237 /* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
1239 elseif(!$this->copy_recursive_with_backup($from, $to, $backup_path, $uninstall)){
1240 die('Failed to copy ' . $from. ' to ' . $to);
1242 /* END - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
1245 function install_custom_fields($fields){
1246 global $beanList, $beanFiles;
1247 include('include/modules.php');
1248 require_once('modules/DynamicFields/FieldCases.php');
1249 foreach($fields as $field){
1251 if(isset($beanList[ $field['module']])){
1252 $class = $beanList[ $field['module']];
1253 if(!isset($field['ext4']))$field['ext4'] = '';
1254 if(!isset($field['mass_update']))$field['mass_update'] = 0;
1255 if(!isset($field['duplicate_merge']))$field['duplicate_merge'] = 0;
1256 if(!isset($field['help']))$field['help'] = '';
1258 //Merge contents of the sugar field extension if we copied one over
1259 if (file_exists("custom/Extension/modules/{$field['module']}/Ext/Vardefs/sugarfield_{$field['name']}.php"))
1261 $dictionary = array();
1262 include ("custom/Extension/modules/{$field['module']}/Ext/Vardefs/sugarfield_{$field['name']}.php");
1263 $obj = BeanFactory::getObjectName($field['module']);
1264 if (!empty($dictionary[$obj]['fields'][$field['name']])) {
1265 $field = array_merge($dictionary[$obj]['fields'][$field['name']], $field);
1269 if(file_exists($beanFiles[$class])){
1270 require_once($beanFiles[$class]);
1271 $mod = new $class();
1273 $fieldObject = get_widget($field['type']);
1274 $fieldObject->populateFromRow($field);
1275 $mod->custom_fields->use_existing_labels = true;
1276 $mod->custom_fields->addFieldObject($fieldObject);
1280 $GLOBALS['log']->debug('Could not install custom field ' . $field['name'] . ' for module ' . $field['module'] . ': Module does not exist');
1285 function uninstall_custom_fields($fields){
1286 global $beanList, $beanFiles;
1287 require_once('modules/DynamicFields/DynamicField.php');
1288 $dyField = new DynamicField();
1290 foreach($fields as $field){
1291 $class = $beanList[ $field['module']];
1292 if(file_exists($beanFiles[$class])){
1293 require_once($beanFiles[$class]);
1294 $mod = new $class();
1295 $dyField->bean = $mod;
1296 $dyField->module = $field['module'];
1297 $dyField->deleteField($field['name']);
1303 * ModuleInstaller->install_relationships calls install_relationship for every file included in the module package that defines a relationship, and then
1304 * writes a custom/Extension/application/Ext/TableDictionary/$module.php file containing an include_once for every relationship metadata file passed to install_relationship.
1305 * 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
1306 * everything in 'Ext/TableDictionary/' into 'tabledictionary.ext.php'
1308 function install_relationships ()
1310 if (isset ( $this->installdefs [ 'relationships' ] ))
1312 $this->log ( translate ( 'LBL_MI_IN_RELATIONSHIPS' ) ) ;
1313 $str = "<?php \n //WARNING: The contents of this file are auto-generated\n" ;
1314 $save_table_dictionary = false ;
1316 if (! file_exists ( "custom/Extension/application/Ext/TableDictionary" ))
1318 mkdir_recursive ( "custom/Extension/application/Ext/TableDictionary", true ) ;
1321 foreach ( $this->installdefs [ 'relationships' ] as $key => $relationship )
1323 $filename = basename ( $relationship [ 'meta_data' ] ) ;
1324 $this->copy_path ( $relationship [ 'meta_data' ], 'custom/metadata/' . $filename ) ;
1325 $this->install_relationship ( 'custom/metadata/' . $filename ) ;
1326 $save_table_dictionary = true ;
1328 if (! empty ( $relationship [ 'module_vardefs' ] ))
1330 $relationship [ 'module_vardefs' ] = str_replace ( '<basepath>', $this->base_dir, $relationship [ 'module_vardefs' ] ) ;
1331 $this->install_vardef ( $relationship [ 'module_vardefs' ], $relationship [ 'module' ] ) ;
1334 if (! empty ( $relationship [ 'module_layoutdefs' ] ))
1336 $relationship [ 'module_layoutdefs' ] = str_replace ( '<basepath>', $this->base_dir, $relationship [ 'module_layoutdefs' ] ) ;
1337 $this->install_layoutdef ( $relationship [ 'module_layoutdefs' ], $relationship [ 'module' ] ) ;
1340 $relName = strpos($filename, "MetaData") !== false ? substr($filename, 0, strlen($filename) - 12) : $filename;
1341 $out = sugar_fopen ( "custom/Extension/application/Ext/TableDictionary/$relName.php", 'w' ) ;
1342 fwrite ( $out, $str . "include('custom/metadata/$filename');\n\n?>" ) ;
1349 Relationship::delete_cache();
1350 $this->rebuild_vardefs () ;
1351 $this->rebuild_layoutdefs () ;
1352 if ($save_table_dictionary)
1354 $this->rebuild_tabledictionary () ;
1356 require_once("data/Relationships/RelationshipFactory.php");
1357 SugarRelationshipFactory::deleteCache();
1362 * Install_relationship obtains a set of relationship definitions from the filename passed in as a parameter.
1363 * For each definition it calls db->createTableParams to build the relationships table if it does not exist,
1364 * and SugarBean::createRelationshipMeta to add the relationship into the 'relationships' table.
1366 function install_relationship($file)
1368 $_REQUEST['moduleInstaller'] = true;
1369 if(!file_exists($file))
1371 $GLOBALS['log']->debug( 'File does not exists : '.$file);
1375 $rel_dictionary = $dictionary;
1376 foreach ($rel_dictionary as $rel_name => $rel_data)
1378 $table = ''; // table is actually optional
1379 // check if we have a table definition - not all relationships require a join table
1380 if ( isset( $rel_data[ 'table' ] ) )
1382 $table = $rel_data[ 'table' ];
1384 if(!$this->db->tableExists($table))
1386 $this->db->createTableParams($table, $rel_data[ 'fields' ], $rel_data[ 'indices' ]);
1391 $GLOBALS['log']->debug("Processing relationship meta for ". $rel_name."...");
1392 SugarBean::createRelationshipMeta($rel_name, $this->db,$table,$rel_dictionary,'');
1393 Relationship::delete_cache();
1395 $GLOBALS['log']->debug( 'done<br>');
1399 function install_layoutfields() {
1400 if (!empty ( $this->installdefs [ 'layoutfields' ] ))
1402 foreach ( $this->installdefs [ 'layoutfields' ] as $fieldSet )
1404 if (!empty($fieldSet['additional_fields']))
1406 $this->addFieldsToLayout($fieldSet['additional_fields']);
1412 function uninstall_layoutfields() {
1413 if (!empty ( $this->installdefs [ 'layoutfields' ] ))
1415 foreach ( $this->installdefs [ 'layoutfields' ] as $fieldSet )
1417 if (!empty($fieldSet['additional_fields']))
1419 $this->removeFieldsFromLayout($fieldSet['additional_fields']);
1425 function uninstall_relationship($file, $rel_dictionary = null){
1426 if ($rel_dictionary == null)
1428 if(!file_exists($file)){
1429 $GLOBALS['log']->debug( 'File does not exists : '.$file);
1433 $rel_dictionary = $dictionary;
1436 foreach ($rel_dictionary as $rel_name => $rel_data)
1438 if (!empty($rel_data['table'])){
1439 $table = $rel_data['table'];
1442 $table = ' One-to-Many ';
1445 if ($this->db->tableExists($table) && isset($GLOBALS['mi_remove_tables']) && $GLOBALS['mi_remove_tables'])
1447 SugarBean::removeRelationshipMeta($rel_name, $this->db,$table,$rel_dictionary,'');
1448 $this->db->dropTableName($table);
1449 if(!$this->silent) $this->log( translate('LBL_MI_UN_RELATIONSHIPS_DROP') . $table);
1452 //Delete Layout defs
1453 // check to see if we have any vardef or layoutdef entries to remove - must have a relationship['module'] parameter if we do
1454 if (!isset($rel_data[ 'module' ]))
1456 $rel_data['relationships'][$rel_name]['lhs_module'],
1457 $rel_data['relationships'][$rel_name]['rhs_module'],
1460 $mods = array($rel_data[ 'module' ]);
1462 $filename = "$rel_name.php";
1464 foreach($mods as $mod) {
1465 if ($mod != 'application' ) {
1466 $basepath = "custom/Extension/modules/$mod/Ext/";
1468 $basepath = "custom/Extension/application/Ext/";
1471 foreach (array($filename , "custom" . $filename, $rel_name ."_". $mod. ".php") as $fn) {
1472 //remove any vardefs
1473 $path = $basepath . "Vardefs/$fn" ;
1474 if (file_exists( $path ))
1475 rmdir_recursive( $path );
1477 //remove any layoutdefs
1478 $path = $basepath . "Layoutdefs/$fn" ;
1479 if( file_exists( $path ))
1481 rmdir_recursive( $path );
1483 $path = $basepath . "WirelessLayoutdefs/$fn";
1484 if (file_exists($path)) {
1485 rmdir_recursive($path);
1488 $relationships_path = 'custom/Extension/modules/relationships/';
1490 $relationships_dirs = array(
1493 'wirelesslayoutdefs'
1495 foreach ($relationships_dirs as $relationship_dir) {
1496 $realtionship_file_path = $relationships_path . $relationship_dir . "/{$rel_name}_{$mod}.php";
1497 if (file_exists($realtionship_file_path)) {
1498 rmdir_recursive($realtionship_file_path);
1501 if (file_exists($relationships_path . "relationships/{$rel_name}MetaData.php")) {
1502 rmdir_recursive($relationships_path . "relationships/{$rel_name}MetaData.php");
1506 foreach (array($filename , "custom" . $filename, $rel_name ."_". $mod. ".php") as $fn) {
1507 // remove the table dictionary extension
1508 if ( file_exists("custom/Extension/application/Ext/TableDictionary/$fn"))
1509 unlink("custom/Extension/application/Ext/TableDictionary/$fn");
1511 if (file_exists("custom/metadata/{$rel_name}MetaData.php"))
1512 unlink( "custom/metadata/{$rel_name}MetaData.php" );
1517 function uninstall_relationships($include_studio_relationships = false){
1518 $relationships = array();
1520 //Find and remove studio created relationships.
1521 global $beanList, $beanFiles, $dictionary;
1522 //Load up the custom relationship definitions.
1523 if(file_exists('custom/application/Ext/TableDictionary/tabledictionary.ext.php')){
1524 include('custom/application/Ext/TableDictionary/tabledictionary.ext.php');
1526 //Find all the relatioships/relate fields involving this module.
1527 $rels_to_remove = array();
1528 foreach($beanList as $mod => $bean) {
1529 //Some modules like cases have a bean name that doesn't match the object name
1530 $bean = BeanFactory::getObjectName($mod);
1531 VardefManager::loadVardef($mod, $bean);
1532 //We can skip modules that are in this package as they will be removed anyhow
1533 if (!in_array($mod, $this->modulesInPackage) && !empty($dictionary[$bean]) && !empty($dictionary[$bean]['fields']))
1535 $field_defs = $dictionary[$bean]['fields'];
1536 foreach($field_defs as $field => $def)
1538 //Weed out most fields first
1539 if (isset ($def['type']))
1541 //Custom relationships created in the relationship editor
1542 if ($def['type'] == "link" && !empty($def['relationship']) && !empty($dictionary[$def['relationship']]))
1544 $rel_name = $def['relationship'];
1546 $rel_def = $dictionary[$rel_name]['relationships'][$rel_name];
1548 //Check against mods to be removed.
1549 foreach($this->modulesInPackage as $removed_mod) {
1550 if ($rel_def['lhs_module'] == $removed_mod || $rel_def['rhs_module'] == $removed_mod )
1552 $dictionary[$rel_name]['from_studio'] = true;
1553 $relationships[$rel_name] = $dictionary[$rel_name];
1557 //Custom "relate" fields created in studio also need to be removed
1558 if ($def['type'] == 'relate' && isset($def['module'])) {
1559 foreach($this->modulesInPackage as $removed_mod) {
1560 if ($def['module'] == $removed_mod)
1562 require_once 'modules/ModuleBuilder/Module/StudioModule.php' ;
1563 $studioMod = new StudioModule ( $mod );
1564 $studioMod->removeFieldFromLayouts( $field );
1565 if (isset($def['custom_module'])) {
1566 require_once ('modules/DynamicFields/DynamicField.php') ;
1567 require_once ($beanFiles [ $bean ]) ;
1568 $seed = new $bean ( ) ;
1569 $df = new DynamicField ( $mod ) ;
1570 $df->setup ( $seed ) ;
1571 //Need to load the entire field_meta_data for some field types
1572 $field_obj = $df->getFieldWidget($mod, $field);
1573 $field_obj->delete ( $df ) ;
1585 $this->uninstall_relationship(null, $relationships);
1587 if(isset($this->installdefs['relationships'])) {
1588 $relationships = $this->installdefs['relationships'];
1589 $this->log(translate('LBL_MI_UN_RELATIONSHIPS') );
1590 foreach($relationships as $relationship)
1592 // remove the metadata entry
1593 $filename = basename ( $relationship['meta_data'] );
1594 $pathname = (file_exists("custom/metadata/$filename")) ? "custom/metadata/$filename" : "metadata/$filename" ;
1595 if(isset($GLOBALS['mi_remove_tables']) && $GLOBALS['mi_remove_tables'])
1596 $this->uninstall_relationship( $pathname );
1597 if (file_exists($pathname))
1598 unlink( $pathname );
1602 if (file_exists("custom/Extension/application/Ext/TableDictionary/{$this->id_name}.php"))
1603 unlink("custom/Extension/application/Ext/TableDictionary/{$this->id_name}.php");
1604 Relationship::delete_cache();
1605 $this->rebuild_tabledictionary();
1611 function uninstall($base_dir){
1612 if(defined('TEMPLATE_URL'))SugarTemplateUtilities::disableCache();
1613 global $app_strings;
1614 $total_steps = 5; //min steps with no tasks
1616 $this->base_dir = $base_dir;
1619 'uninstall_relationships',
1621 'uninstall_dcactions',
1622 'uninstall_dashlets',
1623 'uninstall_connectors',
1624 'uninstall_layoutfields',
1625 'uninstall_extensions',
1626 'uninstall_global_search',
1627 'disable_manifest_logichooks',
1630 $total_steps += count($tasks); //now the real number of steps
1631 if(file_exists($this->base_dir . '/manifest.php')){
1634 display_progress_bar('install', $current_step, $total_steps);
1635 echo '<div id ="displayLoglink" ><a href="#" onclick="toggleDisplay(\'displayLog\')">'.$app_strings['LBL_DISPLAY_LOG'].'</a> </div><div id="displayLog" style="display:none">';
1639 include($this->base_dir . '/manifest.php');
1640 $this->installdefs = $installdefs;
1641 $this->id_name = $this->installdefs['id'];
1642 $installed_modules = array();
1643 if(isset($this->installdefs['beans'])){
1644 foreach($this->installdefs['beans'] as $bean){
1646 $installed_modules[] = $bean['module'];
1647 $this->uninstall_user_prefs($bean['module']);
1649 $this->modulesInPackage = $installed_modules;
1650 $this->uninstall_beans($installed_modules);
1651 $this->uninstall_customizations($installed_modules);
1654 update_progress_bar('install', $total_steps, $total_steps);
1659 update_progress_bar('install', $current_step, $total_steps);
1661 foreach($tasks as $task){
1665 update_progress_bar('install', $current_step, $total_steps);
1668 if(isset($installdefs['custom_fields']) && (isset($GLOBALS['mi_remove_tables']) && $GLOBALS['mi_remove_tables'])){
1669 $this->log(translate('LBL_MI_UN_CUSTOMFIELD'));
1670 $this->uninstall_custom_fields($installdefs['custom_fields']);
1674 update_progress_bar('install', $current_step, $total_steps);
1677 //since we are passing $silent = true to rebuildAll() in that method it will set $this->silent = true, so
1678 //we need to save the setting to set it back after rebuildAll() completes.
1679 $silentBak = $this->silent;
1680 $this->rebuild_all(true);
1681 $this->silent = $silentBak;
1683 //#27877, If the request from MB redeploy a custom module , we will not remove the ACL actions for this package.
1684 if( !isset($_REQUEST['action']) || $_REQUEST['action']!='DeployPackage' ){
1685 $this->remove_acl_actions();
1691 update_progress_bar('install', $current_step, $total_steps);
1695 UpdateSystemTabs('Restore',$installed_modules);
1697 //clear the unified_search_module.php file
1698 require_once('modules/Home/UnifiedSearchAdvanced.php');
1699 UnifiedSearchAdvanced::unlinkUnifiedSearchModulesFile();
1701 $this->log('<br><b>' . translate('LBL_MI_COMPLETE') . '</b>');
1703 update_progress_bar('install', $total_steps, $total_steps);
1706 die("No manifest.php Defined In $this->base_dir/manifest.php");
1710 function rebuild_languages($languages = array(), $modules="")
1712 foreach($languages as $language=>$value){
1713 $this->log(translate('LBL_MI_REBUILDING') . " Language...$language");
1714 $this->merge_files('Ext/Language/', $language.'.lang.ext.php', $language);
1716 foreach($modules as $module){
1717 LanguageManager::clearLanguageCache($module, $language);
1721 sugar_cache_reset();
1724 function rebuild_vardefs()
1726 $this->rebuildExt("Vardefs", 'vardefs.ext.php');
1727 sugar_cache_reset();
1730 function rebuild_dashletcontainers(){
1731 $this->log(translate('LBL_MI_REBUILDING') . " DC Actions...");
1732 $this->merge_files('Ext/DashletContainer/Containers/', 'dcactions.ext.php');
1735 function rebuild_tabledictionary()
1737 $this->rebuildExt("TableDictionary", 'tabledictionary.ext.php');
1740 function rebuild_relationships() {
1741 if(!$this->silent) echo translate('LBL_MI_REBUILDING') . ' Relationships';
1742 $_REQUEST['silent'] = true;
1744 include('include/modules.php');
1745 include("modules/Administration/RebuildRelationship.php");
1748 function remove_acl_actions() {
1749 global $beanFiles, $beanList, $current_user;
1750 include('include/modules.php');
1751 include("modules/ACL/remove_actions.php");
1755 * Wrapper call to modules/Administration/RepairIndex.php
1757 function repair_indices() {
1758 global $current_user,$beanFiles,$dictionary;
1759 $this->log(translate('LBL_MI_REPAIR_INDICES'));
1760 $_REQUEST['silent'] = true; // local var flagging echo'd output in repair script
1761 $_REQUEST['mode'] = 'execute'; // flag to just go ahead and run the script
1762 include("modules/Administration/RepairIndex.php");
1766 * Rebuilds the extension files found in custom/Extension
1767 * @param boolean $silent
1769 function rebuild_all($silent=false){
1770 if(defined('TEMPLATE_URL'))SugarTemplateUtilities::disableCache();
1771 $this->silent=$silent;
1772 global $sugar_config;
1774 //Check for new module extensions
1775 $this->rebuild_modules();
1777 $this->rebuild_languages($sugar_config['languages']);
1778 $this->rebuild_extensions();
1779 $this->rebuild_dashletcontainers();
1780 $this->rebuild_relationships();
1781 $this->rebuild_tabledictionary();
1782 $this->reset_opcodes();
1783 sugar_cache_reset();
1787 * 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
1788 * custom/Extension/modules/$module/<path> (_override files last) and concatenates them to custom/modules/$module/<path>/<file>.
1789 * Then it does the same thing in custom/Extension/application/<path>, concatenating those files and copying the result to custom/application/<path>/<file>
1791 function merge_files($path, $name, $filter = '', $application = false){
1793 $GLOBALS['log']->debug( get_class($this)."->merge_files() : merging module files in custom/Extension/modules/<module>/$path to custom/modules/<module>/$path$name");
1794 foreach($this->modules as $module){
1795 //$GLOBALS['log']->debug("Merging Files for: ".$module);
1796 //$GLOBALS['log']->debug("Merging Files for path: ".$path);
1797 $extension = "<?php \n //WARNING: The contents of this file are auto-generated\n";
1798 $extpath = "modules/$module/$path";
1799 $module_install = 'custom/Extension/'.$extpath;
1800 $shouldSave = false;
1801 if(is_dir($module_install)){
1802 $dir = dir($module_install);
1804 $override = array();
1805 while($entry = $dir->read()){
1806 if((empty($filter) || substr_count($entry, $filter) > 0) && is_file($module_install.'/'.$entry)
1807 && $entry != '.' && $entry != '..' && strtolower(substr($entry, -4)) == ".php")
1809 if (substr($entry, 0, 9) == '_override') {
1810 $override[] = $entry;
1812 $file = file_get_contents($module_install . '/' . $entry);
1813 $GLOBALS['log']->debug(get_class($this)."->merge_files(): found {$module_install}{$entry}") ;
1814 $extension .= "\n". str_replace(array('<?php', '?>', '<?PHP', '<?'), array('','', '' ,'') , $file);
1818 foreach ($override as $entry) {
1819 $file = file_get_contents($module_install . '/' . $entry);
1820 $extension .= "\n". str_replace(array('<?php', '?>', '<?PHP', '<?'), array('','', '' ,'') , $file);
1823 $extension .= "\n?>";
1826 if(!file_exists("custom/$extpath")) {
1827 mkdir_recursive("custom/$extpath", true);
1829 $out = sugar_fopen("custom/$extpath/$name", 'w');
1830 fwrite($out,$extension);
1833 if(file_exists("custom/$extpath/$name")){
1834 unlink("custom/$extpath/$name");
1841 $GLOBALS['log']->debug("Merging application files for $name in $path");
1842 //Now the application stuff
1843 $extension = "<?php \n //WARNING: The contents of this file are auto-generated\n";
1844 $extpath = "application/$path";
1845 $module_install = 'custom/Extension/'.$extpath;
1846 $shouldSave = false;
1847 if(is_dir($module_install)){
1848 $dir = dir($module_install);
1849 while($entry = $dir->read()){
1851 if((empty($filter) || substr_count($entry, $filter) > 0) && is_file($module_install.'/'.$entry)
1852 && $entry != '.' && $entry != '..' && strtolower(substr($entry, -4)) == ".php")
1854 $file = file_get_contents($module_install . '/' . $entry);
1855 $extension .= "\n". str_replace(array('<?php', '?>', '<?PHP', '<?'), array('','', '' ,'') , $file);
1859 $extension .= "\n?>";
1861 if(!file_exists("custom/$extpath")){
1862 mkdir_recursive("custom/$extpath", true);
1864 $out = sugar_fopen("custom/$extpath/$name", 'w');
1865 fwrite($out,$extension);
1868 if(file_exists("custom/$extpath/$name")){
1869 unlink("custom/$extpath/$name");
1875 function install_modules()
1877 $this->installed_modules = array();
1878 $this->tab_modules = array();
1879 if(isset($this->installdefs['beans'])){
1880 $str = "<?php \n //WARNING: The contents of this file are auto-generated\n";
1881 foreach($this->installdefs['beans'] as $bean){
1882 if(!empty($bean['module']) && !empty($bean['class']) && !empty($bean['path'])){
1883 $module = $bean['module'];
1884 $class = $bean['class'];
1885 $path = $bean['path'];
1887 $str .= "\$beanList['$module'] = '$class';\n";
1888 $str .= "\$beanFiles['$class'] = '$path';\n";
1890 $str .= "\$moduleList[] = '$module';\n";
1891 $this->install_user_prefs($module, empty($bean['hide_by_default']));
1892 $this->tab_modules[] = $module;
1894 $str .= "\$modules_exempt_from_availability_check['$module'] = '$module';\n";
1895 $str .= "\$report_include_modules['$module'] = '$module';\n";
1896 $str .= "\$modInvisList[] = '$module';\n";
1898 $this->installed_modules[] = $module;
1900 $errors[] = 'Bean array not well defined.';
1901 $this->abort($errors);
1905 if(!file_exists("custom/Extension/application/Ext/Include")){
1906 mkdir_recursive("custom/Extension/application/Ext/Include", true);
1908 file_put_contents("custom/Extension/application/Ext/Include/{$this->id_name}.php", $str);
1913 * ModuleInstaller->install_beans runs through the list of beans given, instantiates each bean, calls bean->create_tables, and then calls SugarBean::createRelationshipMeta for the
1916 function install_beans($beans){
1917 include('include/modules.php');
1918 foreach($beans as $bean){
1919 $this->log( translate('LBL_MI_IN_BEAN') . " $bean");
1920 if(isset($beanList[$bean])){
1921 $class = $beanList[$bean];
1922 if(file_exists($beanFiles[$class])){
1923 require_once($beanFiles[$class]);
1924 $mod = new $class();
1926 if(is_subclass_of($mod, 'SugarBean') && $mod->disable_vardefs == false ){
1927 $GLOBALS['log']->debug( "Creating Tables Bean : $bean");
1928 $mod->create_tables();
1929 SugarBean::createRelationshipMeta($mod->getObjectName(), $mod->db,$mod->table_name,'',$mod->module_dir);
1932 $GLOBALS['log']->debug( "File Does Not Exist:" . $beanFiles[$class] );
1938 function uninstall_beans($beans){
1939 include('include/modules.php');
1940 foreach($beans as $bean){
1941 $this->log( translate('LBL_MI_UN_BEAN') . " $bean");
1942 if(isset($beanList[$bean])){
1943 $class = $beanList[$bean];
1945 if(file_exists($beanFiles[$class])){
1946 require_once($beanFiles[$class]);
1947 $mod = new $class();
1949 if(is_subclass_of($mod, 'SugarBean')){
1950 $GLOBALS['log']->debug( "Drop Tables : $bean");
1951 if(isset($GLOBALS['mi_remove_tables']) && $GLOBALS['mi_remove_tables'])
1952 $mod->drop_tables();
1955 $GLOBALS['log']->debug( "File Does Not Exist:" . $beanFiles[$class] );
1962 * Remove any customizations made within Studio while the module was installed.
1964 function uninstall_customizations($beans){
1965 foreach($beans as $bean){
1967 'custom/modules/' . $bean,
1968 'custom/Extension/modules/' . $bean,
1969 'custom/working/modules/' . $bean
1971 foreach($dirs as $dir)
1974 rmdir_recursive($dir);
1981 $GLOBALS['log']->debug('ModuleInstaller:'. $str);
1987 /* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:15:18 PM */
1988 function copy_recursive_with_backup( $source, $dest, $backup_path, $uninstall=false ) {
1989 if(is_file($source)) {
1991 $GLOBALS['log']->debug("Restoring ... " . $source. " to " .$dest );
1992 if(copy( $source, $dest)) {
1993 if(is_writable($dest))
1994 sugar_touch( $dest, filemtime($source) );
1995 return(unlink($source));
1998 $GLOBALS['log']->debug( "Can't restore file: " . $source );
2003 if(file_exists($dest)) {
2004 $rest = clean_path($backup_path."/$dest");
2005 if( !is_dir(dirname($rest)) )
2006 mkdir_recursive(dirname($rest), true);
2008 $GLOBALS['log']->debug("Backup ... " . $dest. " to " .$rest );
2009 if(copy( $dest, $rest)) {
2010 if(is_writable($rest))
2011 sugar_touch( $rest, filemtime($dest) );
2014 $GLOBALS['log']->debug( "Can't backup file: " . $dest );
2017 return( copy( $source, $dest ) );
2020 elseif(!is_dir($source)) {
2023 return(unlink($dest));
2025 //don't do anything we already cleaned up the files using uninstall_new_files
2033 if( !is_dir($dest) && !$uninstall){
2034 sugar_mkdir( $dest );
2039 $d = dir( $source );
2040 while( $f = $d->read() ){
2041 if( $f == "." || $f == ".." ){
2044 $status &= $this->copy_recursive_with_backup( "$source/$f", "$dest/$f", $backup_path, $uninstall );
2050 private function dir_get_files($path, $base_path){
2052 if(!is_dir($path))return $files;
2054 while ($e = $d->read()){
2055 //ignore invisible files . .. ._MACOSX
2056 if(substr($e, 0, 1) == '.')continue;
2057 if(is_file($path . '/' . $e))$files[str_replace($base_path , '', $path . '/' . $e)] = str_replace($base_path , '', $path . '/' . $e);
2058 if(is_dir($path . '/' . $e))$files = array_merge($files, $this->dir_get_files($path . '/' . $e, $base_path));
2065 private function dir_file_count($path){
2066 //if its a file then it has at least 1 file in the directory
2067 if(is_file($path)) return 1;
2068 if(!is_dir($path)) return 0;
2071 while ($e = $d->read()){
2072 //ignore invisible files . .. ._MACOSX
2073 if(substr($e, 0, 1) == '.')continue;
2074 if(is_file($path . '/' . $e))$count++;
2075 if(is_dir($path . '/' . $e))$count += $this->dir_file_count($path . '/' . $e);
2082 /* END - RESTORE POINT - by MR. MILK August 31, 2005 02:15:34 PM */
2086 * Static function which allows a module developer to abort their progress, pass in an array of errors and
2087 * redirect back to the main module loader page
2089 * @param errors an array of error messages which will be displayed on the
2090 * main module loader page once it is loaded.
2092 function abort($errors = array()){
2093 //set the errors onto the session so we can display them one the moduler loader page loads
2094 $_SESSION['MODULEINSTALLER_ERRORS'] = $errors;
2095 echo '<META HTTP-EQUIV="Refresh" content="0;url=index.php?module=Administration&action=UpgradeWizard&view=module">';
2097 //header('Location: index.php?module=Administration&action=UpgradeWizard&view=module');
2101 * Return the set of errors stored in the SESSION
2103 * @return an array of errors
2105 function getErrors(){
2106 if(!empty($_SESSION['MODULEINSTALLER_ERRORS'])){
2107 $errors = $_SESSION['MODULEINSTALLER_ERRORS'];
2108 unset($_SESSION['MODULEINSTALLER_ERRORS']);
2116 * Add any fields to the DetailView and EditView of the appropriate modules
2117 * Only add into deployed modules, as addFieldsToUndeployedLayouts has done this already for undeployed modules (and the admin might have edited the layouts already)
2118 * @param array $layoutAdditions An array of module => fieldname
2121 function addFieldsToLayout($layoutAdditions) {
2122 require_once 'modules/ModuleBuilder/parsers/views/GridLayoutMetaDataParser.php' ;
2124 // these modules either lack editviews/detailviews or use custom mechanisms for the editview/detailview.
2125 // In either case, we don't want to attempt to add a relate field to them
2126 // would be better if GridLayoutMetaDataParser could handle this gracefully, so we don't have to maintain this list here
2127 $invalidModules = array ( 'emails' , 'kbdocuments' ) ;
2129 foreach ( $layoutAdditions as $deployedModuleName => $fieldName )
2131 if ( ! in_array( strtolower ( $deployedModuleName ) , $invalidModules ) )
2133 foreach ( array ( MB_EDITVIEW , MB_DETAILVIEW ) as $view )
2135 $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . ": adding $fieldName to $view layout for module $deployedModuleName" ) ;
2136 $parser = new GridLayoutMetaDataParser ( $view, $deployedModuleName ) ;
2137 $parser->addField ( array ( 'name' => $fieldName ) ) ;
2138 $parser->handleSave ( false ) ;
2145 function removeFieldsFromLayout($layoutAdditions) {
2146 require_once 'modules/ModuleBuilder/parsers/views/GridLayoutMetaDataParser.php' ;
2148 // these modules either lack editviews/detailviews or use custom mechanisms for the editview/detailview.
2149 // In either case, we don't want to attempt to add a relate field to them
2150 // would be better if GridLayoutMetaDataParser could handle this gracefully, so we don't have to maintain this list here
2151 $invalidModules = array ( 'emails' , 'kbdocuments' ) ;
2153 foreach ( $layoutAdditions as $deployedModuleName => $fieldName )
2155 if ( ! in_array( strtolower ( $deployedModuleName ) , $invalidModules ) )
2157 foreach ( array ( MB_EDITVIEW , MB_DETAILVIEW ) as $view )
2159 $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . ": adding $fieldName to $view layout for module $deployedModuleName" ) ;
2160 $parser = new GridLayoutMetaDataParser ( $view, $deployedModuleName ) ;
2161 $parser->removeField ( $fieldName ) ;
2162 $parser->handleSave ( false ) ;
2170 //********** DISABLE/ENABLE FUNCTIONS
2172 function enable($base_dir, $is_upgrade = false, $previous_version = ''){
2173 global $app_strings;
2174 $this->base_dir = $base_dir;
2175 $total_steps = 3; //minimum number of steps with no tasks
2180 'enable_relationships',
2181 'enable_extensions',
2182 'enable_global_search',
2183 'enable_manifest_logichooks',
2186 $total_steps += count($tasks);
2187 if(file_exists($this->base_dir . '/manifest.php')){
2190 display_progress_bar('install', $current_step, $total_steps);
2191 echo '<div id ="displayLoglink" ><a href="#" onclick="toggleDisplay(\'displayLog\')">'.$app_strings['LBL_DISPLAY_LOG'].'</a> </div><div id="displayLog" style="display:none">';
2194 require_once($this->base_dir . '/manifest.php');
2195 if($is_upgrade && !empty($previous_version)){
2196 //check if the upgrade path exists
2197 if(!empty($upgrade_manifest)){
2198 if(!empty($upgrade_manifest['upgrade_paths'])){
2199 if(!empty($upgrade_manifest['upgrade_paths'][$previous_version])){
2200 $installdefs = $upgrade_manifest['upgrade_paths'][$previous_version];
2202 $errors[] = 'No Upgrade Path Found in manifest.';
2203 $this->abort($errors);
2208 $this->id_name = $installdefs['id'];
2209 $this->installdefs = $installdefs;
2210 $installed_modules = array();
2211 if(isset($installdefs['beans'])){
2212 foreach($this->installdefs['beans'] as $bean){
2213 $installed_modules[] = $bean['module'];
2218 update_progress_bar('install', $current_step, $total_steps);
2221 foreach($tasks as $task){
2225 update_progress_bar('install', $current_step, $total_steps);
2231 update_progress_bar('install', $current_step, $total_steps);
2234 UpdateSystemTabs('Add',$installed_modules);
2235 $GLOBALS['log']->debug('Complete');
2238 die("No \$installdefs Defined In $this->base_dir/manifest.php");
2242 function disable($base_dir){
2243 global $app_strings;
2244 $total_steps = 3; //min steps with no tasks
2246 $this->base_dir = $base_dir;
2250 'disable_relationships',
2251 'disable_extensions',
2252 'disable_global_search',
2253 'disable_manifest_logichooks',
2256 $total_steps += count($tasks); //now the real number of steps
2257 if(file_exists($this->base_dir . '/manifest.php')){
2260 display_progress_bar('install', $current_step, $total_steps);
2261 echo '<div id ="displayLoglink" ><a href="#" onclick="toggleDisplay(\'displayLog\')">'.$app_strings['LBL_DISPLAY_LOG'].'</a> </div><div id="displayLog" style="display:none">';
2264 require_once($this->base_dir . '/manifest.php');
2265 $this->installdefs = $installdefs;
2266 $this->id_name = $this->installdefs['id'];
2267 $installed_modules = array();
2268 if(isset($this->installdefs['beans'])){
2269 foreach($this->installdefs['beans'] as $bean){
2270 $installed_modules[] = $bean['module'];
2275 update_progress_bar('install', $current_step, $total_steps);
2277 foreach($tasks as $task){
2281 update_progress_bar('install', $current_step, $total_steps);
2286 update_progress_bar('install', $current_step, $total_steps);
2289 UpdateSystemTabs('Restore',$installed_modules);
2292 die("No manifest.php Defined In $this->base_dir/manifest.php");
2296 function enable_vardef($to_module)
2298 $this->enableExt("vardefs", "Vardefs", $to_module);
2301 function enable_layoutdef($to_module)
2303 $this->enableExt("layoutdefs", "Layoutdefs", $to_module);
2306 function enable_relationships(){
2307 if(isset($this->installdefs['relationships'])){
2308 $str = "<?php \n //WARNING: The contents of this file are auto-generated\n";
2309 $save_table_dictionary = false;
2310 foreach($this->installdefs['relationships'] as $relationship){
2311 $filename =basename($relationship['meta_data']);
2313 $save_table_dictionary = true;
2314 $str .= "include_once('metadata/$filename');\n";
2315 if (empty($relationship['module']))
2318 if(!empty($relationship['module_vardefs'])){
2319 $this->enable_vardef($relationship['module']);
2321 if(!empty($relationship['module_layoutdefs'])){
2322 $this->enable_layoutdef($relationship['module']);
2325 $this->rebuild_vardefs();
2326 $this->rebuild_layoutdefs();
2327 if($save_table_dictionary){
2328 if(!file_exists("custom/Extension/application/Ext/TableDictionary")){
2329 mkdir_recursive("custom/Extension/application/Ext/TableDictionary", true);
2331 if (file_exists("custom/Extension/application/Ext/TableDictionary/".DISABLED_PATH."/$this->id_name.php"))
2332 rename("custom/Extension/application/Ext/TableDictionary/".DISABLED_PATH."/$this->id_name.php", "custom/Extension/application/Ext/TableDictionary/$this->id_name.php");
2333 $this->rebuild_tabledictionary();
2338 function disable_relationships($action = 'disable'){
2339 if(isset($this->installdefs['relationships'])){
2340 foreach($this->installdefs['relationships'] as $relationship){
2341 $filename = basename($relationship['meta_data']);
2342 $relName = substr($filename, -12) == "MetaData.php" ? substr($filename,0,strlen($filename) - 12) : "";
2343 if (empty($relationship['module']) && empty($relName))
2346 //remove the vardefs
2347 if (empty($relName))
2348 $path = 'custom/Extension/modules/' . $relationship['module']. '/Ext/Vardefs';
2349 if(!empty($relationship['module']) && $relationship['module'] == 'application'){
2350 $path ='custom/Extension/' . $relationship['module']. '/Ext/Vardefs';
2352 if(!empty($relationship['module_vardefs']) && file_exists($path . '/'. $this->id_name . '.php')){
2353 mkdir_recursive($path . '/'.DISABLED_PATH, true);
2354 rename( $path . '/'. $this->id_name . '.php', $path . '/'.DISABLED_PATH.'/'. $this->id_name . '.php');
2356 //remove the layoutdefs
2357 if ( !empty($relationship['module']) ) {
2358 $path = 'custom/Extension/modules/' . $relationship['module']. '/Ext/Layoutdefs';
2359 if($relationship['module'] == 'application'){
2360 $path ='custom/Extension/' . $relationship['module']. '/Ext/Layoutdefs';
2364 if(!empty($relationship['module_layoutdefs']) && file_exists($path . '/'. $this->id_name . '.php')){
2365 mkdir_recursive($path . '/'.DISABLED_PATH, true);
2366 rename( $path . '/'. $this->id_name . '.php', $path . '/'.DISABLED_PATH.'/'. $this->id_name . '.php');
2370 if(file_exists("custom/Extension/application/Ext/TableDictionary/$this->id_name.php")){
2371 mkdir_recursive("custom/Extension/application/Ext/TableDictionary/".DISABLED_PATH, true);
2372 rename("custom/Extension/application/Ext/TableDictionary/$this->id_name.php", "custom/Extension/application/Ext/TableDictionary/".DISABLED_PATH."/$this->id_name.php");
2374 $this->rebuild_tabledictionary();
2375 $this->rebuild_vardefs();
2376 $this->rebuild_layoutdefs();
2380 function enable_dashlets(){
2381 if(isset($this->installdefs['dashlets'])){
2382 foreach($this->installdefs['dashlets'] as $cp){
2383 $cp['from'] = str_replace('<basepath>', $this->base_dir, $cp['from']);
2384 $path = 'custom/modules/Home/Dashlets/' . $cp['name'] . '/';
2385 $disabled_path = 'custom/modules/Home/'.DISABLED_PATH.'Dashlets/' . $cp['name'];
2386 $GLOBALS['log']->debug("Enabling Dashlet " . $cp['name'] . "..." . $cp['from'] );
2387 if (file_exists($disabled_path))
2389 rename($disabled_path, $path);
2392 include('modules/Administration/RebuildDashlets.php');
2397 function disable_dashlets(){
2398 if(isset($this->installdefs['dashlets'])){
2399 foreach($this->installdefs['dashlets'] as $cp){
2400 $path = 'custom/modules/Home/Dashlets/' . $cp['name'];
2401 $disabled_path = 'custom/modules/Home/'.DISABLED_PATH.'Dashlets/' . $cp['name'];
2402 $GLOBALS['log']->debug('Disabling ' .$path);
2403 if (file_exists($path))
2405 mkdir_recursive('custom/modules/Home/'.DISABLED_PATH.'Dashlets/', true);
2406 rename( $path, $disabled_path);
2409 include('modules/Administration/RebuildDashlets.php');
2413 function enable_copy(){
2414 //copy files back onto file system. first perform md5 check to determine if anything has been modified
2415 //here we should just go through the files in the -restore directory and copy those back
2416 if(isset($GLOBALS['mi_overwrite_files']) && $GLOBALS['mi_overwrite_files']){
2417 if(!empty($this->installdefs['copy'])){
2418 foreach($this->installdefs['copy'] as $cp){
2419 $cp['to'] = clean_path(str_replace('<basepath>', $this->base_dir, $cp['to']));
2420 $backup_path = clean_path( remove_file_extension(urldecode(hashToFile($_REQUEST['install_file'])))."-restore/".$cp['to'] );
2422 //check if this file exists in the -restore directory
2423 if(file_exists($backup_path)){
2424 //since the file exists, then we want do an md5 of the install version and the file system version
2425 //if(is_file($backup_path) && md5_file($backup_path) == md5_file($cp['to'])){
2426 //since the files are the same then we can safely move back from the -restore
2427 //directory into the file system
2428 $GLOBALS['log']->debug("ENABLE COPY:: FROM: ".$cp['from']. " TO: ".$cp['to']);
2429 $this->copy_path($cp['from'], $cp['to']);
2431 //since they are not equal then we need to prompt the user
2439 function disable_copy(){
2440 //when we disable we want to copy the -restore files back into the file system
2441 //but we should check the version in the module install against the version on the file system
2442 //if they match then we can copy the file back, but otherwise we should ask the user.
2444 // $GLOBALS['log']->debug('ModuleInstaller.php->disable_copy()');
2445 if(isset($GLOBALS['mi_overwrite_files']) && $GLOBALS['mi_overwrite_files']){
2446 // $GLOBALS['log']->debug('ModuleInstaller.php->disable_copy():mi_overwrite_files=true');
2447 if(!empty($this->installdefs['copy'])){
2448 // $GLOBALS['log']->debug('ModuleInstaller.php->disable_copy(): installdefs not empty');
2449 foreach($this->installdefs['copy'] as $cp){
2450 $cp['to'] = clean_path(str_replace('<basepath>', $this->base_dir, $cp['to']));
2451 $backup_path = clean_path( remove_file_extension(urldecode(hashToFile($_REQUEST['install_file'])))."-restore/".$cp['to'] ); // bug 16966 tyoung - replaced missing assignment to $backup_path
2452 //check if this file exists in the -restore directory
2453 // $GLOBALS['log']->debug("ModuleInstaller.php->disable_copy(): backup_path=".$backup_path);
2454 if(file_exists($backup_path)){
2455 //since the file exists, then we want do an md5 of the install version and the file system version
2456 $from = str_replace('<basepath>', $this->base_dir, $cp['from']);
2458 //if(is_file($from) && md5_file($from) == md5_file($cp['to'])){
2459 //since the files are the same then we can safely move back from the -restore
2460 //directory into the file system
2461 $GLOBALS['log']->debug("DISABLE COPY:: FROM: ".$backup_path. " TO: ".$cp['to']);
2462 $this->copy_path($backup_path, $cp['to']);
2464 //since they are not equal then we need to prompt the user
2472 public function reset_opcodes()
2474 /* Bug 39354 - added function_exists check. Not optimal fix, but safe nonetheless.
2475 * 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,
2476 * but this file has been updated to 6.1
2478 if(function_exists('sugar_clean_opcodes')){
2479 sugar_clean_opcodes();
2484 * BC implementation to provide specific calls to extensions
2486 public function __call($name, $args)
2488 $nameparts = explode('_', $name);
2489 // name is something_something
2490 if(count($nameparts) == 2 && isset($this->extensions[$nameparts[1]])) {
2491 $ext = $this->extensions[$nameparts[1]];
2492 switch($nameparts[0]) {
2494 return $this->enableExt($ext['section'], $ext['extdir']);
2496 return $this->disableExt($ext['section'], $ext['extdir']);
2498 return $this->installExt($ext['section'], $ext['extdir']);
2500 return $this->uninstallExt($ext['section'], $ext['extdir']);
2502 return $this->rebuildExt($ext['extdir'], $ext['file']);
2505 sugar_die("Unknown method ModuleInstaller::$name called");
2510 function UpdateSystemTabs($action, $installed_modules){
2511 require_once("modules/MySettings/TabController.php");
2512 $controller = new TabController();
2513 $isSystemTabsInDB = $controller->is_system_tabs_in_db();
2514 if ($isSystemTabsInDB && !empty($installed_modules))
2520 $currentTabs = $controller->get_system_tabs();
2521 foreach ($installed_modules as $module)
2523 if(in_array($module, $currentTabs)){
2524 unset($currentTabs[$module]);
2527 $controller->set_system_tabs($currentTabs);;
2530 $currentTabs = $controller->get_system_tabs();
2531 foreach ($installed_modules as $module)
2533 if(!in_array($module, $currentTabs)){
2534 $currentTabs[$module] = $module;
2537 $controller->set_system_tabs($currentTabs);