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