2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4 * SugarCRM is a customer relationship management program developed by
5 * SugarCRM, Inc. Copyright (C) 2004-2011 SugarCRM Inc.
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.
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
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
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.
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.
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 ********************************************************************************/
38 /*********************************************************************************
41 * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc. All Rights
42 * Reserved. Contributor(s): ______________________________________..
43 *********************************************************************************/
44 require_once('include/SugarPHPMailer.php');
46 require_once('include/Pear/HTML_Safe/Safe.php');
48 class Email extends SugarBean {
49 /* SugarBean schema */
53 var $assigned_user_id;
54 var $assigned_user_name;
55 var $modified_user_id;
67 var $type = 'archived';
78 var $description_html;
88 var $date_start; // legacy
89 var $time_start; // legacy
101 var $bcc_addrs_names;
102 var $bcc_addrs_emails;
106 /* Archive Email attrs */
111 var $new_schema = true;
112 var $table_name = 'emails';
113 var $module_dir = 'Emails';
114 var $object_name = 'Email';
117 /* private attributes */
118 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 $cacheFile = 'robin.cache.php';
121 var $replyDelimiter = "> ";
122 var $emailDescription;
123 var $emailDescriptionHTML;
127 var $attachments = array();
129 /* to support Email 2.0 */
137 var $relationshipMap = array(
138 'Contacts' => 'emails_contacts_rel',
139 'Accounts' => 'emails_accounts_rel',
140 'Leads' => 'emails_leads_rel',
141 'Users' => 'emails_users_rel',
142 'Prospects' => 'emails_prospects_rel',
146 var $et; // EmailUI object
154 $this->cachePath = $GLOBALS['sugar_config']['cache_dir'].'modules/Emails';
157 $this->safe = new HTML_Safe();
158 $this->safe->clear();
159 $this->emailAddress = new SugarEmailAddress();
162 function email2init() {
163 require_once('modules/Emails/EmailUI.php');
164 $this->et = new EmailUI();
166 function bean_implements($interface){
168 case 'ACL': return true;
169 default: return false;
175 * Presaves one attachment for new email 2.0 spec
176 * DOES NOT CREATE A NOTE
177 * @return string ID of note associated with the attachment
179 function email2saveAttachment() {
180 global $sugar_config;
183 0 => 'UPLOAD_ERR_OK - There is no error, the file uploaded with success.',
184 1 => 'UPLOAD_ERR_INI_SIZE - The uploaded file exceeds the upload_max_filesize directive in php.ini.',
185 2 => 'UPLOAD_ERR_FORM_SIZE - The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.',
186 3 => 'UPLOAD_ERR_PARTIAL - The uploaded file was only partially uploaded.',
187 4 => 'UPLOAD_ERR_NO_FILE - No file was uploaded.',
188 5 => 'UNKNOWN ERROR',
189 6 => 'UPLOAD_ERR_NO_TMP_DIR - Missing a temporary folder. Introduced in PHP 4.3.10 and PHP 5.0.3.',
190 7 => 'UPLOAD_ERR_CANT_WRITE - Failed to write file to disk. Introduced in PHP 5.1.0.',
193 // cn: Bug 5995 - rudimentary error checking
194 if($_FILES['email_attachment']['error'] != 0 && $_FILES['email_attachment']['error'] != 4) {
195 $GLOBALS['log']->debug('Email Attachment could not be attach due to error: '.$filesError[$_FILES['email_attachment']['error']]);
199 if(isset($_FILES['email_attachment']) && is_uploaded_file($_FILES['email_attachment']['tmp_name'])) {
200 $guid = create_guid();
201 $cleanAttachmentFileName = from_html($_FILES['email_attachment']['name']);
202 $GLOBALS['log']->debug("Email Attachment [ {$cleanAttachmentFileName} ] ");
203 $cleanAttachmentFileName = str_replace("\\", "", $cleanAttachmentFileName);
204 $GLOBALS['log']->debug("Email Attachment [ {$cleanAttachmentFileName} ] ");
205 //$destination = clean_path("{$this->et->userCacheDir}/{$guid}{$cleanAttachmentFileName}");
206 $destination = clean_path("{$this->et->userCacheDir}/{$guid}");
207 $badExt = $this->safeAttachmentName($cleanAttachmentFileName);
209 //$destination = $destination . ".txt";
211 $fileName = $badExt ? $cleanAttachmentFileName . ".txt" : $cleanAttachmentFileName;
212 if(move_uploaded_file($_FILES['email_attachment']['tmp_name'], $destination)) {
215 'name' => $GLOBALS['db']->helper->escape_quote($fileName),
216 'nameForDisplay' => $fileName
219 $GLOBALS['log']->debug("Email Attachment [ {$cleanAttachmentFileName} ] could not be moved to cache dir");
225 function safeAttachmentName($filename) {
226 global $sugar_config;
227 $badExtension = false;
228 //get position of last "." in file name
229 $file_ext_beg = strrpos($filename, ".");
233 if($file_ext_beg !== false) {
234 $file_ext = substr($filename, $file_ext_beg + 1);
237 //check to see if this is a file with extension located in "badext"
238 foreach($sugar_config['upload_badext'] as $badExt) {
239 if(strtolower($file_ext) == strtolower($badExt)) {
240 //if found, then append with .txt and break out of lookup
241 $filename = $filename . ".txt";
242 $badExtension = true;
243 break; // no need to look for more
247 return $badExtension;
251 * takes output from email 2.0 to/cc/bcc fields and returns appropriate arrays for usage by PHPMailer
252 * @param string addresses
255 function email2ParseAddresses($addresses) {
256 $addresses = from_html($addresses);
257 $addresses = $this->et->unifyEmailString($addresses);
259 $pattern = '/@.*,/U';
260 preg_match_all($pattern, $addresses, $matchs);
261 if (!empty($matchs[0])){
263 foreach ($total as $match) {
264 $convertedPattern = str_replace(',', '::;::', $match);
265 $addresses = str_replace($match, $convertedPattern, $addresses);
269 $exAddr = explode("::;::", $addresses);
272 $clean = array("<", ">");
273 $dirty = array("<", ">");
275 foreach($exAddr as $addr) {
278 $addr = str_replace($dirty, $clean, $addr);
280 if((strpos($addr, "<") === false) && (strpos($addr, ">") === false)) {
283 $address = substr($addr, strpos($addr, "<") + 1, strpos($addr, ">") - 1 - strpos($addr, "<"));
284 $name = substr($addr, 0, strpos($addr, "<"));
288 $addrTemp['email'] = trim($address);
289 $addrTemp['display'] = trim($name);
297 * takes output from email 2.0 to/cc/bcc fields and returns appropriate arrays for usage by PHPMailer
298 * @param string addresses
301 function email2ParseAddressesForAddressesOnly($addresses) {
302 $addresses = from_html($addresses);
303 $pattern = '/@.*,/U';
304 preg_match_all($pattern, $addresses, $matchs);
305 if (!empty($matchs[0])){
307 foreach ($total as $match) {
308 $convertedPattern = str_replace(',', '::;::', $match);
309 $addresses = str_replace($match, $convertedPattern, $addresses);
313 $exAddr = explode("::;::", $addresses);
316 $clean = array("<", ">");
317 $dirty = array("<", ">");
319 foreach($exAddr as $addr) {
322 $addr = str_replace($dirty, $clean, $addr);
324 if(strpos($addr, "<") && strpos($addr, ">")) {
325 $address = substr($addr, strpos($addr, "<") + 1, strpos($addr, ">") - 1 - strpos($addr, "<"));
330 $ret[] = trim($address);
337 * Determines MIME-type encoding as possible.
338 * @param string $fileLocation relative path to file
339 * @return string MIME-type
341 function email2GetMime($fileLocation) {
342 if(function_exists('mime_content_type')) {
343 $mime = mime_content_type($fileLocation);
344 } elseif(function_exists('ext2mime')) {
345 $mime = ext2mime($fileLocation);
347 $mime = 'application/octet-stream';
353 function sendEmailTest($mailserver_url, $port, $ssltls, $smtp_auth_req, $smtp_username, $smtppassword, $fromaddress, $toaddress, $mail_sendtype = 'smtp') {
354 global $current_user,$app_strings;
355 $mod_strings = return_module_language($GLOBALS['current_language'], 'Emails'); //Called from EmailMan as well.
356 $mail = new SugarPHPMailer();
357 $mail->Mailer = strtolower($mail_sendtype);
358 if($mail->Mailer == 'smtp')
360 $mail->Host = $mailserver_url;
362 if (isset($ssltls) && !empty($ssltls)) {
363 $mail->protocol = "ssl://";
365 $mail->SMTPSecure = 'ssl';
368 $mail->SMTPSecure = 'tls';
371 $mail->protocol = "tcp://";
373 if ($smtp_auth_req) {
374 $mail->SMTPAuth = TRUE;
375 $mail->Username = $smtp_username;
376 $mail->Password = $smtppassword;
380 $mail->Mailer = 'sendmail';
382 $mail->Subject = from_html($mod_strings['LBL_TEST_EMAIL_SUBJECT']);
383 $mail->From = $fromaddress;
384 $mail->FromName = $current_user->name;
385 $mail->Sender = $mail->From;
386 $mail->AddAddress($toaddress);
387 $mail->Body = $mod_strings['LBL_TEST_EMAIL_BODY'];
393 $return['status'] = false;
394 $return['errorMessage'] = $app_strings['LBL_EMAIL_ERROR_PREPEND']. $mail->ErrorInfo;
397 $return['status'] = true;
401 function decodeDuringSend($htmlData) {
402 $htmlData = str_replace("sugarLessThan", "<", $htmlData);
403 $htmlData = str_replace("sugarGreaterThan", ">", $htmlData);
408 * Returns true or false if this email is a draft.
410 * @param array $request
411 * @return bool True indicates this email is a draft.
413 function isDraftEmail($request)
415 return ( isset($request['saveDraft']) || ($this->type == 'draft' && $this->status == 'draft') );
419 * Sends Email for Email 2.0
421 function email2Send($request) {
424 global $current_user;
425 global $sugar_config;
430 $OBCharset = $locale->getPrecedentPreference('default_email_charset');
432 /**********************************************************************
438 if(!empty($this->id)) {
439 $orignialId = $this->id;
442 if(empty($this->id)) {
443 $this->id = create_guid();
444 $this->new_with_id = true;
447 /* satisfy basic HTML email requirements */
448 $this->name = $request['sendSubject'];
449 $this->description_html = '<html><body>'.$request['sendDescription'].'</body></html>';
451 /**********************************************************************
454 $mail = new SugarPHPMailer();
455 $mail = $this->setMailer($mail, '', $_REQUEST['fromAccount']);
456 if (empty($mail->Host) && !$this->isDraftEmail($request))
458 $this->status = 'send_error';
460 if ($mail->oe->type == 'system')
461 echo($app_strings['LBL_EMAIL_ERROR_PREPEND']. $app_strings['LBL_EMAIL_INVALID_SYSTEM_OUTBOUND']);
463 echo($app_strings['LBL_EMAIL_ERROR_PREPEND']. $app_strings['LBL_EMAIL_INVALID_PERSONAL_OUTBOUND']);
468 $subject = $this->name;
469 $mail->Subject = from_html($this->name);
471 // work-around legacy code in SugarPHPMailer
472 if($_REQUEST['setEditor'] == 1) {
473 $_REQUEST['description_html'] = $_REQUEST['sendDescription'];
474 $this->description_html = $_REQUEST['description_html'];
476 $this->description_html = '';
477 $this->description = $_REQUEST['sendDescription'];
481 if ( $this->isDraftEmail($request) )
483 if($this->type != 'draft' && $this->status != 'draft') {
484 $this->id = create_guid();
485 $this->new_with_id = true;
486 $this->date_entered = "";
488 $q1 = "update emails_email_addr_rel set deleted = 1 WHERE email_id = '{$this->id}'";
489 $r1 = $this->db->query($q1);
492 if (isset($request['saveDraft'])) {
493 $this->type = 'draft';
494 $this->status = 'draft';
497 /* Apply Email Templates */
498 // do not parse email templates if the email is being saved as draft....
499 $toAddresses = $this->email2ParseAddresses($_REQUEST['sendTo']);
500 $sea = new SugarEmailAddress();
501 $object_arr = array();
503 if( isset($_REQUEST['parent_type']) && !empty($_REQUEST['parent_type']) &&
504 isset($_REQUEST['parent_id']) && !empty($_REQUEST['parent_id']) &&
505 ($_REQUEST['parent_type'] == 'Accounts' ||
506 $_REQUEST['parent_type'] == 'Contacts' ||
507 $_REQUEST['parent_type'] == 'Leads' ||
508 $_REQUEST['parent_type'] == 'Users' ||
509 $_REQUEST['parent_type'] == 'Prospects')) {
510 if(isset($beanList[$_REQUEST['parent_type']]) && !empty($beanList[$_REQUEST['parent_type']])) {
511 $className = $beanList[$_REQUEST['parent_type']];
512 if(isset($beanFiles[$className]) && !empty($beanFiles[$className])) {
513 if(!class_exists($className)) {
514 require_once($beanFiles[$className]);
516 $bean = new $className();
517 $bean->retrieve($_REQUEST['parent_id']);
518 $object_arr[$bean->module_dir] = $bean->id;
522 foreach($toAddresses as $addrMeta) {
523 $addr = $addrMeta['email'];
524 $beans = $sea->getBeansByEmailAddress($addr);
525 foreach($beans as $bean) {
526 if (!isset($object_arr[$bean->module_dir])) {
527 $object_arr[$bean->module_dir] = $bean->id;
532 /* template parsing */
533 if (empty($object_arr)) {
534 $object_arr= array('Contacts' => '123');
536 $object_arr['Users'] = $current_user->id;
537 $this->description_html = EmailTemplate::parse_template($this->description_html, $object_arr);
538 $this->name = EmailTemplate::parse_template($this->name, $object_arr);
539 $this->description = EmailTemplate::parse_template($this->description, $object_arr);
540 $this->description = html_entity_decode($this->description,ENT_COMPAT,'UTF-8');
541 if($this->type != 'draft' && $this->status != 'draft') {
542 $this->id = create_guid();
543 $this->date_entered = "";
544 $this->new_with_id = true;
546 $this->status = 'sent';
550 if(isset($_REQUEST['parent_type']) && empty($_REQUEST['parent_type']) &&
551 isset($_REQUEST['parent_id']) && empty($_REQUEST['parent_id']) ) {
552 $this->parent_id = "";
553 $this->parent_type = "";
557 $mail->Subject = $this->name;
558 $mail = $this->handleBody($mail);
559 $mail->Subject = $this->name;
560 $this->description_html = from_html($this->description_html);
561 $this->description_html = $this->decodeDuringSend($this->description_html);
562 $this->description = $this->decodeDuringSend($this->description);
565 $replyToAddress = $current_user->emailAddress->getReplyToAddress($current_user);
567 if(empty($request['fromAccount'])) {
568 $defaults = $current_user->getPreferredEmail();
569 $mail->From = $defaults['email'];
570 $mail->FromName = $defaults['name'];
571 $replyToName = $mail->FromName;
572 //$replyToAddress = $current_user->emailAddress->getReplyToAddress($current_user);
574 // passed -> user -> system default
575 $ie = new InboundEmail();
576 $ie->retrieve($request['fromAccount']);
577 $storedOptions = unserialize(base64_decode($ie->stored_options));
581 //$replyToAddress = "";
582 if (!empty($storedOptions)) {
583 $fromAddress = $storedOptions['from_addr'];
584 $fromName = from_html($storedOptions['from_name']);
585 $replyToAddress = (isset($storedOptions['reply_to_addr']) ? $storedOptions['reply_to_addr'] : "");
586 $replyToName = (isset($storedOptions['reply_to_name']) ? from_html($storedOptions['reply_to_name']) : "");
588 $defaults = $current_user->getPreferredEmail();
589 // Personal Account doesn't have reply To Name and Reply To Address. So add those columns on UI
590 // After adding remove below code
593 if ($ie->is_personal)
595 if (empty($replyToAddress))
597 $replyToAddress = $current_user->emailAddress->getReplyToAddress($current_user);
599 if (empty($replyToName))
601 $replyToName = $defaults['name'];
603 //Personal accounts can have a reply_address, which should
604 //overwrite the users set default.
605 if( !empty($storedOptions['reply_to_addr']) )
606 $replyToAddress = $storedOptions['reply_to_addr'];
609 // end of code to remove
610 $mail->From = (!empty($fromAddress)) ? $fromAddress : $defaults['email'];
611 $mail->FromName = (!empty($fromName)) ? $fromName : $defaults['name'];
612 $replyToName = (!empty($replyToName)) ? $replyToName : $mail->FromName;
615 $mail->Sender = $mail->From; /* set Return-Path field in header to reduce spam score in emails sent via Sugar's Email module */
617 if (!empty($replyToAddress)) {
618 $mail->AddReplyTo($replyToAddress,$locale->translateCharsetMIME(trim( $replyToName), 'UTF-8', $OBCharset));
620 $mail->AddReplyTo($mail->From,$locale->translateCharsetMIME(trim( $mail->FromName), 'UTF-8', $OBCharset));
622 $emailAddressCollection = array(); // used in linking to beans below
624 foreach($this->email2ParseAddresses($request['sendTo']) as $addr_arr) {
625 if(empty($addr_arr['email'])) continue;
627 if(empty($addr_arr['display'])) {
628 $mail->AddAddress($addr_arr['email'], "");
630 $mail->AddAddress($addr_arr['email'],$locale->translateCharsetMIME(trim( $addr_arr['display']), 'UTF-8', $OBCharset));
632 $emailAddressCollection[] = $addr_arr['email'];
634 foreach($this->email2ParseAddresses($request['sendCc']) as $addr_arr) {
635 if(empty($addr_arr['email'])) continue;
637 if(empty($addr_arr['display'])) {
638 $mail->AddCC($addr_arr['email'], "");
640 $mail->AddCC($addr_arr['email'],$locale->translateCharsetMIME(trim( $addr_arr['display']), 'UTF-8', $OBCharset));
642 $emailAddressCollection[] = $addr_arr['email'];
645 foreach($this->email2ParseAddresses($request['sendBcc']) as $addr_arr) {
646 if(empty($addr_arr['email'])) continue;
648 if(empty($addr_arr['display'])) {
649 $mail->AddBCC($addr_arr['email'], "");
651 $mail->AddBCC($addr_arr['email'],$locale->translateCharsetMIME(trim( $addr_arr['display']), 'UTF-8', $OBCharset));
653 $emailAddressCollection[] = $addr_arr['email'];
657 /* parse remove attachments array */
658 $removeAttachments = array();
659 if(!empty($request['templateAttachmentsRemove'])) {
660 $exRemove = explode("::", $request['templateAttachmentsRemove']);
662 foreach($exRemove as $file) {
663 $removeAttachments = substr($file, 0, 36);
667 /* handle attachments */
668 if(!empty($request['attachments'])) {
669 $exAttachments = explode("::", $request['attachments']);
671 foreach($exAttachments as $file) {
672 $file = trim(from_html($file));
673 $file = str_replace("\\", "", $file);
675 //$fileLocation = $this->et->userCacheDir."/{$file}";
676 $fileGUID = substr($file, 0, 36);
677 $fileLocation = $this->et->userCacheDir."/{$fileGUID}";
678 $filename = substr($file, 36, strlen($file)); // strip GUID for PHPMailer class to name outbound file
680 $mail->AddAttachment($fileLocation,$filename, 'base64', $this->email2GetMime($fileLocation));
681 //$mail->AddAttachment($fileLocation, $filename, 'base64');
683 // only save attachments if we're archiving or drafting
684 if((($this->type == 'draft') && !empty($this->id)) || (isset($request['saveToSugar']) && $request['saveToSugar'] == 1)) {
686 $note->id = create_guid();
687 $note->new_with_id = true; // duplicating the note with files
688 $note->parent_id = $this->id;
689 $note->parent_type = $this->module_dir;
690 $note->name = $filename;
691 $note->filename = $filename;
692 $noteFile = "{$sugar_config['upload_dir']}{$note->id}";
693 $note->file_mime_type = $this->email2GetMime($fileLocation);
695 if(!copy($fileLocation, $noteFile)) {
696 $GLOBALS['log']->debug("EMAIL 2.0: could not copy attachment file to cache/upload [ {$fileLocation} ]");
705 /* handle sugar documents */
706 if(!empty($request['documents'])) {
707 $exDocs = explode("::", $request['documents']);
712 foreach($exDocs as $docId) {
713 $docId = trim($docId);
715 $doc = new Document();
716 $docRev = new DocumentRevision();
717 $doc->retrieve($docId);
718 $docRev->retrieve($doc->document_revision_id);
720 $filename = $docRev->filename;
721 $fileLocation = "{$sugar_config['upload_dir']}{$docRev->id}";
722 $mime_type = $docRev->file_mime_type;
723 $mail->AddAttachment($fileLocation,$locale->translateCharsetMIME(trim($filename), 'UTF-8', $OBCharset), 'base64', $mime_type);
725 // only save attachments if we're archiving or drafting
726 if((($this->type == 'draft') && !empty($this->id)) || (isset($request['saveToSugar']) && $request['saveToSugar'] == 1)) {
728 $note->id = create_guid();
729 $note->new_with_id = true; // duplicating the note with files
730 $note->parent_id = $this->id;
731 $note->parent_type = $this->module_dir;
732 $note->name = $filename;
733 $note->filename = $filename;
734 $note->file_mime_type = $mime_type;
735 $noteFile = "{$sugar_config['upload_dir']}{$note->id}";
737 if(!copy($fileLocation, $noteFile)) {
738 $GLOBALS['log']->debug("EMAIL 2.0: could not copy SugarDocument revision file to {$sugar_config['upload_dir']} [ {$fileLocation} ]");
747 /* handle template attachments */
748 if(!empty($request['templateAttachments'])) {
750 $exNotes = explode("::", $request['templateAttachments']);
751 foreach($exNotes as $noteId) {
752 $noteId = trim($noteId);
753 if(!empty($noteId)) {
755 $note->retrieve($noteId);
756 if (!empty($note->id)) {
757 $filename = $note->filename;
758 $fileLocation = "{$sugar_config['upload_dir']}{$note->id}";
759 $mime_type = $note->file_mime_type;
760 if (!$note->embed_flag) {
761 $mail->AddAttachment($fileLocation,$filename, 'base64', $mime_type);
762 // only save attachments if we're archiving or drafting
763 if((($this->type == 'draft') && !empty($this->id)) || (isset($request['saveToSugar']) && $request['saveToSugar'] == 1)) {
765 if ($note->parent_id != $this->id)
766 $this->saveTempNoteAttachments($filename,$fileLocation, $mime_type);
771 //$fileLocation = $this->et->userCacheDir."/{$file}";
772 $fileGUID = substr($noteId, 0, 36);
773 $fileLocation = $this->et->userCacheDir."/{$fileGUID}";
774 //$fileLocation = $this->et->userCacheDir."/{$noteId}";
775 $filename = substr($noteId, 36, strlen($noteId)); // strip GUID for PHPMailer class to name outbound file
777 $mail->AddAttachment($fileLocation,$locale->translateCharsetMIME(trim($filename), 'UTF-8', $OBCharset), 'base64', $this->email2GetMime($fileLocation));
779 //If we are saving an email we were going to forward we need to save the attachments as well.
780 if( (($this->type == 'draft') && !empty($this->id))
781 || (isset($request['saveToSugar']) && $request['saveToSugar'] == 1))
783 $mimeType = $this->email2GetMime($fileLocation);
784 $this->saveTempNoteAttachments($filename,$fileLocation, $mimeType);
793 /**********************************************************************
796 /* save email to sugar? */
799 if($this->type == 'draft' && !isset($request['saveDraft'])) {
800 // sending a draft email
802 $this->status = 'sent';
804 } elseif(isset($request['saveDraft'])) {
805 $this->type = 'draft';
806 $this->status = 'draft';
810 /**********************************************************************
811 * SEND EMAIL (finally!)
814 if ($this->type != 'draft') {
815 $mail->prepForOutbound();
816 $mail->Body = $this->decodeDuringSend($mail->Body);
817 $mail->AltBody = $this->decodeDuringSend($mail->AltBody);
818 if (!$mail->Send()) {
819 $this->status = 'send_error';
821 echo($app_strings['LBL_EMAIL_ERROR_PREPEND']. $mail->ErrorInfo);
826 if ((!(empty($orignialId) || isset($request['saveDraft']) || ($this->type == 'draft' && $this->status == 'draft'))) &&
827 (($_REQUEST['composeType'] == 'reply') || ($_REQUEST['composeType'] == 'replyAll') || ($_REQUEST['composeType'] == 'replyCase')) && ($orignialId != $this->id)) {
828 $originalEmail = new Email();
829 $originalEmail->retrieve($orignialId);
830 $originalEmail->reply_to_status = 1;
831 $originalEmail->save();
832 $this->reply_to_status = 0;
835 if ($_REQUEST['composeType'] == 'reply' || $_REQUEST['composeType'] == 'replyCase') {
836 if (isset($_REQUEST['ieId']) && isset($_REQUEST['mbox'])) {
837 $emailFromIe = new InboundEmail();
838 $emailFromIe->retrieve($_REQUEST['ieId']);
839 $emailFromIe->mailbox = $_REQUEST['mbox'];
840 if (isset($emailFromIe->id) && $emailFromIe->is_personal) {
841 if ($emailFromIe->isPop3Protocol()) {
842 $emailFromIe->mark_answered($this->uid, 'pop3');
844 elseif ($emailFromIe->connectMailserver() == 'true') {
845 $emailFromIe->markEmails($this->uid, 'answered');
846 $emailFromIe->mark_answered($this->uid);
854 $this->type == 'draft' ||
855 (isset($request['saveToSugar']) && $request['saveToSugar'] == 1)) {
857 // saving a draft OR saving a sent email
858 $decodedFromName = mb_decode_mimeheader($mail->FromName);
859 $this->from_addr = "{$decodedFromName} <{$mail->From}>";
860 $this->from_addr_name = $this->from_addr;
861 $this->to_addrs = $_REQUEST['sendTo'];
862 $this->to_addrs_names = $_REQUEST['sendTo'];
863 $this->cc_addrs = $_REQUEST['sendCc'];
864 $this->cc_addrs_names = $_REQUEST['sendCc'];
865 $this->bcc_addrs = $_REQUEST['sendBcc'];
866 $this->bcc_addrs_names = $_REQUEST['sendBcc'];
867 $this->assigned_user_id = $current_user->id;
869 $this->date_sent = $timedate->convert_to_gmt_datetime('now');
870 $this->date_sent = $timedate->to_display_date_time($this->date_sent);
871 ///////////////////////////////////////////////////////////////////
872 //// LINK EMAIL TO SUGARBEANS BASED ON EMAIL ADDY
874 if( isset($_REQUEST['parent_type']) && !empty($_REQUEST['parent_type']) &&
875 isset($_REQUEST['parent_id']) && !empty($_REQUEST['parent_id']) ) {
876 $this->parent_id = $_REQUEST['parent_id'];
877 $this->parent_type = $_REQUEST['parent_type'];
878 $q = "SELECT count(*) c FROM emails_beans WHERE email_id = '{$this->id}' AND bean_id = '{$_REQUEST['parent_id']}' AND bean_module = '{$_REQUEST['parent_type']}'";
879 $r = $this->db->query($q);
880 $a = $this->db->fetchByAssoc($r);
882 if(isset($beanList[$_REQUEST['parent_type']]) && !empty($beanList[$_REQUEST['parent_type']])) {
883 $className = $beanList[$_REQUEST['parent_type']];
884 if(isset($beanFiles[$className]) && !empty($beanFiles[$className])) {
885 if(!class_exists($className)) {
886 require_once($beanFiles[$className]);
888 $bean = new $className();
889 $bean->retrieve($_REQUEST['parent_id']);
890 if($bean->load_relationship('emails')) {
891 $bean->emails->add($this->id);
901 if(!class_exists('aCase')) {
906 if($caseId = InboundEmail::getCaseIdFromCaseNumber($mail->Subject, $c)) {
907 $c->retrieve($caseId);
908 $c->load_relationship('emails');
909 $c->emails->add($this->id);
910 $this->parent_type = "Cases";
911 $this->parent_id = $caseId;
917 //// LINK EMAIL TO SUGARBEANS BASED ON EMAIL ADDY
918 ///////////////////////////////////////////////////////////////////
922 if(!empty($request['fromAccount'])) {
923 if (isset($ie->id) && !$ie->isPop3Protocol()) {
924 $sentFolder = $ie->get_stored_options("sentFolder");
925 if (!empty($sentFolder)) {
926 $data = $mail->CreateHeader() . "\r\n" . $mail->CreateBody() . "\r\n";
927 $ie->mailbox = $sentFolder;
928 if ($ie->connectMailserver() == 'true') {
929 $connectString = $ie->getConnectString($ie->getServiceString(), $ie->mailbox);
930 $returnData = imap_append($ie->conn,$connectString, $data, "\\Seen");
932 $GLOBALS['log']->debug("could not copy email to {$ie->mailbox} for {$ie->name}");
935 $GLOBALS['log']->debug("could not connect to mail serve for folder {$ie->mailbox} for {$ie->name}");
938 $GLOBALS['log']->debug("could not copy email to {$ie->mailbox} sent folder as its empty");
946 * Generates a comma sperated name and addresses to be used in compose email screen for contacts or leads
949 function getNamePlusEmailAddressesForCompose($table, $idsArray) {
952 $table = strtolower($table);
953 $returndata = array();
955 foreach($idsArray as $id) {
956 if ($idsString != "") {
957 $idsString = $idsString . ",";
959 $idsString = $idsString . "'" . $id . "'";
961 $where = "({$table}.deleted = 0 AND {$table}.id in ({$idsString}))";
963 $selectColumn = "{$table}.first_name, {$table}.last_name, {$table}.salutation, {$table}.title";
964 if ($table == 'accounts') {
965 $selectColumn = "{$table}.name";
967 $query = "SELECT {$table}.id, {$selectColumn}, eabr.primary_address, ea.email_address";
968 $query .= " FROM {$table} ";
969 $query .= "JOIN email_addr_bean_rel eabr ON ({$table}.id = eabr.bean_id and eabr.deleted=0) ";
970 $query .= "JOIN email_addresses ea ON (eabr.email_address_id = ea.id) ";
971 $query .= " WHERE ({$where}) ORDER BY eabr.primary_address DESC";
972 $r = $this->db->query($query);
974 while($a = $this->db->fetchByAssoc($r)) {
975 if (!isset($returndata[$a['id']])) {
976 if ($table == 'accounts') {
977 $returndata[$a['id']] = from_html($a['name']) . " <".from_html($a['email_address']).">";
979 $full_name = from_html($locale->getLocaleFormattedName($a['first_name'], $a['last_name'], $a['salutation'], $a['title']));
980 $returndata[$a['id']] = "{$full_name} <".from_html($a['email_address']).">";
985 return join(",", array_values($returndata));
991 ///////////////////////////////////////////////////////////////////////////
993 function save($check_notify = false) {
994 if($this->isDuplicate) {
995 $GLOBALS['log']->debug("EMAIL - tried to save a duplicate Email record");
998 if(empty($this->id)) {
999 $this->id = create_guid();
1000 $this->new_with_id = true;
1002 $this->from_addr_name = $this->cleanEmails($this->from_addr_name);
1003 $this->to_addrs_names = $this->cleanEmails($this->to_addrs_names);
1004 $this->cc_addrs_names = $this->cleanEmails($this->cc_addrs_names);
1005 $this->bcc_addrs_names = $this->cleanEmails($this->bcc_addrs_names);
1006 $this->reply_to_addr = $this->cleanEmails($this->reply_to_addr);
1007 $this->description = to_html($this->safeText(from_html($this->description)));
1008 $this->description_html = $this->safeText($this->description_html);
1009 $this->saveEmailText();
1010 $this->saveEmailAddresses();
1012 $GLOBALS['log']->debug('-------------------------------> Email called save()');
1014 // handle legacy concatenation of date and time fields
1015 if(empty($this->date_sent)) $this->date_sent = $this->date_start." ".$this->time_start;
1016 parent::save($check_notify);
1021 * Helper function to save temporary attachments assocaited to an email as note.
1023 * @param string $filename
1024 * @param string $fileLocation
1025 * @param string $mimeType
1027 function saveTempNoteAttachments($filename,$fileLocation, $mimeType)
1029 global $sugar_config;
1031 $tmpNote = new Note();
1032 $tmpNote->id = create_guid();
1033 $tmpNote->new_with_id = true;
1034 $tmpNote->parent_id = $this->id;
1035 $tmpNote->parent_type = $this->module_dir;
1036 $tmpNote->name = $filename;
1037 $tmpNote->filename = $filename;
1038 $tmpNote->file_mime_type = $mimeType;
1039 $noteFile = "{$sugar_config['upload_dir']}{$tmpNote->id}";
1040 if(!copy($fileLocation, $noteFile))
1041 $GLOBALS['log']->fatal("EMAIL 2.0: could not copy SugarDocument revision file to {$sugar_config['upload_dir']} [ {$fileLocation} ]");
1045 * Handles normalization of Email Addressess
1047 function saveEmailAddresses() {
1048 // from, single address
1049 $fromId = $this->emailAddress->getEmailGUID(from_html($this->from_addr));
1050 if(!empty($fromId)){
1051 $this->linkEmailToAddress($fromId, 'from');
1055 $replace = array(",",";");
1056 $toaddrs = str_replace($replace, "::", from_html($this->to_addrs));
1057 $exToAddrs = explode("::", $toaddrs);
1059 if(!empty($exToAddrs)) {
1060 foreach($exToAddrs as $toaddr) {
1061 $toaddr = trim($toaddr);
1062 if(!empty($toaddr)) {
1063 $toId = $this->emailAddress->getEmailGUID($toaddr);
1064 $this->linkEmailToAddress($toId, 'to');
1070 $ccAddrs = str_replace($replace, "::", from_html($this->cc_addrs));
1071 $exccAddrs = explode("::", $ccAddrs);
1073 if(!empty($exccAddrs)) {
1074 foreach($exccAddrs as $ccAddr) {
1075 $ccAddr = trim($ccAddr);
1076 if(!empty($ccAddr)) {
1077 $ccId = $this->emailAddress->getEmailGUID($ccAddr);
1078 $this->linkEmailToAddress($ccId, 'cc');
1084 $bccAddrs = str_replace($replace, "::", from_html($this->bcc_addrs));
1085 $exbccAddrs = explode("::", $bccAddrs);
1086 if(!empty($exbccAddrs)) {
1087 foreach($exbccAddrs as $bccAddr) {
1088 $bccAddr = trim($bccAddr);
1089 if(!empty($bccAddr)) {
1090 $bccId = $this->emailAddress->getEmailGUID($bccAddr);
1091 $this->linkEmailToAddress($bccId, 'bcc');
1097 function linkEmailToAddress($id, $type) {
1098 // TODO: make this update?
1099 $q1 = "SELECT * FROM emails_email_addr_rel WHERE email_id = '{$this->id}' AND email_address_id = '{$id}' AND address_type = '{$type}' AND deleted = 0";
1100 $r1 = $this->db->query($q1);
1101 $a1 = $this->db->fetchByAssoc($r1);
1103 if(!empty($a1) && !empty($a1['id'])) {
1106 $guid = create_guid();
1107 $q2 = "INSERT INTO emails_email_addr_rel VALUES('{$guid}', '{$this->id}', '{$type}', '{$id}', 0)";
1108 $r2 = $this->db->query($q2);
1114 function cleanEmails($emails)
1116 $emails = str_replace(array(",",";"), "::", from_html($emails));
1117 $addrs = explode("::", $emails);
1119 foreach($addrs as $addr) {
1120 $parts = $this->emailAddress->splitEmailAddress($addr);
1121 if(empty($parts["email"])) {
1124 if(!empty($parts["name"])) {
1125 $res[] = "{$parts["name"]} <{$parts["email"]}>";
1127 $res[] .= $parts["email"];
1130 return join(", ", $res);
1133 function saveEmailText() {
1134 $isOracle = ($this->db->dbType == "oci8") ? true : false;
1137 $description = $this->db->quote(trim($this->description));
1138 $description_html = $this->db->quoteForEmail(trim($this->description_html));
1139 $raw_source = $this->db->quote(trim($this->raw_source));
1140 $fromAddressName = $this->db->helper->escape_quote($this->from_addr_name);
1141 $toAddressName = $this->db->helper->escape_quote($this->to_addrs_names);
1142 $ccAddressName = $this->db->helper->escape_quote($this->cc_addrs_names);
1143 $bccAddressName = $this->db->helper->escape_quote($this->bcc_addrs_names);
1144 $replyToAddrName = $this->db->helper->escape_quote($this->reply_to_addr);
1146 if(!$this->new_with_id) {
1147 $q = "UPDATE emails_text SET from_addr = '{$fromAddressName}', to_addrs = '{$toAddressName}', cc_addrs = '{$ccAddressName}', bcc_addrs = '{$bccAddressName}', reply_to_addr = '{$replyToAddrName}', description = '{$description}', description_html = '{$description_html}', raw_source = '{$raw_source}' WHERE email_id = '{$this->id}'";
1149 $q = "INSERT INTO emails_text (email_id, from_addr, to_addrs, cc_addrs, bcc_addrs, reply_to_addr, description, description_html, raw_source, deleted) VALUES('{$this->id}', '{$fromAddressName}', '{$toAddressName}', '{$ccAddressName}', '{$bccAddressName}', '{$replyToAddrName}', '{$description}', '{$description_html}', '{$raw_source}', 0)";
1151 $this->db->query($q);
1157 ///////////////////////////////////////////////////////////////////////////
1159 function retrieve($id, $encoded=true, $deleted=true) {
1160 // cn: bug 11915, return SugarBean's retrieve() call bean instead of $this
1161 $ret = parent::retrieve($id, $encoded, $deleted);
1164 $ret->retrieveEmailText();
1165 $ret->retrieveEmailAddresses();
1166 $ret->raw_source = to_html($ret->safeText(from_html($ret->raw_source)));
1167 $ret->description = to_html($ret->safeText(from_html($ret->description)));
1168 $ret->description_html = $ret->safeText($ret->description_html);
1170 $ret->date_start = '';
1171 $ret->time_start = '';
1172 $dateSent = explode(' ', $ret->date_sent);
1173 if (!empty($dateSent)) {
1174 $ret->date_start = $dateSent[0];
1175 if ( isset($dateSent[1]) )
1176 $ret->time_start = $dateSent[1];
1179 foreach($ret as $k => $v) {
1188 * Retrieves email addresses from GUIDs
1190 function retrieveEmailAddresses() {
1193 $q = "SELECT email_address, address_type
1194 FROM emails_email_addr_rel eam
1195 JOIN email_addresses ea ON ea.id = eam.email_address_id
1196 WHERE eam.email_id = '{$this->id}' AND eam.deleted=0";
1197 $r = $this->db->query($q);
1199 while($a = $this->db->fetchByAssoc($r)) {
1200 if(!isset($return[$a['address_type']])) {
1201 $return[$a['address_type']] = array();
1203 $return[$a['address_type']][] = $a['email_address'];
1206 if(count($return) > 0) {
1207 if(isset($return['from'])) {
1208 $this->from_addr = implode(", ", $return['from']);
1210 if(isset($return['to'])) {
1211 $this->to_addrs = implode(", ", $return['to']);
1213 if(isset($return['cc'])) {
1214 $this->cc_addrs = implode(", ", $return['cc']);
1216 if(isset($return['bcc'])) {
1217 $this->bcc_addrs = implode(", ", $return['bcc']);
1223 * Handles longtext fields
1225 function retrieveEmailText() {
1226 $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}'";
1227 $r = $this->db->query($q);
1228 $a = $this->db->fetchByAssoc($r, -1, false);
1230 $this->description = $a['description'];
1231 $this->description_html = $a['description_html'];
1232 $this->raw_source = $a['raw_source'];
1233 $this->from_addr_name = $a['from_addr'];
1234 $this->reply_to_addr = $a['reply_to_addr'];
1235 $this->to_addrs_names = $a['to_addrs'];
1236 $this->cc_addrs_names = $a['cc_addrs'];
1237 $this->bcc_addrs_names = $a['bcc_addrs'];
1240 function delete($id='') {
1244 $q = "UPDATE emails SET deleted = 1 WHERE id = '{$id}'";
1245 $qt = "UPDATE emails_text SET deleted = 1 WHERE email_id = '{$id}'";
1246 $r = $this->db->query($q);
1247 $rt = $this->db->query($qt);
1251 * creates the standard "Forward" info at the top of the forwarded message
1254 function getForwardHeader() {
1255 global $mod_strings;
1256 global $current_user;
1258 //$from = str_replace(array(">","<"), array(")","("), $this->from_name);
1259 $from = to_html($this->from_name);
1260 $subject = to_html($this->name);
1261 $ret = "<br /><br />";
1262 $ret .= $this->replyDelimiter."{$mod_strings['LBL_FROM']} {$from}<br />";
1263 $ret .= $this->replyDelimiter."{$mod_strings['LBL_DATE_SENT']} {$this->date_sent}<br />";
1264 $ret .= $this->replyDelimiter."{$mod_strings['LBL_TO']} {$this->to_addrs}<br />";
1265 $ret .= $this->replyDelimiter."{$mod_strings['LBL_CC']} {$this->cc_addrs}<br />";
1266 $ret .= $this->replyDelimiter."{$mod_strings['LBL_SUBJECT']} {$subject}<br />";
1267 $ret .= $this->replyDelimiter."<br />";
1270 //return from_html($ret);
1274 * retrieves Notes that belong to this Email and stuffs them into the "attachments" attribute
1276 function getNotes($id, $duplicate=false) {
1277 if(!class_exists('Note')) {
1281 $exRemoved = array();
1282 if(isset($_REQUEST['removeAttachment'])) {
1283 $exRemoved = explode('::', $_REQUEST['removeAttachment']);
1286 $noteArray = array();
1287 $q = "SELECT id FROM notes WHERE parent_id = '".$id."'";
1288 $r = $this->db->query($q);
1290 while($a = $this->db->fetchByAssoc($r)) {
1291 if(!in_array($a['id'], $exRemoved)) {
1293 $note->retrieve($a['id']);
1295 // duplicate actual file when creating forwards
1297 if(!class_exists('UploadFile')) {
1298 require_once('include/upload_file.php');
1300 // save a brand new Note
1301 $noteDupe->id = create_guid();
1302 $noteDupe->new_with_id = true;
1303 $noteDupe->parent_id = $this->id;
1304 $noteDupe->parent_type = $this->module_dir;
1306 $noteFile = new UploadFile('none');
1307 $noteFile->duplicate_file($a['id'], $note->id, $note->filename);
1311 // add Note to attachments array
1312 $this->attachments[] = $note;
1318 * creates the standard "Reply" info at the top of the forwarded message
1321 function getReplyHeader() {
1322 global $mod_strings;
1323 global $current_user;
1325 $from = str_replace(array(">","<", ">","<"), array(")","(",")","("), $this->from_name);
1326 $ret = "<br>{$mod_strings['LBL_REPLY_HEADER_1']} {$this->date_start}, {$this->time_start}, {$from} {$mod_strings['LBL_REPLY_HEADER_2']}";
1328 return from_html($ret);
1332 * Quotes plain-text email text
1333 * @param string $text
1336 function quotePlainTextEmail($text) {
1340 $desc = nl2br(trim($text));
1341 $exDesc = explode('<br />', $desc);
1343 foreach($exDesc as $k => $line) {
1344 $quoted .= '> '.trim($line)."\r";
1351 * "quotes" (i.e., "> my text yadda" the HTML part of an email
1352 * @param string $text HTML text to quote
1355 function quoteHtmlEmail($text) {
1356 $text = trim(from_html($text));
1361 $out = "<div style='border-left:1px solid #00c; padding:5px; margin-left:10px;'>{$text}</div>";
1367 * "quotes" (i.e., "> my text yadda" the HTML part of an email
1368 * @param string $text HTML text to quote
1371 function quoteHtmlEmailForNewEmailUI($text) {
1372 $text = trim($text);
1377 $text = str_replace("\n", "\n<BR/>", $text);
1378 $out = "<div style='border-left:1px solid #00c; padding:5px; margin-left:10px;'>{$text}</div>";
1385 ///////////////////////////////////////////////////////////////////////////
1388 * Safes description text (both HTML and Plain Text) for display
1389 * @param string str The text to safe
1390 * @return string Safed text
1392 function safeText($str) {
1394 $this->safe->clear();
1395 $ret = $this->safe->parse($str);
1397 // Julian's XSS cleaner
1398 $potentials = clean_xss($str, false);
1400 if(is_array($potentials) && !empty($potentials)) {
1401 //_ppl($potentials);
1402 foreach($potentials as $bad) {
1403 $ret = str_replace($bad, "", $ret);
1407 // clean <HTML> and <BODY> tags
1408 $html = '#<\\\\\?HTML[\w =\'\"\&]*>#sim';
1409 $body = '#<\\\\\?BODY[\w =\'\"\&]*>#sim';
1411 $ret = preg_replace($html, "", $ret);
1412 $ret = preg_replace($body, "", $ret);
1418 * Ensures that the user is able to send outbound emails
1420 function check_email_settings() {
1421 global $current_user;
1423 $mail_fromaddress = $current_user->emailAddress->getPrimaryAddress($current_user);
1424 $replyToName = $current_user->getPreference('mail_fromname');
1425 $mail_fromname = (!empty($replyToName)) ? $current_user->getPreference('mail_fromname') : $current_user->full_name;
1427 if(empty($mail_fromaddress)) {
1430 if(empty($mail_fromname)) {
1434 $send_type = $current_user->getPreference('mail_sendtype') ;
1435 if (!empty($send_type) && $send_type == "SMTP") {
1436 $mail_smtpserver = $current_user->getPreference('mail_smtpserver');
1437 $mail_smtpport = $current_user->getPreference('mail_smtpport');
1438 $mail_smtpauth_req = $current_user->getPreference('mail_smtpauth_req');
1439 $mail_smtpuser = $current_user->getPreference('mail_smtpuser');
1440 $mail_smtppass = $current_user->getPreference('mail_smtppass');
1441 if (empty($mail_smtpserver) ||
1442 empty($mail_smtpport) ||
1443 (!empty($mail_smtpauth_req) && ( empty($mail_smtpuser) || empty($mail_smtppass)))
1452 * outputs JS to set fields in the MassUpdate form in the "My Inbox" view
1454 function js_set_archived() {
1455 global $mod_strings;
1457 <script type="text/javascript" language="JavaScript"><!-- Begin
1458 function setArchived() {
1459 var form = document.getElementById("MassUpdate");
1460 var status = document.getElementById("mass_status");
1463 for(var i=0; i < form.elements.length; i++) {
1464 if(form.elements[i].name == "mass[]") {
1465 if(form.elements[i].checked == true) {
1472 var user = document.getElementById("mass_assigned_user_name");
1473 var team = document.getElementById("team");
1476 for(var j=0; j<status.length; j++) {
1477 if(status.options[j].value == "archived") {
1478 status.options[j].selected = true;
1479 status.selectedIndex = j; // for IE
1485 alert("'.$mod_strings['ERR_ARCHIVE_EMAIL'].'");
1489 // End --></script>';
1494 * replaces the javascript in utils.php - more specialized
1496 function u_get_clear_form_js($type='', $group='', $assigned_user_id='') {
1499 $uAssigned_user_id = '';
1501 if(!empty($type)) { $uType = '&type='.$type; }
1502 if(!empty($group)) { $uGroup = '&group='.$group; }
1503 if(!empty($assigned_user_id)) { $uAssigned_user_id = '&assigned_user_id='.$assigned_user_id; }
1506 <script type="text/javascript" language="JavaScript"><!-- Begin
1507 function clear_form(form) {
1508 var newLoc = "index.php?action=" + form.action.value + "&module=" + form.module.value + "&query=true&clear_query=true'.$uType.$uGroup.$uAssigned_user_id.'";
1509 if(typeof(form.advanced) != "undefined"){
1510 newLoc += "&advanced=" + form.advanced.value;
1512 document.location.href= newLoc;
1514 // End --></script>';
1518 function pickOneButton() {
1520 global $mod_strings;
1521 $out = '<div><input title="'.$mod_strings['LBL_BUTTON_GRAB_TITLE'].'"
1522 accessKey="'.$mod_strings['LBL_BUTTON_GRAB_KEY'].'"
1524 type="button" name="button"
1525 onClick="window.location=\'index.php?module=Emails&action=Grab\';"
1526 style="margin-bottom:2px"
1527 value=" '.$mod_strings['LBL_BUTTON_GRAB'].' "></div>';
1532 * Determines what Editor (HTML or Plain-text) the current_user uses;
1533 * @return string Editor type
1535 function getUserEditorPreference() {
1536 global $sugar_config;
1537 global $current_user;
1541 if(!isset($sugar_config['email_default_editor'])) {
1542 $sugar_config = $current_user->setDefaultsInConfig();
1545 $userEditor = $current_user->getPreference('email_editor_option');
1546 $systemEditor = $sugar_config['email_default_editor'];
1548 if($userEditor != '') {
1549 $editor = $userEditor;
1551 $editor = $systemEditor;
1558 * takes the mess we pass from EditView and tries to create some kind of order
1559 * @param array addrs
1560 * @param array addrs_ids (from contacts)
1561 * @param array addrs_names (from contacts);
1562 * @param array addrs_emails (from contacts);
1563 * @return array Parsed assoc array to feed to PHPMailer
1565 function parse_addrs($addrs, $addrs_ids, $addrs_names, $addrs_emails) {
1566 // cn: bug 9406 - enable commas to separate email addresses
1567 $addrs = str_replace(",", ";", $addrs);
1569 $ltgt = array('<','>');
1570 $gtlt = array('<','>');
1573 $addrs = str_replace($ltgt, '', $addrs);
1574 $addrs_arr = explode(";",$addrs);
1575 $addrs_arr = $this->remove_empty_fields($addrs_arr);
1576 $addrs_ids_arr = explode(";",$addrs_ids);
1577 $addrs_ids_arr = $this->remove_empty_fields($addrs_ids_arr);
1578 $addrs_emails_arr = explode(";",$addrs_emails);
1579 $addrs_emails_arr = $this->remove_empty_fields($addrs_emails_arr);
1580 $addrs_names_arr = explode(";",$addrs_names);
1581 $addrs_names_arr = $this->remove_empty_fields($addrs_names_arr);
1583 ///////////////////////////////////////////////////////////////////////
1584 //// HANDLE EMAILS HAND-WRITTEN
1585 $contactRecipients = array();
1586 $knownEmails = array();
1588 foreach($addrs_arr as $i => $v) {
1590 continue; // skip any "blanks" - will always have 1
1592 $recipient = array();
1594 //// get the email to see if we're dealing with a dupe
1595 //// what crappy coding
1596 preg_match("/[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,}/i",$v, $match);
1598 if(!empty($match[0]) && !in_array(trim($match[0]), $knownEmails)) {
1599 $knownEmails[] = $match[0];
1600 $recipient['email'] = $match[0];
1602 //// handle the Display name
1603 $display = trim(str_replace($match[0], '', $v));
1605 //// only trigger a "displayName" <email@address> when necessary
1606 if(isset($addrs_names_arr[$i])){
1607 $recipient['display'] = $addrs_names_arr[$i];
1609 else if(!empty($display)) {
1610 $recipient['display'] = $display;
1612 if(isset($addrs_ids_arr[$i]) && $addrs_emails_arr[$i] == $match[0]){
1613 $recipient['contact_id'] = $addrs_ids_arr[$i];
1615 $return[] = $recipient;
1622 function remove_empty_fields(&$arr) {
1625 foreach($arr as $field) {
1626 $field = trim($field);
1630 array_push($newarr,$field);
1636 * handles attachments of various kinds when sending email
1638 function handleAttachments() {
1643 global $mod_strings;
1645 ///////////////////////////////////////////////////////////////////////////
1646 //// ATTACHMENTS FROM DRAFTS
1647 if(($this->type == 'out' || $this->type == 'draft') && $this->status == 'draft' && isset($_REQUEST['record'])) {
1648 $this->getNotes($_REQUEST['record']); // cn: get notes from OLD email for use in new email
1650 //// END ATTACHMENTS FROM DRAFTS
1651 ///////////////////////////////////////////////////////////////////////////
1653 ///////////////////////////////////////////////////////////////////////////
1654 //// ATTACHMENTS FROM FORWARDS
1655 // Bug 8034 Jenny - Need the check for type 'draft' here to handle cases where we want to save
1656 // forwarded messages as drafts. We still need to save the original message's attachments.
1657 if(($this->type == 'out' || $this->type == 'draft') &&
1658 isset($_REQUEST['origType']) && $_REQUEST['origType'] == 'forward' &&
1659 isset($_REQUEST['return_id']) && !empty($_REQUEST['return_id'])
1661 $this->getNotes($_REQUEST['return_id'], true);
1664 // cn: bug 8034 - attachments from forward/replies lost when saving in draft
1665 if(isset($_REQUEST['prior_attachments']) && !empty($_REQUEST['prior_attachments']) && $this->new_with_id == true) {
1666 $exIds = explode(",", $_REQUEST['prior_attachments']);
1667 if(!isset($_REQUEST['template_attachment'])) {
1668 $_REQUEST['template_attachment'] = array();
1670 $_REQUEST['template_attachment'] = array_merge($_REQUEST['template_attachment'], $exIds);
1672 //// END ATTACHMENTS FROM FORWARDS
1673 ///////////////////////////////////////////////////////////////////////////
1675 ///////////////////////////////////////////////////////////////////////////
1676 //// ATTACHMENTS FROM TEMPLATES
1677 // to preserve individual email integrity, we must dupe Notes and associated files
1678 // for each outbound email - good for integrity, bad for filespace
1679 if(isset($_REQUEST['template_attachment']) && !empty($_REQUEST['template_attachment'])) {
1680 $removeArr = array();
1681 $noteArray = array();
1683 if(isset($_REQUEST['temp_remove_attachment']) && !empty($_REQUEST['temp_remove_attachment'])) {
1684 $removeArr = $_REQUEST['temp_remove_attachment'];
1688 foreach($_REQUEST['template_attachment'] as $noteId) {
1689 if(in_array($noteId, $removeArr)) {
1692 $noteTemplate = new Note();
1693 $noteTemplate->retrieve($noteId);
1694 $noteTemplate->id = create_guid();
1695 $noteTemplate->new_with_id = true; // duplicating the note with files
1696 $noteTemplate->parent_id = $this->id;
1697 $noteTemplate->parent_type = $this->module_dir;
1698 $noteTemplate->date_entered = '';
1699 $noteTemplate->save();
1701 $noteFile = new UploadFile('none');
1702 $noteFile->duplicate_file($noteId, $noteTemplate->id, $noteTemplate->filename);
1703 $noteArray[] = $noteTemplate;
1705 $this->attachments = array_merge($this->attachments, $noteArray);
1707 //// END ATTACHMENTS FROM TEMPLATES
1708 ///////////////////////////////////////////////////////////////////////////
1710 ///////////////////////////////////////////////////////////////////////////
1711 //// ADDING NEW ATTACHMENTS
1712 $max_files_upload = 10;
1713 // Jenny - Bug 8211 Since attachments for drafts have already been processed,
1714 // we don't need to re-process them.
1715 if($this->status != "draft") {
1716 $notes_list = array();
1717 if(!empty($this->id) && !$this->new_with_id) {
1719 $where = "notes.parent_id='{$this->id}'";
1720 $notes_list = $note->get_full_list("", $where, true);
1722 $this->attachments = array_merge($this->attachments, $notes_list);
1724 // cn: Bug 5995 - rudimentary error checking
1725 $filesError = array(
1726 0 => 'UPLOAD_ERR_OK - There is no error, the file uploaded with success.',
1727 1 => 'UPLOAD_ERR_INI_SIZE - The uploaded file exceeds the upload_max_filesize directive in php.ini.',
1728 2 => 'UPLOAD_ERR_FORM_SIZE - The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.',
1729 3 => 'UPLOAD_ERR_PARTIAL - The uploaded file was only partially uploaded.',
1730 4 => 'UPLOAD_ERR_NO_FILE - No file was uploaded.',
1731 5 => 'UNKNOWN ERROR',
1732 6 => 'UPLOAD_ERR_NO_TMP_DIR - Missing a temporary folder. Introduced in PHP 4.3.10 and PHP 5.0.3.',
1733 7 => 'UPLOAD_ERR_CANT_WRITE - Failed to write file to disk. Introduced in PHP 5.1.0.',
1736 for($i = 0; $i < $max_files_upload; $i++) {
1737 // cn: Bug 5995 - rudimentary error checking
1738 if (!isset($_FILES["email_attachment{$i}"])) {
1739 $GLOBALS['log']->debug("Email Attachment {$i} does not exist.");
1742 if($_FILES['email_attachment'.$i]['error'] != 0 && $_FILES['email_attachment'.$i]['error'] != 4) {
1743 $GLOBALS['log']->debug('Email Attachment could not be attach due to error: '.$filesError[$_FILES['email_attachment'.$i]['error']]);
1748 $note->parent_id = $this->id;
1749 $note->parent_type = $this->module_dir;
1750 $upload_file = new UploadFile('email_attachment'.$i);
1752 if(empty($upload_file)) {
1756 if(isset($_FILES['email_attachment'.$i]) && $upload_file->confirm_upload()) {
1757 $note->filename = $upload_file->get_stored_file_name();
1758 $note->file = $upload_file;
1759 $note->name = $mod_strings['LBL_EMAIL_ATTACHMENT'].': '.$note->file->original_file_name;
1761 $this->attachments[] = $note;
1765 $this->saved_attachments = array();
1766 foreach($this->attachments as $note) {
1767 if(!empty($note->id)) {
1768 array_push($this->saved_attachments, $note);
1771 $note->parent_id = $this->id;
1772 $note->parent_type = 'Emails';
1773 $note->file_mime_type = $note->file->mime_type;
1774 $note_id = $note->save();
1776 $this->saved_attachments[] = $note;
1778 $note->id = $note_id;
1779 $note->file->final_move($note->id);
1781 //// END NEW ATTACHMENTS
1782 ///////////////////////////////////////////////////////////////////////////
1784 ///////////////////////////////////////////////////////////////////////////
1785 //// ATTACHMENTS FROM DOCUMENTS
1786 for($i=0; $i<10; $i++) {
1787 if(isset($_REQUEST['documentId'.$i]) && !empty($_REQUEST['documentId'.$i])) {
1788 $doc = new Document();
1789 $docRev = new DocumentRevision();
1790 $docNote = new Note();
1791 $noteFile = new UploadFile('none');
1793 $doc->retrieve($_REQUEST['documentId'.$i]);
1794 $docRev->retrieve($doc->document_revision_id);
1796 $this->saved_attachments[] = $docRev;
1798 // cn: bug 9723 - Emails with documents send GUID instead of Doc name
1799 $docNote->name = $docRev->getDocumentRevisionNameForDisplay();
1800 $docNote->filename = $docRev->filename;
1801 $docNote->description = $doc->description;
1802 $docNote->parent_id = $this->id;
1803 $docNote->parent_type = 'Emails';
1804 $docNote->file_mime_type = $docRev->file_mime_type;
1805 $docId = $docNote = $docNote->save();
1807 $noteFile->duplicate_file($docRev->id, $docId, $docRev->filename);
1811 //// END ATTACHMENTS FROM DOCUMENTS
1812 ///////////////////////////////////////////////////////////////////////////
1814 ///////////////////////////////////////////////////////////////////////////
1815 //// REMOVE ATTACHMENTS
1816 if(isset($_REQUEST['remove_attachment']) && !empty($_REQUEST['remove_attachment'])) {
1817 foreach($_REQUEST['remove_attachment'] as $noteId) {
1818 $q = 'UPDATE notes SET deleted = 1 WHERE id = \''.$noteId.'\'';
1819 $this->db->query($q);
1823 //this will remove attachments that have been selected to be removed from drafts.
1824 if(isset($_REQUEST['removeAttachment']) && !empty($_REQUEST['removeAttachment'])) {
1825 $exRemoved = explode('::', $_REQUEST['removeAttachment']);
1826 foreach($exRemoved as $noteId) {
1827 $q = 'UPDATE notes SET deleted = 1 WHERE id = \''.$noteId.'\'';
1828 $this->db->query($q);
1831 //// END REMOVE ATTACHMENTS
1832 ///////////////////////////////////////////////////////////////////////////
1837 * Determines if an email body (HTML or Plain) has a User signature already in the content
1838 * @param array Array of signatures
1841 function hasSignatureInBody($sig) {
1842 // strpos can't handle line breaks - normalize
1843 $html = $this->removeAllNewlines($this->description_html);
1844 $htmlSig = $this->removeAllNewlines($sig['signature_html']);
1845 $plain = $this->removeAllNewlines($this->description);
1846 $plainSig = $this->removeAllNewlines($sig['signature']);
1848 // cn: bug 11621 - empty sig triggers notice error
1849 if(!empty($htmlSig) && false !== strpos($html, $htmlSig)) {
1851 } elseif(!empty($plainSig) && false !== strpos($plain, $plainSig)) {
1860 * @param string String to be normalized
1863 function removeAllNewlines($str) {
1864 $bad = array("\r\n", "\n\r", "\n", "\r");
1865 $good = array('', '', '', '');
1867 return str_replace($bad, $good, strip_tags(br2nl(from_html($str))));
1873 * Set navigation anchors to aid DetailView record navigation (VCR buttons)
1874 * @param string uri The URI from the referring page (always ListView)
1875 * @return array start Array of the URI broken down with a special "current_view" for My Inbox Navs
1877 function getStartPage($uri) {
1878 if(strpos($uri, '&')) { // "&" to ensure that we can explode the GET vars - else we're gonna trigger a Notice error
1879 $serial = substr($uri, (strpos($uri, '?')+1), strlen($uri));
1880 $exUri = explode('&', $serial);
1881 $start = array('module' => '', 'action' => '', 'group' => '', 'record' => '', 'type' => '');
1883 foreach($exUri as $k => $pair) {
1884 $exPair = explode('=', $pair);
1885 $start[$exPair[0]] = $exPair[1];
1888 // specific views for current_user
1889 if(isset($start['assigned_user_id'])) {
1890 $start['current_view'] = "{$start['action']}&module={$start['module']}&assigned_user_id={$start['assigned_user_id']}&type={$start['type']}";
1900 * preps SMTP info for email transmission
1901 * @param object mail SugarPHPMailer object
1902 * @param string mailer_id
1903 * @param string ieId
1904 * @return object mail SugarPHPMailer object
1906 function setMailer($mail, $mailer_id='', $ieId='') {
1907 global $current_user;
1909 require_once("include/OutboundEmail/OutboundEmail.php");
1910 $oe = new OutboundEmail();
1911 $oe = $oe->getInboundMailerSettings($current_user, $mailer_id, $ieId);
1913 // ssl or tcp - keeping outside isSMTP b/c a default may inadvertantly set ssl://
1914 $mail->protocol = ($oe->mail_smtpssl) ? "ssl://" : "tcp://";
1915 if($oe->mail_sendtype == "SMTP")
1917 //Set mail send type information
1918 $mail->Mailer = "smtp";
1919 $mail->Host = $oe->mail_smtpserver;
1920 $mail->Port = $oe->mail_smtpport;
1921 if ($oe->mail_smtpssl == 1) {
1922 $mail->SMTPSecure = 'ssl';
1924 if ($oe->mail_smtpssl == 2) {
1925 $mail->SMTPSecure = 'tls';
1928 if($oe->mail_smtpauth_req) {
1929 $mail->SMTPAuth = TRUE;
1930 $mail->Username = $oe->mail_smtpuser;
1931 $mail->Password = $oe->mail_smtppass;
1935 $mail->Mailer = "sendmail";
1942 * preps SugarPHPMailer object for HTML or Plain text sends
1943 * @param object SugarPHPMailer instance
1945 function handleBody($mail) {
1946 global $current_user;
1947 global $sugar_config;
1948 ///////////////////////////////////////////////////////////////////////
1949 //// HANDLE EMAIL FORMAT PREFERENCE
1950 // the if() below is HIGHLY dependent on the Javascript unchecking the Send HTML Email box
1952 if( (isset($_REQUEST['setEditor']) /* from Email EditView navigation */
1953 && $_REQUEST['setEditor'] == 1
1954 && trim($_REQUEST['description_html']) != '')
1955 || trim($this->description_html) != '' /* from email templates */
1956 && $current_user->getPreference('email_editor_option', 'global') !== 'plain' //user preference is not set to plain text
1958 $this->handleBodyInHTMLformat($mail);
1961 $this->description_html = '';
1962 $mail->IsHTML(false);
1963 $plainText = from_html($this->description);
1964 $plainText = str_replace(" ", " ", $plainText);
1965 $plainText = str_replace("</p>", "</p><br />", $plainText);
1966 $plainText = strip_tags(br2nl($plainText));
1967 $plainText = str_replace("&", "&", $plainText);
1968 $plainText = str_replace("'", "'", $plainText);
1969 $mail->Body = wordwrap($plainText, 996);
1970 $mail->Body = $this->decodeDuringSend($mail->Body);
1971 $this->description = $mail->Body;
1974 // wp: if plain text version has lines greater than 998, use base64 encoding
1975 foreach(explode("\n", ($mail->ContentType == "text/html") ? $mail->AltBody : $mail->Body) as $line) {
1976 if(strlen($line) > 998) {
1977 $mail->Encoding = 'base64';
1981 //// HANDLE EMAIL FORMAT PREFERENCE
1982 ///////////////////////////////////////////////////////////////////////
1988 * Retrieve function from handlebody() to unit test easily
1990 * @return formatted $mail body
1992 function handleBodyInHTMLformat($mail) {
1993 global $current_user;
1994 global $sugar_config;
1995 // wp: if body is html, then insert new lines at 996 characters. no effect on client side
1996 // due to RFC 2822 which limits email lines to 998
1997 $mail->IsHTML(true);
1998 $body = from_html(wordwrap($this->description_html, 996));
1999 $mail->Body = $body;
2002 // new plan is to use the selected type (html or plain) to fill the other
2003 $plainText = from_html($this->description_html);
2004 $plainText = strip_tags(br2nl($plainText));
2005 $mail->AltBody = $plainText;
2006 $this->description = $plainText;
2008 $fileBasePath = "{$sugar_config['cache_dir']}images/";
2009 $filePatternSearch = "{$sugar_config['cache_dir']}";
2010 $filePatternSearch = str_replace("/", "\/", $filePatternSearch);
2011 $filePatternSearch = $filePatternSearch . "images\/";
2012 if(strpos($mail->Body, "\"{$fileBasePath}") !== FALSE)
2015 preg_match_all("/{$filePatternSearch}.+?\"/i", $mail->Body, $matches);
2016 foreach($matches[0] as $match) {
2017 $filename = str_replace($fileBasePath, '', $match);
2018 $filename = urldecode(substr($filename, 0, -1));
2020 $file_location = clean_path(getcwd()."/{$sugar_config['cache_dir']}images/{$filename}");
2021 $mime_type = "image/".strtolower(substr($filename, strrpos($filename, ".")+1, strlen($filename)));
2023 if(file_exists($file_location)) {
2024 $mail->AddEmbeddedImage($file_location, $cid, $filename, 'base64', $mime_type);
2028 //replace references to cache with cid tag
2029 $mail->Body = str_replace("/" . $fileBasePath,'cid:',$mail->Body);
2030 $mail->Body = str_replace($fileBasePath,'cid:',$mail->Body);
2031 // remove bad img line from outbound email
2032 $regex = '#<img[^>]+src[^=]*=\"\/([^>]*?[^>]*)>#sim';
2033 $mail->Body = preg_replace($regex, '', $mail->Body);
2035 $fileBasePath = "{$sugar_config['upload_dir']}";
2036 $filePatternSearch = "{$sugar_config['upload_dir']}";
2037 $filePatternSearch = str_replace("/", "\/", $filePatternSearch);
2038 if(strpos($mail->Body, "\"{$fileBasePath}") !== FALSE)
2041 preg_match_all("/{$filePatternSearch}.+?\"/i", $mail->Body, $matches);
2042 foreach($matches[0] as $match) {
2043 $filename = str_replace($fileBasePath, '', $match);
2044 $filename = urldecode(substr($filename, 0, -1));
2046 $file_location = clean_path(getcwd()."/{$sugar_config['upload_dir']}{$filename}");
2047 $mime_type = "image/".strtolower(substr($filename, strrpos($filename, ".")+1, strlen($filename)));
2049 if(file_exists($file_location)) {
2050 $mail->AddEmbeddedImage($file_location, $cid, $filename, 'base64', $mime_type);
2054 //replace references to cache with cid tag
2055 $mail->Body = str_replace("/" . $fileBasePath,'cid:',$mail->Body);
2056 $mail->Body = str_replace($fileBasePath,'cid:',$mail->Body);
2058 // remove bad img line from outbound email
2059 $regex = '#<img[^>]+src[^=]*=\"\/([^>]*?[^>]*)>#sim';
2060 $mail->Body = preg_replace($regex, '', $mail->Body);
2063 //Replace any embeded images using the secure entryPoint for src url.
2064 $noteImgRegex = "/<img[^>]*[\s]+src[^=]*=\"index.php\?entryPoint=download\&id=([^\&]*)[^>]*>/im";
2065 $embededImageMatches = array();
2066 preg_match_all($noteImgRegex, $mail->Body, $embededImageMatches,PREG_SET_ORDER);
2068 foreach ($embededImageMatches as $singleMatch )
2070 $fullMatch = $singleMatch[0];
2071 $noteId = $singleMatch[1];
2073 $filename = $noteId;
2075 //Retrieve note for mimetype
2076 $tmpNote = new Note();
2077 $tmpNote->retrieve($noteId);
2078 //Replace the src part of img tag with new cid tag
2079 $cidRegex = "/src=\"([^\"]*)\"/im";
2080 $replaceMatch = preg_replace($cidRegex, "src=\"cid:$noteId\"", $fullMatch);
2082 //Replace the body, old tag for new tag
2083 $mail->Body = str_replace($fullMatch, $replaceMatch, $mail->Body);
2086 $file_location = clean_path(getcwd()."/{$sugar_config['upload_dir']}{$noteId}");
2088 if(file_exists($file_location))
2089 $mail->AddEmbeddedImage($file_location, $cid, $filename, 'base64', $tmpNote->file_mime_type);
2094 $mail->Body = from_html($mail->Body);
2099 * @return bool True on success
2102 global $mod_strings,$app_strings;
2103 global $current_user;
2104 global $sugar_config;
2106 $OBCharset = $locale->getPrecedentPreference('default_email_charset');
2107 $mail = new SugarPHPMailer();
2109 foreach ($this->to_addrs_arr as $addr_arr) {
2110 if ( empty($addr_arr['display'])) {
2111 $mail->AddAddress($addr_arr['email'], "");
2113 $mail->AddAddress($addr_arr['email'],$locale->translateCharsetMIME(trim( $addr_arr['display']), 'UTF-8', $OBCharset));
2116 foreach ($this->cc_addrs_arr as $addr_arr) {
2117 if ( empty($addr_arr['display'])) {
2118 $mail->AddCC($addr_arr['email'], "");
2120 $mail->AddCC($addr_arr['email'],$locale->translateCharsetMIME(trim($addr_arr['display']), 'UTF-8', $OBCharset));
2124 foreach ($this->bcc_addrs_arr as $addr_arr) {
2125 if ( empty($addr_arr['display'])) {
2126 $mail->AddBCC($addr_arr['email'], "");
2128 $mail->AddBCC($addr_arr['email'],$locale->translateCharsetMIME(trim($addr_arr['display']), 'UTF-8', $OBCharset));
2132 $mail = $this->setMailer($mail);
2135 if(!empty($this->from_addr)) {
2136 $mail->From = $this->from_addr;
2138 $mail->From = $current_user->getPreference('mail_fromaddress');
2139 $this->from_addr = $mail->From;
2142 if(!empty($this->from_name)) {
2143 $mail->FromName = $this->from_name;
2145 $mail->FromName = $current_user->getPreference('mail_fromname');
2146 $this->from_name = $mail->FromName;
2149 //Reply to information for case create and autoreply.
2150 if(!empty($this->reply_to_name)) {
2151 $ReplyToName = $this->reply_to_name;
2153 $ReplyToName = $mail->FromName;
2155 if(!empty($this->reply_to_addr)) {
2156 $ReplyToAddr = $this->reply_to_addr;
2158 $ReplyToAddr = $mail->From;
2160 $mail->Sender = $mail->From; /* set Return-Path field in header to reduce spam score in emails sent via Sugar's Email module */
2161 $mail->AddReplyTo($ReplyToAddr,$locale->translateCharsetMIME(trim($ReplyToName), 'UTF-8', $OBCharset));
2163 //$mail->Subject = html_entity_decode($this->name, ENT_QUOTES, 'UTF-8');
2164 $mail->Subject = $this->name;
2166 ///////////////////////////////////////////////////////////////////////
2168 foreach($this->saved_attachments as $note) {
2169 $mime_type = 'text/plain';
2170 if($note->object_name == 'Note') {
2171 if(!empty($note->file->temp_file_location) && is_file($note->file->temp_file_location)) { // brandy-new file upload/attachment
2172 $file_location = $sugar_config['upload_dir'].$note->id;
2173 $filename = $note->file->original_file_name;
2174 $mime_type = $note->file->mime_type;
2175 } else { // attachment coming from template/forward
2176 $file_location = rawurldecode(UploadFile::get_file_path($note->filename,$note->id));
2177 // cn: bug 9723 - documents from EmailTemplates sent with Doc Name, not file name.
2178 $filename = !empty($note->filename) ? $note->filename : $note->name;
2179 $mime_type = $note->file_mime_type;
2181 } elseif($note->object_name == 'DocumentRevision') { // from Documents
2182 $filePathName = $note->id;
2183 // cn: bug 9723 - Emails with documents send GUID instead of Doc name
2184 $filename = $note->getDocumentRevisionNameForDisplay();
2185 $file_location = getcwd().'/'.$GLOBALS['sugar_config']['upload_dir'].$filePathName;
2186 $mime_type = $note->file_mime_type;
2189 // strip out the "Email attachment label if exists
2190 $filename = str_replace($mod_strings['LBL_EMAIL_ATTACHMENT'].': ', '', $filename);
2192 //is attachment in our list of bad files extensions? If so, append .txt to file location
2193 //get position of last "." in file name
2194 $file_ext_beg = strrpos($file_location,".");
2196 //get file extension
2197 if($file_ext_beg >0){
2198 $file_ext = substr($file_location, $file_ext_beg+1 );
2200 //check to see if this is a file with extension located in "badext"
2201 foreach($sugar_config['upload_badext'] as $badExt) {
2202 if(strtolower($file_ext) == strtolower($badExt)) {
2203 //if found, then append with .txt to filename and break out of lookup
2204 //this will make sure that the file goes out with right extension, but is stored
2206 $file_location = $file_location . ".txt";
2207 break; // no need to look for more
2210 $mail->AddAttachment($file_location,$locale->translateCharsetMIME(trim($filename), 'UTF-8', $OBCharset), 'base64', $mime_type);
2213 if($note->embed_flag == true) {
2215 $mail->AddEmbeddedImage($file_location, $cid, $filename, 'base64',$mime_type);
2218 //// END ATTACHMENTS
2219 ///////////////////////////////////////////////////////////////////////
2221 $mail = $this->handleBody($mail);
2223 $GLOBALS['log']->debug('Email sending --------------------- ');
2225 ///////////////////////////////////////////////////////////////////////
2226 //// I18N TRANSLATION
2227 $mail->prepForOutbound();
2228 //// END I18N TRANSLATION
2229 ///////////////////////////////////////////////////////////////////////
2232 ///////////////////////////////////////////////////////////////////
2233 //// INBOUND EMAIL HANDLING
2235 if(!empty($_REQUEST['inbound_email_id'])) {
2236 $ieMail = new Email();
2237 $ieMail->retrieve($_REQUEST['inbound_email_id']);
2238 $ieMail->status = 'replied';
2241 $GLOBALS['log']->debug(' --------------------- buh bye -- sent successful');
2242 //// END INBOUND EMAIL HANDLING
2243 ///////////////////////////////////////////////////////////////////
2246 $GLOBALS['log']->debug($app_strings['LBL_EMAIL_ERROR_PREPEND'].$mail->ErrorInfo);
2251 function listviewACLHelper(){
2252 $array_assign = parent::listviewACLHelper();
2254 if(!empty($this->parent_name)){
2256 if(!empty($this->parent_name_owner)){
2257 global $current_user;
2258 $is_owner = $current_user->id == $this->parent_name_owner;
2261 if(!ACLController::moduleSupportsACL($this->parent_type) || ACLController::checkAccess($this->parent_type, 'view', $is_owner)){
2262 $array_assign['PARENT'] = 'a';
2264 $array_assign['PARENT'] = 'span';
2267 if(!empty($this->contact_name)) {
2268 if(!empty($this->contact_name_owner)) {
2269 global $current_user;
2270 $is_owner = $current_user->id == $this->contact_name_owner;
2273 if(ACLController::checkAccess('Contacts', 'view', $is_owner)) {
2274 $array_assign['CONTACT'] = 'a';
2276 $array_assign['CONTACT'] = 'span';
2279 return $array_assign;
2282 function getSystemDefaultEmail() {
2285 $r1 = $this->db->query('SELECT config.value FROM config WHERE name=\'fromaddress\'');
2286 $r2 = $this->db->query('SELECT config.value FROM config WHERE name=\'fromname\'');
2287 $a1 = $this->db->fetchByAssoc($r1);
2288 $a2 = $this->db->fetchByAssoc($r2);
2290 $email['email'] = $a1['value'];
2291 $email['name'] = $a2['value'];
2297 function create_new_list_query($order_by, $where,$filter=array(),$params=array(), $show_deleted = 0,$join_type='', $return_array = false,$parentbean=null, $singleSelect = false) {
2299 if ($return_array) {
2300 return parent::create_new_list_query($order_by, $where,$filter,$params, $show_deleted,$join_type, $return_array,$parentbean, $singleSelect);
2302 $custom_join = $this->custom_fields->getJOIN();
2304 $query = "SELECT ".$this->table_name.".*, users.user_name as assigned_user_name\n";
2307 $query .= $custom_join['select'];
2309 $query .= " FROM emails\n";
2310 if ($where != "" && (strpos($where, "contacts.first_name") > 0)) {
2311 $query .= " LEFT JOIN emails_beans ON emails.id = emails_beans.email_id\n";
2314 $query .= " LEFT JOIN users ON emails.assigned_user_id=users.id \n";
2315 if ($where != "" && (strpos($where, "contacts.first_name") > 0)) {
2317 $query .= " JOIN contacts ON contacts.id= emails_beans.bean_id AND emails_beans.bean_module='Contacts' and contacts.deleted=0 \n";
2321 $query .= $custom_join['join'];
2324 if($show_deleted == 0) {
2325 $where_auto = " emails.deleted=0 \n";
2326 }else if($show_deleted == 1){
2327 $where_auto = " emails.deleted=1 \n";
2331 $query .= "WHERE $where AND ".$where_auto;
2333 $query .= "WHERE ".$where_auto;
2336 $query .= " ORDER BY $order_by";
2338 $query .= " ORDER BY date_sent DESC";
2344 function fill_in_additional_list_fields() {
2346 $this->fill_in_additional_detail_fields();
2348 $this->link_action = 'DetailView';
2349 ///////////////////////////////////////////////////////////////////////
2350 //populate attachment_image, used to display attachment icon.
2351 $query = "select 1 from notes where notes.parent_id = '$this->id' and notes.deleted = 0";
2352 $result =$this->db->query($query,true," Error filling in additional list fields: ");
2354 $row = $this->db->fetchByAssoc($result);
2357 $this->attachment_image = SugarThemeRegistry::current()->getImage('attachment',"","","");
2359 $this->attachment_image = SugarThemeRegistry::current()->getImage('blank',"","","");
2361 ///////////////////////////////////////////////////////////////////////
2362 if(empty($this->contact_id) && !empty($this->parent_id) && !empty($this->parent_type) && $this->parent_type === 'Contacts' && !empty($this->parent_name) ){
2363 $this->contact_id = $this->parent_id;
2364 $this->contact_name = $this->parent_name;
2368 function fill_in_additional_detail_fields() {
2369 global $app_list_strings,$mod_strings;
2370 // Fill in the assigned_user_name
2371 $this->assigned_user_name = get_assigned_user_name($this->assigned_user_id, '');
2372 //if ($this->parent_type == 'Contacts') {
2373 $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 ";
2374 $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";
2375 if(!empty($this->parent_id)){
2376 $query .= " AND contacts.id= '".$this->parent_id."' ";
2377 }else if(!empty($_REQUEST['record'])){
2378 $query .= " AND contacts.id= '".$_REQUEST['record']."' ";
2380 $result =$this->db->query($query,true," Error filling in additional detail fields: ");
2382 // Get the id and the name.
2383 $row = $this->db->fetchByAssoc($result);
2387 $contact = new Contact();
2388 $contact->retrieve($row['id']);
2389 $this->contact_name = $contact->full_name;
2390 $this->contact_phone = $row['phone_work'];
2391 $this->contact_id = $row['id'];
2392 $this->contact_email = $contact->emailAddress->getPrimaryAddress($contact);
2393 $this->contact_name_owner = $row['contact_name_owner'];
2394 $this->contact_name_mod = $row['contact_name_mod'];
2395 $GLOBALS['log']->debug("Call($this->id): contact_name = $this->contact_name");
2396 $GLOBALS['log']->debug("Call($this->id): contact_phone = $this->contact_phone");
2397 $GLOBALS['log']->debug("Call($this->id): contact_id = $this->contact_id");
2398 $GLOBALS['log']->debug("Call($this->id): contact_email1 = $this->contact_email");
2401 $this->contact_name = '';
2402 $this->contact_phone = '';
2403 $this->contact_id = '';
2404 $this->contact_email = '';
2405 $this->contact_name_owner = '';
2406 $this->contact_name_mod = '';
2407 $GLOBALS['log']->debug("Call($this->id): contact_name = $this->contact_name");
2408 $GLOBALS['log']->debug("Call($this->id): contact_phone = $this->contact_phone");
2409 $GLOBALS['log']->debug("Call($this->id): contact_id = $this->contact_id");
2410 $GLOBALS['log']->debug("Call($this->id): contact_email1 = $this->contact_email");
2413 $this->created_by_name = get_assigned_user_name($this->created_by);
2414 $this->modified_by_name = get_assigned_user_name($this->modified_user_id);
2416 $this->link_action = 'DetailView';
2418 if(!empty($this->type)) {
2419 if($this->type == 'out' && $this->status == 'send_error') {
2420 $this->type_name = $mod_strings['LBL_NOT_SENT'];
2422 $this->type_name = $app_list_strings['dom_email_types'][$this->type];
2425 if(($this->type == 'out' && $this->status == 'send_error') || $this->type == 'draft') {
2426 $this->link_action = 'EditView';
2430 //todo this isset( $app_list_strings['dom_email_status'][$this->status]) is hack for 3261.
2431 if(!empty($this->status) && isset( $app_list_strings['dom_email_status'][$this->status])) {
2432 $this->status_name = $app_list_strings['dom_email_status'][$this->status];
2435 if ( empty($this->name ) && empty($_REQUEST['record'])) {
2436 $this->name = $mod_strings['LBL_NO_SUBJECT'];
2439 $this->fill_in_additional_parent_fields();
2444 function create_export_query(&$order_by, &$where) {
2445 $contact_required = stristr($where, "contacts");
2446 $custom_join = $this->custom_fields->getJOIN(true, true,$where);
2448 if($contact_required) {
2449 $query = "SELECT emails.*, contacts.first_name, contacts.last_name";
2451 $query .= $custom_join['select'];
2454 $query .= " FROM contacts, emails, emails_contacts ";
2455 $where_auto = "emails_contacts.contact_id = contacts.id AND emails_contacts.email_id = emails.id AND emails.deleted=0 AND contacts.deleted=0";
2457 $query = 'SELECT emails.*';
2459 $query .= $custom_join['select'];
2462 $query .= ' FROM emails ';
2463 $where_auto = "emails.deleted=0";
2467 $query .= $custom_join['join'];
2471 $query .= "where $where AND ".$where_auto;
2473 $query .= "where ".$where_auto;
2476 $query .= " ORDER BY $order_by";
2478 $query .= " ORDER BY emails.name";
2482 function get_list_view_data() {
2483 global $app_list_strings;
2485 global $current_user;
2487 global $mod_strings;
2489 $email_fields = $this->get_list_view_array();
2490 $this->retrieveEmailText();
2491 $email_fields['FROM_ADDR'] = $this->from_addr_name;
2492 $mod_strings = return_module_language($GLOBALS['current_language'], 'Emails'); // hard-coding for Home screen ListView
2494 if($this->status != 'replied') {
2495 $email_fields['QUICK_REPLY'] = '<a href="index.php?module=Emails&action=Compose&replyForward=true&reply=reply&record='.$this->id.'&inbound_email_id='.$this->id.'">'.$mod_strings['LNK_QUICK_REPLY'].'</a>';
2496 $email_fields['STATUS'] = ($email_fields['REPLY_TO_STATUS'] == 1 ? $mod_strings['LBL_REPLIED'] : $email_fields['STATUS']);
2498 $email_fields['QUICK_REPLY'] = $mod_strings['LBL_REPLIED'];
2500 if(!empty($this->parent_type)) {
2501 $email_fields['PARENT_MODULE'] = $this->parent_type;
2503 switch($this->intent) {
2505 $email_fields['CREATE_RELATED'] = '<a href="index.php?module=Cases&action=EditView&inbound_email_id='.$this->id.'" ><img border="0" src="'.SugarThemeRegistry::current()->getImageURL('CreateCases.gif').'">'.$mod_strings['LBL_CREATE_CASE'].'</a>';
2509 $email_fields['CREATE_RELATED'] = '<a href="index.php?module=Leads&action=EditView&inbound_email_id='.$this->id.'" ><img border="0" src="'.SugarThemeRegistry::current()->getImageURL('CreateLeads.gif').'">'.$mod_strings['LBL_CREATE_LEAD'].'</a>';
2513 $email_fields['CREATE_RELATED'] = '<a href="index.php?module=Contacts&action=EditView&inbound_email_id='.$this->id.'" ><img border="0" src="'.SugarThemeRegistry::current()->getImageURL('CreateContacts.gif').'">'.$mod_strings['LBL_CREATE_CONTACT'].'</a>';
2517 $email_fields['CREATE_RELATED'] = '<a href="index.php?module=Bugs&action=EditView&inbound_email_id='.$this->id.'" ><img border="0" src="'.SugarThemeRegistry::current()->getImageURL('CreateBugs.gif').'">'.$mod_strings['LBL_CREATE_BUG'].'</a>';
2521 $email_fields['CREATE_RELATED'] = '<a href="index.php?module=Tasks&action=EditView&inbound_email_id='.$this->id.'" ><img border="0" src="'.SugarThemeRegistry::current()->getImageURL('CreateTasks.gif').'">'.$mod_strings['LBL_CREATE_TASK'].'</a>';
2534 $email_fields['CREATE_RELATED'] = $this->quickCreateForm();
2540 //BUG 17098 - MFH changed $this->from_addr to $this->to_addrs
2541 $email_fields['CONTACT_NAME'] = empty($this->contact_name) ? '</a>'.$this->trimLongTo($this->to_addrs).'<a>' : $this->contact_name;
2542 $email_fields['CONTACT_ID'] = empty($this->contact_id) ? '' : $this->contact_id;
2543 $email_fields['ATTACHMENT_IMAGE'] = $this->attachment_image;
2544 $email_fields['LINK_ACTION'] = $this->link_action;
2546 if(isset($this->type_name))
2547 $email_fields['TYPE_NAME'] = $this->type_name;
2549 return $email_fields;
2552 function quickCreateForm() {
2553 global $mod_strings, $app_strings, $currentModule, $current_language;
2555 // Coming from the home page via Dashlets
2556 if($currentModule != 'Email')
2557 $mod_strings = return_module_language($current_language, 'Emails');
2558 return $mod_strings['LBL_QUICK_CREATE']." <a id='$this->id' onclick='return quick_create_overlib(\"{$this->id}\", \"".SugarThemeRegistry::current()->__toString()."\");' href=\"#\" >".SugarThemeRegistry::current()->getImage("advanced_search","alt='".$mod_strings['LBL_QUICK_CREATE']."' border='0' align='absmiddle'")."</a>";
2562 * Searches all imported emails and returns the result set as an array.
2565 function searchImportedEmails($sort = '', $direction='')
2567 require_once('include/TimeDate.php');
2569 global $current_user;
2571 global $sugar_config;
2572 global $app_strings;
2574 $emailSettings = $current_user->getPreference('emailSettings', 'Emails');
2575 // cn: default to a low number until user specifies otherwise
2576 if(empty($emailSettings['showNumInList']))
2579 $pageSize = $emailSettings['showNumInList'];
2581 if( isset($_REQUEST['start']) && isset($_REQUEST['limit']) )
2582 $page = ceil($_REQUEST['start'] / $_REQUEST['limit']) + 1;
2586 //Determine sort ordering
2588 //Sort ordering parameters in the request do not coincide with actual column names
2589 //so we need to remap them.
2590 $hrSortLocal = array(
2591 'flagged' => 'type',
2592 'status' => 'reply_to_status',
2593 'from' => 'emails_text.from_addr',
2594 'subject' => 'name',
2595 'date' => 'date_sent',
2596 'AssignedTo' => 'assigned_user_id',
2597 'flagged' => 'flagged'
2600 $sort = !empty($_REQUEST['sort']) ? $_REQUEST['sort'] : "";
2601 $direction = !empty($_REQUEST['dir']) ? $_REQUEST['dir'] : "";
2603 $order = ( !empty($sort) && !empty($direction) ) ? " ORDER BY {$hrSortLocal[$sort]} {$direction}" : "";
2605 //Get our main query.
2606 $fullQuery = $this->_genereateSearchImportedEmailsQuery();
2608 //Perform a count query needed for pagination.
2609 $countQuery = $this->create_list_count_query($fullQuery);
2610 $count_rs = $this->db->query($countQuery, false, 'Error executing count query for imported emails search');
2611 $count_row = $this->db->fetchByAssoc($count_rs);
2612 $total_count = ($count_row != null) ? $count_row['c'] : 0;
2614 $start = ($page - 1) * $pageSize;
2617 $rs = $this->db->limitQuery($fullQuery . $order, $start, $pageSize);
2621 while($a = $this->db->fetchByAssoc($rs)) {
2623 $temp['flagged'] = (is_null($a['flagged']) || $a['flagged'] == '0') ? '' : 1;
2624 $temp['status'] = (is_null($a['reply_to_status']) || $a['reply_to_status'] == '0') ? '' : 1;
2625 $temp['subject'] = $a['name'];
2626 $temp['date'] = $timedate->to_display_date_time($a['date_sent']);
2627 $temp['uid'] = $a['id'];
2628 $temp['ieId'] = $a['mailbox_id'];
2629 $temp['site_url'] = $sugar_config['site_url'];
2630 $temp['seen'] = ($a['status'] == 'unread') ? 0 : 1;
2631 $temp['type'] = $a['type'];
2632 $temp['mbox'] = 'sugar::Emails';
2633 $temp['hasAttach'] = $this->doesImportedEmailHaveAttachment($a['id']);
2634 //To and from addresses may be stored in emails_text, if nothing is found, revert to
2635 //regular email addresses.
2636 $temp['to_addrs'] = preg_replace('/[\x00-\x08\x0B-\x1F]/', '', $a['to_addrs']);
2637 $temp['from'] = preg_replace('/[\x00-\x08\x0B-\x1F]/', '', $a['from_addr']);
2638 if( empty($temp['from']) || empty($temp['to_addrs']) )
2640 //Retrieve email addresses seperatly.
2641 $tmpEmail = new Email();
2642 $tmpEmail->id = $a['id'];
2643 $tmpEmail->retrieveEmailAddresses();
2644 $temp['from'] = $tmpEmail->from_addr;
2645 $temp['to_addrs'] = $tmpEmail->to_addrs;
2651 $metadata = array();
2652 $metadata['totalCount'] = $total_count;
2653 $metadata['out'] = $return;
2659 * Determine if an imported email has an attachment by examining the relationship to notes.
2664 function doesImportedEmailHaveAttachment($id)
2666 $hasAttachment = FALSE;
2667 $query = "SELECT id FROM notes where parent_id='$id' AND parent_type='Emails' AND file_mime_type is not null AND deleted=0";
2668 $rs = $this->db->limitQuery($query, 0, 1);
2669 $row = $this->db->fetchByAssoc($rs);
2670 if( !empty($row['id']) )
2671 $hasAttachment = TRUE;
2673 return (int) $hasAttachment;
2677 * Generate the query used for searching imported emails.
2679 * @return String Query to be executed.
2681 function _genereateSearchImportedEmailsQuery()
2685 $additionalWhereClause = $this->_generateSearchImportWhereClause();
2689 $query['select'] = "emails.id , emails.mailbox_id, emails.name, emails.date_sent, emails.status, emails.type, emails.flagged, emails.reply_to_status,
2690 emails_text.from_addr, emails_text.to_addrs FROM emails ";
2692 $query['joins'] = " JOIN emails_text on emails.id = emails_text.email_id ";
2694 //Handle from and to addr joins
2695 if( !empty($_REQUEST['from_addr']) )
2697 $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
2698 AND er_from.address_type='from' AND ea_from.email_address='" . strtolower($_REQUEST['from_addr']) . "'";
2701 if( !empty($_REQUEST['to_addrs']) )
2703 $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
2704 AND er_to.address_type='to' AND ea_to.email_address='" . strtolower($_REQUEST['to_addrs']) . "'";
2707 $query['where'] = " WHERE (emails.type= 'inbound' OR emails.type='archived' OR emails.type='out') AND emails.deleted = 0 ";
2708 if( !empty($additionalWhereClause) )
2709 $query['where'] .= "AND $additionalWhereClause";
2711 //If we are explicitly looking for attachments. Do not use a distinct query as the to_addr is defined
2712 //as a text which equals clob in oracle and the distinct query can not be executed correctly.
2713 $addDistinctKeyword = "";
2714 if( !empty($_REQUEST['attachmentsSearch']) && $_REQUEST['attachmentsSearch'] == 1) //1 indicates yes
2715 $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 )";
2716 else if( !empty($_REQUEST['attachmentsSearch']) && $_REQUEST['attachmentsSearch'] == 2 )
2717 $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 )";
2719 $fullQuery = "SELECT " . $query['select'] . " " . $query['joins'] . " " . $query['where'];
2724 * Generate the where clause for searching imported emails.
2727 function _generateSearchImportWhereClause()
2731 //The clear button was removed so if a user removes the asisgned user name, do not process the id.
2732 if( empty($_REQUEST['assigned_user_name']) && !empty($_REQUEST['assigned_user_id']) )
2733 unset($_REQUEST['assigned_user_id']);
2735 $availableSearchParam = array('name' => array('table_name' =>'emails'),
2736 'data_parent_id_search' => array('table_name' =>'emails','db_key' => 'parent_id','opp' => '='),
2737 'assigned_user_id' => array('table_name' => 'emails', 'opp' => '=') );
2739 $additionalWhereClause = array();
2740 foreach ($availableSearchParam as $key => $properties)
2742 if( !empty($_REQUEST[$key]) )
2744 $db_key = isset($properties['db_key']) ? $properties['db_key'] : $key;
2745 $searchValue = $_REQUEST[$key];
2747 $opp = isset($properties['opp']) ? $properties['opp'] : 'like';
2749 $searchValue = $searchValue . "%";
2751 $additionalWhereClause[] = "{$properties['table_name']}.$db_key $opp '$searchValue' ";
2755 $isDateFromSearchSet = !empty($_REQUEST['dateFrom']);
2756 $isdateToSearchSet = !empty($_REQUEST['dateTo']);
2757 $bothDateRangesSet = $isDateFromSearchSet & $isdateToSearchSet;
2759 //Hanlde date from and to seperately
2760 if($bothDateRangesSet)
2762 $dbFormatDateFrom = $timedate->to_db_date($_REQUEST['dateFrom'], false);
2763 $dbFormatDateFrom = db_convert("'" . $dbFormatDateFrom . "'",'datetime');
2765 $dbFormatDateTo = $timedate->to_db_date($_REQUEST['dateTo'], false);
2766 $dbFormatDateTo = db_convert("'" . $dbFormatDateTo . "'",'datetime');
2768 $additionalWhereClause[] = "( emails.date_sent >= $dbFormatDateFrom AND
2769 emails.date_sent <= $dbFormatDateTo )";
2771 elseif ($isdateToSearchSet)
2773 $dbFormatDateTo = $timedate->to_db_date($_REQUEST['dateTo'], false);
2774 $dbFormatDateTo = db_convert("'" . $dbFormatDateTo . "'",'datetime');
2775 $additionalWhereClause[] = "emails.date_sent <= $dbFormatDateTo ";
2777 elseif ($isDateFromSearchSet)
2779 $dbFormatDateFrom = $timedate->to_db_date($_REQUEST['dateFrom'], false);
2780 $dbFormatDateFrom = db_convert("'" . $dbFormatDateFrom . "'",'datetime');
2781 $additionalWhereClause[] = "emails.date_sent >= $dbFormatDateFrom ";
2784 $additionalWhereClause = implode(" AND ", $additionalWhereClause);
2786 return $additionalWhereClause;
2792 * takes a long TO: string of emails and returns the first appended by an
2795 function trimLongTo($str) {
2796 if(strpos($str, ',')) {
2797 $exStr = explode(',', $str);
2798 return $exStr[0].'...';
2799 } elseif(strpos($str, ';')) {
2800 $exStr = explode(';', $str);
2801 return $exStr[0].'...';
2807 function get_summary_text() {
2813 function distributionForm($where) {
2814 global $app_list_strings;
2815 global $app_strings;
2816 global $mod_strings;
2818 global $current_user;
2820 $distribution = get_select_options_with_id($app_list_strings['dom_email_distribution'], '');
2821 $_SESSION['distribute_where'] = $where;
2824 $out = '<form name="Distribute" id="Distribute">';
2825 $out .= get_form_header($mod_strings['LBL_DIST_TITLE'], '', false);
2832 <table cellpadding="0" cellspacing="0" width="100%" border="0">
2835 <script type="text/javascript">
2838 function checkDeps(form) {
2842 function mySubmit() {
2843 var assform = document.getElementById("Distribute");
2844 var select = document.getElementById("userSelect");
2845 var assign1 = assform.r1.checked;
2846 var assign2 = assform.r2.checked;
2847 var dist = assform.dm.value;
2851 var warn1 = "'.$mod_strings['LBL_WARN_NO_USERS'].'";
2854 if(assign1 || assign2) {
2859 for(i=0; i<select.options.length; i++) {
2860 if(select.options[i].selected == true) {
2869 warn2 = "'.$mod_strings['LBL_WARN_NO_DIST'].'";
2872 if(assign && users && rules) {
2874 if(document.getElementById("r1").checked) {
2875 var mu = document.getElementById("MassUpdate");
2878 for(i=0; i<mu.elements.length; i++) {
2879 if(mu.elements[i].type == "checkbox" && mu.elements[i].checked && mu.elements[i].name.value != "massall") {
2880 if(grabbed != "") { grabbed += "::"; }
2881 grabbed += mu.elements[i].value;
2884 var formgrab = document.getElementById("grabbed");
2885 formgrab.value = grabbed;
2889 alert("'.$mod_strings['LBL_ASSIGN_WARN'].'" + "\n" + warn1 + "\n" + warn2);
2893 function submitDelete() {
2894 if(document.getElementById("r1").checked) {
2895 var mu = document.getElementById("MassUpdate");
2898 for(i=0; i<mu.elements.length; i++) {
2899 if(mu.elements[i].type == "checkbox" && mu.elements[i].checked && mu.elements[i].name != "massall") {
2900 if(grabbed != "") { grabbed += "::"; }
2901 grabbed += mu.elements[i].value;
2904 var formgrab = document.getElementById("grabbed");
2905 formgrab.value = grabbed;
2908 alert("'.$mod_strings['LBL_MASS_DELETE_ERROR'].'");
2910 document.getElementById("Distribute").submit();
2915 <input type="hidden" name="module" value="Emails">
2916 <input type="hidden" name="action" id="action">
2917 <input type="hidden" name="grabbed" id="grabbed">
2919 <table cellpadding="1" cellspacing="0" width="100%" border="0" class="edit view">
2921 <td scope="col" scope="row" NOWRAP align="center">
2922 '.$mod_strings['LBL_ASSIGN_SELECTED_RESULTS_TO'].' ';
2923 $out .= $this->userSelectTable();
2925 <td scope="col" scope="row" NOWRAP align="left">
2926 '.$mod_strings['LBL_USING_RULES'].'
2927 <select name="distribute_method" id="dm" onChange="checkDeps(this.form);">'.$distribution.'</select>
2936 <td scope="col" width="50%" scope="row" NOWRAP align="right" colspan="2">
2937 <input title="'.$mod_strings['LBL_BUTTON_DISTRIBUTE_TITLE'].'"
2939 accessKey="'.$mod_strings['LBL_BUTTON_DISTRIBUTE_KEY'].'"
2940 class="button" onClick="AjaxObject.detailView.handleAssignmentDialogAssignAction();"
2941 type="button" name="button"
2942 value=" '.$mod_strings['LBL_BUTTON_DISTRIBUTE'].' ">';
2952 function userSelectTable() {
2954 global $mod_strings;
2957 $setTeamUserFunction = '';
2961 $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");
2963 $userTable = '<table cellpadding="0" cellspacing="0" border="0">';
2964 $userTable .= '<tr><td colspan="2"><b>'.$mod_strings['LBL_USER_SELECT'].'</b></td></tr>';
2965 $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>';
2966 $userTable .= '<tr><td colspan="2"><select style="visibility:hidden;" name="users[]" id="userSelect" multiple size="12">';
2968 while($a = $this->db->fetchByAssoc($r)) {
2969 $userTable .= '<option value="'.$a['id'].'" id="'.$a['id'].'">'.$a['first_name'].' '.$a['last_name'].'</option>';
2971 $userTable .= '</select></td></tr>';
2972 $userTable .= '</table>';
2974 $out = '<script type="text/javascript">';
2975 $out .= $setTeamUserFunction;
2977 function setCheckMark() {
2978 var select = document.getElementById("userSelect");
2980 for(i=0 ; i<select.options.length; i++) {
2981 if(select.options[i].selected == true) {
2982 document.getElementById("checkMark").style.display="";
2987 document.getElementById("checkMark").style.display="none";
2991 function showUserSelect() {
2992 var targetTable = document.getElementById("user_select");
2993 targetTable.style.visibility="visible";
2994 var userSelectTable = document.getElementById("userSelect");
2995 userSelectTable.style.visibility="visible";
2998 function hideUserSelect() {
2999 var targetTable = document.getElementById("user_select");
3000 targetTable.style.visibility="hidden";
3001 var userSelectTable = document.getElementById("userSelect");
3002 userSelectTable.style.visibility="hidden";
3005 function toggleAll(toggle) {
3006 if(toggle.checked) {
3011 var form = document.getElementById("userSelect");
3012 for(i=0; i<form.options.length; i++) {
3013 form.options[i].selected = stat;
3019 <span id="showUsersDiv" style="position:relative;">
3020 <a href="#" id="showUsers" onClick="javascript:showUserSelect();">
3021 <img border="0" src="'.SugarThemeRegistry::current()->getImageURL('Users.gif').'"></a>
3022 <a href="#" id="showUsers" onClick="javascript:showUserSelect();">
3023 <span style="display:none;" id="checkMark"><img border="0" src="'.SugarThemeRegistry::current()->getImageURL('check_inline.gif').'"></span>
3027 <div id="user_select" style="width:200px;position:absolute;left:2;top:2;visibility:hidden;z-index:1000;">
3028 <table cellpadding="0" cellspacing="0" border="0" class="list view">
3030 <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\';">
3031 <a href="#" onClick="javascript:hideUserSelect();"><img border="0" src="'.SugarThemeRegistry::current()->getImageURL('close.gif').'"></a>
3032 '.$mod_strings['LBL_USER_SELECT'].'
3036 //<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\';">
3037 $out .= ' <td style="padding:5px" class="oddListRowS1" bgcolor="#fdfdfd" valign="top" align="left" style="left:0;top:0;">
3046 function checkInbox($type) {
3048 global $mod_strings;
3049 $out = '<div><input title="'.$mod_strings['LBL_BUTTON_CHECK_TITLE'].'"
3050 accessKey="'.$mod_strings['LBL_BUTTON_CHECK_KEY'].'"
3052 type="button" name="button"
3053 onClick="window.location=\'index.php?module=Emails&action=Check&type='.$type.'\';"
3054 style="margin-bottom:2px"
3055 value=" '.$mod_strings['LBL_BUTTON_CHECK'].' "></div>';
3060 * Guesses Primary Parent id from From: email address. Cascades guesses from Accounts to Contacts to Leads to
3061 * Users. This will not affect the many-to-many relationships already constructed as this is, at best,
3062 * informational linking.
3064 function fillPrimaryParentFields() {
3065 if(empty($this->from_addr))
3068 $GLOBALS['log']->debug("*** Email trying to guess Primary Parent from address [ {$this->from_addr} ]");
3070 $tables = array('accounts');
3072 // loop through types to get hits
3073 foreach($tables as $table) {
3074 $q = "SELECT name, id FROM {$table} WHERE email1 = '{$this->from_addr}' OR email2 = '{$this->from_addr}' AND deleted = 0";
3075 $r = $this->db->query($q);
3076 while($a = $this->db->fetchByAssoc($r)) {
3077 if(!empty($a['name']) && !empty($a['id'])) {
3078 $this->parent_type = ucwords($table);
3079 $this->parent_id = $a['id'];
3080 $this->parent_name = $a['name'];