]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - modules/Emails/Email.php
Release 6.3.0
[Github/sugarcrm.git] / modules / Emails / Email.php
1 <?php
2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4  * SugarCRM Community Edition is a customer relationship management program developed by
5  * SugarCRM, Inc. Copyright (C) 2004-2011 SugarCRM Inc.
6  * 
7  * This program is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU Affero General Public License version 3 as published by the
9  * Free Software Foundation with the addition of the following permission added
10  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11  * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
13  * 
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
17  * details.
18  * 
19  * You should have received a copy of the GNU Affero General Public License along with
20  * this program; if not, see http://www.gnu.org/licenses or write to the Free
21  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22  * 02110-1301 USA.
23  * 
24  * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25  * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
26  * 
27  * The interactive user interfaces in modified source and object code versions
28  * of this program must display Appropriate Legal Notices, as required under
29  * Section 5 of the GNU Affero General Public License version 3.
30  * 
31  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32  * these Appropriate Legal Notices must retain the display of the "Powered by
33  * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34  * technical reasons, the Appropriate Legal Notices must display the words
35  * "Powered by SugarCRM".
36  ********************************************************************************/
37
38 /*********************************************************************************
39
40  * Description:
41  * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc. All Rights
42  * Reserved. Contributor(s): ______________________________________..
43  *********************************************************************************/
44 require_once('include/SugarPHPMailer.php');
45
46 require_once('include/Pear/HTML_Safe/Safe.php');
47
48 class Email extends SugarBean {
49         /* SugarBean schema */
50         var $id;
51         var $date_entered;
52         var $date_modified;
53         var $assigned_user_id;
54         var $assigned_user_name;
55         var $modified_user_id;
56         var $created_by;
57         var $deleted;
58         var $from_addr;
59         var $reply_to_addr;
60         var $to_addrs;
61     var $cc_addrs;
62     var $bcc_addrs;
63         var $message_id;
64
65         /* Bean Attributes */
66         var $name;
67     var $type = 'archived';
68     var $date_sent;
69         var $status;
70         var $intent;
71         var $mailbox_id;
72         var $from_name;
73
74         var $reply_to_status;
75         var $reply_to_name;
76         var $reply_to_email;
77         var $description;
78         var $description_html;
79         var $raw_source;
80         var $parent_id;
81         var $parent_type;
82
83         /* link attributes */
84         var $parent_name;
85
86
87         /* legacy */
88         var $date_start; // legacy
89         var $time_start; // legacy
90         var $from_addr_name;
91         var $to_addrs_arr;
92     var $cc_addrs_arr;
93     var $bcc_addrs_arr;
94         var $to_addrs_ids;
95         var $to_addrs_names;
96         var $to_addrs_emails;
97         var $cc_addrs_ids;
98         var $cc_addrs_names;
99         var $cc_addrs_emails;
100         var $bcc_addrs_ids;
101         var $bcc_addrs_names;
102         var $bcc_addrs_emails;
103         var $contact_id;
104         var $contact_name;
105
106         /* Archive Email attrs */
107         var $duration_hours;
108
109
110
111         var $new_schema = true;
112         var $table_name = 'emails';
113         var $module_dir = 'Emails';
114     var $module_name = 'Emails';
115         var $object_name = 'Email';
116         var $db;
117
118         /* private attributes */
119         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";
120         var $cachePath;
121         var $cacheFile                  = 'robin.cache.php';
122         var $replyDelimiter     = "> ";
123         var $emailDescription;
124         var $emailDescriptionHTML;
125         var $emailRawSource;
126         var $link_action;
127         var $emailAddress;
128         var $attachments = array();
129
130         /* to support Email 2.0 */
131         var $isDuplicate;
132         var $uid;
133         var $to;
134         var $flagged;
135         var $answered;
136         var $seen;
137         var $draft;
138         var $relationshipMap = array(
139                 'Contacts'      => 'emails_contacts_rel',
140                 'Accounts'      => 'emails_accounts_rel',
141                 'Leads'         => 'emails_leads_rel',
142                 'Users'         => 'emails_users_rel',
143                 'Prospects'     => 'emails_prospects_rel',
144         );
145
146         /* public */
147         var $et;                // EmailUI object
148
149
150
151         /**
152          * sole constructor
153          */
154         function Email() {
155             $this->cachePath = $GLOBALS['sugar_config']['cache_dir'].'modules/Emails';
156                 parent::SugarBean();
157
158                 $this->safe = new HTML_Safe();
159                 $this->safe->clear();
160                 $this->emailAddress = new SugarEmailAddress();
161         }
162
163         function email2init() {
164                 require_once('modules/Emails/EmailUI.php');
165                 $this->et = new EmailUI();
166         }
167         function bean_implements($interface){
168                 switch($interface){
169                         case 'ACL': return true;
170                         default: return false;
171                 }
172
173         }
174
175         /**
176          * Presaves one attachment for new email 2.0 spec
177          * DOES NOT CREATE A NOTE
178          * @return string ID of note associated with the attachment
179          */
180         function email2saveAttachment() {
181                 global $sugar_config;
182
183                 $filesError = array(
184                         0 => 'UPLOAD_ERR_OK - There is no error, the file uploaded with success.',
185                         1 => 'UPLOAD_ERR_INI_SIZE - The uploaded file exceeds the upload_max_filesize directive in php.ini.',
186                         2 => 'UPLOAD_ERR_FORM_SIZE - The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.',
187                         3 => 'UPLOAD_ERR_PARTIAL - The uploaded file was only partially uploaded.',
188                         4 => 'UPLOAD_ERR_NO_FILE - No file was uploaded.',
189                         5 => 'UNKNOWN ERROR',
190                         6 => 'UPLOAD_ERR_NO_TMP_DIR - Missing a temporary folder. Introduced in PHP 4.3.10 and PHP 5.0.3.',
191                         7 => 'UPLOAD_ERR_CANT_WRITE - Failed to write file to disk. Introduced in PHP 5.1.0.',
192                 );
193
194                 // cn: Bug 5995 - rudimentary error checking
195                 if($_FILES['email_attachment']['error'] != 0 && $_FILES['email_attachment']['error'] != 4) {
196                         $GLOBALS['log']->debug('Email Attachment could not be attach due to error: '.$filesError[$_FILES['email_attachment']['error']]);
197                         return array();
198                 }
199
200                 if(isset($_FILES['email_attachment']) && is_uploaded_file($_FILES['email_attachment']['tmp_name'])) {
201                         $guid = create_guid();
202                         $cleanAttachmentFileName = from_html($_FILES['email_attachment']['name']);
203                         $GLOBALS['log']->debug("Email Attachment [ {$cleanAttachmentFileName} ] ");
204                         $cleanAttachmentFileName = str_replace("\\", "", $cleanAttachmentFileName);
205                         $GLOBALS['log']->debug("Email Attachment [ {$cleanAttachmentFileName} ] ");
206                         //$destination = clean_path("{$this->et->userCacheDir}/{$guid}{$cleanAttachmentFileName}");
207                         $destination = clean_path("{$this->et->userCacheDir}/{$guid}");
208                         $badExt = $this->safeAttachmentName($cleanAttachmentFileName);
209                         if ($badExt) {
210                                 //$destination = $destination . ".txt";
211                         } // if
212                         $fileName = $badExt ? $cleanAttachmentFileName . ".txt" : $cleanAttachmentFileName;
213                         if(move_uploaded_file($_FILES['email_attachment']['tmp_name'], $destination)) {
214                                 return array(
215                                         'guid' => $guid,
216                                         'name' => $GLOBALS['db']->helper->escape_quote($fileName),
217                                         'nameForDisplay' => $fileName
218                                 );
219                         } else {
220                                 $GLOBALS['log']->debug("Email Attachment [ {$cleanAttachmentFileName} ] could not be moved to cache dir");
221                                 return array();
222                         }
223                 }
224         }
225
226         function safeAttachmentName($filename) {
227                 global $sugar_config;
228                 $badExtension = false;
229                 //get position of last "." in file name
230                 $file_ext_beg = strrpos($filename, ".");
231                 $file_ext = "";
232
233                 //get file extension
234                 if($file_ext_beg !== false) {
235                         $file_ext = substr($filename, $file_ext_beg + 1);
236                 }
237
238                 //check to see if this is a file with extension located in "badext"
239                 foreach($sugar_config['upload_badext'] as $badExt) {
240                         if(strtolower($file_ext) == strtolower($badExt)) {
241                                 //if found, then append with .txt and break out of lookup
242                                 $filename = $filename . ".txt";
243                                 $badExtension = true;
244                                 break; // no need to look for more
245                         } // if
246                 } // foreach
247
248                 return $badExtension;
249         } // fn
250
251         /**
252          * takes output from email 2.0 to/cc/bcc fields and returns appropriate arrays for usage by PHPMailer
253          * @param string addresses
254          * @return array
255          */
256         function email2ParseAddresses($addresses) {
257                 $addresses = from_html($addresses);
258         $addresses = $this->et->unifyEmailString($addresses);
259
260                 $pattern = '/@.*,/U';
261                 preg_match_all($pattern, $addresses, $matchs);
262                 if (!empty($matchs[0])){
263                         $total = $matchs[0];
264                         foreach ($total as $match) {
265                                 $convertedPattern = str_replace(',', '::;::', $match);
266                                 $addresses = str_replace($match, $convertedPattern, $addresses);
267                         } //foreach
268                 }
269
270                 $exAddr = explode("::;::", $addresses);
271
272                 $ret = array();
273                 $clean = array("<", ">");
274                 $dirty = array("&lt;", "&gt;");
275
276                 foreach($exAddr as $addr) {
277                         $name = '';
278
279                         $addr = str_replace($dirty, $clean, $addr);
280
281                         if((strpos($addr, "<") === false) && (strpos($addr, ">") === false)) {
282                                 $address = $addr;
283                         } else {
284                                 $address = substr($addr, strpos($addr, "<") + 1, strpos($addr, ">") - 1 - strpos($addr, "<"));
285                                 $name = substr($addr, 0, strpos($addr, "<"));
286                         }
287
288                         $addrTemp = array();
289                         $addrTemp['email'] = trim($address);
290                         $addrTemp['display'] = trim($name);
291                         $ret[] = $addrTemp;
292                 }
293
294                 return $ret;
295         }
296
297         /**
298          * takes output from email 2.0 to/cc/bcc fields and returns appropriate arrays for usage by PHPMailer
299          * @param string addresses
300          * @return array
301          */
302         function email2ParseAddressesForAddressesOnly($addresses) {
303                 $addresses = from_html($addresses);
304                 $pattern = '/@.*,/U';
305                 preg_match_all($pattern, $addresses, $matchs);
306                 if (!empty($matchs[0])){
307                         $total = $matchs[0];
308                         foreach ($total as $match) {
309                                 $convertedPattern = str_replace(',', '::;::', $match);
310                                 $addresses = str_replace($match, $convertedPattern, $addresses);
311                         } //foreach
312                 }
313
314                 $exAddr = explode("::;::", $addresses);
315
316                 $ret = array();
317                 $clean = array("<", ">");
318                 $dirty = array("&lt;", "&gt;");
319
320                 foreach($exAddr as $addr) {
321                         $name = '';
322
323                         $addr = str_replace($dirty, $clean, $addr);
324
325                         if(strpos($addr, "<") && strpos($addr, ">")) {
326                                 $address = substr($addr, strpos($addr, "<") + 1, strpos($addr, ">") - 1 - strpos($addr, "<"));
327                         } else {
328                                 $address = $addr;
329                         }
330
331                         $ret[] = trim($address);
332                 }
333
334                 return $ret;
335         }
336
337         /**
338          * Determines MIME-type encoding as possible.
339          * @param string $fileLocation relative path to file
340          * @return string MIME-type
341          */
342         function email2GetMime($fileLocation) {
343             if(!is_readable($fileLocation)) {
344                 return 'application/octet-stream';
345             }
346                 if(function_exists('mime_content_type')) {
347                         $mime = mime_content_type($fileLocation);
348                 } elseif(function_exists('ext2mime')) {
349                         $mime = ext2mime($fileLocation);
350                 } else {
351                         $mime = 'application/octet-stream';
352                 }
353                 return $mime;
354         }
355
356
357         function sendEmailTest($mailserver_url, $port, $ssltls, $smtp_auth_req, $smtp_username, $smtppassword, $fromaddress, $toaddress, $mail_sendtype = 'smtp', $fromname = '') {
358                 global $current_user,$app_strings;
359                 $mod_strings = return_module_language($GLOBALS['current_language'], 'Emails'); //Called from EmailMan as well.
360             $mail = new SugarPHPMailer();
361                 $mail->Mailer = strtolower($mail_sendtype);
362                 if($mail->Mailer == 'smtp')
363                 {
364                 $mail->Host = $mailserver_url;
365                 $mail->Port = $port;
366                 if (isset($ssltls) && !empty($ssltls)) {
367                         $mail->protocol = "ssl://";
368                 if ($ssltls == 1) {
369                     $mail->SMTPSecure = 'ssl';
370                 } // if
371                 if ($ssltls == 2) {
372                     $mail->SMTPSecure = 'tls';
373                 } // if
374                 } else {
375                         $mail->protocol = "tcp://";
376                 }
377                 if ($smtp_auth_req) {
378                         $mail->SMTPAuth = TRUE;
379                         $mail->Username = $smtp_username;
380                         $mail->Password = $smtppassword;
381                 }
382                 }
383                 else
384                     $mail->Mailer = 'sendmail';
385
386                 $mail->Subject = from_html($mod_strings['LBL_TEST_EMAIL_SUBJECT']);
387                 $mail->From = $fromaddress;
388
389         if ($fromname != '') {
390             $mail->FromName = html_entity_decode($fromname,ENT_QUOTES);
391         } else {
392             $mail->FromName = $current_user->name;
393         }
394
395         $mail->Sender = $mail->From;
396                 $mail->AddAddress($toaddress);
397                 $mail->Body = $mod_strings['LBL_TEST_EMAIL_BODY'];
398
399                 $return = array();
400
401                 if(!$mail->Send()) {
402                 ob_clean();
403                 $return['status'] = false;
404                 $return['errorMessage'] = $app_strings['LBL_EMAIL_ERROR_PREPEND']. $mail->ErrorInfo;
405                 return $return;
406                 } // if
407                 $return['status'] = true;
408         return $return;
409         } // fn
410
411         function decodeDuringSend($htmlData) {
412             $htmlData = str_replace("sugarLessThan", "&lt;", $htmlData);
413             $htmlData = str_replace("sugarGreaterThan", "&gt;", $htmlData);
414                 return $htmlData;
415         }
416
417         /**
418          * Returns true or false if this email is a draft.
419          *
420          * @param array $request
421          * @return bool True indicates this email is a draft.
422          */
423         function isDraftEmail($request)
424         {
425             return ( isset($request['saveDraft']) || ($this->type == 'draft' && $this->status == 'draft') );
426         }
427
428         /**
429          * Sends Email for Email 2.0
430          */
431         function email2Send($request) {
432                 global $mod_strings;
433                 global $app_strings;
434                 global $current_user;
435                 global $sugar_config;
436                 global $locale;
437                 global $timedate;
438                 global $beanList;
439                 global $beanFiles;
440         $OBCharset = $locale->getPrecedentPreference('default_email_charset');
441
442                 /**********************************************************************
443                  * Sugar Email PREP
444                  */
445                 /* preset GUID */
446
447                 $orignialId = "";
448                 if(!empty($this->id)) {
449                         $orignialId =   $this->id;
450                 } // if
451
452                 if(empty($this->id)) {
453                         $this->id = create_guid();
454                         $this->new_with_id = true;
455                 }
456
457                 /* satisfy basic HTML email requirements */
458                 $this->name = $request['sendSubject'];
459                 $this->description_html = '&lt;html&gt;&lt;body&gt;'.$request['sendDescription'].'&lt;/body&gt;&lt;/html&gt;';
460
461                 /**********************************************************************
462                  * PHPMAILER PREP
463                  */
464                 $mail = new SugarPHPMailer();
465                 $mail = $this->setMailer($mail, '', $_REQUEST['fromAccount']);
466                 if (empty($mail->Host) && !$this->isDraftEmail($request))
467                 {
468             $this->status = 'send_error';
469
470             if ($mail->oe->type == 'system')
471                 echo($app_strings['LBL_EMAIL_ERROR_PREPEND']. $app_strings['LBL_EMAIL_INVALID_SYSTEM_OUTBOUND']);
472              else
473                 echo($app_strings['LBL_EMAIL_ERROR_PREPEND']. $app_strings['LBL_EMAIL_INVALID_PERSONAL_OUTBOUND']);
474
475             return false;
476                 }
477
478                 $subject = $this->name;
479                 $mail->Subject = from_html($this->name);
480
481                 // work-around legacy code in SugarPHPMailer
482                 if($_REQUEST['setEditor'] == 1) {
483                         $_REQUEST['description_html'] = $_REQUEST['sendDescription'];
484                         $this->description_html = $_REQUEST['description_html'];
485                 } else {
486                         $this->description_html = '';
487                         $this->description = $_REQUEST['sendDescription'];
488                 }
489                 // end work-around
490
491                 if ( $this->isDraftEmail($request) )
492                 {
493                         if($this->type != 'draft' && $this->status != 'draft') {
494                         $this->id = create_guid();
495                         $this->new_with_id = true;
496                         $this->date_entered = "";
497                         } // if
498                         $q1 = "update emails_email_addr_rel set deleted = 1 WHERE email_id = '{$this->id}'";
499                         $r1 = $this->db->query($q1);
500                 } // if
501
502                 if (isset($request['saveDraft'])) {
503                         $this->type = 'draft';
504                         $this->status = 'draft';
505                         $forceSave = true;
506                 } else {
507                         /* Apply Email Templates */
508                         // do not parse email templates if the email is being saved as draft....
509                     $toAddresses = $this->email2ParseAddresses($_REQUEST['sendTo']);
510                 $sea = new SugarEmailAddress();
511                 $object_arr = array();
512
513                         if( isset($_REQUEST['parent_type']) && !empty($_REQUEST['parent_type']) &&
514                                 isset($_REQUEST['parent_id']) && !empty($_REQUEST['parent_id']) &&
515                                 ($_REQUEST['parent_type'] == 'Accounts' ||
516                                 $_REQUEST['parent_type'] == 'Contacts' ||
517                                 $_REQUEST['parent_type'] == 'Leads' ||
518                                 $_REQUEST['parent_type'] == 'Users' ||
519                                 $_REQUEST['parent_type'] == 'Prospects')) {
520                                         if(isset($beanList[$_REQUEST['parent_type']]) && !empty($beanList[$_REQUEST['parent_type']])) {
521                                                 $className = $beanList[$_REQUEST['parent_type']];
522                                                 if(isset($beanFiles[$className]) && !empty($beanFiles[$className])) {
523                                                         if(!class_exists($className)) {
524                                                                 require_once($beanFiles[$className]);
525                                                         }
526                                                         $bean = new $className();
527                                                         $bean->retrieve($_REQUEST['parent_id']);
528                                         $object_arr[$bean->module_dir] = $bean->id;
529                                                 } // if
530                                         } // if
531                         }
532                         foreach($toAddresses as $addrMeta) {
533                                 $addr = $addrMeta['email'];
534                                 $beans = $sea->getBeansByEmailAddress($addr);
535                                 foreach($beans as $bean) {
536                                         if (!isset($object_arr[$bean->module_dir])) {
537                                                 $object_arr[$bean->module_dir] = $bean->id;
538                                         }
539                                 }
540                         }
541
542                 /* template parsing */
543                 if (empty($object_arr)) {
544                   $object_arr= array('Contacts' => '123');
545                 }
546                 $object_arr['Users'] = $current_user->id;
547                 $this->description_html = EmailTemplate::parse_template($this->description_html, $object_arr);
548                 $this->name = EmailTemplate::parse_template($this->name, $object_arr);
549                 $this->description = EmailTemplate::parse_template($this->description, $object_arr);
550                 $this->description = html_entity_decode($this->description,ENT_COMPAT,'UTF-8');
551                         if($this->type != 'draft' && $this->status != 'draft') {
552                         $this->id = create_guid();
553                         $this->date_entered = "";
554                         $this->new_with_id = true;
555                         $this->type = 'out';
556                         $this->status = 'sent';
557                         }
558         }
559
560         if(isset($_REQUEST['parent_type']) && empty($_REQUEST['parent_type']) &&
561                         isset($_REQUEST['parent_id']) && empty($_REQUEST['parent_id']) ) {
562                                 $this->parent_id = "";
563                                 $this->parent_type = "";
564                 } // if
565
566
567         $mail->Subject = $this->name;
568         $mail = $this->handleBody($mail);
569         $mail->Subject = $this->name;
570         $this->description_html = from_html($this->description_html);
571         $this->description_html = $this->decodeDuringSend($this->description_html);
572                 $this->description = $this->decodeDuringSend($this->description);
573
574                 /* from account */
575                 $replyToAddress = $current_user->emailAddress->getReplyToAddress($current_user);
576                 $replyToName = "";
577                 if(empty($request['fromAccount'])) {
578                         $defaults = $current_user->getPreferredEmail();
579                         $mail->From = $defaults['email'];
580                         $mail->FromName = $defaults['name'];
581                         $replyToName = $mail->FromName;
582                         //$replyToAddress = $current_user->emailAddress->getReplyToAddress($current_user);
583                 } else {
584                         // passed -> user -> system default
585                         $ie = new InboundEmail();
586                         $ie->retrieve($request['fromAccount']);
587                         $storedOptions = unserialize(base64_decode($ie->stored_options));
588                         $fromName = "";
589                         $fromAddress = "";
590                         $replyToName = "";
591                         //$replyToAddress = "";
592                         if (!empty($storedOptions)) {
593                                 $fromAddress = $storedOptions['from_addr'];
594                                 $fromName = from_html($storedOptions['from_name']);
595                                 $replyToAddress = (isset($storedOptions['reply_to_addr']) ? $storedOptions['reply_to_addr'] : "");
596                                 $replyToName = (isset($storedOptions['reply_to_name']) ? from_html($storedOptions['reply_to_name']) : "");
597                         } // if
598                         $defaults = $current_user->getPreferredEmail();
599                         // Personal Account doesn't have reply To Name and Reply To Address. So add those columns on UI
600                         // After adding remove below code
601
602                         // code to remove
603                         if ($ie->is_personal)
604                         {
605                                 if (empty($replyToAddress))
606                                 {
607                                         $replyToAddress = $current_user->emailAddress->getReplyToAddress($current_user);
608                                 } // if
609                                 if (empty($replyToName))
610                                 {
611                                         $replyToName = $defaults['name'];
612                                 } // if
613                                 //Personal accounts can have a reply_address, which should
614                                 //overwrite the users set default.
615                                 if( !empty($storedOptions['reply_to_addr']) )
616                                         $replyToAddress = $storedOptions['reply_to_addr'];
617
618                         }
619                         // end of code to remove
620                         $mail->From = (!empty($fromAddress)) ? $fromAddress : $defaults['email'];
621                         $mail->FromName = (!empty($fromName)) ? $fromName : $defaults['name'];
622                         $replyToName = (!empty($replyToName)) ? $replyToName : $mail->FromName;
623                 }
624
625                 $mail->Sender = $mail->From; /* set Return-Path field in header to reduce spam score in emails sent via Sugar's Email module */
626
627                 if (!empty($replyToAddress)) {
628                         $mail->AddReplyTo($replyToAddress,$locale->translateCharsetMIME(trim( $replyToName), 'UTF-8', $OBCharset));
629                 } else {
630                         $mail->AddReplyTo($mail->From,$locale->translateCharsetMIME(trim( $mail->FromName), 'UTF-8', $OBCharset));
631                 } // else
632         $emailAddressCollection = array(); // used in linking to beans below
633                 // handle to/cc/bcc
634                 foreach($this->email2ParseAddresses($request['sendTo']) as $addr_arr) {
635                         if(empty($addr_arr['email'])) continue;
636
637                         if(empty($addr_arr['display'])) {
638                                 $mail->AddAddress($addr_arr['email'], "");
639                         } else {
640                                 $mail->AddAddress($addr_arr['email'],$locale->translateCharsetMIME(trim( $addr_arr['display']), 'UTF-8', $OBCharset));
641                         }
642                         $emailAddressCollection[] = $addr_arr['email'];
643                 }
644                 foreach($this->email2ParseAddresses($request['sendCc']) as $addr_arr) {
645                         if(empty($addr_arr['email'])) continue;
646
647                         if(empty($addr_arr['display'])) {
648                                 $mail->AddCC($addr_arr['email'], "");
649                         } else {
650                                 $mail->AddCC($addr_arr['email'],$locale->translateCharsetMIME(trim( $addr_arr['display']), 'UTF-8', $OBCharset));
651                         }
652                         $emailAddressCollection[] = $addr_arr['email'];
653                 }
654
655                 foreach($this->email2ParseAddresses($request['sendBcc']) as $addr_arr) {
656                         if(empty($addr_arr['email'])) continue;
657
658                         if(empty($addr_arr['display'])) {
659                                 $mail->AddBCC($addr_arr['email'], "");
660                         } else {
661                                 $mail->AddBCC($addr_arr['email'],$locale->translateCharsetMIME(trim( $addr_arr['display']), 'UTF-8', $OBCharset));
662                         }
663                         $emailAddressCollection[] = $addr_arr['email'];
664                 }
665
666
667                 /* parse remove attachments array */
668                 $removeAttachments = array();
669                 if(!empty($request['templateAttachmentsRemove'])) {
670                         $exRemove = explode("::", $request['templateAttachmentsRemove']);
671
672                         foreach($exRemove as $file) {
673                                 $removeAttachments = substr($file, 0, 36);
674                         }
675                 }
676
677                 /* handle attachments */
678                 if(!empty($request['attachments'])) {
679                         $exAttachments = explode("::", $request['attachments']);
680
681                         foreach($exAttachments as $file) {
682                                 $file = trim(from_html($file));
683                                 $file = str_replace("\\", "", $file);
684                                 if(!empty($file)) {
685                                         //$fileLocation = $this->et->userCacheDir."/{$file}";
686                                         $fileGUID = substr($file, 0, 36);
687                                         $fileLocation = $this->et->userCacheDir."/{$fileGUID}";
688                                         $filename = substr($file, 36, strlen($file)); // strip GUID     for PHPMailer class to name outbound file
689
690                                         $mail->AddAttachment($fileLocation,$filename, 'base64', $this->email2GetMime($fileLocation));
691                                         //$mail->AddAttachment($fileLocation, $filename, 'base64');
692
693                                         // only save attachments if we're archiving or drafting
694                                         if((($this->type == 'draft') && !empty($this->id)) || (isset($request['saveToSugar']) && $request['saveToSugar'] == 1)) {
695                                                 $note = new Note();
696                                                 $note->id = create_guid();
697                                                 $note->new_with_id = true; // duplicating the note with files
698                                                 $note->parent_id = $this->id;
699                                                 $note->parent_type = $this->module_dir;
700                                                 $note->name = $filename;
701                                                 $note->filename = $filename;
702                                                 $noteFile = "{$sugar_config['upload_dir']}{$note->id}";
703                                                 $note->file_mime_type = $this->email2GetMime($fileLocation);
704
705                                                 if(!copy($fileLocation, $noteFile)) {
706                                                         $GLOBALS['log']->debug("EMAIL 2.0: could not copy attachment file to cache/upload [ {$fileLocation} ]");
707                                                 }
708
709                                                 $note->save();
710                                         }
711                                 }
712                         }
713                 }
714
715                 /* handle sugar documents */
716                 if(!empty($request['documents'])) {
717                         $exDocs = explode("::", $request['documents']);
718
719
720
721
722                         foreach($exDocs as $docId) {
723                                 $docId = trim($docId);
724                                 if(!empty($docId)) {
725                                         $doc = new Document();
726                                         $docRev = new DocumentRevision();
727                                         $doc->retrieve($docId);
728                                         $docRev->retrieve($doc->document_revision_id);
729
730                                         $filename = $docRev->filename;
731                                         $fileLocation = "{$sugar_config['upload_dir']}{$docRev->id}";
732                                         $mime_type = $docRev->file_mime_type;
733                                         $mail->AddAttachment($fileLocation,$locale->translateCharsetMIME(trim($filename), 'UTF-8', $OBCharset), 'base64', $mime_type);
734
735                                         // only save attachments if we're archiving or drafting
736                                         if((($this->type == 'draft') && !empty($this->id)) || (isset($request['saveToSugar']) && $request['saveToSugar'] == 1)) {
737                                                 $note = new Note();
738                                                 $note->id = create_guid();
739                                                 $note->new_with_id = true; // duplicating the note with files
740                                                 $note->parent_id = $this->id;
741                                                 $note->parent_type = $this->module_dir;
742                                                 $note->name = $filename;
743                                                 $note->filename = $filename;
744                                                 $note->file_mime_type = $mime_type;
745                                                 $noteFile = "{$sugar_config['upload_dir']}{$note->id}";
746
747                                                 if(!copy($fileLocation, $noteFile)) {
748                                                         $GLOBALS['log']->debug("EMAIL 2.0: could not copy SugarDocument revision file to {$sugar_config['upload_dir']} [ {$fileLocation} ]");
749                                                 }
750
751                                                 $note->save();
752                                         }
753                                 }
754                         }
755                 }
756
757                 /* handle template attachments */
758                 if(!empty($request['templateAttachments'])) {
759
760                         $exNotes = explode("::", $request['templateAttachments']);
761                         foreach($exNotes as $noteId) {
762                                 $noteId = trim($noteId);
763                                 if(!empty($noteId)) {
764                                         $note = new Note();
765                                         $note->retrieve($noteId);
766                                         if (!empty($note->id)) {
767                                                 $filename = $note->filename;
768                                                 $fileLocation = "{$sugar_config['upload_dir']}{$note->id}";
769                                                 $mime_type = $note->file_mime_type;
770                                                 if (!$note->embed_flag) {
771                                                         $mail->AddAttachment($fileLocation,$filename, 'base64', $mime_type);
772                                                         // only save attachments if we're archiving or drafting
773                                                         if((($this->type == 'draft') && !empty($this->id)) || (isset($request['saveToSugar']) && $request['saveToSugar'] == 1)) {
774
775                                                                 if ($note->parent_id != $this->id)
776                                                                     $this->saveTempNoteAttachments($filename,$fileLocation, $mime_type);
777                                                         } // if
778
779                                                 } // if
780                                         } else {
781                                                 //$fileLocation = $this->et->userCacheDir."/{$file}";
782                                                 $fileGUID = substr($noteId, 0, 36);
783                                                 $fileLocation = $this->et->userCacheDir."/{$fileGUID}";
784                                                 //$fileLocation = $this->et->userCacheDir."/{$noteId}";
785                                                 $filename = substr($noteId, 36, strlen($noteId)); // strip GUID for PHPMailer class to name outbound file
786
787                                                 $mail->AddAttachment($fileLocation,$locale->translateCharsetMIME(trim($filename), 'UTF-8', $OBCharset), 'base64', $this->email2GetMime($fileLocation));
788
789                                                 //If we are saving an email we were going to forward we need to save the attachments as well.
790                                                 if( (($this->type == 'draft') && !empty($this->id))
791                                                       || (isset($request['saveToSugar']) && $request['saveToSugar'] == 1))
792                                                   {
793                                                       $mimeType = $this->email2GetMime($fileLocation);
794                                                       $this->saveTempNoteAttachments($filename,$fileLocation, $mimeType);
795                                                  } // if
796                                         }
797                                 }
798                         }
799                 }
800
801
802
803                 /**********************************************************************
804                  * Final Touches
805                  */
806                 /* save email to sugar? */
807                 $forceSave = false;
808
809                 if($this->type == 'draft' && !isset($request['saveDraft'])) {
810                         // sending a draft email
811                         $this->type = 'out';
812                         $this->status = 'sent';
813                         $forceSave = true;
814                 } elseif(isset($request['saveDraft'])) {
815                         $this->type = 'draft';
816                         $this->status = 'draft';
817                         $forceSave = true;
818                 }
819
820                       /**********************************************************************
821          * SEND EMAIL (finally!)
822          */
823         $mailSent = false;
824         if ($this->type != 'draft') {
825             $mail->prepForOutbound();
826             $mail->Body = $this->decodeDuringSend($mail->Body);
827             $mail->AltBody = $this->decodeDuringSend($mail->AltBody);
828             if (!$mail->Send()) {
829                 $this->status = 'send_error';
830                 ob_clean();
831                 echo($app_strings['LBL_EMAIL_ERROR_PREPEND']. $mail->ErrorInfo);
832                 return false;
833             }
834         }
835
836                 if ((!(empty($orignialId) || isset($request['saveDraft']) || ($this->type == 'draft' && $this->status == 'draft'))) &&
837                         (($_REQUEST['composeType'] == 'reply') || ($_REQUEST['composeType'] == 'replyAll') || ($_REQUEST['composeType'] == 'replyCase')) && ($orignialId != $this->id)) {
838                         $originalEmail = new Email();
839                         $originalEmail->retrieve($orignialId);
840                         $originalEmail->reply_to_status = 1;
841                         $originalEmail->save();
842                         $this->reply_to_status = 0;
843                 } // if
844
845                 if ($_REQUEST['composeType'] == 'reply' || $_REQUEST['composeType'] == 'replyCase') {
846                         if (isset($_REQUEST['ieId']) && isset($_REQUEST['mbox'])) {
847                                 $emailFromIe = new InboundEmail();
848                                 $emailFromIe->retrieve($_REQUEST['ieId']);
849                                 $emailFromIe->mailbox = $_REQUEST['mbox'];
850                                 if (isset($emailFromIe->id) && $emailFromIe->is_personal) {
851                                         if ($emailFromIe->isPop3Protocol()) {
852                                                 $emailFromIe->mark_answered($this->uid, 'pop3');
853                                         }
854                                         elseif ($emailFromIe->connectMailserver() == 'true') {
855                                                 $emailFromIe->markEmails($this->uid, 'answered');
856                                                 $emailFromIe->mark_answered($this->uid);
857                                         }
858                                 }
859                         }
860                 }
861
862
863                 if(     $forceSave ||
864                         $this->type == 'draft' ||
865                         (isset($request['saveToSugar']) && $request['saveToSugar'] == 1)) {
866
867                         // saving a draft OR saving a sent email
868                         $decodedFromName = mb_decode_mimeheader($mail->FromName);
869                         $this->from_addr = "{$decodedFromName} <{$mail->From}>";
870                         $this->from_addr_name = $this->from_addr;
871                         $this->to_addrs = $_REQUEST['sendTo'];
872                         $this->to_addrs_names = $_REQUEST['sendTo'];
873                         $this->cc_addrs = $_REQUEST['sendCc'];
874                         $this->cc_addrs_names = $_REQUEST['sendCc'];
875                         $this->bcc_addrs = $_REQUEST['sendBcc'];
876                         $this->bcc_addrs_names = $_REQUEST['sendBcc'];
877                         $this->assigned_user_id = $current_user->id;
878
879                         $this->date_sent = $timedate->now();
880                         ///////////////////////////////////////////////////////////////////
881                         ////    LINK EMAIL TO SUGARBEANS BASED ON EMAIL ADDY
882
883                         if( isset($_REQUEST['parent_type']) && !empty($_REQUEST['parent_type']) &&
884                                 isset($_REQUEST['parent_id']) && !empty($_REQUEST['parent_id']) ) {
885                         $this->parent_id = $_REQUEST['parent_id'];
886                         $this->parent_type = $_REQUEST['parent_type'];
887                                         $q = "SELECT count(*) c FROM emails_beans WHERE  email_id = '{$this->id}' AND bean_id = '{$_REQUEST['parent_id']}' AND bean_module = '{$_REQUEST['parent_type']}'";
888                                         $r = $this->db->query($q);
889                                         $a = $this->db->fetchByAssoc($r);
890                                         if($a['c'] <= 0) {
891                                                 if(isset($beanList[$_REQUEST['parent_type']]) && !empty($beanList[$_REQUEST['parent_type']])) {
892                                                         $className = $beanList[$_REQUEST['parent_type']];
893                                                         if(isset($beanFiles[$className]) && !empty($beanFiles[$className])) {
894                                                                 if(!class_exists($className)) {
895                                                                         require_once($beanFiles[$className]);
896                                                                 }
897                                                                 $bean = new $className();
898                                                                 $bean->retrieve($_REQUEST['parent_id']);
899                                                                 if($bean->load_relationship('emails')) {
900                                                                         $bean->emails->add($this->id);
901                                                                 } // if
902
903                                                         } // if
904
905                                                 } // if
906
907                                         } // if
908
909                                 } else {
910                                         if(!class_exists('aCase')) {
911
912                                         }
913                                         else{
914                                                 $c = new aCase();
915                                                 if($caseId = InboundEmail::getCaseIdFromCaseNumber($mail->Subject, $c)) {
916                                                         $c->retrieve($caseId);
917                                                         $c->load_relationship('emails');
918                                                         $c->emails->add($this->id);
919                                                         $this->parent_type = "Cases";
920                                                         $this->parent_id = $caseId;
921                                                 } // if
922                                         }
923
924                                 } // else
925
926                         ////    LINK EMAIL TO SUGARBEANS BASED ON EMAIL ADDY
927                         ///////////////////////////////////////////////////////////////////
928                         $this->save();
929                 }
930
931                 if(!empty($request['fromAccount'])) {
932                         if (isset($ie->id) && !$ie->isPop3Protocol()) {
933                                 $sentFolder = $ie->get_stored_options("sentFolder");
934                                 if (!empty($sentFolder)) {
935                                         $data = $mail->CreateHeader() . "\r\n" . $mail->CreateBody() . "\r\n";
936                                         $ie->mailbox = $sentFolder;
937                                         if ($ie->connectMailserver() == 'true') {
938                                                 $connectString = $ie->getConnectString($ie->getServiceString(), $ie->mailbox);
939                                                 $returnData = imap_append($ie->conn,$connectString, $data, "\\Seen");
940                                                 if (!$returnData) {
941                                                         $GLOBALS['log']->debug("could not copy email to {$ie->mailbox} for {$ie->name}");
942                                                 } // if
943                                         } else {
944                                                 $GLOBALS['log']->debug("could not connect to mail serve for folder {$ie->mailbox} for {$ie->name}");
945                                         } // else
946                                 } else {
947                                         $GLOBALS['log']->debug("could not copy email to {$ie->mailbox} sent folder as its empty");
948                                 } // else
949                         } // if
950                 } // if
951                 return true;
952         } // end email2send
953
954         /**
955          * Generates a comma sperated name and addresses to be used in compose email screen for contacts or leads
956          * from listview
957          *
958          * @param $module string module name
959          * @param $idsArray array of record ids to get the email address for
960          * @return string comma delimited list of email addresses
961          */
962         public function getNamePlusEmailAddressesForCompose($module, $idsArray)
963         {
964                 global $locale;
965                 global $db;
966                 $table = SugarModule::get($module)->loadBean()->table_name;
967                 $returndata = array();
968                 $idsString = "";
969                 foreach($idsArray as $id) {
970                         if ($idsString != "") {
971                                 $idsString = $idsString . ",";
972                         } // if
973                         $idsString = $idsString . "'" . $id . "'";
974                 } // foreach
975                 $where = "({$table}.deleted = 0 AND {$table}.id in ({$idsString}))";
976
977                 if ($module == 'Users' || $module == 'Employees') {
978                         $selectColumn = "{$table}.first_name, {$table}.last_name, {$table}.title";
979                 }
980                 elseif (SugarModule::get($module)->moduleImplements('Person')) {
981                         $selectColumn = "{$table}.first_name, {$table}.last_name, {$table}.salutation, {$table}.title";
982                 }
983                 else {
984                     $selectColumn = "{$table}.name";
985                 }
986                 $query = "SELECT {$table}.id, {$selectColumn}, eabr.primary_address, ea.email_address";
987                 $query .= " FROM {$table} ";
988                 $query .= "JOIN email_addr_bean_rel eabr ON ({$table}.id = eabr.bean_id and eabr.deleted=0) ";
989                 $query .= "JOIN email_addresses ea ON (eabr.email_address_id = ea.id) ";
990                 $query .= " WHERE ({$where}) ORDER BY eabr.primary_address DESC";
991                 $r = $this->db->query($query);
992
993                 while($a = $this->db->fetchByAssoc($r)) {
994                         if (!isset($returndata[$a['id']])) {
995                                 if ($module == 'Users' || $module == 'Employees') {
996                                     $full_name = from_html($locale->getLocaleFormattedName($a['first_name'], $a['last_name'], '', $a['title']));
997                                         $returndata[$a['id']] = "{$full_name} <".from_html($a['email_address']).">";
998                                 }
999                                 elseif (SugarModule::get($module)->moduleImplements('Person')) {
1000                                         $full_name = from_html($locale->getLocaleFormattedName($a['first_name'], $a['last_name'], $a['salutation'], $a['title']));
1001                                         $returndata[$a['id']] = "{$full_name} <".from_html($a['email_address']).">";
1002                                 }
1003                                 else {
1004                                         $returndata[$a['id']] = from_html($a['name']) . " <".from_html($a['email_address']).">";
1005                                 } // else
1006                         }
1007                 }
1008
1009                 return join(",", array_values($returndata));
1010     }
1011
1012         /**
1013          * Overrides
1014          */
1015         ///////////////////////////////////////////////////////////////////////////
1016         ////    SAVERS
1017         function save($check_notify = false) {
1018                 if($this->isDuplicate) {
1019                         $GLOBALS['log']->debug("EMAIL - tried to save a duplicate Email record");
1020                 } else {
1021
1022                         if(empty($this->id)) {
1023                                 $this->id = create_guid();
1024                                 $this->new_with_id = true;
1025                         }
1026                         $this->from_addr_name = $this->cleanEmails($this->from_addr_name);
1027                         $this->to_addrs_names = $this->cleanEmails($this->to_addrs_names);
1028                         $this->cc_addrs_names = $this->cleanEmails($this->cc_addrs_names);
1029                         $this->bcc_addrs_names = $this->cleanEmails($this->bcc_addrs_names);
1030                         $this->reply_to_addr = $this->cleanEmails($this->reply_to_addr);
1031                         $this->description = to_html($this->safeText(from_html($this->description)));
1032                         $this->description_html = $this->safeText($this->description_html);
1033                         $this->saveEmailText();
1034                         $this->saveEmailAddresses();
1035
1036                         $GLOBALS['log']->debug('-------------------------------> Email called save()');
1037
1038                         // handle legacy concatenation of date and time fields
1039                         if(empty($this->date_sent)) $this->date_sent = $this->date_start." ".$this->time_start;
1040
1041                         parent::save($check_notify);
1042
1043                         if(!empty($this->parent_type) && !empty($this->parent_id)) {
1044                 if(!empty($this->fetched_row) && !empty($this->fetched_row['parent_id']) && !empty($this->fetched_row['parent_type'])) {
1045                     if($this->fetched_row['parent_id'] != $this->parent_id || $this->fetched_row['parent_type'] != $this->parent_type) {
1046                         $mod = strtolower($this->fetched_row['parent_type']);
1047                         $rel = array_key_exists($mod, $this->field_defs) ? $mod : $mod . "_activities_emails"; //Custom modules rel name
1048                         if($this->load_relationship($rel) ) {
1049                             $this->$rel->delete($this->id, $this->fetched_row['parent_id']);
1050                         }
1051                     }
1052                 }
1053                 $mod = strtolower($this->parent_type);
1054                 $rel = array_key_exists($mod, $this->field_defs) ? $mod : $mod . "_activities_emails"; //Custom modules rel name
1055                 if($this->load_relationship($rel) ) {
1056                     $this->$rel->add($this->parent_id);
1057                 }
1058                         }
1059                 }
1060                 $GLOBALS['log']->debug('-------------------------------> Email save() done');
1061         }
1062
1063         /**
1064          * Helper function to save temporary attachments assocaited to an email as note.
1065          *
1066          * @param string $filename
1067          * @param string $fileLocation
1068          * @param string $mimeType
1069          */
1070         function saveTempNoteAttachments($filename,$fileLocation, $mimeType)
1071         {
1072             global $sugar_config;
1073
1074             $tmpNote = new Note();
1075             $tmpNote->id = create_guid();
1076             $tmpNote->new_with_id = true;
1077             $tmpNote->parent_id = $this->id;
1078             $tmpNote->parent_type = $this->module_dir;
1079             $tmpNote->name = $filename;
1080             $tmpNote->filename = $filename;
1081             $tmpNote->file_mime_type = $mimeType;
1082             $noteFile = "{$sugar_config['upload_dir']}{$tmpNote->id}";
1083             if(!copy($fileLocation, $noteFile))
1084             $GLOBALS['log']->fatal("EMAIL 2.0: could not copy SugarDocument revision file to {$sugar_config['upload_dir']} [ {$fileLocation} ]");
1085             $tmpNote->save();
1086         }
1087         /**
1088          * Handles normalization of Email Addressess
1089          */
1090         function saveEmailAddresses() {
1091                 // from, single address
1092                 $fromId = $this->emailAddress->getEmailGUID(from_html($this->from_addr));
1093         if(!empty($fromId)){
1094                   $this->linkEmailToAddress($fromId, 'from');
1095         }
1096
1097                 // to, multiple
1098                 $replace = array(",",";");
1099                 $toaddrs = str_replace($replace, "::", from_html($this->to_addrs));
1100                 $exToAddrs = explode("::", $toaddrs);
1101
1102                 if(!empty($exToAddrs)) {
1103                         foreach($exToAddrs as $toaddr) {
1104                                 $toaddr = trim($toaddr);
1105                                 if(!empty($toaddr)) {
1106                                         $toId = $this->emailAddress->getEmailGUID($toaddr);
1107                                         $this->linkEmailToAddress($toId, 'to');
1108                                 }
1109                         }
1110                 }
1111
1112                 // cc, multiple
1113                 $ccAddrs = str_replace($replace, "::", from_html($this->cc_addrs));
1114                 $exccAddrs = explode("::", $ccAddrs);
1115
1116                 if(!empty($exccAddrs)) {
1117                         foreach($exccAddrs as $ccAddr) {
1118                                 $ccAddr = trim($ccAddr);
1119                                 if(!empty($ccAddr)) {
1120                                         $ccId = $this->emailAddress->getEmailGUID($ccAddr);
1121                                         $this->linkEmailToAddress($ccId, 'cc');
1122                                 }
1123                         }
1124                 }
1125
1126                 // bcc, multiple
1127                 $bccAddrs = str_replace($replace, "::", from_html($this->bcc_addrs));
1128                 $exbccAddrs = explode("::", $bccAddrs);
1129                 if(!empty($exbccAddrs)) {
1130                         foreach($exbccAddrs as $bccAddr) {
1131                                 $bccAddr = trim($bccAddr);
1132                                 if(!empty($bccAddr)) {
1133                                         $bccId = $this->emailAddress->getEmailGUID($bccAddr);
1134                                         $this->linkEmailToAddress($bccId, 'bcc');
1135                                 }
1136                         }
1137                 }
1138         }
1139
1140         function linkEmailToAddress($id, $type) {
1141                 // TODO: make this update?
1142                 $q1 = "SELECT * FROM emails_email_addr_rel WHERE email_id = '{$this->id}' AND email_address_id = '{$id}' AND address_type = '{$type}' AND deleted = 0";
1143                 $r1 = $this->db->query($q1);
1144                 $a1 = $this->db->fetchByAssoc($r1);
1145
1146                 if(!empty($a1) && !empty($a1['id'])) {
1147                         return $a1['id'];
1148                 } else {
1149                         $guid = create_guid();
1150                         $q2 = "INSERT INTO emails_email_addr_rel VALUES('{$guid}', '{$this->id}', '{$type}', '{$id}', 0)";
1151                         $r2 = $this->db->query($q2);
1152                 }
1153
1154                 return $guid;
1155         }
1156
1157         function cleanEmails($emails)
1158         {
1159                 $emails = str_replace(array(",",";"), "::", from_html($emails));
1160                 $addrs = explode("::", $emails);
1161                 $res = array();
1162                 foreach($addrs as $addr) {
1163             $parts = $this->emailAddress->splitEmailAddress($addr);
1164             if(empty($parts["email"])) {
1165                 continue;
1166             }
1167             if(!empty($parts["name"])) {
1168                 $res[] = "{$parts["name"]} <{$parts["email"]}>";
1169             } else {
1170                 $res[] .= $parts["email"];
1171             }
1172                 }
1173         return join(", ", $res);
1174         }
1175
1176         function saveEmailText() {
1177                 $isOracle = ($this->db->dbType == "oci8") ? true : false;
1178                 if ($isOracle) {
1179                 } else {
1180                         $description = $this->db->quote(trim($this->description));
1181                         $description_html = $this->db->quoteForEmail(trim($this->description_html));
1182                         $raw_source = $this->db->quote(trim($this->raw_source));
1183                         $fromAddressName = $this->db->helper->escape_quote($this->from_addr_name);
1184                         $toAddressName = $this->db->helper->escape_quote($this->to_addrs_names);
1185                         $ccAddressName = $this->db->helper->escape_quote($this->cc_addrs_names);
1186                         $bccAddressName = $this->db->helper->escape_quote($this->bcc_addrs_names);
1187                         $replyToAddrName = $this->db->helper->escape_quote($this->reply_to_addr);
1188
1189                         if(!$this->new_with_id) {
1190                                 $q = "UPDATE emails_text SET from_addr = '{$fromAddressName}', to_addrs = '{$toAddressName}', cc_addrs = '{$ccAddressName}', bcc_addrs = '{$bccAddressName}', reply_to_addr = '{$replyToAddrName}', description = '{$description}', description_html = '{$description_html}', raw_source = '{$raw_source}' WHERE email_id = '{$this->id}'";
1191                         } else {
1192                                 $q = "INSERT INTO emails_text (email_id, from_addr, to_addrs, cc_addrs, bcc_addrs, reply_to_addr, description, description_html, raw_source, deleted) VALUES('{$this->id}', '{$fromAddressName}', '{$toAddressName}', '{$ccAddressName}', '{$bccAddressName}', '{$replyToAddrName}', '{$description}', '{$description_html}', '{$raw_source}', 0)";
1193                         }
1194                         $this->db->query($q);
1195
1196                 } // else
1197         }
1198
1199
1200         ///////////////////////////////////////////////////////////////////////////
1201         ////    RETRIEVERS
1202         function retrieve($id, $encoded=true, $deleted=true) {
1203                 // cn: bug 11915, return SugarBean's retrieve() call bean instead of $this
1204                 $ret = parent::retrieve($id, $encoded, $deleted);
1205
1206                 if($ret) {
1207                         $ret->retrieveEmailText();
1208                         $ret->retrieveEmailAddresses();
1209                         $ret->raw_source = to_html($ret->safeText(from_html($ret->raw_source)));
1210                         $ret->description = to_html($ret->safeText(from_html($ret->description)));
1211                         $ret->description_html = $ret->safeText($ret->description_html);
1212
1213                         $ret->date_start = '';
1214                         $ret->time_start = '';
1215                         $dateSent = explode(' ', $ret->date_sent);
1216                         if (!empty($dateSent)) {
1217                             $ret->date_start = $dateSent[0];
1218                             if ( isset($dateSent[1]) )
1219                                 $ret->time_start = $dateSent[1];
1220                         }
1221                         // for Email 2.0
1222                         foreach($ret as $k => $v) {
1223                                 $this->$k = $v;
1224                         }
1225                 }
1226                 return $ret;
1227         }
1228
1229
1230         /**
1231          * Retrieves email addresses from GUIDs
1232          */
1233         function retrieveEmailAddresses() {
1234                 $return = array();
1235
1236                 $q = "SELECT email_address, address_type
1237                                 FROM emails_email_addr_rel eam
1238                                 JOIN email_addresses ea ON ea.id = eam.email_address_id
1239                                 WHERE eam.email_id = '{$this->id}' AND eam.deleted=0";
1240                 $r = $this->db->query($q);
1241
1242                 while($a = $this->db->fetchByAssoc($r)) {
1243                         if(!isset($return[$a['address_type']])) {
1244                                 $return[$a['address_type']] = array();
1245                         }
1246                         $return[$a['address_type']][] = $a['email_address'];
1247                 }
1248
1249                 if(count($return) > 0) {
1250                         if(isset($return['from'])) {
1251                                 $this->from_addr = implode(", ", $return['from']);
1252                         }
1253                         if(isset($return['to'])) {
1254                                 $this->to_addrs = implode(", ", $return['to']);
1255                         }
1256                         if(isset($return['cc'])) {
1257                                 $this->cc_addrs = implode(", ", $return['cc']);
1258                         }
1259                         if(isset($return['bcc'])) {
1260                                 $this->bcc_addrs = implode(", ", $return['bcc']);
1261                         }
1262                 }
1263         }
1264
1265         /**
1266          * Handles longtext fields
1267          */
1268         function retrieveEmailText() {
1269                 $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}'";
1270                 $r = $this->db->query($q);
1271                 $a = $this->db->fetchByAssoc($r, -1, false);
1272
1273                 $this->description = $a['description'];
1274                 $this->description_html = $a['description_html'];
1275                 $this->raw_source = $a['raw_source'];
1276                 $this->from_addr_name = $a['from_addr'];
1277                 $this->reply_to_addr  = $a['reply_to_addr'];
1278                 $this->to_addrs_names = $a['to_addrs'];
1279                 $this->cc_addrs_names = $a['cc_addrs'];
1280                 $this->bcc_addrs_names = $a['bcc_addrs'];
1281         }
1282
1283         function delete($id='') {
1284                 if(empty($id))
1285                         $id = $this->id;
1286
1287                 $q  = "UPDATE emails SET deleted = 1 WHERE id = '{$id}'";
1288                 $qt = "UPDATE emails_text SET deleted = 1 WHERE email_id = '{$id}'";
1289                 $r  = $this->db->query($q);
1290                 $rt = $this->db->query($qt);
1291         }
1292
1293         /**
1294          * creates the standard "Forward" info at the top of the forwarded message
1295          * @return string
1296          */
1297         function getForwardHeader() {
1298                 global $mod_strings;
1299                 global $current_user;
1300
1301                 //$from = str_replace(array("&gt;","&lt;"), array(")","("), $this->from_name);
1302                 $from = to_html($this->from_name);
1303                 $subject = to_html($this->name);
1304                 $ret  = "<br /><br />";
1305                 $ret .= $this->replyDelimiter."{$mod_strings['LBL_FROM']} {$from}<br />";
1306                 $ret .= $this->replyDelimiter."{$mod_strings['LBL_DATE_SENT']} {$this->date_sent}<br />";
1307                 $ret .= $this->replyDelimiter."{$mod_strings['LBL_TO']} {$this->to_addrs}<br />";
1308                 $ret .= $this->replyDelimiter."{$mod_strings['LBL_CC']} {$this->cc_addrs}<br />";
1309                 $ret .= $this->replyDelimiter."{$mod_strings['LBL_SUBJECT']} {$subject}<br />";
1310                 $ret .= $this->replyDelimiter."<br />";
1311
1312                 return $ret;
1313                 //return from_html($ret);
1314         }
1315
1316     /**
1317      * retrieves Notes that belong to this Email and stuffs them into the "attachments" attribute
1318      */
1319     function getNotes($id, $duplicate=false) {
1320         if(!class_exists('Note')) {
1321
1322         }
1323
1324         $exRemoved = array();
1325                 if(isset($_REQUEST['removeAttachment'])) {
1326                         $exRemoved = explode('::', $_REQUEST['removeAttachment']);
1327                 }
1328
1329         $noteArray = array();
1330         $q = "SELECT id FROM notes WHERE parent_id = '".$id."'";
1331         $r = $this->db->query($q);
1332
1333         while($a = $this->db->fetchByAssoc($r)) {
1334                 if(!in_array($a['id'], $exRemoved)) {
1335                     $note = new Note();
1336                     $note->retrieve($a['id']);
1337
1338                     // duplicate actual file when creating forwards
1339                         if($duplicate) {
1340                                 if(!class_exists('UploadFile')) {
1341                                         require_once('include/upload_file.php');
1342                                 }
1343                                 // save a brand new Note
1344                                 $noteDupe->id = create_guid();
1345                                 $noteDupe->new_with_id = true;
1346                                         $noteDupe->parent_id = $this->id;
1347                                         $noteDupe->parent_type = $this->module_dir;
1348
1349                                         $noteFile = new UploadFile('none');
1350                                         $noteFile->duplicate_file($a['id'], $note->id, $note->filename);
1351
1352                                         $note->save();
1353                         }
1354                         // add Note to attachments array
1355                     $this->attachments[] = $note;
1356                 }
1357         }
1358     }
1359
1360         /**
1361          * creates the standard "Reply" info at the top of the forwarded message
1362          * @return string
1363          */
1364         function getReplyHeader() {
1365                 global $mod_strings;
1366                 global $current_user;
1367
1368                 $from = str_replace(array("&gt;","&lt;", ">","<"), array(")","(",")","("), $this->from_name);
1369                 $ret  = "<br>{$mod_strings['LBL_REPLY_HEADER_1']} {$this->date_start}, {$this->time_start}, {$from} {$mod_strings['LBL_REPLY_HEADER_2']}";
1370
1371                 return from_html($ret);
1372         }
1373
1374         /**
1375          * Quotes plain-text email text
1376          * @param string $text
1377          * @return string
1378          */
1379         function quotePlainTextEmail($text) {
1380                 $quoted = "\n";
1381
1382                 // plain-text
1383                 $desc = nl2br(trim($text));
1384                 $exDesc = explode('<br />', $desc);
1385
1386                 foreach($exDesc as $k => $line) {
1387                         $quoted .= '> '.trim($line)."\r";
1388                 }
1389
1390                 return $quoted;
1391         }
1392
1393         /**
1394          * "quotes" (i.e., "> my text yadda" the HTML part of an email
1395          * @param string $text HTML text to quote
1396          * @return string
1397          */
1398         function quoteHtmlEmail($text) {
1399                 $text = trim(from_html($text));
1400
1401                 if(empty($text)) {
1402                         return '';
1403                 }
1404                 $out = "<div style='border-left:1px solid #00c; padding:5px; margin-left:10px;'>{$text}</div>";
1405
1406                 return $out;
1407         }
1408
1409         /**
1410          * "quotes" (i.e., "> my text yadda" the HTML part of an email
1411          * @param string $text HTML text to quote
1412          * @return string
1413          */
1414         function quoteHtmlEmailForNewEmailUI($text) {
1415                 $text = trim($text);
1416
1417                 if(empty($text)) {
1418                         return '';
1419                 }
1420                 $text = str_replace("\n", "\n<BR/>", $text);
1421                 $out = "<div style='border-left:1px solid #00c; padding:5px; margin-left:10px;'>{$text}</div>";
1422
1423                 return $out;
1424         }
1425
1426
1427
1428         ///////////////////////////////////////////////////////////////////////////
1429         ////    LEGACY CODE
1430         /**
1431          * Safes description text (both HTML and Plain Text) for display
1432          * @param string str The text to safe
1433          * @return string Safed text
1434          */
1435         function safeText($str) {
1436                 // Safe_HTML
1437                 $this->safe->clear();
1438                 $ret = $this->safe->parse($str);
1439
1440                 // Julian's XSS cleaner
1441                 $potentials = clean_xss($str, false);
1442
1443                 if(is_array($potentials) && !empty($potentials)) {
1444                         //_ppl($potentials);
1445                         foreach($potentials as $bad) {
1446                                 $ret = str_replace($bad, "", $ret);
1447                         }
1448                 }
1449
1450                 // clean <HTML> and <BODY> tags
1451                 $html = '#<\\\\\?HTML[\w =\'\"\&]*>#sim';
1452                 $body = '#<\\\\\?BODY[\w =\'\"\&]*>#sim';
1453
1454                 $ret = preg_replace($html, "", $ret);
1455                 $ret = preg_replace($body, "", $ret);
1456
1457                 return $ret;
1458         }
1459
1460         /**
1461          * Ensures that the user is able to send outbound emails
1462          */
1463         function check_email_settings() {
1464                 global $current_user;
1465
1466                 $mail_fromaddress = $current_user->emailAddress->getPrimaryAddress($current_user);
1467                 $replyToName = $current_user->getPreference('mail_fromname');
1468                 $mail_fromname = (!empty($replyToName)) ? $current_user->getPreference('mail_fromname') : $current_user->full_name;
1469
1470                 if(empty($mail_fromaddress)) {
1471                         return false;
1472                 }
1473                 if(empty($mail_fromname)) {
1474                         return false;
1475                 }
1476
1477         $send_type = $current_user->getPreference('mail_sendtype') ;
1478                 if (!empty($send_type) && $send_type == "SMTP") {
1479                         $mail_smtpserver = $current_user->getPreference('mail_smtpserver');
1480                         $mail_smtpport = $current_user->getPreference('mail_smtpport');
1481                         $mail_smtpauth_req = $current_user->getPreference('mail_smtpauth_req');
1482                         $mail_smtpuser = $current_user->getPreference('mail_smtpuser');
1483                         $mail_smtppass = $current_user->getPreference('mail_smtppass');
1484                         if (empty($mail_smtpserver) ||
1485                                 empty($mail_smtpport) ||
1486                 (!empty($mail_smtpauth_req) && ( empty($mail_smtpuser) || empty($mail_smtppass)))
1487                         ) {
1488                                 return false;
1489                         }
1490                 }
1491                 return true;
1492         }
1493
1494         /**
1495          * outputs JS to set fields in the MassUpdate form in the "My Inbox" view
1496          */
1497         function js_set_archived() {
1498                 global $mod_strings;
1499                 $script = '
1500                 <script type="text/javascript" language="JavaScript"><!-- Begin
1501                         function setArchived() {
1502                                 var form = document.getElementById("MassUpdate");
1503                                 var status = document.getElementById("mass_status");
1504                                 var ok = false;
1505
1506                                 for(var i=0; i < form.elements.length; i++) {
1507                                         if(form.elements[i].name == "mass[]") {
1508                                                 if(form.elements[i].checked == true) {
1509                                                         ok = true;
1510                                                 }
1511                                         }
1512                                 }
1513
1514                                 if(ok == true) {
1515                                         var user = document.getElementById("mass_assigned_user_name");
1516                                         var team = document.getElementById("team");
1517
1518                                         user.value = "";
1519                                         for(var j=0; j<status.length; j++) {
1520                                                 if(status.options[j].value == "archived") {
1521                                                         status.options[j].selected = true;
1522                                                         status.selectedIndex = j; // for IE
1523                                                 }
1524                                         }
1525
1526                                         form.submit();
1527                                 } else {
1528                                         alert("'.$mod_strings['ERR_ARCHIVE_EMAIL'].'");
1529                                 }
1530
1531                         }
1532                 //  End --></script>';
1533                 return $script;
1534         }
1535
1536         /**
1537          * replaces the javascript in utils.php - more specialized
1538          */
1539         function u_get_clear_form_js($type='', $group='', $assigned_user_id='') {
1540                 $uType                          = '';
1541                 $uGroup                         = '';
1542                 $uAssigned_user_id      = '';
1543
1544                 if(!empty($type)) { $uType = '&type='.$type; }
1545                 if(!empty($group)) { $uGroup = '&group='.$group; }
1546                 if(!empty($assigned_user_id)) { $uAssigned_user_id = '&assigned_user_id='.$assigned_user_id; }
1547
1548                 $the_script = '
1549                 <script type="text/javascript" language="JavaScript"><!-- Begin
1550                         function clear_form(form) {
1551                                 var newLoc = "index.php?action=" + form.action.value + "&module=" + form.module.value + "&query=true&clear_query=true'.$uType.$uGroup.$uAssigned_user_id.'";
1552                                 if(typeof(form.advanced) != "undefined"){
1553                                         newLoc += "&advanced=" + form.advanced.value;
1554                                 }
1555                                 document.location.href= newLoc;
1556                         }
1557                 //  End --></script>';
1558                 return $the_script;
1559         }
1560
1561         function pickOneButton() {
1562                 global $theme;
1563                 global $mod_strings;
1564                 $out = '<div><input     title="'.$mod_strings['LBL_BUTTON_GRAB_TITLE'].'"
1565                                                 accessKey="'.$mod_strings['LBL_BUTTON_GRAB_KEY'].'"
1566                                                 class="button"
1567                                                 type="button" name="button"
1568                                                 onClick="window.location=\'index.php?module=Emails&action=Grab\';"
1569                                                 style="margin-bottom:2px"
1570                                                 value="  '.$mod_strings['LBL_BUTTON_GRAB'].'  "></div>';
1571                 return $out;
1572         }
1573
1574         /**
1575          * Determines what Editor (HTML or Plain-text) the current_user uses;
1576          * @return string Editor type
1577          */
1578         function getUserEditorPreference() {
1579                 global $sugar_config;
1580                 global $current_user;
1581
1582                 $editor = '';
1583
1584                 if(!isset($sugar_config['email_default_editor'])) {
1585                         $sugar_config = $current_user->setDefaultsInConfig();
1586                 }
1587
1588                 $userEditor = $current_user->getPreference('email_editor_option');
1589                 $systemEditor = $sugar_config['email_default_editor'];
1590
1591                 if($userEditor != '') {
1592                         $editor = $userEditor;
1593                 } else {
1594                         $editor = $systemEditor;
1595                 }
1596
1597                 return $editor;
1598         }
1599
1600         /**
1601          * takes the mess we pass from EditView and tries to create some kind of order
1602          * @param array addrs
1603          * @param array addrs_ids (from contacts)
1604          * @param array addrs_names (from contacts);
1605          * @param array addrs_emails (from contacts);
1606          * @return array Parsed assoc array to feed to PHPMailer
1607          */
1608         function parse_addrs($addrs, $addrs_ids, $addrs_names, $addrs_emails) {
1609                 // cn: bug 9406 - enable commas to separate email addresses
1610                 $addrs = str_replace(",", ";", $addrs);
1611
1612                 $ltgt = array('&lt;','&gt;');
1613                 $gtlt = array('<','>');
1614
1615                 $return                         = array();
1616                 $addrs                          = str_replace($ltgt, '', $addrs);
1617                 $addrs_arr                      = explode(";",$addrs);
1618                 $addrs_arr                      = $this->remove_empty_fields($addrs_arr);
1619                 $addrs_ids_arr          = explode(";",$addrs_ids);
1620                 $addrs_ids_arr          = $this->remove_empty_fields($addrs_ids_arr);
1621                 $addrs_emails_arr       = explode(";",$addrs_emails);
1622                 $addrs_emails_arr       = $this->remove_empty_fields($addrs_emails_arr);
1623                 $addrs_names_arr        = explode(";",$addrs_names);
1624                 $addrs_names_arr        = $this->remove_empty_fields($addrs_names_arr);
1625
1626                 ///////////////////////////////////////////////////////////////////////
1627                 ////    HANDLE EMAILS HAND-WRITTEN
1628                 $contactRecipients = array();
1629                 $knownEmails = array();
1630
1631                 foreach($addrs_arr as $i => $v) {
1632                         if(trim($v) == "")
1633                                 continue; // skip any "blanks" - will always have 1
1634
1635                         $recipient = array();
1636
1637                         //// get the email to see if we're dealing with a dupe
1638                         //// what crappy coding
1639                         preg_match("/[A-Z0-9._%-\']+@[A-Z0-9.-]+\.[A-Z]{2,}/i",$v, $match);
1640                         
1641
1642                         if(!empty($match[0]) && !in_array(trim($match[0]), $knownEmails)) {
1643                                 $knownEmails[] = $match[0];
1644                                 $recipient['email'] = $match[0];
1645
1646                                 //// handle the Display name
1647                                 $display = trim(str_replace($match[0], '', $v));
1648
1649                                 //// only trigger a "displayName" <email@address> when necessary
1650                                 if(isset($addrs_names_arr[$i])){
1651                                                 $recipient['display'] = $addrs_names_arr[$i];
1652                                 }
1653                                 else if(!empty($display)) {
1654                                         $recipient['display'] = $display;
1655                                 }
1656                                 if(isset($addrs_ids_arr[$i]) && $addrs_emails_arr[$i] == $match[0]){
1657                                         $recipient['contact_id'] = $addrs_ids_arr[$i];
1658                                 }
1659                                 $return[] = $recipient;
1660                         }
1661                 }
1662
1663                 return $return;
1664         }
1665
1666         function remove_empty_fields(&$arr) {
1667                 $newarr = array();
1668
1669                 foreach($arr as $field) {
1670                         $field = trim($field);
1671                         if(empty($field)) {
1672                                 continue;
1673                         }
1674                         array_push($newarr,$field);
1675                 }
1676                 return $newarr;
1677         }
1678
1679         /**
1680          * handles attachments of various kinds when sending email
1681          */
1682         function handleAttachments() {
1683
1684
1685
1686
1687                 global $mod_strings;
1688
1689         ///////////////////////////////////////////////////////////////////////////
1690         ////    ATTACHMENTS FROM DRAFTS
1691         if(($this->type == 'out' || $this->type == 'draft') && $this->status == 'draft' && isset($_REQUEST['record'])) {
1692             $this->getNotes($_REQUEST['record']); // cn: get notes from OLD email for use in new email
1693         }
1694         ////    END ATTACHMENTS FROM DRAFTS
1695         ///////////////////////////////////////////////////////////////////////////
1696
1697         ///////////////////////////////////////////////////////////////////////////
1698         ////    ATTACHMENTS FROM FORWARDS
1699         // Bug 8034 Jenny - Need the check for type 'draft' here to handle cases where we want to save
1700         // forwarded messages as drafts.  We still need to save the original message's attachments.
1701         if(($this->type == 'out' || $this->type == 'draft') &&
1702                 isset($_REQUEST['origType']) && $_REQUEST['origType'] == 'forward' &&
1703                 isset($_REQUEST['return_id']) && !empty($_REQUEST['return_id'])
1704         ) {
1705             $this->getNotes($_REQUEST['return_id'], true);
1706         }
1707
1708         // cn: bug 8034 - attachments from forward/replies lost when saving in draft
1709         if(isset($_REQUEST['prior_attachments']) && !empty($_REQUEST['prior_attachments']) && $this->new_with_id == true) {
1710                 $exIds = explode(",", $_REQUEST['prior_attachments']);
1711                 if(!isset($_REQUEST['template_attachment'])) {
1712                         $_REQUEST['template_attachment'] = array();
1713                 }
1714                 $_REQUEST['template_attachment'] = array_merge($_REQUEST['template_attachment'], $exIds);
1715         }
1716         ////    END ATTACHMENTS FROM FORWARDS
1717         ///////////////////////////////////////////////////////////////////////////
1718
1719                 ///////////////////////////////////////////////////////////////////////////
1720                 ////    ATTACHMENTS FROM TEMPLATES
1721                 // to preserve individual email integrity, we must dupe Notes and associated files
1722                 // for each outbound email - good for integrity, bad for filespace
1723                 if(isset($_REQUEST['template_attachment']) && !empty($_REQUEST['template_attachment'])) {
1724                         $removeArr = array();
1725                         $noteArray = array();
1726
1727                         if(isset($_REQUEST['temp_remove_attachment']) && !empty($_REQUEST['temp_remove_attachment'])) {
1728                                 $removeArr = $_REQUEST['temp_remove_attachment'];
1729                         }
1730
1731
1732                         foreach($_REQUEST['template_attachment'] as $noteId) {
1733                                 if(in_array($noteId, $removeArr)) {
1734                                         continue;
1735                                 }
1736                                 $noteTemplate = new Note();
1737                                 $noteTemplate->retrieve($noteId);
1738                                 $noteTemplate->id = create_guid();
1739                                 $noteTemplate->new_with_id = true; // duplicating the note with files
1740                                 $noteTemplate->parent_id = $this->id;
1741                                 $noteTemplate->parent_type = $this->module_dir;
1742                                 $noteTemplate->date_entered = '';
1743                                 $noteTemplate->save();
1744
1745                                 $noteFile = new UploadFile('none');
1746                                 $noteFile->duplicate_file($noteId, $noteTemplate->id, $noteTemplate->filename);
1747                                 $noteArray[] = $noteTemplate;
1748                         }
1749                         $this->attachments = array_merge($this->attachments, $noteArray);
1750                 }
1751                 ////    END ATTACHMENTS FROM TEMPLATES
1752                 ///////////////////////////////////////////////////////////////////////////
1753
1754                 ///////////////////////////////////////////////////////////////////////////
1755                 ////    ADDING NEW ATTACHMENTS
1756                 $max_files_upload = 10;
1757         // Jenny - Bug 8211 Since attachments for drafts have already been processed,
1758         // we don't need to re-process them.
1759         if($this->status != "draft") {
1760                 $notes_list = array();
1761                 if(!empty($this->id) && !$this->new_with_id) {
1762                         $note = new Note();
1763                         $where = "notes.parent_id='{$this->id}'";
1764                         $notes_list = $note->get_full_list("", $where, true);
1765                 }
1766                 $this->attachments = array_merge($this->attachments, $notes_list);
1767         }
1768                 // cn: Bug 5995 - rudimentary error checking
1769                 $filesError = array(
1770                         0 => 'UPLOAD_ERR_OK - There is no error, the file uploaded with success.',
1771                         1 => 'UPLOAD_ERR_INI_SIZE - The uploaded file exceeds the upload_max_filesize directive in php.ini.',
1772                         2 => 'UPLOAD_ERR_FORM_SIZE - The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.',
1773                         3 => 'UPLOAD_ERR_PARTIAL - The uploaded file was only partially uploaded.',
1774                         4 => 'UPLOAD_ERR_NO_FILE - No file was uploaded.',
1775                         5 => 'UNKNOWN ERROR',
1776                         6 => 'UPLOAD_ERR_NO_TMP_DIR - Missing a temporary folder. Introduced in PHP 4.3.10 and PHP 5.0.3.',
1777                         7 => 'UPLOAD_ERR_CANT_WRITE - Failed to write file to disk. Introduced in PHP 5.1.0.',
1778                 );
1779
1780                 for($i = 0; $i < $max_files_upload; $i++) {
1781                         // cn: Bug 5995 - rudimentary error checking
1782                         if (!isset($_FILES["email_attachment{$i}"])) {
1783                                 $GLOBALS['log']->debug("Email Attachment {$i} does not exist.");
1784                                 continue;
1785                         }
1786                         if($_FILES['email_attachment'.$i]['error'] != 0 && $_FILES['email_attachment'.$i]['error'] != 4) {
1787                                 $GLOBALS['log']->debug('Email Attachment could not be attach due to error: '.$filesError[$_FILES['email_attachment'.$i]['error']]);
1788                                 continue;
1789                         }
1790
1791                         $note = new Note();
1792                         $note->parent_id = $this->id;
1793                         $note->parent_type = $this->module_dir;
1794                         $upload_file = new UploadFile('email_attachment'.$i);
1795
1796                         if(empty($upload_file)) {
1797                                 continue;
1798                         }
1799
1800                         if(isset($_FILES['email_attachment'.$i]) && $upload_file->confirm_upload()) {
1801                                 $note->filename = $upload_file->get_stored_file_name();
1802                                 $note->file = $upload_file;
1803                                 $note->name = $mod_strings['LBL_EMAIL_ATTACHMENT'].': '.$note->file->original_file_name;
1804
1805                                 $this->attachments[] = $note;
1806                         }
1807                 }
1808
1809                 $this->saved_attachments = array();
1810                 foreach($this->attachments as $note) {
1811                         if(!empty($note->id)) {
1812                                 array_push($this->saved_attachments, $note);
1813                                 continue;
1814                         }
1815                         $note->parent_id = $this->id;
1816                         $note->parent_type = 'Emails';
1817                         $note->file_mime_type = $note->file->mime_type;
1818                         $note_id = $note->save();
1819
1820                         $this->saved_attachments[] = $note;
1821
1822                         $note->id = $note_id;
1823                         $note->file->final_move($note->id);
1824                 }
1825                 ////    END NEW ATTACHMENTS
1826                 ///////////////////////////////////////////////////////////////////////////
1827
1828                 ///////////////////////////////////////////////////////////////////////////
1829                 ////    ATTACHMENTS FROM DOCUMENTS
1830                 for($i=0; $i<10; $i++) {
1831                         if(isset($_REQUEST['documentId'.$i]) && !empty($_REQUEST['documentId'.$i])) {
1832                                 $doc = new Document();
1833                                 $docRev = new DocumentRevision();
1834                                 $docNote = new Note();
1835                                 $noteFile = new UploadFile('none');
1836
1837                                 $doc->retrieve($_REQUEST['documentId'.$i]);
1838                                 $docRev->retrieve($doc->document_revision_id);
1839
1840                                 $this->saved_attachments[] = $docRev;
1841
1842                                 // cn: bug 9723 - Emails with documents send GUID instead of Doc name
1843                                 $docNote->name = $docRev->getDocumentRevisionNameForDisplay();
1844                                 $docNote->filename = $docRev->filename;
1845                                 $docNote->description = $doc->description;
1846                                 $docNote->parent_id = $this->id;
1847                                 $docNote->parent_type = 'Emails';
1848                                 $docNote->file_mime_type = $docRev->file_mime_type;
1849                                 $docId = $docNote = $docNote->save();
1850
1851                                 $noteFile->duplicate_file($docRev->id, $docId, $docRev->filename);
1852                         }
1853                 }
1854
1855                 ////    END ATTACHMENTS FROM DOCUMENTS
1856                 ///////////////////////////////////////////////////////////////////////////
1857
1858                 ///////////////////////////////////////////////////////////////////////////
1859                 ////    REMOVE ATTACHMENTS
1860         if(isset($_REQUEST['remove_attachment']) && !empty($_REQUEST['remove_attachment'])) {
1861             foreach($_REQUEST['remove_attachment'] as $noteId) {
1862                 $q = 'UPDATE notes SET deleted = 1 WHERE id = \''.$noteId.'\'';
1863                 $this->db->query($q);
1864             }
1865         }
1866
1867         //this will remove attachments that have been selected to be removed from drafts.
1868         if(isset($_REQUEST['removeAttachment']) && !empty($_REQUEST['removeAttachment'])) {
1869             $exRemoved = explode('::', $_REQUEST['removeAttachment']);
1870             foreach($exRemoved as $noteId) {
1871                 $q = 'UPDATE notes SET deleted = 1 WHERE id = \''.$noteId.'\'';
1872                 $this->db->query($q);
1873             }
1874         }
1875                 ////    END REMOVE ATTACHMENTS
1876                 ///////////////////////////////////////////////////////////////////////////
1877         }
1878
1879
1880         /**
1881          * Determines if an email body (HTML or Plain) has a User signature already in the content
1882          * @param array Array of signatures
1883          * @return bool
1884          */
1885         function hasSignatureInBody($sig) {
1886                 // strpos can't handle line breaks - normalize
1887                 $html = $this->removeAllNewlines($this->description_html);
1888                 $htmlSig = $this->removeAllNewlines($sig['signature_html']);
1889                 $plain = $this->removeAllNewlines($this->description);
1890                 $plainSig = $this->removeAllNewlines($sig['signature']);
1891
1892                 // cn: bug 11621 - empty sig triggers notice error
1893                 if(!empty($htmlSig) && false !== strpos($html, $htmlSig)) {
1894                         return true;
1895                 } elseif(!empty($plainSig) && false !== strpos($plain, $plainSig)) {
1896                         return true;
1897                 } else {
1898                         return false;
1899                 }
1900         }
1901
1902         /**
1903          * internal helper
1904          * @param string String to be normalized
1905          * @return string
1906          */
1907         function removeAllNewlines($str) {
1908                 $bad = array("\r\n", "\n\r", "\n", "\r");
1909                 $good = array('', '', '', '');
1910
1911                 return str_replace($bad, $good, strip_tags(br2nl(from_html($str))));
1912         }
1913
1914
1915
1916         /**
1917          * Set navigation anchors to aid DetailView record navigation (VCR buttons)
1918          * @param string uri The URI from the referring page (always ListView)
1919          * @return array start Array of the URI broken down with a special "current_view" for My Inbox Navs
1920          */
1921         function getStartPage($uri) {
1922                 if(strpos($uri, '&')) { // "&" to ensure that we can explode the GET vars - else we're gonna trigger a Notice error
1923                         $serial = substr($uri, (strpos($uri, '?')+1), strlen($uri));
1924                         $exUri = explode('&', $serial);
1925                         $start = array('module' => '', 'action' => '', 'group' => '', 'record' => '', 'type' => '');
1926
1927                         foreach($exUri as $k => $pair) {
1928                                 $exPair = explode('=', $pair);
1929                                 $start[$exPair[0]] = $exPair[1];
1930                         }
1931
1932                         // specific views for current_user
1933                         if(isset($start['assigned_user_id'])) {
1934                                 $start['current_view'] = "{$start['action']}&module={$start['module']}&assigned_user_id={$start['assigned_user_id']}&type={$start['type']}";
1935                         }
1936
1937                         return $start;
1938                 } else {
1939                         return array();
1940                 }
1941         }
1942
1943         /**
1944          * preps SMTP info for email transmission
1945          * @param object mail SugarPHPMailer object
1946          * @param string mailer_id
1947          * @param string ieId
1948          * @return object mail SugarPHPMailer object
1949          */
1950         function setMailer($mail, $mailer_id='', $ieId='') {
1951                 global $current_user;
1952
1953                 require_once("include/OutboundEmail/OutboundEmail.php");
1954                 $oe = new OutboundEmail();
1955                 $oe = $oe->getInboundMailerSettings($current_user, $mailer_id, $ieId);
1956
1957                 // ssl or tcp - keeping outside isSMTP b/c a default may inadvertantly set ssl://
1958                 $mail->protocol = ($oe->mail_smtpssl) ? "ssl://" : "tcp://";
1959         if($oe->mail_sendtype == "SMTP")
1960         {
1961                 //Set mail send type information
1962                 $mail->Mailer = "smtp";
1963                 $mail->Host = $oe->mail_smtpserver;
1964                 $mail->Port = $oe->mail_smtpport;
1965             if ($oe->mail_smtpssl == 1) {
1966                 $mail->SMTPSecure = 'ssl';
1967             } // if
1968             if ($oe->mail_smtpssl == 2) {
1969                 $mail->SMTPSecure = 'tls';
1970             } // if
1971
1972                 if($oe->mail_smtpauth_req) {
1973                         $mail->SMTPAuth = TRUE;
1974                         $mail->Username = $oe->mail_smtpuser;
1975                         $mail->Password = $oe->mail_smtppass;
1976                 }
1977         }
1978         else
1979                         $mail->Mailer = "sendmail";
1980
1981                 $mail->oe = $oe;
1982                 return $mail;
1983         }
1984
1985         /**
1986          * preps SugarPHPMailer object for HTML or Plain text sends
1987          * @param object SugarPHPMailer instance
1988          */
1989         function handleBody($mail) {
1990                 global $current_user;
1991                 global $sugar_config;
1992                 ///////////////////////////////////////////////////////////////////////
1993                 ////    HANDLE EMAIL FORMAT PREFERENCE
1994                 // the if() below is HIGHLY dependent on the Javascript unchecking the Send HTML Email box
1995                 // HTML email
1996                 if( (isset($_REQUEST['setEditor']) /* from Email EditView navigation */
1997                         && $_REQUEST['setEditor'] == 1
1998                         && trim($_REQUEST['description_html']) != '')
1999                         || trim($this->description_html) != '' /* from email templates */
2000             && $current_user->getPreference('email_editor_option', 'global') !== 'plain' //user preference is not set to plain text
2001                 ) {
2002                     $this->handleBodyInHTMLformat($mail);
2003                 } else {
2004                         // plain text only
2005                         $this->description_html = '';
2006                         $mail->IsHTML(false);
2007                         $plainText = from_html($this->description);
2008                         $plainText = str_replace("&nbsp;", " ", $plainText);
2009                         $plainText = str_replace("</p>", "</p><br />", $plainText);
2010                         $plainText = strip_tags(br2nl($plainText));
2011                         $plainText = str_replace("&amp;", "&", $plainText);
2012             $plainText = str_replace("&#39;", "'", $plainText);
2013                         $mail->Body = wordwrap($plainText, 996);
2014                         $mail->Body = $this->decodeDuringSend($mail->Body);
2015                         $this->description = $mail->Body;
2016                 }
2017
2018                 // wp: if plain text version has lines greater than 998, use base64 encoding
2019                 foreach(explode("\n", ($mail->ContentType == "text/html") ? $mail->AltBody : $mail->Body) as $line) {
2020                         if(strlen($line) > 998) {
2021                                 $mail->Encoding = 'base64';
2022                                 break;
2023                         }
2024                 }
2025                 ////    HANDLE EMAIL FORMAT PREFERENCE
2026                 ///////////////////////////////////////////////////////////////////////
2027
2028                 return $mail;
2029         }
2030
2031         /**
2032          * Retrieve function from handlebody() to unit test easily
2033          * @param $mail
2034          * @return formatted $mail body
2035          */
2036         function handleBodyInHTMLformat($mail) {
2037                 global $current_user;
2038                 global $sugar_config;
2039                 // wp: if body is html, then insert new lines at 996 characters. no effect on client side
2040                 // due to RFC 2822 which limits email lines to 998
2041                 $mail->IsHTML(true);
2042                 $body = from_html(wordwrap($this->description_html, 996));
2043                 $mail->Body = $body;
2044
2045                 // cn: bug 9725
2046                 // new plan is to use the selected type (html or plain) to fill the other
2047                 $plainText = from_html($this->description_html);
2048                 $plainText = strip_tags(br2nl($plainText));
2049                 $mail->AltBody = $plainText;
2050                 $this->description = $plainText;
2051
2052                 $fileBasePath = "{$sugar_config['cache_dir']}images/";
2053                 $filePatternSearch = "{$sugar_config['cache_dir']}";
2054                 $filePatternSearch = str_replace("/", "\/", $filePatternSearch);
2055                 $filePatternSearch = $filePatternSearch . "images\/";
2056                 if(strpos($mail->Body, "\"{$fileBasePath}") !== FALSE)
2057                 {  //cache/images
2058                         $matches = array();
2059                         preg_match_all("/{$filePatternSearch}.+?\"/i", $mail->Body, $matches);
2060                         foreach($matches[0] as $match) {
2061                                 $filename = str_replace($fileBasePath, '', $match);
2062                                 $filename = urldecode(substr($filename, 0, -1));
2063                                 $cid = $filename;
2064                                 $file_location = clean_path(getcwd()."/{$sugar_config['cache_dir']}images/{$filename}");
2065                                 $mime_type = "image/".strtolower(substr($filename, strrpos($filename, ".")+1, strlen($filename)));
2066
2067                                 if(file_exists($file_location)) {
2068                                         $mail->AddEmbeddedImage($file_location, $cid, $filename, 'base64', $mime_type);
2069                                 }
2070                         }
2071
2072                         //replace references to cache with cid tag
2073                         $mail->Body = str_replace("/" . $fileBasePath,'cid:',$mail->Body);
2074                         $mail->Body = str_replace($fileBasePath,'cid:',$mail->Body);
2075                         // remove bad img line from outbound email
2076                         $regex = '#<img[^>]+src[^=]*=\"\/([^>]*?[^>]*)>#sim';
2077                         $mail->Body = preg_replace($regex, '', $mail->Body);
2078                 }
2079                 $fileBasePath = "{$sugar_config['upload_dir']}";
2080                 $filePatternSearch = "{$sugar_config['upload_dir']}";
2081                 $filePatternSearch = str_replace("/", "\/", $filePatternSearch);
2082                 if(strpos($mail->Body, "\"{$fileBasePath}") !== FALSE)
2083                 {
2084                         $matches = array();
2085                         preg_match_all("/{$filePatternSearch}.+?\"/i", $mail->Body, $matches);
2086                         foreach($matches[0] as $match) {
2087                                 $filename = str_replace($fileBasePath, '', $match);
2088                                 $filename = urldecode(substr($filename, 0, -1));
2089                                 $cid = $filename;
2090                                 $file_location = clean_path(getcwd()."/{$sugar_config['upload_dir']}{$filename}");
2091                                 $mime_type = "image/".strtolower(substr($filename, strrpos($filename, ".")+1, strlen($filename)));
2092
2093                                 if(file_exists($file_location)) {
2094                                         $mail->AddEmbeddedImage($file_location, $cid, $filename, 'base64', $mime_type);
2095                                 }
2096                         }
2097
2098                         //replace references to cache with cid tag
2099                         $mail->Body = str_replace("/" . $fileBasePath,'cid:',$mail->Body);
2100                         $mail->Body = str_replace($fileBasePath,'cid:',$mail->Body);
2101
2102                         // remove bad img line from outbound email
2103                         $regex = '#<img[^>]+src[^=]*=\"\/([^>]*?[^>]*)>#sim';
2104                         $mail->Body = preg_replace($regex, '', $mail->Body);
2105                 }
2106
2107                 //Replace any embeded images using the secure entryPoint for src url.
2108                 $noteImgRegex = "/<img[^>]*[\s]+src[^=]*=\"index.php\?entryPoint=download\&amp;id=([^\&]*)[^>]*>/im";
2109         $embededImageMatches = array();
2110         preg_match_all($noteImgRegex, $mail->Body, $embededImageMatches,PREG_SET_ORDER);
2111
2112         foreach ($embededImageMatches as $singleMatch )
2113         {
2114             $fullMatch = $singleMatch[0];
2115             $noteId = $singleMatch[1];
2116             $cid = $noteId;
2117             $filename = $noteId;
2118
2119             //Retrieve note for mimetype
2120             $tmpNote = new Note();
2121             $tmpNote->retrieve($noteId);
2122             //Replace the src part of img tag with new cid tag
2123             $cidRegex = "/src=\"([^\"]*)\"/im";
2124             $replaceMatch = preg_replace($cidRegex, "src=\"cid:$noteId\"", $fullMatch);
2125
2126             //Replace the body, old tag for new tag
2127             $mail->Body = str_replace($fullMatch, $replaceMatch, $mail->Body);
2128
2129             //Attach the file
2130             $file_location = clean_path(getcwd()."/{$sugar_config['upload_dir']}{$noteId}");
2131
2132             if(file_exists($file_location))
2133                                         $mail->AddEmbeddedImage($file_location, $cid, $filename, 'base64', $tmpNote->file_mime_type);
2134         }
2135         //End Replace
2136
2137
2138                 $mail->Body = from_html($mail->Body);
2139         }
2140
2141         /**
2142          * Sends Email
2143          * @return bool True on success
2144          */
2145         function send() {
2146                 global $mod_strings,$app_strings;
2147                 global $current_user;
2148                 global $sugar_config;
2149                 global $locale;
2150         $OBCharset = $locale->getPrecedentPreference('default_email_charset');
2151                 $mail = new SugarPHPMailer();
2152
2153                 foreach ($this->to_addrs_arr as $addr_arr) {
2154                         if ( empty($addr_arr['display'])) {
2155                                 $mail->AddAddress($addr_arr['email'], "");
2156                         } else {
2157                                 $mail->AddAddress($addr_arr['email'],$locale->translateCharsetMIME(trim( $addr_arr['display']), 'UTF-8', $OBCharset));
2158                         }
2159                 }
2160                 foreach ($this->cc_addrs_arr as $addr_arr) {
2161                         if ( empty($addr_arr['display'])) {
2162                                 $mail->AddCC($addr_arr['email'], "");
2163                         } else {
2164                                 $mail->AddCC($addr_arr['email'],$locale->translateCharsetMIME(trim($addr_arr['display']), 'UTF-8', $OBCharset));
2165                         }
2166                 }
2167
2168                 foreach ($this->bcc_addrs_arr as $addr_arr) {
2169                         if ( empty($addr_arr['display'])) {
2170                                 $mail->AddBCC($addr_arr['email'], "");
2171                         } else {
2172                                 $mail->AddBCC($addr_arr['email'],$locale->translateCharsetMIME(trim($addr_arr['display']), 'UTF-8', $OBCharset));
2173                         }
2174                 }
2175
2176                 $mail = $this->setMailer($mail);
2177
2178                 // FROM ADDRESS
2179                 if(!empty($this->from_addr)) {
2180                         $mail->From = $this->from_addr;
2181                 } else {
2182                         $mail->From = $current_user->getPreference('mail_fromaddress');
2183                         $this->from_addr = $mail->From;
2184                 }
2185                 // FROM NAME
2186                 if(!empty($this->from_name)) {
2187                         $mail->FromName = $this->from_name;
2188                 } else {
2189                         $mail->FromName =  $current_user->getPreference('mail_fromname');
2190                         $this->from_name = $mail->FromName;
2191                 }
2192
2193                 //Reply to information for case create and autoreply.
2194                 if(!empty($this->reply_to_name)) {
2195                         $ReplyToName = $this->reply_to_name;
2196                 } else {
2197                         $ReplyToName = $mail->FromName;
2198                 }
2199                 if(!empty($this->reply_to_addr)) {
2200                         $ReplyToAddr = $this->reply_to_addr;
2201                 } else {
2202                         $ReplyToAddr = $mail->From;
2203                 }
2204                 $mail->Sender = $mail->From; /* set Return-Path field in header to reduce spam score in emails sent via Sugar's Email module */
2205                 $mail->AddReplyTo($ReplyToAddr,$locale->translateCharsetMIME(trim($ReplyToName), 'UTF-8', $OBCharset));
2206
2207                 //$mail->Subject = html_entity_decode($this->name, ENT_QUOTES, 'UTF-8');
2208                 $mail->Subject = $this->name;
2209
2210                 ///////////////////////////////////////////////////////////////////////
2211                 ////    ATTACHMENTS
2212                 foreach($this->saved_attachments as $note) {
2213                         $mime_type = 'text/plain';
2214                         if($note->object_name == 'Note') {
2215                                 if(!empty($note->file->temp_file_location) && is_file($note->file->temp_file_location)) { // brandy-new file upload/attachment
2216                                         $file_location = $sugar_config['upload_dir'].$note->id;
2217                                         $filename = $note->file->original_file_name;
2218                                         $mime_type = $note->file->mime_type;
2219                                 } else { // attachment coming from template/forward
2220                                         $file_location = rawurldecode(UploadFile::get_file_path($note->filename,$note->id));
2221                                         // cn: bug 9723 - documents from EmailTemplates sent with Doc Name, not file name.
2222                                         $filename = !empty($note->filename) ? $note->filename : $note->name;
2223                                         $mime_type = $note->file_mime_type;
2224                                 }
2225                         } elseif($note->object_name == 'DocumentRevision') { // from Documents
2226                                 $filePathName = $note->id;
2227                                 // cn: bug 9723 - Emails with documents send GUID instead of Doc name
2228                                 $filename = $note->getDocumentRevisionNameForDisplay();
2229                                 $file_location = getcwd().'/'.$GLOBALS['sugar_config']['upload_dir'].$filePathName;
2230                                 $mime_type = $note->file_mime_type;
2231                         }
2232
2233                         // strip out the "Email attachment label if exists
2234                         $filename = str_replace($mod_strings['LBL_EMAIL_ATTACHMENT'].': ', '', $filename);
2235
2236                         //is attachment in our list of bad files extensions?  If so, append .txt to file location
2237                         //get position of last "." in file name
2238                         $file_ext_beg = strrpos($file_location,".");
2239                         $file_ext = "";
2240                         //get file extension
2241                         if($file_ext_beg >0){
2242                                 $file_ext = substr($file_location, $file_ext_beg+1 );
2243                         }
2244                         //check to see if this is a file with extension located in "badext"
2245                         foreach($sugar_config['upload_badext'] as $badExt) {
2246                         if(strtolower($file_ext) == strtolower($badExt)) {
2247                                 //if found, then append with .txt to filename and break out of lookup
2248                                 //this will make sure that the file goes out with right extension, but is stored
2249                                 //as a text in db.
2250                                 $file_location = $file_location . ".txt";
2251                                 break; // no need to look for more
2252                         }
2253                 }
2254                         $mail->AddAttachment($file_location,$locale->translateCharsetMIME(trim($filename), 'UTF-8', $OBCharset), 'base64', $mime_type);
2255
2256                         // embedded Images
2257                         if($note->embed_flag == true) {
2258                                 $cid = $filename;
2259                                 $mail->AddEmbeddedImage($file_location, $cid, $filename, 'base64',$mime_type);
2260                         }
2261                 }
2262                 ////    END ATTACHMENTS
2263                 ///////////////////////////////////////////////////////////////////////
2264
2265                 $mail = $this->handleBody($mail);
2266
2267                 $GLOBALS['log']->debug('Email sending --------------------- ');
2268
2269                 ///////////////////////////////////////////////////////////////////////
2270                 ////    I18N TRANSLATION
2271                 $mail->prepForOutbound();
2272                 ////    END I18N TRANSLATION
2273                 ///////////////////////////////////////////////////////////////////////
2274
2275                 if($mail->Send()) {
2276                         ///////////////////////////////////////////////////////////////////
2277                         ////    INBOUND EMAIL HANDLING
2278                         // mark replied
2279                         if(!empty($_REQUEST['inbound_email_id'])) {
2280                                 $ieMail = new Email();
2281                                 $ieMail->retrieve($_REQUEST['inbound_email_id']);
2282                                 $ieMail->status = 'replied';
2283                                 $ieMail->save();
2284                         }
2285                         $GLOBALS['log']->debug(' --------------------- buh bye -- sent successful');
2286                         ////    END INBOUND EMAIL HANDLING
2287                         ///////////////////////////////////////////////////////////////////
2288                         return true;
2289                 }
2290             $GLOBALS['log']->debug($app_strings['LBL_EMAIL_ERROR_PREPEND'].$mail->ErrorInfo);
2291                 return false;
2292         }
2293
2294
2295         function listviewACLHelper(){
2296                 $array_assign = parent::listviewACLHelper();
2297                 $is_owner = false;
2298                 if(!empty($this->parent_name)){
2299
2300                         if(!empty($this->parent_name_owner)){
2301                                 global $current_user;
2302                                 $is_owner = $current_user->id == $this->parent_name_owner;
2303                         }
2304                 }
2305                 if(!ACLController::moduleSupportsACL($this->parent_type) || ACLController::checkAccess($this->parent_type, 'view', $is_owner)){
2306                         $array_assign['PARENT'] = 'a';
2307                 } else {
2308                         $array_assign['PARENT'] = 'span';
2309                 }
2310                 $is_owner = false;
2311                 if(!empty($this->contact_name)) {
2312                         if(!empty($this->contact_name_owner)) {
2313                                 global $current_user;
2314                                 $is_owner = $current_user->id == $this->contact_name_owner;
2315                         }
2316                 }
2317                 if(ACLController::checkAccess('Contacts', 'view', $is_owner)) {
2318                         $array_assign['CONTACT'] = 'a';
2319                 } else {
2320                         $array_assign['CONTACT'] = 'span';
2321                 }
2322
2323                 return $array_assign;
2324         }
2325
2326         function getSystemDefaultEmail() {
2327                 $email = array();
2328
2329                 $r1 = $this->db->query('SELECT config.value FROM config WHERE name=\'fromaddress\'');
2330                 $r2 = $this->db->query('SELECT config.value FROM config WHERE name=\'fromname\'');
2331                 $a1 = $this->db->fetchByAssoc($r1);
2332                 $a2 = $this->db->fetchByAssoc($r2);
2333
2334                 $email['email'] = $a1['value'];
2335                 $email['name']  = $a2['value'];
2336
2337                 return $email;
2338         }
2339
2340
2341     function create_new_list_query($order_by, $where,$filter=array(),$params=array(), $show_deleted = 0,$join_type='', $return_array = false,$parentbean=null, $singleSelect = false) {
2342
2343                 if ($return_array) {
2344                         return parent::create_new_list_query($order_by, $where,$filter,$params, $show_deleted,$join_type, $return_array,$parentbean, $singleSelect);
2345                 }
2346         $custom_join = $this->custom_fields->getJOIN();
2347
2348                 $query = "SELECT ".$this->table_name.".*, users.user_name as assigned_user_name\n";
2349
2350         if($custom_join){
2351                         $query .= $custom_join['select'];
2352                 }
2353         $query .= " FROM emails\n";
2354         if ($where != "" && (strpos($where, "contacts.first_name") > 0))  {
2355                         $query .= " LEFT JOIN emails_beans ON emails.id = emails_beans.email_id\n";
2356         }
2357
2358         $query .= " LEFT JOIN users ON emails.assigned_user_id=users.id \n";
2359         if ($where != "" && (strpos($where, "contacts.first_name") > 0))  {
2360
2361         $query .= " JOIN contacts ON contacts.id= emails_beans.bean_id AND emails_beans.bean_module='Contacts' and contacts.deleted=0 \n";
2362         }
2363
2364                 if($custom_join){
2365                         $query .= $custom_join['join'];
2366                 }
2367
2368                 if($show_deleted == 0) {
2369                         $where_auto = " emails.deleted=0 \n";
2370                 }else if($show_deleted == 1){
2371                         $where_auto = " emails.deleted=1 \n";
2372                 }
2373
2374         if($where != "")
2375                         $query .= "WHERE $where AND ".$where_auto;
2376                 else
2377                         $query .= "WHERE ".$where_auto;
2378
2379                 if($order_by != "")
2380                         $query .= " ORDER BY $order_by";
2381                 else
2382                         $query .= " ORDER BY date_sent DESC";
2383
2384                 return $query;
2385     } // fn
2386
2387
2388         function fill_in_additional_list_fields() {
2389                 global $timedate;
2390                 $this->fill_in_additional_detail_fields();
2391
2392                 $this->link_action = 'DetailView';
2393                 ///////////////////////////////////////////////////////////////////////
2394                 //populate attachment_image, used to display attachment icon.
2395                 $query =  "select 1 from notes where notes.parent_id = '$this->id' and notes.deleted = 0";
2396                 $result =$this->db->query($query,true," Error filling in additional list fields: ");
2397
2398                 $row = $this->db->fetchByAssoc($result);
2399
2400                 if ($row !=null) {
2401                         $this->attachment_image = SugarThemeRegistry::current()->getImage('attachment',"","","");
2402                 } else {
2403                         $this->attachment_image = SugarThemeRegistry::current()->getImage('blank',"","","");
2404                 }
2405                 ///////////////////////////////////////////////////////////////////////
2406                 if(empty($this->contact_id) && !empty($this->parent_id) && !empty($this->parent_type) && $this->parent_type === 'Contacts' && !empty($this->parent_name) ){
2407                         $this->contact_id = $this->parent_id;
2408                         $this->contact_name = $this->parent_name;
2409                 }
2410         }
2411
2412         function fill_in_additional_detail_fields() {
2413                 global $app_list_strings,$mod_strings;
2414                 // Fill in the assigned_user_name
2415                 $this->assigned_user_name = get_assigned_user_name($this->assigned_user_id, '');
2416                 //if ($this->parent_type == 'Contacts') {
2417                         $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 ";
2418                         $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";
2419                         if(!empty($this->parent_id)){
2420                                 $query .= " AND contacts.id= '".$this->parent_id."' ";
2421                         }else if(!empty($_REQUEST['record'])){
2422                                 $query .= " AND contacts.id= '".$_REQUEST['record']."' ";
2423                         }
2424                         $result =$this->db->query($query,true," Error filling in additional detail fields: ");
2425
2426                         // Get the id and the name.
2427                         $row = $this->db->fetchByAssoc($result);
2428                         if($row != null)
2429                         {
2430
2431                                 $contact = new Contact();
2432                                 $contact->retrieve($row['id']);
2433                                 $this->contact_name = $contact->full_name;
2434                                 $this->contact_phone = $row['phone_work'];
2435                                 $this->contact_id = $row['id'];
2436                                 $this->contact_email = $contact->emailAddress->getPrimaryAddress($contact);
2437                                 $this->contact_name_owner = $row['contact_name_owner'];
2438                                 $this->contact_name_mod = $row['contact_name_mod'];
2439                                 $GLOBALS['log']->debug("Call($this->id): contact_name = $this->contact_name");
2440                                 $GLOBALS['log']->debug("Call($this->id): contact_phone = $this->contact_phone");
2441                                 $GLOBALS['log']->debug("Call($this->id): contact_id = $this->contact_id");
2442                                 $GLOBALS['log']->debug("Call($this->id): contact_email1 = $this->contact_email");
2443                         }
2444                         else {
2445                                 $this->contact_name = '';
2446                                 $this->contact_phone = '';
2447                                 $this->contact_id = '';
2448                                 $this->contact_email = '';
2449                                 $this->contact_name_owner = '';
2450                                 $this->contact_name_mod = '';
2451                                 $GLOBALS['log']->debug("Call($this->id): contact_name = $this->contact_name");
2452                                 $GLOBALS['log']->debug("Call($this->id): contact_phone = $this->contact_phone");
2453                                 $GLOBALS['log']->debug("Call($this->id): contact_id = $this->contact_id");
2454                                 $GLOBALS['log']->debug("Call($this->id): contact_email1 = $this->contact_email");
2455                         }
2456                 //}
2457                 $this->created_by_name = get_assigned_user_name($this->created_by);
2458                 $this->modified_by_name = get_assigned_user_name($this->modified_user_id);
2459
2460                 $this->link_action = 'DetailView';
2461
2462                 if(!empty($this->type)) {
2463                         if($this->type == 'out' && $this->status == 'send_error') {
2464                                 $this->type_name = $mod_strings['LBL_NOT_SENT'];
2465                         } else {
2466                                 $this->type_name = $app_list_strings['dom_email_types'][$this->type];
2467                         }
2468
2469                         if(($this->type == 'out' && $this->status == 'send_error') || $this->type == 'draft') {
2470                                 $this->link_action = 'EditView';
2471                         }
2472                 }
2473
2474                 //todo this  isset( $app_list_strings['dom_email_status'][$this->status]) is hack for 3261.
2475                 if(!empty($this->status) && isset( $app_list_strings['dom_email_status'][$this->status])) {
2476                         $this->status_name = $app_list_strings['dom_email_status'][$this->status];
2477                 }
2478
2479                 if ( empty($this->name ) &&  empty($_REQUEST['record'])) {
2480                         $this->name = $mod_strings['LBL_NO_SUBJECT'];
2481                 }
2482
2483                 $this->fill_in_additional_parent_fields();
2484         }
2485
2486
2487
2488         function create_export_query(&$order_by, &$where) {
2489                 $contact_required = stristr($where, "contacts");
2490                 $custom_join = $this->custom_fields->getJOIN(true, true,$where);
2491
2492                 if($contact_required) {
2493                         $query = "SELECT emails.*, contacts.first_name, contacts.last_name";
2494                         if($custom_join) {
2495                                 $query .= $custom_join['select'];
2496                         }
2497
2498                         $query .= " FROM contacts, emails, emails_contacts ";
2499                         $where_auto = "emails_contacts.contact_id = contacts.id AND emails_contacts.email_id = emails.id AND emails.deleted=0 AND contacts.deleted=0";
2500                 } else {
2501                         $query = 'SELECT emails.*';
2502                         if($custom_join) {
2503                                 $query .= $custom_join['select'];
2504                         }
2505
2506             $query .= ' FROM emails ';
2507             $where_auto = "emails.deleted=0";
2508                 }
2509
2510                 if($custom_join){
2511                         $query .= $custom_join['join'];
2512                 }
2513
2514                 if($where != "")
2515                         $query .= "where $where AND ".$where_auto;
2516         else
2517                         $query .= "where ".$where_auto;
2518
2519         if($order_by != "")
2520                         $query .= " ORDER BY $order_by";
2521         else
2522                         $query .= " ORDER BY emails.name";
2523         return $query;
2524     }
2525
2526         function get_list_view_data() {
2527                 global $app_list_strings;
2528                 global $theme;
2529                 global $current_user;
2530                 global $timedate;
2531                 global $mod_strings;
2532
2533                 $email_fields = $this->get_list_view_array();
2534                 $this->retrieveEmailText();
2535                 $email_fields['FROM_ADDR'] = $this->from_addr_name;
2536                 $mod_strings = return_module_language($GLOBALS['current_language'], 'Emails'); // hard-coding for Home screen ListView
2537
2538                 if($this->status != 'replied') {
2539                         $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>';
2540                         $email_fields['STATUS'] = ($email_fields['REPLY_TO_STATUS'] == 1 ? $mod_strings['LBL_REPLIED'] : $email_fields['STATUS']);
2541                 } else {
2542                         $email_fields['QUICK_REPLY'] = $mod_strings['LBL_REPLIED'];
2543                 }
2544                 if(!empty($this->parent_type)) {
2545                         $email_fields['PARENT_MODULE'] = $this->parent_type;
2546                 } else {
2547                         switch($this->intent) {
2548                                 case 'support':
2549                                         $email_fields['CREATE_RELATED'] = '<a href="index.php?module=Cases&action=EditView&inbound_email_id='.$this->id.'" ><img border="0" src="'.SugarThemeRegistry::current()->getImageURL('CreateCases.gif').'">'.$mod_strings['LBL_CREATE_CASE'].'</a>';
2550                                 break;
2551
2552                                 case 'sales':
2553                                         $email_fields['CREATE_RELATED'] = '<a href="index.php?module=Leads&action=EditView&inbound_email_id='.$this->id.'" ><img border="0" src="'.SugarThemeRegistry::current()->getImageURL('CreateLeads.gif').'">'.$mod_strings['LBL_CREATE_LEAD'].'</a>';
2554                                 break;
2555
2556                                 case 'contact':
2557                                         $email_fields['CREATE_RELATED'] = '<a href="index.php?module=Contacts&action=EditView&inbound_email_id='.$this->id.'" ><img border="0" src="'.SugarThemeRegistry::current()->getImageURL('CreateContacts.gif').'">'.$mod_strings['LBL_CREATE_CONTACT'].'</a>';
2558                                 break;
2559
2560                                 case 'bug':
2561                                         $email_fields['CREATE_RELATED'] = '<a href="index.php?module=Bugs&action=EditView&inbound_email_id='.$this->id.'" ><img border="0" src="'.SugarThemeRegistry::current()->getImageURL('CreateBugs.gif').'">'.$mod_strings['LBL_CREATE_BUG'].'</a>';
2562                                 break;
2563
2564                                 case 'task':
2565                                         $email_fields['CREATE_RELATED'] = '<a href="index.php?module=Tasks&action=EditView&inbound_email_id='.$this->id.'" ><img border="0" src="'.SugarThemeRegistry::current()->getImageURL('CreateTasks.gif').'">'.$mod_strings['LBL_CREATE_TASK'].'</a>';
2566                                 break;
2567
2568                                 case 'bounce':
2569                                 break;
2570
2571                                 case 'pick':
2572                                 // break;
2573
2574                                 case 'info':
2575                                 //break;
2576
2577                                 default:
2578                                         $email_fields['CREATE_RELATED'] = $this->quickCreateForm();
2579                                 break;
2580                         }
2581
2582                 }
2583
2584                 //BUG 17098 - MFH changed $this->from_addr to $this->to_addrs
2585                 $email_fields['CONTACT_NAME']           = empty($this->contact_name) ? '</a>'.$this->trimLongTo($this->to_addrs).'<a>' : $this->contact_name;
2586                 $email_fields['CONTACT_ID']             = empty($this->contact_id) ? '' : $this->contact_id;
2587                 $email_fields['ATTACHMENT_IMAGE']       = $this->attachment_image;
2588                 $email_fields['LINK_ACTION']            = $this->link_action;
2589
2590         if(isset($this->type_name))
2591                 $email_fields['TYPE_NAME'] = $this->type_name;
2592
2593                 return $email_fields;
2594         }
2595
2596     function quickCreateForm() {
2597         global $mod_strings, $app_strings, $currentModule, $current_language;
2598
2599         // Coming from the home page via Dashlets
2600         if($currentModule != 'Email')
2601                 $mod_strings = return_module_language($current_language, 'Emails');
2602         return $mod_strings['LBL_QUICK_CREATE']."&nbsp;<a id='$this->id' onclick='return quick_create_overlib(\"{$this->id}\", \"".SugarThemeRegistry::current()->__toString()."\");' href=\"#\" >".SugarThemeRegistry::current()->getImage("advanced_search","alt='".$mod_strings['LBL_QUICK_CREATE']."'  border='0' align='absmiddle'")."</a>";
2603     }
2604
2605     /**
2606      * Searches all imported emails and returns the result set as an array.
2607      *
2608      */
2609     function searchImportedEmails($sort = '', $direction='')
2610     {
2611         require_once('include/TimeDate.php');
2612                 global $timedate;
2613                 global $current_user;
2614                 global $beanList;
2615                 global $sugar_config;
2616                 global $app_strings;
2617
2618                 $emailSettings = $current_user->getPreference('emailSettings', 'Emails');
2619                 // cn: default to a low number until user specifies otherwise
2620                 if(empty($emailSettings['showNumInList']))
2621                         $pageSize = 20;
2622         else
2623             $pageSize = $emailSettings['showNumInList'];
2624
2625         if( isset($_REQUEST['start']) && isset($_REQUEST['limit']) )
2626                $page = ceil($_REQUEST['start'] / $_REQUEST['limit']) + 1;
2627             else
2628                $page = 1;
2629
2630              //Determine sort ordering
2631
2632              //Sort ordering parameters in the request do not coincide with actual column names
2633              //so we need to remap them.
2634              $hrSortLocal = array(
2635             'flagged' => 'type',
2636             'status'  => 'reply_to_status',
2637             'from'    => 'emails_text.from_addr',
2638             'subject' => 'name',
2639             'date'    => 'date_sent',
2640             'AssignedTo' => 'assigned_user_id',
2641             'flagged' => 'flagged'
2642         );
2643
2644              $sort = !empty($_REQUEST['sort']) ? $_REQUEST['sort'] : "";
2645          $direction = !empty($_REQUEST['dir']) ? $_REQUEST['dir'] : "";
2646
2647          $order = ( !empty($sort) && !empty($direction) ) ? " ORDER BY {$hrSortLocal[$sort]} {$direction}" : "";
2648
2649          //Get our main query.
2650                 $fullQuery = $this->_genereateSearchImportedEmailsQuery();
2651
2652                 //Perform a count query needed for pagination.
2653                 $countQuery = $this->create_list_count_query($fullQuery);
2654                 $count_rs = $this->db->query($countQuery, false, 'Error executing count query for imported emails search');
2655                 $count_row = $this->db->fetchByAssoc($count_rs);
2656                 $total_count = ($count_row != null) ? $count_row['c'] : 0;
2657
2658         $start = ($page - 1) * $pageSize;
2659
2660         //Execute the query
2661                 $rs = $this->db->limitQuery($fullQuery . $order, $start, $pageSize);
2662
2663                 $return = array();
2664
2665                 while($a = $this->db->fetchByAssoc($rs)) {
2666                         $temp = array();
2667                         $temp['flagged'] = (is_null($a['flagged']) || $a['flagged'] == '0') ? '' : 1;
2668                         $temp['status'] = (is_null($a['reply_to_status']) || $a['reply_to_status'] == '0') ? '' : 1;
2669                         $temp['subject'] = $a['name'];
2670                         $temp['date']   = $timedate->to_display_date_time($a['date_sent']);
2671                         $temp['uid'] = $a['id'];
2672                         $temp['ieId'] = $a['mailbox_id'];
2673                         $temp['site_url'] = $sugar_config['site_url'];
2674                         $temp['seen'] = ($a['status'] == 'unread') ? 0 : 1;
2675                         $temp['type'] = $a['type'];
2676                         $temp['mbox'] = 'sugar::Emails';
2677                         $temp['hasAttach'] =  $this->doesImportedEmailHaveAttachment($a['id']);
2678                         //To and from addresses may be stored in emails_text, if nothing is found, revert to
2679                         //regular email addresses.
2680                         $temp['to_addrs'] = preg_replace('/[\x00-\x08\x0B-\x1F]/', '', $a['to_addrs']);
2681                         $temp['from']   = preg_replace('/[\x00-\x08\x0B-\x1F]/', '', $a['from_addr']);
2682                         if( empty($temp['from']) || empty($temp['to_addrs']) )
2683                         {
2684                         //Retrieve email addresses seperatly.
2685                         $tmpEmail = new Email();
2686                         $tmpEmail->id = $a['id'];
2687                         $tmpEmail->retrieveEmailAddresses();
2688                         $temp['from'] = $tmpEmail->from_addr;
2689                         $temp['to_addrs'] = $tmpEmail->to_addrs;
2690                         }
2691
2692                         $return[] = $temp;
2693                 }
2694
2695                 $metadata = array();
2696                 $metadata['totalCount'] = $total_count;
2697                 $metadata['out'] = $return;
2698
2699                 return $metadata;
2700     }
2701
2702     /**
2703      * Determine if an imported email has an attachment by examining the relationship to notes.
2704      *
2705      * @param string $id
2706      * @return boolean
2707      */
2708     function doesImportedEmailHaveAttachment($id)
2709         {
2710            $hasAttachment = FALSE;
2711            $query = "SELECT id FROM notes where parent_id='$id' AND parent_type='Emails' AND file_mime_type is not null AND deleted=0";
2712            $rs = $this->db->limitQuery($query, 0, 1);
2713            $row = $this->db->fetchByAssoc($rs);
2714            if( !empty($row['id']) )
2715                $hasAttachment = TRUE;
2716
2717            return (int) $hasAttachment;
2718         }
2719
2720     /**
2721      * Generate the query used for searching imported emails.
2722      *
2723      * @return String Query to be executed.
2724      */
2725     function _genereateSearchImportedEmailsQuery()
2726     {
2727                 global $timedate;
2728
2729         $additionalWhereClause = $this->_generateSearchImportWhereClause();
2730
2731         $query = array();
2732         $fullQuery = "";
2733         $query['select'] = "emails.id , emails.mailbox_id, emails.name, emails.date_sent, emails.status, emails.type, emails.flagged, emails.reply_to_status,
2734                                       emails_text.from_addr, emails_text.to_addrs  FROM emails ";
2735
2736         $query['joins'] = " JOIN emails_text on emails.id = emails_text.email_id ";
2737
2738         //Handle from and to addr joins
2739         if( !empty($_REQUEST['from_addr']) )
2740         {
2741             $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
2742                                 AND er_from.address_type='from' AND ea_from.email_address='" . strtolower($_REQUEST['from_addr']) . "'";
2743         }
2744
2745         if( !empty($_REQUEST['to_addrs'])  )
2746         {
2747             $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
2748                                     AND er_to.address_type='to' AND ea_to.email_address='" . strtolower($_REQUEST['to_addrs']) . "'";
2749         }
2750
2751         $query['where'] = " WHERE (emails.type= 'inbound' OR emails.type='archived' OR emails.type='out') AND emails.deleted = 0 ";
2752                 if( !empty($additionalWhereClause) )
2753             $query['where'] .= "AND $additionalWhereClause";
2754
2755         //If we are explicitly looking for attachments.  Do not use a distinct query as the to_addr is defined
2756         //as a text which equals clob in oracle and the distinct query can not be executed correctly.
2757         $addDistinctKeyword = "";
2758         if( !empty($_REQUEST['attachmentsSearch']) &&  $_REQUEST['attachmentsSearch'] == 1) //1 indicates yes
2759             $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 )";
2760         else if( !empty($_REQUEST['attachmentsSearch']) &&  $_REQUEST['attachmentsSearch'] == 2 )
2761              $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 )";
2762
2763         $fullQuery = "SELECT " . $query['select'] . " " . $query['joins'] . " " . $query['where'];
2764
2765         return $fullQuery;
2766     }
2767         /**
2768      * Generate the where clause for searching imported emails.
2769      *
2770      */
2771     function _generateSearchImportWhereClause()
2772     {
2773         global $timedate;
2774
2775         //The clear button was removed so if a user removes the asisgned user name, do not process the id.
2776         if( empty($_REQUEST['assigned_user_name']) && !empty($_REQUEST['assigned_user_id'])  )
2777             unset($_REQUEST['assigned_user_id']);
2778
2779         $availableSearchParam = array('name' => array('table_name' =>'emails'),
2780                                         'data_parent_id_search' => array('table_name' =>'emails','db_key' => 'parent_id','opp' => '='),
2781                                         'assigned_user_id' => array('table_name' => 'emails', 'opp' => '=') );
2782
2783                 $additionalWhereClause = array();
2784                 foreach ($availableSearchParam as $key => $properties)
2785                 {
2786                       if( !empty($_REQUEST[$key]) )
2787                       {
2788                           $db_key =  isset($properties['db_key']) ? $properties['db_key'] : $key;
2789                           $searchValue = $_REQUEST[$key];
2790
2791                           $opp = isset($properties['opp']) ? $properties['opp'] : 'like';
2792                           if($opp == 'like')
2793                               $searchValue = $searchValue . "%";
2794
2795                           $additionalWhereClause[] = "{$properties['table_name']}.$db_key $opp '$searchValue' ";
2796                       }
2797         }
2798
2799         $isDateFromSearchSet = !empty($_REQUEST['searchDateFrom']);
2800         $isdateToSearchSet = !empty($_REQUEST['searchDateTo']);
2801
2802         $bothDateRangesSet = $isDateFromSearchSet & $isdateToSearchSet;
2803
2804         //Hanlde date from and to seperately
2805         if($bothDateRangesSet)
2806         {
2807             $dbFormatDateFrom = $timedate->to_db_date($_REQUEST['searchDateFrom'], false);
2808             $dbFormatDateFrom = db_convert("'" . $dbFormatDateFrom . "'",'datetime');
2809
2810             $dbFormatDateTo = $timedate->to_db_date($_REQUEST['searchDateTo'], false);
2811             $dbFormatDateTo = db_convert("'" . $dbFormatDateTo . "'",'datetime');
2812
2813             $additionalWhereClause[] = "( emails.date_sent >= $dbFormatDateFrom AND
2814                                           emails.date_sent <= $dbFormatDateTo )";
2815         }
2816         elseif ($isdateToSearchSet)
2817         {
2818             $dbFormatDateTo = $timedate->to_db_date($_REQUEST['searchDateTo'], false);
2819             $dbFormatDateTo = db_convert("'" . $dbFormatDateTo . "'",'datetime');
2820             $additionalWhereClause[] = "emails.date_sent <= $dbFormatDateTo ";
2821         }
2822         elseif ($isDateFromSearchSet)
2823         {
2824             $dbFormatDateFrom = $timedate->to_db_date($_REQUEST['searchDateFrom'], false);
2825             $dbFormatDateFrom = db_convert("'" . $dbFormatDateFrom . "'",'datetime');
2826             $additionalWhereClause[] = "emails.date_sent >= $dbFormatDateFrom ";
2827         }
2828
2829         $additionalWhereClause = implode(" AND ", $additionalWhereClause);
2830
2831         return $additionalWhereClause;
2832     }
2833
2834
2835
2836         /**
2837          * takes a long TO: string of emails and returns the first appended by an
2838          * elipse
2839          */
2840         function trimLongTo($str) {
2841                 if(strpos($str, ',')) {
2842                         $exStr = explode(',', $str);
2843                         return $exStr[0].'...';
2844                 } elseif(strpos($str, ';')) {
2845                         $exStr = explode(';', $str);
2846                         return $exStr[0].'...';
2847                 } else {
2848                         return $str;
2849                 }
2850         }
2851
2852         function get_summary_text() {
2853                 return $this->name;
2854         }
2855
2856
2857
2858         function distributionForm($where) {
2859                 global $app_list_strings;
2860                 global $app_strings;
2861                 global $mod_strings;
2862                 global $theme;
2863                 global $current_user;
2864
2865                 $distribution   = get_select_options_with_id($app_list_strings['dom_email_distribution'], '');
2866                 $_SESSION['distribute_where'] = $where;
2867
2868
2869                 $out = '<form name="Distribute" id="Distribute">';
2870                 $out .= get_form_header($mod_strings['LBL_DIST_TITLE'], '', false);
2871                 $out .=<<<eoq
2872                 <script>
2873                         enableQS(true);
2874                 </script>
2875 eoq;
2876                 $out .= '
2877                 <table cellpadding="0" cellspacing="0" width="100%" border="0">
2878                         <tr>
2879                                 <td>
2880                                         <script type="text/javascript">
2881
2882
2883                                                 function checkDeps(form) {
2884                                                         return;
2885                                                 }
2886
2887                                                 function mySubmit() {
2888                                                         var assform = document.getElementById("Distribute");
2889                                                         var select = document.getElementById("userSelect");
2890                                                         var assign1 = assform.r1.checked;
2891                                                         var assign2 = assform.r2.checked;
2892                                                         var dist = assform.dm.value;
2893                                                         var assign = false;
2894                                                         var users = false;
2895                                                         var rules = false;
2896                                                         var warn1 = "'.$mod_strings['LBL_WARN_NO_USERS'].'";
2897                                                         var warn2 = "";
2898
2899                                                         if(assign1 || assign2) {
2900                                                                 assign = true;
2901
2902                                                         }
2903
2904                                                         for(i=0; i<select.options.length; i++) {
2905                                                                 if(select.options[i].selected == true) {
2906                                                                         users = true;
2907                                                                         warn1 = "";
2908                                                                 }
2909                                                         }
2910
2911                                                         if(dist != "") {
2912                                                                 rules = true;
2913                                                         } else {
2914                                                                 warn2 = "'.$mod_strings['LBL_WARN_NO_DIST'].'";
2915                                                         }
2916
2917                                                         if(assign && users && rules) {
2918
2919                                                                 if(document.getElementById("r1").checked) {
2920                                                                         var mu = document.getElementById("MassUpdate");
2921                                                                         var grabbed = "";
2922
2923                                                                         for(i=0; i<mu.elements.length; i++) {
2924                                                                                 if(mu.elements[i].type == "checkbox" && mu.elements[i].checked && mu.elements[i].name.value != "massall") {
2925                                                                                         if(grabbed != "") { grabbed += "::"; }
2926                                                                                         grabbed += mu.elements[i].value;
2927                                                                                 }
2928                                                                         }
2929                                                                         var formgrab = document.getElementById("grabbed");
2930                                                                         formgrab.value = grabbed;
2931                                                                 }
2932                                                                 assform.submit();
2933                                                         } else {
2934                                                                 alert("'.$mod_strings['LBL_ASSIGN_WARN'].'" + "\n" + warn1 + "\n" + warn2);
2935                                                         }
2936                                                 }
2937
2938                                                 function submitDelete() {
2939                                                         if(document.getElementById("r1").checked) {
2940                                                                 var mu = document.getElementById("MassUpdate");
2941                                                                 var grabbed = "";
2942
2943                                                                 for(i=0; i<mu.elements.length; i++) {
2944                                                                         if(mu.elements[i].type == "checkbox" && mu.elements[i].checked && mu.elements[i].name != "massall") {
2945                                                                                 if(grabbed != "") { grabbed += "::"; }
2946                                                                                 grabbed += mu.elements[i].value;
2947                                                                         }
2948                                                                 }
2949                                                                 var formgrab = document.getElementById("grabbed");
2950                                                                 formgrab.value = grabbed;
2951                                                         }
2952                                                         if(grabbed == "") {
2953                                                                 alert("'.$mod_strings['LBL_MASS_DELETE_ERROR'].'");
2954                                                         } else {
2955                                                                 document.getElementById("Distribute").submit();
2956                                                         }
2957                                                 }
2958
2959                                         </script>
2960                                                 <input type="hidden" name="module" value="Emails">
2961                                                 <input type="hidden" name="action" id="action">
2962                                                 <input type="hidden" name="grabbed" id="grabbed">
2963
2964                                         <table cellpadding="1" cellspacing="0" width="100%" border="0" class="edit view">
2965                                                 <tr height="20">
2966                                                         <td scope="col" scope="row" NOWRAP align="center">
2967                                                                 &nbsp;'.$mod_strings['LBL_ASSIGN_SELECTED_RESULTS_TO'].'&nbsp;';
2968                                         $out .= $this->userSelectTable();
2969                                         $out .= '</td>
2970                                                         <td scope="col" scope="row" NOWRAP align="left">
2971                                                                 &nbsp;'.$mod_strings['LBL_USING_RULES'].'&nbsp;
2972                                                                 <select name="distribute_method" id="dm" onChange="checkDeps(this.form);">'.$distribution.'</select>
2973                                                         </td>';
2974
2975
2976                                         $out .= '</td>
2977                                                         </tr>';
2978
2979
2980                                         $out .= '<tr>
2981                                                                 <td scope="col" width="50%" scope="row" NOWRAP align="right" colspan="2">
2982                                                                 <input title="'.$mod_strings['LBL_BUTTON_DISTRIBUTE_TITLE'].'"
2983                                                                         id="dist_button"
2984                                                                         accessKey="'.$mod_strings['LBL_BUTTON_DISTRIBUTE_KEY'].'"
2985                                                                         class="button" onClick="AjaxObject.detailView.handleAssignmentDialogAssignAction();"
2986                                                                         type="button" name="button"
2987                                                                         value="  '.$mod_strings['LBL_BUTTON_DISTRIBUTE'].'  ">';
2988                                         $out .= '</tr>
2989                                         </table>
2990                                 </td>
2991                         </tr>
2992                 </table>
2993                 </form>';
2994         return $out;
2995         }
2996
2997         function userSelectTable() {
2998                 global $theme;
2999                 global $mod_strings;
3000
3001                 $colspan = 1;
3002                 $setTeamUserFunction = '';
3003
3004
3005                 // get users
3006                 $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");
3007
3008                 $userTable = '<table cellpadding="0" cellspacing="0" border="0">';
3009                 $userTable .= '<tr><td colspan="2"><b>'.$mod_strings['LBL_USER_SELECT'].'</b></td></tr>';
3010                 $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>';
3011                 $userTable .= '<tr><td colspan="2"><select style="visibility:hidden;" name="users[]" id="userSelect" multiple size="12">';
3012
3013                 while($a = $this->db->fetchByAssoc($r)) {
3014                         $userTable .= '<option value="'.$a['id'].'" id="'.$a['id'].'">'.$a['first_name'].' '.$a['last_name'].'</option>';
3015                 }
3016                 $userTable .= '</select></td></tr>';
3017                 $userTable .= '</table>';
3018
3019                 $out  = '<script type="text/javascript">';
3020                 $out .= $setTeamUserFunction;
3021                 $out .= '
3022                                         function setCheckMark() {
3023                                                 var select = document.getElementById("userSelect");
3024
3025                                                 for(i=0 ; i<select.options.length; i++) {
3026                                                         if(select.options[i].selected == true) {
3027                                                                 document.getElementById("checkMark").style.display="";
3028                                                                 return;
3029                                                         }
3030                                                 }
3031
3032                                                 document.getElementById("checkMark").style.display="none";
3033                                                 return;
3034                                         }
3035
3036                                         function showUserSelect() {
3037                                                 var targetTable = document.getElementById("user_select");
3038                                                 targetTable.style.visibility="visible";
3039                                                 var userSelectTable = document.getElementById("userSelect");
3040                                                 userSelectTable.style.visibility="visible";
3041                                                 return;
3042                                         }
3043                                         function hideUserSelect() {
3044                                                 var targetTable = document.getElementById("user_select");
3045                                                 targetTable.style.visibility="hidden";
3046                                                 var userSelectTable = document.getElementById("userSelect");
3047                                                 userSelectTable.style.visibility="hidden";
3048                                                 return;
3049                                         }
3050                                         function toggleAll(toggle) {
3051                                                 if(toggle.checked) {
3052                                                         var stat = true;
3053                                                 } else {
3054                                                         var stat = false;
3055                                                 }
3056                                                 var form = document.getElementById("userSelect");
3057                                                 for(i=0; i<form.options.length; i++) {
3058                                                         form.options[i].selected = stat;
3059                                                 }
3060                                         }
3061
3062
3063                                 </script>
3064                         <span id="showUsersDiv" style="position:relative;">
3065                                 <a href="#" id="showUsers" onClick="javascript:showUserSelect();">
3066                                         <img border="0" src="'.SugarThemeRegistry::current()->getImageURL('Users.gif').'"></a>&nbsp;
3067                                 <a href="#" id="showUsers" onClick="javascript:showUserSelect();">
3068                                         <span style="display:none;" id="checkMark"><img border="0" src="'.SugarThemeRegistry::current()->getImageURL('check_inline.gif').'"></span>
3069                                 </a>
3070
3071
3072                                 <div id="user_select" style="width:200px;position:absolute;left:2;top:2;visibility:hidden;z-index:1000;">
3073                                 <table cellpadding="0" cellspacing="0" border="0" class="list view">
3074                                         <tr height="20">
3075                                                 <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\';">
3076                                                         <a href="#" onClick="javascript:hideUserSelect();"><img border="0" src="'.SugarThemeRegistry::current()->getImageURL('close.gif').'"></a>
3077                                                         '.$mod_strings['LBL_USER_SELECT'].'
3078                                                 </td>
3079                                         </tr>
3080                                         <tr>';
3081 //<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\';">
3082                 $out .= '               <td style="padding:5px" class="oddListRowS1" bgcolor="#fdfdfd" valign="top" align="left" style="left:0;top:0;">
3083                                                         '.$userTable.'
3084                                                 </td>
3085                                         </tr>
3086                                 </table></div>
3087                         </span>';
3088                 return $out;
3089         }
3090
3091         function checkInbox($type) {
3092                 global $theme;
3093                 global $mod_strings;
3094                 $out = '<div><input     title="'.$mod_strings['LBL_BUTTON_CHECK_TITLE'].'"
3095                                                 accessKey="'.$mod_strings['LBL_BUTTON_CHECK_KEY'].'"
3096                                                 class="button"
3097                                                 type="button" name="button"
3098                                                 onClick="window.location=\'index.php?module=Emails&action=Check&type='.$type.'\';"
3099                                                 style="margin-bottom:2px"
3100                                                 value="  '.$mod_strings['LBL_BUTTON_CHECK'].'  "></div>';
3101                 return $out;
3102         }
3103
3104         /**
3105          * Guesses Primary Parent id from From: email address.  Cascades guesses from Accounts to Contacts to Leads to
3106          * Users.  This will not affect the many-to-many relationships already constructed as this is, at best,
3107          * informational linking.
3108          */
3109         function fillPrimaryParentFields() {
3110                 if(empty($this->from_addr))
3111                         return;
3112
3113                 $GLOBALS['log']->debug("*** Email trying to guess Primary Parent from address [ {$this->from_addr} ]");
3114
3115                 $tables = array('accounts');
3116                 $ret = array();
3117                 // loop through types to get hits
3118                 foreach($tables as $table) {
3119                         $q = "SELECT name, id FROM {$table} WHERE email1 = '{$this->from_addr}' OR email2 = '{$this->from_addr}' AND deleted = 0";
3120                         $r = $this->db->query($q);
3121                         while($a = $this->db->fetchByAssoc($r)) {
3122                                 if(!empty($a['name']) && !empty($a['id'])) {
3123                                         $this->parent_type      = ucwords($table);
3124                                         $this->parent_id        = $a['id'];
3125                                         $this->parent_name      = $a['name'];
3126                                         return;
3127                                 }
3128                         }
3129                 }
3130         }
3131
3132
3133
3134 } // end class def