Issues (4069)

Security Analysis    not enabled

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

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

include/SugarFolders/SugarFolders.php (2 issues)

Upgrade to new PHP Analysis Engine

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

1
<?php
2 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
51
/**
52
 * Polymorphic buckets - place any item in a folder
53
 */
54
class SugarFolder {
55
56
	// public attributes
57
	var $id;
58
	var $name;
59
	var $parent_folder;
60
	var $has_child = 0; // flag node has child
61
	var $is_group = 0;
62
	var $is_dynamic = 0;
63
	var $dynamic_query = '';
64
	var $assign_to_id;
65
	var $created_by;
66
	var $modified_by;
67
	var $date_created;
68
	var $date_modified;
69
	var $deleted;
70
	var $folder_type;
71
72
	var $db;
73
	var $new_with_id = false;
74
75
	// core queries
76
	var $core = "SELECT f.id, f.name, f.has_child, f.is_group, f.is_dynamic, f.dynamic_query, f.folder_type, f.created_by, i.deleted FROM folders f left join inbound_email i on f.id = i.groupfolder_id ";
77
	var $coreSubscribed = "SELECT f.id, f.name, f.has_child, f.is_group, f.is_dynamic, f.dynamic_query, f.folder_type, f.created_by, i.deleted FROM folders f LEFT JOIN folders_subscriptions fs ON f.id = fs.folder_id LEFT JOIN inbound_email i on  i.groupfolder_id = f.id ";
78
	var $coreWhere = "WHERE f.deleted = 0 ";
79
	var $coreWhereSubscribed = "WHERE f.deleted = 0 AND fs.assigned_user_id = ";
80
	var $coreOrderBy = " ORDER BY f.is_dynamic, f.is_group, f.name ASC ";
81
82
	var $hrSortLocal = array(
83
            'flagged' => 'type',
84
            'status'  => 'reply_to_status',
85
            'from'    => 'emails_text.from_addr',
86
            'subject' => 'name',
87
            'date'    => 'date_sent',
88
            'AssignedTo' => 'assigned_user_id',
89
            'flagged' => 'flagged'
90
        );
91
    var $defaultSort = 'date';
92
    var $defaultDirection = "DESC";
93
94
	// private attributes
95
	var $_depth;
96
97
	/**
98
	 * Sole constructor
99
	 */
100 17
	function __construct() {
101 17
		$this->db = DBManagerFactory::getInstance();
102 17
	}
103
104
    /**
105
     * @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
106
     */
107
    function SugarFolder(){
108
        $deprecatedMessage = 'PHP4 Style Constructors are deprecated and will be remove in 7.8, please update your code';
109
        if(isset($GLOBALS['log'])) {
110
            $GLOBALS['log']->deprecated($deprecatedMessage);
111
        }
112
        else {
113
            trigger_error($deprecatedMessage, E_USER_DEPRECATED);
114
        }
115
        self::__construct();
116
    }
117
118
119
	function deleteEmailFromAllFolder($id) {
120
		$q = "delete from folders_rel where polymorphic_module = 'Emails' AND polymorphic_id = '{$id}' ";
121
		$r = $this->db->query($q);
122
	}
123
124
	function deleteEmailFromFolder($id) {
125
		$q = "delete from folders_rel where polymorphic_module = 'Emails' AND polymorphic_id = '{$id}' AND folder_id = '{$this->id}'";
126
		$r = $this->db->query($q);
127
	}
128
129
	function checkEmailExistForFolder($id) {
130
		$q = "SELECT COUNT(*) c from folders_rel where polymorphic_module = 'Emails' AND polymorphic_id = '{$id}' AND folder_id = '{$this->id}'";
131
		$r = $this->db->query($q);
132
		$a = $this->db->fetchByAssoc($r);
133
		if ($a['c'] > 0) {
134
			return true;
135
		} else {
136
			return false;
137
		} // else
138
	}
139
	/**
140
	 * Moves beans from one folder to another folder
141
	 * @param string fromFolder GUID of source folder
142
	 * @param string toFolder GUID of destination folder
143
	 * @param string beanId GUID of SugarBean being moved
144
	 */
145
	function move($fromFolder, $toFolder, $beanId) {
146
		$q = "UPDATE folders_rel SET folder_id = '{$toFolder}' WHERE folder_id = '{$fromFolder}' AND polymorphic_id = '{$beanId}' AND deleted = 0";
147
		$r = $this->db->query($q);
148
	}
149
150
	/**
151
	 * Copies one bean from one folder to another
152
	 */
153
	function copyBean($fromFolder, $toFolder, $beanId, $module) {
154
		$q = "INSERT INTO folders_rel (id, folder_id, polymorphic_module, polymorphic_id, deleted)
155
				VALUES('{$guid}', '{$toFolder}', '{$module}', '{$beanId}', 0)";
156
		$r = $this->db->query($q);
157
	}
158
159
	/**
160
	 * Creates a new group Folder from the passed fields
161
	 * @param array fields
162
	 */
163
	function setFolder($fields) {
164
165
		global $current_user;
166
		if(empty($fields['groupFoldersUser'])) {
167
			$fields['groupFoldersUser'] = $current_user->id;
168
		}
169
170
		$this->name = $fields['name'];
171
		$this->parent_folder = $fields['parent_folder'];
172
		$this->has_child = 0;
173
		$this->is_group = 1;
174
		$this->assign_to_id = $fields['groupFoldersUser'];
175
176
		$this->save();
177
	}
178
179
	/**
180
	 * Returns GUIDs of folders that the user in focus is subscribed to
181
	 * @param object user User object in focus
182
	 * @return array
183
	 */
184
	function getSubscriptions($user) {
185
		if(empty($user)) {
186
			global $current_user;
187
			$user = $current_user;
188
		}
189
190
		$q = "SELECT folder_id FROM folders_subscriptions WHERE assigned_user_id = '{$user->id}'";
191
		$r = $this->db->query($q);
192
		$ret = array();
193
		while($a = $this->db->fetchByAssoc($r)) {
194
			$ret[] = $a['folder_id'];
195
		}
196
		return $ret;
197
	}
198
199
	/**
200
	 * Sets a user's preferences for subscribe folders (Sugar only)
201
	 * @param array subs Array of IDs for subscribed folders
202
	 */
203
	function setSubscriptions($subs) {
204
		global $current_user;
205
206
		if(empty($current_user->id)) {
207
			$GLOBALS['log']->fatal("*** FOLDERS: tried to update folder subscriptions for a user with no ID");
208
			return false;
209
		}
210
211
		$cleanSubscriptions = array();
212
213
		// ensure parent folders are selected, regardless.
214
		foreach($subs as $id) {
215
			$id = trim($id);
216
			if(!empty($id)) {
217
				$cleanSubscriptions[] = $id;
218
				$qChk = "SELECT parent_folder FROM folders WHERE id = '{$id}'";
219
				$rChk = $this->db->query($qChk);
220
				$aChk = $this->db->fetchByAssoc($rChk);
221
222
				if(!empty($aChk['parent_folder'])) {
223
					$cleanSubscriptions = $this->getParentIDRecursive($aChk['parent_folder'], $cleanSubscriptions);
224
				}
225
			}
226
		}
227
228
		$this->clearSubscriptions();
229
230
		foreach($cleanSubscriptions as $id) {
231
		    $this->insertFolderSubscription($id, $current_user->id);
232
		}
233
	}
234
235
	/**
236
	 * Given a folder id and user id, create a folder subscription entry.
237
	 *
238
	 * @param String $folderId
239
	 * @param String $userID
240
	 * @return String The id of the newly created folder subscription.
241
	 */
242
	function insertFolderSubscription($folderId, $userID)
243
	{
244
	    $guid = create_guid();
245
		$query = "INSERT INTO folders_subscriptions (id, folder_id, assigned_user_id) VALUES ('{$guid}', '{$folderId}', '{$userID}')";
246
		$r = $this->db->query($query);
247
		return $guid;
248
	}
249
	/**
250
	 * Recursively finds parent node until it hits root
251
	 * @param string id Starting id to follow up
252
	 * @param array ret collected ids
253
	 * @return array of IDs
254
	 */
255
	function getParentIDRecursive($id, $ret=array()) {
256
		$q = "SELECT * FROM folders WHERE id = '{$id}' AND deleted = 0";
257
		$r = $this->db->query($q);
258
		$a = $this->db->fetchByAssoc($r);
259
260
		if(!in_array($id, $ret)) {
261
			$ret[] = $id;
262
		}
263
264
		if($a['parent_folder'] != '') {
265
			$qChk = "SELECT parent_folder FROM folders WHERE id = '{$id}'";
266
			$rChk = $this->db->query($qChk);
267
			$aChk = $this->db->fetchByAssoc($rChk);
268
269
			if(!empty($aChk['parent_folder'])) {
270
				$ret = $this->getParentIDRecursive($aChk['parent_folder'], $ret);
271
			}
272
		}
273
274
		return $ret;
275
	}
276
277
	/**
278
	 * Deletes subscriptions to folders in preparation for reset
279
	 */
280
	function clearSubscriptions() {
281
		global $current_user;
282
283
		if(!empty($current_user->id)) {
284
			$q = "DELETE FROM folders_subscriptions WHERE assigned_user_id = '{$current_user->id}'";
285
			$r = $this->db->query($q);
286
		}
287
	}
288
289
290
	/**
291
	 * Deletes all subscriptions for a particular folder id
292
	 *
293
	 * @return unknown
294
	 */
295
	function clearSubscriptionsForFolder($folderID)
296
	{
297
	    $query = "DELETE FROM folders_subscriptions WHERE folder_id = '$folderID'";
298
	    $r = $this->db->query($query);
299
	}
300
301
	protected function generateArchiveFolderQuery()
302
	{
303
		global $current_user;
304
	    $q = <<<ENDQ
305
SELECT emails.id , emails.name, emails.date_sent, emails.status, emails.type, emails.flagged, emails.reply_to_status, emails_text.from_addr, emails_text.to_addrs, 'Emails' polymorphic_module FROM emails
306
JOIN emails_text on emails.id = emails_text.email_id
307
WHERE emails.deleted=0 AND emails.type NOT IN ('out', 'draft') AND emails.status NOT IN ('sent', 'draft') AND emails.id IN (
308
SELECT eear.email_id FROM emails_email_addr_rel eear
309
JOIN email_addr_bean_rel eabr ON eabr.email_address_id=eear.email_address_id AND eabr.bean_id = '{$current_user->id}' AND eabr.bean_module = 'Users'
310
WHERE eear.deleted=0
311
)
312
ENDQ;
313
        return $q;
314
	}
315
316
	function generateSugarsDynamicFolderQuery()
317
	{
318
		global $current_user;
319
		$type = $this->folder_type;
320
		if($type == 'archived') {
321
		    return $this->generateArchiveFolderQuery();
322
		}
323
		$status = $type;
324
		if($type == "sent") {
325
			$type = "out";
326
		}
327
		if($type == 'inbound') {
328
			$ret = " AND emails.status NOT IN ('sent', 'archived', 'draft') AND emails.type NOT IN ('out', 'archived', 'draft')";
329
		} else {
330
			$ret = " AND emails.status NOT IN ('archived') AND emails.type NOT IN ('archived')";
331
		}
332
		$q = "SELECT emails.id , emails.name, emails.date_sent, emails.status, emails.type, emails.flagged, emails.reply_to_status, emails_text.from_addr, emails_text.to_addrs, 'Emails' polymorphic_module FROM emails" .
333
								   " JOIN emails_text on emails.id = emails_text.email_id
334
                                   WHERE (type = '{$type}' OR status = '{$status}') AND assigned_user_id = '{$current_user->id}' AND emails.deleted=0";
335
		return $q . $ret;
336
	} // fn
337
338
339
	/**
340
	 * returns array of items for listView display in yui-ext Grid
341
	 */
342
	function getListItemsForEmailXML($folderId, $page = 1, $pageSize = 10, $sort = '', $direction='') {
343
		require_once('include/TimeDate.php');
344
		global $timedate;
345
		global $current_user;
346
		global $beanList;
347
		global $sugar_config;
348
		global $app_strings;
349
350
		$this->retrieve($folderId);
351
		$start = ($page - 1) * $pageSize;
352
353
		$sort = (empty($sort)) ? $this->defaultSort : $sort;
354
        if (!in_array(strtolower($direction), array('asc', 'desc'))) {
355
            $direction = $this->defaultDirection;
356
        }
357
358
        if (!empty($this->hrSortLocal[$sort])) {
359
            $order = " ORDER BY {$this->hrSortLocal[$sort]} {$direction}";
360
        } else {
361
            $order = "";
362
        }
363
364
		if($this->is_dynamic) {
365
			$r = $this->db->limitQuery(from_html($this->generateSugarsDynamicFolderQuery() . $order), $start, $pageSize);
366
		} else {
367
			// get items and iterate through them
368
			$q = "SELECT emails.id , emails.name, emails.date_sent, emails.status, emails.type, emails.flagged, emails.reply_to_status, emails_text.from_addr, emails_text.to_addrs, 'Emails' polymorphic_module FROM emails JOIN folders_rel ON emails.id = folders_rel.polymorphic_id" .
369
				  " JOIN emails_text on emails.id = emails_text.email_id
370
                  WHERE folders_rel.folder_id = '{$folderId}' AND folders_rel.deleted = 0 AND emails.deleted = 0";
371
			if ($this->is_group) {
372
				$q = $q . " AND (emails.assigned_user_id is null or emails.assigned_user_id = '')";
373
			}
374
			$r = $this->db->limitQuery($q . $order, $start, $pageSize);
375
		}
376
377
		$return = array();
378
379
		$email = new Email(); //Needed for email specific functions.
380
381
		while($a = $this->db->fetchByAssoc($r)) {
382
383
			$temp = array();
384
			$temp['flagged'] = (is_null($a['flagged']) || $a['flagged'] == '0') ? '' : 1;
385
			$temp['status'] = (is_null($a['reply_to_status']) || $a['reply_to_status'] == '0') ? '' : 1;
386
			$temp['from']	= preg_replace('/[\x00-\x08\x0B-\x1F]/', '', $a['from_addr']);
387
			$temp['subject'] = $a['name'];
388
			$temp['date']	= $timedate->to_display_date_time($this->db->fromConvert($a['date_sent'], 'datetime'));
389
			$temp['uid'] = $a['id'];
390
			$temp['mbox'] = 'sugar::'.$a['polymorphic_module'];
391
			$temp['ieId'] = $folderId;
392
			$temp['site_url'] = $sugar_config['site_url'];
393
			$temp['seen'] = ($a['status'] == 'unread') ? 0 : 1;
394
			$temp['type'] = $a['type'];
395
			$temp['hasAttach'] = $email->doesImportedEmailHaveAttachment($a['id']);
396
			$temp['to_addrs'] = preg_replace('/[\x00-\x08\x0B-\x1F]/', '', $a['to_addrs']);
397
			$return[] = $temp;
398
		}
399
400
401
		$metadata = array();
402
		$metadata['mbox'] = $app_strings['LBL_EMAIL_SUGAR_FOLDER'].': '.$this->name;
403
		$metadata['ieId'] = $folderId;
404
		$metadata['name'] = $this->name;
405
		$metadata['unreadChecked'] = ($current_user->getPreference('showUnreadOnly', 'Emails') == 1) ? 'CHECKED' : '';
406
		$metadata['out'] = $return;
407
408
		return $metadata;
409
	}
410
411
	function getCountItems ( $folderId ) {
412
		global $current_user ;
413
		global $beanList ;
414
		global $sugar_config ;
415
		global $app_strings ;
416
417
		$this->retrieve ( $folderId ) ;
418
		if ($this->is_dynamic) {
419
	    	$pattern = '/SELECT(.*?)(\s){1}FROM(\s){1}/is';  // ignores the case
420
	    	$replacement = 'SELECT count(*) c FROM ';
421
	    	$modified_select_query = preg_replace($pattern, $replacement, $this->generateSugarsDynamicFolderQuery(), 1);
422
	    	$r = $this->db->query ( from_html ( $modified_select_query )) ;
423
		} else {
424
			// get items and iterate through them
425
			$q = "SELECT count(*) c FROM folders_rel JOIN emails ON emails.id = folders_rel.polymorphic_id" .
426
			" WHERE folder_id = '{$folderId}' AND folders_rel.deleted = 0 AND emails.deleted = 0" ;
427
			if ($this->is_group) {
428
				$q .= " AND (emails.assigned_user_id is null or emails.assigned_user_id = '')";
429
			}
430
			$r = $this->db->query ( $q ) ;
431
		}
432
433
		$a = $this->db->fetchByAssoc($r);
434
		return $a['c'];
435
	}
436
437
    function getCountUnread ( $folderId ) {
438
        global $current_user ;
439
        global $beanList ;
440
        global $sugar_config ;
441
        global $app_strings ;
442
443
        $this->retrieve ( $folderId ) ;
444
        if ($this->is_dynamic) {
445
	    	$pattern = '/SELECT(.*?)(\s){1}FROM(\s){1}/is';  // ignores the case
446
	    	$replacement = 'SELECT count(*) c FROM ';
447
	    	$modified_select_query = preg_replace($pattern, $replacement, $this->generateSugarsDynamicFolderQuery(), 1);
448
	    	$r = $this->db->query (from_html($modified_select_query) . " AND emails.status = 'unread'") ;
449
        } else {
450
            // get items and iterate through them
451
            $q = "SELECT count(*) c FROM folders_rel fr JOIN emails on fr.folder_id = '{$folderId}' AND fr.deleted = 0 " .
452
               "AND fr.polymorphic_id = emails.id AND emails.status = 'unread' AND emails.deleted = 0" ;
453
            if ($this->is_group) {
454
                $q .= " AND (emails.assigned_user_id is null or emails.assigned_user_id = '')";
455
            }
456
            $r = $this->db->query ( $q ) ;
457
        }
458
459
		$a = $this->db->fetchByAssoc($r);
460
        return $a['c'];
461
    }
462
463
464
	/**
465
	 * Convenience method, pass a SugarBean and User to this to add anything to a given folder
466
	 */
467
	function addBean($bean, $user=null) {
468
		if(empty($bean->id) || empty($bean->module_dir)) {
469
			$GLOBALS['log']->fatal("*** FOLDERS: addBean() got empty bean - not saving");
470
			return false;
471
		} elseif(empty($this->id)) {
472
			$GLOBALS['log']->fatal("*** FOLDERS: addBean() is trying to save to a non-saved or non-existent folder");
473
			return false;
474
		}
475
476
		global $current_user;
477
		if($user == null) {
478
			$user = $current_user;
479
		}
480
481
		$guid = create_guid();
482
483
		$q = "INSERT INTO folders_rel (id, folder_id, polymorphic_module, polymorphic_id, deleted)
484
				VALUES('{$guid}', '{$this->id}', '{$bean->module_dir}', '{$bean->id}', 0)";
485
		$r = $this->db->query($q);
486
		return true;
487
	}
488
489
	/**
490
	 * Builds up a metacollection of user/group folders to be passed to processor methods
491
	 * @param object User object, defaults to $current_user
492
	 * @return array Array of abstract folder objects
493
	 */
494
	function retrieveFoldersForProcessing($user, $subscribed=true) {
495
		global $sugar_config;
496
		global $current_language, $current_user;
497
498
		$emails_mod_strings = return_module_language($current_language, "Emails");
499
		$myEmailTypeString = 'inbound';
500
		$myDraftsTypeString = 'draft';
501
		$mySentEmailTypeString = 'sent';
502
503
		if(empty($user)) {
504
			global $current_user;
505
			$user = $current_user;
506
		}
507
		$rootWhere = '';
508
        $teamSecurityClause = '';
509
510
511
512
    	$rootWhere .= "AND (f.parent_folder IS NULL OR f.parent_folder = '')";
513
514
		if($subscribed) {
515
			$q = $this->coreSubscribed.$teamSecurityClause.$this->coreWhereSubscribed."'{$user->id}' ".$rootWhere.$this->coreOrderBy;
516
		} else {
517
			$q = $this->core.$teamSecurityClause.$this->coreWhere.$rootWhere.$this->coreOrderBy;
518
		}
519
		$r = $this->db->query($q);
520
		$return = array();
521
522
		$found = array();
523
		while($a = $this->db->fetchByAssoc($r)) {
524
			if ((($a['folder_type'] == $myEmailTypeString) ||
525
				($a['folder_type'] == $myDraftsTypeString) ||
526
				($a['folder_type'] == $mySentEmailTypeString)) &&
527
				($a['created_by'] != $current_user->id)) {
528
529
				continue;
530
			} // if
531
			if (!isset($found[$a['id']])) {
532
                $found[$a['id']] = true;
533
			    $return[] = $a;
534
			}
535
		}
536
		return $return;
537
	}
538
    /**
539
	 * Preps object array for async call from user's Settings->Folders
540
	 */
541
	function getGroupFoldersForSettings($focusUser=null) {
542
		global $app_strings;
543
544
		$grp = array();
545
546
		$folders = $this->retrieveFoldersForProcessing($focusUser, false);
547
		$subscriptions = $this->getSubscriptions($focusUser);
548
549
		foreach($folders as $a) {
550
			$a['selected'] = (in_array($a['id'], $subscriptions)) ? true : false;
551
            $a['origName'] = $a['name'];
552
553
			if($a['is_group'] == 1)
554
				if ($a['deleted'] != 1)
555
					$grp[] = $a;
556
		}
557
558
		return $grp;
559
	}
560
	/**
561
	 * Preps object array for async call from user's Settings->Folders
562
	 */
563
	function getFoldersForSettings($focusUser=null) {
564
		global $app_strings;
565
566
		$user = array();
567
		$grp = array();
568
		$user[] = array('id' => '', 'name' => $app_strings['LBL_NONE'], 'has_child' => 0, 'is_group' => 0, 'selected' => false);
569
		$grp[] = array('id' => '', 'name' => $app_strings['LBL_NONE'], 'has_child' => 0, 'is_group' => 1, 'selected' => false, 'origName' => "");
570
571
		$folders = $this->retrieveFoldersForProcessing($focusUser, false);
572
		$subscriptions = $this->getSubscriptions($focusUser);
573
574
		foreach($folders as $a) {
575
			$a['selected'] = (in_array($a['id'], $subscriptions)) ? true : false;
576
            $a['origName'] = $a['name'];
577
            if( isset($a['dynamic_query']) )
578
                unset($a['dynamic_query']);
579
			if($a['is_group'] == 1) {
580
				$grp[] = $a;
581
			} else {
582
				$user[] = $a;
583
			}
584
585
			if($a['has_child'] == 1) {
586
				$qGetChildren = $this->core.$this->coreWhere."AND parent_folder = '{$a['id']}'";
587
				$rGetChildren = $this->db->query($qGetChildren);
588
589
				while($aGetChildren = $this->db->fetchByAssoc($rGetChildren)) {
590
					if($a['is_group']) {
591
						$this->_depth = 1;
592
						$grp = $this->getFoldersChildForSettings($aGetChildren, $grp, $subscriptions);
593
					} else {
594
						$this->_depth = 1;
595
						$user = $this->getFoldersChildForSettings($aGetChildren, $user, $subscriptions);
596
					}
597
				}
598
			}
599
		}
600
601
		$ret = array(
602
			'userFolders'	=> $user,
603
			'groupFolders'	=> $grp,
604
		);
605
		return $ret;
606
	}
607
608
	function getFoldersChildForSettings($a, $collection, $subscriptions) {
609
		$a['selected'] = (in_array($a['id'], $subscriptions)) ? true : false;
610
		$a['origName'] = $a['name'];
611
612
		if(isset($a['dynamic_query']))
613
		{
614
		   unset($a['dynamic_query']);
615
		}
616
617
		for($i=0; $i<$this->_depth; $i++)
618
		{
619
			$a['name'] = ".".$a['name'];
620
		}
621
622
		$collection[] = $a;
623
624
		if($a['has_child'] == 1) {
625
			$this->_depth++;
626
			$qGetChildren = $this->core.$this->coreWhere."AND parent_folder = '{$a['id']}'";
627
			$rGetChildren = $this->db->query($qGetChildren);
628
			while($aGetChildren = $this->db->fetchByAssoc($rGetChildren)) {
629
				$collection = $this->getFoldersChildForSettings($aGetChildren, $collection, $subscriptions);
630
			}
631
		}
632
633
		return $collection;
634
	}
635
636
	/**
637
	 * Returns the number of "new" items (based on passed criteria)
638
	 * @param string id ID of folder
639
	 * @param array criteria
640
	 * 		expected:
641
	 * 		array('field' => 'status',
642
	 * 				'value' => 'unread');
643
	 * @param array
644
	 * @return int
645
	 */
646
	function getCountNewItems($id, $criteria, $folder) {
647
		global $current_user;
648
649
		$sugarFolder = new SugarFolder();
650
		return $sugarFolder->getCountUnread($id);
651
	}
652
653
	/**
654
	 * Collects, sorts, and builds tree of user's folders
655
	 * @param objec $rootNode Reference to tree root node
656
	 * @param array $folderStates User pref folder open/closed states
657
	 * @param object $user Optional User in focus, default current_user
658
	 * @return array
659
	 */
660
	function getUserFolders(&$rootNode, $folderStates, $user=null, $forRefresh=false) {
661
		if(empty($user)) {
662
			global $current_user;
663
			$user = $current_user;
664
		}
665
		global $mod_strings;
666
		$folders = $this->retrieveFoldersForProcessing($user, true);
667
		$subscriptions = $this->getSubscriptions($user);
668
669
		$refresh = ($forRefresh) ? array() : null;
670
671
		if(!is_array($folderStates)) {
672
			$folderStates = array();
673
		}
674
675
		foreach($folders as $a) {
676
			if ($a['deleted'] == 1)
677
				continue;
678
			$label = ($a['name'] == 'My Email' ? $mod_strings['LNK_MY_INBOX'] : $a['name']);
679
680
			$unseen = $this->getCountNewItems($a['id'], array('field' => 'status', 'value' => 'unread'), $a);
681
682
			$folderNode = new ExtNode($a['id'], $label);
683
			$folderNode->dynamicloadfunction = '';
684
			$folderNode->expanded = false;
685
686
			if(array_key_exists('Home::'.$a['id'], $folderStates)) {
687
				if($folderStates['Home::'.$a['id']] == 'open') {
688
					$folderNode->expanded = true;
689
				}
690
			}
691
			$nodePath = "Home::".$folderNode->_properties['id'];
692
693
			$folderNode->dynamic_load = true;
694
	        //$folderNode->set_property('click', " SUGAR.email2.listView.populateListFrameSugarFolder(YAHOO.namespace('frameFolders').selectednode, '{$a['id']}', 'false');");
695
	        $folderNode->set_property('ieId', 'folder');
696
	        $folderNode->set_property('is_group', ($a['is_group'] == 1) ? 'true' : 'false');
697
	        $folderNode->set_property('is_dynamic', ($a['is_dynamic'] == 1) ? 'true' : 'false');
698
	        $folderNode->set_property('mbox', $folderNode->_properties['id']);
699
	        $folderNode->set_property('unseen', $unseen);
700
	        $folderNode->set_property('id', $a['id']);
701
	        $folderNode->set_property('folder_type', $a['folder_type']);
702
	        $folderNode->set_property('children', array());
703
704
			if(in_array($a['id'], $subscriptions) && $a['has_child'] == 1) {
705
				$qGetChildren = $this->core.$this->coreWhere."AND parent_folder = '{$a['id']}'";
706
				$rGetChildren = $this->db->query($qGetChildren);
707
708
				while($aGetChildren = $this->db->fetchByAssoc($rGetChildren)) {
709
					if(in_array($aGetChildren['id'], $subscriptions)) {
710
						$folderNode->add_node($this->buildTreeNodeFolders($aGetChildren, $nodePath, $folderStates, $subscriptions));
711
					}
712
				}
713
			}
714
			$rootNode->add_node($folderNode);
715
		}
716
717
		/* the code below is called only by Settings->Folders when selecting folders to subscribe to */
718
		if($forRefresh) {
719
			$metaNode = array();
720
721
			if(!empty($rootNode->nodes)) {
722
				foreach($rootNode->nodes as $node) {
723
					$metaNode[] = $this->buildTreeNodeRefresh($node, $subscriptions);
724
				}
725
			}
726
			return $metaNode;
727
		}
728
	}
729
730
	/**
731
	 * Builds up a metanode for folder refresh (Sugar folders only)
732
	 */
733
	function buildTreeNodeRefresh($folderNode, $subscriptions) {
734
		$metaNode = $folderNode->_properties;
735
		$metaNode['expanded'] = $folderNode->expanded;
736
		$metaNode['text'] = $folderNode->_label;
737
		if($metaNode['is_group'] == 'true') {
738
			$metaNode['cls'] = 'groupFolder';
739
		} else {
740
		    $metaNode['cls'] = 'sugarFolder';
741
		}
742
		$metaNode['id'] = $folderNode->_properties['id'];
743
		$metaNode['children'] = array();
744
		$metaNode['type'] = 1;
745
		$metaNode['leaf'] = false;
746
		$metaNode['isTarget'] = true;
747
		$metaNode['allowChildren'] = true;
748
749
		if(!empty($folderNode->nodes)) {
750
			foreach($folderNode->nodes as $node) {
751
				if(in_array($node->_properties['id'], $subscriptions))
752
					$metaNode['children'][] = $this->buildTreeNodeRefresh($node, $subscriptions);
753
			}
754
		}
755
		return $metaNode;
756
	}
757
758
	/**
759
	 * Builds children nodes for folders for TreeView
760
	 * @return $folderNode TreeView node
0 ignored issues
show
The doc-type $folderNode could not be parsed: Unknown type name "$folderNode" 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...
761
	 */
762
	function buildTreeNodeFolders($a, $nodePath, $folderStates, $subscriptions) {
763
		$label = $a['name'];
764
		global $mod_strings;
765
		if($a['name'] == 'My Drafts') {
766
			$label = $mod_strings['LBL_LIST_TITLE_MY_DRAFTS'];
767
		}
768
		if($a['name'] == 'Sent Emails') {
769
			$label = $mod_strings['LBL_LIST_TITLE_MY_SENT'];
770
		}
771
		$unseen = $this->getCountNewItems($a['id'], array('field' => 'status', 'value' => 'unread'), $a);
772
773
		$folderNode = new ExtNode($a['id'], $label);
774
		$folderNode->dynamicloadfunction = '';
775
		$folderNode->expanded = false;
776
777
		$nodePath .= "::{$a['id']}";
778
779
		if(array_key_exists($nodePath, $folderStates)) {
780
			if($folderStates[$nodePath] == 'open') {
781
				$folderNode->expanded = true;
782
			}
783
		}
784
785
		$folderNode->dynamic_load = true;
786
        $folderNode->set_property('click', "SUGAR.email2.listView.populateListFrameSugarFolder(YAHOO.namespace('frameFolders').selectednode, '{$a['id']}', 'false');");
787
        $folderNode->set_property('ieId', 'folder');
788
        $folderNode->set_property('mbox', $a['id']);
789
		$folderNode->set_property('is_group', ($a['is_group'] == 1) ? 'true' : 'false');
790
        $folderNode->set_property('is_dynamic', ($a['is_dynamic'] == 1) ? 'true' : 'false');
791
        $folderNode->set_property('unseen', $unseen);
792
	    $folderNode->set_property('folder_type', $a['folder_type']);
793
794
		if(in_array($a['id'], $subscriptions) && $a['has_child'] == 1) {
795
			$qGetChildren = $this->core.$this->coreWhere."AND parent_folder = '{$a['id']}' ".$this->coreOrderBy;
796
			$rGetChildren = $this->db->query($qGetChildren);
797
798
			while($aGetChildren = $this->db->fetchByAssoc($rGetChildren)) {
799
				$folderNode->add_node($this->buildTreeNodeFolders($aGetChildren, $nodePath, $folderStates, $subscriptions));
800
			}
801
		}
802
		return $folderNode;
803
	}
804
805
	/**
806
	 * Flags a folder as deleted
807
	 * @return bool True on success
808
	 */
809
	function delete() {
810
		global $current_user;
811
812
		if(!empty($this->id)) {
813
			if($this->has_child) {
814
				$this->deleteChildrenCascade($this->id);
815
			}
816
817
			$ownerCheck = ($current_user->is_admin == 0) ? " AND created_by = '{$current_user->id}'" : "";
818
			$q = "UPDATE folders SET deleted = 1 WHERE id = '{$this->id}'{$ownerCheck}";
819
			$r = $this->db->query($q);
820
			return true;
821
		}
822
		return false;
823
	}
824
825
	/**
826
	 * Deletes all children in a cascade
827
	 * @param string $id ID of parent
828
	 * @return bool True on success
829
	 */
830
	function deleteChildrenCascade($id) {
831
		global $current_user;
832
833
		$canContinue = true;
834
		$checkInboundQuery = "SELECT count(*) c FROM inbound_email WHERE groupfolder_id = '{$id}' and deleted = 0";
835
		$resultSet = $this->db->query($checkInboundQuery);
836
		$a = $this->db->fetchByAssoc($resultSet);
837
		if ($a['c'] > 0) {
838
			return false;
839
		} // if
840
841
		$q = "SELECT COUNT(*) c from folders_rel where polymorphic_module = 'Emails' AND polymorphic_id = '{$id}' AND folder_id = '{$this->id}'";
842
843
		$checkEmailQuery = "SELECT count(*) c FROM folders_rel where polymorphic_module = 'Emails' and folder_id = '{$id}' and deleted = 0";
844
		$resultSet = $this->db->query($checkEmailQuery);
845
		$a = $this->db->fetchByAssoc($resultSet);
846
		if ($a['c'] > 0) {
847
			return false;
848
		} // if
849
850
		$q = "SELECT * FROM folders WHERE id = '{$id}'";
851
		$r = $this->db->query($q);
852
		$a = $this->db->fetchByAssoc($r);
853
854
		if($a['has_child'] == 1) {
855
			$q2 = "SELECT id FROM folders WHERE parent_folder = '{$id}'";
856
			$r2 = $this->db->query($q2);
857
858
			while($a2 = $this->db->fetchByAssoc($r2)) {
859
				$canContinue = $this->deleteChildrenCascade($a2['id']);
860
			}
861
		}
862
863
		if ($canContinue) {
864
			// flag deleted
865
			$ownerCheck = ($current_user->is_admin == 0) ? " AND created_by = '{$current_user->id}'" : "";
866
			$q3 = "UPDATE folders SET deleted = 1 WHERE id = '{$id}'{$ownerCheck}";
867
			$r3 = $this->db->query($q3);
868
869
			// flag rels
870
			$qRel = "UPDATE folders_rel SET deleted = 1 WHERE folder_id = '{$id}'";
871
			$rRel = $this->db->query($qRel);
872
873
			// delete subscriptions
874
			$qSub = "DELETE FROM folders_subscriptions WHERE folder_id = '{$id}'";
875
			$rSub = $this->db->query($qSub);
876
		}
877
		return $canContinue;
878
		//_pp($q3);_pp($qRel);_pp($qSub);
879
	}
880
881
882
	/**
883
	 * Saves folder
884
	 * @return bool
885
	 */
886 1
	function save($addSubscriptions = TRUE) {
887 1
		global $current_user;
888
889 1
		$this->dynamic_query = $this->db->quote($this->dynamic_query);
890
891 1
		if((empty($this->id) && $this->new_with_id == false) || (!empty($this->id) && $this->new_with_id == true))
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

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

Loading history...
892
		{
893
894 1
		    if( empty($this->id) )
895
		    {
896
			    $guid = create_guid();
897
			    $this->id = $guid;
898
		    }
899
900
			$q = "INSERT INTO folders(id, name, folder_type, parent_folder, has_child, is_group, is_dynamic, dynamic_query, assign_to_id, ".
901
				"created_by, modified_by, deleted)".
902
903 1
				" VALUES('{$this->id}', '{$this->name}', '{$this->folder_type}', '{$this->parent_folder}', {$this->has_child}, {$this->is_group}, {$this->is_dynamic}, '{$this->dynamic_query}', '{$this->assign_to_id}', " .
904 1
				"'{$current_user->id}', '{$current_user->id}', 0)";
905
906
907 1
			if($addSubscriptions)
908
			{
909
			    // create default subscription
910 1
			    $this->addSubscriptionsToGroupFolder();
911
			}
912
913
			// if parent_id is set, update parent's has_child flag
914 1
			$q3 = "UPDATE folders SET has_child = 1 WHERE id = '{$this->parent_folder}'";
915 1
			$r3 = $this->db->query($q3);
916
		}
917
		else {
918
			$q = "UPDATE folders SET name = '{$this->name}', parent_folder = '{$this->parent_folder}', dynamic_query = '{$this->dynamic_query}', assign_to_id = '{$this->assign_to_id}', " .
919
				"modified_by = '{$current_user->id}' WHERE id = '{$this->id}'";
920
		}
921
922 1
		$this->db->query($q, true);
923
924 1
		return true;
925
	}
926
927
	/**
928
	 * Add subscriptions to this group folder.
929
	 *
930
	 */
931 1
	function addSubscriptionsToGroupFolder()
932
	{
933 1
	    global $current_user;
934
935 1
	    $this->createSubscriptionForUser($current_user->id);
936
937 1
	}
938
939
940
941
    /**
942
	 * Add subscriptions to this group folder.
943
	 *
944
	 */
945 1
	function createSubscriptionForUser($user_id)
946
	{
947 1
	   $guid2 = create_guid();
948 1
	   $query = "INSERT INTO folders_subscriptions VALUES('{$guid2}', '{$this->id}', '{$user_id}')";
949 1
	   $this->db->query($query);
950 1
	}
951
952
953
	function updateFolder($fields) {
954
		global $current_user;
955
956
		$this->dynamic_query = $this->db->quote($this->dynamic_query);
957
		$id = $fields['record'];
958
		$name = $fields['name'];
959
		$parent_folder = $fields['parent_folder'];
960
		// first do the retrieve
961
		$this->retrieve($id);
962
		if ($this->has_child) {
963
			$childrenArray = array();
964
			$this->findAllChildren($id, $childrenArray);
965
			if (in_array($parent_folder, $childrenArray)) {
966
				return array('status' => "failed", 'message' => "Can not add this folder to its children");
967
			}
968
		}
969
		// update has_child to 0 for this parent folder if this is the only child it has
970
		$q1 = "select count(*) count from folders where deleted = 0 AND parent_folder = '{$this->parent_folder}'";
971
		$r1 = $this->db->query($q1);
972
		$a1 = $this->db->fetchByAssoc($r1);
973
		if ($a1['count'] == 1) {
974
			$q1 = "UPDATE folders SET has_child = 0 WHERE id = '{$this->parent_folder}'";
975
			$r1 = $this->db->query($q1);
976
		} // if
977
978
979
		$this->name = $name;
980
		$this->parent_folder = $parent_folder;
981
		$q2 = "UPDATE folders SET name = '{$this->name}', parent_folder = '{$this->parent_folder}', 			dynamic_query = '{$this->dynamic_query}', " .
982
			"modified_by = '{$current_user->id}' WHERE id = '{$this->id}'";
983
		$r2 = $this->db->query($q2);
984
		if (!empty($this->parent_folder)) {
985
			$q3 = "UPDATE folders SET has_child = 1 WHERE id = '{$this->parent_folder}'";
986
			$r3 = $this->db->query($q3);
987
		} // if
988
		return array('status' => "done");
989
990
	} // fn
991
992
	function findAllChildren($folderId, &$childrenArray) {
993
		$q = "SELECT * FROM folders WHERE id = '{$folderId}'";
994
		$r = $this->db->query($q);
995
		$a = $this->db->fetchByAssoc($r);
996
997
		if($a['has_child'] == 1) {
998
			$q2 = "SELECT id FROM folders WHERE deleted = 0 AND parent_folder = '{$folderId}'";
999
			$r2 = $this->db->query($q2);
1000
1001
			while($a2 = $this->db->fetchByAssoc($r2)) {
1002
				$childrenArray[] = $a2['id'];
1003
				$this->findAllChildren($a2['id'], $childrenArray);
1004
			} // while
1005
		} // if
1006
1007
	} // fn
1008
1009
	/**
1010
	 * Retrieves and populates object
1011
	 * @param string $id ID of folder
1012
	 * @return bool True on success
1013
	 */
1014
	function retrieve($id) {
1015
		$q = "SELECT * FROM folders WHERE id = '{$id}' AND deleted = 0";
1016
		$r = $this->db->query($q);
1017
		$a = $this->db->fetchByAssoc($r);
1018
1019
		if(!empty($a)) {
1020
			foreach($a as $k => $v) {
1021
				if($k == 'dynamic_query') {
1022
					$v = from_html($v);
1023
				}
1024
				$this->$k = $v;
1025
			}
1026
			return true;
1027
		}
1028
1029
		return false;
1030
	}
1031
} // end class def
1032