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