Email::email2Send()   F
last analyzed

Complexity

Conditions 133
Paths > 20000

Size

Total Lines 519
Code Lines 327

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 17822
Metric Value
cc 133
eloc 327
nc 429496.7295
nop 1
dl 0
loc 519
ccs 0
cts 1
cp 0
crap 17822
rs 2

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 1
require_once('include/SugarPHPMailer.php');
43 1
require_once 'include/upload_file.php';
44
45
class Email extends SugarBean {
46
	/* SugarBean schema */
47
	var $id;
48
	var $date_entered;
49
	var $date_modified;
50
	var $assigned_user_id;
51
	var $assigned_user_name;
52
	var $modified_user_id;
53
	var $created_by;
54
	var $deleted;
55
	var $from_addr;
56
	var $reply_to_addr;
57
	var $to_addrs;
58
    var $cc_addrs;
59
    var $bcc_addrs;
60
	var $message_id;
61
62
	/* Bean Attributes */
63
	var $name;
64
    var $type = 'archived';
65
    var $date_sent;
66
	var $status;
67
	var $intent;
68
	var $mailbox_id;
69
	var $from_name;
70
71
	var $reply_to_status;
72
	var $reply_to_name;
73
	var $reply_to_email;
74
	var $description;
75
	var $description_html;
76
	var $raw_source;
77
	var $parent_id;
78
	var $parent_type;
79
80
	/* link attributes */
81
	var $parent_name;
82
83
84
	/* legacy */
85
	var $date_start; // legacy
86
	var $time_start; // legacy
87
	var $from_addr_name;
88
	var $to_addrs_arr;
89
    var $cc_addrs_arr;
90
    var $bcc_addrs_arr;
91
	var $to_addrs_ids;
92
	var $to_addrs_names;
93
	var $to_addrs_emails;
94
	var $cc_addrs_ids;
95
	var $cc_addrs_names;
96
	var $cc_addrs_emails;
97
	var $bcc_addrs_ids;
98
	var $bcc_addrs_names;
99
	var $bcc_addrs_emails;
100
	var $contact_id;
101
	var $contact_name;
102
103
	/* Archive Email attrs */
104
	var $duration_hours;
105
106
107
108
	var $new_schema = true;
109
	var $table_name = 'emails';
110
	var $module_dir = 'Emails';
111
    var $module_name = 'Emails';
112
	var $object_name = 'Email';
113
	var $db;
114
115
	/* private attributes */
116
	var $rolloverStyle		= "<style>div#rollover {position: relative;float: left;margin: none;text-decoration: none;}div#rollover a:hover {padding: 0;text-decoration: none;}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";
117
	var $cachePath;
118
	var $cacheFile			= 'robin.cache.php';
119
	var $replyDelimiter	= "> ";
120
	var $emailDescription;
121
	var $emailDescriptionHTML;
122
	var $emailRawSource;
123
	var $link_action;
124
	var $emailAddress;
125
	var $attachments = array();
126
127
	/* to support Email 2.0 */
128
	var $isDuplicate;
129
	var $uid;
130
	var $to;
131
	var $flagged;
132
	var $answered;
133
	var $seen;
134
	var $draft;
135
	var $relationshipMap = array(
136
		'Contacts'	=> 'emails_contacts_rel',
137
		'Accounts'	=> 'emails_accounts_rel',
138
		'Leads'		=> 'emails_leads_rel',
139
		'Users'		=> 'emails_users_rel',
140
		'Prospects'	=> 'emails_prospects_rel',
141
	);
142
143
	/* public */
144
	var $et;		// EmailUI object
145
	// prefix to use when importing inlinge images in emails
146
	public $imagePrefix;
147
148
    /**
149
     * Used for keeping track of field defs that have been modified
150
     *
151
     * @var array
152
     */
153
    public $modifiedFieldDefs = array();
154
155
    public $attachment_image;
156
157
	/**
158
	 * sole constructor
159
	 */
160 83
	public function __construct()
161
	{
162 83
	    global $current_user;
163 83
	    $this->cachePath = sugar_cached('modules/Emails');
164 83
		parent::__construct();
165
166 83
		$this->emailAddress = new SugarEmailAddress();
167
168 83
		$this->imagePrefix = rtrim($GLOBALS['sugar_config']['site_url'], "/")."/cache/images/";
169 83
	}
170
171
    /**
172
     * @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
173
     */
174
    public function Email(){
175
        $deprecatedMessage = 'PHP4 Style Constructors are deprecated and will be remove in 7.8, please update your code';
176
        if(isset($GLOBALS['log'])) {
177
            $GLOBALS['log']->deprecated($deprecatedMessage);
178
        }
179
        else {
180
            trigger_error($deprecatedMessage, E_USER_DEPRECATED);
181
        }
182
        self::__construct();
183
    }
184
185
186 5
	function email2init() {
187 5
		require_once('modules/Emails/EmailUI.php');
188 5
		$this->et = new EmailUI();
189 5
	}
190
191 83
	function bean_implements($interface){
192
		switch($interface){
193 83
			case 'ACL': return true;
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

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

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

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

Loading history...
194 1
			default: return false;
0 ignored issues
show
Coding Style introduced by
The default body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a default statement must start on the line immediately following the statement.

switch ($expr) {
    default:
        doSomething(); //right
        break;
}


switch ($expr) {
    default:

        doSomething(); //wrong
        break;
}

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

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

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

Loading history...
195
		}
196
197
	}
198
199
	/**
200
	 * Presaves one attachment for new email 2.0 spec
201
	 * DOES NOT CREATE A NOTE
202
	 * @return string ID of note associated with the attachment
203
	 */
204 1
	public function email2saveAttachment()
205
	{
206 1
        $email_uploads = "modules/Emails/{$GLOBALS['current_user']->id}";
207 1
	    $upload = new UploadFile('email_attachment');
208 1
		if(!$upload->confirm_upload()) {
209 1
		    $err = $upload->get_upload_error();
210 1
   		    if($err) {
211
   		        $GLOBALS['log']->error("Email Attachment could not be attached due to error: $err");
212
   		    }
213 1
   		    return array();
214
		}
215
216
		$guid = create_guid();
217
		$fileName = $upload->create_stored_filename();
218
        $GLOBALS['log']->debug("Email Attachment [$fileName]");
219
        if($upload->final_move($guid)) {
220
        	copy("upload://$guid", sugar_cached("$email_uploads/$guid"));
221
			return array(
222
					'guid' => $guid,
223
					'name' => $GLOBALS['db']->quote($fileName),
224
					'nameForDisplay' => $fileName
225
				);
226
        } else {
227
			$GLOBALS['log']->debug("Email Attachment [$fileName] could not be moved to upload dir");
228
			return array();
229
        }
230
	}
231
232 1
	function safeAttachmentName($filename) {
233 1
		global $sugar_config;
234 1
		$badExtension = false;
235
		//get position of last "." in file name
236 1
		$file_ext_beg = strrpos($filename, ".");
237 1
		$file_ext = "";
238
239
		//get file extension
240 1
		if($file_ext_beg !== false) {
241 1
			$file_ext = substr($filename, $file_ext_beg + 1);
242
		}
243
244
		//check to see if this is a file with extension located in "badext"
245 1
		foreach($sugar_config['upload_badext'] as $badExt) {
246 1
			if(strtolower($file_ext) == strtolower($badExt)) {
247
				//if found, then append with .txt and break out of lookup
248 1
				$filename = $filename . ".txt";
249 1
				$badExtension = true;
250 1
				break; // no need to look for more
251
			} // if
252
		} // foreach
253
254 1
		return $badExtension;
255
	} // fn
256
257
	/**
258
	 * takes output from email 2.0 to/cc/bcc fields and returns appropriate arrays for usage by PHPMailer
259
	 * @param string addresses
260
	 * @return array
261
	 */
262 1
	function email2ParseAddresses($addresses) {
263 1
		$addresses = from_html($addresses);
264 1
        $addresses = $this->et->unifyEmailString($addresses);
265
266 1
		$pattern = '/@.*,/U';
267 1
		preg_match_all($pattern, $addresses, $matchs);
268 1
		if (!empty($matchs[0])){
269 1
			$total = $matchs[0];
270 1
			foreach ($total as $match) {
271 1
				$convertedPattern = str_replace(',', '::;::', $match);
272 1
				$addresses = str_replace($match, $convertedPattern, $addresses);
273
			} //foreach
274
		}
275
276 1
		$exAddr = explode("::;::", $addresses);
277
278 1
		$ret = array();
279 1
		$clean = array("<", ">");
280 1
		$dirty = array("&lt;", "&gt;");
281
282 1
		foreach($exAddr as $addr) {
283 1
			$name = '';
284
285 1
			$addr = str_replace($dirty, $clean, $addr);
286
287 1
			if((strpos($addr, "<") === false) && (strpos($addr, ">") === false)) {
288
				$address = $addr;
289
			} else {
290 1
				$address = substr($addr, strpos($addr, "<") + 1, strpos($addr, ">") - 1 - strpos($addr, "<"));
291 1
				$name = substr($addr, 0, strpos($addr, "<"));
292
			}
293
294 1
			$addrTemp = array();
295 1
			$addrTemp['email'] = trim($address);
296 1
			$addrTemp['display'] = trim($name);
297 1
			$ret[] = $addrTemp;
298
		}
299
300 1
		return $ret;
301
	}
302
303
	/**
304
	 * takes output from email 2.0 to/cc/bcc fields and returns appropriate arrays for usage by PHPMailer
305
	 * @param string addresses
306
	 * @return array
307
	 */
308 1
	function email2ParseAddressesForAddressesOnly($addresses) {
309 1
		$addresses = from_html($addresses);
310 1
		$pattern = '/@.*,/U';
311 1
		preg_match_all($pattern, $addresses, $matchs);
312 1
		if (!empty($matchs[0])){
313 1
			$total = $matchs[0];
314 1
			foreach ($total as $match) {
315 1
				$convertedPattern = str_replace(',', '::;::', $match);
316 1
				$addresses = str_replace($match, $convertedPattern, $addresses);
317
			} //foreach
318
		}
319
320 1
		$exAddr = explode("::;::", $addresses);
321
322 1
		$ret = array();
323 1
		$clean = array("<", ">");
324 1
		$dirty = array("&lt;", "&gt;");
325
326 1
		foreach($exAddr as $addr) {
327 1
			$name = '';
328
329 1
			$addr = str_replace($dirty, $clean, $addr);
330
331 1
			if(strpos($addr, "<") && strpos($addr, ">")) {
332 1
				$address = substr($addr, strpos($addr, "<") + 1, strpos($addr, ">") - 1 - strpos($addr, "<"));
333
			} else {
334 1
				$address = $addr;
335
			}
336
337 1
			$ret[] = trim($address);
338
		}
339
340 1
		return $ret;
341
	}
342
343
	/**
344
	 * Determines MIME-type encoding as possible.
345
	 * @param string $fileLocation relative path to file
346
	 * @return string MIME-type
347
	 */
348 1
	function email2GetMime($fileLocation) {
349 1
	    if(!is_readable($fileLocation)) {
350 1
	        return 'application/octet-stream';
351
	    }
352 1
		if(function_exists('mime_content_type')) {
353 1
			$mime = mime_content_type($fileLocation);
354
		} elseif(function_exists('ext2mime')) {
355
			$mime = ext2mime($fileLocation);
356
		} else {
357
			$mime = 'application/octet-stream';
358
		}
359 1
		return $mime;
360
	}
361
362
363
	function sendEmailTest($mailserver_url, $port, $ssltls, $smtp_auth_req, $smtp_username, $smtppassword, $fromaddress, $toaddress, $mail_sendtype = 'smtp', $fromname = '') {
364
		global $current_user,$app_strings;
365
		$mod_strings = return_module_language($GLOBALS['current_language'], 'Emails'); //Called from EmailMan as well.
366
	    $mail = new SugarPHPMailer();
367
		$mail->Mailer = strtolower($mail_sendtype);
368
		if($mail->Mailer == 'smtp')
369
		{
370
    		$mail->Host = $mailserver_url;
371
    		$mail->Port = $port;
372
    		if (isset($ssltls) && !empty($ssltls)) {
373
    			$mail->protocol = "ssl://";
374
    	        if ($ssltls == 1) {
375
    	            $mail->SMTPSecure = 'ssl';
376
    	        } // if
377
    	        if ($ssltls == 2) {
378
    	            $mail->SMTPSecure = 'tls';
379
    	        } // if
380
    		} else {
381
    			$mail->protocol = "tcp://";
382
    		}
383
    		if ($smtp_auth_req) {
384
    			$mail->SMTPAuth = TRUE;
385
    			$mail->Username = $smtp_username;
386
    			$mail->Password = $smtppassword;
387
    		}
388
		}
389
		else
390
		    $mail->Mailer = 'sendmail';
391
392
		$mail->Subject = from_html($mod_strings['LBL_TEST_EMAIL_SUBJECT']);
393
		$mail->From = $fromaddress;
394
395
        if ($fromname != '') {
396
            $mail->FromName = html_entity_decode($fromname,ENT_QUOTES);
397
        } else {
398
            $mail->FromName = $current_user->name;
399
        }
400
401
        $mail->Sender = $mail->From;
402
		$mail->AddAddress($toaddress);
403
		$mail->Body = $mod_strings['LBL_TEST_EMAIL_BODY'];
404
405
		$return = array();
406
407
		if(!$mail->Send()) {
408
	        ob_clean();
409
	        $return['status'] = false;
410
	        $return['errorMessage'] = $app_strings['LBL_EMAIL_ERROR_PREPEND']. $mail->ErrorInfo;
411
	        return $return;
412
		} // if
413
		$return['status'] = true;
414
        return $return;
415
	} // fn
416
417 3
	function decodeDuringSend($htmlData) {
418 3
	    $htmlData = str_replace("sugarLessThan", "&lt;", $htmlData);
419 3
	    $htmlData = str_replace("sugarGreaterThan", "&gt;", $htmlData);
420 3
		return $htmlData;
421
	}
422
423
	/**
424
	 * Returns true or false if this email is a draft.
425
	 *
426
	 * @param array $request
427
	 * @return bool True indicates this email is a draft.
428
	 */
429 1
	function isDraftEmail($request)
430
	{
431 1
	    return ( isset($request['saveDraft']) || ($this->type == 'draft' && $this->status == 'draft') );
432
	}
433
434
	/**
435
	 * Sends Email for Email 2.0
436
	 */
437
	function email2Send($request) {
438
		global $mod_strings;
439
		global $app_strings;
440
		global $current_user;
441
		global $sugar_config;
442
		global $locale;
443
		global $timedate;
444
		global $beanList;
445
		global $beanFiles;
446
        $OBCharset = $locale->getPrecedentPreference('default_email_charset');
447
448
		/**********************************************************************
449
		 * Sugar Email PREP
450
		 */
451
		/* preset GUID */
452
453
		$orignialId = "";
454
		if(!empty($this->id)) {
455
			$orignialId = 	$this->id;
456
		} // if
457
458
		if(empty($this->id)) {
459
			$this->id = create_guid();
460
			$this->new_with_id = true;
461
		}
462
463
		/* satisfy basic HTML email requirements */
464
		$this->name = $request['sendSubject'];
465
		$this->description_html = '&lt;html&gt;&lt;body&gt;'.$request['sendDescription'].'&lt;/body&gt;&lt;/html&gt;';
466
467
		/**********************************************************************
468
		 * PHPMAILER PREP
469
		 */
470
		$mail = new SugarPHPMailer();
471
		$mail = $this->setMailer($mail, '', $_REQUEST['fromAccount']);
472
		if (empty($mail->Host) && !$this->isDraftEmail($request))
473
		{
474
            $this->status = 'send_error';
475
476
            if ($mail->oe->type == 'system')
477
            	echo($app_strings['LBL_EMAIL_ERROR_PREPEND']. $app_strings['LBL_EMAIL_INVALID_SYSTEM_OUTBOUND']);
478
             else
479
            	echo($app_strings['LBL_EMAIL_ERROR_PREPEND']. $app_strings['LBL_EMAIL_INVALID_PERSONAL_OUTBOUND']);
480
481
            return false;
482
		}
483
484
		$subject = $this->name;
485
		$mail->Subject = from_html($this->name);
486
487
		// work-around legacy code in SugarPHPMailer
488
		if($_REQUEST['setEditor'] == 1) {
489
			$_REQUEST['description_html'] = $_REQUEST['sendDescription'];
490
			$this->description_html = $_REQUEST['description_html'];
491
		} else {
492
			$this->description_html = '';
493
			$this->description = $_REQUEST['sendDescription'];
494
		}
495
		// end work-around
496
497
		if ( $this->isDraftEmail($request) )
498
		{
499
			if($this->type != 'draft' && $this->status != 'draft') {
500
	        	$this->id = create_guid();
501
	        	$this->new_with_id = true;
502
	        	$this->date_entered = "";
503
			} // if
504
			$q1 = "update emails_email_addr_rel set deleted = 1 WHERE email_id = '{$this->id}'";
505
			$r1 = $this->db->query($q1);
506
		} // if
507
508
		if (isset($request['saveDraft'])) {
509
			$this->type = 'draft';
510
			$this->status = 'draft';
511
			$forceSave = true;
512
		} else {
513
			/* Apply Email Templates */
514
			// do not parse email templates if the email is being saved as draft....
515
		    $toAddresses = $this->email2ParseAddresses($_REQUEST['sendTo']);
516
	        $sea = new SugarEmailAddress();
517
	        $object_arr = array();
518
519
			if( isset($_REQUEST['parent_type']) && !empty($_REQUEST['parent_type']) &&
520
				isset($_REQUEST['parent_id']) && !empty($_REQUEST['parent_id']) &&
521
				($_REQUEST['parent_type'] == 'Accounts' ||
522
				$_REQUEST['parent_type'] == 'Contacts' ||
523
				$_REQUEST['parent_type'] == 'Leads' ||
524
				$_REQUEST['parent_type'] == 'Users' ||
525
				$_REQUEST['parent_type'] == 'Prospects')) {
526
					if(isset($beanList[$_REQUEST['parent_type']]) && !empty($beanList[$_REQUEST['parent_type']])) {
527
						$className = $beanList[$_REQUEST['parent_type']];
528
						if(isset($beanFiles[$className]) && !empty($beanFiles[$className])) {
529
							if(!class_exists($className)) {
530
								require_once($beanFiles[$className]);
531
							}
532
							$bean = new $className();
533
							$bean->retrieve($_REQUEST['parent_id']);
534
	                		$object_arr[$bean->module_dir] = $bean->id;
535
						} // if
536
					} // if
537
			}
538
			foreach($toAddresses as $addrMeta) {
539
				$addr = $addrMeta['email'];
540
				$beans = $sea->getBeansByEmailAddress($addr);
541
				foreach($beans as $bean) {
542
					if (!isset($object_arr[$bean->module_dir])) {
543
						$object_arr[$bean->module_dir] = $bean->id;
544
					}
545
				}
546
			}
547
548
	        /* template parsing */
549
	        if (empty($object_arr)) {
550
	          $object_arr= array('Contacts' => '123');
551
	        }
552
	        $object_arr['Users'] = $current_user->id;
553
	        $this->description_html = EmailTemplate::parse_template($this->description_html, $object_arr);
554
	        $this->name = EmailTemplate::parse_template($this->name, $object_arr);
555
	        $this->description = EmailTemplate::parse_template($this->description, $object_arr);
556
	        $this->description = html_entity_decode($this->description,ENT_COMPAT,'UTF-8');
0 ignored issues
show
Unused Code introduced by
The call to html_entity_decode() has too many arguments starting with 'UTF-8'.

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...
557
			if($this->type != 'draft' && $this->status != 'draft') {
558
	        	$this->id = create_guid();
559
	        	$this->date_entered = "";
560
	        	$this->new_with_id = true;
561
		        $this->type = 'out';
562
		        $this->status = 'sent';
563
			}
564
        }
565
566
        if(isset($_REQUEST['parent_type']) && empty($_REQUEST['parent_type']) &&
567
			isset($_REQUEST['parent_id']) && empty($_REQUEST['parent_id']) ) {
568
				$this->parent_id = "";
569
				$this->parent_type = "";
570
		} // if
571
572
573
        $mail->Subject = $this->name;
574
        $mail = $this->handleBody($mail);
575
        $mail->Subject = $this->name;
576
        $this->description_html = from_html($this->description_html);
577
        $this->description_html = $this->decodeDuringSend($this->description_html);
578
		$this->description = $this->decodeDuringSend($this->description);
579
580
		/* from account */
581
		$replyToAddress = $current_user->emailAddress->getReplyToAddress($current_user, true);
582
		$replyToName = "";
583
		if(empty($request['fromAccount'])) {
584
			$defaults = $current_user->getPreferredEmail();
585
			$mail->From = $defaults['email'];
586
			$mail->FromName = $defaults['name'];
587
			$replyToName = $mail->FromName;
588
			//$replyToAddress = $current_user->emailAddress->getReplyToAddress($current_user);
589
		} else {
590
			// passed -> user -> system default
591
			$ie = new InboundEmail();
592
			$ie->retrieve($request['fromAccount']);
593
			$storedOptions = unserialize(base64_decode($ie->stored_options));
594
			$fromName = "";
595
			$fromAddress = "";
596
			$replyToName = "";
597
			//$replyToAddress = "";
598
			if (!empty($storedOptions)) {
599
				$fromAddress = $storedOptions['from_addr'];
600
				$fromName = from_html($storedOptions['from_name']);
601
				$replyToAddress = (isset($storedOptions['reply_to_addr']) ? $storedOptions['reply_to_addr'] : "");
602
				$replyToName = (isset($storedOptions['reply_to_name']) ? from_html($storedOptions['reply_to_name']) : "");
603
			} // if
604
			$defaults = $current_user->getPreferredEmail();
605
			// Personal Account doesn't have reply To Name and Reply To Address. So add those columns on UI
606
			// After adding remove below code
607
608
			// code to remove
609
			if ($ie->is_personal)
610
			{
611
				if (empty($replyToAddress))
612
				{
613
					$replyToAddress = $current_user->emailAddress->getReplyToAddress($current_user, true);
614
				} // if
615
				if (empty($replyToName))
616
				{
617
					$replyToName = $defaults['name'];
618
				} // if
619
				//Personal accounts can have a reply_address, which should
620
				//overwrite the users set default.
621
				if( !empty($storedOptions['reply_to_addr']) )
622
					$replyToAddress = $storedOptions['reply_to_addr'];
623
624
			}
625
			// end of code to remove
626
			$mail->From = (!empty($fromAddress)) ? $fromAddress : $defaults['email'];
627
			$mail->FromName = (!empty($fromName)) ? $fromName : $defaults['name'];
628
			$replyToName = (!empty($replyToName)) ? $replyToName : $mail->FromName;
629
		}
630
631
		$mail->Sender = $mail->From; /* set Return-Path field in header to reduce spam score in emails sent via Sugar's Email module */
632
633
		if (!empty($replyToAddress)) {
634
			$mail->AddReplyTo($replyToAddress,$locale->translateCharsetMIME(trim( $replyToName), 'UTF-8', $OBCharset));
635
		} else {
636
			$mail->AddReplyTo($mail->From,$locale->translateCharsetMIME(trim( $mail->FromName), 'UTF-8', $OBCharset));
637
		} // else
638
        $emailAddressCollection = array(); // used in linking to beans below
639
		// handle to/cc/bcc
640
		foreach($this->email2ParseAddresses($request['sendTo']) as $addr_arr) {
641
			if(empty($addr_arr['email'])) continue;
642
643
			if(empty($addr_arr['display'])) {
644
				$mail->AddAddress($addr_arr['email'], "");
645
			} else {
646
				$mail->AddAddress($addr_arr['email'],$locale->translateCharsetMIME(trim( $addr_arr['display']), 'UTF-8', $OBCharset));
647
			}
648
			$emailAddressCollection[] = $addr_arr['email'];
649
		}
650
		foreach($this->email2ParseAddresses($request['sendCc']) as $addr_arr) {
651
			if(empty($addr_arr['email'])) continue;
652
653
			if(empty($addr_arr['display'])) {
654
				$mail->AddCC($addr_arr['email'], "");
655
			} else {
656
				$mail->AddCC($addr_arr['email'],$locale->translateCharsetMIME(trim( $addr_arr['display']), 'UTF-8', $OBCharset));
657
			}
658
			$emailAddressCollection[] = $addr_arr['email'];
659
		}
660
661
		foreach($this->email2ParseAddresses($request['sendBcc']) as $addr_arr) {
662
			if(empty($addr_arr['email'])) continue;
663
664
			if(empty($addr_arr['display'])) {
665
				$mail->AddBCC($addr_arr['email'], "");
666
			} else {
667
				$mail->AddBCC($addr_arr['email'],$locale->translateCharsetMIME(trim( $addr_arr['display']), 'UTF-8', $OBCharset));
668
			}
669
			$emailAddressCollection[] = $addr_arr['email'];
670
		}
671
672
673
		/* parse remove attachments array */
674
		$removeAttachments = array();
675
		if(!empty($request['templateAttachmentsRemove'])) {
676
			$exRemove = explode("::", $request['templateAttachmentsRemove']);
677
678
			foreach($exRemove as $file) {
679
				$removeAttachments = substr($file, 0, 36);
680
			}
681
		}
682
683
		/* handle attachments */
684
		if(!empty($request['attachments'])) {
685
			$exAttachments = explode("::", $request['attachments']);
686
687
			foreach($exAttachments as $file) {
688
				$file = trim(from_html($file));
689
				$file = str_replace("\\", "", $file);
690
				if(!empty($file)) {
691
					//$fileLocation = $this->et->userCacheDir."/{$file}";
692
					$fileGUID = preg_replace('/[^a-z0-9\-]/', "", substr($file, 0, 36));
693
					$fileLocation = $this->et->userCacheDir."/{$fileGUID}";
694
					$filename = substr($file, 36, strlen($file)); // strip GUID	for PHPMailer class to name outbound file
695
696
					$mail->AddAttachment($fileLocation,$filename, 'base64', $this->email2GetMime($fileLocation));
697
					//$mail->AddAttachment($fileLocation, $filename, 'base64');
698
699
					// only save attachments if we're archiving or drafting
700
					if((($this->type == 'draft') && !empty($this->id)) || (isset($request['saveToSugar']) && $request['saveToSugar'] == 1)) {
701
						$note = new Note();
702
						$note->id = create_guid();
703
						$note->new_with_id = true; // duplicating the note with files
704
						$note->parent_id = $this->id;
705
						$note->parent_type = $this->module_dir;
706
						$note->name = $filename;
707
						$note->filename = $filename;
708
						$note->file_mime_type = $this->email2GetMime($fileLocation);
709
                        $dest = "upload://{$note->id}";
710
						if(!copy($fileLocation, $dest)) {
711
							$GLOBALS['log']->debug("EMAIL 2.0: could not copy attachment file to $fileLocation => $dest");
712
						}
713
714
						$note->save();
715
					}
716
				}
717
			}
718
		}
719
720
		/* handle sugar documents */
721
		if(!empty($request['documents'])) {
722
			$exDocs = explode("::", $request['documents']);
723
724
			foreach($exDocs as $docId) {
725
				$docId = trim($docId);
726
				if(!empty($docId)) {
727
					$doc = new Document();
728
					$docRev = new DocumentRevision();
729
					$doc->retrieve($docId);
730
					$docRev->retrieve($doc->document_revision_id);
731
732
					$filename = $docRev->filename;
733
					$docGUID = preg_replace('/[^a-z0-9\-]/', "", $docRev->id);
734
					$fileLocation = "upload://{$docGUID}";
735
					$mime_type = $docRev->file_mime_type;
736
					$mail->AddAttachment($fileLocation,$locale->translateCharsetMIME(trim($filename), 'UTF-8', $OBCharset), 'base64', $mime_type);
737
738
					// only save attachments if we're archiving or drafting
739
					if((($this->type == 'draft') && !empty($this->id)) || (isset($request['saveToSugar']) && $request['saveToSugar'] == 1)) {
740
						$note = new Note();
741
						$note->id = create_guid();
742
						$note->new_with_id = true; // duplicating the note with files
743
						$note->parent_id = $this->id;
744
						$note->parent_type = $this->module_dir;
745
						$note->name = $filename;
746
						$note->filename = $filename;
747
						$note->file_mime_type = $mime_type;
748
                        $dest = "upload://{$note->id}";
749
						if(!copy($fileLocation, $dest)) {
750
							$GLOBALS['log']->debug("EMAIL 2.0: could not copy SugarDocument revision file $fileLocation => $dest");
751
						}
752
753
						$note->save();
754
					}
755
				}
756
			}
757
		}
758
759
		/* handle template attachments */
760
		if(!empty($request['templateAttachments'])) {
761
762
			$exNotes = explode("::", $request['templateAttachments']);
763
			foreach($exNotes as $noteId) {
764
				$noteId = trim($noteId);
765
				if(!empty($noteId)) {
766
					$note = new Note();
767
					$note->retrieve($noteId);
768
					if (!empty($note->id)) {
769
						$filename = $note->filename;
770
						$noteGUID = preg_replace('/[^a-z0-9\-]/', "", $note->id);
771
						$fileLocation = "upload://{$noteGUID}";
772
						$mime_type = $note->file_mime_type;
773
						if (!$note->embed_flag) {
774
							$mail->AddAttachment($fileLocation,$filename, 'base64', $mime_type);
775
							// only save attachments if we're archiving or drafting
776
							if((($this->type == 'draft') && !empty($this->id)) || (isset($request['saveToSugar']) && $request['saveToSugar'] == 1)) {
777
778
								if ($note->parent_id != $this->id)
779
								    $this->saveTempNoteAttachments($filename,$fileLocation, $mime_type);
780
							} // if
781
782
						} // if
783
					} else {
784
						//$fileLocation = $this->et->userCacheDir."/{$file}";
785
						$fileGUID = preg_replace('/[^a-z0-9\-]/', "", substr($noteId, 0, 36));
786
						$fileLocation = $this->et->userCacheDir."/{$fileGUID}";
787
						//$fileLocation = $this->et->userCacheDir."/{$noteId}";
788
						$filename = substr($noteId, 36, strlen($noteId)); // strip GUID	for PHPMailer class to name outbound file
789
790
						$mail->AddAttachment($fileLocation,$locale->translateCharsetMIME(trim($filename), 'UTF-8', $OBCharset), 'base64', $this->email2GetMime($fileLocation));
791
792
						//If we are saving an email we were going to forward we need to save the attachments as well.
793
						if( (($this->type == 'draft') && !empty($this->id))
794
						      || (isset($request['saveToSugar']) && $request['saveToSugar'] == 1))
795
						  {
796
						      $mimeType = $this->email2GetMime($fileLocation);
797
						      $this->saveTempNoteAttachments($filename,$fileLocation, $mimeType);
798
						 } // if
799
					}
800
				}
801
			}
802
		}
803
804
805
806
		/**********************************************************************
807
		 * Final Touches
808
		 */
809
		/* save email to sugar? */
810
		$forceSave = false;
811
812
		if($this->type == 'draft' && !isset($request['saveDraft'])) {
813
			// sending a draft email
814
			$this->type = 'out';
815
			$this->status = 'sent';
816
			$forceSave = true;
817
		} elseif(isset($request['saveDraft'])) {
818
			$this->type = 'draft';
819
			$this->status = 'draft';
820
			$forceSave = true;
821
		}
822
823
		      /**********************************************************************
824
         * SEND EMAIL (finally!)
825
         */
826
        $mailSent = false;
827
        if ($this->type != 'draft') {
828
            $mail->prepForOutbound();
829
            $mail->Body = $this->decodeDuringSend($mail->Body);
830
            $mail->AltBody = $this->decodeDuringSend($mail->AltBody);
831
            if (!$mail->Send()) {
832
                $this->status = 'send_error';
833
                ob_clean();
834
                echo($app_strings['LBL_EMAIL_ERROR_PREPEND']. $mail->ErrorInfo);
835
                return false;
836
            }
837
        }
838
839
		if ((!(empty($orignialId) || isset($request['saveDraft']) || ($this->type == 'draft' && $this->status == 'draft'))) &&
840
			(($_REQUEST['composeType'] == 'reply') || ($_REQUEST['composeType'] == 'replyAll') || ($_REQUEST['composeType'] == 'replyCase')) && ($orignialId != $this->id)) {
841
			$originalEmail = new Email();
842
			$originalEmail->retrieve($orignialId);
843
			$originalEmail->reply_to_status = 1;
844
			$originalEmail->save();
845
			$this->reply_to_status = 0;
846
		} // if
847
848
		if ($_REQUEST['composeType'] == 'reply' || $_REQUEST['composeType'] == 'replyCase') {
849
			if (isset($_REQUEST['ieId']) && isset($_REQUEST['mbox'])) {
850
				$emailFromIe = new InboundEmail();
851
				$emailFromIe->retrieve($_REQUEST['ieId']);
852
				$emailFromIe->mailbox = $_REQUEST['mbox'];
853
				if (isset($emailFromIe->id) && $emailFromIe->is_personal) {
854
					if ($emailFromIe->isPop3Protocol()) {
855
						$emailFromIe->mark_answered($this->uid, 'pop3');
856
					}
857
					elseif ($emailFromIe->connectMailserver() == 'true') {
858
						$emailFromIe->markEmails($this->uid, 'answered');
859
						$emailFromIe->mark_answered($this->uid);
860
					}
861
				}
862
			}
863
		}
864
865
866
		if(	$forceSave ||
867
			$this->type == 'draft' ||
868
			(isset($request['saveToSugar']) && $request['saveToSugar'] == 1)) {
869
870
			// saving a draft OR saving a sent email
871
			$decodedFromName = mb_decode_mimeheader($mail->FromName);
872
			$this->from_addr = "{$decodedFromName} <{$mail->From}>";
873
			$this->from_addr_name = $this->from_addr;
874
			$this->to_addrs = $_REQUEST['sendTo'];
875
			$this->to_addrs_names = $_REQUEST['sendTo'];
876
			$this->cc_addrs = $_REQUEST['sendCc'];
877
			$this->cc_addrs_names = $_REQUEST['sendCc'];
878
			$this->bcc_addrs = $_REQUEST['sendBcc'];
879
			$this->bcc_addrs_names = $_REQUEST['sendBcc'];
880
			$this->assigned_user_id = $current_user->id;
881
882
			$this->date_sent = $timedate->now();
883
			///////////////////////////////////////////////////////////////////
884
			////	LINK EMAIL TO SUGARBEANS BASED ON EMAIL ADDY
885
886
			if( isset($_REQUEST['parent_type']) && !empty($_REQUEST['parent_type']) &&
887
				isset($_REQUEST['parent_id']) && !empty($_REQUEST['parent_id']) ) {
888
	                $this->parent_id = $_REQUEST['parent_id'];
889
	                $this->parent_type = $_REQUEST['parent_type'];
890
					$q = "SELECT count(*) c FROM emails_beans WHERE  email_id = '{$this->id}' AND bean_id = '{$_REQUEST['parent_id']}' AND bean_module = '{$_REQUEST['parent_type']}'";
891
					$r = $this->db->query($q);
892
					$a = $this->db->fetchByAssoc($r);
893
					if($a['c'] <= 0) {
894
						if(isset($beanList[$_REQUEST['parent_type']]) && !empty($beanList[$_REQUEST['parent_type']])) {
895
							$className = $beanList[$_REQUEST['parent_type']];
896
							if(isset($beanFiles[$className]) && !empty($beanFiles[$className])) {
897
								if(!class_exists($className)) {
898
									require_once($beanFiles[$className]);
899
								}
900
								$bean = new $className();
901
								$bean->retrieve($_REQUEST['parent_id']);
902
								if($bean->load_relationship('emails')) {
903
									$bean->emails->add($this->id);
904
								} // if
905
906
							} // if
907
908
						} // if
909
910
					} // if
911
912
				} else {
913
					if(!class_exists('aCase')) {
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...
914
915
					}
916
					else{
917
						$c = new aCase();
918
						if($caseId = InboundEmail::getCaseIdFromCaseNumber($mail->Subject, $c)) {
919
							$c->retrieve($caseId);
920
							$c->load_relationship('emails');
921
							$c->emails->add($this->id);
922
							$this->parent_type = "Cases";
923
							$this->parent_id = $caseId;
924
						} // if
925
					}
926
927
				} // else
928
929
			////	LINK EMAIL TO SUGARBEANS BASED ON EMAIL ADDY
930
			///////////////////////////////////////////////////////////////////
931
			$this->save();
932
		}
933
934
		if(!empty($request['fromAccount'])) {
935
			if (isset($ie->id) && !$ie->isPop3Protocol() && $mail->oe->mail_smtptype != 'gmail') {
936
				$sentFolder = $ie->get_stored_options("sentFolder");
937
				if (!empty($sentFolder)) {
938
					$data = $mail->CreateHeader() . "\r\n" . $mail->CreateBody() . "\r\n";
939
					$ie->mailbox = $sentFolder;
940
					if ($ie->connectMailserver() == 'true') {
941
						$connectString = $ie->getConnectString($ie->getServiceString(), $ie->mailbox);
942
						$returnData = imap_append($ie->conn,$connectString, $data, "\\Seen");
943
						if (!$returnData) {
944
							$GLOBALS['log']->debug("could not copy email to {$ie->mailbox} for {$ie->name}");
945
						} // if
946
					} else {
947
						$GLOBALS['log']->debug("could not connect to mail serve for folder {$ie->mailbox} for {$ie->name}");
948
					} // else
949
				} else {
950
					$GLOBALS['log']->debug("could not copy email to {$ie->mailbox} sent folder as its empty");
951
				} // else
952
			} // if
953
		} // if
954
		return true;
955
	} // end email2send
956
957
    /**
958
     * Generates a config-specified separated name and addresses to be used in compose email screen for
959
     * contacts or leads from listview
960
     * By default, use comma, but allow for non-standard delimiters as specified in email_address_separator
961
     *
962
     * @param $module string module name
963
     * @param $idsArray array of record ids to get the email address for
964
     * @return string (config-specified) delimited list of email addresses
965
     */
966 1
    public function getNamePlusEmailAddressesForCompose($module, $idsArray)
967
    {
968 1
        global $locale;
969 1
        $result = array();
970
971 1
        foreach ($idsArray as $id)
972
        {
973
            // Load bean
974 1
            $bean = BeanFactory::getBean($module, $id);
975
976
            // Got a bean
977 1
            if (!empty($bean))
978
            {
979
                // For CE, just get primary e-mail address
980 1
                $emailAddress = $bean->email1;
981
982
983
                // If we have an e-mail address loaded
984 1
                if (!empty($emailAddress))
985
                {
986
                    // Use bean name by default
987
                    $fullName = $bean->name;
988
989
                    // Depending on module, format the name
990
                    if (in_array($module, array('Users', 'Employees')))
991
                    {
992
                        $fullName = from_html(
993
                            $locale->getLocaleFormattedName(
994
                                $bean->first_name,
995
                                $bean->last_name,
996
                                '',
997
                                $bean->title
998
                            )
999
                        );
1000
                    }
1001
                    else if (SugarModule::get($module)->moduleImplements('Person'))
1002
                    {
1003
                        $fullName = from_html(
1004
                            $locale->getLocaleFormattedName(
1005
                                $bean->first_name,
1006
                                $bean->last_name,
1007
                                $bean->salutation,
1008
                                $bean->title
1009
                            )
1010
                        );
1011
                    }
1012
1013
                    // Make e-mail address in format "Name <@email>"
1014 1
                    $result[$bean->id] = $fullName . " <" . from_html($emailAddress) . ">";
1015
                }
1016
            }
1017
        }
1018
1019
        // Broken out of method to facilitate unit testing
1020 1
        return $this->_arrayToDelimitedString($result);
1021
    }
1022
1023
    /**
1024
     * @param Array $arr - list of strings
1025
     * @return string the list of strings delimited by email_address_separator
1026
     */
1027 2
    function _arrayToDelimitedString($arr)
1028
    {
1029
        // bug 51804: outlook does not respect the correct email address separator (',') , so let
1030
        // clients override the default.
1031 2
        $separator = (isset($GLOBALS['sugar_config']['email_address_separator']) &&
1032 2
                        !empty($GLOBALS['sugar_config']['email_address_separator'])) ?
1033 2
                     $GLOBALS['sugar_config']['email_address_separator'] :
1034 2
                     ',';
1035
1036 2
		return join($separator, array_values($arr));
1037
    }
1038
1039
	/**
1040
	 * Overrides
1041
	 */
1042
	///////////////////////////////////////////////////////////////////////////
1043
	////	SAVERS
1044 3
	function save($check_notify = false) {
1045 3
        global $current_user;
1046
1047 3
		if($this->isDuplicate) {
1048
			$GLOBALS['log']->debug("EMAIL - tried to save a duplicate Email record");
1049
		} else {
1050
1051 3
			if(empty($this->id)) {
1052 3
				$this->id = create_guid();
1053 3
				$this->new_with_id = true;
1054
			}
1055 3
			$this->from_addr_name = $this->cleanEmails($this->from_addr_name);
1056 3
			$this->to_addrs_names = $this->cleanEmails($this->to_addrs_names);
1057 3
			$this->cc_addrs_names = $this->cleanEmails($this->cc_addrs_names);
1058 3
			$this->bcc_addrs_names = $this->cleanEmails($this->bcc_addrs_names);
1059 3
			$this->reply_to_addr = $this->cleanEmails($this->reply_to_addr);
1060 3
			$this->description = SugarCleaner::cleanHtml($this->description);
1061 3
            $this->description_html = SugarCleaner::cleanHtml($this->description_html, true);
1062 3
            $this->raw_source = SugarCleaner::cleanHtml($this->raw_source, true);
1063 3
			$this->saveEmailText();
1064 3
			$this->saveEmailAddresses();
1065
1066 3
			$GLOBALS['log']->debug('-------------------------------> Email called save()');
1067
1068
			// handle legacy concatenation of date and time fields
1069
			//Bug 39503 - SugarBean is not setting date_sent when seconds missing
1070 3
 			if(empty($this->date_sent)) {
1071 3
				global $timedate;
1072 3
				$date_sent_obj = $timedate->fromUser($timedate->merge_date_time($this->date_start, $this->time_start), $current_user);
1073 3
                 if (!empty($date_sent_obj) && ($date_sent_obj instanceof SugarDateTime)) {
1074 1
 				    $this->date_sent = $date_sent_obj->asDb();
1075
                 }
1076
			}
1077
1078 3
			parent::save($check_notify);
1079
1080 3
			if(!empty($this->parent_type) && !empty($this->parent_id)) {
1081 1
                if(!empty($this->fetched_row) && !empty($this->fetched_row['parent_id']) && !empty($this->fetched_row['parent_type'])) {
1082
                    if($this->fetched_row['parent_id'] != $this->parent_id || $this->fetched_row['parent_type'] != $this->parent_type) {
1083
                        $mod = strtolower($this->fetched_row['parent_type']);
1084
                        $rel = array_key_exists($mod, $this->field_defs) ? $mod : $mod . "_activities_emails"; //Custom modules rel name
1085
                        if($this->load_relationship($rel) ) {
1086
                            $this->$rel->delete($this->id, $this->fetched_row['parent_id']);
1087
                        }
1088
                    }
1089
                }
1090 1
                $mod = strtolower($this->parent_type);
1091 1
                $rel = array_key_exists($mod, $this->field_defs) ? $mod : $mod . "_activities_emails"; //Custom modules rel name
1092 1
                if($this->load_relationship($rel) ) {
1093
                    $this->$rel->add($this->parent_id);
1094
                }
1095
			}
1096
		}
1097 3
		$GLOBALS['log']->debug('-------------------------------> Email save() done');
1098 3
	}
1099
1100
	/**
1101
	 * Helper function to save temporary attachments assocaited to an email as note.
1102
	 *
1103
	 * @param string $filename
1104
	 * @param string $fileLocation
1105
	 * @param string $mimeType
1106
	 */
1107 1
	function saveTempNoteAttachments($filename,$fileLocation, $mimeType)
1108
	{
1109 1
	    $tmpNote = new Note();
1110 1
	    $tmpNote->id = create_guid();
1111 1
	    $tmpNote->new_with_id = true;
1112 1
	    $tmpNote->parent_id = $this->id;
1113 1
	    $tmpNote->parent_type = $this->module_dir;
1114 1
	    $tmpNote->name = $filename;
1115 1
	    $tmpNote->filename = $filename;
1116 1
	    $tmpNote->file_mime_type = $mimeType;
1117 1
	    $noteFile = "upload://{$tmpNote->id}";
1118 1
	    if(!copy($fileLocation, $noteFile)) {
1119 1
    	    $GLOBALS['log']->fatal("EMAIL 2.0: could not copy SugarDocument revision file $fileLocation => $noteFile");
1120
	    }
1121 1
	    $tmpNote->save();
1122 1
	}
1123
	/**
1124
	 * Handles normalization of Email Addressess
1125
	 */
1126 3
	function saveEmailAddresses() {
1127
		// from, single address
1128 3
		$fromId = $this->emailAddress->getEmailGUID(from_html($this->from_addr));
0 ignored issues
show
Bug introduced by
It seems like $this->from_addr can also be of type array<string,?,{"name":"?"}> or null; however, from_html() 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...
1129 3
        if(!empty($fromId)){
1130 3
		  $this->linkEmailToAddress($fromId, 'from');
1131
        }
1132
1133
		// to, multiple
1134 3
		$replace = array(",",";");
1135 3
		$toaddrs = str_replace($replace, "::", from_html($this->to_addrs));
0 ignored issues
show
Bug introduced by
It seems like $this->to_addrs can also be of type array<string,?,{"name":"?"}> or null; however, from_html() 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...
1136 3
		$exToAddrs = explode("::", $toaddrs);
1137
1138 3
		if(!empty($exToAddrs)) {
1139 3
			foreach($exToAddrs as $toaddr) {
1140 3
				$toaddr = trim($toaddr);
1141 3
				if(!empty($toaddr)) {
1142 2
					$toId = $this->emailAddress->getEmailGUID($toaddr);
1143 3
					$this->linkEmailToAddress($toId, 'to');
1144
				}
1145
			}
1146
		}
1147
1148
		// cc, multiple
1149 3
		$ccAddrs = str_replace($replace, "::", from_html($this->cc_addrs));
1150 3
		$exccAddrs = explode("::", $ccAddrs);
1151
1152 3
		if(!empty($exccAddrs)) {
1153 3
			foreach($exccAddrs as $ccAddr) {
1154 3
				$ccAddr = trim($ccAddr);
1155 3
				if(!empty($ccAddr)) {
1156 1
					$ccId = $this->emailAddress->getEmailGUID($ccAddr);
1157 3
					$this->linkEmailToAddress($ccId, 'cc');
1158
				}
1159
			}
1160
		}
1161
1162
		// bcc, multiple
1163 3
		$bccAddrs = str_replace($replace, "::", from_html($this->bcc_addrs));
1164 3
		$exbccAddrs = explode("::", $bccAddrs);
1165 3
		if(!empty($exbccAddrs)) {
1166 3
			foreach($exbccAddrs as $bccAddr) {
1167 3
				$bccAddr = trim($bccAddr);
1168 3
				if(!empty($bccAddr)) {
1169 1
					$bccId = $this->emailAddress->getEmailGUID($bccAddr);
1170 3
					$this->linkEmailToAddress($bccId, 'bcc');
1171
				}
1172
			}
1173
		}
1174 3
	}
1175
1176 3
	function linkEmailToAddress($id, $type) {
1177
		// TODO: make this update?
1178 3
		$q1 = "SELECT * FROM emails_email_addr_rel WHERE email_id = '{$this->id}' AND email_address_id = '{$id}' AND address_type = '{$type}' AND deleted = 0";
1179 3
		$r1 = $this->db->query($q1);
1180 3
		$a1 = $this->db->fetchByAssoc($r1);
1181
1182 3
		if(!empty($a1) && !empty($a1['id'])) {
1183 1
			return $a1['id'];
1184
		} else {
1185 3
			$guid = create_guid();
1186 3
			$q2 = "INSERT INTO emails_email_addr_rel VALUES('{$guid}', '{$this->id}', '{$type}', '{$id}', 0)";
1187 3
			$r2 = $this->db->query($q2);
1188
		}
1189
1190 3
		return $guid;
1191
	}
1192
1193
    protected $email_to_text = array(
1194
        "email_id" => "id",
1195
        "description" => "description",
1196
        "description_html" => "description_html",
1197
        "raw_source" => "raw_source",
1198
        "from_addr" => "from_addr_name",
1199
        "reply_to_addr" => "reply_to_addr",
1200
    	"to_addrs" => "to_addrs_names",
1201
        "cc_addrs" => "cc_addrs_names",
1202
        "bcc_addrs" => "bcc_addrs_names",
1203
    );
1204
1205 4
	function cleanEmails($emails)
1206
	{
1207 4
	    if(empty($emails)) return '';
1208 4
		$emails = str_replace(array(",",";"), "::", from_html($emails));
1209 4
		$addrs = explode("::", $emails);
1210 4
		$res = array();
1211 4
		foreach($addrs as $addr) {
1212 4
            $parts = $this->emailAddress->splitEmailAddress($addr);
1213 4
            if(empty($parts["email"])) {
1214 3
                continue;
1215
            }
1216 2
            if(!empty($parts["name"])) {
1217 1
                $res[] = "{$parts['name']} <{$parts['email']}>";
1218
            } else {
1219 2
                $res[] .= $parts["email"];
1220
            }
1221
		}
1222 4
		return join(", ", $res);
1223
	}
1224
1225 3
	protected function saveEmailText()
1226
	{
1227 3
        $text = SugarModule::get("EmailText")->loadBean();
1228 3
        foreach($this->email_to_text as $textfield=>$mailfield) {
1229 3
            $text->$textfield = $this->$mailfield;
1230
        }
1231 3
        $text->email_id = $this->id;
1232 3
		if(!$this->new_with_id) {
1233 1
            $this->db->update($text);
1234
		} else {
1235 3
		    $this->db->insert($text);
1236
		}
1237 3
	}
1238
1239
	///////////////////////////////////////////////////////////////////////////
1240
	////	RETRIEVERS
1241 2
	function retrieve($id = -1, $encoded=true, $deleted=true) {
1242
		// cn: bug 11915, return SugarBean's retrieve() call bean instead of $this
1243 2
		$ret = parent::retrieve($id, $encoded, $deleted);
1244
1245 2
		if($ret) {
1246 1
			$ret->retrieveEmailText();
0 ignored issues
show
Bug introduced by
The method retrieveEmailText() does not exist on SugarBean. Did you maybe mean retrieve()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1247
            //$ret->raw_source = SugarCleaner::cleanHtml($ret->raw_source);
1248 1
			$ret->description = to_html($ret->description);
1249
            //$ret->description_html = SugarCleaner::cleanHtml($ret->description_html);
1250 1
			$ret->retrieveEmailAddresses();
0 ignored issues
show
Bug introduced by
The method retrieveEmailAddresses() does not exist on SugarBean. Did you maybe mean retrieve()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1251
1252 1
			$ret->date_start = '';
1253 1
			$ret->time_start = '';
1254 1
			$dateSent = explode(' ', $ret->date_sent);
1255 1
			if (!empty($dateSent)) {
1256 1
			    $ret->date_start = $dateSent[0];
1257 1
			    if ( isset($dateSent[1]) )
1258
			        $ret->time_start = $dateSent[1];
1259
			}
1260
			// for Email 2.0
1261 1
			foreach($ret as $k => $v) {
0 ignored issues
show
Bug introduced by
The expression $ret of type object<SugarBean> is not traversable.
Loading history...
1262 1
				$this->$k = $v;
1263
			}
1264
		}
1265 2
		return $ret;
1266
	}
1267
1268
1269
	/**
1270
	 * Retrieves email addresses from GUIDs
1271
	 */
1272 1
	function retrieveEmailAddresses() {
1273 1
		$return = array();
1274
1275
		$q = "SELECT email_address, address_type
1276
				FROM emails_email_addr_rel eam
1277
				JOIN email_addresses ea ON ea.id = eam.email_address_id
1278 1
				WHERE eam.email_id = '{$this->id}' AND eam.deleted=0";
1279 1
		$r = $this->db->query($q);
1280
1281 1
		while($a = $this->db->fetchByAssoc($r)) {
1282 1
			if(!isset($return[$a['address_type']])) {
1283 1
				$return[$a['address_type']] = array();
1284
			}
1285 1
			$return[$a['address_type']][] = $a['email_address'];
1286
		}
1287
1288 1
		if(count($return) > 0) {
1289 1
			if(isset($return['from'])) {
1290 1
				$this->from_addr = implode(", ", $return['from']);
1291
			}
1292 1
			if(isset($return['to'])) {
1293 1
				$this->to_addrs = implode(", ", $return['to']);
1294
			}
1295 1
			if(isset($return['cc'])) {
1296 1
				$this->cc_addrs = implode(", ", $return['cc']);
1297
			}
1298 1
			if(isset($return['bcc'])) {
1299 1
				$this->bcc_addrs = implode(", ", $return['bcc']);
1300
			}
1301
		}
1302 1
	}
1303
1304
	/**
1305
	 * Handles longtext fields
1306
	 */
1307 2
	function retrieveEmailText() {
1308 2
		$q = "SELECT from_addr, reply_to_addr, to_addrs, cc_addrs, bcc_addrs, description, description_html, raw_source FROM emails_text WHERE email_id = '{$this->id}'";
1309 2
		$r = $this->db->query($q);
1310 2
		$a = $this->db->fetchByAssoc($r, false);
1311
1312 2
		$this->description = $a['description'];
1313 2
		$this->description_html = $a['description_html'];
1314 2
		$this->raw_source = $a['raw_source'];
1315 2
		$this->from_addr_name = $a['from_addr'];
1316 2
		$this->reply_to_addr  = $a['reply_to_addr'];
1317 2
		$this->to_addrs_names = $a['to_addrs'];
1318 2
		$this->cc_addrs_names = $a['cc_addrs'];
1319 2
		$this->bcc_addrs_names = $a['bcc_addrs'];
1320 2
	}
1321
1322 2
	function delete($id='') {
1323 2
		if(empty($id))
1324
			$id = $this->id;
1325
1326 2
        $id = $this->db->quote($id);
1327
1328 2
		$q  = "UPDATE emails SET deleted = 1 WHERE id = '{$id}'";
1329 2
		$qt = "UPDATE emails_text SET deleted = 1 WHERE email_id = '{$id}'";
1330 2
		$qf = "UPDATE folders_rel SET deleted = 1 WHERE polymorphic_id = '{$id}' AND polymorphic_module = 'Emails'";
1331 2
        $qn = "UPDATE notes SET deleted = 1 WHERE parent_id = '{$id}' AND parent_type = 'Emails'";
1332 2
        $this->db->query($q);
1333 2
        $this->db->query($qt);
1334 2
        $this->db->query($qf);
1335 2
        $this->db->query($qn);
1336 2
	}
1337
1338
	/**
1339
	 * creates the standard "Forward" info at the top of the forwarded message
1340
	 * @return string
1341
	 */
1342 1
	function getForwardHeader() {
1343 1
		global $mod_strings;
1344 1
		global $current_user;
1345
1346
		//$from = str_replace(array("&gt;","&lt;"), array(")","("), $this->from_name);
1347 1
		$from = to_html($this->from_name);
1348 1
		$subject = to_html($this->name);
1349 1
		$ret  = "<br /><br />";
1350 1
		$ret .= $this->replyDelimiter."{$mod_strings['LBL_FROM']} {$from}<br />";
1351 1
		$ret .= $this->replyDelimiter."{$mod_strings['LBL_DATE_SENT']} {$this->date_sent}<br />";
1352 1
		$ret .= $this->replyDelimiter."{$mod_strings['LBL_TO']} {$this->to_addrs}<br />";
1353 1
		$ret .= $this->replyDelimiter."{$mod_strings['LBL_CC']} {$this->cc_addrs}<br />";
1354 1
		$ret .= $this->replyDelimiter."{$mod_strings['LBL_SUBJECT']} {$subject}<br />";
1355 1
		$ret .= $this->replyDelimiter."<br />";
1356
1357 1
		return $ret;
1358
		//return from_html($ret);
1359
	}
1360
1361
    /**
1362
     * retrieves Notes that belong to this Email and stuffs them into the "attachments" attribute
1363
     */
1364 2
    function getNotes($id, $duplicate=false) {
1365 2
        if(!class_exists('Note')) {
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...
1366
1367
        }
1368
1369 2
        $exRemoved = array();
1370 2
		if(isset($_REQUEST['removeAttachment'])) {
1371
			$exRemoved = explode('::', $_REQUEST['removeAttachment']);
1372
		}
1373
1374 2
        $noteArray = array();
1375 2
        $q = "SELECT id FROM notes WHERE parent_id = '".$id."'";
1376 2
        $r = $this->db->query($q);
1377
1378 2
        while($a = $this->db->fetchByAssoc($r)) {
1379 1
        	if(!in_array($a['id'], $exRemoved)) {
1380 1
	            $note = new Note();
1381 1
	            $note->retrieve($a['id']);
1382
1383
	            // duplicate actual file when creating forwards
1384 1
		        if($duplicate) {
1385
		        	if(!class_exists('UploadFile')) {
1386
		        		require_once('include/upload_file.php');
1387
		        	}
1388
		        	// save a brand new Note
1389
		        	$noteDupe->id = create_guid();
1390
		        	$noteDupe->new_with_id = true;
1391
					$noteDupe->parent_id = $this->id;
1392
					$noteDupe->parent_type = $this->module_dir;
1393
1394
					$noteFile = new UploadFile();
1395
					$noteFile->duplicate_file($a['id'], $note->id, $note->filename);
1396
1397
					$note->save();
1398
		        }
1399
		        // add Note to attachments array
1400 1
	            $this->attachments[] = $note;
1401
        	}
1402
        }
1403 2
    }
1404
1405
	/**
1406
	 * creates the standard "Reply" info at the top of the forwarded message
1407
	 * @return string
1408
	 */
1409 1
	function getReplyHeader() {
1410 1
		global $mod_strings;
1411 1
		global $current_user;
1412
1413 1
		$from = str_replace(array("&gt;","&lt;", ">","<"), array(")","(",")","("), $this->from_name);
1414 1
		$ret  = "<br>{$mod_strings['LBL_REPLY_HEADER_1']} {$this->date_start}, {$this->time_start}, {$from} {$mod_strings['LBL_REPLY_HEADER_2']}";
1415
1416 1
		return from_html($ret);
1417
	}
1418
1419
	/**
1420
	 * Quotes plain-text email text
1421
	 * @param string $text
1422
	 * @return string
1423
	 */
1424 1
	function quotePlainTextEmail($text) {
1425 1
		$quoted = "\n";
1426
1427
		// plain-text
1428 1
		$desc = nl2br(trim($text));
1429 1
		$exDesc = explode('<br />', $desc);
1430
1431 1
		foreach($exDesc as $k => $line) {
1432 1
			$quoted .= '> '.trim($line)."\r";
1433
		}
1434
1435 1
		return $quoted;
1436
	}
1437
1438
	/**
1439
	 * "quotes" (i.e., "> my text yadda" the HTML part of an email
1440
	 * @param string $text HTML text to quote
1441
	 * @return string
1442
	 */
1443 1
	function quoteHtmlEmail($text) {
1444 1
		$text = trim(from_html($text));
1445
1446 1
		if(empty($text)) {
1447 1
			return '';
1448
		}
1449 1
		$out = "<div style='border-left:1px solid #00c; padding:5px; margin-left:10px;'>{$text}</div>";
1450
1451 1
		return $out;
1452
	}
1453
1454
	/**
1455
	 * "quotes" (i.e., "> my text yadda" the HTML part of an email
1456
	 * @param string $text HTML text to quote
1457
	 * @return string
1458
	 */
1459 1
	function quoteHtmlEmailForNewEmailUI($text) {
1460 1
		$text = trim($text);
1461
1462 1
		if(empty($text)) {
1463 1
			return '';
1464
		}
1465 1
		$text = str_replace("\n", "<BR/>", $text);
1466 1
		$out = "<div style='border-left:1px solid #00c; padding:5px; margin-left:10px;'>{$text}</div>";
1467
1468 1
		return $out;
1469
	}
1470
1471
	/**
1472
	 * Ensures that the user is able to send outbound emails
1473
	 */
1474 1
	function check_email_settings() {
1475 1
		global $current_user;
1476
1477 1
		$mail_fromaddress = $current_user->emailAddress->getPrimaryAddress($current_user);
1478 1
		$replyToName = $current_user->getPreference('mail_fromname');
1479 1
		$mail_fromname = (!empty($replyToName)) ? $current_user->getPreference('mail_fromname') : $current_user->full_name;
1480
1481 1
		if(empty($mail_fromaddress)) {
1482 1
			return false;
1483
		}
1484
		if(empty($mail_fromname)) {
1485
	  		return false;
1486
		}
1487
1488
    	$send_type = $current_user->getPreference('mail_sendtype') ;
1489
		if (!empty($send_type) && $send_type == "SMTP") {
1490
			$mail_smtpserver = $current_user->getPreference('mail_smtpserver');
1491
			$mail_smtpport = $current_user->getPreference('mail_smtpport');
1492
			$mail_smtpauth_req = $current_user->getPreference('mail_smtpauth_req');
1493
			$mail_smtpuser = $current_user->getPreference('mail_smtpuser');
1494
			$mail_smtppass = $current_user->getPreference('mail_smtppass');
1495
			if (empty($mail_smtpserver) ||
1496
				empty($mail_smtpport) ||
1497
                (!empty($mail_smtpauth_req) && ( empty($mail_smtpuser) || empty($mail_smtppass)))
1498
			) {
1499
				return false;
1500
			}
1501
		}
1502
		return true;
1503
	}
1504
1505
	/**
1506
	 * outputs JS to set fields in the MassUpdate form in the "My Inbox" view
1507
	 */
1508 1
	function js_set_archived() {
1509 1
		global $mod_strings;
1510
		$script = '
1511
		<script type="text/javascript" language="JavaScript"><!-- Begin
1512
			function setArchived() {
1513
				var form = document.getElementById("MassUpdate");
1514
				var status = document.getElementById("mass_status");
1515
				var ok = false;
1516
1517
				for(var i=0; i < form.elements.length; i++) {
1518
					if(form.elements[i].name == "mass[]") {
1519
						if(form.elements[i].checked == true) {
1520
							ok = true;
1521
						}
1522
					}
1523
				}
1524
1525
				if(ok == true) {
1526
					var user = document.getElementById("mass_assigned_user_name");
1527
					var team = document.getElementById("team");
1528
1529
					user.value = "";
1530
					for(var j=0; j<status.length; j++) {
1531
						if(status.options[j].value == "archived") {
1532
							status.options[j].selected = true;
1533
							status.selectedIndex = j; // for IE
1534
						}
1535
					}
1536
1537
					form.submit();
1538
				} else {
1539 1
					alert("'.$mod_strings['ERR_ARCHIVE_EMAIL'].'");
1540
				}
1541
1542
			}
1543 1
		//  End --></script>';
1544 1
		return $script;
1545
	}
1546
1547
	/**
1548
	 * replaces the javascript in utils.php - more specialized
1549
	 */
1550 1
	function u_get_clear_form_js($type='', $group='', $assigned_user_id='') {
1551 1
		$uType				= '';
1552 1
		$uGroup				= '';
1553 1
		$uAssigned_user_id	= '';
1554
1555 1
		if(!empty($type)) { $uType = '&type='.$type; }
1556 1
		if(!empty($group)) { $uGroup = '&group='.$group; }
1557 1
		if(!empty($assigned_user_id)) { $uAssigned_user_id = '&assigned_user_id='.$assigned_user_id; }
1558
1559
		$the_script = '
1560
		<script type="text/javascript" language="JavaScript"><!-- Begin
1561
			function clear_form(form) {
1562 1
				var newLoc = "index.php?action=" + form.action.value + "&module=" + form.module.value + "&query=true&clear_query=true'.$uType.$uGroup.$uAssigned_user_id.'";
1563
				if(typeof(form.advanced) != "undefined"){
1564
					newLoc += "&advanced=" + form.advanced.value;
1565
				}
1566
				document.location.href= newLoc;
1567
			}
1568 1
		//  End --></script>';
1569 1
		return $the_script;
1570
	}
1571
1572 1
	function pickOneButton() {
1573 1
		global $theme;
1574 1
		global $mod_strings;
1575 1
		$out = '<div><input	title="'.$mod_strings['LBL_BUTTON_GRAB_TITLE'].'"
1576
						class="button"
1577
						type="button" name="button"
1578
						onClick="window.location=\'index.php?module=Emails&action=Grab\';"
1579
						style="margin-bottom:2px"
1580 1
						value="  '.$mod_strings['LBL_BUTTON_GRAB'].'  "></div>';
1581 1
		return $out;
1582
	}
1583
1584
	/**
1585
	 * Determines what Editor (HTML or Plain-text) the current_user uses;
1586
	 * @return string Editor type
1587
	 */
1588 1
	function getUserEditorPreference() {
1589 1
		global $sugar_config;
1590 1
		global $current_user;
1591
1592 1
		$editor = '';
1593
1594 1
		if(!isset($sugar_config['email_default_editor'])) {
1595
			$sugar_config = $current_user->setDefaultsInConfig();
1596
		}
1597
1598 1
		$userEditor = $current_user->getPreference('email_editor_option');
1599 1
		$systemEditor = $sugar_config['email_default_editor'];
1600
1601 1
		if($userEditor != '') {
1602
			$editor = $userEditor;
1603
		} else {
1604 1
			$editor = $systemEditor;
1605
		}
1606
1607 1
		return $editor;
1608
	}
1609
1610
	/**
1611
	 * takes the mess we pass from EditView and tries to create some kind of order
1612
	 * @param array addrs
1613
	 * @param array addrs_ids (from contacts)
1614
	 * @param array addrs_names (from contacts);
1615
	 * @param array addrs_emails (from contacts);
1616
	 * @return array Parsed assoc array to feed to PHPMailer
1617
	 */
1618 1
	function parse_addrs($addrs, $addrs_ids, $addrs_names, $addrs_emails) {
1619
		// cn: bug 9406 - enable commas to separate email addresses
1620 1
		$addrs = str_replace(",", ";", $addrs);
1621
1622 1
		$ltgt = array('&lt;','&gt;');
1623 1
		$gtlt = array('<','>');
1624
1625 1
		$return				= array();
1626 1
		$addrs				= str_replace($ltgt, '', $addrs);
1627 1
		$addrs_arr			= explode(";",$addrs);
1628 1
		$addrs_arr			= $this->remove_empty_fields($addrs_arr);
1629 1
		$addrs_ids_arr		= explode(";",$addrs_ids);
1630 1
		$addrs_ids_arr		= $this->remove_empty_fields($addrs_ids_arr);
1631 1
		$addrs_emails_arr	= explode(";",$addrs_emails);
1632 1
		$addrs_emails_arr	= $this->remove_empty_fields($addrs_emails_arr);
1633 1
		$addrs_names_arr	= explode(";",$addrs_names);
1634 1
		$addrs_names_arr	= $this->remove_empty_fields($addrs_names_arr);
1635
1636
		///////////////////////////////////////////////////////////////////////
1637
		////	HANDLE EMAILS HAND-WRITTEN
1638 1
		$contactRecipients = array();
1639 1
		$knownEmails = array();
1640
1641 1
		foreach($addrs_arr as $i => $v) {
1642 1
			if(trim($v) == "")
1643
				continue; // skip any "blanks" - will always have 1
1644
1645 1
			$recipient = array();
1646
1647
			//// get the email to see if we're dealing with a dupe
1648
			//// what crappy coding
1649 1
			preg_match("/[A-Z0-9._%-\']+@[A-Z0-9.-]+\.[A-Z]{2,}/i",$v, $match);
1650
1651
1652 1
			if(!empty($match[0]) && !in_array(trim($match[0]), $knownEmails)) {
1653 1
				$knownEmails[] = $match[0];
1654 1
				$recipient['email'] = $match[0];
1655
1656
				//// handle the Display name
1657 1
				$display = trim(str_replace($match[0], '', $v));
1658
1659
				//// only trigger a "displayName" <email@address> when necessary
1660 1
				if(isset($addrs_names_arr[$i])){
1661 1
						$recipient['display'] = $addrs_names_arr[$i];
1662
				}
1663
				else if(!empty($display)) {
1664
					$recipient['display'] = $display;
1665
				}
1666 1
				if(isset($addrs_ids_arr[$i]) && $addrs_emails_arr[$i] == $match[0]){
1667 1
					$recipient['contact_id'] = $addrs_ids_arr[$i];
1668
				}
1669 1
				$return[] = $recipient;
1670
			}
1671
		}
1672
1673 1
		return $return;
1674
	}
1675
1676 2
	function remove_empty_fields(&$arr) {
1677 2
		$newarr = array();
1678
1679 2
		foreach($arr as $field) {
1680 2
			$field = trim($field);
1681 2
			if(empty($field)) {
1682 1
				continue;
1683
			}
1684 2
			array_push($newarr,$field);
1685
		}
1686 2
		return $newarr;
1687
	}
1688
1689
	/**
1690
	 * handles attachments of various kinds when sending email
1691
	 */
1692 1
	function handleAttachments() {
1693
1694
1695
1696
1697 1
		global $mod_strings;
1698
1699
        ///////////////////////////////////////////////////////////////////////////
1700
        ////    ATTACHMENTS FROM DRAFTS
1701 1
        if(($this->type == 'out' || $this->type == 'draft') && $this->status == 'draft' && isset($_REQUEST['record'])) {
1702 1
            $this->getNotes($_REQUEST['record']); // cn: get notes from OLD email for use in new email
1703
        }
1704
        ////    END ATTACHMENTS FROM DRAFTS
1705
        ///////////////////////////////////////////////////////////////////////////
1706
1707
        ///////////////////////////////////////////////////////////////////////////
1708
        ////    ATTACHMENTS FROM FORWARDS
1709
        // Bug 8034 Jenny - Need the check for type 'draft' here to handle cases where we want to save
1710
        // forwarded messages as drafts.  We still need to save the original message's attachments.
1711 1
        if(($this->type == 'out' || $this->type == 'draft') &&
1712 1
        	isset($_REQUEST['origType']) && $_REQUEST['origType'] == 'forward' &&
1713 1
        	isset($_REQUEST['return_id']) && !empty($_REQUEST['return_id'])
1714
        ) {
1715
            $this->getNotes($_REQUEST['return_id'], true);
1716
        }
1717
1718
        // cn: bug 8034 - attachments from forward/replies lost when saving in draft
1719 1
        if(isset($_REQUEST['prior_attachments']) && !empty($_REQUEST['prior_attachments']) && $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...
1720
        	$exIds = explode(",", $_REQUEST['prior_attachments']);
1721
        	if(!isset($_REQUEST['template_attachment'])) {
1722
        		$_REQUEST['template_attachment'] = array();
1723
        	}
1724
        	$_REQUEST['template_attachment'] = array_merge($_REQUEST['template_attachment'], $exIds);
1725
        }
1726
        ////    END ATTACHMENTS FROM FORWARDS
1727
        ///////////////////////////////////////////////////////////////////////////
1728
1729
		///////////////////////////////////////////////////////////////////////////
1730
		////	ATTACHMENTS FROM TEMPLATES
1731
		// to preserve individual email integrity, we must dupe Notes and associated files
1732
		// for each outbound email - good for integrity, bad for filespace
1733 1
		if(isset($_REQUEST['template_attachment']) && !empty($_REQUEST['template_attachment'])) {
1734
			$removeArr = array();
1735
			$noteArray = array();
1736
1737
			if(isset($_REQUEST['temp_remove_attachment']) && !empty($_REQUEST['temp_remove_attachment'])) {
1738
				$removeArr = $_REQUEST['temp_remove_attachment'];
1739
			}
1740
1741
1742
			foreach($_REQUEST['template_attachment'] as $noteId) {
1743
				if(in_array($noteId, $removeArr)) {
1744
					continue;
1745
				}
1746
				$noteTemplate = new Note();
1747
				$noteTemplate->retrieve($noteId);
1748
				$noteTemplate->id = create_guid();
1749
				$noteTemplate->new_with_id = true; // duplicating the note with files
1750
				$noteTemplate->parent_id = $this->id;
1751
				$noteTemplate->parent_type = $this->module_dir;
1752
				$noteTemplate->date_entered = '';
1753
				$noteTemplate->save();
1754
1755
				$noteFile = new UploadFile();
1756
				$noteFile->duplicate_file($noteId, $noteTemplate->id, $noteTemplate->filename);
1757
				$noteArray[] = $noteTemplate;
1758
			}
1759
			$this->attachments = array_merge($this->attachments, $noteArray);
1760
		}
1761
		////	END ATTACHMENTS FROM TEMPLATES
1762
		///////////////////////////////////////////////////////////////////////////
1763
1764
		///////////////////////////////////////////////////////////////////////////
1765
		////	ADDING NEW ATTACHMENTS
1766 1
		$max_files_upload = 10;
1767
        // Jenny - Bug 8211 Since attachments for drafts have already been processed,
1768
        // we don't need to re-process them.
1769 1
        if($this->status != "draft") {
1770
    		$notes_list = array();
1771
    		if(!empty($this->id) && !$this->new_with_id) {
1772
    			$note = new Note();
1773
    			$where = "notes.parent_id='{$this->id}'";
1774
    			$notes_list = $note->get_full_list("", $where, true);
1775
    		}
1776
    		$this->attachments = array_merge($this->attachments, $notes_list);
1777
        }
1778
		// cn: Bug 5995 - rudimentary error checking
1779
		$filesError = array(
1780 1
			0 => 'UPLOAD_ERR_OK - There is no error, the file uploaded with success.',
1781
			1 => 'UPLOAD_ERR_INI_SIZE - The uploaded file exceeds the upload_max_filesize directive in php.ini.',
1782
			2 => 'UPLOAD_ERR_FORM_SIZE - The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.',
1783
			3 => 'UPLOAD_ERR_PARTIAL - The uploaded file was only partially uploaded.',
1784
			4 => 'UPLOAD_ERR_NO_FILE - No file was uploaded.',
1785
			5 => 'UNKNOWN ERROR',
1786
			6 => 'UPLOAD_ERR_NO_TMP_DIR - Missing a temporary folder. Introduced in PHP 4.3.10 and PHP 5.0.3.',
1787
			7 => 'UPLOAD_ERR_CANT_WRITE - Failed to write file to disk. Introduced in PHP 5.1.0.',
1788
		);
1789
1790 1
		for($i = 0; $i < $max_files_upload; $i++) {
1791
			// cn: Bug 5995 - rudimentary error checking
1792 1
			if (!isset($_FILES["email_attachment{$i}"])) {
1793 1
				$GLOBALS['log']->debug("Email Attachment {$i} does not exist.");
1794 1
				continue;
1795
			}
1796
			if($_FILES['email_attachment'.$i]['error'] != 0 && $_FILES['email_attachment'.$i]['error'] != 4) {
1797
				$GLOBALS['log']->debug('Email Attachment could not be attach due to error: '.$filesError[$_FILES['email_attachment'.$i]['error']]);
1798
				continue;
1799
			}
1800
1801
			$note = new Note();
1802
			$note->parent_id = $this->id;
1803
			$note->parent_type = $this->module_dir;
1804
			$upload_file = new UploadFile('email_attachment'.$i);
1805
1806
			if(empty($upload_file)) {
1807
				continue;
1808
			}
1809
1810
			if(isset($_FILES['email_attachment'.$i]) && $upload_file->confirm_upload()) {
1811
				$note->filename = $upload_file->get_stored_file_name();
1812
				$note->file = $upload_file;
1813
				$note->name = $mod_strings['LBL_EMAIL_ATTACHMENT'].': '.$note->file->original_file_name;
1814
1815
				$this->attachments[] = $note;
1816
			}
1817
		}
1818
1819 1
		$this->saved_attachments = array();
1820 1
		foreach($this->attachments as $note) {
1821
			if(!empty($note->id)) {
1822
				array_push($this->saved_attachments, $note);
1823
				continue;
1824
			}
1825
			$note->parent_id = $this->id;
1826
			$note->parent_type = 'Emails';
1827
			$note->file_mime_type = $note->file->mime_type;
1828
			$note_id = $note->save();
1829
1830
			$this->saved_attachments[] = $note;
1831
1832
			$note->id = $note_id;
1833
			$note->file->final_move($note->id);
1834
		}
1835
		////	END NEW ATTACHMENTS
1836
		///////////////////////////////////////////////////////////////////////////
1837
1838
		///////////////////////////////////////////////////////////////////////////
1839
		////	ATTACHMENTS FROM DOCUMENTS
1840 1
		for($i=0; $i<10; $i++) {
1841 1
			if(isset($_REQUEST['documentId'.$i]) && !empty($_REQUEST['documentId'.$i])) {
1842
				$doc = new Document();
1843
				$docRev = new DocumentRevision();
1844
				$docNote = new Note();
1845
				$noteFile = new UploadFile();
1846
1847
				$doc->retrieve($_REQUEST['documentId'.$i]);
1848
				$docRev->retrieve($doc->document_revision_id);
1849
1850
				$this->saved_attachments[] = $docRev;
1851
1852
				// cn: bug 9723 - Emails with documents send GUID instead of Doc name
1853
				$docNote->name = $docRev->getDocumentRevisionNameForDisplay();
1854
				$docNote->filename = $docRev->filename;
1855
				$docNote->description = $doc->description;
1856
				$docNote->parent_id = $this->id;
1857
				$docNote->parent_type = 'Emails';
1858
				$docNote->file_mime_type = $docRev->file_mime_type;
1859
				$docId = $docNote = $docNote->save();
1860
1861
				$noteFile->duplicate_file($docRev->id, $docId, $docRev->filename);
1862
			}
1863
		}
1864
1865
		////	END ATTACHMENTS FROM DOCUMENTS
1866
		///////////////////////////////////////////////////////////////////////////
1867
1868
		///////////////////////////////////////////////////////////////////////////
1869
		////	REMOVE ATTACHMENTS
1870 1
        if(isset($_REQUEST['remove_attachment']) && !empty($_REQUEST['remove_attachment'])) {
1871
            foreach($_REQUEST['remove_attachment'] as $noteId) {
1872
                $q = 'UPDATE notes SET deleted = 1 WHERE id = \''.$noteId.'\'';
1873
                $this->db->query($q);
1874
            }
1875
        }
1876
1877
        //this will remove attachments that have been selected to be removed from drafts.
1878 1
        if(isset($_REQUEST['removeAttachment']) && !empty($_REQUEST['removeAttachment'])) {
1879
            $exRemoved = explode('::', $_REQUEST['removeAttachment']);
1880
            foreach($exRemoved as $noteId) {
1881
                $q = 'UPDATE notes SET deleted = 1 WHERE id = \''.$noteId.'\'';
1882
                $this->db->query($q);
1883
            }
1884
        }
1885
		////	END REMOVE ATTACHMENTS
1886
		///////////////////////////////////////////////////////////////////////////
1887 1
	}
1888
1889
1890
	/**
1891
	 * Determines if an email body (HTML or Plain) has a User signature already in the content
1892
	 * @param array Array of signatures
1893
	 * @return bool
1894
	 */
1895 1
	function hasSignatureInBody($sig) {
1896
		// strpos can't handle line breaks - normalize
1897 1
		$html = $this->removeAllNewlines($this->description_html);
1898 1
		$htmlSig = $this->removeAllNewlines($sig['signature_html']);
1899 1
		$plain = $this->removeAllNewlines($this->description);
1900 1
		$plainSig = $this->removeAllNewlines($sig['signature']);
1901
1902
		// cn: bug 11621 - empty sig triggers notice error
1903 1
		if(!empty($htmlSig) && false !== strpos($html, $htmlSig)) {
1904 1
			return true;
1905 1
		} elseif(!empty($plainSig) && false !== strpos($plain, $plainSig)) {
1906
			return true;
1907
		} else {
1908 1
			return false;
1909
		}
1910
	}
1911
1912
	/**
1913
	 * internal helper
1914
	 * @param string String to be normalized
1915
	 * @return string
1916
	 */
1917 2
	function removeAllNewlines($str) {
1918 2
		$bad = array("\r\n", "\n\r", "\n", "\r");
1919 2
		$good = array('', '', '', '');
1920
1921 2
		return str_replace($bad, $good, strip_tags(br2nl(from_html($str))));
1922
	}
1923
1924
1925
1926
	/**
1927
	 * Set navigation anchors to aid DetailView record navigation (VCR buttons)
1928
	 * @param string uri The URI from the referring page (always ListView)
1929
	 * @return array start Array of the URI broken down with a special "current_view" for My Inbox Navs
1930
	 */
1931 1
	function getStartPage($uri) {
1932 1
		if(strpos($uri, '&')) { // "&" to ensure that we can explode the GET vars - else we're gonna trigger a Notice error
1933 1
			$serial = substr($uri, (strpos($uri, '?')+1), strlen($uri));
1934 1
			$exUri = explode('&', $serial);
1935 1
			$start = array('module' => '', 'action' => '', 'group' => '', 'record' => '', 'type' => '');
1936
1937 1
			foreach($exUri as $k => $pair) {
1938 1
				$exPair = explode('=', $pair);
1939 1
				$start[$exPair[0]] = $exPair[1];
1940
			}
1941
1942
			// specific views for current_user
1943 1
			if(isset($start['assigned_user_id'])) {
1944 1
				$start['current_view'] = "{$start['action']}&module={$start['module']}&assigned_user_id={$start['assigned_user_id']}&type={$start['type']}";
1945
			}
1946
1947 1
			return $start;
1948
		} else {
1949
			return array();
1950
		}
1951
	}
1952
1953
	/**
1954
	 * preps SMTP info for email transmission
1955
	 * @param object mail SugarPHPMailer object
1956
	 * @param string mailer_id
1957
	 * @param string ieId
1958
	 * @return object mail SugarPHPMailer object
1959
	 */
1960 2
	function setMailer($mail, $mailer_id='', $ieId='') {
1961 2
		global $current_user;
1962
1963 2
		require_once("include/OutboundEmail/OutboundEmail.php");
1964 2
		$oe = new OutboundEmail();
1965 2
		$oe = $oe->getInboundMailerSettings($current_user, $mailer_id, $ieId);
1966
1967
		// ssl or tcp - keeping outside isSMTP b/c a default may inadvertantly set ssl://
1968 2
		$mail->protocol = ($oe->mail_smtpssl) ? "ssl://" : "tcp://";
1969 2
        if($oe->mail_sendtype == "SMTP")
1970
        {
1971
    		//Set mail send type information
1972 2
    		$mail->Mailer = "smtp";
1973 2
    		$mail->Host = $oe->mail_smtpserver;
1974 2
    		$mail->Port = $oe->mail_smtpport;
1975 2
            if ($oe->mail_smtpssl == 1) {
1976
                $mail->SMTPSecure = 'ssl';
1977
            } // if
1978 2
            if ($oe->mail_smtpssl == 2) {
1979
                $mail->SMTPSecure = 'tls';
1980
            } // if
1981
1982 2
    		if($oe->mail_smtpauth_req) {
1983 2
    			$mail->SMTPAuth = TRUE;
1984 2
    			$mail->Username = $oe->mail_smtpuser;
1985 2
    			$mail->Password = $oe->mail_smtppass;
1986
    		}
1987
        }
1988
        else
1989
			$mail->Mailer = "sendmail";
1990
1991 2
		$mail->oe = $oe;
1992 2
		return $mail;
1993
	}
1994
1995
	/**
1996
	 * preps SugarPHPMailer object for HTML or Plain text sends
1997
	 * @param SugarPHPMailer $mail SugarPHPMailer instance
1998
	 */
1999 2
	function handleBody($mail) {
2000 2
		global $current_user;
2001 2
		global $sugar_config;
2002
		///////////////////////////////////////////////////////////////////////
2003
		////	HANDLE EMAIL FORMAT PREFERENCE
2004
		// the if() below is HIGHLY dependent on the Javascript unchecking the Send HTML Email box
2005
		// HTML email
2006 2
		if( (isset($_REQUEST['setEditor']) /* from Email EditView navigation */
2007 2
			&& $_REQUEST['setEditor'] == 1
2008 1
			&& trim($_REQUEST['description_html']) != '')
2009 2
			|| trim($this->description_html) != '' /* from email templates */
2010 2
            && $current_user->getPreference('email_editor_option', 'global') !== 'plain' //user preference is not set to plain text
2011
		) {
2012 1
		    $this->handleBodyInHTMLformat($mail);
2013
		} else {
2014
			// plain text only
2015 2
			$this->description_html = '';
2016 2
			$mail->IsHTML(false);
2017 2
			$plainText = from_html($this->description);
2018 2
			$plainText = str_replace("&nbsp;", " ", $plainText);
2019 2
			$plainText = str_replace("</p>", "</p><br />", $plainText);
2020 2
			$plainText = strip_tags(br2nl($plainText));
2021 2
			$plainText = str_replace("&amp;", "&", $plainText);
2022 2
            $plainText = str_replace("&#39;", "'", $plainText);
2023 2
			$mail->Body = wordwrap($plainText, 996);
2024 2
			$mail->Body = $this->decodeDuringSend($mail->Body);
2025 2
			$this->description = $mail->Body;
2026
		}
2027
2028
		// wp: if plain text version has lines greater than 998, use base64 encoding
2029 2
		foreach(explode("\n", ($mail->ContentType == "text/html") ? $mail->AltBody : $mail->Body) as $line) {
2030 2
			if(strlen($line) > 998) {
2031
				$mail->Encoding = 'base64';
2032 2
				break;
2033
			}
2034
		}
2035
		////	HANDLE EMAIL FORMAT PREFERENCE
2036
		///////////////////////////////////////////////////////////////////////
2037
2038 2
		return $mail;
2039
	}
2040
2041
	/**
2042
	 * Retrieve function from handlebody() to unit test easily
2043
	 * @param SugarPHPMailer $mail SugarPHPMailer instance
2044
	 * @return formatted $mail body
2045
	 */
2046 2
	function handleBodyInHTMLformat($mail) {
2047 2
		global $sugar_config;
2048
		// wp: if body is html, then insert new lines at 996 characters. no effect on client side
2049
		// due to RFC 2822 which limits email lines to 998
2050 2
		$mail->IsHTML(true);
2051 2
		$body = from_html(wordwrap($this->description_html, 996));
2052 2
		$mail->Body = $body;
2053
2054
		// cn: bug 9725
2055
		// new plan is to use the selected type (html or plain) to fill the other
2056 2
		$plainText = from_html($this->description_html);
2057 2
		$plainText = strip_tags(br2nl($plainText));
2058 2
		$mail->AltBody = $plainText;
2059 2
		$this->description = $plainText;
2060
2061 2
		$mail->replaceImageByRegex("(?:{$sugar_config['site_url']})?/?cache/images/", sugar_cached("images/"));
2062
2063
		//Replace any embeded images using the secure entryPoint for src url.
2064 2
		$mail->replaceImageByRegex("(?:{$sugar_config['site_url']})?/?index.php[?]entryPoint=download&(?:amp;)?[^\"]+?id=", "upload://", true);
2065
2066 2
		$mail->Body = from_html($mail->Body);
2067 2
	}
2068
2069
	/**
2070
	 * Sends Email
2071
	 * @return bool True on success
2072
	 */
2073 1
	function send() {
2074 1
		global $mod_strings,$app_strings;
2075 1
		global $current_user;
2076 1
		global $sugar_config;
2077 1
		global $locale;
2078 1
        $OBCharset = $locale->getPrecedentPreference('default_email_charset');
2079 1
		$mail = new SugarPHPMailer();
2080
2081 1
		foreach ($this->to_addrs_arr as $addr_arr) {
2082 1
			if ( empty($addr_arr['display'])) {
2083 1
				$mail->AddAddress($addr_arr['email'], "");
2084
			} else {
2085 1
				$mail->AddAddress($addr_arr['email'],$locale->translateCharsetMIME(trim( $addr_arr['display']), 'UTF-8', $OBCharset));
2086
			}
2087
		}
2088 1
		foreach ($this->cc_addrs_arr as $addr_arr) {
2089
			if ( empty($addr_arr['display'])) {
2090
				$mail->AddCC($addr_arr['email'], "");
2091
			} else {
2092
				$mail->AddCC($addr_arr['email'],$locale->translateCharsetMIME(trim($addr_arr['display']), 'UTF-8', $OBCharset));
2093
			}
2094
		}
2095
2096 1
		foreach ($this->bcc_addrs_arr as $addr_arr) {
2097
			if ( empty($addr_arr['display'])) {
2098
				$mail->AddBCC($addr_arr['email'], "");
2099
			} else {
2100
				$mail->AddBCC($addr_arr['email'],$locale->translateCharsetMIME(trim($addr_arr['display']), 'UTF-8', $OBCharset));
2101
			}
2102
		}
2103
2104 1
		$mail = $this->setMailer($mail);
2105
2106
		// FROM ADDRESS
2107 1
		if(!empty($this->from_addr)) {
2108
			$mail->From = $this->from_addr;
2109
		} else {
2110 1
			$mail->From = $current_user->getPreference('mail_fromaddress');
2111 1
			$this->from_addr = $mail->From;
2112
		}
2113
		// FROM NAME
2114 1
		if(!empty($this->from_name)) {
2115
			$mail->FromName = $this->from_name;
2116
		} else {
2117 1
			$mail->FromName =  $current_user->getPreference('mail_fromname');
2118 1
			$this->from_name = $mail->FromName;
2119
		}
2120
2121
		//Reply to information for case create and autoreply.
2122 1
		if(!empty($this->reply_to_name)) {
2123
			$ReplyToName = $this->reply_to_name;
2124
		} else {
2125 1
			$ReplyToName = $mail->FromName;
2126
		}
2127 1
		if(!empty($this->reply_to_addr)) {
2128
			$ReplyToAddr = $this->reply_to_addr;
2129
		} else {
2130 1
			$ReplyToAddr = $mail->From;
2131
		}
2132 1
		$mail->Sender = $mail->From; /* set Return-Path field in header to reduce spam score in emails sent via Sugar's Email module */
2133 1
		$mail->AddReplyTo($ReplyToAddr,$locale->translateCharsetMIME(trim($ReplyToName), 'UTF-8', $OBCharset));
2134
2135
		//$mail->Subject = html_entity_decode($this->name, ENT_QUOTES, 'UTF-8');
2136 1
		$mail->Subject = $this->name;
2137
2138
		///////////////////////////////////////////////////////////////////////
2139
		////	ATTACHMENTS
2140 1
		foreach($this->saved_attachments as $note) {
2141
			$mime_type = 'text/plain';
2142
			if($note->object_name == 'Note') {
2143
				if(!empty($note->file->temp_file_location) && is_file($note->file->temp_file_location)) { // brandy-new file upload/attachment
2144
					$file_location = "upload://$note->id";
2145
					$filename = $note->file->original_file_name;
2146
					$mime_type = $note->file->mime_type;
2147
				} else { // attachment coming from template/forward
2148
					$file_location = "upload://{$note->id}";
2149
					// cn: bug 9723 - documents from EmailTemplates sent with Doc Name, not file name.
2150
					$filename = !empty($note->filename) ? $note->filename : $note->name;
2151
					$mime_type = $note->file_mime_type;
2152
				}
2153
			} elseif($note->object_name == 'DocumentRevision') { // from Documents
2154
				$filePathName = $note->id;
2155
				// cn: bug 9723 - Emails with documents send GUID instead of Doc name
2156
				$filename = $note->getDocumentRevisionNameForDisplay();
2157
				$file_location = "upload://$note->id";
2158
				$mime_type = $note->file_mime_type;
2159
			}
2160
2161
			// strip out the "Email attachment label if exists
2162
			$filename = str_replace($mod_strings['LBL_EMAIL_ATTACHMENT'].': ', '', $filename);
2163
            $file_ext = pathinfo($filename, PATHINFO_EXTENSION);
2164
			//is attachment in our list of bad files extensions?  If so, append .txt to file location
2165
			//check to see if this is a file with extension located in "badext"
2166
			foreach($sugar_config['upload_badext'] as $badExt) {
2167
		       	if(strtolower($file_ext) == strtolower($badExt)) {
2168
			       	//if found, then append with .txt to filename and break out of lookup
2169
			       	//this will make sure that the file goes out with right extension, but is stored
2170
			       	//as a text in db.
2171
			        $file_location = $file_location . ".txt";
2172
			        break; // no need to look for more
2173
		       	}
2174
	        }
2175
			$mail->AddAttachment($file_location,$locale->translateCharsetMIME(trim($filename), 'UTF-8', $OBCharset), 'base64', $mime_type);
2176
2177
			// embedded Images
2178
			if($note->embed_flag == true) {
2179
				$cid = $filename;
2180
				$mail->AddEmbeddedImage($file_location, $cid, $filename, 'base64',$mime_type);
2181
			}
2182
		}
2183
		////	END ATTACHMENTS
2184
		///////////////////////////////////////////////////////////////////////
2185
2186 1
		$mail = $this->handleBody($mail);
2187
2188 1
		$GLOBALS['log']->debug('Email sending --------------------- ');
2189
2190
		///////////////////////////////////////////////////////////////////////
2191
		////	I18N TRANSLATION
2192 1
		$mail->prepForOutbound();
2193
		////	END I18N TRANSLATION
2194
		///////////////////////////////////////////////////////////////////////
2195
2196 1
		if($mail->Send()) {
2197
			///////////////////////////////////////////////////////////////////
2198
			////	INBOUND EMAIL HANDLING
2199
			// mark replied
2200
			if(!empty($_REQUEST['inbound_email_id'])) {
2201
				$ieMail = new Email();
2202
				$ieMail->retrieve($_REQUEST['inbound_email_id']);
2203
				$ieMail->status = 'replied';
2204
				$ieMail->save();
2205
			}
2206
			$GLOBALS['log']->debug(' --------------------- buh bye -- sent successful');
2207
			////	END INBOUND EMAIL HANDLING
2208
			///////////////////////////////////////////////////////////////////
2209
  			return true;
2210
		}
2211 1
	    $GLOBALS['log']->debug($app_strings['LBL_EMAIL_ERROR_PREPEND'].$mail->ErrorInfo);
2212 1
		return false;
2213
	}
2214
2215
2216 1
	function listviewACLHelper(){
2217 1
		$array_assign = parent::listviewACLHelper();
2218 1
		$is_owner = false;
2219 1
		$in_group = false; //SECURITY GROUPS
2220 1
		if(!empty($this->parent_name)){
2221
2222
			if(!empty($this->parent_name_owner)){
2223
				global $current_user;
2224
				$is_owner = $current_user->id == $this->parent_name_owner;
2225
			}
2226
			/* BEGIN - SECURITY GROUPS */
2227
			//parent_name_owner not being set for whatever reason so we need to figure this out
2228
			else if(!empty($this->parent_type) && !empty($this->parent_id)) {
2229
				global $current_user;
2230
                $parent_bean = BeanFactory::getBean($this->parent_type,$this->parent_id);
2231
                if($parent_bean !== false) {
2232
                	$is_owner = $current_user->id == $parent_bean->assigned_user_id;
2233
                }
2234
			}
2235
			require_once("modules/SecurityGroups/SecurityGroup.php");
2236
			$in_group = SecurityGroup::groupHasAccess($this->parent_type, $this->parent_id, 'view');
2237
        	/* END - SECURITY GROUPS */
2238
		}
2239
		/* BEGIN - SECURITY GROUPS */
2240
		/**
2241
		if(!ACLController::moduleSupportsACL($this->parent_type) || ACLController::checkAccess($this->parent_type, 'view', $is_owner)){
2242
		*/
2243 1
		if(!ACLController::moduleSupportsACL($this->parent_type) || ACLController::checkAccess($this->parent_type, 'view', $is_owner, 'module', $in_group)){
2244
        /* END - SECURITY GROUPS */
2245 1
			$array_assign['PARENT'] = 'a';
2246
		} else {
2247
			$array_assign['PARENT'] = 'span';
2248
		}
2249 1
		$is_owner = false;
2250 1
		$in_group = false; //SECURITY GROUPS
2251 1
		if(!empty($this->contact_name)) {
2252
			if(!empty($this->contact_name_owner)) {
2253
				global $current_user;
2254
				$is_owner = $current_user->id == $this->contact_name_owner;
2255
			}
2256
			/* BEGIN - SECURITY GROUPS */
2257
			//contact_name_owner not being set for whatever reason so we need to figure this out
2258
			else {
2259
				global $current_user;
2260
                $parent_bean = BeanFactory::getBean('Contacts',$this->contact_id);
2261
                if($parent_bean !== false) {
2262
                	$is_owner = $current_user->id == $parent_bean->assigned_user_id;
2263
                }
2264
			}
2265
			require_once("modules/SecurityGroups/SecurityGroup.php");
2266
			$in_group = SecurityGroup::groupHasAccess('Contacts', $this->contact_id, 'view');
2267
        	/* END - SECURITY GROUPS */
2268
		}
2269
		/* BEGIN - SECURITY GROUPS */
2270
		/**
2271
		if(ACLController::checkAccess('Contacts', 'view', $is_owner)) {
2272
		*/
2273 1
		if(ACLController::checkAccess('Contacts', 'view', $is_owner, 'module', $in_group)) {
2274
        /* END - SECURITY GROUPS */
2275 1
			$array_assign['CONTACT'] = 'a';
2276
		} else {
2277
			$array_assign['CONTACT'] = 'span';
2278
		}
2279
2280 1
		return $array_assign;
2281
	}
2282
2283 2
	function getSystemDefaultEmail() {
2284 2
		$email = array();
2285
2286 2
		$r1 = $this->db->query('SELECT config.value FROM config WHERE name=\'fromaddress\'');
2287 2
		$r2 = $this->db->query('SELECT config.value FROM config WHERE name=\'fromname\'');
2288 2
		$a1 = $this->db->fetchByAssoc($r1);
2289 2
		$a2 = $this->db->fetchByAssoc($r2);
2290
2291 2
		$email['email'] = $a1['value'];
2292 2
		$email['name']  = $a2['value'];
2293
2294 2
		return $email;
2295
	}
2296
2297
2298 1
    function create_new_list_query($order_by, $where,$filter=array(),$params=array(), $show_deleted = 0,$join_type='', $return_array = false,$parentbean=null, $singleSelect = false, $ifListForExport = false) {
2299
2300 1
		if ($return_array) {
2301
			return parent::create_new_list_query($order_by, $where,$filter,$params, $show_deleted,$join_type, $return_array,$parentbean, $singleSelect);
2302
		}
2303 1
        $custom_join = $this->getCustomJoin();
2304
2305 1
		$query = "SELECT ".$this->table_name.".*, users.user_name as assigned_user_name\n";
2306
2307 1
        $query .= $custom_join['select'];
2308 1
    	$query .= " FROM emails\n";
2309 1
    	if ($where != "" && (strpos($where, "contacts.first_name") > 0))  {
2310
			$query .= " LEFT JOIN emails_beans ON emails.id = emails_beans.email_id\n";
2311
    	}
2312
2313 1
    	$query .= " LEFT JOIN users ON emails.assigned_user_id=users.id \n";
2314 1
    	if ($where != "" && (strpos($where, "contacts.first_name") > 0))  {
2315
2316
        $query .= " JOIN contacts ON contacts.id= emails_beans.bean_id AND emails_beans.bean_module='Contacts' and contacts.deleted=0 \n";
2317
    	}
2318
2319 1
        $query .= $custom_join['join'];
2320
2321 1
		if($show_deleted == 0) {
2322 1
			$where_auto = " emails.deleted=0 \n";
2323
		}else if($show_deleted == 1){
2324
			$where_auto = " emails.deleted=1 \n";
2325
		}
2326
2327 1
        if($where != "")
2328 1
			$query .= "WHERE $where AND ".$where_auto;
2329
		else
2330 1
			$query .= "WHERE ".$where_auto;
2331
2332 1
		if($order_by != "")
2333 1
			$query .= " ORDER BY $order_by";
2334
		else
2335 1
			$query .= " ORDER BY date_sent DESC";
2336
2337 1
		return $query;
2338
    } // fn
2339
2340
2341 2
	function fill_in_additional_list_fields() {
2342 2
		global $timedate, $mod_strings;
2343 2
		$this->fill_in_additional_detail_fields();
2344
2345 2
		$this->link_action = 'DetailView';
2346
		///////////////////////////////////////////////////////////////////////
2347
		//populate attachment_image, used to display attachment icon.
2348 2
		$query =  "select 1 from notes where notes.parent_id = '$this->id' and notes.deleted = 0";
2349 2
		$result =$this->db->query($query,true," Error filling in additional list fields: ");
2350
2351 2
		$row = $this->db->fetchByAssoc($result);
2352
2353 2
        if ($row) {
2354
            $this->attachment_image = SugarThemeRegistry::current()->getImage(
2355
                'attachment',
2356
                '',
2357
                null,
2358
                null,
2359
                '.gif',
2360
                translate('LBL_ATTACHMENT', 'Emails')
2361
            );
2362
        } else {
2363 2
            $this->attachment_image = '';
2364
        }
2365
2366
		///////////////////////////////////////////////////////////////////////
2367 2
		if(empty($this->contact_id) && !empty($this->parent_id) && !empty($this->parent_type) && $this->parent_type === 'Contacts' && !empty($this->parent_name) ){
2368
			$this->contact_id = $this->parent_id;
2369
			$this->contact_name = $this->parent_name;
2370
		}
2371 2
	}
2372
2373 3
	function fill_in_additional_detail_fields() {
2374 3
		global $app_list_strings,$mod_strings;
2375
		// Fill in the assigned_user_name
2376 3
		$this->assigned_user_name = get_assigned_user_name($this->assigned_user_id, '');
2377
		//if ($this->parent_type == 'Contacts') {
2378 3
			$query  = "SELECT contacts.first_name, contacts.last_name, contacts.phone_work, contacts.id, contacts.assigned_user_id contact_name_owner, 'Contacts' contact_name_mod FROM contacts, emails_beans ";
2379 3
			$query .= "WHERE emails_beans.email_id='$this->id' AND emails_beans.bean_id=contacts.id AND emails_beans.bean_module = 'Contacts' AND emails_beans.deleted=0 AND contacts.deleted=0";
2380 3
			if(!empty($this->parent_id)){
2381 1
				$query .= " AND contacts.id= '".$this->parent_id."' ";
2382 2
			}else if(!empty($_REQUEST['record'])){
2383
				$query .= " AND contacts.id= '".$_REQUEST['record']."' ";
2384
			}
2385 3
			$result =$this->db->query($query,true," Error filling in additional detail fields: ");
2386
2387
			// Get the id and the name.
2388 3
			$row = $this->db->fetchByAssoc($result);
2389 3
			if($row != null)
2390
			{
2391
2392
				$contact = new Contact();
2393
				$contact->retrieve($row['id']);
2394
				$this->contact_name = $contact->full_name;
2395
				$this->contact_phone = $row['phone_work'];
2396
				$this->contact_id = $row['id'];
2397
				$this->contact_email = $contact->emailAddress->getPrimaryAddress($contact);
2398
				$this->contact_name_owner = $row['contact_name_owner'];
2399
				$this->contact_name_mod = $row['contact_name_mod'];
2400
				$GLOBALS['log']->debug("Call($this->id): contact_name = $this->contact_name");
2401
				$GLOBALS['log']->debug("Call($this->id): contact_phone = $this->contact_phone");
2402
				$GLOBALS['log']->debug("Call($this->id): contact_id = $this->contact_id");
2403
				$GLOBALS['log']->debug("Call($this->id): contact_email1 = $this->contact_email");
2404
			}
2405
			else {
2406 3
				$this->contact_name = '';
2407 3
				$this->contact_phone = '';
2408 3
				$this->contact_id = '';
2409 3
				$this->contact_email = '';
2410 3
				$this->contact_name_owner = '';
2411 3
				$this->contact_name_mod = '';
2412 3
				$GLOBALS['log']->debug("Call($this->id): contact_name = $this->contact_name");
2413 3
				$GLOBALS['log']->debug("Call($this->id): contact_phone = $this->contact_phone");
2414 3
				$GLOBALS['log']->debug("Call($this->id): contact_id = $this->contact_id");
2415 3
				$GLOBALS['log']->debug("Call($this->id): contact_email1 = $this->contact_email");
2416
			}
2417
		//}
2418 3
		$this->created_by_name = get_assigned_user_name($this->created_by);
2419 3
		$this->modified_by_name = get_assigned_user_name($this->modified_user_id);
2420
2421 3
		$this->link_action = 'DetailView';
2422
2423 3
		if(!empty($this->type)) {
2424 3
			if($this->type == 'out' && $this->status == 'send_error') {
2425 1
				$this->type_name = $mod_strings['LBL_NOT_SENT'];
2426
			} else {
2427 2
				$this->type_name = $app_list_strings['dom_email_types'][$this->type];
2428
			}
2429
2430 3
			if(($this->type == 'out' && $this->status == 'send_error') || $this->type == 'draft') {
2431 1
				$this->link_action = 'EditView';
2432
			}
2433
		}
2434
2435
		//todo this  isset( $app_list_strings['dom_email_status'][$this->status]) is hack for 3261.
2436 3
		if(!empty($this->status) && isset( $app_list_strings['dom_email_status'][$this->status])) {
2437 1
			$this->status_name = $app_list_strings['dom_email_status'][$this->status];
2438
		}
2439
2440 3
		if ( empty($this->name ) &&  empty($_REQUEST['record'])) {
2441 3
			$this->name = $mod_strings['LBL_NO_SUBJECT'];
2442
		}
2443
2444 3
		$this->fill_in_additional_parent_fields();
2445 3
	}
2446
2447
2448
2449 1
	function create_export_query($order_by, $where)
2450
    {
2451 1
		$contact_required = stristr($where, "contacts");
2452 1
		$custom_join = $this->getCustomJoin(true, true, $where);
2453
2454 1
		if($contact_required) {
2455
			$query = "SELECT emails.*, contacts.first_name, contacts.last_name";
2456
            $query .= $custom_join['select'];
2457
2458
			$query .= " FROM contacts, emails, emails_contacts ";
2459
			$where_auto = "emails_contacts.contact_id = contacts.id AND emails_contacts.email_id = emails.id AND emails.deleted=0 AND contacts.deleted=0";
2460
		} else {
2461 1
			$query = 'SELECT emails.*';
2462 1
            $query .= $custom_join['select'];
2463
2464 1
            $query .= ' FROM emails ';
2465 1
            $where_auto = "emails.deleted=0";
2466
		}
2467
2468 1
        $query .= $custom_join['join'];
2469
2470 1
		if($where != "")
2471 1
			$query .= "where $where AND ".$where_auto;
2472
        else
2473 1
			$query .= "where ".$where_auto;
2474
2475 1
        if($order_by != "")
2476 1
			$query .= " ORDER BY $order_by";
2477
        else
2478 1
			$query .= " ORDER BY emails.name";
2479 1
        return $query;
2480
    }
2481
2482 1
	function get_list_view_data() {
2483 1
		global $app_list_strings;
2484 1
		global $theme;
2485 1
		global $current_user;
2486 1
		global $timedate;
2487 1
		global $mod_strings;
2488
2489 1
		$email_fields = $this->get_list_view_array();
2490 1
		$this->retrieveEmailText();
2491 1
		$email_fields['FROM_ADDR'] = $this->from_addr_name;
2492 1
		$mod_strings = return_module_language($GLOBALS['current_language'], 'Emails'); // hard-coding for Home screen ListView
2493
2494 1
		if($this->status != 'replied') {
2495 1
			$email_fields['QUICK_REPLY'] = '<a  href="index.php?module=Emails&action=Compose&replyForward=true&reply=reply&record='.$this->id.'&inbound_email_id='.$this->id.'">'.$mod_strings['LNK_QUICK_REPLY'].'</a>';
2496 1
			$email_fields['STATUS'] = ($email_fields['REPLY_TO_STATUS'] == 1 ? $mod_strings['LBL_REPLIED'] : $email_fields['STATUS']);
2497
		} else {
2498
			$email_fields['QUICK_REPLY'] = $mod_strings['LBL_REPLIED'];
2499
		}
2500 1
		if(!empty($this->parent_type)) {
2501
			$email_fields['PARENT_MODULE'] = $this->parent_type;
2502
		} else {
2503 1
			switch($this->intent) {
2504 1
				case 'support':
2505 1
					$email_fields['CREATE_RELATED'] = '<a href="index.php?module=Cases&action=EditView&inbound_email_id='.$this->id.'" >' . SugarThemeRegistry::current()->getImage('CreateCases', 'border="0"', null, null, ".gif", $mod_strings['LBL_CREATE_CASES']).$mod_strings['LBL_CREATE_CASE'].'</a>';
2506 1
				break;
2507
2508
				case 'sales':
2509
					$email_fields['CREATE_RELATED'] = '<a href="index.php?module=Leads&action=EditView&inbound_email_id='.$this->id.'" >'.SugarThemeRegistry::current()->getImage('CreateLeads', 'border="0"', null, null, ".gif", $mod_strings['LBL_CREATE_LEADS']).$mod_strings['LBL_CREATE_LEAD'].'</a>';
2510
				break;
2511
2512
				case 'contact':
2513
					$email_fields['CREATE_RELATED'] = '<a href="index.php?module=Contacts&action=EditView&inbound_email_id='.$this->id.'" >'.SugarThemeRegistry::current()->getImage('CreateContacts', 'border="0"', null, null, ".gif", $mod_strings['LBL_CREATE_CONTACTS']).$mod_strings['LBL_CREATE_CONTACT'].'</a>';
2514
				break;
2515
2516
				case 'bug':
2517
					$email_fields['CREATE_RELATED'] = '<a href="index.php?module=Bugs&action=EditView&inbound_email_id='.$this->id.'" >'.SugarThemeRegistry::current()->getImage('CreateBugs', 'border="0"', null, null, ".gif", $mod_strings['LBL_CREATE_BUGS']).$mod_strings['LBL_CREATE_BUG'].'</a>';
2518
				break;
2519
2520
				case 'task':
2521
					$email_fields['CREATE_RELATED'] = '<a href="index.php?module=Tasks&action=EditView&inbound_email_id='.$this->id.'" >'.SugarThemeRegistry::current()->getImage('CreateTasks', 'border="0"', null, null, ".gif", $mod_strings['LBL_CREATE_TASKS']).$mod_strings['LBL_CREATE_TASK'].'</a>';
2522
				break;
2523
2524
				case 'bounce':
2525
				break;
2526
2527
				case 'pick':
2528
				// break;
2529
2530
				case 'info':
2531
				//break;
2532
2533
				default:
2534
					$email_fields['CREATE_RELATED'] = $this->quickCreateForm();
2535
				break;
2536
			}
2537
2538
		}
2539
2540
		//BUG 17098 - MFH changed $this->from_addr to $this->to_addrs
2541 1
		$email_fields['CONTACT_NAME']		= empty($this->contact_name) ? '</a>'.$this->trimLongTo($this->to_addrs).'<a>' : $this->contact_name;
2542 1
		$email_fields['CONTACT_ID']		= empty($this->contact_id) ? '' : $this->contact_id;
2543 1
		$email_fields['ATTACHMENT_IMAGE']	= $this->attachment_image;
2544 1
		$email_fields['LINK_ACTION']		= $this->link_action;
2545
2546 1
    	if(isset($this->type_name))
2547 1
	      	$email_fields['TYPE_NAME'] = $this->type_name;
2548
2549 1
		return $email_fields;
2550
	}
2551
2552 1
    function quickCreateForm() {
2553 1
        global $mod_strings, $app_strings, $currentModule, $current_language;
2554
2555
        // Coming from the home page via Dashlets
2556 1
        if($currentModule != 'Email')
2557 1
        	$mod_strings = return_module_language($current_language, 'Emails');
2558 1
        return $mod_strings['LBL_QUICK_CREATE']."&nbsp;<a id='$this->id' onclick='return quick_create_overlib(\"{$this->id}\", \"".SugarThemeRegistry::current()->__toString()."\", this);' href=\"#\" >".SugarThemeRegistry::current()->getImage("advanced_search","border='0' align='absmiddle'", null,null,'.gif',$mod_strings['LBL_QUICK_CREATE'])."</a>";
2559
    }
2560
2561
    /**
2562
     * Searches all imported emails and returns the result set as an array.
2563
     *
2564
     */
2565 1
    function searchImportedEmails($sort = '', $direction='')
2566
    {
2567 1
       	require_once('include/TimeDate.php');
2568 1
		global $timedate;
2569 1
		global $current_user;
2570 1
		global $beanList;
2571 1
		global $sugar_config;
2572 1
		global $app_strings;
2573
2574 1
		$emailSettings = $current_user->getPreference('emailSettings', 'Emails');
2575
		// cn: default to a low number until user specifies otherwise
2576 1
		if(empty($emailSettings['showNumInList']))
2577 1
			$pageSize = 20;
2578
        else
2579
            $pageSize = $emailSettings['showNumInList'];
2580
2581 1
        if( isset($_REQUEST['start']) && isset($_REQUEST['limit']) )
2582
	       $page = ceil($_REQUEST['start'] / $_REQUEST['limit']) + 1;
2583
	    else
2584 1
	       $page = 1;
2585
2586
	     //Determine sort ordering
2587
2588
	     //Sort ordering parameters in the request do not coincide with actual column names
2589
	     //so we need to remap them.
2590
	     $hrSortLocal = array(
2591 1
            'flagged' => 'type',
2592
            'status'  => 'reply_to_status',
2593
            'from'    => 'emails_text.from_addr',
2594
            'subject' => 'name',
2595
            'date'    => 'date_sent',
2596
            'AssignedTo' => 'assigned_user_id',
2597
            'flagged' => 'flagged'
2598
        );
2599
2600 1
	     $sort = !empty($_REQUEST['sort']) ? $this->db->getValidDBName($_REQUEST['sort']) : "";
2601 1
         $direction = !empty($_REQUEST['dir'])  && in_array(strtolower($_REQUEST['dir']), array("asc", "desc")) ? $_REQUEST['dir'] : "";
2602
2603 1
         $order = ( !empty($sort) && !empty($direction) ) ? " ORDER BY {$hrSortLocal[$sort]} {$direction}" : "";
2604
2605
         //Get our main query.
2606 1
		$fullQuery = $this->_genereateSearchImportedEmailsQuery();
2607
2608
		//Perform a count query needed for pagination.
2609 1
		$countQuery = $this->create_list_count_query($fullQuery);
2610
2611 1
		$count_rs = $this->db->query($countQuery, false, 'Error executing count query for imported emails search');
2612 1
		$count_row = $this->db->fetchByAssoc($count_rs);
2613 1
		$total_count = ($count_row != null) ? $count_row['c'] : 0;
2614
2615 1
        $start = ($page - 1) * $pageSize;
2616
2617
        //Execute the query
2618 1
		$rs = $this->db->limitQuery($fullQuery . $order, $start, $pageSize);
2619
2620 1
		$return = array();
2621
2622 1
		while($a = $this->db->fetchByAssoc($rs)) {
2623
			$temp = array();
2624
			$temp['flagged'] = (is_null($a['flagged']) || $a['flagged'] == '0') ? '' : 1;
2625
			$temp['status'] = (is_null($a['reply_to_status']) || $a['reply_to_status'] == '0') ? '' : 1;
2626
			$temp['subject'] = $a['name'];
2627
			$temp['date']	= $timedate->to_display_date_time($a['date_sent']);
2628
			$temp['uid'] = $a['id'];
2629
			$temp['ieId'] = $a['mailbox_id'];
2630
			$temp['site_url'] = $sugar_config['site_url'];
2631
			$temp['seen'] = ($a['status'] == 'unread') ? 0 : 1;
2632
			$temp['type'] = $a['type'];
2633
			$temp['mbox'] = 'sugar::Emails';
2634
			$temp['hasAttach'] =  $this->doesImportedEmailHaveAttachment($a['id']);
2635
			//To and from addresses may be stored in emails_text, if nothing is found, revert to
2636
			//regular email addresses.
2637
			$temp['to_addrs'] = preg_replace('/[\x00-\x08\x0B-\x1F]/', '', $a['to_addrs']);
2638
			$temp['from']	= preg_replace('/[\x00-\x08\x0B-\x1F]/', '', $a['from_addr']);
2639
			if( empty($temp['from']) || empty($temp['to_addrs']) )
2640
			{
2641
    			//Retrieve email addresses seperatly.
2642
    			$tmpEmail = new Email();
2643
    			$tmpEmail->id = $a['id'];
2644
    			$tmpEmail->retrieveEmailAddresses();
2645
    			$temp['from'] = $tmpEmail->from_addr;
2646
    			$temp['to_addrs'] = $tmpEmail->to_addrs;
2647
			}
2648
2649
			$return[] = $temp;
2650
		}
2651
2652 1
		$metadata = array();
2653 1
		$metadata['totalCount'] = $total_count;
2654 1
		$metadata['out'] = $return;
2655
2656 1
		return $metadata;
2657
    }
2658
2659
    /**
2660
     * Determine if an imported email has an attachment by examining the relationship to notes.
2661
     *
2662
     * @param string $id
2663
     * @return boolean
2664
     */
2665 1
    function doesImportedEmailHaveAttachment($id)
2666
	{
2667 1
	   $hasAttachment = FALSE;
2668 1
	   $query = "SELECT id FROM notes where parent_id='$id' AND parent_type='Emails' AND file_mime_type is not null AND deleted=0";
2669 1
	   $rs = $this->db->limitQuery($query, 0, 1);
2670 1
	   $row = $this->db->fetchByAssoc($rs);
2671 1
	   if( !empty($row['id']) )
2672 1
	       $hasAttachment = TRUE;
2673
2674 1
	   return (int) $hasAttachment;
2675
	}
2676
2677
    /**
2678
     * Generate the query used for searching imported emails.
2679
     *
2680
     * @return String Query to be executed.
2681
     */
2682 2
    function _genereateSearchImportedEmailsQuery()
2683
    {
2684 2
		global $timedate;
2685
2686 2
        $additionalWhereClause = $this->_generateSearchImportWhereClause();
2687
2688 2
        $query = array();
2689 2
        $fullQuery = "";
2690 2
        $query['select'] = "emails.id , emails.mailbox_id, emails.name, emails.date_sent, emails.status, emails.type, emails.flagged, emails.reply_to_status,
2691
		                      emails_text.from_addr, emails_text.to_addrs  FROM emails ";
2692
2693 2
        $query['joins'] = " JOIN emails_text on emails.id = emails_text.email_id ";
2694
2695
        //Handle from and to addr joins
2696 2
        if( !empty($_REQUEST['from_addr']) )
2697
        {
2698
            $from_addr = $this->db->quote(strtolower($_REQUEST['from_addr']));
2699
            $query['joins'] .= "INNER JOIN emails_email_addr_rel er_from ON er_from.email_id = emails.id AND er_from.deleted = 0 INNER JOIN email_addresses ea_from ON ea_from.id = er_from.email_address_id
2700
                                AND er_from.address_type='from' AND emails_text.from_addr LIKE '%" . $from_addr . "%'";
2701
        }
2702
2703 2
        if( !empty($_REQUEST['to_addrs'])  )
2704
        {
2705
            $to_addrs = $this->db->quote(strtolower($_REQUEST['to_addrs']));
2706
            $query['joins'] .= "INNER JOIN emails_email_addr_rel er_to ON er_to.email_id = emails.id AND er_to.deleted = 0 INNER JOIN email_addresses ea_to ON ea_to.id = er_to.email_address_id
2707
                                    AND er_to.address_type='to' AND ea_to.email_address LIKE '%" . $to_addrs . "%'";
2708
        }
2709
2710 2
        $query['where'] = " WHERE (emails.type= 'inbound' OR emails.type='archived' OR emails.type='out') AND emails.deleted = 0 ";
2711 2
		if( !empty($additionalWhereClause) )
2712
    	    $query['where'] .= "AND $additionalWhereClause";
2713
2714
    	//If we are explicitly looking for attachments.  Do not use a distinct query as the to_addr is defined
2715
    	//as a text which equals clob in oracle and the distinct query can not be executed correctly.
2716 2
    	$addDistinctKeyword = "";
2717 2
        if( !empty($_REQUEST['attachmentsSearch']) &&  $_REQUEST['attachmentsSearch'] == 1) //1 indicates yes
2718
            $query['where'] .= " AND EXISTS ( SELECT id FROM notes n WHERE n.parent_id = emails.id AND n.deleted = 0 AND n.filename is not null )";
2719 2
        else if( !empty($_REQUEST['attachmentsSearch']) &&  $_REQUEST['attachmentsSearch'] == 2 )
2720
             $query['where'] .= " AND NOT EXISTS ( SELECT id FROM notes n WHERE n.parent_id = emails.id AND n.deleted = 0 AND n.filename is not null )";
2721
2722 2
        $fullQuery = "SELECT " . $query['select'] . " " . $query['joins'] . " " . $query['where'];
2723
2724 2
        return $fullQuery;
2725
    }
2726
        /**
2727
     * Generate the where clause for searching imported emails.
2728
     *
2729
     */
2730 3
    function _generateSearchImportWhereClause()
2731
    {
2732 3
        global $timedate;
2733
2734
        //The clear button was removed so if a user removes the asisgned user name, do not process the id.
2735 3
        if( empty($_REQUEST['assigned_user_name']) && !empty($_REQUEST['assigned_user_id'])  )
2736
            unset($_REQUEST['assigned_user_id']);
2737
2738 3
        $availableSearchParam = array('name' => array('table_name' =>'emails'),
2739
                                      'data_parent_id_search' => array('table_name' =>'emails','db_key' => 'parent_id','opp' => '='),
2740
                                      'assigned_user_id' => array('table_name' => 'emails', 'opp' => '=') );
2741
2742 3
		$additionalWhereClause = array();
2743 3
		foreach ($availableSearchParam as $key => $properties)
2744
		{
2745 3
		      if( !empty($_REQUEST[$key]) )
2746
		      {
2747
		          $db_key =  isset($properties['db_key']) ? $properties['db_key'] : $key;
2748
                  $searchValue = $this->db->quote($_REQUEST[$key]);
2749
2750
		          $opp = isset($properties['opp']) ? $properties['opp'] : 'like';
2751
		          if($opp == 'like')
2752
		              $searchValue = "%" . $searchValue . "%";
2753
2754 3
		          $additionalWhereClause[] = "{$properties['table_name']}.$db_key $opp '$searchValue' ";
2755
		      }
2756
        }
2757
2758
2759
2760 3
        $isDateFromSearchSet = !empty($_REQUEST['searchDateFrom']);
2761 3
        $isdateToSearchSet = !empty($_REQUEST['searchDateTo']);
2762 3
        $bothDateRangesSet = $isDateFromSearchSet & $isdateToSearchSet;
2763
2764
        //Hanlde date from and to separately
2765 3
        if($bothDateRangesSet)
2766
        {
2767 1
            $dbFormatDateFrom = $timedate->to_db_date($_REQUEST['searchDateFrom'], false);
2768 1
            $dbFormatDateFrom = db_convert("'" . $dbFormatDateFrom . "'",'datetime');
0 ignored issues
show
Deprecated Code introduced by
The function db_convert() has been deprecated with message: use DBManager::convert() instead.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
2769
2770 1
            $dbFormatDateTo = $timedate->to_db_date($_REQUEST['searchDateTo'], false);
2771 1
            $dbFormatDateTo = db_convert("'" . $dbFormatDateTo . "'",'datetime');
0 ignored issues
show
Deprecated Code introduced by
The function db_convert() has been deprecated with message: use DBManager::convert() instead.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
2772
2773 1
            $additionalWhereClause[] = "( emails.date_sent >= $dbFormatDateFrom AND
2774 1
                                          emails.date_sent <= $dbFormatDateTo )";
2775
        }
2776 3
        elseif ($isdateToSearchSet)
2777
        {
2778 1
            $dbFormatDateTo = $timedate->to_db_date($_REQUEST['searchDateTo'], false);
2779 1
            $dbFormatDateTo = db_convert("'" . $dbFormatDateTo . "'",'datetime');
0 ignored issues
show
Deprecated Code introduced by
The function db_convert() has been deprecated with message: use DBManager::convert() instead.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
2780 1
            $additionalWhereClause[] = "emails.date_sent <= $dbFormatDateTo ";
2781
        }
2782 3
        elseif ($isDateFromSearchSet)
2783
        {
2784 1
            $dbFormatDateFrom = $timedate->to_db_date($_REQUEST['searchDateFrom'], false);
2785 1
            $dbFormatDateFrom = db_convert("'" . $dbFormatDateFrom . "'",'datetime');
0 ignored issues
show
Deprecated Code introduced by
The function db_convert() has been deprecated with message: use DBManager::convert() instead.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
2786 1
            $additionalWhereClause[] = "emails.date_sent >= $dbFormatDateFrom ";
2787
        }
2788
2789 3
        $additionalWhereClause = implode(" AND ", $additionalWhereClause);
2790
2791 3
        return $additionalWhereClause;
2792
    }
2793
2794
2795
2796
	/**
2797
	 * takes a long TO: string of emails and returns the first appended by an
2798
	 * elipse
2799
	 */
2800 2
	function trimLongTo($str) {
2801 2
		if(strpos($str, ',')) {
2802 1
			$exStr = explode(',', $str);
2803 1
			return $exStr[0].'...';
2804 2
		} elseif(strpos($str, ';')) {
2805 1
			$exStr = explode(';', $str);
2806 1
			return $exStr[0].'...';
2807
		} else {
2808 2
			return $str;
2809
		}
2810
	}
2811
2812 4
	function get_summary_text() {
2813 4
		return $this->name;
2814
	}
2815
2816
2817
2818 1
	function distributionForm($where) {
2819 1
		global $app_list_strings;
2820 1
		global $app_strings;
2821 1
		global $mod_strings;
2822 1
		global $theme;
2823 1
		global $current_user;
2824
2825 1
		$distribution	= get_select_options_with_id($app_list_strings['dom_email_distribution'], '');
2826 1
		$_SESSION['distribute_where'] = $where;
2827
2828
2829 1
		$out = '<form name="Distribute" id="Distribute">';
2830 1
		$out .= get_form_header($mod_strings['LBL_DIST_TITLE'], '', false);
2831
		$out .=<<<eoq
2832
		<script>
2833
			enableQS(true);
2834 1
		</script>
2835
eoq;
2836
		$out .= '
2837
		<table cellpadding="0" cellspacing="0" width="100%" border="0">
2838
			<tr>
2839
				<td>
2840
					<script type="text/javascript">
2841
2842
2843
						function checkDeps(form) {
2844
							return;
2845
						}
2846
2847
						function mySubmit() {
2848
							var assform = document.getElementById("Distribute");
2849
							var select = document.getElementById("userSelect");
2850
							var assign1 = assform.r1.checked;
2851
							var assign2 = assform.r2.checked;
2852
							var dist = assform.dm.value;
2853
							var assign = false;
2854
							var users = false;
2855
							var rules = false;
2856 1
							var warn1 = "'.$mod_strings['LBL_WARN_NO_USERS'].'";
2857
							var warn2 = "";
2858
2859
							if(assign1 || assign2) {
2860
								assign = true;
2861
2862
							}
2863
2864
							for(i=0; i<select.options.length; i++) {
2865
								if(select.options[i].selected == true) {
2866
									users = true;
2867
									warn1 = "";
2868
								}
2869
							}
2870
2871
							if(dist != "") {
2872
								rules = true;
2873
							} else {
2874 1
								warn2 = "'.$mod_strings['LBL_WARN_NO_DIST'].'";
2875
							}
2876
2877
							if(assign && users && rules) {
2878
2879
								if(document.getElementById("r1").checked) {
2880
									var mu = document.getElementById("MassUpdate");
2881
									var grabbed = "";
2882
2883
									for(i=0; i<mu.elements.length; i++) {
2884
										if(mu.elements[i].type == "checkbox" && mu.elements[i].checked && mu.elements[i].name.value != "massall") {
2885
											if(grabbed != "") { grabbed += "::"; }
2886
											grabbed += mu.elements[i].value;
2887
										}
2888
									}
2889
									var formgrab = document.getElementById("grabbed");
2890
									formgrab.value = grabbed;
2891
								}
2892
								assform.submit();
2893
							} else {
2894 1
								alert("'.$mod_strings['LBL_ASSIGN_WARN'].'" + "\n" + warn1 + "\n" + warn2);
2895
							}
2896
						}
2897
2898
						function submitDelete() {
2899
							if(document.getElementById("r1").checked) {
2900
								var mu = document.getElementById("MassUpdate");
2901
								var grabbed = "";
2902
2903
								for(i=0; i<mu.elements.length; i++) {
2904
									if(mu.elements[i].type == "checkbox" && mu.elements[i].checked && mu.elements[i].name != "massall") {
2905
										if(grabbed != "") { grabbed += "::"; }
2906
										grabbed += mu.elements[i].value;
2907
									}
2908
								}
2909
								var formgrab = document.getElementById("grabbed");
2910
								formgrab.value = grabbed;
2911
							}
2912
							if(grabbed == "") {
2913 1
								alert("'.$mod_strings['LBL_MASS_DELETE_ERROR'].'");
2914
							} else {
2915
								document.getElementById("Distribute").submit();
2916
							}
2917
						}
2918
2919
					</script>
2920
						<input type="hidden" name="module" value="Emails">
2921
						<input type="hidden" name="action" id="action">
2922
						<input type="hidden" name="grabbed" id="grabbed">
2923
2924
					<table cellpadding="1" cellspacing="0" width="100%" border="0" class="edit view">
2925
						<tr height="20">
2926
							<td scope="col" scope="row" NOWRAP align="center">
2927 1
								&nbsp;'.$mod_strings['LBL_ASSIGN_SELECTED_RESULTS_TO'].'&nbsp;';
2928 1
					$out .= $this->userSelectTable();
2929
					$out .=	'</td>
2930
							<td scope="col" scope="row" NOWRAP align="left">
2931 1
								&nbsp;'.$mod_strings['LBL_USING_RULES'].'&nbsp;
2932 1
								<select name="distribute_method" id="dm" onChange="checkDeps(this.form);">'.$distribution.'</select>
2933 1
							</td>';
2934
2935
2936
					$out .= '</td>
2937 1
							</tr>';
2938
2939
2940
					$out .= '<tr>
2941
								<td scope="col" width="50%" scope="row" NOWRAP align="right" colspan="2">
2942 1
								<input title="'.$mod_strings['LBL_BUTTON_DISTRIBUTE_TITLE'].'"
2943
									id="dist_button"
2944
									class="button" onClick="AjaxObject.detailView.handleAssignmentDialogAssignAction();"
2945
									type="button" name="button"
2946 1
									value="  '.$mod_strings['LBL_BUTTON_DISTRIBUTE'].'  ">';
2947
					$out .= '</tr>
2948
					</table>
2949
				</td>
2950
			</tr>
2951
		</table>
2952 1
		</form>';
2953 1
	return $out;
2954
	}
2955
2956 2
	function userSelectTable() {
2957 2
		global $theme;
2958 2
		global $mod_strings;
2959
2960 2
		$colspan = 1;
2961 2
		$setTeamUserFunction = '';
2962
2963
2964
		// get users
2965 2
		$r = $this->db->query("SELECT users.id, users.user_name, users.first_name, users.last_name FROM users WHERE deleted=0 AND status = 'Active' AND is_group=0 ORDER BY users.last_name, users.first_name");
2966
2967 2
		$userTable = '<table cellpadding="0" cellspacing="0" border="0">';
2968 2
		$userTable .= '<tr><td colspan="2"><b>'.$mod_strings['LBL_USER_SELECT'].'</b></td></tr>';
2969 2
		$userTable .= '<tr><td><input type="checkbox" style="border:0px solid #000000" onClick="toggleAll(this); setCheckMark(); checkDeps(this.form);"></td> <td>'.$mod_strings['LBL_TOGGLE_ALL'].'</td></tr>';
2970 2
		$userTable .= '<tr><td colspan="2"><select style="visibility:hidden;" name="users[]" id="userSelect" multiple size="12">';
2971
2972 2
		while($a = $this->db->fetchByAssoc($r)) {
2973 2
			$userTable .= '<option value="'.$a['id'].'" id="'.$a['id'].'">'.$a['first_name'].' '.$a['last_name'].'</option>';
2974
		}
2975 2
		$userTable .= '</select></td></tr>';
2976 2
		$userTable .= '</table>';
2977
2978 2
		$out  = '<script type="text/javascript">';
2979 2
		$out .= $setTeamUserFunction;
2980
		$out .= '
2981
					function setCheckMark() {
2982
						var select = document.getElementById("userSelect");
2983
2984
						for(i=0 ; i<select.options.length; i++) {
2985
							if(select.options[i].selected == true) {
2986
								document.getElementById("checkMark").style.display="";
2987
								return;
2988
							}
2989
						}
2990
2991
						document.getElementById("checkMark").style.display="none";
2992
						return;
2993
					}
2994
2995
					function showUserSelect() {
2996
						var targetTable = document.getElementById("user_select");
2997
						targetTable.style.visibility="visible";
2998
						var userSelectTable = document.getElementById("userSelect");
2999
						userSelectTable.style.visibility="visible";
3000
						return;
3001
					}
3002
					function hideUserSelect() {
3003
						var targetTable = document.getElementById("user_select");
3004
						targetTable.style.visibility="hidden";
3005
						var userSelectTable = document.getElementById("userSelect");
3006
						userSelectTable.style.visibility="hidden";
3007
						return;
3008
					}
3009
					function toggleAll(toggle) {
3010
						if(toggle.checked) {
3011
							var stat = true;
3012
						} else {
3013
							var stat = false;
3014
						}
3015
						var form = document.getElementById("userSelect");
3016
						for(i=0; i<form.options.length; i++) {
3017
							form.options[i].selected = stat;
3018
						}
3019
					}
3020
3021
3022
				</script>
3023
			<span id="showUsersDiv" style="position:relative;">
3024
				<a href="#" id="showUsers" onClick="javascript:showUserSelect();">
3025 2
					'.SugarThemeRegistry::current()->getImage('Users', '', null, null, ".gif", $mod_strings['LBL_USERS']).'</a>&nbsp;
3026
				<a href="#" id="showUsers" onClick="javascript:showUserSelect();">
3027 2
					<span style="display:none;" id="checkMark">'.SugarThemeRegistry::current()->getImage('check_inline', 'border="0"', null, null, ".gif", $mod_strings['LBL_CHECK_INLINE']).'</span>
3028
				</a>
3029
3030
3031
				<div id="user_select" style="width:200px;position:absolute;left:2;top:2;visibility:hidden;z-index:1000;">
3032
				<table cellpadding="0" cellspacing="0" border="0" class="list view">
3033
					<tr height="20">
3034 2
						<td  colspan="'.$colspan.'" id="hiddenhead" onClick="hideUserSelect();" onMouseOver="this.style.border = \'outset red 1px\';" onMouseOut="this.style.border = \'inset white 0px\';this.style.borderBottom = \'inset red 1px\';">
3035 2
							<a href="#" onClick="javascript:hideUserSelect();">'.SugarThemeRegistry::current()->getImage('close', 'border="0"', null, null, ".gif", $mod_strings['LBL_CLOSE']).'</a>
3036 2
							'.$mod_strings['LBL_USER_SELECT'].'
3037
						</td>
3038
					</tr>
3039 2
					<tr>';
3040
//<td valign="middle" height="30"  colspan="'.$colspan.'" id="hiddenhead" onClick="hideUserSelect();" onMouseOver="this.style.border = \'outset red 1px\';" onMouseOut="this.style.border = \'inset white 0px\';this.style.borderBottom = \'inset red 1px\';">
3041
		$out .=	'		<td style="padding:5px" class="oddListRowS1" bgcolor="#fdfdfd" valign="top" align="left" style="left:0;top:0;">
3042 2
							'.$userTable.'
3043
						</td>
3044
					</tr>
3045
				</table></div>
3046 2
			</span>';
3047 2
		return $out;
3048
	}
3049
3050 1
	function checkInbox($type) {
3051 1
		global $theme;
3052 1
		global $mod_strings;
3053 1
		$out = '<div><input	title="'.$mod_strings['LBL_BUTTON_CHECK_TITLE'].'"
3054
						class="button"
3055
						type="button" name="button"
3056 1
						onClick="window.location=\'index.php?module=Emails&action=Check&type='.$type.'\';"
3057
						style="margin-bottom:2px"
3058 1
						value="  '.$mod_strings['LBL_BUTTON_CHECK'].'  "></div>';
3059 1
		return $out;
3060
	}
3061
3062
        /**
3063
         * Guesses Primary Parent id from From: email address.  Cascades guesses from Accounts to Contacts to Leads to
3064
         * Users.  This will not affect the many-to-many relationships already constructed as this is, at best,
3065
         * informational linking.
3066
         */
3067 1
        function fillPrimaryParentFields() {
3068 1
                if(empty($this->from_addr))
3069 1
                        return;
3070
3071
                $GLOBALS['log']->debug("*** Email trying to guess Primary Parent from address [ {$this->from_addr} ]");
3072
3073
                $tables = array('accounts');
3074
                $ret = array();
3075
                // loop through types to get hits
3076
                foreach($tables as $table) {
3077
                        $q = "SELECT name, id FROM {$table} WHERE email1 = '{$this->from_addr}' OR email2 = '{$this->from_addr}' AND deleted = 0";
3078
                        $r = $this->db->query($q);
3079
                        while($a = $this->db->fetchByAssoc($r)) {
3080
                                if(!empty($a['name']) && !empty($a['id'])) {
3081
                                        $this->parent_type      = ucwords($table);
3082
                                        $this->parent_id        = $a['id'];
3083
                                        $this->parent_name      = $a['name'];
3084
                                        return;
3085
                                }
3086
                        }
3087
                }
3088
        }
3089
3090
        /**
3091
         * Convert reference to inline image (stored as Note) to URL link
3092
         * Enter description here ...
3093
         * @param string $note ID of the note
0 ignored issues
show
Bug introduced by
There is no parameter named $note. 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...
3094
         * @param string $ext type of the note
0 ignored issues
show
Bug introduced by
There is no parameter named $ext. 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...
3095
         */
3096 1
        public function cid2Link($noteId, $noteType)
3097
        {
3098 1
            if(empty($this->description_html)) return;
3099 1
			list($type, $subtype) = explode('/', $noteType);
3100 1
			if(strtolower($type) != 'image') {
3101
			    return;
3102
			}
3103 1
            $upload = new UploadFile();
3104 1
			$this->description_html = preg_replace("#class=\"image\" src=\"cid:$noteId\.(.+?)\"#", "class=\"image\" src=\"{$this->imagePrefix}{$noteId}.\\1\"", $this->description_html);
3105
	        // ensure the image is in the cache
3106 1
			$imgfilename = sugar_cached("images/")."$noteId.".strtolower($subtype);
3107 1
			$src = "upload://$noteId";
3108 1
			if(!file_exists($imgfilename) && file_exists($src)) {
3109
				copy($src, $imgfilename);
3110
			}
3111 1
        }
3112
3113
        /**
3114
         * Convert all cid: links in this email into URLs
3115
         */
3116 1
    	function cids2Links()
3117
    	{
3118 1
            if(empty($this->description_html)) return;
3119 1
    	    $q = "SELECT id, file_mime_type FROM notes WHERE parent_id = '{$this->id}' AND deleted = 0";
3120 1
    		$r = $this->db->query($q);
3121 1
            while($a = $this->db->fetchByAssoc($r)) {
3122
                $this->cid2Link($a['id'], $a['file_mime_type']);
3123
            }
3124 1
    	}
3125
3126
    /**
3127
     * Bugs 50972, 50973
3128
     * Sets the field def for a field to allow null values
3129
     *
3130
     * @todo Consider moving to SugarBean to allow other models to set fields to NULL
3131
     * @param string $field The field name to modify
3132
     * @return void
3133
     */
3134 1
    public function setFieldNullable($field)
3135
    {
3136 1
        if (isset($this->field_defs[$field]) && is_array($this->field_defs[$field]))
3137
        {
3138 1
            if (empty($this->modifiedFieldDefs[$field]))
3139
            {
3140
                if (
3141 1
                    isset($this->field_defs[$field]['isnull']) &&
3142 1
                    (strtolower($this->field_defs[$field]['isnull']) == 'false' || $this->field_defs[$field]['isnull'] === false)
3143
                )
3144
                {
3145
                    $this->modifiedFieldDefs[$field]['isnull'] = $this->field_defs[$field]['isnull'];
3146
                    unset($this->field_defs[$field]['isnull']);
3147
                }
3148
3149 1
                if (isset($this->field_defs[$field]['dbType']) && $this->field_defs[$field]['dbType'] == 'id')
3150
                {
3151
                    $this->modifiedFieldDefs[$field]['dbType'] = $this->field_defs[$field]['dbType'];
3152
                    unset($this->field_defs[$field]['dbType']);
3153
                }
3154
            }
3155
        }
3156 1
    }
3157
3158
    /**
3159
     * Bugs 50972, 50973
3160
     * Set the field def back to the way it was prior to modification
3161
     *
3162
     * @param $field
3163
     * @return void
3164
     */
3165 1
    public function revertFieldNullable($field)
3166
    {
3167 1
        if (!empty($this->modifiedFieldDefs[$field]) && is_array($this->modifiedFieldDefs[$field]))
3168
        {
3169
            foreach ($this->modifiedFieldDefs[$field] as $k => $v)
3170
            {
3171
                $this->field_defs[$field][$k] = $v;
3172
            }
3173
3174
            unset($this->modifiedFieldDefs[$field]);
3175
            }
3176 1
    	}
3177
} // end class def
3178