EmailUI::_generateComposeConfigData()   D
last analyzed

Complexity

Conditions 10
Paths 144

Size

Total Lines 92
Code Lines 60

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 110
Metric Value
cc 10
eloc 60
nc 144
nop 1
dl 0
loc 92
ccs 0
cts 57
cp 0
crap 110
rs 4.8407

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2 1
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
 * Description:
44
 * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc. All Rights
45
 * Reserved. Contributor(s): ______________________________________..
46
 *********************************************************************************/
47
48 1
require_once("include/ytree/Tree.php");
49 1
require_once("include/ytree/ExtNode.php");
50 1
require_once("include/SugarFolders/SugarFolders.php");
51
52
53
54
class EmailUI {
55
	var $db;
56
	var $folder; // place holder for SugarFolder object
57
	var $folderStates = array(); // array of folderPath names and their states (1/0)
58
	var $smarty;
59
	var $addressSeparators = array(";", ",");
60
	var $rolloverStyle = "<style>div#rollover {position: relative;float: left;margin: none;text-decoration: none;}div#rollover a:hover {padding: 0;}div#rollover a span {display: none;}div#rollover a:hover span {text-decoration: none;display: block;width: 250px;margin-top: 5px;margin-left: 5px;position: absolute;padding: 10px;color: #333;	border: 1px solid #ccc;	background-color: #fff;	font-size: 12px;z-index: 1000;}</style>\n";
61
	var $groupCss = "<span class='groupInbox'>";
62
	var $cacheTimeouts = array(
63
		'messages'		=> 86400,	// 24 hours
64
		'folders'		=> 300,		// 5 mins
65
		'attachments'	=> 86400,	// 24 hours
66
	);
67
	var $userCacheDir = '';
68
	var $coreDynamicFolderQuery = "SELECT emails.id polymorphic_id, 'Emails' polymorphic_module FROM emails
69
								   JOIN emails_text on emails.id = emails_text.email_id
70
                                   WHERE (type = '::TYPE::' OR status = '::STATUS::') AND assigned_user_id = '::USER_ID::' AND emails.deleted = '0'";
71
72
	/**
73
	 * Sole constructor
74
	 */
75 16
	function __construct() {
76 16
		global $sugar_config;
77 16
		global $current_user;
78
79 16
		$folderStateSerial = $current_user->getPreference('folderOpenState', 'Emails');
80
81 16
		if(!empty($folderStateSerial)) {
82
			$this->folderStates = unserialize($folderStateSerial);
83
		}
84
85 16
		$this->smarty = new Sugar_Smarty();
86 16
		$this->folder = new SugarFolder();
87 16
		$this->userCacheDir = sugar_cached("modules/Emails/{$current_user->id}");
88 16
		$this->db = DBManagerFactory::getInstance();
89 16
	}
90
91
	/**
92
	 * @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
93
	 */
94
	function EmailUI(){
95
		$deprecatedMessage = 'PHP4 Style Constructors are deprecated and will be remove in 7.8, please update your code';
96
		if(isset($GLOBALS['log'])) {
97
			$GLOBALS['log']->deprecated($deprecatedMessage);
98
		}
99
		else {
100
			trigger_error($deprecatedMessage, E_USER_DEPRECATED);
101
		}
102
		self::__construct();
103
	}
104
105
	///////////////////////////////////////////////////////////////////////////
106
	////	CORE
107
	/**
108
	 * Renders the frame for emails
109
	 */
110
	function displayEmailFrame() {
111
112
		require_once("include/OutboundEmail/OutboundEmail.php");
113
114
		global $app_strings, $app_list_strings;
115
		global $mod_strings;
116
		global $sugar_config;
117
		global $current_user;
118
		global $locale;
119
		global $timedate;
120
		global $theme;
121
		global $sugar_version;
122
		global $sugar_flavor;
123
		global $current_language;
124
		global $server_unique_key;
125
126
		$this->preflightUserCache();
127
		$ie = new InboundEmail();
128
129
		// focus listView
130
		$list = array(
131
			'mbox' => 'Home',
132
			'ieId' => '',
133
			'name' => 'Home',
134
			'unreadChecked' => 0,
135
			'out' => array(),
136
		);
137
138
		$this->_generateComposeConfigData('email_compose');
139
140
141
        //Check quick create module access
142
        $QCAvailableModules = $this->_loadQuickCreateModules();
143
144
        //Get the quickSearch js needed for assigned user id on Search Tab
145
        require_once('include/QuickSearchDefaults.php');
146
        $qsd = QuickSearchDefaults::getQuickSearchDefaults();
147
        $qsd->setFormName('advancedSearchForm');
148
        $quicksearchAssignedUser = "if(typeof sqs_objects == 'undefined'){var sqs_objects = new Array;}";
149
        $quicksearchAssignedUser .= "sqs_objects['advancedSearchForm_assigned_user_name']=" . json_encode($qsd->getQSUser()) . ";";
150
        $qsd->setFormName('Distribute');
151
        $quicksearchAssignedUser .= "sqs_objects['Distribute_assigned_user_name']=" . json_encode($qsd->getQSUser()) . ";";
152
        $this->smarty->assign('quickSearchForAssignedUser', $quicksearchAssignedUser);
153
154
155
		///////////////////////////////////////////////////////////////////////
156
		////	BASIC ASSIGNS
157
		$this->smarty->assign("currentUserId",$current_user->id);
158
		$this->smarty->assign("CURRENT_USER_EMAIL",$current_user->email1);
159
        $this->smarty->assign("currentUserName",$current_user->name);
160
		$this->smarty->assign('yuiPath', 'modules/Emails/javascript/yui-ext/');
161
		$this->smarty->assign('app_strings', $app_strings);
162
		$this->smarty->assign('mod_strings', $mod_strings);
163
		$this->smarty->assign('theme', $theme);
164
		$this->smarty->assign('sugar_config', $sugar_config);
165
		$this->smarty->assign('is_admin', $current_user->is_admin);
166
		$this->smarty->assign('sugar_version', $sugar_version);
167
		$this->smarty->assign('sugar_flavor', $sugar_flavor);
168
		$this->smarty->assign('current_language', $current_language);
169
		$this->smarty->assign('server_unique_key', $server_unique_key);
170
		$this->smarty->assign('qcModules', json_encode($QCAvailableModules));
171
		$extAllDebugValue = "ext-all.js";
172
		$this->smarty->assign('extFileName', $extAllDebugValue);
173
174
		// settings: general
175
		$e2UserPreferences = $this->getUserPrefsJS();
176
		$emailSettings = $e2UserPreferences['emailSettings'];
177
178
		///////////////////////////////////////////////////////////////////////
179
		////	USER SETTINGS
180
		// settings: accounts
181
182
		$cuDatePref = $current_user->getUserDateTimePreferences();
183
		$this->smarty->assign('dateFormat', $cuDatePref['date']);
184
		$this->smarty->assign('dateFormatExample', str_replace(array("Y", "m", "d"), array("yyyy", "mm", "dd"), $cuDatePref['date']));
185
		$this->smarty->assign('calFormat', $timedate->get_cal_date_format());
186
        $this->smarty->assign('TIME_FORMAT', $timedate->get_user_time_format());
187
188
		$ieAccounts = $ie->retrieveByGroupId($current_user->id);
189
		$ieAccountsOptions = "<option value=''>{$app_strings['LBL_NONE']}</option>\n";
190
191
		foreach($ieAccounts as $k => $v) {
192
			$disabled = (!$v->is_personal) ? "DISABLED" : "";
193
			$group = (!$v->is_personal) ? $app_strings['LBL_EMAIL_GROUP']."." : "";
194
			$ieAccountsOptions .= "<option value='{$v->id}' $disabled>{$group}{$v->name}</option>\n";
195
		}
196
197
		$this->smarty->assign('ieAccounts', $ieAccountsOptions);
198
		$this->smarty->assign('rollover', $this->rolloverStyle);
199
200
		$protocol = filterInboundEmailPopSelection($app_list_strings['dom_email_server_type']);
201
		$this->smarty->assign('PROTOCOL', get_select_options_with_id($protocol, ''));
202
		$this->smarty->assign('MAIL_SSL_OPTIONS', get_select_options_with_id($app_list_strings['email_settings_for_ssl'], ''));
203
		$this->smarty->assign('ie_mod_strings', return_module_language($current_language, 'InboundEmail'));
204
205
		$charsetSelectedValue = isset($emailSettings['defaultOutboundCharset']) ? $emailSettings['defaultOutboundCharset'] : false;
206
		if (!$charsetSelectedValue) {
207
			$charsetSelectedValue = $current_user->getPreference('default_export_charset', 'global');
208
			if (!$charsetSelectedValue) {
209
				$charsetSelectedValue = $locale->getPrecedentPreference('default_email_charset');
210
			}
211
		}
212
		$charset = array(
213
			'options' => $locale->getCharsetSelect(),
214
			'selected' => $charsetSelectedValue,
215
		);
216
		$this->smarty->assign('charset', $charset);
217
218
		$emailCheckInterval = array('options' => $app_strings['LBL_EMAIL_CHECK_INTERVAL_DOM'], 'selected' => $emailSettings['emailCheckInterval']);
219
		$this->smarty->assign('emailCheckInterval', $emailCheckInterval);
220
		$this->smarty->assign('attachmentsSearchOptions', $app_list_strings['checkbox_dom']);
221
		$this->smarty->assign('sendPlainTextChecked', ($emailSettings['sendPlainText'] == 1) ? 'CHECKED' : '');
222
		$this->smarty->assign('showNumInList', get_select_options_with_id($app_list_strings['email_settings_num_dom'], $emailSettings['showNumInList']));
223
224
		////	END USER SETTINGS
225
		///////////////////////////////////////////////////////////////////////
226
227
		///////////////////////////////////////////////////////////////////////
228
		////	SIGNATURES
229
		$prependSignature = ($current_user->getPreference('signature_prepend')) ? 'true' : 'false';
230
		$defsigID = $current_user->getPreference('signature_default');
231
		$this->smarty->assign('signatures', $current_user->getSignatures(false, $defsigID));
232
		$this->smarty->assign('signaturesSettings', $current_user->getSignatures(false, $defsigID, false));
233
		$signatureButtons = $current_user->getSignatureButtons('SUGAR.email2.settings.createSignature', !empty($defsigID));
234
		if (!empty($defsigID)) {
235
			$signatureButtons = $signatureButtons . '<span name="delete_sig" id="delete_sig" style="visibility:inherit;"><input class="button" onclick="javascript:SUGAR.email2.settings.deleteSignature();" value="'.$app_strings['LBL_EMAIL_DELETE'].'" type="button" tabindex="392">&nbsp;
236
					</span>';
237
		} else {
238
			$signatureButtons = $signatureButtons . '<span name="delete_sig" id="delete_sig" style="visibility:hidden;"><input class="button" onclick="javascript:SUGAR.email2.settings.deleteSignature();" value="'.$app_strings['LBL_EMAIL_DELETE'].'" type="button" tabindex="392">&nbsp;
239
					</span>';
240
		}
241
		$this->smarty->assign('signatureButtons', $signatureButtons);
242
		$this->smarty->assign('signaturePrepend', $prependSignature == 'true' ? 'CHECKED' : '');
243
		////	END SIGNATURES
244
		///////////////////////////////////////////////////////////////////////
245
246
		///////////////////////////////////////////////////////////////////////
247
		////	EMAIL TEMPLATES
248
		$email_templates_arr = $this->getEmailTemplatesArray();
249
		natcasesort($email_templates_arr);
250
		$this->smarty->assign('EMAIL_TEMPLATE_OPTIONS', get_select_options_with_id($email_templates_arr, ''));
251
		////	END EMAIL TEMPLATES
252
		///////////////////////////////////////////////////////////////////////
253
254
		///////////////////////////////////////////////////////////////////////
255
		////	FOLDERS & TreeView
256
		$this->smarty->assign('groupUserOptions', $ie->getGroupsWithSelectOptions(array('' => $app_strings['LBL_EMAIL_CREATE_NEW'])));
257
258
		$tree = $this->getMailboxNodes();
259
260
		// preloaded folder
261
		$preloadFolder = 'lazyLoadFolder = ';
262
		$focusFolderSerial = $current_user->getPreference('focusFolder', 'Emails');
263
		if(!empty($focusFolderSerial)) {
264
			$focusFolder = unserialize($focusFolderSerial);
265
			//$focusFolder['ieId'], $focusFolder['folder']
266
			$preloadFolder .= json_encode($focusFolder).";";
267
		} else {
268
			$preloadFolder .= "new Object();";
269
		}
270
		////	END FOLDERS
271
		///////////////////////////////////////////////////////////////////////
272
273
		$out = "";
274
		$out .= $this->smarty->fetch("modules/Emails/templates/_baseEmail.tpl");
275
		$out .= $tree->generate_header();
276
		$out .= $tree->generateNodesNoInit(true, 'email2treeinit');
277
		$out .=<<<eoq
278
			<script type="text/javascript" language="javascript">
279
280
				var loader = new YAHOO.util.YUILoader({
281
				    require : [
282
				    	"layout", "element", "tabview", "menu",
283
				    	"cookie", "sugarwidgets"
284
				    ],
285
				    loadOptional: true,
286
				    skin: { base: 'blank', defaultSkin: '' },
287
				    onSuccess: email2init,
288
				    allowRollup: true,
289
				    base: "include/javascript/yui/build/"
290
				});
291
				loader.addModule({
292
				    name :"sugarwidgets",
293
				    type : "js",
294
				    fullpath: "include/javascript/sugarwidgets/SugarYUIWidgets.js",
295
				    varName: "YAHOO.SUGAR",
296
				    requires: ["datatable", "dragdrop", "treeview", "tabview", "calendar"]
297
				});
298
				loader.insert();
299
300
				{$preloadFolder};
301
302
			</script>
303
eoq;
304
305
306
		return $out;
307
	}
308
309
	/**
310
	 * Generate the frame needed for the quick compose email UI.  This frame is loaded dynamically
311
	 * by an ajax call.
312
	 *
313
	 * @return JSON An object containing html markup and js script variables.
314
	 */
315
	function displayQuickComposeEmailFrame()
316
	{
317
        $this->preflightUserCache();
318
319
	    $this->_generateComposeConfigData('email_compose_light');
320
		$javascriptOut = $this->smarty->fetch("modules/Emails/templates/_baseConfigData.tpl");
321
322
        $divOut = $this->smarty->fetch("modules/Emails/templates/overlay.tpl");
323
        $divOut .= $this->smarty->fetch("modules/Emails/templates/addressSearchContent.tpl");
324
325
        $outData = array('jsData' => $javascriptOut,'divData'=> $divOut);
326
        $out = json_encode($outData);
327
        return $out;
328
    }
329
330
    /**
331
     * Load the modules from the metadata file and include in a custom one if it exists
332
     *
333
     * @return array
334
     */
335
    protected function _loadQuickCreateModules()
336
    {
337
        $QCAvailableModules = array();
338
        $QCModules = array();
339
340
        include('modules/Emails/metadata/qcmodulesdefs.php');
341
        if (file_exists('custom/modules/Emails/metadata/qcmodulesdefs.php')) {
342
            include('custom/modules/Emails/metadata/qcmodulesdefs.php');
343
        }
344
345
        foreach($QCModules as $module) {
346
            $seed = SugarModule::get($module)->loadBean();
347
            if ( ( $seed instanceOf SugarBean ) && $seed->ACLAccess('edit') ) {
348
                $QCAvailableModules[] = $module;
349
            }
350
        }
351
352
        return $QCAvailableModules;
353
    }
354
355
    /**
356
     * Given an email link url (eg. index.php?action=Compose&parent_type=Contacts...) break up the
357
     * request components and create a compose package that can be used by the quick compose UI. The
358
     * result is typically passed into the js call SUGAR.quickCompose.init which initalizes the quick compose
359
     * UI.
360
     *
361
     * @param String $emailLinkUrl
362
     * @return JSON Object containing the composePackage and full link url
363
     */
364 11
    function generateComposePackageForQuickCreateFromComposeUrl($emailLinkUrl, $lazyLoad=false)
365
    {
366 11
        $composeData = explode("&",$emailLinkUrl);
367 11
        $a_composeData = array();
368 11
    	foreach ($composeData as $singleRequest)
369
    	{
370 11
    		$tmp = explode("=",$singleRequest);
371 11
    		$a_composeData[$tmp[0]] = urldecode($tmp[1]);
372
    	}
373
374 11
    	return $this->generateComposePackageForQuickCreate($a_composeData,$emailLinkUrl, $lazyLoad);
375
    }
376
    /**
377
     * Generate the composePackage for the quick compose email UI.  The package contains
378
     * key/value pairs generated by the Compose.php file which are then set into the
379
     * quick compose email UI (eg. to addr, parent id, parent type, etc)
380
     *
381
     * @param Array $composeData Associative array read and processed by generateComposeDataPackage.
382
     * @param String $fullLinkUrl A link that contains all pertinant information so the user can be
383
     *                              directed to the full compose screen if needed
384
     * @param SugarBean $bean Optional - the parent object bean with data
385
     * @return JSON Object containg composePackage and fullLinkUrl
386
     */
387 11
    function generateComposePackageForQuickCreate($composeData,$fullLinkUrl, $lazyLoad=false, $bean = null)
388
    {
389 11
        $_REQUEST['forQuickCreate'] = true;
390
391 11
        if(!$lazyLoad){
392
    	    require_once('modules/Emails/Compose.php');
393
    	    $composePackage = generateComposeDataPackage($composeData,FALSE, $bean);
0 ignored issues
show
Unused Code introduced by
The call to generateComposeDataPackage() has too many arguments starting with $bean.

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...
394
        }else{
395 11
            $composePackage = $composeData;
396
        }
397
398
    	// JSON object is passed into the function defined within the a href onclick event
399
    	// which is delimeted by '.  Need to escape all single quotes and &, <, >
400
    	// but not double quotes since json would escape them
401 11
    	foreach ($composePackage as $key => $singleCompose)
0 ignored issues
show
Bug introduced by
The expression $composePackage of type null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
402
    	{
403 11
    	   if (is_string($singleCompose))
404 11
    	       $composePackage[$key] = str_replace("&nbsp;", " ", from_html($singleCompose));
405
    	}
406
407 11
    	$quickComposeOptions = array('fullComposeUrl' => $fullLinkUrl,'composePackage' => $composePackage);
408
409 11
    	$j_quickComposeOptions = JSON::encode($quickComposeOptions, false ,true);
410
411
412 11
    	return $j_quickComposeOptions;
413
    }
414
415
    /**
416
     * Generate the config data needed for the Full Compose UI and the Quick Compose UI.  The set of config data
417
     * returned is the minimum set needed by the quick compose UI.
418
     *
419
     * @param String $type Drives which tinyMCE options will be included.
420
     */
421
	function _generateComposeConfigData($type = "email_compose_light" )
422
	{
423
		global $app_list_strings,$current_user, $app_strings, $mod_strings,$current_language,$locale;
424
425
		//Link drop-downs
426
		$parent_types = $app_list_strings['record_type_display'];
427
		$disabled_parent_types = ACLController::disabledModuleList($parent_types, false, 'list');
428
429
		foreach($disabled_parent_types as $disabled_parent_type) {
430
		  unset($parent_types[$disabled_parent_type]);
431
		}
432
		asort($parent_types);
433
		$linkBeans = json_encode(get_select_options_with_id($parent_types, ''));
434
435
		//TinyMCE Config
436
		require_once("include/SugarTinyMCE.php");
437
        $tiny = new SugarTinyMCE();
438
        $tinyConf = $tiny->getConfig($type);
439
440
        //Generate Language Packs
441
		$lang = "var app_strings = new Object();\n";
442
		foreach($app_strings as $k => $v) {
443
			if(strpos($k, 'LBL_EMAIL_') !== false) {
444
				$lang .= "app_strings.{$k} = '{$v}';\n";
445
			}
446
		}
447
		//Get the email mod strings but don't use the global variable as this may be overridden by
448
		//other modules when the quick create is rendered.
449
		$email_mod_strings = return_module_language($current_language,'Emails');
450
		$modStrings = "var mod_strings = new Object();\n";
451
		foreach($email_mod_strings as $k => $v) {
0 ignored issues
show
Bug introduced by
The expression $email_mod_strings of type array|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
452
			$v = str_replace("'", "\'", $v);
453
			$modStrings .= "mod_strings.{$k} = '{$v}';\n";
454
		}
455
		$lang .= "\n\n{$modStrings}\n";
456
457
		//Grab the Inboundemail language pack
458
		$ieModStrings = "var ie_mod_strings = new Object();\n";
459
		$ie_mod_strings = return_module_language($current_language,'InboundEmail');
460
		foreach($ie_mod_strings as $k => $v) {
0 ignored issues
show
Bug introduced by
The expression $ie_mod_strings of type array|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
461
			$v = str_replace("'", "\'", $v);
462
			$ieModStrings .= "ie_mod_strings.{$k} = '{$v}';\n";
463
		}
464
		$lang .= "\n\n{$ieModStrings}\n";
465
466
		$this->smarty->assign('linkBeans', $linkBeans);
467
        $this->smarty->assign('linkBeansOptions', $parent_types);
468
        $this->smarty->assign('tinyMCE', $tinyConf);
469
        $this->smarty->assign('lang', $lang);
470
        $this->smarty->assign('app_strings', $app_strings);
471
		$this->smarty->assign('mod_strings', $email_mod_strings);
472
        $ie1 = new InboundEmail();
473
474
        //Signatures
475
        $defsigID = $current_user->getPreference('signature_default');
476
		$defaultSignature = $current_user->getDefaultSignature();
477
		$sigJson = !empty($defaultSignature) ? json_encode(array($defaultSignature['id'] => from_html($defaultSignature['signature_html']))) : "new Object()";
478
		$this->smarty->assign('defaultSignature', $sigJson);
479
		$this->smarty->assign('signatureDefaultId', (isset($defaultSignature['id'])) ? $defaultSignature['id'] : "");
480
		//User Preferences
481
		$this->smarty->assign('userPrefs', json_encode($this->getUserPrefsJS()));
482
483
		//Get the users default outbound id
484
		$defaultOutID = $ie1->getUsersDefaultOutboundServerId($current_user);
485
		$this->smarty->assign('defaultOutID', $defaultOutID);
486
487
		//Character Set
488
		$charsets = json_encode($locale->getCharsetSelect());
489
		$this->smarty->assign('emailCharsets', $charsets);
490
491
		//Relateable List of People for address book search
492
		//#20776 jchi
493
		$peopleTables = array("users",
494
		                      "contacts",
495
		                      "leads",
496
		                      "prospects",
497
		                      "accounts");
498
		$filterPeopleTables = array();
499
		global $app_list_strings, $app_strings;
500
		$filterPeopleTables['LBL_DROPDOWN_LIST_ALL'] = $app_strings['LBL_DROPDOWN_LIST_ALL'];
501
		foreach($peopleTables as $table) {
502
			$module = ucfirst($table);
503
            $class = substr($module, 0, strlen($module) - 1);
504
            require_once("modules/{$module}/{$class}.php");
505
            $person = new $class();
506
507
            if (!$person->ACLAccess('list')) continue;
508
            $filterPeopleTables[$person->table_name] = $app_list_strings['moduleList'][$person->module_dir];
509
		}
510
		$this->smarty->assign('listOfPersons' , get_select_options_with_id($filterPeopleTables,''));
511
512
	}
513
514
515
516
	////	END CORE
517
	///////////////////////////////////////////////////////////////////////////
518
519
	///////////////////////////////////////////////////////////////////////////
520
	////	ADDRESS BOOK
521
	/**
522
	 * Retrieves all relationship metadata for a user's address book
523
	 * @return array
524
	 */
525
	function getContacts() {
526
		global $current_user;
527
528
		$q = "SELECT * FROM address_book WHERE assigned_user_id = '{$current_user->id}' ORDER BY bean DESC";
529
		$r = $this->db->query($q);
530
531
		$ret = array();
532
533
		while($a = $this->db->fetchByAssoc($r)) {
534
			$ret[$a['bean_id']] = array(
535
				'id'		=> $a['bean_id'],
536
				'module'	=> $a['bean'],
537
			);
538
		}
539
540
		return $ret;
541
	}
542
543
	/**
544
	 * Saves changes to a user's address book
545
	 * @param array contacts
546
	 */
547
	function setContacts($contacts) {
548
		global $current_user;
549
550
		$oldContacts = $this->getContacts();
551
552
		foreach($contacts as $cid => $contact) {
553
			if(!in_array($contact['id'], $oldContacts)) {
554
				$q = "INSERT INTO address_book (assigned_user_id, bean, bean_id) VALUES ('{$current_user->id}', '{$contact['module']}', '{$contact['id']}')";
555
				$r = $this->db->query($q, true);
556
			}
557
		}
558
	}
559
560
	/**
561
	 * Removes contacts from the user's address book
562
	 * @param array ids
563
	 */
564
	function removeContacts($ids) {
565
		global $current_user;
566
567
		$concat = "";
568
569
		foreach($ids as $id) {
570
			if(!empty($concat))
571
				$concat .= ", ";
572
573
			$concat .= "'{$id}'";
574
		}
575
576
		$q = "DELETE FROM address_book WHERE assigned_user_id = '{$current_user->id}' AND bean_id IN ({$concat})";
577
		$r = $this->db->query($q);
578
	}
579
580
	/**
581
	 * saves editted Contact info
582
	 * @param string $str JSON serialized object
583
	 */
584
	function saveContactEdit($str) {
585
586
		$json = getJSONobj();
587
588
		$str = from_html($str);
589
		$obj = $json->decode($str);
590
591
		$contact = new Contact();
592
		$contact->retrieve($obj['contact_id']);
593
		$contact->first_name = $obj['contact_first_name'];
594
		$contact->last_name = $obj['contact_last_name'];
595
		$contact->save();
596
597
		// handle email address changes
598
		$addresses = array();
599
600
		foreach($obj as $k => $req) {
0 ignored issues
show
Bug introduced by
The expression $obj of type string is not traversable.
Loading history...
601
			if(strpos($k, 'emailAddress') !== false) {
602
				$addresses[$k] = $req;
603
			}
604
		}
605
606
		// prefill some REQUEST vars for emailAddress save
607
		$_REQUEST['emailAddressOptOutFlag'] = $obj['optOut'];
608
		$_REQUEST['emailAddressInvalidFlag'] = $obj['invalid'];
609
		$contact->emailAddress->save($obj['contact_id'], 'Contacts', $addresses, $obj['primary'], '');
610
	}
611
612
	/**
613
	 * Prepares the Edit Contact mini-form via template assignment
614
	 * @param string id ID of contact in question
615
	 * @param string module Module in focus
616
	 * @return array
617
	 */
618
	function getEditContact($id, $module) {
619
		global $app_strings;
620
621
622
		if(!class_exists("Contact")) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
623
624
		}
625
626
		$contact = new Contact();
627
		$contact->retrieve($_REQUEST['id']);
628
		$ret = array();
629
630
		if($contact->ACLAccess('edit')) {
631
			$contactMeta = array();
632
			$contactMeta['id'] = $contact->id;
633
			$contactMeta['module'] = $contact->module_dir;
634
			$contactMeta['first_name'] = $contact->first_name;
635
			$contactMeta['last_name'] = $contact->last_name;
636
637
			$this->smarty->assign("app_strings", $app_strings);
638
			$this->smarty->assign("contact_strings", return_module_language($_SESSION['authenticated_user_language'], 'Contacts'));
639
			$this->smarty->assign("contact", $contactMeta);
640
641
			$ea = new SugarEmailAddress();
642
			$newEmail = $ea->getEmailAddressWidgetEditView($id, $module, true);
643
			$this->smarty->assign("emailWidget", $newEmail['html']);
644
645
			$ret['form'] = $this->smarty->fetch("modules/Emails/templates/editContact.tpl");
646
			$ret['prefillData'] = $newEmail['prefillData'];
647
		} else {
648
			$id = "";
649
			$ret['form'] = $app_strings['LBL_EMAIL_ERROR_NO_ACCESS'];
650
			$ret['prefillData'] = '{}';
651
		}
652
653
		$ret['id'] = $id;
654
		$ret['contactName'] = $contact->full_name;
655
656
		return $ret;
657
	}
658
659
660
	/**
661
	 * Retrieves a concatenated list of contacts, those with assigned_user_id = user's id and those in the address_book
662
	 * table
663
	 * @param array $contacts Array of contact types -> IDs
664
	 * @param object $user User in focus
665
	 * @return array
666
	 */
667
	function getUserContacts($contacts, $user=null) {
668
669
		global $current_user;
670
		global $locale;
671
672
		if(empty($user)) {
673
			$user = $current_user;
674
		}
675
676
		$emailAddress = new SugarEmailAddress();
677
		$ret = array();
678
679
		$union = '';
680
681
		$modules = array();
682
		foreach($contacts as $contact) {
683
			if(!isset($modules[$contact['module']])) {
684
				$modules[$contact['module']] = array();
685
			}
686
			$modules[$contact['module']][] = $contact;
687
		}
688
689
		foreach($modules as $module => $contacts) {
690
			if(!empty($union)) {
691
				$union .= " UNION ALL ";
692
			}
693
694
			$table = strtolower($module);
695
			$idsSerial = '';
696
697
			foreach($contacts as $contact) {
698
				if(!empty($idsSerial)) {
699
					$idsSerial .= ",";
700
				}
701
				$idsSerial .= "'{$contact['id']}'";
702
			}
703
704
			$union .= "(SELECT id, first_name, last_name, title, '{$module}' module FROM {$table} WHERE id IN({$idsSerial}) AND deleted = 0 )";
705
		}
706
		if(!empty($union)) {
707
			$union .= " ORDER BY last_name";
708
		}
709
710
		$r = $user->db->query($union);
711
712
		//_pp($union);
713
714
		while($a = $user->db->fetchByAssoc($r)) {
715
			$c = array();
716
717
			$c['name'] = $locale->getLocaleFormattedName($a['first_name'], "<b>{$a['last_name']}</b>", '', $a['title'], '', $user);
718
			$c['id'] = $a['id'];
719
			$c['module'] = $a['module'];
720
			$c['email'] = $emailAddress->getAddressesByGUID($a['id'], $a['module']);
721
			$ret[$a['id']] = $c;
722
		}
723
724
		return $ret;
725
	}
726
	////	END ADDRESS BOOK
727
	///////////////////////////////////////////////////////////////////////////
728
729
730
	///////////////////////////////////////////////////////////////////////////
731
	////	EMAIL 2.0 Preferences
732
	function getUserPrefsJS() {
733
		global $current_user;
734
		global $locale;
735
736
		// sort order per mailbox view
737
		$sortSerial = $current_user->getPreference('folderSortOrder', 'Emails');
738
		$sortArray = array();
739
		if(!empty($sortSerial)) {
740
			$sortArray = unserialize($sortSerial);
741
		}
742
743
		// treeview collapsed/open states
744
		$folderStateSerial = $current_user->getPreference('folderOpenState', 'Emails');
745
		$folderStates = array();
746
		if(!empty($folderStateSerial)) {
747
			$folderStates = unserialize($folderStateSerial);
748
		}
749
750
		// subscribed accounts
751
		$showFolders = unserialize(base64_decode($current_user->getPreference('showFolders', 'Emails')));
752
753
		// general settings
754
		$emailSettings = $current_user->getPreference('emailSettings', 'Emails');
755
756
		if(empty($emailSettings)) {
757
			$emailSettings = array();
758
			$emailSettings['emailCheckInterval'] = -1;
759
			$emailSettings['autoImport'] = '';
760
			$emailSettings['alwaysSaveOutbound'] = '1';
761
			$emailSettings['sendPlainText'] = '';
762
			$emailSettings['defaultOutboundCharset'] = $GLOBALS['sugar_config']['default_email_charset'];
763
			$emailSettings['showNumInList'] = 20;
764
		}
765
766
		// focus folder
767
		$focusFolder = $current_user->getPreference('focusFolder', 'Emails');
768
		$focusFolder = !empty($focusFolder) ? unserialize($focusFolder) : array();
769
770
		// unread only flag
771
		$showUnreadOnly = $current_user->getPreference('showUnreadOnly', 'Emails');
772
773
		$listViewSort = array(
774
			"sortBy" => 'date',
775
			"sortDirection" => 'DESC',
776
		);
777
778
		// signature prefs
779
		$signaturePrepend = $current_user->getPreference('signature_prepend') ? 'true' : 'false';
780
		$signatureDefault = $current_user->getPreference('signature_default');
781
		$signatures = array(
782
			'signature_prepend' => $signaturePrepend,
783
			'signature_default' => $signatureDefault
784
		);
785
786
787
		// current_user
788
		$user = array(
789
			'emailAddresses' => $current_user->emailAddress->getAddressesByGUID($current_user->id, 'Users'),
790
			'full_name' => from_html($current_user->full_name),
791
		);
792
793
		$userPreferences = array();
794
		$userPreferences['sort'] = $sortArray;
795
		$userPreferences['folderStates'] = $folderStates;
796
		$userPreferences['showFolders'] = $showFolders;
797
		$userPreferences['emailSettings'] = $emailSettings;
798
		$userPreferences['focusFolder'] = $focusFolder;
799
		$userPreferences['showUnreadOnly'] = $showUnreadOnly;
800
		$userPreferences['listViewSort'] = $listViewSort;
801
		$userPreferences['signatures'] = $signatures;
802
		$userPreferences['current_user'] = $user;
803
		return $userPreferences;
804
	}
805
806
807
808
	///////////////////////////////////////////////////////////////////////////
809
	////	FOLDER FUNCTIONS
810
811
	/**
812
	 * Creates a new Sugar folder
813
	 * @param string $nodeLabel New sugar folder name
814
	 * @param string $parentLabel Parent folder name
0 ignored issues
show
Bug introduced by
There is no parameter named $parentLabel. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
815
	 */
816
	function saveNewFolder($nodeLabel, $parentId, $isGroup=0) {
817
		global $current_user;
818
819
		$this->folder->name = $nodeLabel;
820
		$this->folder->is_group = $isGroup;
821
		$this->folder->parent_folder = ($parentId == 'Home') ? "" : $parentId;
822
		$this->folder->has_child = 0;
823
		$this->folder->created_by = $current_user->id;
824
		$this->folder->modified_by = $current_user->id;
825
		$this->folder->date_modified = $this->folder->date_created = TimeDate::getInstance()->nowDb();
826
827
		$this->folder->save();
828
		return array(
829
			'action' => 'newFolderSave',
830
			'id' => $this->folder->id,
831
			'name' => $this->folder->name,
832
			'is_group' => $this->folder->is_group,
833
			'is_dynamic' => $this->folder->is_dynamic
834
		);
835
	}
836
837
	/**
838
	 * Saves user sort prefernces
839
	 */
840
	function saveListViewSortOrder($ieId, $focusFolder, $sortBy, $sortDir) {
841
		global $current_user;
842
843
		$sortArray = array();
844
845
		$sortSerial = $current_user->getPreference('folderSortOrder', 'Emails');
846
		if(!empty($sortSerial)) {
847
			$sortArray = unserialize($sortSerial);
848
		}
849
850
		$sortArray[$ieId][$focusFolder]['current']['sort'] = $sortBy;
851
		$sortArray[$ieId][$focusFolder]['current']['direction'] = $sortDir;
852
		$sortSerial = serialize($sortArray);
853
		$current_user->setPreference('folderSortOrder', $sortSerial, '', 'Emails');
854
	}
855
856
	/**
857
	 * Stickies folder collapse/open state
858
	 */
859
	function saveFolderOpenState($focusFolder, $focusFolderOpen) {
860
		global $current_user;
861
862
		$folderStateSerial = $current_user->getPreference('folderOpenState', 'Emails');
863
		$folderStates = array();
864
865
		if(!empty($folderStateSerial)) {
866
			$folderStates = unserialize($folderStateSerial);
867
		}
868
869
		$folderStates[$focusFolder] = $focusFolderOpen;
870
		$newFolderStateSerial = serialize($folderStates);
871
		$current_user->setPreference('folderOpenState', $newFolderStateSerial, '', 'Emails');
872
	}
873
874
	/**
875
	 * saves a folder's view state
876
	 */
877
	function saveListView($ieId, $folder) {
878
		global $current_user;
879
880
		$saveState = array();
881
		$saveState['ieId'] = $ieId;
882
		$saveState['folder'] = $folder;
883
		$saveStateSerial = serialize($saveState);
884
		$current_user->setPreference('focusFolder', $saveStateSerial, '', 'Emails');
885
	}
886
887
	/**
888
	 * Generates cache folder structure
889
	 */
890
	function preflightEmailCache($cacheRoot) {
891
		// base
892
		if(!file_exists($cacheRoot))
893
			mkdir_recursive(clean_path($cacheRoot));
894
895
		// folders
896
		if(!file_exists($cacheRoot."/folders"))
897
			mkdir_recursive(clean_path("{$cacheRoot}/folders"));
898
899
		// messages
900
		if(!file_exists($cacheRoot."/messages"))
901
			mkdir_recursive(clean_path("{$cacheRoot}/messages"));
902
903
		// attachments
904
		if(!file_exists($cacheRoot."/attachments"))
905
			mkdir_recursive(clean_path("{$cacheRoot}/attachments"));
906
	}
907
908
	function deleteEmailCacheForFolders($cacheRoot) {
909
		$filePath = $cacheRoot."/folders/folders.php";
910
		if (file_exists($filePath)) {
911
			unlink($filePath);
912
		}
913
	}
914
	///////////////////////////////////////////////////////////////////////////
915
	////	IMAP FUNCTIONS
916
	/**
917
	 * Identifies subscribed mailboxes and empties the trash
918
	 * @param object $ie InboundEmail
919
	 */
920
	function emptyTrash(&$ie) {
921
		global $current_user;
922
923
		$showFolders = unserialize(base64_decode($current_user->getPreference('showFolders', 'Emails')));
924
925
		if(is_array($showFolders)) {
926
			foreach($showFolders as $ieId) {
927
				if(!empty($ieId)) {
928
					$ie->retrieve($ieId);
929
					$ie->emptyTrash();
930
				}
931
			}
932
		}
933
	}
934
935
	/**
936
	 * returns an array of nodes that correspond to IMAP mailboxes.
937
	 * @param bool $forceRefresh
0 ignored issues
show
Bug introduced by
There is no parameter named $forceRefresh. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
938
	 * @return object TreeView object
939
	 */
940
	function getMailboxNodes() {
941
		global $sugar_config;
942
		global $current_user;
943
		global $app_strings;
944
945
		$tree = new Tree("frameFolders");
946
		$tree->tree_style= 'include/ytree/TreeView/css/check/tree.css';
947
948
		$nodes = array();
949
		$ie = new InboundEmail();
950
		$refreshOffset = $this->cacheTimeouts['folders']; // 5 mins.  this will be set via user prefs
951
952
		$rootNode = new ExtNode($app_strings['LBL_EMAIL_HOME_FOLDER'], $app_strings['LBL_EMAIL_HOME_FOLDER']);
953
		$rootNode->dynamicloadfunction = '';
954
		$rootNode->expanded = true;
955
		$rootNode->dynamic_load = true;
956
		$showFolders = unserialize(base64_decode($current_user->getPreference('showFolders', 'Emails')));
957
958
		if(empty($showFolders)) {
959
			$showFolders = array();
960
		}
961
962
		// INBOX NODES
963
		if($current_user->hasPersonalEmail()) {
964
			$personals = $ie->retrieveByGroupId($current_user->id);
965
966
			foreach($personals as $k => $personalAccount) {
967
				if(in_array($personalAccount->id, $showFolders)) {
968
					// check for cache value
969
					$cacheRoot = sugar_cached("modules/Emails/{$personalAccount->id}");
970
					$this->preflightEmailCache($cacheRoot);
971
972
					if($this->validCacheFileExists($personalAccount->id, 'folders', "folders.php")) {
973
						$mailboxes = $this->getMailBoxesFromCacheValue($personalAccount);
974
					} else {
975
						$mailboxes = $personalAccount->getMailboxes();
976
					}
977
978
					$acctNode = new ExtNode('Home::' . $personalAccount->name, $personalAccount->name);
979
					$acctNode->dynamicloadfunction = '';
980
					$acctNode->expanded = false;
981
					$acctNode->set_property('cls', 'ieFolder');
982
					$acctNode->set_property('ieId', $personalAccount->id);
983
		        	$acctNode->set_property('protocol', $personalAccount->protocol);
984
985
					if(array_key_exists('Home::'.$personalAccount->name, $this->folderStates)) {
986
						if($this->folderStates['Home::'.$personalAccount->name] == 'open') {
987
							$acctNode->expanded = true;
988
						}
989
					}
990
					$acctNode->dynamic_load = true;
991
992
					$nodePath = $acctNode->_properties['id'];
993
994
					foreach($mailboxes as $k => $mbox) {
995
						$acctNode->add_node($this->buildTreeNode($k, $k, $mbox, $personalAccount->id,
996
						    $nodePath, false, $personalAccount));
997
					}
998
999
					$rootNode->add_node($acctNode);
1000
				}
1001
			}
1002
		}
1003
1004
		// GROUP INBOX NODES
1005
		$beans = $ie->retrieveAllByGroupId($current_user->id, false);
1006
		foreach($beans as $k => $groupAccount) {
1007
			if(in_array($groupAccount->id, $showFolders)) {
1008
				// check for cache value
1009
				$cacheRoot = sugar_cached("modules/Emails/{$groupAccount->id}");
1010
				$this->preflightEmailCache($cacheRoot);
1011
				//$groupAccount->connectMailserver();
1012
1013
				if($this->validCacheFileExists($groupAccount->id, 'folders', "folders.php")) {
1014
					$mailboxes = $this->getMailBoxesFromCacheValue($groupAccount);
1015
				} else {
1016
					$mailboxes = $groupAccount->getMailBoxesForGroupAccount();
1017
				}
1018
1019
				$acctNode = new ExtNode($groupAccount->name, "group.{$groupAccount->name}");
1020
				$acctNode->dynamicloadfunction = '';
1021
				$acctNode->expanded = false;
1022
		        $acctNode->set_property('isGroup', 'true');
1023
		        $acctNode->set_property('ieId', $groupAccount->id);
1024
		        $acctNode->set_property('protocol', $groupAccount->protocol);
1025
1026
				if(array_key_exists('Home::'.$groupAccount->name, $this->folderStates)) {
1027
					if($this->folderStates['Home::'.$groupAccount->name] == 'open') {
1028
						$acctNode->expanded = true;
1029
					}
1030
				}
1031
				$acctNode->dynamic_load = true;
1032
				$nodePath = $rootNode->_properties['id']."::".$acctNode->_properties['id'];
1033
1034
				foreach($mailboxes as $k => $mbox) {
1035
					$acctNode->add_node($this->buildTreeNode($k, $k, $mbox, $groupAccount->id,
1036
					   $nodePath, true, $groupAccount));
1037
				}
1038
1039
				$rootNode->add_node($acctNode);
1040
			}
1041
		}
1042
1043
		// SugarFolder nodes
1044
		/* SugarFolders are built at onload when the UI renders */
1045
1046
		$tree->add_node($rootNode);
1047
		return $tree;
1048
	}
1049
1050
	function getMailBoxesFromCacheValue($mailAccount) {
1051
		$foldersCache = $this->getCacheValue($mailAccount->id, 'folders', "folders.php", 'foldersCache');
1052
		$mailboxes = $foldersCache['mailboxes'];
1053
		$mailboxesArray = $mailAccount->generateFlatArrayFromMultiDimArray($mailboxes, $mailAccount->retrieveDelimiter());
1054
		$mailAccount->saveMailBoxFolders($mailboxesArray);
1055
		$this->deleteEmailCacheForFolders($cacheRoot);
1056
		return $mailboxes;
1057
	} // fn
1058
1059
	/**
1060
	 * Builds up a TreeView Node object
1061
	 * @param mixed
1062
	 * @param mixed
1063
	 * @param string
1064
	 * @param string ID of InboundEmail instance
1065
	 * @param string nodePath Serialized path from root node to current node
1066
	 * @param bool isGroup
1067
	 * @param bool forceRefresh
1068
	 * @return mixed
1069
	 */
1070
	function buildTreeNode($key, $label, $mbox, $ieId, $nodePath, $isGroup, $ie) {
1071
		global $sugar_config;
1072
1073
		// get unread counts
1074
		$exMbox = explode("::", $nodePath);
1075
		$unseen = 0;
1076
		$GLOBALS['log']->debug("$key --- $nodePath::$label");
1077
1078
		if(count($exMbox) >= 2) {
1079
			$mailbox = "";
1080
			for($i=2; $i<count($exMbox); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
1081
				if($mailbox != "") {
1082
					$mailbox .= ".";
1083
				}
1084
				$mailbox .= "{$exMbox[$i]}";
1085
			}
1086
1087
		    $mailbox = substr($key, strpos($key, '.'));
1088
1089
			$unseen = $this->getUnreadCount($ie, $mailbox);
1090
1091
			if($unseen > 0) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
1092
				//$label = " <span id='span{$ie->id}{$ie->mailbox}' style='font-weight:bold'>{$label} (<span id='span{$ie->id}{$ie->mailbox}nums'>{$unseen}</span>)</span>";
1093
			}
1094
		}
1095
1096
		$nodePath = $nodePath."::".$label;
1097
        $node = new ExtNode($nodePath, $label);
1098
        $node->dynamicloadfunction = '';
1099
        $node->expanded = false;
1100
        $node->set_property('labelStyle', "remoteFolder");
1101
1102
1103
        if(array_key_exists($nodePath, $this->folderStates)) {
1104
        	if($this->folderStates[$nodePath] == 'open') {
1105
        		$node->expanded = true;
1106
        	}
1107
        }
1108
1109
		$group = ($isGroup) ? 'true' : 'false';
1110
        $node->dynamic_load = true;
1111
        //$node->set_property('href', " SUGAR.email2.listView.populateListFrame(YAHOO.namespace('frameFolders').selectednode, '{$ieId}', 'false');");
1112
        $node->set_property('isGroup', $group);
1113
        $node->set_property('isDynamic', 'false');
1114
        $node->set_property('ieId', $ieId);
1115
        $node->set_property('mbox', $key);
1116
        $node->set_property('unseen', $unseen);
1117
        $node->set_property('cls', 'ieFolder');
1118
1119
        if(is_array($mbox)) {
1120
        	foreach($mbox as $k => $v) {
1121
        		$node->add_node($this->buildTreeNode("$key.$k", $k, $v, $ieId, $nodePath, $isGroup, $ie));
1122
        	}
1123
        }
1124
1125
        return $node;
1126
	}
1127
1128
	/**
1129
	 * Totals the unread emails
1130
	 */
1131
	function getUnreadCount(&$ie, $mailbox) {
1132
		global $sugar_config;
1133
		$unseen = 0;
1134
1135
		// use cache
1136
		return $ie->getCacheUnreadCount($mailbox);
1137
	}
1138
1139
	///////////////////////////////////////////////////////////////////////////
1140
	////	DISPLAY CODE
1141
	/**
1142
	 * Used exclusively by draft code.  Returns Notes and Documents as attachments.
1143
	 * @param array $ret
1144
	 * @return array
1145
	 */
1146
	function getDraftAttachments($ret) {
1147
		global $db;
1148
1149
		// $ret['uid'] is the draft Email object's GUID
1150
		$ret['attachments'] = array();
1151
1152
		$q = "SELECT id, filename FROM notes WHERE parent_id = '{$ret['uid']}' AND deleted = 0";
1153
		$r = $db->query($q);
1154
1155
		while($a = $db->fetchByAssoc($r)) {
1156
			$ret['attachments'][$a['id']] = array(
1157
				'id'		=> $a['id'],
1158
				'filename'	=> $a['filename'],
1159
			);
1160
		}
1161
1162
		return $ret;
1163
	}
1164
1165
	function createCopyOfInboundAttachment($ie, $ret, $uid) {
1166
		global $sugar_config;
1167
		if ($ie->isPop3Protocol()) {
1168
			// get the UIDL from database;
1169
			$cachedUIDL = md5($uid);
1170
			$cache = sugar_cached("modules/Emails/{$ie->id}/messages/{$ie->mailbox}{$cachedUIDL}.php");
1171
		} else {
1172
			$cache = sugar_cached("modules/Emails/{$ie->id}/messages/{$ie->mailbox}{$uid}.php");
1173
		}
1174
		if(file_exists($cache)) {
1175
			include($cache); // profides $cacheFile
1176
			$metaOut = unserialize($cacheFile['out']);
1177
			$meta = $metaOut['meta']['email'];
1178
			if (isset($meta['attachments'])) {
1179
				$attachmentHtmlData = $meta['attachments'];
1180
				$actualAttachmentInfo = array();
1181
				$this->parseAttachmentInfo($actualAttachmentInfo, $attachmentHtmlData);
1182
				if (sizeof($actualAttachmentInfo) > 0) {
1183
					foreach($actualAttachmentInfo as $key => $value) {
1184
					    $info_vars = array();
1185
					    parse_str($value, $info_vars);
1186
					    $fileName = $info_vars['tempName'];
1187
					    $attachmentid = $info_vars['id'];
1188
						$guid = create_guid();
1189
						$destination = clean_path("{$this->userCacheDir}/{$guid}");
1190
1191
						$attachmentFilePath = sugar_cached("modules/Emails/{$ie->id}/attachments/{$attachmentid}");
1192
						copy($attachmentFilePath, $destination);
1193
						$ret['attachments'][$guid] = array();
1194
						$ret['attachments'][$guid]['id'] = $guid . $fileName;
1195
						$ret['attachments'][$guid]['filename'] = $fileName;
1196
					} // for
1197
				} // if
1198
			} // if
1199
1200
		} // if
1201
		return $ret;
1202
1203
	} // fn
1204
1205
	function parseAttachmentInfo(&$actualAttachmentInfo, $attachmentHtmlData) {
1206
	 	$downLoadPHP = strpos($attachmentHtmlData, "index.php?entryPoint=download&");
1207
		while ($downLoadPHP) {
1208
		 	$attachmentHtmlData = substr($attachmentHtmlData, $downLoadPHP+30);
1209
		 	$final = strpos($attachmentHtmlData, "\">");
1210
		 	$actualAttachmentInfo[] = substr($attachmentHtmlData, 0, $final);
1211
		 	$attachmentHtmlData = substr($attachmentHtmlData, $final);
1212
		 	$downLoadPHP = strpos($attachmentHtmlData, "index.php?entryPoint=download&");
1213
		} // while
1214
	}
1215
	/**
1216
	 * Renders the QuickCreate form from Smarty and returns HTML
1217
	 * @param array $vars request variable global
1218
	 * @param object $email Fetched email object
1219
	 * @param bool $addToAddressBook
0 ignored issues
show
Documentation introduced by
There is no parameter named $addToAddressBook. Did you maybe mean $addToAddressBookButton?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
1220
	 * @return array
1221
	 */
1222
	function getQuickCreateForm($vars, $email, $addToAddressBookButton=false) {
1223
		require_once("include/EditView/EditView2.php");
1224
		global $app_strings;
1225
		global $mod_strings;
1226
		global $current_user;
1227
		global $beanList;
1228
		global $beanFiles;
1229
		global $current_language;
1230
1231
		//Setup the current module languge
1232
		$mod_strings = return_module_language($current_language, $_REQUEST['qc_module']);
1233
1234
		$bean = $beanList[$_REQUEST['qc_module']];
1235
		$class = $beanFiles[$bean];
1236
		require_once($class);
1237
1238
		$focus = new $bean();
1239
1240
		$people = array(
1241
		'Contact'
1242
		,'Lead'
1243
		);
1244
		$emailAddress = array();
1245
1246
		// people
1247
		if(in_array($bean, $people)) {
1248
			// lead specific
1249
			$focus->lead_source = 'Email';
1250
			$focus->lead_source_description = trim($email->name);
1251
1252
			$from = (isset($email->from_name) && !empty($email->from_name)) ? $email->from_name : $email->from_addr;
1253
1254
            if(isset($_REQUEST['sugarEmail']) && !empty($_REQUEST['sugarEmail']))
1255
            {
1256
                if($email->status == "sent")
1257
                {
1258
                    $from = (isset($email->to_addrs_names) && !empty($email->to_addrs_names)) ? $email->to_addrs_names : $email->to_addrs;
1259
                }else{
1260
                    $from = (isset($email->from_name) && !empty($email->from_name)) ? $email->from_name : $email->from_addr_name;
1261
                }
1262
            }
1263
1264
			$name = explode(" ", trim($from));
1265
1266
			$address = trim(array_pop($name));
1267
			$address = str_replace(array("<",">","&lt;","&gt;"), "", $address);
1268
1269
			$emailAddress[] = array(
1270
				'email_address'		=> $address,
1271
				'primary_address'	=> 1,
1272
				'invalid_email'		=> 0,
1273
				'opt_out'			=> 0,
1274
				'reply_to_address'	=> 1
1275
			);
1276
1277
			$focus->email1 = $address;
1278
1279
			if(!empty($name)) {
1280
				$focus->last_name = trim(array_pop($name));
1281
1282
				foreach($name as $first) {
1283
					if(!empty($focus->first_name)) {
1284
						$focus->first_name .= " ";
1285
					}
1286
					$focus->first_name .= trim($first);
1287
				}
1288
			}
1289
		} else {
1290
			// bugs, cases, tasks
1291
			$focus->name = trim($email->name);
1292
		}
1293
1294
		$focus->description = trim(strip_tags($email->description));
1295
		$focus->assigned_user_id = $current_user->id;
1296
1297
1298
		$EditView = new EditView();
0 ignored issues
show
Bug introduced by
The call to EditView::__construct() misses some required arguments starting with $module.
Loading history...
Deprecated Code introduced by
The class EditView has been deprecated.

This class, trait or interface has been deprecated.

Loading history...
1299
		$EditView->ss = new Sugar_Smarty();
1300
		//MFH BUG#20283 - checks for custom quickcreate fields
1301
		$EditView->setup($_REQUEST['qc_module'], $focus, 'custom/modules/'.$focus->module_dir.'/metadata/editviewdefs.php', 'include/EditView/EditView.tpl');
1302
		$EditView->process();
1303
		$EditView->render();
0 ignored issues
show
Bug introduced by
The method render() does not seem to exist on object<EditView>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1304
1305
		$EditView->defs['templateMeta']['form']['buttons'] = array(
1306
			'email2save' => array(
1307
				'id' => 'e2AjaxSave',
1308
				'customCode' => '<input type="button" class="button" value="   '.$app_strings['LBL_SAVE_BUTTON_LABEL']
1309
				              . '   " onclick="SUGAR.email2.detailView.saveQuickCreate(false);" />'
1310
			),
1311
			'email2saveandreply' => array(
1312
			    'id' => 'e2SaveAndReply',
1313
			    'customCode' => '<input type="button" class="button" value="   '.$app_strings['LBL_EMAIL_SAVE_AND_REPLY']
1314
			                  . '   " onclick="SUGAR.email2.detailView.saveQuickCreate(\'reply\');" />'
1315
			),
1316
			'email2cancel' => array(
1317
			     'id' => 'e2cancel',
1318
			     'customCode' => '<input type="button" class="button" value="   '.$app_strings['LBL_EMAIL_CANCEL']
1319
                              . '   " onclick="SUGAR.email2.detailView.quickCreateDialog.hide();" />'
1320
			)
1321
		);
1322
1323
1324
		if($addToAddressBookButton) {
1325
			$EditView->defs['templateMeta']['form']['buttons']['email2saveAddToAddressBook'] = array(
1326
				'id' => 'e2addToAddressBook',
1327
				'customCode' => '<input type="button" class="button" value="   '.$app_strings['LBL_EMAIL_ADDRESS_BOOK_SAVE_AND_ADD']
1328
				              . '   " onclick="SUGAR.email2.detailView.saveQuickCreate(true);" />'
1329
			);
1330
		}
1331
1332
		//Get the module language for javascript
1333
	    if(!is_file(sugar_cached('jsLanguage/') . $_REQUEST['qc_module'] . '/' . $GLOBALS['current_language'] . '.js')) {
1334
            require_once('include/language/jsLanguage.php');
1335
            jsLanguage::createModuleStringsCache($_REQUEST['qc_module'], $GLOBALS['current_language']);
1336
        }
1337
		$jsLanguage = getVersionedScript("cache/jsLanguage/{$_REQUEST['qc_module']}/{$GLOBALS['current_language']}.js", $GLOBALS['sugar_config']['js_lang_version']);
1338
1339
1340
		$EditView->view = 'EmailQCView';
1341
		$EditView->defs['templateMeta']['form']['headerTpl'] = 'include/EditView/header.tpl';
1342
		$EditView->defs['templateMeta']['form']['footerTpl'] = 'include/EditView/footer.tpl';
1343
		$meta = array();
1344
		$meta['html'] = $jsLanguage . $EditView->display(false, true);
0 ignored issues
show
Unused Code introduced by
The call to EditView::display() has too many arguments starting with false.

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...
1345
		$meta['html'] = str_replace("src='".getVersionedPath('include/SugarEmailAddress/SugarEmailAddress.js')."'", '', $meta['html']);
1346
		$meta['emailAddress'] = $emailAddress;
1347
1348
		$mod_strings = return_module_language($current_language, 'Emails');
1349
1350
		return $meta;
1351
	}
1352
1353
	/**
1354
     * Renders the Import form from Smarty and returns HTML
1355
     * @param array $vars request variable global
1356
     * @param object $email Fetched email object
1357
     * @param bool $addToAddressBook
0 ignored issues
show
Bug introduced by
There is no parameter named $addToAddressBook. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1358
     * @return array
1359
     */
1360
    function getImportForm($vars, $email, $formName = 'ImportEditView') {
1361
		require_once("include/EditView/EditView2.php");
1362
        require_once("include/TemplateHandler/TemplateHandler.php");
1363
		require_once('include/QuickSearchDefaults.php');
1364
        $qsd = QuickSearchDefaults::getQuickSearchDefaults();
1365
		$qsd->setFormName($formName);
1366
1367
        global $app_strings;
1368
        global $current_user;
1369
        global $app_list_strings;
1370
		$sqs_objects = array(
1371
		                     "{$formName}_parent_name" => $qsd->getQSParent(),
1372
		);
1373
        $smarty = new Sugar_Smarty();
1374
        $smarty->assign("APP",$app_strings);
1375
        $smarty->assign('formName',$formName);
1376
        $showAssignTo = false;
1377
        if (!isset($vars['showAssignTo']) || $vars['showAssignTo'] == true) {
1378
        	$showAssignTo = true;
1379
		} // if
1380
		if ($showAssignTo) {
1381
	        if(empty($email->assigned_user_id) && empty($email->id))
1382
	            $email->assigned_user_id = $current_user->id;
1383
	        if(empty($email->assigned_name) && empty($email->id))
1384
	            $email->assigned_user_name = $current_user->user_name;
1385
	        $sqs_objects["{$formName}_assigned_user_name"] = $qsd->getQSUser();
1386
		}
1387
		$smarty->assign("showAssignedTo",$showAssignTo);
1388
1389
        $showDelete = false;
1390
        if (!isset($vars['showDelete']) || $vars['showDelete'] == true) {
1391
            $showDelete = true;
1392
        }
1393
        $smarty->assign("showDelete",$showDelete);
1394
1395
        $smarty->assign("userId",$email->assigned_user_id);
1396
        $smarty->assign("userName",$email->assigned_user_name);
1397
        $parent_types = $app_list_strings['record_type_display'];
1398
        $smarty->assign('parentOptions', get_select_options_with_id($parent_types, $email->parent_type));
1399
1400
		$quicksearch_js = '<script type="text/javascript" language="javascript">sqs_objects = ' . json_encode($sqs_objects) . '</script>';
1401
        $smarty->assign('SQS', $quicksearch_js);
1402
1403
        $meta = array();
1404
        $meta['html'] = $smarty->fetch("modules/Emails/templates/importRelate.tpl");
1405
        return $meta;
1406
    }
1407
1408
    /**
1409
     * This function returns the detail view for email in new 2.0 interface
1410
     *
1411
     */
1412
    function getDetailViewForEmail2($emailId) {
1413
1414
		require_once('include/DetailView/DetailView.php');
1415
		global $app_strings, $app_list_strings;
1416
		global $mod_strings;
1417
1418
        $smarty = new Sugar_Smarty();
1419
1420
		// SETTING DEFAULTS
1421
		$focus		= new Email();
1422
		$focus->retrieve($emailId);
1423
		$detailView->ss = new Sugar_Smarty();
0 ignored issues
show
Bug introduced by
The variable $detailView seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
1424
		$detailView	= new DetailView();
1425
		$title = "";
1426
		$offset		= 0;
1427
		if($focus->type == 'out') {
1428
			$title = getClassicModuleTitle('Emails', array($mod_strings['LBL_SENT_MODULE_NAME'],$focus->name), true);
1429
		} elseif ($focus->type == 'draft') {
1430
			$title = getClassicModuleTitle('Emails', array($mod_strings['LBL_LIST_FORM_DRAFTS_TITLE'],$focus->name), true);
1431
		} elseif($focus->type == 'inbound') {
1432
			$title = getClassicModuleTitle('Emails', array($mod_strings['LBL_INBOUND_TITLE'],$focus->name), true);
1433
		}
1434
		$smarty->assign("emailTitle", $title);
1435
1436
		// DEFAULT TO TEXT IF NO HTML CONTENT:
1437
		$html = trim(from_html($focus->description_html));
1438
		if(empty($html)) {
1439
			$smarty->assign('SHOW_PLAINTEXT', 'true');
1440
		} else {
1441
			$smarty->assign('SHOW_PLAINTEXT', 'false');
1442
		}
1443
1444
		//if not empty or set to test (from test campaigns)
1445
		if (!empty($focus->parent_type) && $focus->parent_type !='test') {
1446
			$smarty->assign('PARENT_MODULE', $focus->parent_type);
1447
			$smarty->assign('PARENT_TYPE', $app_list_strings['record_type_display'][$focus->parent_type] . ":");
1448
		}
1449
1450
        global $gridline;
1451
		$smarty->assign('MOD', $mod_strings);
1452
		$smarty->assign('APP', $app_strings);
1453
		$smarty->assign('GRIDLINE', $gridline);
1454
		$smarty->assign('PRINT_URL', 'index.php?'.$GLOBALS['request_string']);
1455
		$smarty->assign('ID', $focus->id);
1456
		$smarty->assign('TYPE', $focus->type);
1457
		$smarty->assign('PARENT_NAME', $focus->parent_name);
1458
		$smarty->assign('PARENT_ID', $focus->parent_id);
1459
		$smarty->assign('NAME', $focus->name);
1460
		$smarty->assign('ASSIGNED_TO', $focus->assigned_user_name);
1461
		$smarty->assign('DATE_MODIFIED', $focus->date_modified);
1462
		$smarty->assign('DATE_ENTERED', $focus->date_entered);
1463
		$smarty->assign('DATE_START', $focus->date_start);
1464
		$smarty->assign('TIME_START', $focus->time_start);
1465
		$smarty->assign('FROM', $focus->from_addr);
1466
		$smarty->assign('TO', nl2br($focus->to_addrs));
1467
		$smarty->assign('CC', nl2br($focus->cc_addrs));
1468
		$smarty->assign('BCC', nl2br($focus->bcc_addrs));
1469
		$smarty->assign('CREATED_BY', $focus->created_by_name);
1470
		$smarty->assign('MODIFIED_BY', $focus->modified_by_name);
1471
		$smarty->assign('DATE_SENT', $focus->date_entered);
1472
		$smarty->assign('EMAIL_NAME', 'RE: '.$focus->name);
1473
		$smarty->assign("TAG", $focus->listviewACLHelper());
1474
		$smarty->assign("SUGAR_VERSION", $GLOBALS['sugar_version']);
1475
		$smarty->assign("JS_CUSTOM_VERSION", $GLOBALS['sugar_config']['js_custom_version']);
1476
		if(!empty($focus->reply_to_email)) {
1477
			$replyTo = "
1478
				<tr>
1479
		        <td class=\"tabDetailViewDL\"><slot>".$mod_strings['LBL_REPLY_TO_NAME']."</slot></td>
1480
		        <td colspan=3 class=\"tabDetailViewDF\"><slot>".$focus->reply_to_addr."</slot></td>
1481
		        </tr>";
1482
		 	$smarty->assign("REPLY_TO", $replyTo);
1483
		}
1484
		///////////////////////////////////////////////////////////////////////////////
1485
		////	JAVASCRIPT VARS
1486
		$jsVars  = '';
1487
		$jsVars .= "var showRaw = '{$mod_strings['LBL_BUTTON_RAW_LABEL']}';";
1488
		$jsVars .= "var hideRaw = '{$mod_strings['LBL_BUTTON_RAW_LABEL_HIDE']}';";
1489
		$smarty->assign("JS_VARS", $jsVars);
1490
		///////////////////////////////////////////////////////////////////////////////
1491
		////	NOTES (attachements, etc.)
1492
		///////////////////////////////////////////////////////////////////////////////
1493
1494
		$note = new Note();
1495
		$where = "notes.parent_id='{$focus->id}'";
1496
		//take in account if this is from campaign and the template id is stored in the macros.
1497
1498
		if(isset($macro_values) && isset($macro_values['email_template_id'])){
0 ignored issues
show
Bug introduced by
The variable $macro_values seems to never exist, and therefore isset should always return false. 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...
1499
		    $where = "notes.parent_id='{$macro_values['email_template_id']}'";
1500
		}
1501
		$notes_list = $note->get_full_list("notes.name", $where, true);
1502
1503
		if(! isset($notes_list)) {
1504
			$notes_list = array();
1505
		}
1506
1507
		$attachments = '';
1508
		for($i=0; $i<count($notes_list); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
1509
			$the_note = $notes_list[$i];
1510
			$attachments .= "<a href=\"index.php?entryPoint=download&id={$the_note->id}&type=Notes\">".$the_note->name."</a><br />";
1511
			$focus->cid2Link($the_note->id, $the_note->file_mime_type);
1512
		}
1513
		$smarty->assign('DESCRIPTION', nl2br($focus->description));
1514
		$smarty->assign('DESCRIPTION_HTML', from_html($focus->description_html));
1515
		$smarty->assign("ATTACHMENTS", $attachments);
1516
		///////////////////////////////////////////////////////////////////////////////
1517
		////    SUBPANELS
1518
		///////////////////////////////////////////////////////////////////////////////
1519
		$show_subpanels = true;
1520
		if ($show_subpanels) {
1521
		    require_once('include/SubPanel/SubPanelTiles.php');
1522
		    $subpanel = new SubPanelTiles($focus, 'Emails');
1523
		    $smarty->assign("SUBPANEL", $subpanel->display());
1524
		}
1525
        $meta['html'] = $smarty->fetch("modules/Emails/templates/emailDetailView.tpl");
1526
        return $meta;
1527
1528
    } // fn
1529
1530
	/**
1531
	 * Sets the "read" flag in the overview cache
1532
	 */
1533
	function setReadFlag($ieId, $mbox, $uid) {
1534
		$this->markEmails('read', $ieId, $mbox, $uid);
1535
	}
1536
1537
	/**
1538
	 * Marks emails with the passed flag type.  This will be applied to local
1539
	 * cache files as well as remote emails.
1540
	 * @param string $type Flag type
1541
	 * @param string $ieId
1542
	 * @param string $folder IMAP folder structure or SugarFolder GUID
1543
	 * @param string $uids Comma sep list of UIDs or GUIDs
1544
	 */
1545
	function markEmails($type, $ieId, $folder, $uids) {
1546
1547
		global $app_strings;
1548
		$uids = $this->_cleanUIDList($uids);
1549
		$exUids = explode($app_strings['LBL_EMAIL_DELIMITER'], $uids);
1550
1551
		if(strpos($folder, 'sugar::') !== false) {
1552
			// dealing with a sugar email object, uids are GUIDs
1553
			foreach($exUids as $id) {
1554
				$email = new Email();
1555
				$email->retrieve($id);
1556
1557
                // BUG FIX BEGIN
1558
                // Bug 50973 - marking unread in group inbox removes message
1559
                if (empty($email->assigned_user_id))
1560
                {
1561
                    $email->setFieldNullable('assigned_user_id');
1562
                }
1563
                // BUG FIX END
1564
1565
				switch($type) {
1566
					case "unread":
1567
						$email->status = 'unread';
1568
						$email->save();
1569
					break;
1570
1571
					case "read":
1572
						$email->status = 'read';
1573
						$email->save();
1574
					break;
1575
1576
					case "deleted":
1577
						$email->delete();
1578
					break;
1579
1580
					case "flagged":
1581
						$email->flagged = 1;
1582
						$email->save();
1583
					break;
1584
1585
					case "unflagged":
1586
						$email->flagged = 0;
1587
						$email->save();
1588
					break;
1589
1590
				}
1591
1592
                // BUG FIX BEGIN
1593
                // Bug 50973 - reset assigned_user_id field defs
1594
                if (empty($email->assigned_user_id))
1595
                {
1596
                    $email->revertFieldNullable('assigned_user_id');
1597
                }
1598
                // BUG FIX END
1599
			}
1600
		} else {
1601
			/* dealing with IMAP email, uids are IMAP uids */
1602
			global $ie; // provided by EmailUIAjax.php
1603
			if(empty($ie)) {
1604
1605
				$ie = new InboundEmail();
1606
			}
1607
			$ie->retrieve($ieId);
1608
			$ie->mailbox = $folder;
1609
			$ie->connectMailserver();
1610
			// mark cache files
1611
			if($type == 'deleted') {
1612
				$ie->deleteMessageOnMailServer($uids);
0 ignored issues
show
Bug introduced by
It seems like $uids defined by $this->_cleanUIDList($uids) on line 1548 can also be of type array; however, InboundEmail::deleteMessageOnMailServer() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1613
				$ie->deleteMessageFromCache($uids);
1614
			} else {
1615
				$overviews = $ie->getCacheValueForUIDs($ie->mailbox, $exUids);
1616
				$manipulated = array();
1617
1618
				foreach($overviews['retArr'] as $k => $overview) {
1619
					if(in_array($overview->uid, $exUids)) {
1620
						switch($type) {
1621
							case "unread":
1622
								$overview->seen = 0;
1623
							break;
1624
1625
							case "read":
1626
								$overview->seen = 1;
1627
							break;
1628
1629
							case "flagged":
1630
								$overview->flagged = 1;
1631
							break;
1632
1633
							case "unflagged":
1634
								$overview->flagged = 0;
1635
							break;
1636
						}
1637
						$manipulated[] = $overview;
1638
					}
1639
				}
1640
1641
				if(!empty($manipulated)) {
1642
					$ie->setCacheValue($ie->mailbox, array(), $manipulated);
1643
					/* now mark emails on email server */
1644
					$ie->markEmails(implode(",", explode($app_strings['LBL_EMAIL_DELIMITER'], $uids)), $type);
1645
				}
1646
			} // end not type == deleted
1647
		}
1648
	}
1649
1650
function doAssignment($distributeMethod, $ieid, $folder, $uids, $users) {
1651
	global $app_strings;
1652
	$users = explode(",", $users);
1653
	$emailIds = explode($app_strings['LBL_EMAIL_DELIMITER'], $uids);
1654
	$out = "";
1655
	if($folder != 'sugar::Emails') {
1656
		$emailIds = array();
1657
		$uids = explode($app_strings['LBL_EMAIL_DELIMITER'], $uids);
1658
		$ie = new InboundEmail();
1659
		$ie->retrieve($ieid);
1660
		$messageIndex = 1;
1661
		// dealing with an inbound email data so we need to import an email and then
1662
		foreach($uids as $uid) {
1663
			$ie->mailbox = $folder;
1664
			$ie->connectMailserver();
1665
			$msgNo = $uid;
1666
			if (!$ie->isPop3Protocol()) {
1667
				$msgNo = imap_msgno($ie->conn, $uid);
1668
			} else {
1669
				$msgNo = $ie->getCorrectMessageNoForPop3($uid);
1670
			}
1671
1672
			if(!empty($msgNo)) {
1673
				if ($ie->importOneEmail($msgNo, $uid)) {
1674
					$emailIds[] = $ie->email->id;
1675
					$ie->deleteMessageOnMailServer($uid);
1676
					//$ie->retrieve($ieid);
1677
					//$ie->connectMailserver();
1678
					$ie->mailbox = $folder;
1679
					$ie->deleteMessageFromCache(($uids[] = $uid));
1680
				} else {
1681
					$out = $out . "Message No : " . $messageIndex . " failed. Reason : Message already imported \r\n";
1682
				}
1683
			}
1684
			$messageIndex++;
1685
		} // for
1686
	} // if
1687
1688
	if (count($emailIds) > 0) {
1689
		$this->doDistributionWithMethod($users, $emailIds, $distributeMethod);
1690
	} // if
1691
	return $out;
1692
} // fn
1693
1694
/**
1695
 * get team id and team set id from request
1696
 * @return  array
1697
 */
1698
function getTeams() {
1699
}
1700
1701
function doDistributionWithMethod($users, $emailIds, $distributionMethod) {
1702
	// we have users and the items to distribute
1703
	if($distributionMethod == 'roundRobin') {
1704
		$this->distRoundRobin($users, $emailIds);
1705
	} elseif($distributionMethod == 'leastBusy') {
1706
		$this->distLeastBusy($users, $emailIds);
1707
	} elseif($distributionMethod == 'direct') {
1708
		if(count($users) > 1) {
1709
			// only 1 user allowed in direct assignment
1710
			$error = 1;
1711
		} else {
1712
			$user = $users[0];
1713
			$this->distDirect($user, $emailIds);
1714
		} // else
1715
	} // elseif
1716
1717
} // fn
1718
1719
/**
1720
 * distributes emails to users on Round Robin basis
1721
 * @param	$userIds	array of users to dist to
1722
 * @param	$mailIds	array of email ids to push on those users
1723
 * @return  boolean		true on success
1724
 */
1725
function distRoundRobin($userIds, $mailIds) {
1726
	// check if we have a 'lastRobin'
1727
	$lastRobin = $userIds[0];
1728
	foreach($mailIds as $k => $mailId) {
1729
		$userIdsKeys = array_flip($userIds); // now keys are values
1730
		$thisRobinKey = $userIdsKeys[$lastRobin] + 1;
1731
		if(!empty($userIds[$thisRobinKey])) {
1732
			$thisRobin = $userIds[$thisRobinKey];
1733
			$lastRobin = $userIds[$thisRobinKey];
1734
		} else {
1735
			$thisRobin = $userIds[0];
1736
			$lastRobin = $userIds[0];
1737
		}
1738
1739
		$email = new Email();
1740
		$email->retrieve($mailId);
1741
		$email->assigned_user_id = $thisRobin;
1742
		$email->status = 'unread';
1743
		$email->save();
1744
	}
1745
1746
	return true;
1747
}
1748
1749
/**
1750
 * distributes emails to users on Least Busy basis
1751
 * @param	$userIds	array of users to dist to
1752
 * @param	$mailIds	array of email ids to push on those users
1753
 * @return  boolean		true on success
1754
 */
1755
function distLeastBusy($userIds, $mailIds) {
1756
	foreach($mailIds as $k => $mailId) {
1757
		$email = new Email();
1758
		$email->retrieve($mailId);
1759
		foreach($userIds as $k => $id) {
1760
			$r = $this->db->query("SELECT count(*) AS c FROM emails WHERE assigned_user_id = '.$id.' AND status = 'unread'");
1761
			$a = $this->db->fetchByAssoc($r);
1762
			$counts[$id] = $a['c'];
1763
		}
1764
		asort($counts); // lowest to highest
1765
		$countsKeys = array_flip($counts); // keys now the 'count of items'
1766
		$leastBusy = array_shift($countsKeys); // user id of lowest item count
1767
		$email->assigned_user_id = $leastBusy;
1768
		$email->status = 'unread';
1769
		$email->save();
1770
	}
1771
	return true;
1772
}
1773
1774
/**
1775
 * distributes emails to 1 user
1776
 * @param	$user		users to dist to
1777
 * @param	$mailIds	array of email ids to push
1778
 * @return  boolean		true on success
1779
 */
1780
function distDirect($user, $mailIds) {
1781
	foreach($mailIds as $k => $mailId) {
1782
		$email = new Email();
1783
		$email->retrieve($mailId);
1784
		$email->assigned_user_id = $user;
1785
		$email->status = 'unread';
1786
1787
		$email->save();
1788
	}
1789
	return true;
1790
}
1791
1792
function getAssignedEmailsCountForUsers($userIds) {
1793
	$counts = array();
1794
	foreach($userIds as $id) {
1795
		$r = $this->db->query("SELECT count(*) AS c FROM emails WHERE assigned_user_id = '$id' AND status = 'unread'");
1796
		$a = $this->db->fetchByAssoc($r);
1797
		$counts[$id] = $a['c'];
1798
	} // foreach
1799
	return $counts;
1800
} // fn
1801
1802
function getLastRobin($ie) {
1803
	$lastRobin = "";
1804
	if($this->validCacheFileExists($ie->id, 'folders', "robin.cache.php")) {
1805
		$lastRobin = $this->getCacheValue($ie->id, 'folders', "robin.cache.php", 'robin');
1806
	} // if
1807
	return $lastRobin;
1808
} // fn
1809
1810
function setLastRobin($ie, $lastRobin) {
1811
    global $sugar_config;
1812
    $cacheFolderPath = sugar_cached("modules/Emails/{$ie->id}/folders");
1813
    if (!file_exists($cacheFolderPath)) {
1814
    	mkdir_recursive($cacheFolderPath);
1815
    }
1816
	$this->writeCacheFile('robin', $lastRobin, $ie->id, 'folders', "robin.cache.php");
1817
} // fn
1818
1819
	/**
1820
	 * returns the metadata defining a single email message for display.  Uses cache file if it exists
1821
	 * @return array
1822
	 */
1823
function getSingleMessage($ie) {
1824
1825
		global $timedate;
1826
		global $app_strings,$mod_strings;
1827
		$ie->retrieve($_REQUEST['ieId']);
1828
		$noCache = true;
1829
1830
		$ie->mailbox = $_REQUEST['mbox'];
1831
		$filename = $_REQUEST['mbox'].$_REQUEST['uid'].".php";
1832
		$md5uidl = "";
1833
		if ($ie->isPop3Protocol()) {
1834
			$md5uidl = md5($_REQUEST['uid']);
1835
			$filename = $_REQUEST['mbox'].$md5uidl.".php";
1836
		} // if
1837
1838
		if(isset($filename) && strpos($filename, "..") !== false){
1839
			die("Directory navigation attack denied.");
1840
		}
1841
1842
		if($this->validCacheFileExists($_REQUEST['ieId'], 'messages', $filename)) {
1843
			$out = $this->getCacheValue($_REQUEST['ieId'], 'messages', $filename, 'out');
1844
			$noCache = false;
1845
1846
			// something fubar'd the cache?
1847
			if(empty($out['meta']['email']['name']) && empty($out['meta']['email']['description'])) {
1848
				$noCache = true;
1849
			} else {
1850
				// When sending data from cache, convert date into users preffered format
1851
				$dateTimeInGMTFormat = $out['meta']['email']['date_start'];
1852
				$out['meta']['email']['date_start'] = $timedate->to_display_date_time($dateTimeInGMTFormat);
1853
			} // else
1854
		}
1855
1856
		if($noCache) {
1857
			$writeToCacheFile = true;
1858
			if ($ie->isPop3Protocol()) {
1859
				$status = $ie->setEmailForDisplay($_REQUEST['uid'], true, true, true);
1860
			} else {
1861
				$status = $ie->setEmailForDisplay($_REQUEST['uid'], false, true, true);
1862
			}
1863
			$out = $ie->displayOneEmail($_REQUEST['uid'], $_REQUEST['mbox']);
1864
			// modify the out object to store date in GMT format on the local cache file
1865
			$dateTimeInUserFormat = $out['meta']['email']['date_start'];
1866
			$out['meta']['email']['date_start'] = $timedate->to_db($dateTimeInUserFormat);
1867
			if ($status == 'error') {
1868
				$writeToCacheFile = false;
1869
			}
1870
			if ($writeToCacheFile) {
1871
				if ($ie->isPop3Protocol()) {
1872
					$this->writeCacheFile('out', $out, $_REQUEST['ieId'], 'messages', "{$_REQUEST['mbox']}{$md5uidl}.php");
1873
				} else {
1874
					$this->writeCacheFile('out', $out, $_REQUEST['ieId'], 'messages', "{$_REQUEST['mbox']}{$_REQUEST['uid']}.php");
1875
				} // else
1876
			// restore date in the users preferred format to be send on to UI for diaply
1877
			$out['meta']['email']['date_start'] = $dateTimeInUserFormat;
1878
			} // if
1879
		}
1880
		$out['meta']['email']['toaddrs'] = $this->generateExpandableAddrs($out['meta']['email']['toaddrs']);
1881
		if(!empty($out['meta']['email']['cc_addrs'])) {
1882
            $ccs = $this->generateExpandableAddrs($out['meta']['email']['cc_addrs']);
1883
		    $out['meta']['cc'] = <<<eoq
1884
				<tr>
1885
					<td NOWRAP valign="top" class="displayEmailLabel">
1886
						{$app_strings['LBL_EMAIL_CC']}:
1887
					</td>
1888
					<td class="displayEmailValue">
1889
						{$ccs}
1890
					</td>
1891
				</tr>
1892
eoq;
1893
		}
1894
1895
		 if(empty($out['meta']['email']['description']))
1896
                $out['meta']['email']['description'] = $mod_strings['LBL_EMPTY_EMAIL_BODY'];
1897
1898
		if($noCache) {
1899
			$GLOBALS['log']->debug("EMAILUI: getSingleMessage() NOT using cache file");
1900
		} else {
1901
			$GLOBALS['log']->debug("EMAILUI: getSingleMessage() using cache file [ ".$_REQUEST['mbox'].$_REQUEST['uid'].".php ]");
1902
		}
1903
1904
		$this->setReadFlag($_REQUEST['ieId'], $_REQUEST['mbox'], $_REQUEST['uid']);
1905
		return $out;
1906
	}
1907
1908
1909
	/**
1910
	 * Returns the HTML for a list of emails in a given folder
1911
	 * @param GUID $ieId GUID to InboundEmail instance
1912
	 * @param string $mbox Mailbox path name in dot notation
1913
	 * @param int $folderListCacheOffset Seconds for valid cache file
1914
	 * @return string HTML render of list.
1915
	 */
1916
	function getListEmails($ieId, $mbox, $folderListCacheOffset, $forceRefresh='false') {
1917
		global $sugar_config;
1918
1919
1920
		$ie = new InboundEmail();
1921
		$ie->retrieve($ieId);
1922
		$list = $ie->displayFolderContents($mbox, $forceRefresh);
0 ignored issues
show
Bug introduced by
The call to displayFolderContents() misses a required argument $page.

This check looks for function calls that miss required arguments.

Loading history...
1923
1924
		return $list;
1925
	}
1926
1927
	/**
1928
	 * Returns the templatized compose screen.  Used by reply, forwards and draft status messages.
1929
	 * @param object email Email bean in focus
1930
	 */
1931
	function displayComposeEmail($email) {
1932
		global $locale;
1933
		global $current_user;
1934
1935
1936
		$ea = new SugarEmailAddress();
1937
1938
		if(!empty($email)) {
1939
		    $email->cids2Links();
1940
			$description = (empty($email->description_html)) ? $email->description : $email->description_html;
1941
		}
1942
1943
		//Get the most complete address list availible for this email
1944
		$addresses = array('toAddresses' => 'to', 'ccAddresses' => 'cc', 'bccAddresses' => 'bcc');
1945
		foreach($addresses as $var => $type)
1946
		{
1947
			$$var = "";
1948
			foreach (array("{$type}_addrs_names", "{$type}addrs", "{$type}_addrs") as $emailVar)
1949
			{
1950
				if (!empty($email->$emailVar)) {
1951
					$$var = $email->$emailVar;
1952
					break;
1953
				}
1954
			}
1955
		}
1956
1957
		$ret = array();
1958
		$ret['type'] = $email->type;
1959
		$ret['name'] = $email->name;
1960
		$ret['description'] = $description;
1961
		$ret['from'] = (isset($_REQUEST['composeType']) && $_REQUEST['composeType'] == 'forward') ? "" : $email->from_addr;
1962
		$ret['to'] = from_html($toAddresses);
0 ignored issues
show
Bug introduced by
The variable $toAddresses seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
1963
		$ret['uid'] = $email->id;
1964
		$ret['parent_name'] = $email->parent_name;
1965
		$ret['parent_type'] = $email->parent_type;
1966
		$ret['parent_id'] = $email->parent_id;
1967
1968
       if ($email->type == 'draft') {
1969
            $ret['cc'] = from_html($ccAddresses);
0 ignored issues
show
Bug introduced by
The variable $ccAddresses does not exist. Did you mean $addresses?

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...
1970
            $ret['bcc'] = $bccAddresses;
0 ignored issues
show
Bug introduced by
The variable $bccAddresses does not exist. Did you mean $addresses?

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...
1971
        }
1972
		// reply all
1973
		if(isset($_REQUEST['composeType']) && $_REQUEST['composeType'] == 'replyAll') {
1974
		    $ret['cc'] = from_html($ccAddresses);
0 ignored issues
show
Bug introduced by
The variable $ccAddresses does not exist. Did you mean $addresses?

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...
1975
		    $ret['bcc'] = $bccAddresses;
0 ignored issues
show
Bug introduced by
The variable $bccAddresses does not exist. Did you mean $addresses?

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...
1976
1977
			$userEmails = array();
1978
			$userEmailsMeta = $ea->getAddressesByGUID($current_user->id, 'Users');
1979
			foreach($userEmailsMeta as $emailMeta) {
1980
				$userEmails[] = from_html(strtolower(trim($emailMeta['email_address'])));
1981
			}
1982
			$userEmails[] = from_html(strtolower(trim($email->from_addr)));
1983
1984
			$ret['cc'] = from_html($email->cc_addrs);
1985
			$toAddresses = from_html($toAddresses);
0 ignored issues
show
Bug introduced by
The variable $toAddresses seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
1986
			$to = str_replace($this->addressSeparators, "::", $toAddresses);
1987
			$exTo = explode("::", $to);
1988
1989
			if(is_array($exTo)) {
1990
				foreach($exTo as $addr) {
1991
					$addr = strtolower(trim($addr));
1992
					if(!in_array($addr, $userEmails)) {
1993
						if(!empty($ret['cc'])) {
1994
							$ret['cc'] = $ret['cc'].", ";
1995
						}
1996
						$ret['cc'] = $ret['cc'].trim($addr);
1997
					}
1998
				}
1999
			} elseif(!empty($exTo)) {
2000
				$exTo = trim($exTo);
2001
				if(!in_array($exTo, $userEmails)) {
2002
					$ret['cc'] = $ret['cc'].", ".$exTo;
2003
				}
2004
			}
2005
		}
2006
		return $ret;
2007
	}
2008
	/**
2009
	 * Formats email body on reply/forward
2010
	 * @param object email Email object in focus
2011
	 * @param string type
2012
	 * @return object email
2013
	 */
2014
	function handleReplyType($email, $type) {
2015
		global $mod_strings;
2016
		 $GLOBALS['log']->debug("****At Handle Reply Type: $type");
2017
		switch($type) {
2018
			case "reply":
2019
			case "replyAll":
2020
				$header = $email->getReplyHeader();
2021
                if(!preg_match('/^(re:)+/i', $email->name)) {
2022
                    $email->name = "{$mod_strings['LBL_RE']} {$email->name}";
2023
                }
2024
				if ($type == "reply") {
2025
					$email->cc_addrs = "";
2026
					if (!empty($email->reply_to_addr)) {
2027
						$email->from_addr = $email->reply_to_addr;
2028
					} // if
2029
				} else {
2030
					if (!empty($email->reply_to_addr)) {
2031
						$email->to_addrs = $email->to_addrs . "," . $email->reply_to_addr;
2032
					} // if
2033
				} // else
2034
			break;
2035
2036
			case "forward":
2037
				$header = $email->getForwardHeader();
2038
				if(!preg_match('/^(fw:)+/i', $email->name)) {
2039
                    $email->name = "{$mod_strings['LBL_FW']} {$email->name}";
2040
                }
2041
				$email->cc_addrs = "";
2042
			break;
2043
2044
			case "replyCase":
2045
				$GLOBALS['log']->debug("EMAILUI: At reply case");
2046
				$header = $email->getReplyHeader();
2047
2048
                $myCase = new aCase();
2049
                $myCase->retrieve($email->parent_id);
2050
                $myCaseMacro = $myCase->getEmailSubjectMacro();
2051
                $email->parent_name = $myCase->name;
2052
                $GLOBALS['log']->debug("****Case # : {$myCase->case_number} macro: $myCaseMacro");
2053
				if(!strpos($email->name, str_replace('%1',$myCase->case_number,$myCaseMacro))) {
2054
		        	$GLOBALS['log']->debug("Replacing");
2055
		            $email->name = str_replace('%1',$myCase->case_number,$myCaseMacro) . ' '. $email->name;
2056
		        }
2057
                $email->name = "{$mod_strings['LBL_RE']} {$email->name}";
2058
            break;
2059
		}
2060
2061
		$html = trim($email->description_html);
2062
		$plain = trim($email->description);
2063
2064
		$desc = (!empty($html)) ? $html : $plain;
2065
2066
		$email->description = $header.$email->quoteHtmlEmailForNewEmailUI($desc);
2067
		return $email;
2068
2069
	}
2070
2071
	///////////////////////////////////////////////////////////////////////////
2072
	////	PRIVATE HELPERS
2073
	/**
2074
	 * Generates a UNION query to get one list of users, contacts, leads, and
2075
	 * prospects; used specifically for the addressBook
2076
	 */
2077
	function _getPeopleUnionQuery($whereArr , $person) {
2078
		global $current_user , $app_strings;
2079
		global $db;
2080
		if(!isset($person) || $person === 'LBL_DROPDOWN_LIST_ALL'){
2081
			$peopleTables = array("users",
2082
			                      "contacts",
2083
			                      "leads",
2084
			                      "prospects",
2085
			                      "accounts"
2086
			                     );
2087
		}else{
2088
			$peopleTables = array($person);
2089
		}
2090
		$q = '';
2091
2092
		$whereAdd = "";
2093
2094
		foreach($whereArr as $column => $clause) {
2095
			if(!empty($whereAdd)) {
2096
				$whereAdd .= " AND ";
2097
			}
2098
			$clause = $current_user->db->quote($clause);
2099
			$whereAdd .= "{$column} LIKE '{$clause}%'";
2100
		}
2101
2102
2103
		foreach($peopleTables as $table) {
2104
			$module = ucfirst($table);
2105
            $class = substr($module, 0, strlen($module) - 1);
2106
            require_once("modules/{$module}/{$class}.php");
2107
            $person = new $class();
2108
			if (!$person->ACLAccess('list')) {
2109
				continue;
2110
			} // if
2111
			$where = "({$table}.deleted = 0 AND eabr.primary_address = 1 AND {$table}.id <> '{$current_user->id}')";
2112
2113
            if (ACLController::requireOwner($module, 'list')) {
2114
            	$where = $where . " AND ({$table}.assigned_user_id = '{$current_user->id}')";
2115
            } // if
2116
			if(!empty($whereAdd)) {
2117
				$where .= " AND ({$whereAdd})";
2118
			}
2119
2120
			if ($person === 'accounts') {
2121
				$t = "SELECT {$table}.id, '' first_name, {$table}.name, eabr.primary_address, ea.email_address, '{$module}' module ";
2122
			} else {
2123
				$t = "SELECT {$table}.id, {$table}.first_name, {$table}.last_name, eabr.primary_address, ea.email_address, '{$module}' module ";
2124
			}
2125
			$t .= "FROM {$table} ";
2126
			$t .= "JOIN email_addr_bean_rel eabr ON ({$table}.id = eabr.bean_id and eabr.deleted=0) ";
2127
			$t .= "JOIN email_addresses ea ON (eabr.email_address_id = ea.id) ";
2128
			$t .= " WHERE {$where}";
2129
2130
			/* BEGIN - SECURITY GROUPS */
2131
			//this function may not even be used anymore. Seems like findEmailFromBeanIds is preferred now
2132
			if($person->bean_implements('ACL') && ACLController::requireSecurityGroup($module, 'list') )
2133
			{
2134
				require_once('modules/SecurityGroups/SecurityGroup.php');
2135
				global $current_user;
2136
				$owner_where = $person->getOwnerWhere($current_user->id);
2137
				$group_where = SecurityGroup::getGroupWhere($table,$module,$current_user->id);
2138
				$t .= " AND (".  $owner_where." or ".$group_where.") ";
2139
			}
2140
			/* END - SECURITY GROUPS */
2141
2142
2143
			if(!empty($q)) {
2144
				$q .= "\n UNION ALL \n";
2145
			}
2146
2147
			$q .= "({$t})";
2148
		}
2149
		$countq = "SELECT count(people.id) c from ($q) people";
2150
		$q .= "ORDER BY last_name";
2151
2152
		return array('query' => $q, 'countQuery' => $countq);
2153
    }
2154
2155
    /**
2156
     * get emails of related bean for a given bean id
2157
     * @param $beanType
2158
     * @param $condition array of conditions inclued bean id
2159
     * @return array('query' => $q, 'countQuery' => $countq);
0 ignored issues
show
Documentation introduced by
The doc-type array('query' could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
2160
     */
2161
    function getRelatedEmail($beanType, $whereArr, $relatedBeanInfoArr = ''){
2162
    	global $beanList, $current_user, $app_strings, $db;
2163
    	$finalQuery = '';
2164
		$searchBeans = null;
2165
		if($beanType === 'LBL_DROPDOWN_LIST_ALL')
2166
			$searchBeans = array("users",
2167
			                     "contacts",
2168
			                     "leads",
2169
			                     "prospects",
2170
			                     "accounts"
2171
			                    );
2172
2173
    	if ($relatedBeanInfoArr == '' || empty($relatedBeanInfoArr['related_bean_type']) )
2174
    	{
2175
			if ($searchBeans != null)
2176
			{
2177
				$q = array();
2178
				foreach ($searchBeans as $searchBean)
2179
				{
2180
				    $searchq = $this->findEmailFromBeanIds('', $searchBean, $whereArr);
2181
				    if(!empty($searchq)) {
2182
					    $q[] = "($searchq)";
2183
				    }
2184
				}
2185
				if (!empty($q))
2186
    			    $finalQuery .= implode("\n UNION ALL \n", $q);
2187
			}
2188
			else
2189
				$finalQuery = $this->findEmailFromBeanIds('', $beanType, $whereArr);
2190
    	}
2191
    	else
2192
    	{
2193
    	    $class = $beanList[$relatedBeanInfoArr['related_bean_type']];
2194
    	    $focus = new $class();
2195
    	    $focus->retrieve($relatedBeanInfoArr['related_bean_id']);
2196
    	    if ($searchBeans != null)
2197
    	    {
2198
    	        $q = array();
2199
    	        foreach ($searchBeans as $searchBean)
2200
    	        {
2201
    	            if ($focus->load_relationship($searchBean))
2202
    	            {
2203
    	                $data = $focus->$searchBean->get();
2204
    	                if (count($data) != 0)
2205
    	                $q[] = '('.$this->findEmailFromBeanIds($data, $searchBean, $whereArr).')';
2206
    	            }
2207
    	        }
2208
    	        if (!empty($q))
2209
    	        $finalQuery .= implode("\n UNION ALL \n", $q);
2210
    	    }
2211
    	    else
2212
    	    {
2213
    	        if ($focus->load_relationship($beanType))
2214
    	        {
2215
    	            $data = $focus->$beanType->get();
2216
    	            if (count($data) != 0)
2217
    	            $finalQuery = $this->findEmailFromBeanIds($data, $beanType, $whereArr);
2218
    	        }
2219
    	    }
2220
    	}
2221
    	$countq = "SELECT count(people.id) c from ($finalQuery) people";
2222
	   	return array('query' => $finalQuery, 'countQuery' => $countq);
2223
    }
2224
2225
    function findEmailFromBeanIds($beanIds, $beanType, $whereArr) {
2226
    	global $current_user;
2227
		$q = '';
2228
		$whereAdd = "";
2229
		$relatedIDs = '';
2230
		if ($beanIds != '') {
2231
			foreach ($beanIds as $key => $value) {
2232
				$beanIds[$key] = '\''.$value.'\'';
2233
			}
2234
			$relatedIDs = implode(',', $beanIds);
2235
		}
2236
2237
		if ($beanType == 'accounts') {
2238
			if (isset($whereArr['first_name'])) {
2239
				$whereArr['name'] = $whereArr['first_name'];
2240
			}
2241
			unset($whereArr['last_name']);
2242
			unset($whereArr['first_name']);
2243
		}
2244
2245
		foreach($whereArr as $column => $clause) {
2246
			if(!empty($whereAdd)) {
2247
				$whereAdd .= " OR ";
2248
			}
2249
			$clause = $current_user->db->quote($clause);
2250
			$whereAdd .= "{$column} LIKE '{$clause}%'";
2251
		}
2252
		$table = $beanType;
2253
		$module = ucfirst($table);
2254
	    $class = substr($module, 0, strlen($module) - 1);
2255
	    require_once("modules/{$module}/{$class}.php");
2256
	    $person = new $class();
2257
		if ($person->ACLAccess('list')) {
2258
			if ($relatedIDs != '') {
2259
				$where = "({$table}.deleted = 0 AND eabr.primary_address = 1 AND {$table}.id in ($relatedIDs))";
2260
			} else {
2261
				$where = "({$table}.deleted = 0 AND eabr.primary_address = 1)";
2262
			}
2263
2264
			if (ACLController::requireOwner($module, 'list')) {
2265
				$where = $where . " AND ({$table}.assigned_user_id = '{$current_user->id}')";
2266
			} // if
2267
			if(!empty($whereAdd)) {
2268
				$where .= " AND ({$whereAdd})";
2269
			}
2270
2271
			if ($beanType === 'accounts') {
2272
				$t = "SELECT {$table}.id, '' first_name, {$table}.name last_name, eabr.primary_address, ea.email_address, '{$module}' module ";
2273
			} else {
2274
				$t = "SELECT {$table}.id, {$table}.first_name, {$table}.last_name, eabr.primary_address, ea.email_address, '{$module}' module ";
2275
			}
2276
2277
			$t .= "FROM {$table} ";
2278
			$t .= "JOIN email_addr_bean_rel eabr ON ({$table}.id = eabr.bean_id and eabr.deleted=0) ";
2279
			$t .= "JOIN email_addresses ea ON (eabr.email_address_id = ea.id) ";
2280
			$t .= " WHERE {$where}";
2281
			/* BEGIN - SECURITY GROUPS */
2282
			//this function may not even be used anymore. Seems like findEmailFromBeanIds is preferred now
2283
			if($person->bean_implements('ACL') && ACLController::requireSecurityGroup($module, 'list') )
2284
			{
2285
				require_once('modules/SecurityGroups/SecurityGroup.php');
2286
				global $current_user;
2287
				$owner_where = $person->getOwnerWhere($current_user->id);
2288
				$group_where = SecurityGroup::getGroupWhere($table,$module,$current_user->id);
2289
				$t .= " AND (".  $owner_where." or ".$group_where.") ";
2290
			}
2291
			/* END - SECURITY GROUPS */
2292
2293
		} // if
2294
		return $t;
2295
    }
2296
2297
	/**
2298
	 * Cleans UID lists
2299
	 * @param mixed $uids
2300
	 * @param bool $returnString False will return an array
2301
	 * @return mixed
2302
	 */
2303 2
	function _cleanUIDList($uids, $returnString=false) {
2304 2
		global $app_strings;
2305 2
		$GLOBALS['log']->debug("_cleanUIDList: before - [ {$uids} ]");
2306
2307 2
		if(!is_array($uids)) {
2308 2
			$returnString = true;
2309
2310 2
			$exUids = explode($app_strings['LBL_EMAIL_DELIMITER'], $uids);
2311 2
			$uids = $exUids;
2312
		}
2313
2314 2
		$cleanUids = array();
2315 2
		foreach($uids as $uid) {
2316 2
			$cleanUids[$uid] = $uid;
2317
		}
2318
2319 2
		sort($cleanUids);
2320
2321 2
		if($returnString) {
2322 2
			$cleanImplode = implode($app_strings['LBL_EMAIL_DELIMITER'], $cleanUids);
2323 2
			$GLOBALS['log']->debug("_cleanUIDList: after - [ {$cleanImplode} ]");
2324 2
			return $cleanImplode;
2325
		}
2326
2327
		return $cleanUids;
2328
	}
2329
2330
    /**
2331
     * Creates Mail folder
2332
     *
2333
     * @param object $user User in focus
2334
     * @param array $folder_params Array of parameters for folder creation
2335
     */
2336
    protected function createFolder($user, $folder_params)
2337
    {
2338
        $folder = new SugarFolder();
2339
        foreach ($folder_params as $key => $val) {
2340
            $folder->$key = $val;
2341
        }
2342
2343
        $folder->save();
2344
2345
        return $folder;
2346
    }
2347
2348
	/**
2349
	 * Creates defaults for the User
2350
	 * @param object $user User in focus
2351
	 */
2352
    public function preflightUser(&$user)
2353
    {
2354
        global $mod_strings;
2355
        $folder_types = array();
2356
2357
        $params = array(
2358
            // My Emails
2359
            "inbound" => array(
2360
                'name' => $mod_strings['LNK_MY_INBOX'],
2361
                'folder_type' => "inbound",
2362
                'has_child' => 1,
2363
                'dynamic_query' => $this->generateDynamicFolderQuery("inbound", $user->id),
2364
                'is_dynamic' => 1,
2365
                'created_by' => $user->id,
2366
                'modified_by' => $user->id,
2367
            ),
2368
            // My Drafts
2369
            "draft" => array(
2370
                'name' => $mod_strings['LNK_MY_DRAFTS'],
2371
                'folder_type' => "draft",
2372
                'has_child' => 0,
2373
                'dynamic_query' => $this->generateDynamicFolderQuery("draft", $user->id),
2374
                'is_dynamic' => 1,
2375
                'created_by' => $user->id,
2376
                'modified_by' => $user->id,
2377
            ),
2378
            // Sent Emails
2379
            "sent" => array(
2380
                'name' => $mod_strings['LNK_SENT_EMAIL_LIST'],
2381
                'folder_type' => "sent",
2382
                'has_child' => 0,
2383
                'dynamic_query' => $this->generateDynamicFolderQuery("sent", $user->id),
2384
                'is_dynamic' => 1,
2385
                'created_by' => $user->id,
2386
                'modified_by' => $user->id,
2387
            ),
2388
            // Archived Emails
2389
            "archived" => array(
2390
                'name' => $mod_strings['LBL_LIST_TITLE_MY_ARCHIVES'],
2391
                'folder_type' => "archived",
2392
                'has_child' => 0,
2393
                'dynamic_query' => '',
2394
                'is_dynamic' => 1,
2395
                'created_by' => $user->id,
2396
                'modified_by' => $user->id,
2397
            ),
2398
        );
2399
2400
        $q = "SELECT * FROM folders f WHERE f.created_by = '{$user->id}' AND f.deleted = 0 AND coalesce(" . $user->db->convert("f.folder_type", "length") . ",0) > 0";
2401
        $r = $user->db->query($q);
2402
2403
        while ($row = $GLOBALS['db']->fetchByAssoc($r)) {
2404
            if ($row['folder_type'] == 'inbound') {
2405
                $parent_id = $row['id'];
2406
            }
2407
            if (!in_array($row['folder_type'], $folder_types)) {
2408
                array_push($folder_types, $row['folder_type']);
2409
            }
2410
            if (isset($params[$row['folder_type']])) {
2411
                unset($params[$row['folder_type']]);
2412
            }
2413
        }
2414
2415
        require_once("include/SugarFolders/SugarFolders.php");
2416
2417
        foreach ($params as $type => $type_params) {
2418
            if ($type == "inbound") {
2419
2420
                $folder = $this->createFolder($user, $params[$type]);
2421
2422
                $parent_id = $folder->id;
2423
2424
                // handle the case where inbound folder was deleted, but other folders exist
2425
                if (count($folder_types) != 0) {
2426
                    // This update query will exclude inbound parent, and any custom created folders.
2427
                    // For others, it will update their parent_id for the current user.
2428
                    $q = "UPDATE folders SET parent_folder = '".$parent_id.
2429
                    "' WHERE folder_type IN ('draft', 'sent', 'archived') AND created_by = '".$user->id."'";
2430
                    $q = "UPDATE folders SET parent_folder = '".$parent_id.
2431
                    "' WHERE folder_type IN ('draft', 'sent', 'archived') AND created_by = '".$user->id."'";
2432
                    $r = $user->db->query($q);
2433
                }
2434
            } else {
2435
                $params[$type]['parent_folder'] = $parent_id;
2436
2437
                $this->createFolder($user, $params[$type]);
2438
            }
2439
        }
2440
    }
2441
2442
	/**
2443
	 * Parses the core dynamic folder query
2444
	 * @param string $type 'inbound', 'draft', etc.
2445
	 * @param string $userId
2446
	 * @return string
2447
	 */
2448
	function generateDynamicFolderQuery($type, $userId) {
2449
		$q = $this->coreDynamicFolderQuery;
2450
2451
		$status = $type;
2452
2453
		if($type == "sent") {
2454
			$type = "out";
2455
		}
2456
2457
		$replacee = array("::TYPE::", "::STATUS::", "::USER_ID::");
2458
		$replacer = array($type, $status, $userId);
2459
2460
		$ret = str_replace($replacee, $replacer, $q);
2461
2462
		if($type == 'inbound') {
2463
			$ret .= " AND status NOT IN ('sent', 'archived', 'draft') AND type NOT IN ('out', 'archived', 'draft')";
2464
		} else {
2465
			$ret .= " AND status NOT IN ('archived') AND type NOT IN ('archived')";
2466
		}
2467
2468
		return $ret;
2469
	}
2470
2471
	/**
2472
	 * Preps the User's cache dir
2473
	 */
2474
	function preflightUserCache() {
2475
		$path = clean_path($this->userCacheDir);
2476
		if(!file_exists($this->userCacheDir))
2477
			mkdir_recursive($path);
2478
2479
		$files = findAllFiles($path, array());
2480
2481
		foreach($files as $file) {
2482
			unlink($file);
2483
		}
2484
	}
2485
2486
	function clearInboundAccountCache($ieId) {
2487
		global $sugar_config;
2488
		$cacheRoot = sugar_cached("modules/Emails/{$ieId}");
2489
		$files = findAllFiles($cacheRoot."/messages/", array());
2490
		foreach($files as $file) {
2491
			unlink($file);
2492
		} // fn
2493
		$files = findAllFiles($cacheRoot."/attachments/", array());
2494
		foreach($files as $file) {
2495
			unlink($file);
2496
		} // for
2497
	} // fn
2498
2499
	/**
2500
	 * returns an array of EmailTemplates that the user has access to for the compose email screen
2501
	 * @return array
2502
	 */
2503
	function getEmailTemplatesArray() {
2504
2505
		global $app_strings;
2506
2507
		if(ACLController::checkAccess('EmailTemplates', 'list', true) && ACLController::checkAccess('EmailTemplates', 'view', true)) {
2508
			$et = new EmailTemplate();
2509
            $etResult = $et->db->query($et->create_new_list_query('',"(email_templates.type IS NULL OR email_templates.type='' OR email_templates.type='email')",array(),array(),''));
0 ignored issues
show
Bug introduced by
It seems like $et->create_new_list_que..., array(), array(), '') targeting SugarBean::create_new_list_query() can also be of type array<string,?>; however, DBManager::query() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
2510
			$email_templates_arr = array('' => $app_strings['LBL_NONE']);
2511
			while($etA = $et->db->fetchByAssoc($etResult)) {
2512
				$email_templates_arr[$etA['id']] = $etA['name'];
2513
			}
2514
		} else {
2515
			$email_templates_arr = array('' => $app_strings['LBL_NONE']);
2516
		}
2517
2518
		return $email_templates_arr;
2519
	}
2520
2521
	function getFromAccountsArray($ie) {
2522
        global $current_user;
2523
        global $app_strings;
2524
2525
        $ieAccountsFull = $ie->retrieveAllByGroupIdWithGroupAccounts($current_user->id);
2526
        $ieAccountsFrom= array();
2527
2528
        $oe = new OutboundEmail();
2529
        $system = $oe->getSystemMailerSettings();
2530
        $ret = $current_user->getUsersNameAndEmail();
2531
		$ret['name'] = from_html($ret['name']);
2532
		$useMyAccountString = true;
2533
2534
        if(empty($ret['email'])) {
2535
        	$systemReturn = $current_user->getSystemDefaultNameAndEmail();
2536
        	$ret['email'] = $systemReturn['email'];
2537
        	$ret['name'] = from_html($systemReturn['name']);
2538
        	$useMyAccountString = false;
2539
		} // if
2540
2541
		$myAccountString = '';
2542
		if ($useMyAccountString) {
2543
			$myAccountString = " - {$app_strings['LBL_MY_ACCOUNT']}";
2544
		} // if
2545
2546
		//Check to make sure that the user has set the associated inbound email account -> outbound account is active.
2547
		$showFolders = unserialize(base64_decode($current_user->getPreference('showFolders', 'Emails')));
2548
        $sf = new SugarFolder();
2549
        $groupSubs = $sf->getSubscriptions($current_user);
2550
2551
        foreach($ieAccountsFull as $k => $v)
2552
        {
2553
            $personalSelected = (!empty($showFolders) && in_array($v->id, $showFolders));
2554
2555
            $allowOutboundGroupUsage = $v->get_stored_options('allow_outbound_group_usage',FALSE);
2556
            $groupSelected = ( in_array($v->groupfolder_id, $groupSubs)  && $allowOutboundGroupUsage);
2557
            $selected = ( $personalSelected || $groupSelected );
2558
2559
            if(!$selected)
2560
            {
2561
                $GLOBALS['log']->debug("Inbound Email {$v->name}, not selected and will not be available for selection within compose UI.");
2562
                continue;
2563
            }
2564
2565
        	$name = $v->get_stored_options('from_name');
2566
        	$addr = $v->get_stored_options('from_addr');
2567
        	if ($name != null && $addr != null) {
2568
        		$name = from_html($name);
2569
        		if (!$v->is_personal) {
2570
                	$ieAccountsFrom[] = array("value" => $v->id, "text" => "{$name} ({$addr}) - {$app_strings['LBL_EMAIL_UPPER_CASE_GROUP']}");
2571
        		} else {
2572
                	$ieAccountsFrom[] = array("value" => $v->id, "text" => "{$name} ({$addr})");
2573
        		} // else
2574
        	} // if
2575
        } // foreach
2576
2577
2578
        $userSystemOverride = $oe->getUsersMailerForSystemOverride($current_user->id);
2579
        //Substitute in the users system override if its available.
2580
        if($userSystemOverride != null)
2581
		    $system = $userSystemOverride;
2582
2583
        if( !empty($system->mail_smtpserver) )
2584
        {
2585
            $admin = new Administration();
2586
            $admin->retrieveSettings(); //retrieve all admin settings.
2587
            $ieAccountsFrom[] = array("value" => $system->id, "text" =>
2588
                "{$ret['name']} ({$ret['email']}){$myAccountString}");
2589
        }
2590
2591
        return $ieAccountsFrom;
2592
    } // fn
2593
2594
    /**
2595
     * This function will return all the accounts this user has access to based on the
2596
     * match of the emailId passed in as a parameter
2597
     *
2598
     * @param unknown_type $ie
2599
     * @return unknown
2600
     */
2601
	function getFromAllAccountsArray($ie, $ret) {
2602
        global $current_user;
2603
        global $app_strings;
2604
2605
        $ret['fromAccounts'] = array();
2606
        if (!isset($ret['to']) && !empty($ret['from'])) {
2607
	        $ret['fromAccounts']['status'] = false;
2608
	        return $ret;
2609
        }
2610
        $ieAccountsFull = $ie->retrieveAllByGroupIdWithGroupAccounts($current_user->id);
2611
		$foundInPersonalAccounts = false;
2612
		$foundInGroupAccounts = false;
2613
		$foundInSystemAccounts = false;
2614
2615
		//$toArray = array();
2616
		if ($ret['type'] == "draft") {
2617
			$toArray = explode(",", $ret['from']);
2618
		} else {
2619
			$toArray = $ie->email->email2ParseAddressesForAddressesOnly($ret['to']);
2620
		} // else
2621
        foreach($ieAccountsFull as $k => $v) {
2622
        	$storedOptions = unserialize(base64_decode($v->stored_options));
2623
			if (  array_search_insensitive($storedOptions['from_addr'], $toArray)) {
2624
        		if ($v->is_personal) {
2625
					$foundInPersonalAccounts = true;
2626
					break;
2627
				} else  {
2628
					$foundInGroupAccounts = true;
2629
				} // else
2630
			} // if
2631
        } // foreach
2632
2633
	    $oe = new OutboundEmail();
2634
        $system = $oe->getSystemMailerSettings();
2635
2636
        $return = $current_user->getUsersNameAndEmail();
2637
		$return['name'] = from_html($return['name']);
2638
		$useMyAccountString = true;
2639
2640
        if(empty($return['email'])) {
2641
        	$systemReturn = $current_user->getSystemDefaultNameAndEmail();
2642
        	$return['email'] = $systemReturn['email'];
2643
        	$return['name'] = from_html($systemReturn['name']);
2644
        	$useMyAccountString = false;
2645
		} // if
2646
2647
		$myAccountString = '';
2648
		if ($useMyAccountString) {
2649
			$myAccountString = " - {$app_strings['LBL_MY_ACCOUNT']}";
2650
		} // if
2651
2652
        if(!empty($system->id)) {
2653
2654
            $admin = new Administration();
2655
            $admin->retrieveSettings(); //retrieve all admin settings.
2656
            if (in_array(trim($return['email']), $toArray)) {
2657
            	$foundInSystemAccounts = true;
2658
            } // if
2659
        } // if
2660
2661
        if (!$foundInPersonalAccounts && !$foundInGroupAccounts && !$foundInSystemAccounts) {
2662
	        $ret['fromAccounts']['status'] = false;
2663
	        return $ret;
2664
        } // if
2665
2666
        $ieAccountsFrom= array();
2667
        foreach($ieAccountsFull as $k => $v) {
2668
        	$storedOptions = unserialize(base64_decode($v->stored_options));
2669
        	$storedOptionsName = from_html($storedOptions['from_name']);
2670
2671
        	$selected = false;
2672
			if (array_search_insensitive($storedOptions['from_addr'], $toArray)) {
2673
        	//if ($ret['to'] == $storedOptions['from_addr']) {
2674
        		$selected = true;
2675
			} // if
2676
        	if ($foundInPersonalAccounts) {
2677
        		if ($v->is_personal) {
2678
            		$ieAccountsFrom[] = array("value" => $v->id, "selected" => $selected, "text" => "{$storedOptionsName} ({$storedOptions['from_addr']})");
2679
        		} // if
2680
        	} else {
2681
            	$ieAccountsFrom[] = array("value" => $v->id, "selected" => $selected, "text" => "{$storedOptionsName} ({$storedOptions['from_addr']}) - {$app_strings['LBL_EMAIL_UPPER_CASE_GROUP']}");
2682
        	} // else
2683
        } // foreach
2684
2685
        if(!empty($system->id)) {
2686
            if (!$foundInPersonalAccounts && !$foundInGroupAccounts && $foundInSystemAccounts) {
2687
            $ieAccountsFrom[] = array("value" => $system->id, "selected" => true, "text" =>
2688
                "{$return['name']} ({$return['email']}){$myAccountString}");
2689
            } else {
2690
            $ieAccountsFrom[] = array("value" => $system->id, "text" =>
2691
                "{$return['name']} ({$return['email']}){$myAccountString}");
2692
            } // else
2693
        } // if
2694
2695
        $ret['fromAccounts']['status'] = ($foundInPersonalAccounts || $foundInGroupAccounts || $foundInSystemAccounts) ? true : false;
2696
		$ret['fromAccounts']['data'] = $ieAccountsFrom;
2697
        return $ret;
2698
    } // fn
2699
2700
2701
	/**
2702
	 * takes an array and creates XML
2703
	 * @param array Array to convert
2704
	 * @param string Name to wrap highest level items in array
2705
	 * @return string XML
2706
	 */
2707
	function arrayToXML($a, $paramName) {
2708
		if(!is_array($a))
2709
			return '';
2710
2711
		$bad = array("<",">","'",'"',"&");
2712
		$good = array("&lt;", "&gt;", "&#39;", "&quot;","&amp;");
2713
2714
		$ret = "";
2715
2716
		for($i=0; $i<count($a); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
2717
			$email = $a[$i];
2718
			$ret .= "\n<{$paramName}>";
2719
2720
			foreach($email as $k => $v) {
2721
				$ret .= "\n\t<{$k}>".str_replace($bad, $good, $v)."</{$k}>";
2722
			}
2723
			$ret .= "\n</{$paramName}>";
2724
		}
2725
		return $ret;
2726
	}
2727
2728
	/**
2729
	 * Re-used option getter for Show Accounts multiselect pane
2730
	 */
2731
	function getShowAccountsOptions(&$ie) {
2732
		global $current_user;
2733
		global $app_strings;
2734
		global $mod_strings;
2735
2736
		$ieAccountsFull = $ie->retrieveAllByGroupId($current_user->id);
2737
		$ieAccountsShowOptionsMeta = array();
2738
		$showFolders = unserialize(base64_decode($current_user->getPreference('showFolders', 'Emails')));
2739
2740
		$defaultIEAccount = $ie->getUsersDefaultOutboundServerId($current_user);
2741
2742
		foreach($ieAccountsFull as $k => $v) {
2743
			$selected = (!empty($showFolders) && in_array($v->id, $showFolders)) ? true : false;
2744
			$default = ($defaultIEAccount == $v->id) ? TRUE : FALSE;
2745
			$has_groupfolder = !empty($v->groupfolder_id) ? TRUE : FALSE;
2746
			$type = ($v->is_personal) ? $mod_strings['LBL_MAILBOX_TYPE_PERSONAL'] : $mod_strings['LBL_MAILBOX_TYPE_GROUP'];
2747
2748
			$ieAccountsShowOptionsMeta[] = array("id" => $v->id, "name" => $v->name, 'is_active' => $selected,
2749
													'server_url' => $v->server_url, 'is_group' => !$v->is_personal,'group_id' => $v->group_id,
2750
													'is_default' => $default, 'has_groupfolder' => $has_groupfolder,'type' => $type );
2751
		}
2752
2753
		//Retrieve the grou folders
2754
		$f = new SugarFolder();
2755
		$groupFolders = $f->getGroupFoldersForSettings($current_user);
2756
2757
		foreach ($groupFolders as $singleGroup)
2758
		{
2759
		    //Retrieve the related IE accounts.
2760
            $relatedIEAccounts = $ie->retrieveByGroupFolderId($singleGroup['id']);
2761
2762
            if(count($relatedIEAccounts) == 0)
2763
                $server_url = $app_strings['LBL_EMAIL_MULT_GROUP_FOLDER_ACCOUNTS_EMPTY'];
2764
            else if(count($relatedIEAccounts) == 1)
2765
            {
2766
                if($relatedIEAccounts[0]->status != 'Active' || $relatedIEAccounts[0]->mailbox_type == 'bounce')
2767
                    continue;
2768
2769
                $server_url = $relatedIEAccounts[0]->server_url;
2770
            }
2771
            else
2772
                $server_url = $app_strings['LBL_EMAIL_MULT_GROUP_FOLDER_ACCOUNTS'];
2773
2774
            $type = $mod_strings['LBL_MAILBOX_TYPE_GROUP_FOLDER'];
2775
		    $ieAccountsShowOptionsMeta[] = array("id" => $singleGroup['id'], "name" => $singleGroup['origName'], 'is_active' => $singleGroup['selected'],
2776
													'server_url' => $server_url, 'is_group' => true,'group_id' => $singleGroup['id'],
2777
													'is_default' => FALSE, 'has_groupfolder' => true,'type' => $type);
2778
		}
2779
2780
2781
		return $ieAccountsShowOptionsMeta;
2782
	}
2783
2784
	function getShowAccountsOptionsForSearch(&$ie) {
2785
		global $current_user;
2786
		global $app_strings;
2787
2788
		$ieAccountsFull = $ie->retrieveAllByGroupId($current_user->id);
2789
		//$ieAccountsShowOptions = "<option value=''>{$app_strings['LBL_NONE']}</option>\n";
2790
		$ieAccountsShowOptionsMeta = array();
2791
		$ieAccountsShowOptionsMeta[] = array("value" => "", "text" => $app_strings['LBL_NONE'], 'selected' => '');
2792
		$showFolders = unserialize(base64_decode($current_user->getPreference('showFolders', 'Emails')));
2793
2794
		foreach($ieAccountsFull as $k => $v) {
2795
			if(!in_array($v->id, $showFolders)) {
2796
				continue;
2797
			}
2798
			$group = (!$v->is_personal) ? $app_strings['LBL_EMAIL_GROUP']."." : "";
2799
			$ieAccountsShowOptionsMeta[] = array("value" => $v->id, "text" => $group.$v->name, 'protocol' => $v->protocol);
2800
		}
2801
2802
		return $ieAccountsShowOptionsMeta;
2803
	}
2804
	/**
2805
	 * Formats a display message on successful async call
2806
	 * @param string $type Type of message to display
2807
	 */
2808
	function displaySuccessMessage($type) {
2809
		global $app_strings;
2810
2811
		switch($type) {
2812
			case "delete":
2813
				$message = $app_strings['LBL_EMAIL_DELETE_SUCCESS'];
2814
			break;
2815
2816
			default:
2817
				$message = "NOOP: invalid type";
2818
			break;
2819
		}
2820
2821
		$this->smarty->assign('app_strings', $app_strings);
2822
		$this->smarty->assign('message', $message);
2823
		echo $this->smarty->fetch("modules/Emails/templates/successMessage.tpl");
2824
	}
2825
2826
	/**
2827
	 * Validates existence and expiration of a cache file
2828
	 * @param string $ieId
2829
	 * @param string $type Type of cache file: folders, messages, etc.
2830
	 * @param string $file The cachefile name
2831
	 * @param int refreshOffset Refresh time in secs.
2832
	 * @return mixed.
0 ignored issues
show
Documentation introduced by
The doc-type mixed. could not be parsed: Unknown type name "mixed." at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
2833
	 */
2834
	function validCacheFileExists($ieId, $type, $file, $refreshOffset=-1) {
2835
		global $sugar_config;
2836
2837
		if($refreshOffset == -1) {
2838
			$refreshOffset = $this->cacheTimeouts[$type]; // use defaults
2839
		}
2840
2841
		$cacheFilePath = sugar_cached("modules/Emails/{$ieId}/{$type}/{$file}");
2842
		if(file_exists($cacheFilePath)) {
2843
			return true;
2844
		}
2845
2846
		return false;
2847
	}
2848
2849
	/**
2850
	 * retrieves the cached value
2851
	 * @param string $ieId
2852
	 * @param string $type Type of cache file: folders, messages, etc.
2853
	 * @param string $file The cachefile name
2854
	 * @param string $key name of cache value
2855
	 * @return mixed
2856
	 */
2857
	function getCacheValue($ieId, $type, $file, $key) {
2858
		global $sugar_config;
2859
2860
		$cleanIeId = cleanDirName($ieId);
2861
		$cleanType = cleanDirName($type);
2862
		$cleanFile = cleanFileName($file);
2863
		$cacheFilePath = sugar_cached("modules/Emails/{$cleanIeId}/{$cleanType}/{$cleanFile}");
2864
		$cacheFile = array();
2865
2866
		if(file_exists($cacheFilePath)) {
2867
			include($cacheFilePath); // provides $cacheFile
2868
2869
			if(isset($cacheFile[$key])) {
2870
				$ret = unserialize($cacheFile[$key]);
2871
				return $ret;
2872
			}
2873
		} else {
2874
			$GLOBALS['log']->debug("EMAILUI: cache file not found [ {$cacheFilePath} ] - creating blank cache file");
2875
			$this->writeCacheFile('retArr', array(), $ieId, $type, $file);
2876
		}
2877
2878
		return null;
2879
	}
2880
2881
	/**
2882
	 * retrieves the cache file last touched time
2883
	 * @param string $ieId
2884
	 * @param string $type Type of cache file: folders, messages, etc.
2885
	 * @param string $file The cachefile name
2886
	 * @return string
2887
	 */
2888
	function getCacheTimestamp($ieId, $type, $file) {
2889
		global $sugar_config;
2890
2891
		$cacheFilePath = sugar_cached("modules/Emails/{$ieId}/{$type}/{$file}");
2892
		$cacheFile = array();
2893
2894
		if(file_exists($cacheFilePath)) {
2895
			include($cacheFilePath); // provides $cacheFile['timestamp']
2896
2897
			if(isset($cacheFile['timestamp'])) {
2898
				$GLOBALS['log']->debug("EMAILUI: found timestamp [ {$cacheFile['timestamp']} ]");
2899
				return $cacheFile['timestamp'];
2900
			}
2901
		}
2902
2903
		return '';
2904
	}
2905
2906
	/**
2907
	 * Updates the timestamp for a cache file - usually to mark a "check email"
2908
	 * process
2909
	 * @param string $ieId
2910
	 * @param string $type Type of cache file: folders, messages, etc.
2911
	 * @param string $file The cachefile name
2912
	 */
2913
	function setCacheTimestamp($ieId, $type, $file) {
2914
		global $sugar_config;
2915
2916
		$cacheFilePath = sugar_cached("modules/Emails/{$ieId}/{$type}/{$file}");
2917
		$cacheFile = array();
2918
2919
		if(file_exists($cacheFilePath)) {
2920
			include($cacheFilePath); // provides $cacheFile['timestamp']
2921
2922
			if(isset($cacheFile['timestamp'])) {
2923
				$cacheFile['timestamp'] = strtotime('now');
2924
				$GLOBALS['log']->debug("EMAILUI: setting updated timestamp [ {$cacheFile['timestamp']} ]");
2925
				return $this->_writeCacheFile($cacheFile, $cacheFilePath);
2926
			}
2927
		}
2928
	}
2929
2930
2931
	/**
2932
	 * Writes caches to flat file in cache dir.
2933
	 * @param string $key Key to the main cache entry (not timestamp)
2934
	 * @param mixed $var Variable to be cached
2935
	 * @param string $ieId I-E focus ID
2936
	 * @param string $type Folder in cache
2937
	 * @param string $file Cache file name
2938
	 */
2939
	function writeCacheFile($key, $var, $ieId, $type, $file) {
2940
		global $sugar_config;
2941
2942
		$cleanIeId = cleanDirName($ieId);
2943
		$cleanType = cleanDirName($type);
2944
		$cleanFile = cleanFileName($file);
2945
		$the_file = sugar_cached("modules/Emails/{$cleanIeId}/{$cleanType}/{$cleanFile}");
2946
		$timestamp = strtotime('now');
2947
		$array = array();
2948
		$array['timestamp'] = $timestamp;
2949
		$array[$key] = serialize($var); // serialized since varexport_helper() can't handle PHP objects
2950
2951
		return $this->_writeCacheFile($array, $the_file);
2952
	}
2953
2954
	/**
2955
	 * Performs the actual file write.  Abstracted from writeCacheFile() for
2956
	 * flexibility
2957
	 * @param array $array The array to write to the cache
2958
	 * @param string $file Full path (relative) with cache file name
2959
	 * @return bool
2960
	 */
2961
	function _writeCacheFile($array, $file) {
2962
		global $sugar_config;
2963
2964
		$arrayString = var_export_helper($array);
2965
2966
		$date = date("r");
2967
	    $the_string =<<<eoq
2968
<?php // created: {$date}
2969
	\$cacheFile = {$arrayString};
2970
?>
2971
eoq;
2972
	    if($fh = @sugar_fopen($file, "w")) {
2973
	        fputs($fh, $the_string);
2974
	        fclose($fh);
2975
	        return true;
2976
	    } else {
2977
	    	$GLOBALS['log']->debug("EMAILUI: Could not write cache file [ {$file} ]");
2978
	        return false;
2979
	    }
2980
	}
2981
2982
	/**
2983
	 * Generate JSON encoded data to be consumed by yui datatable.
2984
	 *
2985
	 * @param array $data
2986
	 * @param string $resultsParam The resultsList name
2987
	 * @return string
2988
	 */
2989
	function jsonOuput($data, $resultsParam, $count=0, $fromCache=true, $unread=-1) {
2990
	    global $app_strings;
2991
2992
		$count = ($count > 0) ? $count : 0;
2993
2994
		if(isset($a['fromCache']))
0 ignored issues
show
Bug introduced by
The variable $a seems to never exist, and therefore isset should always return false. 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...
2995
			$cached = ($a['fromCache'] == 1) ? 1 : 0;
2996
		else
2997
			$cached = ($fromCache) ? 1 : 0;
2998
2999
		if($data['mbox'] == 'undefined' || empty($data['mbox']))
3000
			$data['mbox'] = $app_strings['LBL_NONE'];
3001
3002
		$jsonOut = array('TotalCount' => $count, 'FromCache' => $cached, 'UnreadCount' => $unread, $resultsParam => $data['out']);
3003
3004
		return json_encode($jsonOut);
3005
	}
3006
	/**
3007
	 * generates XML output from an array
3008
	 * @param array
3009
	 * @param string master list Item
3010
	 * @return string
3011
	 */
3012
	function xmlOutput($a, $paramName, $count=0, $fromCache=true, $unread=-1) {
3013
		global $app_strings;
3014
		$count = ($count > 0) ? $count : 0;
3015
3016
		if(isset($a['fromCache'])) {
3017
			$cached = ($a['fromCache'] == 1) ? 1 : 0;
3018
		} else {
3019
			$cached = ($fromCache) ? 1 : 0;
3020
		}
3021
3022
		if($a['mbox'] == 'undefined' || empty($a['mbox'])) {
3023
			$a['mbox'] = $app_strings['LBL_NONE'];
3024
		}
3025
3026
		$xml = $this->arrayToXML($a['out'], $paramName);
3027
3028
		$ret =<<<eoq
3029
<?xml version="1.0" encoding="UTF-8"?>
3030
<EmailPage>
3031
<TotalCount>{$count}</TotalCount>
3032
<UnreadCount>{$unread}</UnreadCount>
3033
<FromCache> {$cached} </FromCache>
3034
<{$paramName}s>
3035
{$xml}
3036
</{$paramName}s>
3037
</EmailPage>
3038
eoq;
3039
		return $ret;
3040
	}
3041
3042
    /**
3043
     * Generate to/cc addresses string in email detailview.
3044
     *
3045
     * @param string $str
3046
     * @param string $target values: to, cc
0 ignored issues
show
Bug introduced by
There is no parameter named $target. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
3047
     * @param int $defaultNum
0 ignored issues
show
Bug introduced by
There is no parameter named $defaultNum. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
3048
     * @return string $str
3049
     */
3050
	function generateExpandableAddrs($str) {
3051
	    global $mod_strings;
3052
	    $tempStr = $str.',';
3053
        $tempStr = html_entity_decode($tempStr);
3054
	    $tempStr = $this->unifyEmailString($tempStr);
3055
        $defaultNum = 2;
3056
        $pattern = '/@.*,/U';
3057
        preg_match_all($pattern, $tempStr, $matchs);
3058
        $totalCount = count($matchs[0]);
3059
3060
        if(!empty($matchs[0]) && $totalCount > $defaultNum) {
3061
            $position = strpos($tempStr, $matchs[0][$defaultNum]);
3062
            $hiddenCount = $totalCount - $defaultNum;
3063
            $frontStr = substr($tempStr, 0, $position);
3064
            $backStr = substr($tempStr, $position, -1);
3065
            $str = htmlentities($frontStr) . '<a class="utilsLink" onclick="javascript: SUGAR.email2.detailView.displayAllAddrs(this);">...['.$mod_strings['LBL_EMAIL_DETAIL_VIEW_SHOW'].$hiddenCount.$mod_strings['LBL_EMAIL_DETAIL_VIEW_MORE'].']</a><span style="display: none;">' .htmlentities($backStr).'</span>';
3066
        }
3067
3068
        return $str;
3069
    }
3070
3071
    /**
3072
     * Unify the seperator as ,
3073
     *
3074
     * @param String $str email address string
3075
     * @return String converted string
3076
     */
3077 1
    function unifyEmailString($str) {
3078 1
        preg_match_all('/@.*;/U', $str, $matches);
3079 1
        if(!empty($matches[0])) {
3080
            foreach($matches[0] as $key => $value) {
3081
                $new[] = str_replace(";",",",$value);
3082
            }
3083
            return str_replace($matches[0], $new, $str);
3084
        }
3085 1
        return $str;
3086
    }
3087
} // end class def
3088