]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - ModuleInstall/ModuleInstaller.php
Release 6.5.15
[Github/sugarcrm.git] / ModuleInstall / ModuleInstaller.php
1 <?php
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.
6  * 
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.
13  * 
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
17  * details.
18  * 
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
22  * 02110-1301 USA.
23  * 
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.
26  * 
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.
30  * 
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  ********************************************************************************/
37
38
39 /*
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)
43  *
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";
49  */
50
51
52
53 require_once('include/utils/progress_bar_utils.php');
54
55 require_once('ModuleInstall/ModuleScanner.php');
56 define('DISABLED_PATH', 'Disabled');
57
58 class ModuleInstaller{
59         var $modules = array();
60         var $silent = false;
61         var $base_dir  = '';
62         var $modulesInPackage = array();
63         public $disabled_path = DISABLED_PATH;
64     public $id_name;
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;
71         }
72
73    /*
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.
79     *
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.
81     */
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();
89                                 sugar_cleanup(true);
90                         }
91                 }
92
93         // workaround for bug 45812 - refresh vardefs cache before unpacking to avoid partial vardefs in cache
94         global $beanList;
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);
99             }
100         }
101
102                 global $app_strings, $mod_strings;
103                 $this->base_dir = $base_dir;
104                 $total_steps = 5; //minimum number of steps with no tasks
105                 $current_step = 0;
106                 $tasks = array(
107                         'pre_execute',
108                         'install_copy',
109                     'install_extensions',
110                         'install_images',
111                         'install_dcactions',
112                         'install_dashlets',
113                         'install_connectors',
114                         'install_layoutfields',
115                         'install_relationships',
116             'enable_manifest_logichooks',
117                         'post_execute',
118                         'reset_opcodes',
119                 );
120
121                 $total_steps += count($tasks);
122                 if(file_exists($this->base_dir . '/manifest.php')){
123                                 if(!$this->silent){
124                                         $current_step++;
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">';
128                                 }
129
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];
137                                                         }else{
138                                                                 $errors[] = 'No Upgrade Path Found in manifest.';
139                                                                 $this->abort($errors);
140                                                         }//fi
141                                                 }//fi
142                                         }//fi
143                                 }//fi
144                                 $this->id_name = $installdefs['id'];
145                                 $this->installdefs = $installdefs;
146                                 if(!$this->silent){
147                                         $current_step++;
148                                         update_progress_bar('install', $current_step, $total_steps);
149                                 }
150
151                                 foreach($tasks as $task){
152                                         $this->$task();
153                                         if(!$this->silent){
154                                                 $current_step++;
155                                                 update_progress_bar('install', $current_step, $total_steps);
156                                         }
157                                 }
158                                 $this->install_beans($this->installed_modules);
159                                 if(!$this->silent){
160                                         $current_step++;
161                                         update_progress_bar('install', $total_steps, $total_steps);
162                                 }
163                                 if(isset($installdefs['custom_fields'])){
164                                         $this->log(translate('LBL_MI_IN_CUSTOMFIELD'));
165                                         $this->install_custom_fields($installdefs['custom_fields']);
166                                 }
167                                 if(!$this->silent){
168                                         $current_step++;
169                                         update_progress_bar('install', $current_step, $total_steps);
170                                         echo '</div>';
171                                 }
172                                 if(!$this->silent){
173                                         $current_step++;
174                                         update_progress_bar('install', $current_step, $total_steps);
175                                         echo '</div>';
176                                 }
177                                 $selectedActions = array(
178                         'clearTpls',
179                         'clearJsFiles',
180                         'clearDashlets',
181                         'clearVardefs',
182                         'clearJsLangFiles',
183                         'rebuildAuditTables',
184                         'repairDatabase',
185                 );
186                                 VardefManager::clearVardef();
187                                 global $beanList, $beanFiles, $moduleList;
188                                 if (file_exists('custom/application/Ext/Include/modules.ext.php'))
189                                 {
190                                     include('custom/application/Ext/Include/modules.ext.php');
191                                 }
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 );
204                 sugar_cache_reset();
205
206                                 //clear the unified_search_module.php file
207                     require_once('modules/Home/UnifiedSearchAdvanced.php');
208                     UnifiedSearchAdvanced::unlinkUnifiedSearchModulesFile();
209
210                                 $this->log('<br><b>' . translate('LBL_MI_COMPLETE') . '</b>');
211                 }else{
212                         die("No \$installdefs Defined In $this->base_dir/manifest.php");
213                 }
214
215         }
216
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);
221         }
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);
226         }
227
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));
233                         }
234                 }
235         }
236
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));
242                         }
243                 }
244         }
245
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));
251                         }
252                 }
253         }
254
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));
260                         }
261                 }
262         }
263
264         /*
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.
267      */
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 */
279                         }
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();
282                 }
283         }
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']);
292
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 */
297                                         }
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);
301                                 }
302         }
303
304
305         /**
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.
308          *
309          */
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)) {
321                                             unlink($to);
322                                         }
323                                 }else{
324                                         $GLOBALS['log']->fatal('ModuleInstaller[uninstall_new_file] Could not remove file ' . $to . ' as no backup file was found to restore to');
325                                 }
326                         }
327                 }
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']);
333                 }
334
335         }
336
337         /**
338          * Get directory where module's extensions go
339          * @param string $module Module name
340          */
341     public function getExtDir($module)
342     {
343             if($module == 'application') {
344             return "custom/Extension/application/Ext";
345         } else {
346                         return "custom/Extension/modules/$module/Ext";
347         }
348     }
349
350         /**
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
355          */
356         public function installExt($section, $extname, $module = '')
357         {
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']);
363                             } else {
364                                 $from = '';
365                             }
366                                 if(!empty($module)) {
367                                     $item['to_module'] = $module;
368                                 }
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";
372                 } else {
373                                     $path = "custom/Extension/modules/{$item['to_module']}/Ext/$extname";
374                 }
375                                 if(!file_exists($path)){
376                                         mkdir_recursive($path, true);
377                                 }
378                                 if(isset($item["name"])) {
379                                     $target = $item["name"];
380                                 } else if (!empty($from)){
381                     $target = basename($from, ".php");
382                 } else {
383                                     $target = $this->id_name;
384                                 }
385                                 if(!empty($from)) {
386                                     copy_recursive($from , "$path/$target.php");
387                                 }
388                         }
389                 }
390         }
391
392         /**
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
397          */
398         public function uninstallExt($section, $extname, $module = '')
399         {
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']);
405                             } else {
406                                 $from = '';
407                             }
408                             if(!empty($module)) {
409                                     $item['to_module'] = $module;
410                                 }
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";
414                 } else {
415                                     $path = "custom/Extension/modules/{$item['to_module']}/Ext/$extname";
416                 }
417                                 if(isset($item["name"])) {
418                                     $target = $item["name"];
419                                 } else if (!empty($from)){
420                     $target = basename($from, ".php");
421                 } else {
422                                     $target = $this->id_name;
423                                 }
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));
433                                 }
434                     }
435             }
436         }
437
438         /**
439      * Rebuild generic extension
440      * @param string $ext Extension directory
441      * @param string $filename Target filename
442      */
443         public function rebuildExt($ext, $filename)
444         {
445             $this->log(translate('LBL_MI_REBUILDING') . " $ext...");
446                         $this->merge_files("Ext/$ext/", $filename);
447         }
448
449         /**
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
454          */
455         public function disableExt($section, $extname, $module = '')
456         {
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']);
461                             } else {
462                                 $from = '';
463                             }
464                                 if(!empty($module)) {
465                                     $item['to_module'] = $module;
466                                 }
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";
470                 } else {
471                                     $path = "custom/Extension/modules/{$item['to_module']}/Ext/$extname";
472                 }
473                                 if(isset($item["name"])) {
474                                     $target = $item["name"];
475                                 } else if (!empty($from)){
476                     $target = basename($from, ".php");
477                 }else {
478                                     $target = $this->id_name;
479                                 }
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));
487                                 }
488                         }
489                 }
490         }
491
492         /**
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
497          */
498         public function enableExt($section, $extname, $module = '')
499         {
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']);
504                             } else {
505                                 $from = '';
506                             }
507                             if(!empty($module)) {
508                                     $item['to_module'] = $module;
509                                 }
510                                 $GLOBALS['log']->debug("Enabling $extname ... from $from for " .$item['to_module']);
511
512                 if($item['to_module'] == 'application') {
513                     $path = "custom/Extension/application/Ext/$extname";
514                 } else {
515                                     $path = "custom/Extension/modules/{$item['to_module']}/Ext/$extname";
516                 }
517                                 if(isset($item["name"])) {
518                                     $target = $item["name"];
519                                 } else if (!empty($from)){
520                     $target = basename($from, ".php");
521                 } else {
522                                     $target = $this->id_name;
523                                 }
524                                 if(!file_exists($path)) {
525                                     mkdir_recursive($path, true);
526                                 }
527                 $disabled_path = $path.'/'.DISABLED_PATH;
528                                 if (file_exists("$disabled_path/$target.php")) {
529                                         rename("$disabled_path/$target.php",  "$path/$target.php");
530                                 }
531                                 if (!empty($from) && file_exists($disabled_path . '/'. basename($from))) {
532                                         rename($disabled_path.'/'. basename($from),  $path . '/'. basename($from));
533                                 }
534                         }
535                 }
536     }
537
538     /**
539      * Method removes module from global search configurations
540      *
541      * return bool
542      */
543     public function uninstall_global_search()
544     {
545         if (empty($this->installdefs['beans']))
546         {
547             return true;
548         }
549
550         if (is_file('custom/modules/unified_search_modules_display.php') == false)
551         {
552             return true;
553         }
554
555         $user = new User();
556         $users = get_user_array();
557         $unified_search_modules_display = array();
558         require('custom/modules/unified_search_modules_display.php');
559
560         foreach($this->installdefs['beans'] as $beanDefs)
561         {
562             if (array_key_exists($beanDefs['module'], $unified_search_modules_display) == false)
563             {
564                 continue;
565             }
566             unset($unified_search_modules_display[$beanDefs['module']]);
567             foreach($users as $userId => $userName)
568             {
569                 if (empty($userId))
570                 {
571                     continue;
572                 }
573                 $user->retrieve($userId);
574                 $prefs = $user->getPreference('globalSearch', 'search');
575                 if (array_key_exists($beanDefs['module'], $prefs) == false)
576                 {
577                     continue;
578                 }
579                 unset($prefs[$beanDefs['module']]);
580                 $user->setPreference('globalSearch', $prefs, 0, 'search');
581                 $user->savePreferencesToDB();
582             }
583         }
584
585         if (write_array_to_file("unified_search_modules_display", $unified_search_modules_display, 'custom/modules/unified_search_modules_display.php') == false)
586         {
587             global $app_strings;
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);
591             return false;
592         }
593         return true;
594     }
595
596     /**
597      * Method enables module in global search configurations by disabled_module_visible key
598      *
599      * return bool
600      */
601     public function enable_global_search()
602     {
603         if (empty($this->installdefs['beans']))
604         {
605             return true;
606         }
607
608         if (is_file('custom/modules/unified_search_modules_display.php') == false)
609         {
610             return true;
611         }
612
613         $unified_search_modules_display = array();
614         require('custom/modules/unified_search_modules_display.php');
615
616         foreach($this->installdefs['beans'] as $beanDefs)
617         {
618             if (array_key_exists($beanDefs['module'], $unified_search_modules_display) == false)
619             {
620                 continue;
621             }
622             if (isset($unified_search_modules_display[$beanDefs['module']]['disabled_module_visible']) == false)
623             {
624                 continue;
625             }
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']);
628         }
629
630         if (write_array_to_file("unified_search_modules_display", $unified_search_modules_display, 'custom/modules/unified_search_modules_display.php') == false)
631         {
632             global $app_strings;
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);
636             return false;
637         }
638         return true;
639     }
640
641     /**
642      * Method disables module in global search configurations by disabled_module_visible key
643      *
644      * return bool
645      */
646     public function disable_global_search()
647     {
648         if (empty($this->installdefs['beans']))
649         {
650             return true;
651         }
652
653         if (is_file('custom/modules/unified_search_modules_display.php') == false)
654         {
655             return true;
656         }
657
658         $unified_search_modules_display = array();
659         require('custom/modules/unified_search_modules_display.php');
660
661         foreach($this->installdefs['beans'] as $beanDefs)
662         {
663             if (array_key_exists($beanDefs['module'], $unified_search_modules_display) == false)
664             {
665                 continue;
666             }
667             if (isset($unified_search_modules_display[$beanDefs['module']]['visible']) == false)
668             {
669                 continue;
670             }
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;
673         }
674
675         if (write_array_to_file("unified_search_modules_display", $unified_search_modules_display, 'custom/modules/unified_search_modules_display.php') == false)
676         {
677             global $app_strings;
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);
681             return false;
682         }
683         return true;
684     }
685
686     public function install_extensions()
687         {
688             foreach($this->extensions as $extname => $ext) {
689                 $install = "install_$extname";
690                 if(method_exists($this, $install)) {
691                     // non-standard function
692                 $this->$install();
693                 } else {
694                     if(!empty($ext["section"])) {
695                         $module = isset($ext['module'])?$ext['module']:'';
696                         $this->installExt($ext["section"], $ext["extdir"], $module);
697                     }
698                 }
699             }
700             $this->rebuild_extensions();
701         }
702
703         public function uninstall_extensions()
704         {
705             foreach($this->extensions as $extname => $ext) {
706                 $func = "uninstall_$extname";
707                 if(method_exists($this, $func)) {
708                     // non-standard function
709                 $this->$func();
710                 } else {
711                     if(!empty($ext["section"])) {
712                         $module = isset($ext['module'])?$ext['module']:'';
713                         $this->uninstallExt($ext["section"], $ext["extdir"], $module);
714                     }
715                 }
716             }
717             $this->rebuild_extensions();
718         }
719
720         public function rebuild_extensions()
721         {
722             foreach($this->extensions as $extname => $ext) {
723                 $func = "rebuild_$extname";
724                 if(method_exists($this, $func)) {
725                     // non-standard function
726                 $this->$func();
727                 } else {
728                 $this->rebuildExt($ext["extdir"], $ext["file"]);
729                 }
730             }
731         }
732
733         public function disable_extensions()
734         {
735             foreach($this->extensions as $extname => $ext) {
736                 $func = "disable_$extname";
737                 if(method_exists($this, $func)) {
738                     // non-standard install
739                 $this->$func();
740                 } else {
741                     if(!empty($ext["section"])) {
742                         $module = isset($ext['module'])?$ext['module']:'';
743                         $this->disableExt($ext["section"], $ext["extdir"], $module);
744                     }
745                 }
746             }
747             $this->rebuild_extensions();
748         }
749
750         public function enable_extensions()
751         {
752             foreach($this->extensions as $extname => $ext) {
753                 $func = "enable_$extname";
754                 if(method_exists($this, $func)) {
755                     // non-standard install
756                 $this->$func();
757                 } else {
758                     if(!empty($ext["section"])) {
759                         $module = isset($ext['module'])?$ext['module']:'';
760                         $this->enableExt($ext["section"], $ext["extdir"], $module);
761                     }
762                 }
763             }
764             $this->rebuild_extensions();
765         }
766
767         function install_dashlets()
768         {
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);
777                                 }
778                                 copy_recursive($cp['from'] , $path);
779                         }
780                         include('modules/Administration/RebuildDashlets.php');
781
782                 }
783         }
784
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);
793                         }
794                         include('modules/Administration/RebuildDashlets.php');
795                 }
796         }
797
798
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');
803
804                 }
805         }
806
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);
816                                 }
817                                 copy_recursive($action['from'] , $path . '/'. $this->id_name . '.php');
818                         }
819                         $this->rebuild_dashletcontainers();
820                 }
821         }
822
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'))
831                                 {
832                                         rmdir_recursive( $path . '/'. $this->id_name . '.php');
833                                 }
834                                 else if (sugar_is_file($path . '/'. DISABLED_PATH . '/'. $this->id_name . '.php', 'w'))
835                                 {
836                                         rmdir_recursive( $path . '/'. DISABLED_PATH . '/'. $this->id_name . '.php');
837                                 }
838                         }
839                         $this->rebuild_dashletcontainers();
840                 }
841         }
842
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);
853                                 }
854                                 copy_recursive($cp['connector'] , $source_path);
855
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);
862                                         }
863                                         copy_recursive($cp['formatter'] , $formatter_path);
864                                 }
865                         }
866                         require_once('include/connectors/utils/ConnectorUtils.php');
867                         ConnectorUtils::installSource($cp['name']);
868                 }
869
870         }
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);
881                         }
882                         require_once('include/connectors/utils/ConnectorUtils.php');
883                         //ConnectorUtils::getConnectors(true);
884                         ConnectorUtils::uninstallSource($cp['name']);
885                 }
886         }
887
888         function install_vardef($from, $to_module)
889         {
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';
894                         }
895                         if(!file_exists($path)){
896                                 mkdir_recursive($path, true);
897                         }
898                         copy_recursive($from , $path.'/'. basename($from));
899         }
900
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';
906                         }
907                         if(!file_exists($path)){
908                                 mkdir_recursive($path, true);
909                         }
910                         copy_recursive($from , $path.'/'. basename($from));
911         }
912
913     // Non-standard - needs special rebuild call
914         function install_languages()
915         {
916         $languages = array();
917         if(isset($this->installdefs['language']))
918         {
919             $this->log(translate('LBL_MI_IN_LANG') );
920             foreach($this->installdefs['language'] as $packs)
921             {
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);
929                 }
930                 copy_recursive($packs['from'], $path);
931
932                         }
933                         $this->rebuild_languages($languages, $modules);
934
935                 }
936         }
937
938     /**
939      * Function return path to file where store label
940      * 
941      * @param $packs
942      * @return string
943      */
944     protected function getInstallLanguagesPath($packs)
945     {
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';
949         }
950         $path .= '/'.$packs['language'].'.'. $this->id_name . '.php';
951         return $path;
952     }
953
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';
967                                                 }
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');
972                                                 }
973                                         }
974                                         $this->rebuild_languages($languages, $modules);
975
976                                 }
977         }
978
979     // Non-standard, needs special rebuild
980         public function disable_languages()
981         {
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";
991                 } else {
992                                     $path = "custom/Extension/modules/{$item['to_module']}/Ext/Language";
993                 }
994                                 if(isset($item["name"])) {
995                                     $target = $item["name"];
996                                 } else {
997                                     $target = $this->id_name;
998                                 }
999                                 $target = "{$item['language']}.$target";
1000
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));
1008                                 }
1009                         }
1010                         $this->rebuild_languages($languages, $modules);
1011                 }
1012         }
1013
1014     // Non-standard, needs special rebuild
1015         public function enable_languages()
1016         {
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;
1025                                 }
1026
1027                 if($item['to_module'] == 'application') {
1028                     $path = "custom/Extension/application/Ext/Language";
1029                 } else {
1030                                     $path = "custom/Extension/modules/{$item['to_module']}/Ext/Language";
1031                 }
1032                                 if(isset($item["name"])) {
1033                                     $target = $item["name"];
1034                                 } else {
1035                                     $target = $this->id_name;
1036                                 }
1037                                 $target = "{$item['language']}.$target";
1038
1039                                 if(!file_exists($path)) {
1040                                     mkdir_recursive($path, true);
1041                                 }
1042                 $disabled_path = $path.'/'.DISABLED_PATH;
1043                                 if (file_exists("$disabled_path/$target.php")) {
1044                                         rename("$disabled_path/$target.php",  "$path/$target.php");
1045                                 }
1046                                 if (file_exists($disabled_path . '/'. basename($from))) {
1047                                         rename($disabled_path.'/'. basename($from),  $path . '/'. basename($from));
1048                                 }
1049                         }
1050                         $this->rebuild_languages($languages, $modules);
1051                 }
1052     }
1053
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(
1058      ... blah blah ...
1059          'logic_hooks' => array(
1060              array('module'      => 'Accounts',
1061                    'hook'        => 'after_save',
1062                    'order'       => 99,
1063                    'description' => 'Account sample logic hook',
1064                    'file'        => 'modules/Sample/sample_account_logic_hook_file.php',
1065                    'class'       => 'SampleLogicClass',
1066                    'function'    => 'accountAfterSave',
1067              ),
1068          ),
1069      ... blah blah ...
1070      );
1071      */
1072     function enable_manifest_logichooks() {
1073         if(empty($this->installdefs['logic_hooks']) || !is_array($this->installdefs['logic_hooks'])) {
1074            return;
1075         }
1076
1077
1078
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']));
1081         }
1082     }
1083
1084     function disable_manifest_logichooks() {
1085         if(empty($this->installdefs['logic_hooks']) || !is_array($this->installdefs['logic_hooks'])) {
1086             return;
1087         }
1088
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']));
1091         }
1092     }
1093
1094     /**
1095      * Check labels inside label files and remove them
1096      * 
1097      * @param $basePath - path to files with labels
1098      * @param array $labelDefinitions - format like output from AbstractRelationship buildLabels()
1099      */
1100     public function uninstallLabels($basePath, $labelDefinitions)
1101     {
1102
1103         foreach ($labelDefinitions as $definition) {
1104
1105             $filename = $basePath . "{$definition['module']}.php";
1106
1107             if (!file_exists($filename)) {
1108                 continue;
1109             }
1110
1111             $uninstalLabes = $this->getLabelsToUninstall($labelDefinitions);
1112             $this->uninstallLabel($uninstalLabes, $definition, $filename);
1113         }
1114
1115     }
1116
1117     /**
1118      * Check labels inside label file and remove them
1119      * 
1120      * @param $uninstalLabes
1121      * @param $definition
1122      * @param $filename
1123      */
1124     protected function uninstallLabel($uninstalLabes, $definition, $filename)
1125     {
1126         $app_list_strings = array();
1127         $mod_strings = array();
1128         $stringsName = $definition['module'] == 'application' ? 'app_list_strings' : 'mod_strings';
1129
1130         include($filename);
1131         if ('app_list_strings' == $stringsName) {
1132             $strings = $app_list_strings;
1133         } else {
1134             $strings = $mod_strings;
1135         }
1136
1137         foreach ($uninstalLabes AS $label) {
1138             if (isset($strings[$label])) {
1139                 unset($strings[$label]);
1140             }
1141         }
1142
1143         if (count($strings)) {
1144             $this->saveContentToFile($filename, $stringsName, $strings);
1145         } else {
1146             unlink($filename);
1147         }
1148     }
1149
1150     /**
1151      * Save labels that not need be uninstalled at this case
1152      * 
1153      * @param $filename
1154      * @param $stringsName
1155      * @param $strings
1156      */
1157     protected function saveContentToFile($filename, $stringsName, $strings)
1158     {
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);
1162         }
1163         sugar_file_put_contents($filename, $fileContent);
1164     }
1165
1166     /**
1167      * Uninstall extend labels
1168      * 
1169      * @param $labelDefinitions
1170      */
1171     public function uninstallExtLabels($labelDefinitions)
1172     {
1173         foreach ($labelDefinitions as $definition) {
1174             if (!isset($GLOBALS['sugar_config']['languages']) || !is_array($GLOBALS['sugar_config']['languages'])) {
1175                 continue;
1176             }
1177             
1178             foreach (array_keys($GLOBALS['sugar_config']['languages']) AS $language) {
1179                 $pathDef = array(
1180                     'language' => $language,
1181                     'to_module' => $definition['module']
1182                 );
1183                 $path = $this->getInstallLanguagesPath($pathDef);
1184                 if (file_exists($path)) {
1185                     unlink($path);
1186                 }
1187             }
1188         }
1189
1190     }
1191
1192     /**
1193      * Returns the names of the label(key 'system_label') from a multi-dimensional array $labelDefinitions
1194      * 
1195      * @param $labelDefinitions
1196      * @return array of labels
1197      */
1198     protected function getLabelsToUninstall($labelDefinitions)
1199     {
1200         $labels = array();
1201         foreach($labelDefinitions AS $definition){
1202             $labels[] = $definition['system_label'];
1203         }
1204         return $labels;
1205     }
1206
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);
1212
1213                 if(!$uninstall) {
1214                 $from = str_replace('<basepath>', $this->base_dir, $from);
1215                 $GLOBALS['log']->debug('Copy ' . $from);
1216                 }
1217                 else {
1218                         $from = str_replace('<basepath>', $backup_path, $from);
1219                         //$GLOBALS['log']->debug('Restore ' . $from);
1220                 }
1221                 $from = clean_path($from);
1222                 $to = clean_path($to);
1223
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)){
1227                         $dir = $to;
1228                 }
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);
1236                 }
1237 /* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
1238                 }
1239                 elseif(!$this->copy_recursive_with_backup($from, $to, $backup_path, $uninstall)){
1240                         die('Failed to copy ' . $from. ' to ' . $to);
1241                 }
1242 /* END - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
1243         }
1244
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){
1250                         $installed = false;
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'] = '';
1257
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"))
1260                 {
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);
1266                     }
1267                 }
1268
1269                 if(file_exists($beanFiles[$class])){
1270                                         require_once($beanFiles[$class]);
1271                                         $mod = new $class();
1272                                         $installed = true;
1273                                         $fieldObject = get_widget($field['type']);
1274                                         $fieldObject->populateFromRow($field);
1275                                         $mod->custom_fields->use_existing_labels =  true;
1276                                         $mod->custom_fields->addFieldObject($fieldObject);
1277                                 }
1278                         }
1279                         if(!$installed){
1280                                 $GLOBALS['log']->debug('Could not install custom field ' . $field['name'] . ' for module ' .  $field['module'] . ': Module does not exist');
1281                         }
1282                 }
1283         }
1284
1285         function uninstall_custom_fields($fields){
1286                 global $beanList, $beanFiles;
1287                 require_once('modules/DynamicFields/DynamicField.php');
1288                 $dyField = new DynamicField();
1289
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']);
1298                         }
1299                 }
1300         }
1301
1302         /*
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'
1307      */
1308     function install_relationships ()
1309     {
1310         if (isset ( $this->installdefs [ 'relationships' ] ))
1311         {
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 ;
1315
1316             if (! file_exists ( "custom/Extension/application/Ext/TableDictionary" ))
1317             {
1318                 mkdir_recursive ( "custom/Extension/application/Ext/TableDictionary", true ) ;
1319             }
1320
1321             foreach ( $this->installdefs [ 'relationships' ] as $key => $relationship )
1322             {
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 ;
1327
1328                 if (! empty ( $relationship [ 'module_vardefs' ] ))
1329                 {
1330                     $relationship [ 'module_vardefs' ] = str_replace ( '<basepath>', $this->base_dir, $relationship [ 'module_vardefs' ] ) ;
1331                     $this->install_vardef ( $relationship [ 'module_vardefs' ], $relationship [ 'module' ] ) ;
1332                 }
1333
1334                 if (! empty ( $relationship [ 'module_layoutdefs' ] ))
1335                 {
1336                     $relationship [ 'module_layoutdefs' ] = str_replace ( '<basepath>', $this->base_dir, $relationship [ 'module_layoutdefs' ] ) ;
1337                     $this->install_layoutdef ( $relationship [ 'module_layoutdefs' ], $relationship [ 'module' ] ) ;
1338                 }
1339
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?>" ) ;
1343                 fclose ( $out ) ;
1344             }
1345
1346
1347
1348
1349             Relationship::delete_cache();
1350             $this->rebuild_vardefs () ;
1351             $this->rebuild_layoutdefs () ;
1352             if ($save_table_dictionary)
1353             {
1354                 $this->rebuild_tabledictionary () ;
1355             }
1356             require_once("data/Relationships/RelationshipFactory.php");
1357             SugarRelationshipFactory::deleteCache();
1358         }
1359     }
1360
1361         /*
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.
1365      */
1366         function install_relationship($file)
1367         {
1368                 $_REQUEST['moduleInstaller'] = true;
1369                 if(!file_exists($file))
1370                 {
1371                         $GLOBALS['log']->debug( 'File does not exists : '.$file);
1372                         return;
1373                 }
1374                 include($file);
1375                 $rel_dictionary = $dictionary;
1376                 foreach ($rel_dictionary as $rel_name => $rel_data)
1377             {
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' ] ) )
1381             {
1382                 $table = $rel_data[ 'table' ];
1383
1384                 if(!$this->db->tableExists($table))
1385                 {
1386                     $this->db->createTableParams($table, $rel_data[ 'fields' ], $rel_data[ 'indices' ]);
1387                 }
1388             }
1389
1390             if(!$this->silent)
1391                 $GLOBALS['log']->debug("Processing relationship meta for ". $rel_name."...");
1392             SugarBean::createRelationshipMeta($rel_name, $this->db,$table,$rel_dictionary,'');
1393             Relationship::delete_cache();
1394             if(!$this->silent)
1395                 $GLOBALS['log']->debug( 'done<br>');
1396         }
1397         }
1398
1399         function install_layoutfields() {
1400                  if (!empty ( $this->installdefs [ 'layoutfields' ] ))
1401                  {
1402                         foreach ( $this->installdefs [ 'layoutfields' ] as $fieldSet )
1403             {
1404                                 if (!empty($fieldSet['additional_fields']))
1405                                 {
1406                                         $this->addFieldsToLayout($fieldSet['additional_fields']);
1407                                 }
1408             }
1409                  }
1410         }
1411
1412         function uninstall_layoutfields() {
1413                  if (!empty ( $this->installdefs [ 'layoutfields' ] ))
1414                  {
1415                         foreach ( $this->installdefs [ 'layoutfields' ] as $fieldSet )
1416             {
1417                                 if (!empty($fieldSet['additional_fields']))
1418                                 {
1419                                         $this->removeFieldsFromLayout($fieldSet['additional_fields']);
1420                                 }
1421             }
1422                  }
1423         }
1424
1425         function uninstall_relationship($file, $rel_dictionary = null){
1426         if ($rel_dictionary == null)
1427                 {
1428                         if(!file_exists($file)){
1429                                 $GLOBALS['log']->debug( 'File does not exists : '.$file);
1430                                 return;
1431                         }
1432                         include($file);
1433                         $rel_dictionary = $dictionary;
1434                 }
1435
1436                 foreach ($rel_dictionary as $rel_name => $rel_data)
1437                 {
1438                         if (!empty($rel_data['table'])){
1439                                 $table = $rel_data['table'];
1440                         }
1441                         else{
1442                                 $table = ' One-to-Many ';
1443                         }
1444
1445                         if ($this->db->tableExists($table) && isset($GLOBALS['mi_remove_tables']) && $GLOBALS['mi_remove_tables'])
1446                         {
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);
1450                         }
1451
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' ]))
1455                                 $mods = array(
1456                                         $rel_data['relationships'][$rel_name]['lhs_module'],
1457                                         $rel_data['relationships'][$rel_name]['rhs_module'],
1458                                 );
1459                         else
1460                                 $mods = array($rel_data[ 'module' ]);
1461
1462                         $filename = "$rel_name.php";
1463
1464                         foreach($mods as $mod) {
1465                                 if ($mod != 'application' )  {
1466                                         $basepath = "custom/Extension/modules/$mod/Ext/";
1467                                 } else {
1468                                         $basepath = "custom/Extension/application/Ext/";
1469                                 }
1470
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 );
1476
1477                                         //remove any layoutdefs
1478                                         $path = $basepath . "Layoutdefs/$fn" ;
1479                                         if( file_exists( $path ))
1480                                         {
1481                                                 rmdir_recursive( $path );
1482                                         }
1483                     $path = $basepath . "WirelessLayoutdefs/$fn";
1484                     if (file_exists($path)) {
1485                         rmdir_recursive($path);
1486                     }
1487                                 }
1488                 $relationships_path = 'custom/Extension/modules/relationships/';
1489
1490                 $relationships_dirs = array(
1491                     'layoutdefs',
1492                     'vardefs',
1493                     'wirelesslayoutdefs'
1494                 );
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);
1499                     }
1500                 }
1501                 if (file_exists($relationships_path . "relationships/{$rel_name}MetaData.php")) {
1502                     rmdir_recursive($relationships_path . "relationships/{$rel_name}MetaData.php");
1503                 }
1504             }
1505
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");
1510
1511                                 if (file_exists("custom/metadata/{$rel_name}MetaData.php"))
1512                                         unlink( "custom/metadata/{$rel_name}MetaData.php" );
1513                         }
1514                 }
1515         }
1516
1517         function uninstall_relationships($include_studio_relationships = false){
1518                 $relationships = array();
1519
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');
1525                 }
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']))
1534                         {
1535                                 $field_defs = $dictionary[$bean]['fields'];
1536                                 foreach($field_defs as $field => $def)
1537                                 {
1538                                         //Weed out most fields first
1539                                         if (isset ($def['type']))
1540                                         {
1541                                                 //Custom relationships created in the relationship editor
1542                                                 if ($def['type'] == "link" && !empty($def['relationship']) && !empty($dictionary[$def['relationship']]))
1543                                                 {
1544                                                         $rel_name = $def['relationship'];
1545
1546                                                         $rel_def = $dictionary[$rel_name]['relationships'][$rel_name];
1547
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 )
1551                                                                 {
1552                                                                         $dictionary[$rel_name]['from_studio'] = true;
1553                                                                         $relationships[$rel_name] = $dictionary[$rel_name];
1554                                                                 }
1555                                                         }
1556                                                 }
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)
1561                                                                 {
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 ) ;
1574                                                                         }
1575                                                                 }
1576                                                         }
1577                                                 }
1578                                         }
1579                                 }
1580                         }
1581                 }
1582
1583
1584
1585                 $this->uninstall_relationship(null, $relationships);
1586
1587                 if(isset($this->installdefs['relationships'])) {
1588                         $relationships = $this->installdefs['relationships'];
1589                         $this->log(translate('LBL_MI_UN_RELATIONSHIPS') );
1590                         foreach($relationships as $relationship)
1591                         {
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 );
1599                         }
1600                 }
1601
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();
1606         }
1607
1608
1609
1610
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
1615                 $current_step = 0;
1616                 $this->base_dir = $base_dir;
1617                 $tasks = array(
1618                         'pre_uninstall',
1619                         'uninstall_relationships',
1620                         'uninstall_copy',
1621                         'uninstall_dcactions',
1622                         'uninstall_dashlets',
1623                         'uninstall_connectors',
1624                         'uninstall_layoutfields',
1625                     'uninstall_extensions',
1626             'uninstall_global_search',
1627                         'disable_manifest_logichooks',
1628                         'post_uninstall',
1629                 );
1630                 $total_steps += count($tasks); //now the real number of steps
1631                 if(file_exists($this->base_dir . '/manifest.php')){
1632                                 if(!$this->silent){
1633                                         $current_step++;
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">';
1636                                 }
1637
1638                                 global $moduleList;
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){
1645
1646                                                 $installed_modules[] = $bean['module'];
1647                                                 $this->uninstall_user_prefs($bean['module']);
1648                                         }
1649                                         $this->modulesInPackage = $installed_modules;
1650                                         $this->uninstall_beans($installed_modules);
1651                                         $this->uninstall_customizations($installed_modules);
1652                                         if(!$this->silent){
1653                                                 $current_step++;
1654                                                 update_progress_bar('install', $total_steps, $total_steps);
1655                                         }
1656                                 }
1657                                 if(!$this->silent){
1658                                         $current_step++;
1659                                         update_progress_bar('install', $current_step, $total_steps);
1660                                 }
1661                                 foreach($tasks as $task){
1662                                         $this->$task();
1663                                         if(!$this->silent){
1664                                                 $current_step++;
1665                                                 update_progress_bar('install', $current_step, $total_steps);
1666                                         }
1667                                 }
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']);
1671                                 }
1672                                 if(!$this->silent){
1673                                         $current_step++;
1674                                         update_progress_bar('install', $current_step, $total_steps);
1675                                         echo '</div>';
1676                                 }
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;
1682
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();
1686                                 }
1687                                 //end
1688
1689                                 if(!$this->silent){
1690                                         $current_step++;
1691                                         update_progress_bar('install', $current_step, $total_steps);
1692                                         echo '</div>';
1693                                 }
1694
1695                                 UpdateSystemTabs('Restore',$installed_modules);
1696
1697                     //clear the unified_search_module.php file
1698                     require_once('modules/Home/UnifiedSearchAdvanced.php');
1699                     UnifiedSearchAdvanced::unlinkUnifiedSearchModulesFile();
1700
1701                                 $this->log('<br><b>' . translate('LBL_MI_COMPLETE') . '</b>');
1702                                 if(!$this->silent){
1703                                         update_progress_bar('install', $total_steps, $total_steps);
1704                                 }
1705                 }else{
1706                         die("No manifest.php Defined In $this->base_dir/manifest.php");
1707                 }
1708         }
1709
1710         function rebuild_languages($languages = array(), $modules="")
1711         {
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);
1715                     if($modules!=""){
1716                         foreach($modules as $module){
1717                                 LanguageManager::clearLanguageCache($module, $language);
1718                         }
1719                     }
1720                         }
1721                         sugar_cache_reset();
1722         }
1723
1724         function rebuild_vardefs()
1725         {
1726             $this->rebuildExt("Vardefs", 'vardefs.ext.php');
1727                 sugar_cache_reset();
1728         }
1729
1730         function rebuild_dashletcontainers(){
1731             $this->log(translate('LBL_MI_REBUILDING') . " DC Actions...");
1732                         $this->merge_files('Ext/DashletContainer/Containers/', 'dcactions.ext.php');
1733         }
1734
1735         function rebuild_tabledictionary()
1736         {
1737             $this->rebuildExt("TableDictionary", 'tabledictionary.ext.php');
1738         }
1739
1740         function rebuild_relationships() {
1741         if(!$this->silent) echo translate('LBL_MI_REBUILDING') . ' Relationships';
1742                 $_REQUEST['silent'] = true;
1743                 global $beanFiles;
1744                 include('include/modules.php');
1745                 include("modules/Administration/RebuildRelationship.php");
1746         }
1747
1748         function remove_acl_actions() {
1749                 global $beanFiles, $beanList, $current_user;
1750                 include('include/modules.php');
1751                 include("modules/ACL/remove_actions.php");
1752         }
1753
1754         /**
1755          * Wrapper call to modules/Administration/RepairIndex.php
1756          */
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");
1763         }
1764
1765         /**
1766          * Rebuilds the extension files found in custom/Extension
1767          * @param boolean $silent
1768          */
1769         function rebuild_all($silent=false){
1770                 if(defined('TEMPLATE_URL'))SugarTemplateUtilities::disableCache();
1771                 $this->silent=$silent;
1772                 global $sugar_config;
1773
1774                 //Check for new module extensions
1775                 $this->rebuild_modules();
1776
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();
1784         }
1785
1786         /*
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>
1790      */
1791         function merge_files($path, $name, $filter = '', $application = false){
1792                 if(!$application){
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);
1803                                         $shouldSave = true;
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")
1808                                                 {
1809                                                      if (substr($entry, 0, 9) == '_override') {
1810                                                         $override[] = $entry;
1811                                                     } else {
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);
1815                                                     }
1816                                                 }
1817                                         }
1818                                         foreach ($override as $entry) {
1819                         $file = file_get_contents($module_install . '/' . $entry);
1820                         $extension .= "\n". str_replace(array('<?php', '?>', '<?PHP', '<?'), array('','', '' ,'') , $file);
1821                                         }
1822                                 }
1823                                 $extension .= "\n?>";
1824
1825                                 if($shouldSave){
1826                                         if(!file_exists("custom/$extpath")) {
1827                                             mkdir_recursive("custom/$extpath", true);
1828                                     }
1829                                         $out = sugar_fopen("custom/$extpath/$name", 'w');
1830                                         fwrite($out,$extension);
1831                                         fclose($out);
1832                                 }else{
1833                                         if(file_exists("custom/$extpath/$name")){
1834                                                 unlink("custom/$extpath/$name");
1835                                         }
1836                                 }
1837                         }
1838
1839                 }
1840
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()){
1850                                                                 $shouldSave = true;
1851                                                                 if((empty($filter) || substr_count($entry, $filter) > 0) && is_file($module_install.'/'.$entry)
1852                                                                   && $entry != '.' && $entry != '..' && strtolower(substr($entry, -4)) == ".php")
1853                                                                 {
1854                                                                         $file = file_get_contents($module_install . '/' . $entry);
1855                                                                         $extension .= "\n". str_replace(array('<?php', '?>', '<?PHP', '<?'), array('','', '' ,'') , $file);
1856                                                                 }
1857                                                 }
1858                                         }
1859                                         $extension .= "\n?>";
1860                                         if($shouldSave){
1861                                                 if(!file_exists("custom/$extpath")){
1862                                                         mkdir_recursive("custom/$extpath", true);
1863                                                 }
1864                                                 $out = sugar_fopen("custom/$extpath/$name", 'w');
1865                                                 fwrite($out,$extension);
1866                                                 fclose($out);
1867                                         }else{
1868                                         if(file_exists("custom/$extpath/$name")){
1869                                                 unlink("custom/$extpath/$name");
1870                                         }
1871                                 }
1872
1873 }
1874
1875     function install_modules()
1876     {
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'];
1886
1887                                         $str .= "\$beanList['$module'] = '$class';\n";
1888                                         $str .= "\$beanFiles['$class'] = '$path';\n";
1889                                         if($bean['tab']){
1890                                                 $str .= "\$moduleList[] = '$module';\n";
1891                                                 $this->install_user_prefs($module, empty($bean['hide_by_default']));
1892                                                 $this->tab_modules[] = $module;
1893                                         }else{
1894                                                 $str .= "\$modules_exempt_from_availability_check['$module'] = '$module';\n";
1895                                                 $str .= "\$report_include_modules['$module'] = '$module';\n";
1896                                                 $str .= "\$modInvisList[] = '$module';\n";
1897                                         }
1898                                     $this->installed_modules[] = $module;
1899                                 }else{
1900                                                 $errors[] = 'Bean array not well defined.';
1901                                                 $this->abort($errors);
1902                                 }
1903                         }
1904                         $str.= "\n?>";
1905                         if(!file_exists("custom/Extension/application/Ext/Include")){
1906                                 mkdir_recursive("custom/Extension/application/Ext/Include", true);
1907                         }
1908                         file_put_contents("custom/Extension/application/Ext/Include/{$this->id_name}.php", $str);
1909         }
1910     }
1911
1912     /*
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
1914      * bean/module.
1915      */
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();
1925                                         //#30273
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);    
1930                                         }
1931                                 }else{
1932                                         $GLOBALS['log']->debug( "File Does Not Exist:" . $beanFiles[$class] );
1933                                 }
1934                         }
1935                 }
1936         }
1937
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];
1944
1945                                 if(file_exists($beanFiles[$class])){
1946                                         require_once($beanFiles[$class]);
1947                                         $mod = new $class();
1948
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();
1953                                         }
1954                                 }else{
1955                                         $GLOBALS['log']->debug( "File Does Not Exist:" . $beanFiles[$class] );
1956                                 }
1957                         }
1958                 }
1959         }
1960
1961         /**
1962          * Remove any customizations made within Studio while the module was installed.
1963          */
1964         function uninstall_customizations($beans){
1965         foreach($beans as $bean){
1966                         $dirs = array(
1967                                 'custom/modules/' . $bean,
1968                                 'custom/Extension/modules/' . $bean,
1969                 'custom/working/modules/' . $bean
1970                         );
1971                 foreach($dirs as $dir)
1972                 {
1973                                 if(is_dir($dir)){
1974                                         rmdir_recursive($dir);
1975                                 }
1976                 }
1977                 }
1978         }
1979
1980         function log($str){
1981                 $GLOBALS['log']->debug('ModuleInstaller:'. $str);
1982                 if(!$this->silent){
1983                         echo $str . '<br>';
1984                 }
1985         }
1986
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)) {
1990             if($uninstall) {
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));
1996                 }
1997                     else {
1998                         $GLOBALS['log']->debug( "Can't restore file: " . $source );
1999                         return true;
2000                 }
2001             }
2002             else {
2003                         if(file_exists($dest)) {
2004                                 $rest = clean_path($backup_path."/$dest");
2005                                 if( !is_dir(dirname($rest)) )
2006                                         mkdir_recursive(dirname($rest), true);
2007
2008                                 $GLOBALS['log']->debug("Backup ... " . $dest.  " to " .$rest );
2009                                 if(copy( $dest, $rest)) {
2010                                         if(is_writable($rest))
2011                                                 sugar_touch( $rest, filemtime($dest) );
2012                                 }
2013                                 else {
2014                                         $GLOBALS['log']->debug( "Can't backup file: " . $dest );
2015                                 }
2016                         }
2017                         return( copy( $source, $dest ) );
2018                 }
2019     }
2020     elseif(!is_dir($source)) {
2021             if($uninstall) {
2022                         if(is_file($dest))
2023                                 return(unlink($dest));
2024                         else {
2025                                 //don't do anything we already cleaned up the files using uninstall_new_files
2026                                 return true;
2027                         }
2028                 }
2029                 else
2030                         return false;
2031         }
2032
2033     if( !is_dir($dest) && !$uninstall){
2034         sugar_mkdir( $dest );
2035     }
2036
2037     $status = true;
2038
2039     $d = dir( $source );
2040     while( $f = $d->read() ){
2041         if( $f == "." || $f == ".." ){
2042             continue;
2043         }
2044         $status &= $this->copy_recursive_with_backup( "$source/$f", "$dest/$f", $backup_path, $uninstall );
2045     }
2046     $d->close();
2047     return( $status );
2048 }
2049
2050 private function dir_get_files($path, $base_path){
2051         $files = array();
2052         if(!is_dir($path))return $files;
2053         $d = dir($path);
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));
2059         }
2060         $d->close();
2061         return $files;
2062
2063 }
2064
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;
2069         $d = dir($path);
2070         $count = 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);
2076         }
2077         $d->close();
2078         return $count;
2079
2080
2081 }
2082 /* END - RESTORE POINT - by MR. MILK August 31, 2005 02:15:34 PM */
2083
2084
2085         /**
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
2088          *
2089          * @param errors        an array of error messages which will be displayed on the
2090          *                                      main module loader page once it is loaded.
2091          */
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">';
2096                 die();
2097                 //header('Location: index.php?module=Administration&action=UpgradeWizard&view=module');
2098         }
2099
2100         /**
2101          * Return the set of errors stored in the SESSION
2102          *
2103          * @return an array of errors
2104          */
2105         function getErrors(){
2106                 if(!empty($_SESSION['MODULEINSTALLER_ERRORS'])){
2107                         $errors = $_SESSION['MODULEINSTALLER_ERRORS'];
2108                         unset($_SESSION['MODULEINSTALLER_ERRORS']);
2109                         return $errors;
2110                 }
2111                 else
2112                         return null;
2113         }
2114
2115         /*
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
2119      * return null
2120      */
2121         function addFieldsToLayout($layoutAdditions) {
2122         require_once 'modules/ModuleBuilder/parsers/views/GridLayoutMetaDataParser.php' ;
2123
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' ) ;
2128
2129         foreach ( $layoutAdditions as $deployedModuleName => $fieldName )
2130         {
2131             if ( ! in_array( strtolower ( $deployedModuleName ) , $invalidModules ) )
2132             {
2133                 foreach ( array ( MB_EDITVIEW , MB_DETAILVIEW ) as $view )
2134                 {
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 ) ;
2139                 }
2140             }
2141         }
2142
2143         }
2144
2145         function removeFieldsFromLayout($layoutAdditions) {
2146         require_once 'modules/ModuleBuilder/parsers/views/GridLayoutMetaDataParser.php' ;
2147
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' ) ;
2152
2153         foreach ( $layoutAdditions as $deployedModuleName => $fieldName )
2154         {
2155             if ( ! in_array( strtolower ( $deployedModuleName ) , $invalidModules ) )
2156             {
2157                 foreach ( array ( MB_EDITVIEW , MB_DETAILVIEW ) as $view )
2158                 {
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 ) ;
2163                 }
2164             }
2165         }
2166
2167         }
2168
2169         ///////////////////
2170         //********** DISABLE/ENABLE FUNCTIONS
2171         ///////////////////
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
2176                 $current_step = 0;
2177                 $tasks = array(
2178                                                                 'enable_copy',
2179                                                                 'enable_dashlets',
2180                                                                 'enable_relationships',
2181                                         'enable_extensions',
2182                                 'enable_global_search',
2183                                         'enable_manifest_logichooks',
2184                                                                 'reset_opcodes',
2185                 );
2186                 $total_steps += count($tasks);
2187                 if(file_exists($this->base_dir . '/manifest.php')){
2188                                 if(!$this->silent){
2189                                         $current_step++;
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">';
2192                                 }
2193
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];
2201                                                         }else{
2202                                                                 $errors[] = 'No Upgrade Path Found in manifest.';
2203                                                                 $this->abort($errors);
2204                                                         }//fi
2205                                                 }//fi
2206                                         }//fi
2207                                 }//fi
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'];
2214                                         }
2215                                 }
2216                                 if(!$this->silent){
2217                                         $current_step++;
2218                                         update_progress_bar('install', $current_step, $total_steps);
2219                                 }
2220
2221                                 foreach($tasks as $task){
2222                                         $this->$task();
2223                                         if(!$this->silent){
2224                                                 $current_step++;
2225                                                 update_progress_bar('install', $current_step, $total_steps);
2226                                         }
2227                                 }
2228
2229                                 if(!$this->silent){
2230                                         $current_step++;
2231                                         update_progress_bar('install', $current_step, $total_steps);
2232                                         echo '</div>';
2233                                 }
2234                                 UpdateSystemTabs('Add',$installed_modules);
2235                                 $GLOBALS['log']->debug('Complete');
2236
2237                 }else{
2238                         die("No \$installdefs Defined In $this->base_dir/manifest.php");
2239                 }
2240
2241         }
2242         function disable($base_dir){
2243                 global $app_strings;
2244                 $total_steps = 3; //min steps with no tasks
2245                 $current_step = 0;
2246                 $this->base_dir = $base_dir;
2247                 $tasks = array(
2248                                                         'disable_copy',
2249                                                         'disable_dashlets',
2250                                                         'disable_relationships',
2251                                     'disable_extensions',
2252                             'disable_global_search',
2253                                                         'disable_manifest_logichooks',
2254                                                         'reset_opcodes',
2255                                                         );
2256                 $total_steps += count($tasks); //now the real number of steps
2257                 if(file_exists($this->base_dir . '/manifest.php')){
2258                                 if(!$this->silent){
2259                                         $current_step++;
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">';
2262                                 }
2263
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'];
2271                                         }
2272                                 }
2273                                 if(!$this->silent){
2274                                         $current_step++;
2275                                         update_progress_bar('install', $current_step, $total_steps);
2276                                 }
2277                                 foreach($tasks as $task){
2278                                         $this->$task();
2279                                         if(!$this->silent){
2280                                                 $current_step++;
2281                                                 update_progress_bar('install', $current_step, $total_steps);
2282                                         }
2283                                 }
2284                                 if(!$this->silent){
2285                                         $current_step++;
2286                                         update_progress_bar('install', $current_step, $total_steps);
2287                                         echo '</div>';
2288                                 }
2289                         UpdateSystemTabs('Restore',$installed_modules);
2290
2291                 }else{
2292                         die("No manifest.php Defined In $this->base_dir/manifest.php");
2293                 }
2294         }
2295
2296         function enable_vardef($to_module)
2297         {
2298             $this->enableExt("vardefs", "Vardefs", $to_module);
2299         }
2300
2301         function enable_layoutdef($to_module)
2302         {
2303             $this->enableExt("layoutdefs", "Layoutdefs", $to_module);
2304         }
2305
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']);
2312
2313                                 $save_table_dictionary  = true;
2314                                 $str .= "include_once('metadata/$filename');\n";
2315                                 if (empty($relationship['module']))
2316                                     continue;
2317
2318                                 if(!empty($relationship['module_vardefs'])){
2319                                         $this->enable_vardef($relationship['module']);
2320                                 }
2321                                 if(!empty($relationship['module_layoutdefs'])){
2322                                         $this->enable_layoutdef($relationship['module']);
2323                                 }
2324                         }
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);
2330                                 }
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();
2334                         }
2335                 }
2336         }
2337
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))
2344                         continue;
2345
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';
2351                                 }
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');
2355                                 }
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';
2361                     }
2362                                 }
2363
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');
2367                                 }
2368
2369                         }
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");
2373                         }
2374                         $this->rebuild_tabledictionary();
2375                         $this->rebuild_vardefs();
2376                         $this->rebuild_layoutdefs();
2377                 }
2378         }
2379
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))
2388                                 {
2389                                         rename($disabled_path,  $path);
2390                                 }
2391                         }
2392                         include('modules/Administration/RebuildDashlets.php');
2393
2394                 }
2395         }
2396
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))
2404                                                 {
2405                                                         mkdir_recursive('custom/modules/Home/'.DISABLED_PATH.'Dashlets/', true);
2406                                                         rename( $path, $disabled_path);
2407                                                 }
2408                                         }
2409                                         include('modules/Administration/RebuildDashlets.php');
2410                                 }
2411         }
2412
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'] );
2421
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']);
2430                                                 /*}else{
2431                                                         //since they are not equal then we need to prompt the user
2432                                                 }*/
2433                                         }//fi
2434                                 }//rof
2435                         }//fi
2436                 }//fi
2437         }
2438
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.
2443
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']);
2457
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']);
2463                                                 /*}else{
2464                                                         //since they are not equal then we need to prompt the user
2465                                                 }*/
2466                                         }//fi
2467                                 }//rof
2468                         }//fi
2469                 }//fi
2470         }
2471
2472         public function reset_opcodes()
2473     {
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
2477          */
2478         if(function_exists('sugar_clean_opcodes')){
2479             sugar_clean_opcodes();
2480         }
2481     }
2482
2483     /**
2484      * BC implementation to provide specific calls to extensions
2485      */
2486     public function __call($name, $args)
2487     {
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]) {
2493                 case 'enable':
2494                     return $this->enableExt($ext['section'], $ext['extdir']);
2495                 case 'disable':
2496                     return $this->disableExt($ext['section'], $ext['extdir']);
2497                 case 'install':
2498                     return $this->installExt($ext['section'], $ext['extdir']);
2499                 case 'uninstall':
2500                     return $this->uninstallExt($ext['section'], $ext['extdir']);
2501                 case 'rebuild':
2502                     return $this->rebuildExt($ext['extdir'], $ext['file']);
2503             }
2504         }
2505         sugar_die("Unknown method ModuleInstaller::$name called");
2506     }
2507
2508 }
2509
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))
2515         {
2516             global $moduleList;
2517             switch ($action)
2518             {
2519                 case 'Restore' :
2520                     $currentTabs = $controller->get_system_tabs();
2521                     foreach ($installed_modules as $module)
2522                     {
2523                         if(in_array($module, $currentTabs)){
2524                             unset($currentTabs[$module]);
2525                         }
2526                     }
2527                     $controller->set_system_tabs($currentTabs);;
2528                     break;
2529                 case 'Add' :
2530                     $currentTabs = $controller->get_system_tabs();
2531                     foreach ($installed_modules as $module)
2532                     {
2533                         if(!in_array($module, $currentTabs)){
2534                             $currentTabs[$module] = $module;
2535                         }
2536                     }
2537                     $controller->set_system_tabs($currentTabs);
2538                 default:
2539                     break;
2540             }
2541     }
2542
2543 }