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-2011 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(!empty($GLOBALS['sugar_config']['moduleInstaller']['packageScan'])){
85 $this->ms->scanPackage($base_dir);
86 if($this->ms->hasIssues()){
87 $this->ms->displayIssues();
92 global $app_strings, $mod_strings;
93 $this->base_dir = $base_dir;
94 $total_steps = 5; //minimum number of steps with no tasks
103 'install_connectors',
104 'install_layoutfields',
105 'install_relationships',
106 'enable_manifest_logichooks',
111 $total_steps += count($tasks);
112 if(file_exists($this->base_dir . '/manifest.php')){
115 display_progress_bar('install', $current_step, $total_steps);
116 echo '<div id ="displayLoglink" ><a href="#" onclick="document.getElementById(\'displayLog\').style.display=\'\'">'
117 .$app_strings['LBL_DISPLAY_LOG'].'</a> </div><div id="displayLog" style="display:none">';
120 include($this->base_dir . '/manifest.php');
121 if($is_upgrade && !empty($previous_version)){
122 //check if the upgrade path exists
123 if(!empty($upgrade_manifest)){
124 if(!empty($upgrade_manifest['upgrade_paths'])){
125 if(!empty($upgrade_manifest['upgrade_paths'][$previous_version])){
126 $installdefs = $upgrade_manifest['upgrade_paths'][$previous_version];
128 $errors[] = 'No Upgrade Path Found in manifest.';
129 $this->abort($errors);
134 $this->id_name = $installdefs['id'];
135 $this->installdefs = $installdefs;
138 update_progress_bar('install', $current_step, $total_steps);
141 foreach($tasks as $task){
145 update_progress_bar('install', $current_step, $total_steps);
148 $this->install_beans($this->installed_modules);
151 update_progress_bar('install', $total_steps, $total_steps);
153 if(isset($installdefs['custom_fields'])){
154 $this->log(translate('LBL_MI_IN_CUSTOMFIELD'));
155 $this->install_custom_fields($installdefs['custom_fields']);
159 update_progress_bar('install', $current_step, $total_steps);
164 update_progress_bar('install', $current_step, $total_steps);
167 $selectedActions = array(
173 'rebuildAuditTables',
176 VardefManager::clearVardef();
177 global $beanList, $beanFiles, $moduleList;
178 if (file_exists('custom/application/Ext/Include/modules.ext.php'))
180 include('custom/application/Ext/Include/modules.ext.php');
182 require_once("modules/Administration/upgrade_custom_relationships.php");
183 upgrade_custom_relationships($this->installed_modules);
184 $this->rebuild_all(true);
185 require_once('modules/Administration/QuickRepairAndRebuild.php');
186 $rac = new RepairAndClear();
187 $rac->repairAndClearAll($selectedActions, $this->installed_modules,true, false);
188 $this->rebuild_relationships();
189 UpdateSystemTabs('Add',$this->tab_modules);
191 //clear the unified_search_module.php file
192 require_once('modules/Home/UnifiedSearchAdvanced.php');
193 UnifiedSearchAdvanced::unlinkUnifiedSearchModulesFile();
195 $this->log('<br><b>' . translate('LBL_MI_COMPLETE') . '</b>');
197 die("No \$installdefs Defined In $this->base_dir/manifest.php");
202 function install_user_prefs($module, $hide_from_user=false){
203 UserPreference::updateAllUserPrefs('display_tabs', $module, '', true, !$hide_from_user);
204 UserPreference::updateAllUserPrefs('hide_tabs', $module, '', true, $hide_from_user);
205 UserPreference::updateAllUserPrefs('remove_tabs', $module, '', true, $hide_from_user);
207 function uninstall_user_prefs($module){
208 UserPreference::updateAllUserPrefs('display_tabs', $module, '', true, true);
209 UserPreference::updateAllUserPrefs('hide_tabs', $module, '', true, true);
210 UserPreference::updateAllUserPrefs('remove_tabs', $module, '', true, true);
213 function pre_execute(){
214 require_once($this->base_dir . '/manifest.php');
215 if(isset($this->installdefs['pre_execute']) && is_array($this->installdefs['pre_execute'])){
216 foreach($this->installdefs['pre_execute'] as $includefile){
217 require_once(str_replace('<basepath>', $this->base_dir, $includefile));
222 function post_execute(){
223 require_once($this->base_dir . '/manifest.php');
224 if(isset($this->installdefs['post_execute']) && is_array($this->installdefs['post_execute'])){
225 foreach($this->installdefs['post_execute'] as $includefile){
226 require_once(str_replace('<basepath>', $this->base_dir, $includefile));
231 function pre_uninstall(){
232 require_once($this->base_dir . '/manifest.php');
233 if(isset($this->installdefs['pre_uninstall']) && is_array($this->installdefs['pre_uninstall'])){
234 foreach($this->installdefs['pre_uninstall'] as $includefile){
235 require_once(str_replace('<basepath>', $this->base_dir, $includefile));
240 function post_uninstall(){
241 require_once($this->base_dir . '/manifest.php');
242 if(isset($this->installdefs['post_uninstall']) && is_array($this->installdefs['post_uninstall'])){
243 foreach($this->installdefs['post_uninstall'] as $includefile){
244 require_once(str_replace('<basepath>', $this->base_dir, $includefile));
250 * 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
251 * (specified as from and to in the manifest), replacing <basepath> by the base_dir value passed in to install.
253 function install_copy(){
254 if(isset($this->installdefs['copy'])){
255 /* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:22:11 PM */
256 $backup_path = clean_path( remove_file_extension(urldecode(hashToFile($_REQUEST['install_file'])))."-restore" );
257 /* END - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
258 foreach($this->installdefs['copy'] as $cp){
259 $GLOBALS['log']->debug("Copying ..." . $cp['from']. " to " .$cp['to'] );
260 /* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:22:11 PM */
261 //$this->copy_path($cp['from'], $cp['to']);
262 $this->copy_path($cp['from'], $cp['to'], $backup_path);
263 /* END - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
265 //here we should get the module list again as we could have copied something to the modules dir
266 $this->modules = get_module_dir_list();
269 function uninstall_copy(){
270 if(!empty($this->installdefs['copy'])){
271 foreach($this->installdefs['copy'] as $cp){
272 $cp['to'] = clean_path(str_replace('<basepath>', $this->base_dir, $cp['to']));
273 $cp['from'] = clean_path(str_replace('<basepath>', $this->base_dir, $cp['from']));
274 $GLOBALS['log']->debug('Unlink ' . $cp['to']);
275 /* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:22:11 PM */
276 //rmdir_recursive($cp['to']);
278 $backup_path = clean_path( remove_file_extension(urldecode(hashToFile($_REQUEST['install_file'])))."-restore/".$cp['to'] );
279 $this->uninstall_new_files($cp, $backup_path);
280 $this->copy_path($backup_path, $cp['to'], $backup_path, true);
281 /* END - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
283 $backup_path = clean_path( remove_file_extension(urldecode(hashToFile($_REQUEST['install_file'])))."-restore");
284 if(file_exists($backup_path))
285 rmdir_recursive($backup_path);
291 * Removes any files that were added by the loaded module. If the files already existed prior to install
292 * it will be handled by copy_path with the uninstall parameter.
295 function uninstall_new_files($cp, $backup_path){
296 $zip_files = $this->dir_get_files($cp['from'],$cp['from']);
297 $backup_files = $this->dir_get_files($backup_path, $backup_path);
298 foreach($zip_files as $k=>$v){
299 //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
300 if(!isset($backup_files[$k])){
301 $to = $cp['to'] . $k;
302 //if it's not a sugar file then we remove it otherwise we can't restor it
303 if(!$this->ms->sugarFileExists($to)){
304 $GLOBALS['log']->debug('ModuleInstaller[uninstall_new_file] deleting file ' . $to);
305 if(file_exists($to)) {
309 $GLOBALS['log']->fatal('ModuleInstaller[uninstall_new_file] Could not remove file ' . $to . ' as no backup file was found to restore to');
313 //lets check if the directory is empty if it is we will delete it as well
314 $files_remaining = $this->dir_file_count($cp['to']);
315 if(file_exists($cp['to']) && $files_remaining == 0){
316 $GLOBALS['log']->debug('ModuleInstaller[uninstall_new_file] deleting directory ' . $cp['to']);
317 rmdir_recursive($cp['to']);
323 * Get directory where module's extensions go
324 * @param string $module Module name
326 public function getExtDir($module)
328 if($module == 'application') {
329 return "custom/Extension/application/Ext";
331 return "custom/Extension/modules/$module/Ext";
336 * Install file(s) into Ext/ part
337 * @param string $section Name of the install file section
338 * @param string $extname Name in Ext directory
339 * @param string $module This extension belongs to a specific module
341 public function installExt($section, $extname, $module = '')
343 if(isset($this->installdefs[$section])){
344 $this->log(sprintf(translate("LBL_MI_IN_EXT"), $section));
345 foreach($this->installdefs[$section] as $item){
346 if(isset($item['from'])) {
347 $from = str_replace('<basepath>', $this->base_dir, $item['from']);
351 if(!empty($module)) {
352 $item['to_module'] = $module;
354 $GLOBALS['log']->debug("Installing section $section from $from for " .$item['to_module'] );
355 if($item['to_module'] == 'application') {
356 $path = "custom/Extension/application/Ext/$extname";
358 $path = "custom/Extension/modules/{$item['to_module']}/Ext/$extname";
360 if(!file_exists($path)){
361 mkdir_recursive($path, true);
363 if(isset($item["name"])) {
364 $target = $item["name"];
365 } else if (!empty($from)){
366 $target = basename($from, ".php");
368 $target = $this->id_name;
371 copy_recursive($from , "$path/$target.php");
378 * Uninstall file(s) into Ext/ part
379 * @param string $section Name of the install file section
380 * @param string $extname Name in Ext directory
381 * @param string $module This extension belongs to a specific module
383 public function uninstallExt($section, $extname, $module = '')
385 if(isset($this->installdefs[$section])){
386 $this->log(sprintf(translate("LBL_MI_UN_EXT"), $section));
387 foreach($this->installdefs[$section] as $item){
388 if(isset($item['from'])) {
389 $from = str_replace('<basepath>', $this->base_dir, $item['from']);
393 if(!empty($module)) {
394 $item['to_module'] = $module;
396 $GLOBALS['log']->debug("Uninstalling section $section from $from for " .$item['to_module'] );
397 if($item['to_module'] == 'application') {
398 $path = "custom/Extension/application/Ext/$extname";
400 $path = "custom/Extension/modules/{$item['to_module']}/Ext/$extname";
402 if(isset($item["name"])) {
403 $target = $item["name"];
404 } else if (!empty($from)){
405 $target = basename($from, ".php");
407 $target = $this->id_name;
409 $disabled_path = $path.'/'.DISABLED_PATH;
410 if (file_exists("$path/$target.php")) {
411 rmdir_recursive("$path/$target.php");
412 } else if (file_exists("$disabled_path/$target.php")) {
413 rmdir_recursive("$disabled_path/$target.php");
414 } else if (!empty($from) && file_exists($path . '/'. basename($from))) {
415 rmdir_recursive( $path . '/'. basename($from));
416 } else if (!empty($from) && file_exists($disabled_path . '/'. basename($from))) {
417 rmdir_recursive( $disabled_path . '/'. basename($from));
424 * Rebuild generic extension
425 * @param string $ext Extension directory
426 * @param string $filename Target filename
428 public function rebuildExt($ext, $filename)
430 $this->log(translate('LBL_MI_REBUILDING') . " $ext...");
431 $this->merge_files("Ext/$ext/", $filename);
435 * Disable generic extension
436 * @param string $section Install file section name
437 * @param string $extname Extension directory
438 * @param string $module This extension belongs to a specific module
440 public function disableExt($section, $extname, $module = '')
442 if(isset($this->installdefs[$section])) {
443 foreach($this->installdefs[$section] as $item) {
444 if(isset($item['from'])) {
445 $from = str_replace('<basepath>', $this->base_dir, $item['from']);
449 if(!empty($module)) {
450 $item['to_module'] = $module;
452 $GLOBALS['log']->debug("Disabling $extname ... from $from for " .$item['to_module']);
453 if($item['to_module'] == 'application') {
454 $path = "custom/Extension/application/Ext/$extname";
456 $path = "custom/Extension/modules/{$item['to_module']}/Ext/$extname";
458 if(isset($item["name"])) {
459 $target = $item["name"];
460 } else if (!empty($from)){
461 $target = basename($from, ".php");
463 $target = $this->id_name;
465 $disabled_path = $path.'/'.DISABLED_PATH;
466 if (file_exists("$path/$target.php")) {
467 mkdir_recursive($disabled_path, true);
468 rename("$path/$target.php", "$disabled_path/$target.php");
469 } else if (!empty($from) && file_exists($path . '/'. basename($from))) {
470 mkdir_recursive($disabled_path, true);
471 rename( $path . '/'. basename($from), $disabled_path.'/'. basename($from));
478 * Enable generic extension
479 * @param string $section Install file section name
480 * @param string $extname Extension directory
481 * @param string $module This extension belongs to a specific module
483 public function enableExt($section, $extname, $module = '')
485 if(isset($this->installdefs[$section])) {
486 foreach($this->installdefs[$section] as $item) {
487 if(isset($item['from'])) {
488 $from = str_replace('<basepath>', $this->base_dir, $item['from']);
492 if(!empty($module)) {
493 $item['to_module'] = $module;
495 $GLOBALS['log']->debug("Enabling $extname ... from $from for " .$item['to_module']);
497 if($item['to_module'] == 'application') {
498 $path = "custom/Extension/application/Ext/$extname";
500 $path = "custom/Extension/modules/{$item['to_module']}/Ext/$extname";
502 if(isset($item["name"])) {
503 $target = $item["name"];
504 } else if (!empty($from)){
505 $target = basename($from, ".php");
507 $target = $this->id_name;
509 if(!file_exists($path)) {
510 mkdir_recursive($path, true);
512 $disabled_path = $path.'/'.DISABLED_PATH;
513 if (file_exists("$disabled_path/$target.php")) {
514 rename("$disabled_path/$target.php", "$path/$target.php");
516 if (!empty($from) && file_exists($disabled_path . '/'. basename($from))) {
517 rename($disabled_path.'/'. basename($from), $path . '/'. basename($from));
523 public function install_extensions()
525 foreach($this->extensions as $extname => $ext) {
526 $install = "install_$extname";
527 if(method_exists($this, $install)) {
528 // non-standard function
531 if(!empty($ext["section"])) {
532 $module = isset($ext['module'])?$ext['module']:'';
533 $this->installExt($ext["section"], $ext["extdir"], $module);
537 $this->rebuild_extensions();
540 public function uninstall_extensions()
542 foreach($this->extensions as $extname => $ext) {
543 $func = "uninstall_$extname";
544 if(method_exists($this, $func)) {
545 // non-standard function
548 if(!empty($ext["section"])) {
549 $module = isset($ext['module'])?$ext['module']:'';
550 $this->uninstallExt($ext["section"], $ext["extdir"], $module);
554 $this->rebuild_extensions();
557 public function rebuild_extensions()
559 foreach($this->extensions as $extname => $ext) {
560 $func = "rebuild_$extname";
561 if(method_exists($this, $func)) {
562 // non-standard function
565 $this->rebuildExt($ext["extdir"], $ext["file"]);
570 public function disable_extensions()
572 foreach($this->extensions as $extname => $ext) {
573 $func = "disable_$extname";
574 if(method_exists($this, $func)) {
575 // non-standard install
578 if(!empty($ext["section"])) {
579 $module = isset($ext['module'])?$ext['module']:'';
580 $this->disableExt($ext["section"], $ext["extdir"], $module);
584 $this->rebuild_extensions();
587 public function enable_extensions()
589 foreach($this->extensions as $extname => $ext) {
590 $func = "enable_$extname";
591 if(method_exists($this, $func)) {
592 // non-standard install
595 if(!empty($ext["section"])) {
596 $module = isset($ext['module'])?$ext['module']:'';
597 $this->enableExt($ext["section"], $ext["extdir"], $module);
601 $this->rebuild_extensions();
604 function install_dashlets()
606 if(isset($this->installdefs['dashlets'])){
607 foreach($this->installdefs['dashlets'] as $cp){
608 $this->log(translate('LBL_MI_IN_DASHLETS') . $cp['name']);
609 $cp['from'] = str_replace('<basepath>', $this->base_dir, $cp['from']);
610 $path = 'custom/modules/Home/Dashlets/' . $cp['name'] . '/';
611 $GLOBALS['log']->debug("Installing Dashlet " . $cp['name'] . "..." . $cp['from'] );
612 if(!file_exists($path)){
613 mkdir_recursive($path, true);
615 copy_recursive($cp['from'] , $path);
617 include('modules/Administration/RebuildDashlets.php');
622 function uninstall_dashlets(){
623 if(isset($this->installdefs['dashlets'])){
624 foreach($this->installdefs['dashlets'] as $cp){
625 $this->log(translate('LBL_MI_UN_DASHLETS') . $cp['name']);
626 $path = 'custom/modules/Home/Dashlets/' . $cp['name'];
627 $GLOBALS['log']->debug('Unlink ' .$path);
628 if (file_exists($path))
629 rmdir_recursive($path);
631 include('modules/Administration/RebuildDashlets.php');
636 function install_images(){
637 if(isset($this->installdefs['image_dir'])){
638 $this->log( translate('LBL_MI_IN_IMAGES') );
639 $this->copy_path($this->installdefs['image_dir'] , 'custom/themes');
644 function install_dcactions(){
645 if(isset($this->installdefs['dcaction'])){
646 $this->log(translate('LBL_MI_IN_MENUS'));
647 foreach($this->installdefs['dcaction'] as $action){
648 $action['from'] = str_replace('<basepath>', $this->base_dir, $action['from']);
649 $GLOBALS['log']->debug("Installing DCActions ..." . $action['from']);
650 $path = 'custom/Extension/application/Ext/DashletContainer/Containers';
651 if(!file_exists($path)){
652 mkdir_recursive($path, true);
654 copy_recursive($action['from'] , $path . '/'. $this->id_name . '.php');
656 $this->rebuild_dashletcontainers();
660 function uninstall_dcactions(){
661 if(isset($this->installdefs['dcaction'])){
662 $this->log(translate('LBL_MI_UN_MENUS'));
663 foreach($this->installdefs['dcaction'] as $action){
664 $action['from'] = str_replace('<basepath>', $this->base_dir, $action['from']);
665 $GLOBALS['log']->debug("Uninstalling DCActions ..." . $action['from'] );
666 $path = 'custom/Extension/application/Ext/DashletContainer/Containers';
667 if (sugar_is_file($path . '/'. $this->id_name . '.php', 'w'))
669 rmdir_recursive( $path . '/'. $this->id_name . '.php');
671 else if (sugar_is_file($path . '/'. DISABLED_PATH . '/'. $this->id_name . '.php', 'w'))
673 rmdir_recursive( $path . '/'. DISABLED_PATH . '/'. $this->id_name . '.php');
676 $this->rebuild_dashletcontainers();
680 function install_connectors(){
681 if(isset($this->installdefs['connectors'])){
682 foreach($this->installdefs['connectors'] as $cp){
683 $this->log(translate('LBL_MI_IN_CONNECTORS') . $cp['name']);
684 $dir = str_replace('_','/',$cp['name']);
685 $cp['connector'] = str_replace('<basepath>', $this->base_dir, $cp['connector']);
686 $source_path = 'custom/modules/Connectors/connectors/sources/' . $dir. '/';
687 $GLOBALS['log']->debug("Installing Connector " . $cp['name'] . "..." . $cp['connector'] );
688 if(!file_exists($source_path)){
689 mkdir_recursive($source_path, true);
691 copy_recursive($cp['connector'] , $source_path);
693 //Install optional formatter code if it is specified
694 if(!empty($cp['formatter'])) {
695 $cp['formatter'] = str_replace('<basepath>', $this->base_dir, $cp['formatter']);
696 $formatter_path = 'custom/modules/Connectors/connectors/formatters/' . $dir. '/';
697 if(!file_exists($formatter_path)){
698 mkdir_recursive($formatter_path, true);
700 copy_recursive($cp['formatter'] , $formatter_path);
703 require_once('include/connectors/utils/ConnectorUtils.php');
704 ConnectorUtils::installSource($cp['name']);
708 function uninstall_connectors(){
709 if(isset($this->installdefs['connectors'])){
710 foreach($this->installdefs['connectors'] as $cp){
711 $this->log(translate('LBL_MI_UN_CONNECTORS') . $cp['name']);
712 $dir = str_replace('_','/',$cp['name']);
713 $source_path = 'custom/modules/Connectors/connectors/sources/' . $dir;
714 $formatter_path = 'custom/modules/Connectors/connectors/formatters/' . $dir;
715 $GLOBALS['log']->debug('Unlink ' .$source_path);
716 rmdir_recursive($source_path);
717 rmdir_recursive($formatter_path);
719 require_once('include/connectors/utils/ConnectorUtils.php');
720 //ConnectorUtils::getConnectors(true);
721 ConnectorUtils::uninstallSource($cp['name']);
725 function install_vardef($from, $to_module)
727 $GLOBALS['log']->debug("Installing Vardefs ..." . $from . " for " .$to_module);
728 $path = 'custom/Extension/modules/' . $to_module. '/Ext/Vardefs';
729 if($to_module == 'application'){
730 $path ='custom/Extension/' . $to_module. '/Ext/Vardefs';
732 if(!file_exists($path)){
733 mkdir_recursive($path, true);
735 copy_recursive($from , $path.'/'. basename($from));
738 function install_layoutdef($from, $to_module){
739 $GLOBALS['log']->debug("Installing Layout Defs ..." . $from . " for " .$to_module);
740 $path = 'custom/Extension/modules/' . $to_module. '/Ext/Layoutdefs';
741 if($to_module == 'application'){
742 $path ='custom/Extension/' . $to_module. '/Ext/Layoutdefs';
744 if(!file_exists($path)){
745 mkdir_recursive($path, true);
747 copy_recursive($from , $path.'/'. basename($from));
750 // Non-standard - needs special rebuild call
751 function install_languages()
753 $languages = array();
754 if(isset($this->installdefs['language']))
756 $this->log(translate('LBL_MI_IN_LANG') );
757 foreach($this->installdefs['language'] as $packs)
759 $modules[]=$packs['to_module'];
760 $languages[$packs['language']] = $packs['language'];
761 $packs['from'] = str_replace('<basepath>', $this->base_dir, $packs['from']);
762 $GLOBALS['log']->debug("Installing Language Pack ..." . $packs['from'] . " for " .$packs['to_module']);
763 $path = 'custom/Extension/modules/' . $packs['to_module']. '/Ext/Language';
764 if($packs['to_module'] == 'application'){
765 $path ='custom/Extension/' . $packs['to_module']. '/Ext/Language';
768 if(!file_exists($path)){
769 mkdir_recursive($path, true);
771 copy_recursive($packs['from'] , $path.'/'.$packs['language'].'.'. $this->id_name . '.php');
773 $this->rebuild_languages($languages, $modules);
778 // Non-standard, needs special rebuild
779 function uninstall_languages(){
780 $languages = array();
781 if(isset($this->installdefs['language'])){
782 $this->log(translate('LBL_MI_UN_LANG') );
783 foreach($this->installdefs['language'] as $packs){
784 $modules[]=$packs['to_module'];
785 $languages[$packs['language']] = $packs['language'];
786 $packs['from'] = str_replace('<basepath>', $this->base_dir, $packs['from']);
787 $GLOBALS['log']->debug("Uninstalling Language Pack ..." . $packs['from'] . " for " .$packs['to_module']);
788 $path = 'custom/Extension/modules/' . $packs['to_module']. '/Ext/Language';
789 if($packs['to_module'] == 'application'){
790 $path ='custom/Extension/' . $packs['to_module']. '/Ext/Language';
792 if (sugar_is_file($path.'/'.$packs['language'].'.'. $this->id_name . '.php', 'w')) {
793 rmdir_recursive( $path.'/'.$packs['language'].'.'. $this->id_name . '.php');
794 } else if (sugar_is_file($path.'/'.DISABLED_PATH.'/'.$packs['language'].'.'. $this->id_name . '.php', 'w')) {
795 rmdir_recursive($path.'/'.DISABLED_PATH.'/'.$packs['language'].'.'. $this->id_name . '.php', 'w');
798 $this->rebuild_languages($languages, $modules);
803 // Non-standard, needs special rebuild
804 public function disable_languages()
806 if(isset($this->installdefs['language'])) {
807 $languages = $modules = array();
808 foreach($this->installdefs['language'] as $item) {
809 $from = str_replace('<basepath>', $this->base_dir, $item['from']);
810 $GLOBALS['log']->debug("Disabling Language {$item['language']}... from $from for " .$item['to_module']);
811 $modules[]=$item['to_module'];
812 $languages[$item['language']] = $item['language'];
813 if($item['to_module'] == 'application') {
814 $path = "custom/Extension/application/Ext/Language";
816 $path = "custom/Extension/modules/{$item['to_module']}/Ext/Language";
818 if(isset($item["name"])) {
819 $target = $item["name"];
821 $target = $this->id_name;
823 $target = "{$item['language']}.$target";
825 $disabled_path = $path.'/'.DISABLED_PATH;
826 if (file_exists("$path/$target.php")) {
827 mkdir_recursive($disabled_path, true);
828 rename("$path/$target.php", "$disabled_path/$target.php");
829 } else if (file_exists($path . '/'. basename($from))) {
830 mkdir_recursive($disabled_path, true);
831 rename( $path . '/'. basename($from), $disabled_path.'/'. basename($from));
834 $this->rebuild_languages($languages, $modules);
838 // Non-standard, needs special rebuild
839 public function enable_languages()
841 if(isset($this->installdefs['language'])) {
842 foreach($this->installdefs['language'] as $item) {
843 $from = str_replace('<basepath>', $this->base_dir, $item['from']);
844 $GLOBALS['log']->debug("Enabling Language {$item['language']}... from $from for " .$item['to_module']);
845 $modules[]=$item['to_module'];
846 $languages[$item['language']] = $item['language'];
847 if(!empty($module)) {
848 $item['to_module'] = $module;
851 if($item['to_module'] == 'application') {
852 $path = "custom/Extension/application/Ext/Language";
854 $path = "custom/Extension/modules/{$item['to_module']}/Ext/Language";
856 if(isset($item["name"])) {
857 $target = $item["name"];
859 $target = $this->id_name;
861 $target = "{$item['language']}.$target";
863 if(!file_exists($path)) {
864 mkdir_recursive($path, true);
866 $disabled_path = $path.'/'.DISABLED_PATH;
867 if (file_exists("$disabled_path/$target.php")) {
868 rename("$disabled_path/$target.php", "$path/$target.php");
870 if (file_exists($disabled_path . '/'. basename($from))) {
871 rename($disabled_path.'/'. basename($from), $path . '/'. basename($from));
874 $this->rebuild_languages($languages, $modules);
878 // Functions for adding and removing logic hooks from uploaded files
879 // 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
880 /* The module hook definition should look like this:
881 $installdefs = array(
883 'logic_hooks' => array(
884 array('module' => 'Accounts',
885 'hook' => 'after_save',
887 'description' => 'Account sample logic hook',
888 'file' => 'modules/Sample/sample_account_logic_hook_file.php',
889 'class' => 'SampleLogicClass',
890 'function' => 'accountAfterSave',
896 function enable_manifest_logichooks() {
897 if(empty($this->installdefs['logic_hooks']) || !is_array($this->installdefs['logic_hooks'])) {
903 foreach($this->installdefs['logic_hooks'] as $hook ) {
904 check_logic_hook_file($hook['module'], $hook['hook'], array($hook['order'], $hook['description'], $hook['file'], $hook['class'], $hook['function']));
908 function disable_manifest_logichooks() {
909 if(empty($this->installdefs['logic_hooks']) || !is_array($this->installdefs['logic_hooks'])) {
913 foreach($this->installdefs['logic_hooks'] as $hook ) {
914 remove_logic_hook($hook['module'], $hook['hook'], array($hook['order'], $hook['description'], $hook['file'], $hook['class'], $hook['function']));
918 /* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
919 function copy_path($from, $to, $backup_path='', $uninstall=false){
920 //function copy_path($from, $to){
921 /* END - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
922 $to = str_replace('<basepath>', $this->base_dir, $to);
925 $from = str_replace('<basepath>', $this->base_dir, $from);
926 $GLOBALS['log']->debug('Copy ' . $from);
929 $from = str_replace('<basepath>', $backup_path, $from);
930 //$GLOBALS['log']->debug('Restore ' . $from);
932 $from = clean_path($from);
933 $to = clean_path($to);
936 //there are cases where if we need to create a directory in the root directory
937 if($dir == '.' && is_dir($from)){
940 if(!sugar_is_dir($dir, 'instance'))
941 mkdir_recursive($dir, true);
942 /* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
943 if(empty($backup_path)) {
944 /* END - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
945 if(!copy_recursive($from, $to)){
946 die('Failed to copy ' . $from. ' ' . $to);
948 /* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
950 elseif(!$this->copy_recursive_with_backup($from, $to, $backup_path, $uninstall)){
951 die('Failed to copy ' . $from. ' to ' . $to);
953 /* END - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
956 function install_custom_fields($fields){
957 global $beanList, $beanFiles;
958 include('include/modules.php');
959 require_once('modules/DynamicFields/FieldCases.php');
960 foreach($fields as $field){
962 if(isset($beanList[ $field['module']])){
963 $class = $beanList[ $field['module']];
964 if(!isset($field['ext4']))$field['ext4'] = '';
965 if(!isset($field['mass_update']))$field['mass_update'] = 0;
966 if(!isset($field['duplicate_merge']))$field['duplicate_merge'] = 0;
967 if(!isset($field['help']))$field['help'] = '';
969 if(file_exists($beanFiles[$class])){
970 require_once($beanFiles[$class]);
973 $fieldObject = get_widget($field['type']);
974 $fieldObject->populateFromRow($field);
975 $mod->custom_fields->use_existing_labels = true;
976 $mod->custom_fields->addFieldObject($fieldObject);
980 $GLOBALS['log']->debug('Could not install custom field ' . $field['name'] . ' for module ' . $field['module'] . ': Module does not exist');
985 function uninstall_custom_fields($fields){
986 global $beanList, $beanFiles;
987 require_once('modules/DynamicFields/DynamicField.php');
988 $dyField = new DynamicField();
990 foreach($fields as $field){
991 $class = $beanList[ $field['module']];
992 if(file_exists($beanFiles[$class])){
993 require_once($beanFiles[$class]);
995 $dyField->bean = $mod;
996 $dyField->module = $field['module'];
997 $dyField->deleteField($field['name']);
1003 * ModuleInstaller->install_relationships calls install_relationship for every file included in the module package that defines a relationship, and then
1004 * writes a custom/Extension/application/Ext/TableDictionary/$module.php file containing an include_once for every relationship metadata file passed to install_relationship.
1005 * 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
1006 * everything in 'Ext/TableDictionary/' into 'tabledictionary.ext.php'
1008 function install_relationships ()
1010 if (isset ( $this->installdefs [ 'relationships' ] ))
1012 $this->log ( translate ( 'LBL_MI_IN_RELATIONSHIPS' ) ) ;
1013 $str = "<?php \n //WARNING: The contents of this file are auto-generated\n" ;
1014 $save_table_dictionary = false ;
1016 if (! file_exists ( "custom/Extension/application/Ext/TableDictionary" ))
1018 mkdir_recursive ( "custom/Extension/application/Ext/TableDictionary", true ) ;
1021 foreach ( $this->installdefs [ 'relationships' ] as $key => $relationship )
1023 $filename = basename ( $relationship [ 'meta_data' ] ) ;
1024 $this->copy_path ( $relationship [ 'meta_data' ], 'custom/metadata/' . $filename ) ;
1025 $this->install_relationship ( 'custom/metadata/' . $filename ) ;
1026 $save_table_dictionary = true ;
1028 if (! empty ( $relationship [ 'module_vardefs' ] ))
1030 $relationship [ 'module_vardefs' ] = str_replace ( '<basepath>', $this->base_dir, $relationship [ 'module_vardefs' ] ) ;
1031 $this->install_vardef ( $relationship [ 'module_vardefs' ], $relationship [ 'module' ] ) ;
1034 if (! empty ( $relationship [ 'module_layoutdefs' ] ))
1036 $relationship [ 'module_layoutdefs' ] = str_replace ( '<basepath>', $this->base_dir, $relationship [ 'module_layoutdefs' ] ) ;
1037 $this->install_layoutdef ( $relationship [ 'module_layoutdefs' ], $relationship [ 'module' ] ) ;
1040 $relName = strpos($filename, "MetaData") !== false ? substr($filename, 0, strlen($filename) - 12) : $filename;
1041 $out = sugar_fopen ( "custom/Extension/application/Ext/TableDictionary/$relName.php", 'w' ) ;
1042 fwrite ( $out, $str . "include('custom/metadata/$filename');\n\n?>" ) ;
1049 Relationship::delete_cache();
1050 $this->rebuild_vardefs () ;
1051 $this->rebuild_layoutdefs () ;
1052 if ($save_table_dictionary)
1054 $this->rebuild_tabledictionary () ;
1056 require_once("data/Relationships/RelationshipFactory.php");
1057 SugarRelationshipFactory::deleteCache();
1062 * Install_relationship obtains a set of relationship definitions from the filename passed in as a parameter.
1063 * For each definition it calls db->createTableParams to build the relationships table if it does not exist,
1064 * and SugarBean::createRelationshipMeta to add the relationship into the 'relationships' table.
1066 function install_relationship($file)
1068 $_REQUEST['moduleInstaller'] = true;
1069 if(!file_exists($file))
1071 $GLOBALS['log']->debug( 'File does not exists : '.$file);
1075 $rel_dictionary = $dictionary;
1076 foreach ($rel_dictionary as $rel_name => $rel_data)
1078 $table = ''; // table is actually optional
1079 // check if we have a table definition - not all relationships require a join table
1080 if ( isset( $rel_data[ 'table' ] ) )
1082 $table = $rel_data[ 'table' ];
1084 if(!$this->db->tableExists($table))
1086 $this->db->createTableParams($table, $rel_data[ 'fields' ], $rel_data[ 'indices' ]);
1091 $GLOBALS['log']->debug("Processing relationship meta for ". $rel_name."...");
1092 SugarBean::createRelationshipMeta($rel_name, $this->db,$table,$rel_dictionary,'');
1093 Relationship::delete_cache();
1095 $GLOBALS['log']->debug( 'done<br>');
1099 function install_layoutfields() {
1100 if (!empty ( $this->installdefs [ 'layoutfields' ] ))
1102 foreach ( $this->installdefs [ 'layoutfields' ] as $fieldSet )
1104 if (!empty($fieldSet['additional_fields']))
1106 $this->addFieldsToLayout($fieldSet['additional_fields']);
1112 function uninstall_layoutfields() {
1113 if (!empty ( $this->installdefs [ 'layoutfields' ] ))
1115 foreach ( $this->installdefs [ 'layoutfields' ] as $fieldSet )
1117 if (!empty($fieldSet['additional_fields']))
1119 $this->removeFieldsFromLayout($fieldSet['additional_fields']);
1125 function uninstall_relationship($file, $rel_dictionary = null){
1126 if ($rel_dictionary == null)
1128 if(!file_exists($file)){
1129 $GLOBALS['log']->debug( 'File does not exists : '.$file);
1133 $rel_dictionary = $dictionary;
1136 foreach ($rel_dictionary as $rel_name => $rel_data)
1138 if (!empty($rel_data['table'])){
1139 $table = $rel_data['table'];
1142 $table = ' One-to-Many ';
1145 if ($this->db->tableExists($table) && isset($GLOBALS['mi_remove_tables']) && $GLOBALS['mi_remove_tables'])
1147 SugarBean::removeRelationshipMeta($rel_name, $this->db,$table,$rel_dictionary,'');
1148 $this->db->dropTableName($table);
1149 if(!$this->silent) $this->log( translate('LBL_MI_UN_RELATIONSHIPS_DROP') . $table);
1152 //Delete Layout defs
1153 // check to see if we have any vardef or layoutdef entries to remove - must have a relationship['module'] parameter if we do
1154 if (!isset($rel_data[ 'module' ]))
1156 $rel_data['relationships'][$rel_name]['lhs_module'],
1157 $rel_data['relationships'][$rel_name]['rhs_module'],
1160 $mods = array($rel_data[ 'module' ]);
1162 $filename = "$rel_name.php";
1164 foreach($mods as $mod) {
1165 if ($mod != 'application' ) {
1166 $basepath = "custom/Extension/modules/$mod/Ext/";
1168 $basepath = "custom/Extension/application/Ext/";
1171 foreach (array($filename , "custom" . $filename) as $fn) {
1172 //remove any vardefs
1173 $path = $basepath . "Vardefs/$fn" ;
1174 if (file_exists( $path ))
1175 rmdir_recursive( $path );
1177 //remove any layoutdefs
1178 $path = $basepath . "Layoutdefs/$fn" ;
1179 if( file_exists( $path ))
1181 rmdir_recursive( $path );
1186 foreach (array($filename , "custom" . $filename) as $fn) {
1187 // remove the table dictionary extension
1188 if ( file_exists("custom/Extension/application/Ext/TableDictionary/$fn"))
1189 unlink("custom/Extension/application/Ext/TableDictionary/$fn");
1191 if (file_exists("custom/metadata/{$rel_name}MetaData.php"))
1192 unlink( "custom/metadata/{$rel_name}MetaData.php" );
1197 function uninstall_relationships($include_studio_relationships = false){
1198 $relationships = array();
1200 //Find and remove studio created relationships.
1201 global $beanList, $beanFiles, $dictionary;
1202 //Load up the custom relationship definitions.
1203 if(file_exists('custom/application/Ext/TableDictionary/tabledictionary.ext.php')){
1204 include('custom/application/Ext/TableDictionary/tabledictionary.ext.php');
1206 //Find all the relatioships/relate fields involving this module.
1207 $rels_to_remove = array();
1208 foreach($beanList as $mod => $bean) {
1209 VardefManager::loadVardef($mod, $bean);
1210 //We can skip modules that are in this package as they will be removed anyhow
1211 if (!in_array($mod, $this->modulesInPackage) && !empty($dictionary[$bean]) && !empty($dictionary[$bean]['fields']))
1213 $field_defs = $dictionary[$bean]['fields'];
1214 foreach($field_defs as $field => $def)
1216 //Weed out most fields first
1217 if (isset ($def['type']))
1219 //Custom relationships created in the relationship editor
1220 if ($def['type'] == "link" && !empty($def['relationship']) && !empty($dictionary[$def['relationship']]))
1222 $rel_name = $def['relationship'];
1224 $rel_def = $dictionary[$rel_name]['relationships'][$rel_name];
1226 //Check against mods to be removed.
1227 foreach($this->modulesInPackage as $removed_mod) {
1228 if ($rel_def['lhs_module'] == $removed_mod || $rel_def['rhs_module'] == $removed_mod )
1230 $dictionary[$rel_name]['from_studio'] = true;
1231 $relationships[$rel_name] = $dictionary[$rel_name];
1235 //Custom "relate" fields created in studio also need to be removed
1236 if ($def['type'] == 'relate' && isset($def['module'])) {
1237 foreach($this->modulesInPackage as $removed_mod) {
1238 if ($def['module'] == $removed_mod)
1240 require_once 'modules/ModuleBuilder/Module/StudioModule.php' ;
1241 $studioMod = new StudioModule ( $mod );
1242 $studioMod->removeFieldFromLayouts( $field );
1243 if (isset($def['custom_module'])) {
1244 require_once ('modules/DynamicFields/DynamicField.php') ;
1245 require_once ($beanFiles [ $bean ]) ;
1246 $seed = new $bean ( ) ;
1247 $df = new DynamicField ( $mod ) ;
1248 $df->setup ( $seed ) ;
1249 //Need to load the entire field_meta_data for some field types
1250 $field_obj = $df->getFieldWidget($mod, $field);
1251 $field_obj->delete ( $df ) ;
1263 $this->uninstall_relationship(null, $relationships);
1265 if(isset($this->installdefs['relationships'])) {
1266 $relationships = $this->installdefs['relationships'];
1267 $this->log(translate('LBL_MI_UN_RELATIONSHIPS') );
1268 foreach($relationships as $relationship)
1270 // remove the metadata entry
1271 $filename = basename ( $relationship['meta_data'] );
1272 $pathname = (file_exists("custom/metadata/$filename")) ? "custom/metadata/$filename" : "metadata/$filename" ;
1273 if(isset($GLOBALS['mi_remove_tables']) && $GLOBALS['mi_remove_tables'])
1274 $this->uninstall_relationship( $pathname );
1275 if (file_exists($pathname))
1276 unlink( $pathname );
1280 if (file_exists("custom/Extension/application/Ext/TableDictionary/{$this->id_name}.php"))
1281 unlink("custom/Extension/application/Ext/TableDictionary/{$this->id_name}.php");
1282 Relationship::delete_cache();
1283 $this->rebuild_tabledictionary();
1289 function uninstall($base_dir){
1290 if(defined('TEMPLATE_URL'))SugarTemplateUtilities::disableCache();
1291 global $app_strings;
1292 $total_steps = 5; //min steps with no tasks
1294 $this->base_dir = $base_dir;
1297 'uninstall_relationships',
1299 'uninstall_dcactions',
1300 'uninstall_dashlets',
1301 'uninstall_connectors',
1302 'uninstall_layoutfields',
1303 'uninstall_extensions',
1304 'disable_manifest_logichooks',
1307 $total_steps += count($tasks); //now the real number of steps
1308 if(file_exists($this->base_dir . '/manifest.php')){
1311 display_progress_bar('install', $current_step, $total_steps);
1312 echo '<div id ="displayLoglink" ><a href="#" onclick="toggleDisplay(\'displayLog\')">'.$app_strings['LBL_DISPLAY_LOG'].'</a> </div><div id="displayLog" style="display:none">';
1316 include($this->base_dir . '/manifest.php');
1317 $this->installdefs = $installdefs;
1318 $this->id_name = $this->installdefs['id'];
1319 $installed_modules = array();
1320 if(isset($this->installdefs['beans'])){
1321 foreach($this->installdefs['beans'] as $bean){
1323 $installed_modules[] = $bean['module'];
1324 $this->uninstall_user_prefs($bean['module']);
1326 $this->modulesInPackage = $installed_modules;
1327 $this->uninstall_beans($installed_modules);
1328 $this->uninstall_customizations($installed_modules);
1331 update_progress_bar('install', $total_steps, $total_steps);
1336 update_progress_bar('install', $current_step, $total_steps);
1338 foreach($tasks as $task){
1342 update_progress_bar('install', $current_step, $total_steps);
1345 if(isset($installdefs['custom_fields']) && (isset($GLOBALS['mi_remove_tables']) && $GLOBALS['mi_remove_tables'])){
1346 $this->log(translate('LBL_MI_UN_CUSTOMFIELD'));
1347 $this->uninstall_custom_fields($installdefs['custom_fields']);
1351 update_progress_bar('install', $current_step, $total_steps);
1354 //since we are passing $silent = true to rebuildAll() in that method it will set $this->silent = true, so
1355 //we need to save the setting to set it back after rebuildAll() completes.
1356 $silentBak = $this->silent;
1357 $this->rebuild_all(true);
1358 $this->silent = $silentBak;
1360 //#27877, If the request from MB redeploy a custom module , we will not remove the ACL actions for this package.
1361 if( !isset($_REQUEST['action']) || $_REQUEST['action']!='DeployPackage' ){
1362 $this->remove_acl_actions();
1368 update_progress_bar('install', $current_step, $total_steps);
1372 UpdateSystemTabs('Restore',$installed_modules);
1374 //clear the unified_search_module.php file
1375 require_once('modules/Home/UnifiedSearchAdvanced.php');
1376 UnifiedSearchAdvanced::unlinkUnifiedSearchModulesFile();
1378 $this->log('<br><b>' . translate('LBL_MI_COMPLETE') . '</b>');
1380 update_progress_bar('install', $total_steps, $total_steps);
1383 die("No manifest.php Defined In $this->base_dir/manifest.php");
1387 function rebuild_languages($languages = array(), $modules="")
1389 foreach($languages as $language=>$value){
1390 $this->log(translate('LBL_MI_REBUILDING') . " Language...$language");
1391 $this->merge_files('Ext/Language/', $language.'.lang.ext.php', $language);
1393 foreach($modules as $module){
1394 LanguageManager::clearLanguageCache($module, $language);
1398 sugar_cache_reset();
1401 function rebuild_vardefs()
1403 $this->rebuildExt("Vardefs", 'vardefs.ext.php');
1404 sugar_cache_reset();
1407 function rebuild_dashletcontainers(){
1408 $this->log(translate('LBL_MI_REBUILDING') . " DC Actions...");
1409 $this->merge_files('Ext/DashletContainer/Containers/', 'dcactions.ext.php');
1412 function rebuild_tabledictionary()
1414 $this->rebuildExt("TableDictionary", 'tabledictionary.ext.php');
1417 function rebuild_relationships() {
1418 if(!$this->silent) echo translate('LBL_MI_REBUILDING') . ' Relationships';
1419 $_REQUEST['silent'] = true;
1421 include('include/modules.php');
1422 include("modules/Administration/RebuildRelationship.php");
1425 function remove_acl_actions() {
1426 global $beanFiles, $beanList, $current_user;
1427 include('include/modules.php');
1428 include("modules/ACL/remove_actions.php");
1432 * Wrapper call to modules/Administration/RepairIndex.php
1434 function repair_indices() {
1435 global $current_user,$beanFiles,$dictionary;
1436 $this->log(translate('LBL_MI_REPAIR_INDICES'));
1437 $_REQUEST['silent'] = true; // local var flagging echo'd output in repair script
1438 $_REQUEST['mode'] = 'execute'; // flag to just go ahead and run the script
1439 include("modules/Administration/RepairIndex.php");
1443 * Rebuilds the extension files found in custom/Extension
1444 * @param boolean $silent
1446 function rebuild_all($silent=false){
1447 if(defined('TEMPLATE_URL'))SugarTemplateUtilities::disableCache();
1448 $this->silent=$silent;
1449 global $sugar_config;
1451 //Check for new module extensions
1452 $this->rebuild_modules();
1454 $this->rebuild_languages($sugar_config['languages']);
1455 $this->rebuild_extensions();
1456 $this->rebuild_dashletcontainers();
1457 $this->rebuild_relationships();
1458 $this->rebuild_tabledictionary();
1459 $this->reset_opcodes();
1460 sugar_cache_reset();
1464 * 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
1465 * custom/Extension/modules/$module/<path> (_override files last) and concatenates them to custom/modules/$module/<path>/<file>.
1466 * Then it does the same thing in custom/Extension/application/<path>, concatenating those files and copying the result to custom/application/<path>/<file>
1468 function merge_files($path, $name, $filter = '', $application = false){
1470 $GLOBALS['log']->debug( get_class($this)."->merge_files() : merging module files in custom/Extension/modules/<module>/$path to custom/modules/<module>/$path$name");
1471 foreach($this->modules as $module){
1472 //$GLOBALS['log']->debug("Merging Files for: ".$module);
1473 //$GLOBALS['log']->debug("Merging Files for path: ".$path);
1474 $extension = "<?php \n //WARNING: The contents of this file are auto-generated\n";
1475 $extpath = "modules/$module/$path";
1476 $module_install = 'custom/Extension/'.$extpath;
1477 $shouldSave = false;
1478 if(is_dir($module_install)){
1479 $dir = dir($module_install);
1481 $override = array();
1482 while($entry = $dir->read()){
1483 if((empty($filter) || substr_count($entry, $filter) > 0) && is_file($module_install.'/'.$entry)
1484 && $entry != '.' && $entry != '..' && strtolower(substr($entry, -4)) == ".php")
1486 if (substr($entry, 0, 9) == '_override') {
1487 $override[] = $entry;
1489 $file = file_get_contents($module_install . '/' . $entry);
1490 $GLOBALS['log']->debug(get_class($this)."->merge_files(): found {$module_install}{$entry}") ;
1491 $extension .= "\n". str_replace(array('<?php', '?>', '<?PHP', '<?'), array('','', '' ,'') , $file);
1495 foreach ($override as $entry) {
1496 $file = file_get_contents($module_install . '/' . $entry);
1497 $extension .= "\n". str_replace(array('<?php', '?>', '<?PHP', '<?'), array('','', '' ,'') , $file);
1500 $extension .= "\n?>";
1503 if(!file_exists("custom/$extpath")) {
1504 mkdir_recursive("custom/$extpath", true);
1506 $out = sugar_fopen("custom/$extpath/$name", 'w');
1507 fwrite($out,$extension);
1510 if(file_exists("custom/$extpath/$name")){
1511 unlink("custom/$extpath/$name");
1518 $GLOBALS['log']->debug("Merging application files for $name in $path");
1519 //Now the application stuff
1520 $extension = "<?php \n //WARNING: The contents of this file are auto-generated\n";
1521 $extpath = "application/$path";
1522 $module_install = 'custom/Extension/'.$extpath;
1523 $shouldSave = false;
1524 if(is_dir($module_install)){
1525 $dir = dir($module_install);
1526 while($entry = $dir->read()){
1528 if((empty($filter) || substr_count($entry, $filter) > 0) && is_file($module_install.'/'.$entry)
1529 && $entry != '.' && $entry != '..' && strtolower(substr($entry, -4)) == ".php")
1531 $file = file_get_contents($module_install . '/' . $entry);
1532 $extension .= "\n". str_replace(array('<?php', '?>', '<?PHP', '<?'), array('','', '' ,'') , $file);
1536 $extension .= "\n?>";
1538 if(!file_exists("custom/$extpath")){
1539 mkdir_recursive("custom/$extpath", true);
1541 $out = sugar_fopen("custom/$extpath/$name", 'w');
1542 fwrite($out,$extension);
1545 if(file_exists("custom/$extpath/$name")){
1546 unlink("custom/$extpath/$name");
1552 function install_modules()
1554 $this->installed_modules = array();
1555 $this->tab_modules = array();
1556 if(isset($this->installdefs['beans'])){
1557 $str = "<?php \n //WARNING: The contents of this file are auto-generated\n";
1558 foreach($this->installdefs['beans'] as $bean){
1559 if(!empty($bean['module']) && !empty($bean['class']) && !empty($bean['path'])){
1560 $module = $bean['module'];
1561 $class = $bean['class'];
1562 $path = $bean['path'];
1564 $str .= "\$beanList['$module'] = '$class';\n";
1565 $str .= "\$beanFiles['$class'] = '$path';\n";
1567 $str .= "\$moduleList[] = '$module';\n";
1568 $this->install_user_prefs($module, empty($bean['hide_by_default']));
1569 $this->tab_modules[] = $module;
1571 $str .= "\$modules_exempt_from_availability_check['$module'] = '$module';\n";
1572 $str .= "\$modInvisList[] = '$module';\n";
1574 $this->installed_modules[] = $module;
1576 $errors[] = 'Bean array not well defined.';
1577 $this->abort($errors);
1581 if(!file_exists("custom/Extension/application/Ext/Include")){
1582 mkdir_recursive("custom/Extension/application/Ext/Include", true);
1584 file_put_contents("custom/Extension/application/Ext/Include/{$this->id_name}.php", $str);
1589 * ModuleInstaller->install_beans runs through the list of beans given, instantiates each bean, calls bean->create_tables, and then calls SugarBean::createRelationshipMeta for the
1592 function install_beans($beans){
1593 include('include/modules.php');
1594 foreach($beans as $bean){
1595 $this->log( translate('LBL_MI_IN_BEAN') . " $bean");
1596 if(isset($beanList[$bean])){
1597 $class = $beanList[$bean];
1598 if(file_exists($beanFiles[$class])){
1599 require_once($beanFiles[$class]);
1600 $mod = new $class();
1602 if(is_subclass_of($mod, 'SugarBean') && $mod->disable_vardefs == false ){
1603 $GLOBALS['log']->debug( "Creating Tables Bean : $bean");
1604 $mod->create_tables();
1605 SugarBean::createRelationshipMeta($mod->getObjectName(), $mod->db,$mod->table_name,'',$mod->module_dir);
1608 $GLOBALS['log']->debug( "File Does Not Exist:" . $beanFiles[$class] );
1614 function uninstall_beans($beans){
1615 include('include/modules.php');
1616 foreach($beans as $bean){
1617 $this->log( translate('LBL_MI_UN_BEAN') . " $bean");
1618 if(isset($beanList[$bean])){
1619 $class = $beanList[$bean];
1621 if(file_exists($beanFiles[$class])){
1622 require_once($beanFiles[$class]);
1623 $mod = new $class();
1625 if(is_subclass_of($mod, 'SugarBean')){
1626 $GLOBALS['log']->debug( "Drop Tables : $bean");
1627 if(isset($GLOBALS['mi_remove_tables']) && $GLOBALS['mi_remove_tables'])
1628 $mod->drop_tables();
1631 $GLOBALS['log']->debug( "File Does Not Exist:" . $beanFiles[$class] );
1638 * Remove any customizations made within Studio while the module was installed.
1640 function uninstall_customizations($beans){
1641 foreach($beans as $bean){
1643 'custom/modules/' . $bean,
1644 'custom/Extension/modules/' . $bean
1646 foreach($dirs as $dir)
1649 rmdir_recursive($dir);
1656 $GLOBALS['log']->debug('ModuleInstaller:'. $str);
1662 /* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:15:18 PM */
1663 function copy_recursive_with_backup( $source, $dest, $backup_path, $uninstall=false ) {
1664 if(is_file($source)) {
1666 $GLOBALS['log']->debug("Restoring ... " . $source. " to " .$dest );
1667 if(copy( $source, $dest)) {
1668 if(is_writable($dest))
1669 sugar_touch( $dest, filemtime($source) );
1670 return(unlink($source));
1673 $GLOBALS['log']->debug( "Can't restore file: " . $source );
1678 if(file_exists($dest)) {
1679 $rest = clean_path($backup_path."/$dest");
1680 if( !is_dir(dirname($rest)) )
1681 mkdir_recursive(dirname($rest), true);
1683 $GLOBALS['log']->debug("Backup ... " . $dest. " to " .$rest );
1684 if(copy( $dest, $rest)) {
1685 if(is_writable($rest))
1686 sugar_touch( $rest, filemtime($dest) );
1689 $GLOBALS['log']->debug( "Can't backup file: " . $dest );
1692 return( copy( $source, $dest ) );
1695 elseif(!is_dir($source)) {
1698 return(unlink($dest));
1700 //don't do anything we already cleaned up the files using uninstall_new_files
1708 if( !is_dir($dest) && !$uninstall){
1709 sugar_mkdir( $dest );
1714 $d = dir( $source );
1715 while( $f = $d->read() ){
1716 if( $f == "." || $f == ".." ){
1719 $status &= $this->copy_recursive_with_backup( "$source/$f", "$dest/$f", $backup_path, $uninstall );
1725 private function dir_get_files($path, $base_path){
1727 if(!is_dir($path))return $files;
1729 while ($e = $d->read()){
1730 //ignore invisible files . .. ._MACOSX
1731 if(substr($e, 0, 1) == '.')continue;
1732 if(is_file($path . '/' . $e))$files[str_replace($base_path , '', $path . '/' . $e)] = str_replace($base_path , '', $path . '/' . $e);
1733 if(is_dir($path . '/' . $e))$files = array_merge($files, $this->dir_get_files($path . '/' . $e, $base_path));
1740 private function dir_file_count($path){
1741 //if its a file then it has at least 1 file in the directory
1742 if(is_file($path)) return 1;
1743 if(!is_dir($path)) return 0;
1746 while ($e = $d->read()){
1747 //ignore invisible files . .. ._MACOSX
1748 if(substr($e, 0, 1) == '.')continue;
1749 if(is_file($path . '/' . $e))$count++;
1750 if(is_dir($path . '/' . $e))$count += $this->dir_file_count($path . '/' . $e);
1757 /* END - RESTORE POINT - by MR. MILK August 31, 2005 02:15:34 PM */
1761 * Static function which allows a module developer to abort their progress, pass in an array of errors and
1762 * redirect back to the main module loader page
1764 * @param errors an array of error messages which will be displayed on the
1765 * main module loader page once it is loaded.
1767 function abort($errors = array()){
1768 //set the errors onto the session so we can display them one the moduler loader page loads
1769 $_SESSION['MODULEINSTALLER_ERRORS'] = $errors;
1770 echo '<META HTTP-EQUIV="Refresh" content="0;url=index.php?module=Administration&action=UpgradeWizard&view=module">';
1772 //header('Location: index.php?module=Administration&action=UpgradeWizard&view=module');
1776 * Return the set of errors stored in the SESSION
1778 * @return an array of errors
1780 function getErrors(){
1781 if(!empty($_SESSION['MODULEINSTALLER_ERRORS'])){
1782 $errors = $_SESSION['MODULEINSTALLER_ERRORS'];
1783 unset($_SESSION['MODULEINSTALLER_ERRORS']);
1791 * Add any fields to the DetailView and EditView of the appropriate modules
1792 * Only add into deployed modules, as addFieldsToUndeployedLayouts has done this already for undeployed modules (and the admin might have edited the layouts already)
1793 * @param array $layoutAdditions An array of module => fieldname
1796 function addFieldsToLayout($layoutAdditions) {
1797 require_once 'modules/ModuleBuilder/parsers/views/GridLayoutMetaDataParser.php' ;
1799 // these modules either lack editviews/detailviews or use custom mechanisms for the editview/detailview.
1800 // In either case, we don't want to attempt to add a relate field to them
1801 // would be better if GridLayoutMetaDataParser could handle this gracefully, so we don't have to maintain this list here
1802 $invalidModules = array ( 'emails' , 'kbdocuments' ) ;
1804 foreach ( $layoutAdditions as $deployedModuleName => $fieldName )
1806 if ( ! in_array( strtolower ( $deployedModuleName ) , $invalidModules ) )
1808 foreach ( array ( MB_EDITVIEW , MB_DETAILVIEW ) as $view )
1810 $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . ": adding $fieldName to $view layout for module $deployedModuleName" ) ;
1811 $parser = new GridLayoutMetaDataParser ( $view, $deployedModuleName ) ;
1812 $parser->addField ( array ( 'name' => $fieldName ) ) ;
1813 $parser->handleSave ( false ) ;
1820 function removeFieldsFromLayout($layoutAdditions) {
1821 require_once 'modules/ModuleBuilder/parsers/views/GridLayoutMetaDataParser.php' ;
1823 // these modules either lack editviews/detailviews or use custom mechanisms for the editview/detailview.
1824 // In either case, we don't want to attempt to add a relate field to them
1825 // would be better if GridLayoutMetaDataParser could handle this gracefully, so we don't have to maintain this list here
1826 $invalidModules = array ( 'emails' , 'kbdocuments' ) ;
1828 foreach ( $layoutAdditions as $deployedModuleName => $fieldName )
1830 if ( ! in_array( strtolower ( $deployedModuleName ) , $invalidModules ) )
1832 foreach ( array ( MB_EDITVIEW , MB_DETAILVIEW ) as $view )
1834 $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . ": adding $fieldName to $view layout for module $deployedModuleName" ) ;
1835 $parser = new GridLayoutMetaDataParser ( $view, $deployedModuleName ) ;
1836 $parser->removeField ( $fieldName ) ;
1837 $parser->handleSave ( false ) ;
1845 //********** DISABLE/ENABLE FUNCTIONS
1847 function enable($base_dir, $is_upgrade = false, $previous_version = ''){
1848 global $app_strings;
1849 $this->base_dir = $base_dir;
1850 $total_steps = 3; //minimum number of steps with no tasks
1855 'enable_relationships',
1856 'enable_extensions',
1857 'enable_manifest_logichooks',
1860 $total_steps += count($tasks);
1861 if(file_exists($this->base_dir . '/manifest.php')){
1864 display_progress_bar('install', $current_step, $total_steps);
1865 echo '<div id ="displayLoglink" ><a href="#" onclick="toggleDisplay(\'displayLog\')">'.$app_strings['LBL_DISPLAY_LOG'].'</a> </div><div id="displayLog" style="display:none">';
1868 require_once($this->base_dir . '/manifest.php');
1869 if($is_upgrade && !empty($previous_version)){
1870 //check if the upgrade path exists
1871 if(!empty($upgrade_manifest)){
1872 if(!empty($upgrade_manifest['upgrade_paths'])){
1873 if(!empty($upgrade_manifest['upgrade_paths'][$previous_version])){
1874 $installdefs = $upgrade_manifest['upgrade_paths'][$previous_version];
1876 $errors[] = 'No Upgrade Path Found in manifest.';
1877 $this->abort($errors);
1882 $this->id_name = $installdefs['id'];
1883 $this->installdefs = $installdefs;
1884 $installed_modules = array();
1885 if(isset($installdefs['beans'])){
1886 foreach($this->installdefs['beans'] as $bean){
1887 $installed_modules[] = $bean['module'];
1892 update_progress_bar('install', $current_step, $total_steps);
1895 foreach($tasks as $task){
1899 update_progress_bar('install', $current_step, $total_steps);
1905 update_progress_bar('install', $current_step, $total_steps);
1908 UpdateSystemTabs('Add',$installed_modules);
1909 $GLOBALS['log']->debug('Complete');
1912 die("No \$installdefs Defined In $this->base_dir/manifest.php");
1916 function disable($base_dir){
1917 global $app_strings;
1918 $total_steps = 3; //min steps with no tasks
1920 $this->base_dir = $base_dir;
1924 'disable_relationships',
1925 'disable_extensions',
1926 'disable_manifest_logichooks',
1929 $total_steps += count($tasks); //now the real number of steps
1930 if(file_exists($this->base_dir . '/manifest.php')){
1933 display_progress_bar('install', $current_step, $total_steps);
1934 echo '<div id ="displayLoglink" ><a href="#" onclick="toggleDisplay(\'displayLog\')">'.$app_strings['LBL_DISPLAY_LOG'].'</a> </div><div id="displayLog" style="display:none">';
1937 require_once($this->base_dir . '/manifest.php');
1938 $this->installdefs = $installdefs;
1939 $this->id_name = $this->installdefs['id'];
1940 $installed_modules = array();
1941 if(isset($this->installdefs['beans'])){
1942 foreach($this->installdefs['beans'] as $bean){
1943 $installed_modules[] = $bean['module'];
1948 update_progress_bar('install', $current_step, $total_steps);
1950 foreach($tasks as $task){
1954 update_progress_bar('install', $current_step, $total_steps);
1959 update_progress_bar('install', $current_step, $total_steps);
1962 UpdateSystemTabs('Restore',$installed_modules);
1965 die("No manifest.php Defined In $this->base_dir/manifest.php");
1969 function enable_vardef($to_module)
1971 $this->enableExt("vardefs", "Vardefs", $to_module);
1974 function enable_layoutdef($to_module)
1976 $this->enableExt("layoutdefs", "Layoutdefs", $to_module);
1979 function enable_relationships(){
1980 if(isset($this->installdefs['relationships'])){
1981 $str = "<?php \n //WARNING: The contents of this file are auto-generated\n";
1982 $save_table_dictionary = false;
1983 foreach($this->installdefs['relationships'] as $relationship){
1984 $filename =basename($relationship['meta_data']);
1986 $save_table_dictionary = true;
1987 $str .= "include_once('metadata/$filename');\n";
1988 if (empty($relationship['module']))
1991 if(!empty($relationship['module_vardefs'])){
1992 $this->enable_vardef($relationship['module']);
1994 if(!empty($relationship['module_layoutdefs'])){
1995 $this->enable_layoutdef($relationship['module']);
1998 $this->rebuild_vardefs();
1999 $this->rebuild_layoutdefs();
2000 if($save_table_dictionary){
2001 if(!file_exists("custom/Extension/application/Ext/TableDictionary")){
2002 mkdir_recursive("custom/Extension/application/Ext/TableDictionary", true);
2004 if (file_exists("custom/Extension/application/Ext/TableDictionary/".DISABLED_PATH."/$this->id_name.php"))
2005 rename("custom/Extension/application/Ext/TableDictionary/".DISABLED_PATH."/$this->id_name.php", "custom/Extension/application/Ext/TableDictionary/$this->id_name.php");
2006 $this->rebuild_tabledictionary();
2011 function disable_relationships($action = 'disable'){
2012 if(isset($this->installdefs['relationships'])){
2013 foreach($this->installdefs['relationships'] as $relationship){
2014 $filename = basename($relationship['meta_data']);
2015 $relName = substr($filename, -12) == "MetaData.php" ? substr($filename,0,strlen($filename) - 12) : "";
2016 if (empty($relationship['module']) && empty($relName))
2019 //remove the vardefs
2020 if (empty($relName))
2021 $path = 'custom/Extension/modules/' . $relationship['module']. '/Ext/Vardefs';
2022 if(!empty($relationship['module']) && $relationship['module'] == 'application'){
2023 $path ='custom/Extension/' . $relationship['module']. '/Ext/Vardefs';
2025 if(!empty($relationship['module_vardefs']) && file_exists($path . '/'. $this->id_name . '.php')){
2026 mkdir_recursive($path . '/'.DISABLED_PATH, true);
2027 rename( $path . '/'. $this->id_name . '.php', $path . '/'.DISABLED_PATH.'/'. $this->id_name . '.php');
2029 //remove the layoutdefs
2030 if ( !empty($relationship['module']) ) {
2031 $path = 'custom/Extension/modules/' . $relationship['module']. '/Ext/Layoutdefs';
2032 if($relationship['module'] == 'application'){
2033 $path ='custom/Extension/' . $relationship['module']. '/Ext/Layoutdefs';
2037 if(!empty($relationship['module_layoutdefs']) && file_exists($path . '/'. $this->id_name . '.php')){
2038 mkdir_recursive($path . '/'.DISABLED_PATH, true);
2039 rename( $path . '/'. $this->id_name . '.php', $path . '/'.DISABLED_PATH.'/'. $this->id_name . '.php');
2043 if(file_exists("custom/Extension/application/Ext/TableDictionary/$this->id_name.php")){
2044 mkdir_recursive("custom/Extension/application/Ext/TableDictionary/".DISABLED_PATH, true);
2045 rename("custom/Extension/application/Ext/TableDictionary/$this->id_name.php", "custom/Extension/application/Ext/TableDictionary/".DISABLED_PATH."/$this->id_name.php");
2047 $this->rebuild_tabledictionary();
2048 $this->rebuild_vardefs();
2049 $this->rebuild_layoutdefs();
2053 function enable_dashlets(){
2054 if(isset($this->installdefs['dashlets'])){
2055 foreach($this->installdefs['dashlets'] as $cp){
2056 $cp['from'] = str_replace('<basepath>', $this->base_dir, $cp['from']);
2057 $path = 'custom/modules/Home/Dashlets/' . $cp['name'] . '/';
2058 $disabled_path = 'custom/modules/Home/'.DISABLED_PATH.'Dashlets/' . $cp['name'];
2059 $GLOBALS['log']->debug("Enabling Dashlet " . $cp['name'] . "..." . $cp['from'] );
2060 if (file_exists($disabled_path))
2062 rename($disabled_path, $path);
2065 include('modules/Administration/RebuildDashlets.php');
2070 function disable_dashlets(){
2071 if(isset($this->installdefs['dashlets'])){
2072 foreach($this->installdefs['dashlets'] as $cp){
2073 $path = 'custom/modules/Home/Dashlets/' . $cp['name'];
2074 $disabled_path = 'custom/modules/Home/'.DISABLED_PATH.'Dashlets/' . $cp['name'];
2075 $GLOBALS['log']->debug('Disabling ' .$path);
2076 if (file_exists($path))
2078 mkdir_recursive('custom/modules/Home/'.DISABLED_PATH.'Dashlets/', true);
2079 rename( $path, $disabled_path);
2082 include('modules/Administration/RebuildDashlets.php');
2086 function enable_copy(){
2087 //copy files back onto file system. first perform md5 check to determine if anything has been modified
2088 //here we should just go through the files in the -restore directory and copy those back
2089 if(isset($GLOBALS['mi_overwrite_files']) && $GLOBALS['mi_overwrite_files']){
2090 if(!empty($this->installdefs['copy'])){
2091 foreach($this->installdefs['copy'] as $cp){
2092 $cp['to'] = clean_path(str_replace('<basepath>', $this->base_dir, $cp['to']));
2093 $backup_path = clean_path( remove_file_extension(urldecode(hashToFile($_REQUEST['install_file'])))."-restore/".$cp['to'] );
2095 //check if this file exists in the -restore directory
2096 if(file_exists($backup_path)){
2097 //since the file exists, then we want do an md5 of the install version and the file system version
2098 //if(is_file($backup_path) && md5_file($backup_path) == md5_file($cp['to'])){
2099 //since the files are the same then we can safely move back from the -restore
2100 //directory into the file system
2101 $GLOBALS['log']->debug("ENABLE COPY:: FROM: ".$cp['from']. " TO: ".$cp['to']);
2102 $this->copy_path($cp['from'], $cp['to']);
2104 //since they are not equal then we need to prompt the user
2112 function disable_copy(){
2113 //when we disable we want to copy the -restore files back into the file system
2114 //but we should check the version in the module install against the version on the file system
2115 //if they match then we can copy the file back, but otherwise we should ask the user.
2117 // $GLOBALS['log']->debug('ModuleInstaller.php->disable_copy()');
2118 if(isset($GLOBALS['mi_overwrite_files']) && $GLOBALS['mi_overwrite_files']){
2119 // $GLOBALS['log']->debug('ModuleInstaller.php->disable_copy():mi_overwrite_files=true');
2120 if(!empty($this->installdefs['copy'])){
2121 // $GLOBALS['log']->debug('ModuleInstaller.php->disable_copy(): installdefs not empty');
2122 foreach($this->installdefs['copy'] as $cp){
2123 $cp['to'] = clean_path(str_replace('<basepath>', $this->base_dir, $cp['to']));
2124 $backup_path = clean_path( remove_file_extension(urldecode(hashToFile($_REQUEST['install_file'])))."-restore/".$cp['to'] ); // bug 16966 tyoung - replaced missing assignment to $backup_path
2125 //check if this file exists in the -restore directory
2126 // $GLOBALS['log']->debug("ModuleInstaller.php->disable_copy(): backup_path=".$backup_path);
2127 if(file_exists($backup_path)){
2128 //since the file exists, then we want do an md5 of the install version and the file system version
2129 $from = str_replace('<basepath>', $this->base_dir, $cp['from']);
2131 //if(is_file($from) && md5_file($from) == md5_file($cp['to'])){
2132 //since the files are the same then we can safely move back from the -restore
2133 //directory into the file system
2134 $GLOBALS['log']->debug("DISABLE COPY:: FROM: ".$backup_path. " TO: ".$cp['to']);
2135 $this->copy_path($backup_path, $cp['to']);
2137 //since they are not equal then we need to prompt the user
2145 public function reset_opcodes()
2147 /* Bug 39354 - added function_exists check. Not optimal fix, but safe nonetheless.
2148 * 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,
2149 * but this file has been updated to 6.1
2151 if(function_exists('sugar_clean_opcodes')){
2152 sugar_clean_opcodes();
2157 * BC implementation to provide specific calls to extensions
2159 public function __call($name, $args)
2161 $nameparts = explode('_', $name);
2162 // name is something_something
2163 if(count($nameparts) == 2 && isset($this->extensions[$nameparts[1]])) {
2164 $ext = $this->extensions[$nameparts[1]];
2165 switch($nameparts[0]) {
2167 return $this->enableExt($ext['section'], $ext['extdir']);
2169 return $this->disableExt($ext['section'], $ext['extdir']);
2171 return $this->installExt($ext['section'], $ext['extdir']);
2173 return $this->uninstallExt($ext['section'], $ext['extdir']);
2175 return $this->rebuildExt($ext['extdir'], $ext['file']);
2178 sugar_die("Unknown method ModuleInstaller::$name called");
2183 function UpdateSystemTabs($action, $installed_modules){
2184 require_once("modules/MySettings/TabController.php");
2185 $controller = new TabController();
2186 $isSystemTabsInDB = $controller->is_system_tabs_in_db();
2187 if ($isSystemTabsInDB && !empty($installed_modules))
2193 $currentTabs = $controller->get_system_tabs();
2194 foreach ($installed_modules as $module)
2196 if(in_array($module, $currentTabs)){
2197 unset($currentTabs[$module]);
2200 $controller->set_system_tabs($currentTabs);;
2203 $currentTabs = $controller->get_system_tabs();
2204 foreach ($installed_modules as $module)
2206 if(!in_array($module, $currentTabs)){
2207 $currentTabs[$module] = $module;
2210 $controller->set_system_tabs($currentTabs);