Issues (4069)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

ModuleInstall/ModuleInstaller.php (30 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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
 * SuiteCRM is an extension to SugarCRM Community Edition developed by Salesagility Ltd.
8
 * Copyright (C) 2011 - 2014 Salesagility Ltd.
9
 *
10
 * This program is free software; you can redistribute it and/or modify it under
11
 * the terms of the GNU Affero General Public License version 3 as published by the
12
 * Free Software Foundation with the addition of the following permission added
13
 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
14
 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
15
 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
16
 *
17
 * This program is distributed in the hope that it will be useful, but WITHOUT
18
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19
 * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
20
 * details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License along with
23
 * this program; if not, see http://www.gnu.org/licenses or write to the Free
24
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25
 * 02110-1301 USA.
26
 *
27
 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
28
 * SW2-130, Cupertino, CA 95014, USA. or at email address [email protected].
29
 *
30
 * The interactive user interfaces in modified source and object code versions
31
 * of this program must display Appropriate Legal Notices, as required under
32
 * Section 5 of the GNU Affero General Public License version 3.
33
 *
34
 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
35
 * these Appropriate Legal Notices must retain the display of the "Powered by
36
 * SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
37
 * reasonably feasible for  technical reasons, the Appropriate Legal Notices must
38
 * display the words  "Powered by SugarCRM" and "Supercharged by SuiteCRM".
39
 ********************************************************************************/
40
41
42
/*
43
 * ModuleInstaller - takes an installation package from files in the custom/Extension/X directories, and moves them into custom/X to install them.
44
 * If a directory has multiple files they are concatenated together.
45
 * Relevant directories (X) are Layoutdefs, Vardefs, Include (bean stuff), Language, TableDictionary (relationships)
46
 *
47
 * Installation steps that involve more than just copying files:
48
 * 1. installing custom fields - calls bean->custom_fields->addField
49
 * 2. installing relationships - calls createTableParams to build the relationship table, and createRelationshipMeta to add the relationship to the relationship table
50
 * 3. rebuilding the relationships - at almost the last step in install(), calls modules/Administration/RebuildRelationship.php
51
 * 4. repair indices - uses "modules/Administration/RepairIndex.php";
52
 */
53
54
55
56
require_once('include/utils/progress_bar_utils.php');
57
58
require_once('ModuleInstall/ModuleScanner.php');
59
define('DISABLED_PATH', 'Disabled');
60
61
class ModuleInstaller{
62
	var $modules = array();
63
	var $silent = false;
64
	var $base_dir  = '';
65
	var $modulesInPackage = array();
66
	public $disabled_path = DISABLED_PATH;
67
    public $id_name;
68
	function __construct(){
69
		$this->ms = new ModuleScanner();
70
		$this->modules = get_module_dir_list();
71
		$this->db = DBManagerFactory::getInstance();
72
        include("ModuleInstall/extensions.php");
73
        $this->extensions = $extensions;
74
	}
75
76
    /**
77
     * @deprecated deprecated since version 7.6, PHP4 Style Constructors are deprecated and will be remove in 7.8, please update your code, use __construct instead
78
     */
79
    function ModuleInstaller(){
80
        $deprecatedMessage = 'PHP4 Style Constructors are deprecated and will be remove in 7.8, please update your code';
81
        if(isset($GLOBALS['log'])) {
82
            $GLOBALS['log']->deprecated($deprecatedMessage);
83
        }
84
        else {
85
            trigger_error($deprecatedMessage, E_USER_DEPRECATED);
86
        }
87
        self::__construct();
88
    }
89
90
91
   /*
92
    * 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
93
    * 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
94
    * and moduleList - and then calls ModuleInstaller->merge_files('Ext/Include', 'modules.ext.php', '', true) to merge the individual module files into a combined file
95
    * /custom/Extension/application/Ext/Include/modules.ext.php (which now contains a list of all $beanList, $beanFiles and $moduleList for all extension modules) -
96
    * this file modules.ext.php is included at the end of modules.php.
97
    *
98
    * 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.
99
    */
100
	function install($base_dir, $is_upgrade = false, $previous_version = ''){
101
		if(defined('TEMPLATE_URL'))SugarTemplateUtilities::disableCache();
102
        if ((defined('MODULE_INSTALLER_PACKAGE_SCAN') && MODULE_INSTALLER_PACKAGE_SCAN)
103
            || !empty($GLOBALS['sugar_config']['moduleInstaller']['packageScan'])) {
104
			$this->ms->scanPackage($base_dir);
105
			if($this->ms->hasIssues()){
106
				$this->ms->displayIssues();
107
				sugar_cleanup(true);
108
			}
109
		}
110
111
        // workaround for bug 45812 - refresh vardefs cache before unpacking to avoid partial vardefs in cache
112
        global $beanList;
113
        foreach ($this->modules as $module_name) {
114
            if (!empty($beanList[$module_name])) {
115
                $objectName = BeanFactory::getObjectName($module_name);
116
                VardefManager::loadVardef($module_name, $objectName);
117
            }
118
        }
119
120
		global $app_strings, $mod_strings;
121
		$this->base_dir = $base_dir;
122
		$total_steps = 5; //minimum number of steps with no tasks
123
		$current_step = 0;
124
		$tasks = array(
125
			'pre_execute',
126
			'install_copy',
127
		    'install_extensions',
128
			'install_images',
129
			'install_dcactions',
130
			'install_dashlets',
131
			'install_connectors',
132
			'install_layoutfields',
133
			'install_relationships',
134
            'enable_manifest_logichooks',
135
			'post_execute',
136
			'reset_opcodes',
137
		);
138
139
		$total_steps += count($tasks);
140
		if(file_exists($this->base_dir . '/manifest.php')){
141
				if(!$this->silent){
142
					$current_step++;
143
					display_progress_bar('install', $current_step, $total_steps);
144
					echo '<div id ="displayLoglink" ><a href="#" onclick="document.getElementById(\'displayLog\').style.display=\'\'">'
145
						.$app_strings['LBL_DISPLAY_LOG'].'</a> </div><div id="displayLog" style="display:none">';
146
				}
147
148
				include($this->base_dir . '/manifest.php');
149
				if($is_upgrade && !empty($previous_version)){
150
					//check if the upgrade path exists
151
					if(!empty($upgrade_manifest)){
0 ignored issues
show
The variable $upgrade_manifest seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
152
						if(!empty($upgrade_manifest['upgrade_paths'])){
153
							if(!empty($upgrade_manifest['upgrade_paths'][$previous_version])){
154
								$installdefs = 	$upgrade_manifest['upgrade_paths'][$previous_version];
155
							}else{
156
								$errors[] = 'No Upgrade Path Found in manifest.';
157
								$this->abort($errors);
158
							}//fi
159
						}//fi
160
					}//fi
161
				}//fi
162
				$this->id_name = $installdefs['id'];
163
				$this->installdefs = $installdefs;
164
				if(!$this->silent){
165
					$current_step++;
166
					update_progress_bar('install', $current_step, $total_steps);
167
				}
168
169
				foreach($tasks as $task){
170
					$this->$task();
171
					if(!$this->silent){
172
						$current_step++;
173
						update_progress_bar('install', $current_step, $total_steps);
174
					}
175
				}
176
				$this->install_beans($this->installed_modules);
177
				if(!$this->silent){
178
					$current_step++;
179
					update_progress_bar('install', $total_steps, $total_steps);
180
				}
181
				if(isset($installdefs['custom_fields'])){
182
					$this->log(translate('LBL_MI_IN_CUSTOMFIELD'));
183
					$this->install_custom_fields($installdefs['custom_fields']);
184
				}
185
				if(!$this->silent){
186
					$current_step++;
187
					update_progress_bar('install', $current_step, $total_steps);
188
					echo '</div>';
189
				}
190
				if(!$this->silent){
191
					$current_step++;
192
					update_progress_bar('install', $current_step, $total_steps);
193
					echo '</div>';
194
				}
195
				$selectedActions = array(
196
			'clearTpls',
197
			'clearJsFiles',
198
			'clearDashlets',
199
			'clearVardefs',
200
			'clearJsLangFiles',
201
			'rebuildAuditTables',
202
			'repairDatabase',
203
		);
204
				VardefManager::clearVardef();
205
				global $beanList, $beanFiles, $moduleList;
206
				if (file_exists('custom/application/Ext/Include/modules.ext.php'))
207
				{
208
				    include('custom/application/Ext/Include/modules.ext.php');
209
				}
210
				require_once("modules/Administration/upgrade_custom_relationships.php");
211
            	upgrade_custom_relationships($this->installed_modules);
212
				$this->rebuild_all(true);
213
				require_once('modules/Administration/QuickRepairAndRebuild.php');
214
				$rac = new RepairAndClear();
215
				$rac->repairAndClearAll($selectedActions, $this->installed_modules,true, false);
216
				$this->rebuild_relationships();
217
				UpdateSystemTabs('Add',$this->tab_modules);
218
                //Clear out all the langauge cache files.
219
                clearAllJsAndJsLangFilesWithoutOutput();
220
                $cache_key = 'app_list_strings.'.$GLOBALS['current_language'];
221
                sugar_cache_clear($cache_key );
222
                sugar_cache_reset();
223
224
				//clear the unified_search_module.php file
225
	            require_once('modules/Home/UnifiedSearchAdvanced.php');
226
	            UnifiedSearchAdvanced::unlinkUnifiedSearchModulesFile();
227
228
				$this->log('<br><b>' . translate('LBL_MI_COMPLETE') . '</b>');
229
		}else{
230
			die("No \$installdefs Defined In $this->base_dir/manifest.php");
231
		}
232
233
	}
234
235
	function install_user_prefs($module, $hide_from_user=false){
236
		UserPreference::updateAllUserPrefs('display_tabs', $module, '', true, !$hide_from_user);
237
		UserPreference::updateAllUserPrefs('hide_tabs', $module, '', true, $hide_from_user);
238
		UserPreference::updateAllUserPrefs('remove_tabs', $module, '', true, $hide_from_user);
239
	}
240
	function uninstall_user_prefs($module){
241
		UserPreference::updateAllUserPrefs('display_tabs', $module, '', true, true);
242
		UserPreference::updateAllUserPrefs('hide_tabs', $module, '', true, true);
243
		UserPreference::updateAllUserPrefs('remove_tabs', $module, '', true, true);
244
	}
245
246
	function pre_execute(){
247
		require_once($this->base_dir . '/manifest.php');
248
		if(isset($this->installdefs['pre_execute']) && is_array($this->installdefs['pre_execute'])){
249
			foreach($this->installdefs['pre_execute'] as $includefile){
250
				require_once(str_replace('<basepath>', $this->base_dir, $includefile));
251
			}
252
		}
253
	}
254
255
	function post_execute(){
256
		require_once($this->base_dir . '/manifest.php');
257
		if(isset($this->installdefs['post_execute']) && is_array($this->installdefs['post_execute'])){
258
			foreach($this->installdefs['post_execute'] as $includefile){
259
				require_once(str_replace('<basepath>', $this->base_dir, $includefile));
260
			}
261
		}
262
	}
263
264
	function pre_uninstall(){
265
		require_once($this->base_dir . '/manifest.php');
266
		if(isset($this->installdefs['pre_uninstall']) && is_array($this->installdefs['pre_uninstall'])){
267
			foreach($this->installdefs['pre_uninstall'] as $includefile){
268
				require_once(str_replace('<basepath>', $this->base_dir, $includefile));
269
			}
270
		}
271
	}
272
273
	function post_uninstall(){
274
		require_once($this->base_dir . '/manifest.php');
275
		if(isset($this->installdefs['post_uninstall']) && is_array($this->installdefs['post_uninstall'])){
276
			foreach($this->installdefs['post_uninstall'] as $includefile){
277
				require_once(str_replace('<basepath>', $this->base_dir, $includefile));
278
			}
279
		}
280
	}
281
282
	/*
283
     * 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
284
     * (specified as from and to in the manifest), replacing <basepath> by the base_dir value passed in to install.
285
     */
286
	function install_copy(){
287
		if(isset($this->installdefs['copy'])){
288
			/* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:22:11 PM */
289
			$backup_path = clean_path( remove_file_extension(urldecode($_REQUEST['install_file']))."-restore" );
290
			/* END - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
291
			foreach($this->installdefs['copy'] as $cp){
292
				$GLOBALS['log']->debug("Copying ..." . $cp['from'].  " to " .$cp['to'] );
293
				/* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:22:11 PM */
294
				//$this->copy_path($cp['from'], $cp['to']);
295
				$this->copy_path($cp['from'], $cp['to'], $backup_path);
296
				/* END - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
297
			}
298
			//here we should get the module list again as we could have copied something to the modules dir
299
			$this->modules = get_module_dir_list();
300
		}
301
	}
302
	function uninstall_copy(){
303
		if(!empty($this->installdefs['copy'])){
304
					foreach($this->installdefs['copy'] as $cp){
305
						$cp['to'] = clean_path(str_replace('<basepath>', $this->base_dir, $cp['to']));
306
						$cp['from'] = clean_path(str_replace('<basepath>', $this->base_dir, $cp['from']));
307
						$GLOBALS['log']->debug('Unlink ' . $cp['to']);
308
				/* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:22:11 PM */
309
						//rmdir_recursive($cp['to']);
310
311
						$backup_path = clean_path( remove_file_extension(urldecode(hashToFile($_REQUEST['install_file'])))."-restore/".$cp['to'] );
312
						$this->uninstall_new_files($cp, $backup_path);
313
						$this->copy_path($backup_path, $cp['to'], $backup_path, true);
314
				/* END - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
315
					}
316
					$backup_path = clean_path( remove_file_extension(urldecode(hashToFile($_REQUEST['install_file'])))."-restore");
317
					if(file_exists($backup_path))
318
						rmdir_recursive($backup_path);
319
				}
320
	}
321
322
323
	/**
324
	 * Removes any files that were added by the loaded module. If the files already existed prior to install
325
	 * it will be handled by copy_path with the uninstall parameter.
326
	 *
327
	 */
328
	function uninstall_new_files($cp, $backup_path){
329
		$zip_files = $this->dir_get_files($cp['from'],$cp['from']);
330
		$backup_files = $this->dir_get_files($backup_path, $backup_path);
331
		foreach($zip_files as $k=>$v){
332
			//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
333
			if(!isset($backup_files[$k])){
334
				$to = $cp['to'] . $k;
335
				//if it's not a sugar file then we remove it otherwise we can't restor it
336
				if(!$this->ms->sugarFileExists($to)){
337
					$GLOBALS['log']->debug('ModuleInstaller[uninstall_new_file] deleting file ' . $to);
338
					if(file_exists($to)) {
339
					    unlink($to);
340
					}
341
				}else{
342
					$GLOBALS['log']->fatal('ModuleInstaller[uninstall_new_file] Could not remove file ' . $to . ' as no backup file was found to restore to');
343
				}
344
			}
345
		}
346
		//lets check if the directory is empty if it is we will delete it as well
347
		$files_remaining = $this->dir_file_count($cp['to']);
348
		if(file_exists($cp['to']) && $files_remaining == 0){
349
			$GLOBALS['log']->debug('ModuleInstaller[uninstall_new_file] deleting directory ' . $cp['to']);
350
			rmdir_recursive($cp['to']);
351
		}
352
353
	}
354
355
	/**
356
	 * Get directory where module's extensions go
357
	 * @param string $module Module name
358
	 */
359
    public function getExtDir($module)
360
    {
361
	    if($module == 'application') {
362
            return "custom/Extension/application/Ext";
363
        } else {
364
			return "custom/Extension/modules/$module/Ext";
365
        }
366
    }
367
368
	/**
369
	 * Install file(s) into Ext/ part
370
	 * @param string $section Name of the install file section
371
	 * @param string $extname Name in Ext directory
372
	 * @param string $module This extension belongs to a specific module
373
	 */
374
	public function installExt($section, $extname, $module = '')
375
	{
376
        if(isset($this->installdefs[$section])){
377
			$this->log(sprintf(translate("LBL_MI_IN_EXT"), $section));
378
			foreach($this->installdefs[$section] as $item){
379
			    if(isset($item['from'])) {
380
				    $from = str_replace('<basepath>', $this->base_dir, $item['from']);
381
			    } else {
382
			        $from = '';
383
			    }
384
				if(!empty($module)) {
385
				    $item['to_module'] = $module;
386
				}
387
				$GLOBALS['log']->debug("Installing section $section from $from for " .$item['to_module'] );
388
                if($item['to_module'] == 'application') {
389
                    $path = "custom/Extension/application/Ext/$extname";
390
                } else {
391
				    $path = "custom/Extension/modules/{$item['to_module']}/Ext/$extname";
392
                }
393
				if(!file_exists($path)){
394
					mkdir_recursive($path, true);
395
				}
396
				if(isset($item["name"])) {
397
				    $target = $item["name"];
398
				} else if (!empty($from)){
399
                    $target = basename($from, ".php");
400
                } else {
401
				    $target = $this->id_name;
402
				}
403
				if(!empty($from)) {
404
				    copy_recursive($from , "$path/$target.php");
405
				}
406
			}
407
		}
408
	}
409
410
	/**
411
	 * Uninstall file(s) into Ext/ part
412
	 * @param string $section Name of the install file section
413
	 * @param string $extname Name in Ext directory
414
	 * @param string $module This extension belongs to a specific module
415
	 */
416
	public function uninstallExt($section, $extname, $module = '')
417
	{
418
        if(isset($this->installdefs[$section])){
419
			$this->log(sprintf(translate("LBL_MI_UN_EXT"), $section));
420
			foreach($this->installdefs[$section] as $item){
421
			    if(isset($item['from'])) {
422
				    $from = str_replace('<basepath>', $this->base_dir, $item['from']);
423
			    } else {
424
			        $from = '';
425
			    }
426
			    if(!empty($module)) {
427
				    $item['to_module'] = $module;
428
				}
429
				$GLOBALS['log']->debug("Uninstalling section $section from $from for " .$item['to_module'] );
430
                if($item['to_module'] == 'application') {
431
                    $path = "custom/Extension/application/Ext/$extname";
432
                } else {
433
				    $path = "custom/Extension/modules/{$item['to_module']}/Ext/$extname";
434
                }
435
				if(isset($item["name"])) {
436
				    $target = $item["name"];
437
				} else if (!empty($from)){
438
                    $target = basename($from, ".php");
439
                } else {
440
				    $target = $this->id_name;
441
				}
442
				$disabled_path = $path.'/'.DISABLED_PATH;
443
				if (file_exists("$path/$target.php")) {
444
				    rmdir_recursive("$path/$target.php");
445
                } else if (file_exists("$disabled_path/$target.php")) {
446
                    rmdir_recursive("$disabled_path/$target.php");
447
				} else if (!empty($from) && file_exists($path . '/'. basename($from))) {
448
				    rmdir_recursive( $path . '/'. basename($from));
449
                } else if (!empty($from) && file_exists($disabled_path . '/'. basename($from))) {
450
					rmdir_recursive( $disabled_path . '/'. basename($from));
451
				}
452
		    }
453
	    }
454
	}
455
456
	/**
457
     * Rebuild generic extension
458
     * @param string $ext Extension directory
459
     * @param string $filename Target filename
460
     */
461
	public function rebuildExt($ext, $filename)
462
	{
463
            $this->log(translate('LBL_MI_REBUILDING') . " $ext...");
464
			$this->merge_files("Ext/$ext/", $filename);
465
	}
466
467
	/**
468
	 * Disable generic extension
469
	 * @param string $section Install file section name
470
	 * @param string $extname Extension directory
471
 	 * @param string $module This extension belongs to a specific module
472
	 */
473
	public function disableExt($section, $extname, $module = '')
474
	{
475
		if(isset($this->installdefs[$section])) {
476
			foreach($this->installdefs[$section] as $item) {
477
			    if(isset($item['from'])) {
478
				    $from = str_replace('<basepath>', $this->base_dir, $item['from']);
479
			    } else {
480
			        $from = '';
481
			    }
482
				if(!empty($module)) {
483
				    $item['to_module'] = $module;
484
				}
485
				$GLOBALS['log']->debug("Disabling $extname ... from $from for " .$item['to_module']);
486
                if($item['to_module'] == 'application') {
487
                    $path = "custom/Extension/application/Ext/$extname";
488
                } else {
489
				    $path = "custom/Extension/modules/{$item['to_module']}/Ext/$extname";
490
                }
491
				if(isset($item["name"])) {
492
				    $target = $item["name"];
493
				} else if (!empty($from)){
494
                    $target = basename($from, ".php");
495
                }else {
496
				    $target = $this->id_name;
497
				}
498
				$disabled_path = $path.'/'.DISABLED_PATH;
499
                if (file_exists("$path/$target.php")) {
500
					mkdir_recursive($disabled_path, true);
501
					rename("$path/$target.php", "$disabled_path/$target.php");
502
				} else if (!empty($from) && file_exists($path . '/'. basename($from))) {
503
					mkdir_recursive($disabled_path, true);
504
				    rename( $path . '/'. basename($from), $disabled_path.'/'. basename($from));
505
				}
506
			}
507
		}
508
	}
509
510
	/**
511
	 * Enable generic extension
512
	 * @param string $section Install file section name
513
	 * @param string $extname Extension directory
514
 	 * @param string $module This extension belongs to a specific module
515
	 */
516
	public function enableExt($section, $extname, $module = '')
517
	{
518
		if(isset($this->installdefs[$section])) {
519
			foreach($this->installdefs[$section] as $item) {
520
			    if(isset($item['from'])) {
521
				    $from = str_replace('<basepath>', $this->base_dir, $item['from']);
522
			    } else {
523
			        $from = '';
524
			    }
525
			    if(!empty($module)) {
526
				    $item['to_module'] = $module;
527
				}
528
				$GLOBALS['log']->debug("Enabling $extname ... from $from for " .$item['to_module']);
529
530
                if($item['to_module'] == 'application') {
531
                    $path = "custom/Extension/application/Ext/$extname";
532
                } else {
533
				    $path = "custom/Extension/modules/{$item['to_module']}/Ext/$extname";
534
                }
535
				if(isset($item["name"])) {
536
				    $target = $item["name"];
537
				} else if (!empty($from)){
538
                    $target = basename($from, ".php");
539
                } else {
540
				    $target = $this->id_name;
541
				}
542
				if(!file_exists($path)) {
543
				    mkdir_recursive($path, true);
544
				}
545
                $disabled_path = $path.'/'.DISABLED_PATH;
546
				if (file_exists("$disabled_path/$target.php")) {
547
					rename("$disabled_path/$target.php",  "$path/$target.php");
548
				}
549
				if (!empty($from) && file_exists($disabled_path . '/'. basename($from))) {
550
					rename($disabled_path.'/'. basename($from),  $path . '/'. basename($from));
551
				}
552
			}
553
		}
554
    }
555
556
    /**
557
     * Method removes module from global search configurations
558
     *
559
     * return bool
560
     */
561
    public function uninstall_global_search()
562
    {
563
        if (empty($this->installdefs['beans']))
564
        {
565
            return true;
566
        }
567
568
        if (is_file('custom/modules/unified_search_modules_display.php') == false)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
569
        {
570
            return true;
571
        }
572
573
        $user = new User();
574
        $users = get_user_array();
575
        $unified_search_modules_display = array();
576
        require('custom/modules/unified_search_modules_display.php');
577
578
        foreach($this->installdefs['beans'] as $beanDefs)
579
        {
580
            if (array_key_exists($beanDefs['module'], $unified_search_modules_display) == false)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
581
            {
582
                continue;
583
            }
584
            unset($unified_search_modules_display[$beanDefs['module']]);
585
            foreach($users as $userId => $userName)
586
            {
587
                if (empty($userId))
588
                {
589
                    continue;
590
                }
591
                $user->retrieve($userId);
592
                $prefs = $user->getPreference('globalSearch', 'search');
593
                if (array_key_exists($beanDefs['module'], $prefs) == false)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
594
                {
595
                    continue;
596
                }
597
                unset($prefs[$beanDefs['module']]);
598
                $user->setPreference('globalSearch', $prefs, 0, 'search');
599
                $user->savePreferencesToDB();
600
            }
601
        }
602
603
        if (write_array_to_file("unified_search_modules_display", $unified_search_modules_display, 'custom/modules/unified_search_modules_display.php') == false)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
604
        {
605
            global $app_strings;
606
            $msg = string_format($app_strings['ERR_FILE_WRITE'], array('custom/modules/unified_search_modules_display.php'));
607
            $GLOBALS['log']->error($msg);
608
            throw new Exception($msg);
609
            return false;
0 ignored issues
show
return false; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
610
        }
611
        return true;
612
    }
613
614
    /**
615
     * Method enables module in global search configurations by disabled_module_visible key
616
     *
617
     * return bool
618
     */
619
    public function enable_global_search()
620
    {
621
        if (empty($this->installdefs['beans']))
622
        {
623
            return true;
624
        }
625
626
        if (is_file('custom/modules/unified_search_modules_display.php') == false)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
627
        {
628
            return true;
629
        }
630
631
        $unified_search_modules_display = array();
632
        require('custom/modules/unified_search_modules_display.php');
633
634
        foreach($this->installdefs['beans'] as $beanDefs)
635
        {
636
            if (array_key_exists($beanDefs['module'], $unified_search_modules_display) == false)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
637
            {
638
                continue;
639
            }
640
            if (isset($unified_search_modules_display[$beanDefs['module']]['disabled_module_visible']) == false)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
641
            {
642
                continue;
643
            }
644
            $unified_search_modules_display[$beanDefs['module']]['visible'] = $unified_search_modules_display[$beanDefs['module']]['disabled_module_visible'];
645
            unset($unified_search_modules_display[$beanDefs['module']]['disabled_module_visible']);
646
        }
647
648
        if (write_array_to_file("unified_search_modules_display", $unified_search_modules_display, 'custom/modules/unified_search_modules_display.php') == false)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
649
        {
650
            global $app_strings;
651
            $msg = string_format($app_strings['ERR_FILE_WRITE'], array('custom/modules/unified_search_modules_display.php'));
652
            $GLOBALS['log']->error($msg);
653
            throw new Exception($msg);
654
            return false;
0 ignored issues
show
return false; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
655
        }
656
        return true;
657
    }
658
659
    /**
660
     * Method disables module in global search configurations by disabled_module_visible key
661
     *
662
     * return bool
663
     */
664
    public function disable_global_search()
665
    {
666
        if (empty($this->installdefs['beans']))
667
        {
668
            return true;
669
        }
670
671
        if (is_file('custom/modules/unified_search_modules_display.php') == false)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
672
        {
673
            return true;
674
        }
675
676
        $unified_search_modules_display = array();
677
        require('custom/modules/unified_search_modules_display.php');
678
679
        foreach($this->installdefs['beans'] as $beanDefs)
680
        {
681
            if (array_key_exists($beanDefs['module'], $unified_search_modules_display) == false)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
682
            {
683
                continue;
684
            }
685
            if (isset($unified_search_modules_display[$beanDefs['module']]['visible']) == false)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
686
            {
687
                continue;
688
            }
689
            $unified_search_modules_display[$beanDefs['module']]['disabled_module_visible'] = $unified_search_modules_display[$beanDefs['module']]['visible'];
690
            $unified_search_modules_display[$beanDefs['module']]['visible'] = false;
691
        }
692
693
        if (write_array_to_file("unified_search_modules_display", $unified_search_modules_display, 'custom/modules/unified_search_modules_display.php') == false)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
694
        {
695
            global $app_strings;
696
            $msg = string_format($app_strings['ERR_FILE_WRITE'], array('custom/modules/unified_search_modules_display.php'));
697
            $GLOBALS['log']->error($msg);
698
            throw new Exception($msg);
699
            return false;
0 ignored issues
show
return false; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
700
        }
701
        return true;
702
    }
703
704
    public function install_extensions()
705
	{
706
	    foreach($this->extensions as $extname => $ext) {
707
	        $install = "install_$extname";
708
	        if(method_exists($this, $install)) {
709
	            // non-standard function
710
                $this->$install();
711
	        } else {
712
	            if(!empty($ext["section"])) {
713
	                $module = isset($ext['module'])?$ext['module']:'';
714
	                $this->installExt($ext["section"], $ext["extdir"], $module);
715
	            }
716
	        }
717
	    }
718
	    $this->rebuild_extensions();
719
	}
720
721
	public function uninstall_extensions()
722
	{
723
	    foreach($this->extensions as $extname => $ext) {
724
	        $func = "uninstall_$extname";
725
	        if(method_exists($this, $func)) {
726
	            // non-standard function
727
                $this->$func();
728
	        } else {
729
	            if(!empty($ext["section"])) {
730
	                $module = isset($ext['module'])?$ext['module']:'';
731
	                $this->uninstallExt($ext["section"], $ext["extdir"], $module);
732
	            }
733
	        }
734
	    }
735
	    $this->rebuild_extensions();
736
	}
737
738
	public function rebuild_extensions()
739
	{
740
	    foreach($this->extensions as $extname => $ext) {
741
	        $func = "rebuild_$extname";
742
	        if(method_exists($this, $func)) {
743
	            // non-standard function
744
                $this->$func();
745
	        } else {
746
    	        $this->rebuildExt($ext["extdir"], $ext["file"]);
747
	        }
748
	    }
749
	}
750
751
	public function disable_extensions()
752
	{
753
	    foreach($this->extensions as $extname => $ext) {
754
	        $func = "disable_$extname";
755
	        if(method_exists($this, $func)) {
756
	            // non-standard install
757
                $this->$func();
758
	        } else {
759
	            if(!empty($ext["section"])) {
760
	                $module = isset($ext['module'])?$ext['module']:'';
761
	                $this->disableExt($ext["section"], $ext["extdir"], $module);
762
	            }
763
	        }
764
	    }
765
	    $this->rebuild_extensions();
766
	}
767
768
	public function enable_extensions()
769
	{
770
	    foreach($this->extensions as $extname => $ext) {
771
	        $func = "enable_$extname";
772
	        if(method_exists($this, $func)) {
773
	            // non-standard install
774
                $this->$func();
775
	        } else {
776
	            if(!empty($ext["section"])) {
777
	                $module = isset($ext['module'])?$ext['module']:'';
778
	                $this->enableExt($ext["section"], $ext["extdir"], $module);
779
	            }
780
	        }
781
	    }
782
	    $this->rebuild_extensions();
783
	}
784
785
	function install_dashlets()
786
	{
787
        if(isset($this->installdefs['dashlets'])){
788
			foreach($this->installdefs['dashlets'] as $cp){
789
				$this->log(translate('LBL_MI_IN_DASHLETS') . $cp['name']);
790
				$cp['from'] = str_replace('<basepath>', $this->base_dir, $cp['from']);
791
				$path = 'custom/modules/Home/Dashlets/' . $cp['name'] . '/';
792
				$GLOBALS['log']->debug("Installing Dashlet " . $cp['name'] . "..." . $cp['from'] );
793
				if(!file_exists($path)){
794
					mkdir_recursive($path, true);
795
				}
796
				copy_recursive($cp['from'] , $path);
797
			}
798
			include('modules/Administration/RebuildDashlets.php');
799
800
		}
801
	}
802
803
	function uninstall_dashlets(){
804
        if(isset($this->installdefs['dashlets'])){
805
			foreach($this->installdefs['dashlets'] as $cp){
806
				$this->log(translate('LBL_MI_UN_DASHLETS') . $cp['name']);
807
				$path = 'custom/modules/Home/Dashlets/' . $cp['name'];
808
				$GLOBALS['log']->debug('Unlink ' .$path);
809
				if (file_exists($path))
810
					rmdir_recursive($path);
811
			}
812
			include('modules/Administration/RebuildDashlets.php');
813
		}
814
	}
815
816
817
	function install_images(){
818
        if(isset($this->installdefs['image_dir'])){
819
			$this->log( translate('LBL_MI_IN_IMAGES') );
820
			$this->copy_path($this->installdefs['image_dir'] , 'custom/themes');
821
822
		}
823
	}
824
825
	function install_dcactions(){
826
		if(isset($this->installdefs['dcaction'])){
827
			$this->log(translate('LBL_MI_IN_MENUS'));
828
			foreach($this->installdefs['dcaction'] as $action){
829
				$action['from'] = str_replace('<basepath>', $this->base_dir, $action['from']);
830
				$GLOBALS['log']->debug("Installing DCActions ..." . $action['from']);
831
				$path = 'custom/Extension/application/Ext/DashletContainer/Containers';
832
				if(!file_exists($path)){
833
					mkdir_recursive($path, true);
834
				}
835
				copy_recursive($action['from'] , $path . '/'. $this->id_name . '.php');
836
			}
837
			$this->rebuild_dashletcontainers();
838
		}
839
	}
840
841
	function uninstall_dcactions(){
842
        if(isset($this->installdefs['dcaction'])){
843
			$this->log(translate('LBL_MI_UN_MENUS'));
844
			foreach($this->installdefs['dcaction'] as $action){
845
				$action['from'] = str_replace('<basepath>', $this->base_dir, $action['from']);
846
				$GLOBALS['log']->debug("Uninstalling DCActions ..." . $action['from'] );
847
				$path = 'custom/Extension/application/Ext/DashletContainer/Containers';
848
				if (sugar_is_file($path . '/'. $this->id_name . '.php', 'w'))
849
				{
850
					rmdir_recursive( $path . '/'. $this->id_name . '.php');
851
				}
852
				else if (sugar_is_file($path . '/'. DISABLED_PATH . '/'. $this->id_name . '.php', 'w'))
853
				{
854
					rmdir_recursive( $path . '/'. DISABLED_PATH . '/'. $this->id_name . '.php');
855
				}
856
			}
857
			$this->rebuild_dashletcontainers();
858
		}
859
	}
860
861
	function install_connectors(){
862
        if(isset($this->installdefs['connectors'])){
863
        	foreach($this->installdefs['connectors'] as $cp){
864
				$this->log(translate('LBL_MI_IN_CONNECTORS') . $cp['name']);
865
				$dir = str_replace('_','/',$cp['name']);
866
				$cp['connector'] = str_replace('<basepath>', $this->base_dir, $cp['connector']);
867
				$source_path = 'custom/modules/Connectors/connectors/sources/' . $dir. '/';
868
				$GLOBALS['log']->debug("Installing Connector " . $cp['name'] . "..." . $cp['connector'] );
869
				if(!file_exists($source_path)){
870
					mkdir_recursive($source_path, true);
871
				}
872
				copy_recursive($cp['connector'] , $source_path);
873
874
				//Install optional formatter code if it is specified
875
				if(!empty($cp['formatter'])) {
876
					$cp['formatter'] = str_replace('<basepath>', $this->base_dir, $cp['formatter']);
877
					$formatter_path = 'custom/modules/Connectors/connectors/formatters/' . $dir. '/';
878
					if(!file_exists($formatter_path)){
879
						mkdir_recursive($formatter_path, true);
880
					}
881
					copy_recursive($cp['formatter'] , $formatter_path);
882
				}
883
			}
884
			require_once('include/connectors/utils/ConnectorUtils.php');
885
			ConnectorUtils::installSource($cp['name']);
0 ignored issues
show
The variable $cp seems to be defined by a foreach iteration on line 863. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
886
		}
887
888
	}
889
	function uninstall_connectors(){
890
    	if(isset($this->installdefs['connectors'])){
891
    		foreach($this->installdefs['connectors'] as $cp){
892
				$this->log(translate('LBL_MI_UN_CONNECTORS') . $cp['name']);
893
				$dir = str_replace('_','/',$cp['name']);
894
				$source_path = 'custom/modules/Connectors/connectors/sources/' . $dir;
895
				$formatter_path = 'custom/modules/Connectors/connectors/formatters/' . $dir;
896
				$GLOBALS['log']->debug('Unlink ' .$source_path);
897
				rmdir_recursive($source_path);
898
				rmdir_recursive($formatter_path);
899
			}
900
			require_once('include/connectors/utils/ConnectorUtils.php');
901
			//ConnectorUtils::getConnectors(true);
902
			ConnectorUtils::uninstallSource($cp['name']);
0 ignored issues
show
The variable $cp seems to be defined by a foreach iteration on line 891. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
903
		}
904
	}
905
906
	function install_vardef($from, $to_module)
907
	{
908
			$GLOBALS['log']->debug("Installing Vardefs ..." . $from .  " for " .$to_module);
909
			$path = 'custom/Extension/modules/' . $to_module. '/Ext/Vardefs';
910
			if($to_module == 'application'){
911
				$path ='custom/Extension/' . $to_module. '/Ext/Vardefs';
912
			}
913
			if(!file_exists($path)){
914
				mkdir_recursive($path, true);
915
			}
916
			copy_recursive($from , $path.'/'. basename($from));
917
	}
918
919
	function install_layoutdef($from, $to_module){
920
			$GLOBALS['log']->debug("Installing Layout Defs ..." . $from .  " for " .$to_module);
921
			$path = 'custom/Extension/modules/' . $to_module. '/Ext/Layoutdefs';
922
			if($to_module == 'application'){
923
				$path ='custom/Extension/' . $to_module. '/Ext/Layoutdefs';
924
			}
925
			if(!file_exists($path)){
926
				mkdir_recursive($path, true);
927
			}
928
			copy_recursive($from , $path.'/'. basename($from));
929
	}
930
931
    // Non-standard - needs special rebuild call
932
	function install_languages()
933
	{
934
        $languages = array();
935
        if(isset($this->installdefs['language']))
936
        {
937
            $this->log(translate('LBL_MI_IN_LANG') );
938
            foreach($this->installdefs['language'] as $packs)
939
            {
940
                $modules[]=$packs['to_module'];
941
                $languages[$packs['language']] = $packs['language'];
942
				$packs['from'] = str_replace('<basepath>', $this->base_dir, $packs['from']);
943
				$GLOBALS['log']->debug("Installing Language Pack ..." . $packs['from']  .  " for " .$packs['to_module']);
944
                $path = $this->getInstallLanguagesPath($packs);
945
                if (!file_exists(dirname($path))) {
946
                    mkdir_recursive(dirname($path), true);
947
                }
948
                copy_recursive($packs['from'], $path);
949
950
			}
951
			$this->rebuild_languages($languages, $modules);
952
953
		}
954
	}
955
956
    /**
957
     * Function return path to file where store label
958
     *
959
     * @param $packs
960
     * @return string
961
     */
962
    protected function getInstallLanguagesPath($packs)
963
    {
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
        $path .= '/'.$packs['language'].'.'. $this->id_name . '.php';
969
        return $path;
970
    }
971
972
    // Non-standard, needs special rebuild
973
	function uninstall_languages(){
974
        $languages = array();
975
				if(isset($this->installdefs['language'])){
976
					$this->log(translate('LBL_MI_UN_LANG') );
977
					foreach($this->installdefs['language'] as $packs){
978
						$modules[]=$packs['to_module'];
979
						$languages[$packs['language']] = $packs['language'];
980
						$packs['from'] = str_replace('<basepath>', $this->base_dir, $packs['from']);
981
						$GLOBALS['log']->debug("Uninstalling Language Pack ..." . $packs['from']  .  " for " .$packs['to_module']);
982
						$path = 'custom/Extension/modules/' . $packs['to_module']. '/Ext/Language';
983
						if($packs['to_module'] == 'application'){
984
							$path ='custom/Extension/' . $packs['to_module']. '/Ext/Language';
985
						}
986
						if (sugar_is_file($path.'/'.$packs['language'].'.'. $this->id_name . '.php', 'w')) {
987
							rmdir_recursive( $path.'/'.$packs['language'].'.'. $this->id_name . '.php');
988
						} else if (sugar_is_file($path.'/'.DISABLED_PATH.'/'.$packs['language'].'.'. $this->id_name . '.php', 'w')) {
989
							rmdir_recursive($path.'/'.DISABLED_PATH.'/'.$packs['language'].'.'. $this->id_name . '.php', 'w');
0 ignored issues
show
The call to rmdir_recursive() has too many arguments starting with 'w'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
990
						}
991
					}
992
					$this->rebuild_languages($languages, $modules);
993
994
				}
995
	}
996
997
    // Non-standard, needs special rebuild
998
	public function disable_languages()
999
	{
1000
		if(isset($this->installdefs['language'])) {
1001
		    $languages = $modules = array();
1002
			foreach($this->installdefs['language'] as $item) {
1003
				$from = str_replace('<basepath>', $this->base_dir, $item['from']);
1004
				$GLOBALS['log']->debug("Disabling Language {$item['language']}... from $from for " .$item['to_module']);
1005
				$modules[]=$item['to_module'];
1006
				$languages[$item['language']] = $item['language'];
1007
				if($item['to_module'] == 'application') {
1008
                    $path = "custom/Extension/application/Ext/Language";
1009
                } else {
1010
				    $path = "custom/Extension/modules/{$item['to_module']}/Ext/Language";
1011
                }
1012
				if(isset($item["name"])) {
1013
				    $target = $item["name"];
1014
				} else {
1015
				    $target = $this->id_name;
1016
				}
1017
				$target = "{$item['language']}.$target";
1018
1019
				$disabled_path = $path.'/'.DISABLED_PATH;
1020
                if (file_exists("$path/$target.php")) {
1021
					mkdir_recursive($disabled_path, true);
1022
					rename("$path/$target.php", "$disabled_path/$target.php");
1023
				} else if (file_exists($path . '/'. basename($from))) {
1024
					mkdir_recursive($disabled_path, true);
1025
				    rename( $path . '/'. basename($from), $disabled_path.'/'. basename($from));
1026
				}
1027
			}
1028
			$this->rebuild_languages($languages, $modules);
1029
		}
1030
	}
1031
1032
    // Non-standard, needs special rebuild
1033
	public function enable_languages()
1034
	{
1035
		if(isset($this->installdefs['language'])) {
1036
			foreach($this->installdefs['language'] as $item) {
1037
				$from = str_replace('<basepath>', $this->base_dir, $item['from']);
1038
				$GLOBALS['log']->debug("Enabling Language {$item['language']}... from $from for " .$item['to_module']);
1039
				$modules[]=$item['to_module'];
1040
				$languages[$item['language']] = $item['language'];
1041
				if(!empty($module)) {
1042
				    $item['to_module'] = $module;
1043
				}
1044
1045
                if($item['to_module'] == 'application') {
1046
                    $path = "custom/Extension/application/Ext/Language";
1047
                } else {
1048
				    $path = "custom/Extension/modules/{$item['to_module']}/Ext/Language";
1049
                }
1050
				if(isset($item["name"])) {
1051
				    $target = $item["name"];
1052
				} else {
1053
				    $target = $this->id_name;
1054
				}
1055
				$target = "{$item['language']}.$target";
1056
1057
				if(!file_exists($path)) {
1058
				    mkdir_recursive($path, true);
1059
				}
1060
                $disabled_path = $path.'/'.DISABLED_PATH;
1061
				if (file_exists("$disabled_path/$target.php")) {
1062
					rename("$disabled_path/$target.php",  "$path/$target.php");
1063
				}
1064
				if (file_exists($disabled_path . '/'. basename($from))) {
1065
					rename($disabled_path.'/'. basename($from),  $path . '/'. basename($from));
1066
				}
1067
			}
1068
			$this->rebuild_languages($languages, $modules);
1069
		}
1070
    }
1071
1072
    // Functions for adding and removing logic hooks from uploaded files
1073
    // 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
1074
    /* The module hook definition should look like this:
1075
     $installdefs = array(
1076
     ... blah blah ...
1077
         'logic_hooks' => array(
1078
             array('module'      => 'Accounts',
1079
                   'hook'        => 'after_save',
1080
                   'order'       => 99,
1081
                   'description' => 'Account sample logic hook',
1082
                   'file'        => 'modules/Sample/sample_account_logic_hook_file.php',
1083
                   'class'       => 'SampleLogicClass',
1084
                   'function'    => 'accountAfterSave',
1085
             ),
1086
         ),
1087
     ... blah blah ...
1088
     );
1089
     */
1090
    function enable_manifest_logichooks() {
1091
        if(empty($this->installdefs['logic_hooks']) || !is_array($this->installdefs['logic_hooks'])) {
1092
           return;
1093
        }
1094
1095
1096
1097
        foreach($this->installdefs['logic_hooks'] as $hook ) {
1098
            check_logic_hook_file($hook['module'], $hook['hook'], array($hook['order'], $hook['description'],  $hook['file'], $hook['class'], $hook['function']));
1099
        }
1100
    }
1101
1102
    function disable_manifest_logichooks() {
1103
        if(empty($this->installdefs['logic_hooks']) || !is_array($this->installdefs['logic_hooks'])) {
1104
            return;
1105
        }
1106
1107
        foreach($this->installdefs['logic_hooks'] as $hook ) {
1108
            remove_logic_hook($hook['module'], $hook['hook'], array($hook['order'], $hook['description'],  $hook['file'], $hook['class'], $hook['function']));
1109
        }
1110
    }
1111
1112
    /**
1113
     * Check labels inside label files and remove them
1114
     *
1115
     * @param $basePath - path to files with labels
1116
     * @param array $labelDefinitions - format like output from AbstractRelationship buildLabels()
1117
     */
1118
    public function uninstallLabels($basePath, $labelDefinitions)
1119
    {
1120
1121
        foreach ($labelDefinitions as $definition) {
1122
1123
            $filename = $basePath . "{$definition['module']}.php";
1124
1125
            if (!file_exists($filename)) {
1126
                continue;
1127
            }
1128
1129
            $uninstalLabes = $this->getLabelsToUninstall($labelDefinitions);
1130
            $this->uninstallLabel($uninstalLabes, $definition, $filename);
1131
        }
1132
1133
    }
1134
1135
    /**
1136
     * Check labels inside label file and remove them
1137
     *
1138
     * @param $uninstalLabes
1139
     * @param $definition
1140
     * @param $filename
1141
     */
1142
    protected function uninstallLabel($uninstalLabes, $definition, $filename)
1143
    {
1144
        $app_list_strings = array();
1145
        $mod_strings = array();
1146
        $stringsName = $definition['module'] == 'application' ? 'app_list_strings' : 'mod_strings';
1147
1148
        include($filename);
1149
        if ('app_list_strings' == $stringsName) {
1150
            $strings = $app_list_strings;
1151
        } else {
1152
            $strings = $mod_strings;
1153
        }
1154
1155
        foreach ($uninstalLabes AS $label) {
1156
            if (isset($strings[$label])) {
1157
                unset($strings[$label]);
1158
            }
1159
        }
1160
1161
        if (count($strings)) {
1162
            $this->saveContentToFile($filename, $stringsName, $strings);
1163
        } else {
1164
            unlink($filename);
1165
        }
1166
    }
1167
1168
    /**
1169
     * Save labels that not need be uninstalled at this case
1170
     *
1171
     * @param $filename
1172
     * @param $stringsName
1173
     * @param $strings
1174
     */
1175
    protected function saveContentToFile($filename, $stringsName, $strings)
1176
    {
1177
        $fileContent = "<?php\n//THIS FILE IS AUTO GENERATED, DO NOT MODIFY\n";
1178
        foreach ($strings as $key => $val) {
1179
            $fileContent .= override_value_to_string_recursive2($stringsName, $key, $val);
1180
        }
1181
        sugar_file_put_contents($filename, $fileContent);
1182
    }
1183
1184
    /**
1185
     * Uninstall extend labels
1186
     *
1187
     * @param $labelDefinitions
1188
     */
1189
    public function uninstallExtLabels($labelDefinitions)
1190
    {
1191
        foreach ($labelDefinitions as $definition) {
1192
            if (!isset($GLOBALS['sugar_config']['languages']) || !is_array($GLOBALS['sugar_config']['languages'])) {
1193
                continue;
1194
            }
1195
1196
            foreach (array_keys($GLOBALS['sugar_config']['languages']) AS $language) {
1197
                $pathDef = array(
1198
                    'language' => $language,
1199
                    'to_module' => $definition['module']
1200
                );
1201
                $path = $this->getInstallLanguagesPath($pathDef);
1202
                if (file_exists($path)) {
1203
                    unlink($path);
1204
                }
1205
            }
1206
        }
1207
1208
    }
1209
1210
    /**
1211
     * Returns the names of the label(key 'system_label') from a multi-dimensional array $labelDefinitions
1212
     *
1213
     * @param $labelDefinitions
1214
     * @return array of labels
1215
     */
1216
    protected function getLabelsToUninstall($labelDefinitions)
1217
    {
1218
        $labels = array();
1219
        foreach($labelDefinitions AS $definition){
1220
            $labels[] = $definition['system_label'];
1221
        }
1222
        return $labels;
1223
    }
1224
1225
/* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
1226
	function copy_path($from, $to, $backup_path='', $uninstall=false){
1227
	//function copy_path($from, $to){
1228
/* END - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
1229
		$to = str_replace('<basepath>', $this->base_dir, $to);
1230
1231
		if(!$uninstall) {
1232
		$from = str_replace('<basepath>', $this->base_dir, $from);
1233
		$GLOBALS['log']->debug('Copy ' . $from);
1234
		}
1235
		else {
1236
			$from = str_replace('<basepath>', $backup_path, $from);
1237
			//$GLOBALS['log']->debug('Restore ' . $from);
1238
		}
1239
		$from = clean_path($from);
1240
		$to = clean_path($to);
1241
1242
		$dir = dirname($to);
1243
		//there are cases where if we need to create a directory in the root directory
1244
		if($dir == '.' && is_dir($from)){
1245
			$dir = $to;
1246
		}
1247
		if(!sugar_is_dir($dir, 'instance'))
1248
			mkdir_recursive($dir, true);
1249
/* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
1250
		if(empty($backup_path)) {
1251
/* END - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
1252
		if(!copy_recursive($from, $to)){
1253
			die('Failed to copy ' . $from. ' ' . $to);
1254
		}
1255
/* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
1256
		}
1257
		elseif(!$this->copy_recursive_with_backup($from, $to, $backup_path, $uninstall)){
1258
			die('Failed to copy ' . $from. ' to ' . $to);
1259
		}
1260
/* END - RESTORE POINT - by MR. MILK August 31, 2005 02:22:18 PM */
1261
	}
1262
1263
	function install_custom_fields($fields){
1264
		global $beanList, $beanFiles;
1265
		include('include/modules.php');
1266
		require_once('modules/DynamicFields/FieldCases.php');
1267
		foreach($fields as $field){
1268
			$installed = false;
1269
			if(isset($beanList[ $field['module']])){
1270
				$class = $beanList[ $field['module']];
1271
                if(!isset($field['ext4']))$field['ext4'] = '';
1272
                if(!isset($field['mass_update']))$field['mass_update'] = 0;
1273
                if(!isset($field['duplicate_merge']))$field['duplicate_merge'] = 0;
1274
                if(!isset($field['help']))$field['help'] = '';
1275
1276
                //Merge contents of the sugar field extension if we copied one over
1277
                if (file_exists("custom/Extension/modules/{$field['module']}/Ext/Vardefs/sugarfield_{$field['name']}.php"))
1278
                {
1279
                    $dictionary = array();
1280
                    include ("custom/Extension/modules/{$field['module']}/Ext/Vardefs/sugarfield_{$field['name']}.php");
1281
                    $obj = BeanFactory::getObjectName($field['module']);
1282
                    if (!empty($dictionary[$obj]['fields'][$field['name']])) {
1283
                        $field = array_merge($dictionary[$obj]['fields'][$field['name']], $field);
1284
                    }
1285
                }
1286
1287
                if(file_exists($beanFiles[$class])){
1288
					require_once($beanFiles[$class]);
1289
					$mod = new $class();
1290
					$installed = true;
1291
					$fieldObject = get_widget($field['type']);
1292
					$fieldObject->populateFromRow($field);
1293
					$mod->custom_fields->use_existing_labels =  true;
1294
					$mod->custom_fields->addFieldObject($fieldObject);
1295
				}
1296
			}
1297
			if(!$installed){
1298
				$GLOBALS['log']->debug('Could not install custom field ' . $field['name'] . ' for module ' .  $field['module'] . ': Module does not exist');
1299
			}
1300
		}
1301
	}
1302
1303
	function uninstall_custom_fields($fields){
1304
		global $beanList, $beanFiles;
1305
		require_once('modules/DynamicFields/DynamicField.php');
1306
		$dyField = new DynamicField();
1307
1308
		foreach($fields as $field){
1309
			$class = $beanList[ $field['module']];
1310
			if(file_exists($beanFiles[$class])){
1311
					require_once($beanFiles[$class]);
1312
					$mod = new $class();
1313
					$dyField->bean = $mod;
1314
					$dyField->module = $field['module'];
1315
					$dyField->deleteField($field['name']);
1316
			}
1317
		}
1318
	}
1319
1320
        /*
1321
     * ModuleInstaller->install_relationships calls install_relationship for every file included in the module package that defines a relationship, and then
1322
     * writes a custom/Extension/application/Ext/TableDictionary/$module.php file containing an include_once for every relationship metadata file passed to install_relationship.
1323
     * 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
1324
     * everything in 'Ext/TableDictionary/' into 'tabledictionary.ext.php'
1325
     */
1326
    function install_relationships ()
1327
    {
1328
        if (isset ( $this->installdefs [ 'relationships' ] ))
1329
        {
1330
            $this->log ( translate ( 'LBL_MI_IN_RELATIONSHIPS' ) ) ;
1331
            $str = "<?php \n //WARNING: The contents of this file are auto-generated\n" ;
1332
            $save_table_dictionary = false ;
1333
1334
            if (! file_exists ( "custom/Extension/application/Ext/TableDictionary" ))
1335
            {
1336
                mkdir_recursive ( "custom/Extension/application/Ext/TableDictionary", true ) ;
1337
            }
1338
1339
            foreach ( $this->installdefs [ 'relationships' ] as $key => $relationship )
1340
            {
1341
                $filename = basename ( $relationship [ 'meta_data' ] ) ;
1342
                $this->copy_path ( $relationship [ 'meta_data' ], 'custom/metadata/' . $filename ) ;
1343
                $this->install_relationship ( 'custom/metadata/' . $filename ) ;
1344
                $save_table_dictionary = true ;
1345
1346
                if (! empty ( $relationship [ 'module_vardefs' ] ))
1347
                {
1348
                    $relationship [ 'module_vardefs' ] = str_replace ( '<basepath>', $this->base_dir, $relationship [ 'module_vardefs' ] ) ;
1349
                    $this->install_vardef ( $relationship [ 'module_vardefs' ], $relationship [ 'module' ] ) ;
1350
                }
1351
1352
                if (! empty ( $relationship [ 'module_layoutdefs' ] ))
1353
                {
1354
                    $relationship [ 'module_layoutdefs' ] = str_replace ( '<basepath>', $this->base_dir, $relationship [ 'module_layoutdefs' ] ) ;
1355
                    $this->install_layoutdef ( $relationship [ 'module_layoutdefs' ], $relationship [ 'module' ] ) ;
1356
                }
1357
1358
                $relName = strpos($filename, "MetaData") !== false ? substr($filename, 0, strlen($filename) - 12) : $filename;
1359
                $out = sugar_fopen ( "custom/Extension/application/Ext/TableDictionary/$relName.php", 'w' ) ;
1360
                fwrite ( $out, $str . "include('custom/metadata/$filename');\n\n?>" ) ;
1361
                fclose ( $out ) ;
1362
            }
1363
1364
1365
1366
1367
            Relationship::delete_cache();
1368
            $this->rebuild_vardefs () ;
1369
            $this->rebuild_layoutdefs () ;
0 ignored issues
show
Documentation Bug introduced by
The method rebuild_layoutdefs does not exist on object<ModuleInstaller>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1370
            if ($save_table_dictionary)
1371
            {
1372
                $this->rebuild_tabledictionary () ;
1373
            }
1374
            require_once("data/Relationships/RelationshipFactory.php");
1375
            SugarRelationshipFactory::deleteCache();
1376
        }
1377
    }
1378
1379
	/*
1380
     * Install_relationship obtains a set of relationship definitions from the filename passed in as a parameter.
1381
     * For each definition it calls db->createTableParams to build the relationships table if it does not exist,
1382
     * and SugarBean::createRelationshipMeta to add the relationship into the 'relationships' table.
1383
     */
1384
	function install_relationship($file)
1385
	{
1386
		$_REQUEST['moduleInstaller'] = true;
1387
		if(!file_exists($file))
1388
		{
1389
			$GLOBALS['log']->debug( 'File does not exists : '.$file);
1390
			return;
1391
		}
1392
		include($file);
1393
		$rel_dictionary = $dictionary;
1394
		foreach ($rel_dictionary as $rel_name => $rel_data)
1395
   	    {
1396
   	        $table = ''; // table is actually optional
1397
   	        // check if we have a table definition - not all relationships require a join table
1398
            if ( isset( $rel_data[ 'table' ] ) )
1399
            {
1400
                $table = $rel_data[ 'table' ];
1401
1402
                if(!$this->db->tableExists($table))
1403
                {
1404
                    $this->db->createTableParams($table, $rel_data[ 'fields' ], $rel_data[ 'indices' ]);
1405
                }
1406
            }
1407
1408
            if(!$this->silent)
1409
                $GLOBALS['log']->debug("Processing relationship meta for ". $rel_name."...");
1410
            SugarBean::createRelationshipMeta($rel_name, $this->db,$table,$rel_dictionary,'');
1411
            Relationship::delete_cache();
1412
            if(!$this->silent)
1413
                $GLOBALS['log']->debug( 'done<br>');
1414
        }
1415
	}
1416
1417
	function install_layoutfields() {
1418
		 if (!empty ( $this->installdefs [ 'layoutfields' ] ))
1419
		 {
1420
		 	foreach ( $this->installdefs [ 'layoutfields' ] as $fieldSet )
1421
            {
1422
		 		if (!empty($fieldSet['additional_fields']))
1423
		 		{
1424
		 			$this->addFieldsToLayout($fieldSet['additional_fields']);
1425
		 		}
1426
            }
1427
		 }
1428
	}
1429
1430
	function uninstall_layoutfields() {
1431
		 if (!empty ( $this->installdefs [ 'layoutfields' ] ))
1432
		 {
1433
		 	foreach ( $this->installdefs [ 'layoutfields' ] as $fieldSet )
1434
            {
1435
		 		if (!empty($fieldSet['additional_fields']))
1436
		 		{
1437
		 			$this->removeFieldsFromLayout($fieldSet['additional_fields']);
1438
		 		}
1439
            }
1440
		 }
1441
	}
1442
1443
	function uninstall_relationship($file, $rel_dictionary = null){
1444
        if ($rel_dictionary == null)
1445
		{
1446
			if(!file_exists($file)){
1447
				$GLOBALS['log']->debug( 'File does not exists : '.$file);
1448
				return;
1449
			}
1450
			include($file);
1451
			$rel_dictionary = $dictionary;
0 ignored issues
show
The variable $dictionary does not exist. Did you mean $rel_dictionary?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
1452
		}
1453
1454
		foreach ($rel_dictionary as $rel_name => $rel_data)
1455
		{
1456
			if (!empty($rel_data['table'])){
1457
				$table = $rel_data['table'];
1458
			}
1459
			else{
1460
				$table = ' One-to-Many ';
1461
			}
1462
1463
			if ($this->db->tableExists($table) && isset($GLOBALS['mi_remove_tables']) && $GLOBALS['mi_remove_tables'])
1464
			{
1465
				SugarBean::removeRelationshipMeta($rel_name, $this->db,$table,$rel_dictionary,'');
1466
				$this->db->dropTableName($table);
1467
				if(!$this->silent) $this->log( translate('LBL_MI_UN_RELATIONSHIPS_DROP') . $table);
1468
			}
1469
1470
			//Delete Layout defs
1471
			// check to see if we have any vardef or layoutdef entries to remove - must have a relationship['module'] parameter if we do
1472
			if (!isset($rel_data[ 'module' ]))
1473
				$mods = array(
1474
					$rel_data['relationships'][$rel_name]['lhs_module'],
1475
					$rel_data['relationships'][$rel_name]['rhs_module'],
1476
				);
1477
			else
1478
				$mods = array($rel_data[ 'module' ]);
1479
1480
			$filename = "$rel_name.php";
1481
1482
			foreach($mods as $mod) {
1483
				if ($mod != 'application' )  {
1484
					$basepath = "custom/Extension/modules/$mod/Ext/";
1485
				} else {
1486
					$basepath = "custom/Extension/application/Ext/";
1487
				}
1488
1489
				foreach (array($filename , "custom" . $filename, $rel_name ."_". $mod. ".php") as $fn) {
1490
					//remove any vardefs
1491
					$path = $basepath . "Vardefs/$fn" ;
1492
					if (file_exists( $path ))
1493
						rmdir_recursive( $path );
1494
1495
					//remove any layoutdefs
1496
					$path = $basepath . "Layoutdefs/$fn" ;
1497
					if( file_exists( $path ))
1498
					{
1499
						rmdir_recursive( $path );
1500
					}
1501
                    $path = $basepath . "WirelessLayoutdefs/$fn";
1502
                    if (file_exists($path)) {
1503
                        rmdir_recursive($path);
1504
                    }
1505
				}
1506
                $relationships_path = 'custom/Extension/modules/relationships/';
1507
1508
                $relationships_dirs = array(
1509
                    'layoutdefs',
1510
                    'vardefs',
1511
                    'wirelesslayoutdefs'
1512
                );
1513
                foreach ($relationships_dirs as $relationship_dir) {
1514
                    $realtionship_file_path = $relationships_path . $relationship_dir . "/{$rel_name}_{$mod}.php";
1515
                    if (file_exists($realtionship_file_path)) {
1516
                        rmdir_recursive($realtionship_file_path);
1517
                    }
1518
                }
1519
                if (file_exists($relationships_path . "relationships/{$rel_name}MetaData.php")) {
1520
                    rmdir_recursive($relationships_path . "relationships/{$rel_name}MetaData.php");
1521
                }
1522
            }
1523
1524
			foreach (array($filename , "custom" . $filename, $rel_name ."_". $mod. ".php") as $fn) {
0 ignored issues
show
The variable $mod seems to be defined by a foreach iteration on line 1482. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
1525
				// remove the table dictionary extension
1526
				if ( file_exists("custom/Extension/application/Ext/TableDictionary/$fn"))
1527
				    unlink("custom/Extension/application/Ext/TableDictionary/$fn");
1528
1529
				if (file_exists("custom/metadata/{$rel_name}MetaData.php"))
1530
					unlink( "custom/metadata/{$rel_name}MetaData.php" );
1531
			}
1532
		}
1533
	}
1534
1535
	function uninstall_relationships($include_studio_relationships = false){
1536
		$relationships = array();
1537
1538
		//Find and remove studio created relationships.
1539
		global $beanList, $beanFiles, $dictionary;
1540
		//Load up the custom relationship definitions.
1541
		if(file_exists('custom/application/Ext/TableDictionary/tabledictionary.ext.php')){
1542
			include('custom/application/Ext/TableDictionary/tabledictionary.ext.php');
1543
		}
1544
		//Find all the relatioships/relate fields involving this module.
1545
		$rels_to_remove = array();
1546
		foreach($beanList as $mod => $bean) {
1547
			//Some modules like cases have a bean name that doesn't match the object name
1548
			$bean = BeanFactory::getObjectName($mod);
1549
            VardefManager::loadVardef($mod, $bean);
1550
			//We can skip modules that are in this package as they will be removed anyhow
1551
			if (!in_array($mod, $this->modulesInPackage) && !empty($dictionary[$bean]) && !empty($dictionary[$bean]['fields']))
1552
			{
1553
				$field_defs = $dictionary[$bean]['fields'];
1554
				foreach($field_defs as $field => $def)
1555
				{
1556
					//Weed out most fields first
1557
					if (isset ($def['type']))
1558
					{
1559
						//Custom relationships created in the relationship editor
1560
						if ($def['type'] == "link" && !empty($def['relationship']) && !empty($dictionary[$def['relationship']]))
1561
						{
1562
							$rel_name = $def['relationship'];
1563
1564
							$rel_def = $dictionary[$rel_name]['relationships'][$rel_name];
1565
1566
							//Check against mods to be removed.
1567
							foreach($this->modulesInPackage as $removed_mod) {
1568
								if ($rel_def['lhs_module'] == $removed_mod || $rel_def['rhs_module'] == $removed_mod )
1569
								{
1570
									$dictionary[$rel_name]['from_studio'] = true;
1571
									$relationships[$rel_name] = $dictionary[$rel_name];
1572
								}
1573
							}
1574
						}
1575
						//Custom "relate" fields created in studio also need to be removed
1576
						if ($def['type'] == 'relate' && isset($def['module'])) {
1577
							foreach($this->modulesInPackage as $removed_mod) {
1578
								if ($def['module'] == $removed_mod)
1579
								{
1580
									require_once 'modules/ModuleBuilder/Module/StudioModule.php' ;
1581
									$studioMod = new StudioModule ( $mod );
1582
									$studioMod->removeFieldFromLayouts( $field );
1583
									if (isset($def['custom_module'])) {
1584
										require_once ('modules/DynamicFields/DynamicField.php') ;
1585
										require_once ($beanFiles [ $bean ]) ;
1586
										$seed = new $bean ( ) ;
1587
										$df = new DynamicField ( $mod ) ;
1588
										$df->setup ( $seed ) ;
1589
										//Need to load the entire field_meta_data for some field types
1590
										$field_obj = $df->getFieldWidget($mod, $field);
1591
										$field_obj->delete ( $df ) ;
1592
									}
1593
								}
1594
							}
1595
						}
1596
					}
1597
				}
1598
			}
1599
		}
1600
1601
1602
1603
		$this->uninstall_relationship(null, $relationships);
1604
1605
		if(isset($this->installdefs['relationships'])) {
1606
			$relationships = $this->installdefs['relationships'];
1607
			$this->log(translate('LBL_MI_UN_RELATIONSHIPS') );
1608
			foreach($relationships as $relationship)
1609
			{
1610
				// remove the metadata entry
1611
				$filename = basename ( $relationship['meta_data'] );
1612
				$pathname = (file_exists("custom/metadata/$filename")) ? "custom/metadata/$filename" : "metadata/$filename" ;
1613
				if(isset($GLOBALS['mi_remove_tables']) && $GLOBALS['mi_remove_tables'])
1614
				$this->uninstall_relationship( $pathname );
1615
				if (file_exists($pathname))
1616
					unlink( $pathname );
1617
			}
1618
		}
1619
1620
		if (file_exists("custom/Extension/application/Ext/TableDictionary/{$this->id_name}.php"))
1621
			unlink("custom/Extension/application/Ext/TableDictionary/{$this->id_name}.php");
1622
		Relationship::delete_cache();
1623
		$this->rebuild_tabledictionary();
1624
	}
1625
1626
1627
1628
1629
	function uninstall($base_dir){
1630
		if(defined('TEMPLATE_URL'))SugarTemplateUtilities::disableCache();
1631
		global $app_strings;
1632
		$total_steps = 5; //min steps with no tasks
1633
		$current_step = 0;
1634
		$this->base_dir = $base_dir;
1635
		$tasks = array(
1636
			'pre_uninstall',
1637
			'uninstall_relationships',
1638
			'uninstall_copy',
1639
			'uninstall_dcactions',
1640
			'uninstall_dashlets',
1641
			'uninstall_connectors',
1642
			'uninstall_layoutfields',
1643
		    'uninstall_extensions',
1644
            'uninstall_global_search',
1645
			'disable_manifest_logichooks',
1646
			'post_uninstall',
1647
		);
1648
		$total_steps += count($tasks); //now the real number of steps
1649
		if(file_exists($this->base_dir . '/manifest.php')){
1650
				if(!$this->silent){
1651
					$current_step++;
1652
					display_progress_bar('install', $current_step, $total_steps);
1653
					echo '<div id ="displayLoglink" ><a href="#" onclick="toggleDisplay(\'displayLog\')">'.$app_strings['LBL_DISPLAY_LOG'].'</a> </div><div id="displayLog" style="display:none">';
1654
				}
1655
1656
				global $moduleList;
1657
				include($this->base_dir . '/manifest.php');
1658
				$this->installdefs = $installdefs;
1659
				$this->id_name = $this->installdefs['id'];
1660
				$installed_modules = array();
1661
				if(isset($this->installdefs['beans'])){
1662
					foreach($this->installdefs['beans'] as $bean){
1663
1664
						$installed_modules[] = $bean['module'];
1665
						$this->uninstall_user_prefs($bean['module']);
1666
					}
1667
					$this->modulesInPackage = $installed_modules;
1668
					$this->uninstall_beans($installed_modules);
1669
					$this->uninstall_customizations($installed_modules);
1670
					if(!$this->silent){
1671
						$current_step++;
1672
						update_progress_bar('install', $total_steps, $total_steps);
1673
					}
1674
				}
1675
				if(!$this->silent){
1676
					$current_step++;
1677
					update_progress_bar('install', $current_step, $total_steps);
1678
				}
1679
				foreach($tasks as $task){
1680
					$this->$task();
1681
					if(!$this->silent){
1682
						$current_step++;
1683
						update_progress_bar('install', $current_step, $total_steps);
1684
					}
1685
				}
1686
				if(isset($installdefs['custom_fields']) && (isset($GLOBALS['mi_remove_tables']) && $GLOBALS['mi_remove_tables'])){
1687
					$this->log(translate('LBL_MI_UN_CUSTOMFIELD'));
1688
					$this->uninstall_custom_fields($installdefs['custom_fields']);
1689
				}
1690
				if(!$this->silent){
1691
					$current_step++;
1692
					update_progress_bar('install', $current_step, $total_steps);
1693
					echo '</div>';
1694
				}
1695
				//since we are passing $silent = true to rebuildAll() in that method it will set $this->silent = true, so
1696
				//we need to save the setting to set it back after rebuildAll() completes.
1697
				$silentBak = $this->silent;
1698
				$this->rebuild_all(true);
1699
				$this->silent = $silentBak;
1700
1701
				//#27877, If the request from MB redeploy a custom module , we will not remove the ACL actions for this package.
1702
				if( !isset($_REQUEST['action']) || $_REQUEST['action']!='DeployPackage' ){
1703
					$this->remove_acl_actions();
1704
				}
1705
				//end
1706
1707
				if(!$this->silent){
1708
					$current_step++;
1709
					update_progress_bar('install', $current_step, $total_steps);
1710
					echo '</div>';
1711
				}
1712
1713
				UpdateSystemTabs('Restore',$installed_modules);
1714
1715
	            //clear the unified_search_module.php file
1716
	            require_once('modules/Home/UnifiedSearchAdvanced.php');
1717
	            UnifiedSearchAdvanced::unlinkUnifiedSearchModulesFile();
1718
1719
				$this->log('<br><b>' . translate('LBL_MI_COMPLETE') . '</b>');
1720
				if(!$this->silent){
1721
					update_progress_bar('install', $total_steps, $total_steps);
1722
				}
1723
		}else{
1724
			die("No manifest.php Defined In $this->base_dir/manifest.php");
1725
		}
1726
	}
1727
1728
	function rebuild_languages($languages = array(), $modules="")
1729
	{
1730
            foreach($languages as $language=>$value){
1731
				$this->log(translate('LBL_MI_REBUILDING') . " Language...$language");
1732
				$this->merge_files('Ext/Language/', $language.'.lang.ext.php', $language);
1733
	            if($modules!=""){
1734
	                foreach($modules as $module){
0 ignored issues
show
The expression $modules of type string is not traversable.
Loading history...
1735
	                	LanguageManager::clearLanguageCache($module, $language);
1736
	                }
1737
	            }
1738
			}
1739
			sugar_cache_reset();
1740
	}
1741
1742
	function rebuild_vardefs()
1743
	{
1744
	    $this->rebuildExt("Vardefs", 'vardefs.ext.php');
1745
		sugar_cache_reset();
1746
	}
1747
1748
	function rebuild_dashletcontainers(){
1749
            $this->log(translate('LBL_MI_REBUILDING') . " DC Actions...");
1750
			$this->merge_files('Ext/DashletContainer/Containers/', 'dcactions.ext.php');
1751
	}
1752
1753
	function rebuild_tabledictionary()
1754
	{
1755
	    $this->rebuildExt("TableDictionary", 'tabledictionary.ext.php');
1756
	}
1757
1758
	function rebuild_relationships() {
1759
        if(!$this->silent) echo translate('LBL_MI_REBUILDING') . ' Relationships';
1760
		$_REQUEST['silent'] = true;
1761
		global $beanFiles;
1762
		include('include/modules.php');
1763
		include("modules/Administration/RebuildRelationship.php");
1764
	}
1765
1766
	function remove_acl_actions() {
1767
		global $beanFiles, $beanList, $current_user;
1768
		include('include/modules.php');
1769
		include("modules/ACL/remove_actions.php");
1770
	}
1771
1772
	/**
1773
	 * Wrapper call to modules/Administration/RepairIndex.php
1774
	 */
1775
	function repair_indices() {
1776
		global $current_user,$beanFiles,$dictionary;
1777
		$this->log(translate('LBL_MI_REPAIR_INDICES'));
1778
		$_REQUEST['silent'] = true; // local var flagging echo'd output in repair script
1779
		$_REQUEST['mode'] = 'execute'; // flag to just go ahead and run the script
1780
		include("modules/Administration/RepairIndex.php");
1781
	}
1782
1783
	/**
1784
	 * Rebuilds the extension files found in custom/Extension
1785
	 * @param boolean $silent
1786
	 */
1787
	function rebuild_all($silent=false){
1788
		if(defined('TEMPLATE_URL'))SugarTemplateUtilities::disableCache();
1789
		$this->silent=$silent;
1790
		global $sugar_config;
1791
1792
		//Check for new module extensions
1793
		$this->rebuild_modules();
0 ignored issues
show
Documentation Bug introduced by
The method rebuild_modules does not exist on object<ModuleInstaller>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1794
1795
		$this->rebuild_languages($sugar_config['languages']);
1796
		$this->rebuild_extensions();
1797
		$this->rebuild_dashletcontainers();
1798
		$this->rebuild_relationships();
1799
		$this->rebuild_tabledictionary();
1800
        $this->reset_opcodes();
1801
		sugar_cache_reset();
1802
	}
1803
1804
	/*
1805
     * 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
1806
     * custom/Extension/modules/$module/<path> (_override files last) and concatenates them to custom/modules/$module/<path>/<file>.
1807
     * Then it does the same thing in custom/Extension/application/<path>, concatenating those files and copying the result to custom/application/<path>/<file>
1808
     */
1809
	function merge_files($path, $name, $filter = '', $application = false){
1810
		if(!$application){
1811
		$GLOBALS['log']->debug( get_class($this)."->merge_files() : merging module files in custom/Extension/modules/<module>/$path to custom/modules/<module>/$path$name");
1812
		foreach($this->modules as $module){
1813
				//$GLOBALS['log']->debug("Merging Files for: ".$module);
1814
				//$GLOBALS['log']->debug("Merging Files for path: ".$path);
1815
				$extension = "<?php \n //WARNING: The contents of this file are auto-generated\n";
1816
				$extpath = "modules/$module/$path";
1817
				$module_install  = 'custom/Extension/'.$extpath;
1818
				$shouldSave = false;
1819
				if(is_dir($module_install)){
1820
					$dir = dir($module_install);
1821
					$shouldSave = true;
1822
					$override = array();
1823
					while($entry = $dir->read()){
1824
						if((empty($filter) || substr_count($entry, $filter) > 0) && is_file($module_install.'/'.$entry)
1825
						  && $entry != '.' && $entry != '..' && strtolower(substr($entry, -4)) == ".php")
1826
						{
1827
						     if (substr($entry, 0, 9) == '_override') {
1828
						    	$override[] = $entry;
1829
						    } else {
1830
							    $file = file_get_contents($module_install . '/' . $entry);
1831
							    $GLOBALS['log']->debug(get_class($this)."->merge_files(): found {$module_install}{$entry}") ;
1832
							    $extension .= "\n". str_replace(array('<?php', '?>', '<?PHP', '<?'), array('','', '' ,'') , $file);
1833
						    }
1834
						}
1835
					}
1836
					foreach ($override as $entry) {
1837
                        $file = file_get_contents($module_install . '/' . $entry);
1838
                        $extension .= "\n". str_replace(array('<?php', '?>', '<?PHP', '<?'), array('','', '' ,'') , $file);
1839
					}
1840
				}
1841
				$extension .= "\n?>";
1842
1843
				if($shouldSave){
1844
					if(!file_exists("custom/$extpath")) {
1845
					    mkdir_recursive("custom/$extpath", true);
1846
				    }
1847
					$out = sugar_fopen("custom/$extpath/$name", 'w');
1848
					fwrite($out,$extension);
1849
					fclose($out);
1850
				}else{
1851
					if(file_exists("custom/$extpath/$name")){
1852
						unlink("custom/$extpath/$name");
1853
					}
1854
				}
1855
			}
1856
1857
		}
1858
1859
		$GLOBALS['log']->debug("Merging application files for $name in $path");
1860
		//Now the application stuff
1861
		$extension = "<?php \n //WARNING: The contents of this file are auto-generated\n";
1862
		$extpath = "application/$path";
1863
		$module_install  = 'custom/Extension/'.$extpath;
1864
		$shouldSave = false;
1865
					if(is_dir($module_install)){
1866
						$dir = dir($module_install);
1867
						while($entry = $dir->read()){
1868
								$shouldSave = true;
1869
								if((empty($filter) || substr_count($entry, $filter) > 0) && is_file($module_install.'/'.$entry)
1870
								  && $entry != '.' && $entry != '..' && strtolower(substr($entry, -4)) == ".php")
1871
								{
1872
									$file = file_get_contents($module_install . '/' . $entry);
1873
									$extension .= "\n". str_replace(array('<?php', '?>', '<?PHP', '<?'), array('','', '' ,'') , $file);
1874
								}
1875
						}
1876
					}
1877
					$extension .= "\n?>";
1878
					if($shouldSave){
1879
						if(!file_exists("custom/$extpath")){
1880
							mkdir_recursive("custom/$extpath", true);
1881
						}
1882
						$out = sugar_fopen("custom/$extpath/$name", 'w');
1883
						fwrite($out,$extension);
1884
						fclose($out);
1885
					}else{
1886
					if(file_exists("custom/$extpath/$name")){
1887
						unlink("custom/$extpath/$name");
1888
					}
1889
				}
1890
1891
}
1892
1893
    function install_modules()
1894
    {
1895
	    $this->installed_modules = array();
1896
		$this->tab_modules = array();
1897
        if(isset($this->installdefs['beans'])){
1898
			$str = "<?php \n //WARNING: The contents of this file are auto-generated\n";
1899
			foreach($this->installdefs['beans'] as $bean){
1900
				if(!empty($bean['module']) && !empty($bean['class']) && !empty($bean['path'])){
1901
					$module = $bean['module'];
1902
					$class = $bean['class'];
1903
					$path = $bean['path'];
1904
1905
					$str .= "\$beanList['$module'] = '$class';\n";
1906
					$str .= "\$beanFiles['$class'] = '$path';\n";
1907
					if($bean['tab']){
1908
						$str .= "\$moduleList[] = '$module';\n";
1909
						$this->install_user_prefs($module, empty($bean['hide_by_default']));
1910
						$this->tab_modules[] = $module;
1911
					}else{
1912
						$str .= "\$modules_exempt_from_availability_check['$module'] = '$module';\n";
1913
						$str .= "\$report_include_modules['$module'] = '$module';\n";
1914
						$str .= "\$modInvisList[] = '$module';\n";
1915
					}
1916
				    $this->installed_modules[] = $module;
1917
				}else{
1918
						$errors[] = 'Bean array not well defined.';
1919
						$this->abort($errors);
1920
				}
1921
			}
1922
			$str.= "\n?>";
1923
			if(!file_exists("custom/Extension/application/Ext/Include")){
1924
				mkdir_recursive("custom/Extension/application/Ext/Include", true);
1925
			}
1926
			file_put_contents("custom/Extension/application/Ext/Include/{$this->id_name}.php", $str);
1927
        }
1928
    }
1929
1930
    /*
1931
     * ModuleInstaller->install_beans runs through the list of beans given, instantiates each bean, calls bean->create_tables, and then calls SugarBean::createRelationshipMeta for the
1932
     * bean/module.
1933
     */
1934
	function install_beans($beans){
1935
        include('include/modules.php');
1936
		foreach($beans as $bean){
1937
			$this->log( translate('LBL_MI_IN_BEAN') . " $bean");
1938
			if(isset($beanList[$bean])){
1939
				$class = $beanList[$bean];
1940
				if(file_exists($beanFiles[$class])){
1941
					require_once($beanFiles[$class]);
1942
					$mod = new $class();
1943
					//#30273
1944
					if(is_subclass_of($mod, 'SugarBean')  && $mod->disable_vardefs == false ){
1945
						$GLOBALS['log']->debug( "Creating Tables Bean : $bean");
1946
						$mod->create_tables();
1947
						SugarBean::createRelationshipMeta($mod->getObjectName(), $mod->db,$mod->table_name,'',$mod->module_dir);
1948
					}
1949
				}else{
1950
					$GLOBALS['log']->debug( "File Does Not Exist:" . $beanFiles[$class] );
1951
				}
1952
			}
1953
		}
1954
	}
1955
1956
		function uninstall_beans($beans){
1957
		include('include/modules.php');
1958
        foreach($beans as $bean){
1959
			$this->log( translate('LBL_MI_UN_BEAN') . " $bean");
1960
			if(isset($beanList[$bean])){
1961
				$class = $beanList[$bean];
1962
1963
				if(file_exists($beanFiles[$class])){
1964
					require_once($beanFiles[$class]);
1965
					$mod = new $class();
1966
1967
					if(is_subclass_of($mod, 'SugarBean')){
1968
						$GLOBALS['log']->debug( "Drop Tables : $bean");
1969
						if(isset($GLOBALS['mi_remove_tables']) && $GLOBALS['mi_remove_tables'])
1970
							$mod->drop_tables();
1971
					}
1972
				}else{
1973
					$GLOBALS['log']->debug( "File Does Not Exist:" . $beanFiles[$class] );
1974
				}
1975
			}
1976
		}
1977
	}
1978
1979
	/**
1980
	 * Remove any customizations made within Studio while the module was installed.
1981
	 */
1982
	function uninstall_customizations($beans){
1983
        foreach($beans as $bean){
1984
			$dirs = array(
1985
				'custom/modules/' . $bean,
1986
				'custom/Extension/modules/' . $bean,
1987
                'custom/working/modules/' . $bean
1988
			);
1989
        	foreach($dirs as $dir)
1990
        	{
1991
				if(is_dir($dir)){
1992
					rmdir_recursive($dir);
1993
				}
1994
        	}
1995
		}
1996
	}
1997
1998
	function log($str){
1999
		$GLOBALS['log']->debug('ModuleInstaller:'. $str);
2000
		if(!$this->silent){
2001
			echo $str . '<br>';
2002
		}
2003
	}
2004
2005
/* BEGIN - RESTORE POINT - by MR. MILK August 31, 2005 02:15:18 PM 	*/
2006
function copy_recursive_with_backup( $source, $dest, $backup_path, $uninstall=false ) {
2007
	if(is_file($source)) {
2008
	    if($uninstall) {
2009
		    $GLOBALS['log']->debug("Restoring ... " . $source.  " to " .$dest );
2010
		    if(copy( $source, $dest)) {
2011
			    if(is_writable($dest))
2012
			    	sugar_touch( $dest, filemtime($source) );
2013
		    	return(unlink($source));
2014
	    	}
2015
		    else {
2016
		    	$GLOBALS['log']->debug( "Can't restore file: " . $source );
2017
		    	return true;
2018
	    	}
2019
	    }
2020
	    else {
2021
			if(file_exists($dest)) {
2022
				$rest = clean_path($backup_path."/$dest");
2023
				if( !is_dir(dirname($rest)) )
2024
					mkdir_recursive(dirname($rest), true);
2025
2026
				$GLOBALS['log']->debug("Backup ... " . $dest.  " to " .$rest );
2027
				if(copy( $dest, $rest)) {
2028
					if(is_writable($rest))
2029
						sugar_touch( $rest, filemtime($dest) );
2030
				}
2031
				else {
2032
					$GLOBALS['log']->debug( "Can't backup file: " . $dest );
2033
				}
2034
			}
2035
			return( copy( $source, $dest ) );
2036
		}
2037
    }
2038
    elseif(!is_dir($source)) {
2039
	    if($uninstall) {
2040
			if(is_file($dest))
2041
				return(unlink($dest));
2042
			else {
2043
				//don't do anything we already cleaned up the files using uninstall_new_files
2044
				return true;
2045
			}
2046
		}
2047
		else
2048
			return false;
2049
	}
2050
2051
    if( !is_dir($dest) && !$uninstall){
2052
        sugar_mkdir( $dest );
2053
    }
2054
2055
    $status = true;
2056
2057
    $d = dir( $source );
2058
    while( $f = $d->read() ){
2059
        if( $f == "." || $f == ".." ){
2060
            continue;
2061
        }
2062
        $status &= $this->copy_recursive_with_backup( "$source/$f", "$dest/$f", $backup_path, $uninstall );
2063
    }
2064
    $d->close();
2065
    return( $status );
2066
}
2067
2068
private function dir_get_files($path, $base_path){
2069
	$files = array();
2070
	if(!is_dir($path))return $files;
2071
	$d = dir($path);
2072
	while ($e = $d->read()){
2073
		//ignore invisible files . .. ._MACOSX
2074
		if(substr($e, 0, 1) == '.')continue;
2075
		if(is_file($path . '/' . $e))$files[str_replace($base_path , '', $path . '/' . $e)] = str_replace($base_path , '', $path . '/' . $e);
2076
		if(is_dir($path . '/' . $e))$files = array_merge($files, $this->dir_get_files($path . '/' . $e, $base_path));
2077
	}
2078
	$d->close();
2079
	return $files;
2080
2081
}
2082
2083
private function dir_file_count($path){
2084
	//if its a file then it has at least 1 file in the directory
2085
	if(is_file($path)) return 1;
2086
	if(!is_dir($path)) return 0;
2087
	$d = dir($path);
2088
	$count = 0;
2089
	while ($e = $d->read()){
2090
		//ignore invisible files . .. ._MACOSX
2091
		if(substr($e, 0, 1) == '.')continue;
2092
		if(is_file($path . '/' . $e))$count++;
2093
		if(is_dir($path . '/' . $e))$count += $this->dir_file_count($path . '/' . $e);
2094
	}
2095
	$d->close();
2096
	return $count;
2097
2098
2099
}
2100
/* END - RESTORE POINT - by MR. MILK August 31, 2005 02:15:34 PM */
2101
2102
2103
	/**
2104
	 * Static function which allows a module developer to abort their progress, pass in an array of errors and
2105
	 * redirect back to the main module loader page
2106
	 *
2107
	 * @param errors	an array of error messages which will be displayed on the
2108
	 * 					main module loader page once it is loaded.
2109
	 */
2110
	function abort($errors = array()){
2111
		//set the errors onto the session so we can display them one the moduler loader page loads
2112
		$_SESSION['MODULEINSTALLER_ERRORS'] = $errors;
2113
		echo '<META HTTP-EQUIV="Refresh" content="0;url=index.php?module=Administration&action=UpgradeWizard&view=module">';
2114
		die();
2115
		//header('Location: index.php?module=Administration&action=UpgradeWizard&view=module');
2116
	}
2117
2118
	/**
2119
	 * Return the set of errors stored in the SESSION
2120
	 *
2121
	 * @return an array of errors
2122
	 */
2123
	static function getErrors(){
2124
		if(!empty($_SESSION['MODULEINSTALLER_ERRORS'])){
2125
			$errors = $_SESSION['MODULEINSTALLER_ERRORS'];
2126
			unset($_SESSION['MODULEINSTALLER_ERRORS']);
2127
			return $errors;
2128
		}
2129
		else
2130
			return null;
2131
	}
2132
2133
	/*
2134
     * Add any fields to the DetailView and EditView of the appropriate modules
2135
     * Only add into deployed modules, as addFieldsToUndeployedLayouts has done this already for undeployed modules (and the admin might have edited the layouts already)
2136
     * @param array $layoutAdditions  An array of module => fieldname
2137
     * return null
2138
     */
2139
	function addFieldsToLayout($layoutAdditions) {
2140
	require_once 'modules/ModuleBuilder/parsers/views/GridLayoutMetaDataParser.php' ;
2141
2142
        // these modules either lack editviews/detailviews or use custom mechanisms for the editview/detailview.
2143
        // In either case, we don't want to attempt to add a relate field to them
2144
        // would be better if GridLayoutMetaDataParser could handle this gracefully, so we don't have to maintain this list here
2145
        $invalidModules = array ( 'emails' , 'kbdocuments' ) ;
2146
2147
        foreach ( $layoutAdditions as $deployedModuleName => $fieldName )
2148
        {
2149
            if ( ! in_array( strtolower ( $deployedModuleName ) , $invalidModules ) )
2150
            {
2151
                foreach ( array ( MB_EDITVIEW , MB_DETAILVIEW ) as $view )
2152
                {
2153
                    $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . ": adding $fieldName to $view layout for module $deployedModuleName" ) ;
2154
                    $parser = new GridLayoutMetaDataParser ( $view, $deployedModuleName ) ;
2155
                    $parser->addField ( array ( 'name' => $fieldName ) ) ;
2156
                    $parser->handleSave ( false ) ;
2157
                }
2158
            }
2159
        }
2160
2161
	}
2162
2163
	function removeFieldsFromLayout($layoutAdditions) {
2164
	require_once 'modules/ModuleBuilder/parsers/views/GridLayoutMetaDataParser.php' ;
2165
2166
        // these modules either lack editviews/detailviews or use custom mechanisms for the editview/detailview.
2167
        // In either case, we don't want to attempt to add a relate field to them
2168
        // would be better if GridLayoutMetaDataParser could handle this gracefully, so we don't have to maintain this list here
2169
        $invalidModules = array ( 'emails' , 'kbdocuments' ) ;
2170
2171
        foreach ( $layoutAdditions as $deployedModuleName => $fieldName )
2172
        {
2173
            if ( ! in_array( strtolower ( $deployedModuleName ) , $invalidModules ) )
2174
            {
2175
                foreach ( array ( MB_EDITVIEW , MB_DETAILVIEW ) as $view )
2176
                {
2177
                    $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . ": adding $fieldName to $view layout for module $deployedModuleName" ) ;
2178
                    $parser = new GridLayoutMetaDataParser ( $view, $deployedModuleName ) ;
2179
                    $parser->removeField ( $fieldName ) ;
2180
                    $parser->handleSave ( false ) ;
2181
                }
2182
            }
2183
        }
2184
2185
	}
2186
2187
	///////////////////
2188
	//********** DISABLE/ENABLE FUNCTIONS
2189
	///////////////////
2190
	function enable($base_dir, $is_upgrade = false, $previous_version = ''){
2191
		global $app_strings;
2192
		$this->base_dir = $base_dir;
2193
		$total_steps = 3; //minimum number of steps with no tasks
2194
		$current_step = 0;
2195
		$tasks = array(
2196
								'enable_copy',
2197
								'enable_dashlets',
2198
								'enable_relationships',
2199
		                        'enable_extensions',
2200
                                'enable_global_search',
2201
		                        'enable_manifest_logichooks',
2202
								'reset_opcodes',
2203
		);
2204
		$total_steps += count($tasks);
2205
		if(file_exists($this->base_dir . '/manifest.php')){
2206
				if(!$this->silent){
2207
					$current_step++;
2208
					display_progress_bar('install', $current_step, $total_steps);
2209
					echo '<div id ="displayLoglink" ><a href="#" onclick="toggleDisplay(\'displayLog\')">'.$app_strings['LBL_DISPLAY_LOG'].'</a> </div><div id="displayLog" style="display:none">';
2210
				}
2211
2212
				require_once($this->base_dir . '/manifest.php');
2213
				if($is_upgrade && !empty($previous_version)){
2214
					//check if the upgrade path exists
2215
					if(!empty($upgrade_manifest)){
0 ignored issues
show
The variable $upgrade_manifest seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
2216
						if(!empty($upgrade_manifest['upgrade_paths'])){
2217
							if(!empty($upgrade_manifest['upgrade_paths'][$previous_version])){
2218
								$installdefs = 	$upgrade_manifest['upgrade_paths'][$previous_version];
2219
							}else{
2220
								$errors[] = 'No Upgrade Path Found in manifest.';
2221
								$this->abort($errors);
2222
							}//fi
2223
						}//fi
2224
					}//fi
2225
				}//fi
2226
				$this->id_name = $installdefs['id'];
2227
				$this->installdefs = $installdefs;
2228
				$installed_modules = array();
2229
				if(isset($installdefs['beans'])){
2230
					foreach($this->installdefs['beans'] as $bean){
2231
						$installed_modules[] = $bean['module'];
2232
					}
2233
				}
2234
				if(!$this->silent){
2235
					$current_step++;
2236
					update_progress_bar('install', $current_step, $total_steps);
2237
				}
2238
2239
				foreach($tasks as $task){
2240
					$this->$task();
2241
					if(!$this->silent){
2242
						$current_step++;
2243
						update_progress_bar('install', $current_step, $total_steps);
2244
					}
2245
				}
2246
2247
				if(!$this->silent){
2248
					$current_step++;
2249
					update_progress_bar('install', $current_step, $total_steps);
2250
					echo '</div>';
2251
				}
2252
				UpdateSystemTabs('Add',$installed_modules);
2253
				$GLOBALS['log']->debug('Complete');
2254
2255
		}else{
2256
			die("No \$installdefs Defined In $this->base_dir/manifest.php");
2257
		}
2258
2259
	}
2260
	function disable($base_dir){
2261
		global $app_strings;
2262
		$total_steps = 3; //min steps with no tasks
2263
		$current_step = 0;
2264
		$this->base_dir = $base_dir;
2265
		$tasks = array(
2266
							'disable_copy',
2267
							'disable_dashlets',
2268
							'disable_relationships',
2269
		                    'disable_extensions',
2270
                            'disable_global_search',
2271
							'disable_manifest_logichooks',
2272
							'reset_opcodes',
2273
							);
2274
		$total_steps += count($tasks); //now the real number of steps
2275
		if(file_exists($this->base_dir . '/manifest.php')){
2276
				if(!$this->silent){
2277
					$current_step++;
2278
					display_progress_bar('install', $current_step, $total_steps);
2279
					echo '<div id ="displayLoglink" ><a href="#" onclick="toggleDisplay(\'displayLog\')">'.$app_strings['LBL_DISPLAY_LOG'].'</a> </div><div id="displayLog" style="display:none">';
2280
				}
2281
2282
				require_once($this->base_dir . '/manifest.php');
2283
				$this->installdefs = $installdefs;
2284
				$this->id_name = $this->installdefs['id'];
2285
				$installed_modules = array();
2286
				if(isset($this->installdefs['beans'])){
2287
					foreach($this->installdefs['beans'] as $bean){
2288
						$installed_modules[] = $bean['module'];
2289
					}
2290
				}
2291
				if(!$this->silent){
2292
					$current_step++;
2293
					update_progress_bar('install', $current_step, $total_steps);
2294
				}
2295
				foreach($tasks as $task){
2296
					$this->$task();
2297
					if(!$this->silent){
2298
						$current_step++;
2299
						update_progress_bar('install', $current_step, $total_steps);
2300
					}
2301
				}
2302
				if(!$this->silent){
2303
					$current_step++;
2304
					update_progress_bar('install', $current_step, $total_steps);
2305
					echo '</div>';
2306
				}
2307
			UpdateSystemTabs('Restore',$installed_modules);
2308
2309
		}else{
2310
			die("No manifest.php Defined In $this->base_dir/manifest.php");
2311
		}
2312
	}
2313
2314
	function enable_vardef($to_module)
2315
	{
2316
	    $this->enableExt("vardefs", "Vardefs", $to_module);
2317
	}
2318
2319
	function enable_layoutdef($to_module)
2320
	{
2321
	    $this->enableExt("layoutdefs", "Layoutdefs", $to_module);
2322
	}
2323
2324
	function enable_relationships(){
2325
		if(isset($this->installdefs['relationships'])){
2326
			$str = "<?php \n //WARNING: The contents of this file are auto-generated\n";
2327
			$save_table_dictionary = false;
2328
			foreach($this->installdefs['relationships'] as $relationship){
2329
                $filename	=basename($relationship['meta_data']);
2330
2331
				$save_table_dictionary  = true;
2332
				$str .= "include_once('metadata/$filename');\n";
2333
				if (empty($relationship['module']))
2334
				    continue;
2335
2336
				if(!empty($relationship['module_vardefs'])){
2337
					$this->enable_vardef($relationship['module']);
2338
				}
2339
				if(!empty($relationship['module_layoutdefs'])){
2340
					$this->enable_layoutdef($relationship['module']);
2341
				}
2342
			}
2343
			$this->rebuild_vardefs();
2344
			$this->rebuild_layoutdefs();
0 ignored issues
show
Documentation Bug introduced by
The method rebuild_layoutdefs does not exist on object<ModuleInstaller>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
2345
			if($save_table_dictionary){
2346
				if(!file_exists("custom/Extension/application/Ext/TableDictionary")){
2347
					mkdir_recursive("custom/Extension/application/Ext/TableDictionary", true);
2348
				}
2349
				if (file_exists("custom/Extension/application/Ext/TableDictionary/".DISABLED_PATH."/$this->id_name.php"))
2350
				   rename("custom/Extension/application/Ext/TableDictionary/".DISABLED_PATH."/$this->id_name.php", "custom/Extension/application/Ext/TableDictionary/$this->id_name.php");
2351
				$this->rebuild_tabledictionary();
2352
			}
2353
		}
2354
	}
2355
2356
	function disable_relationships($action = 'disable'){
2357
		if(isset($this->installdefs['relationships'])){
2358
			foreach($this->installdefs['relationships'] as $relationship){
2359
				$filename = basename($relationship['meta_data']);
2360
                $relName = substr($filename, -12) == "MetaData.php" ? substr($filename,0,strlen($filename) - 12) : "";
2361
				if (empty($relationship['module']) && empty($relName))
2362
                	continue;
2363
2364
				//remove the vardefs
2365
				if (empty($relName))
2366
					$path = 'custom/Extension/modules/' . $relationship['module']. '/Ext/Vardefs';
2367
				if(!empty($relationship['module']) && $relationship['module'] == 'application'){
2368
					$path ='custom/Extension/' . $relationship['module']. '/Ext/Vardefs';
2369
				}
2370
				if(!empty($relationship['module_vardefs']) && file_exists($path . '/'. $this->id_name . '.php')){
2371
					mkdir_recursive($path . '/'.DISABLED_PATH, true);
2372
					rename( $path . '/'. $this->id_name . '.php', $path . '/'.DISABLED_PATH.'/'. $this->id_name . '.php');
2373
				}
2374
				//remove the layoutdefs
2375
				if ( !empty($relationship['module']) ) {
2376
                    $path = 'custom/Extension/modules/' . $relationship['module']. '/Ext/Layoutdefs';
2377
                    if($relationship['module'] == 'application'){
2378
                        $path ='custom/Extension/' . $relationship['module']. '/Ext/Layoutdefs';
2379
                    }
2380
				}
2381
2382
				if(!empty($relationship['module_layoutdefs']) && file_exists($path . '/'. $this->id_name . '.php')){
2383
					mkdir_recursive($path . '/'.DISABLED_PATH, true);
2384
					rename( $path . '/'. $this->id_name . '.php', $path . '/'.DISABLED_PATH.'/'. $this->id_name . '.php');
2385
				}
2386
2387
			}
2388
			if(file_exists("custom/Extension/application/Ext/TableDictionary/$this->id_name.php")){
2389
				mkdir_recursive("custom/Extension/application/Ext/TableDictionary/".DISABLED_PATH, true);
2390
				rename("custom/Extension/application/Ext/TableDictionary/$this->id_name.php", "custom/Extension/application/Ext/TableDictionary/".DISABLED_PATH."/$this->id_name.php");
2391
			}
2392
			$this->rebuild_tabledictionary();
2393
			$this->rebuild_vardefs();
2394
			$this->rebuild_layoutdefs();
0 ignored issues
show
Documentation Bug introduced by
The method rebuild_layoutdefs does not exist on object<ModuleInstaller>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
2395
		}
2396
	}
2397
2398
	function enable_dashlets(){
2399
		if(isset($this->installdefs['dashlets'])){
2400
			foreach($this->installdefs['dashlets'] as $cp){
2401
				$cp['from'] = str_replace('<basepath>', $this->base_dir, $cp['from']);
2402
				$path = 'custom/modules/Home/Dashlets/' . $cp['name'] . '/';
2403
				$disabled_path = 'custom/modules/Home/'.DISABLED_PATH.'Dashlets/' . $cp['name'];
2404
				$GLOBALS['log']->debug("Enabling Dashlet " . $cp['name'] . "..." . $cp['from'] );
2405
				if (file_exists($disabled_path))
2406
				{
2407
					rename($disabled_path,  $path);
2408
				}
2409
			}
2410
			include('modules/Administration/RebuildDashlets.php');
2411
2412
		}
2413
	}
2414
2415
	function disable_dashlets(){
2416
		if(isset($this->installdefs['dashlets'])){
2417
					foreach($this->installdefs['dashlets'] as $cp){
2418
						$path = 'custom/modules/Home/Dashlets/' . $cp['name'];
2419
						$disabled_path = 'custom/modules/Home/'.DISABLED_PATH.'Dashlets/' . $cp['name'];
2420
						$GLOBALS['log']->debug('Disabling ' .$path);
2421
						if (file_exists($path))
2422
						{
2423
							mkdir_recursive('custom/modules/Home/'.DISABLED_PATH.'Dashlets/', true);
2424
							rename( $path, $disabled_path);
2425
						}
2426
					}
2427
					include('modules/Administration/RebuildDashlets.php');
2428
				}
2429
	}
2430
2431
	function enable_copy(){
2432
		//copy files back onto file system. first perform md5 check to determine if anything has been modified
2433
		//here we should just go through the files in the -restore directory and copy those back
2434
		if(isset($GLOBALS['mi_overwrite_files']) && $GLOBALS['mi_overwrite_files']){
2435
			if(!empty($this->installdefs['copy'])){
2436
				foreach($this->installdefs['copy'] as $cp){
2437
					$cp['to'] = clean_path(str_replace('<basepath>', $this->base_dir, $cp['to']));
2438
					$backup_path = clean_path( remove_file_extension(urldecode(hashToFile($_REQUEST['install_file'])))."-restore/".$cp['to'] );
2439
2440
					//check if this file exists in the -restore directory
2441
					if(file_exists($backup_path)){
2442
						//since the file exists, then we want do an md5 of the install version and the file system version
2443
						//if(is_file($backup_path) && md5_file($backup_path) == md5_file($cp['to'])){
2444
							//since the files are the same then we can safely move back from the -restore
2445
							//directory into the file system
2446
							$GLOBALS['log']->debug("ENABLE COPY:: FROM: ".$cp['from']. " TO: ".$cp['to']);
2447
							$this->copy_path($cp['from'], $cp['to']);
2448
						/*}else{
2449
							//since they are not equal then we need to prompt the user
2450
						}*/
2451
					}//fi
2452
				}//rof
2453
			}//fi
2454
		}//fi
2455
	}
2456
2457
	function disable_copy(){
2458
		//when we disable we want to copy the -restore files back into the file system
2459
		//but we should check the version in the module install against the version on the file system
2460
		//if they match then we can copy the file back, but otherwise we should ask the user.
2461
2462
//		$GLOBALS['log']->debug('ModuleInstaller.php->disable_copy()');
2463
		if(isset($GLOBALS['mi_overwrite_files']) && $GLOBALS['mi_overwrite_files']){
2464
//		$GLOBALS['log']->debug('ModuleInstaller.php->disable_copy():mi_overwrite_files=true');
2465
			if(!empty($this->installdefs['copy'])){
2466
//				$GLOBALS['log']->debug('ModuleInstaller.php->disable_copy(): installdefs not empty');
2467
				foreach($this->installdefs['copy'] as $cp){
2468
					$cp['to'] = clean_path(str_replace('<basepath>', $this->base_dir, $cp['to']));
2469
					$backup_path = clean_path( remove_file_extension(urldecode(hashToFile($_REQUEST['install_file'])))."-restore/".$cp['to'] ); // bug 16966 tyoung - replaced missing assignment to $backup_path
2470
					//check if this file exists in the -restore directory
2471
//					$GLOBALS['log']->debug("ModuleInstaller.php->disable_copy(): backup_path=".$backup_path);
2472
					if(file_exists($backup_path)){
2473
						//since the file exists, then we want do an md5 of the install version and the file system version
2474
						$from = str_replace('<basepath>', $this->base_dir, $cp['from']);
2475
2476
						//if(is_file($from) && md5_file($from) == md5_file($cp['to'])){
2477
							//since the files are the same then we can safely move back from the -restore
2478
							//directory into the file system
2479
							$GLOBALS['log']->debug("DISABLE COPY:: FROM: ".$backup_path. " TO: ".$cp['to']);
2480
							$this->copy_path($backup_path, $cp['to']);
2481
						/*}else{
2482
							//since they are not equal then we need to prompt the user
2483
						}*/
2484
					}//fi
2485
				}//rof
2486
			}//fi
2487
		}//fi
2488
	}
2489
2490
	public function reset_opcodes()
2491
    {
2492
        /* Bug 39354 - added function_exists check. Not optimal fix, but safe nonetheless.
2493
         * 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,
2494
         * but this file has been updated to 6.1
2495
         */
2496
        if(function_exists('sugar_clean_opcodes')){
2497
            sugar_clean_opcodes();
2498
        }
2499
    }
2500
2501
    /**
2502
     * BC implementation to provide specific calls to extensions
2503
     */
2504
    public function __call($name, $args)
2505
    {
2506
        $nameparts = explode('_', $name);
2507
        // name is something_something
2508
        if(count($nameparts) == 2 && isset($this->extensions[$nameparts[1]])) {
2509
            $ext = $this->extensions[$nameparts[1]];
2510
            switch($nameparts[0]) {
2511
                case 'enable':
2512
                    return $this->enableExt($ext['section'], $ext['extdir']);
2513
                case 'disable':
2514
                    return $this->disableExt($ext['section'], $ext['extdir']);
2515
                case 'install':
2516
                    return $this->installExt($ext['section'], $ext['extdir']);
2517
                case 'uninstall':
2518
                    return $this->uninstallExt($ext['section'], $ext['extdir']);
2519
                case 'rebuild':
2520
                    return $this->rebuildExt($ext['extdir'], $ext['file']);
2521
            }
2522
        }
2523
        sugar_die("Unknown method ModuleInstaller::$name called");
2524
    }
2525
2526
}
2527
2528
    function UpdateSystemTabs($action, $installed_modules){
2529
        require_once("modules/MySettings/TabController.php");
2530
        $controller = new TabController();
2531
        $isSystemTabsInDB = $controller->is_system_tabs_in_db();
2532
        if ($isSystemTabsInDB && !empty($installed_modules))
2533
        {
2534
            global $moduleList;
2535
            switch ($action)
2536
            {
2537
                case 'Restore' :
0 ignored issues
show
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
2538
                    $currentTabs = $controller->get_system_tabs();
2539
                    foreach ($installed_modules as $module)
2540
                    {
2541
                        if(in_array($module, $currentTabs)){
2542
                            unset($currentTabs[$module]);
2543
                        }
2544
                    }
2545
                    $controller->set_system_tabs($currentTabs);;
2546
                    break;
2547
                case 'Add' :
0 ignored issues
show
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
2548
                    $currentTabs = $controller->get_system_tabs();
2549
                    foreach ($installed_modules as $module)
2550
                    {
2551
                        if(!in_array($module, $currentTabs)){
2552
                            $currentTabs[$module] = $module;
2553
                        }
2554
                    }
2555
                    $controller->set_system_tabs($currentTabs);
2556
                default:
2557
                    break;
2558
            }
2559
    }
2560
2561
}
2562