PageRenderTime 50ms CodeModel.GetById 8ms RepoModel.GetById 0ms app.codeStats 1ms

/modules/Emails/Email.php

https://github.com/mikmagic/sugarcrm_dev
PHP | 3135 lines | 2274 code | 430 blank | 431 comment | 498 complexity | 41d07af157ef12df023cee3d06209145 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-2.1, BSD-3-Clause, AGPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
  3. /*********************************************************************************
  4. * SugarCRM Community Edition is a customer relationship management program developed by
  5. * SugarCRM, Inc. Copyright (C) 2004-2011 SugarCRM Inc.
  6. *
  7. * This program is free software; you can redistribute it and/or modify it under
  8. * the terms of the GNU Affero General Public License version 3 as published by the
  9. * Free Software Foundation with the addition of the following permission added
  10. * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
  11. * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
  12. * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
  13. *
  14. * This program is distributed in the hope that it will be useful, but WITHOUT
  15. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  16. * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
  17. * details.
  18. *
  19. * You should have received a copy of the GNU Affero General Public License along with
  20. * this program; if not, see http://www.gnu.org/licenses or write to the Free
  21. * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  22. * 02110-1301 USA.
  23. *
  24. * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
  25. * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
  26. *
  27. * The interactive user interfaces in modified source and object code versions
  28. * of this program must display Appropriate Legal Notices, as required under
  29. * Section 5 of the GNU Affero General Public License version 3.
  30. *
  31. * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
  32. * these Appropriate Legal Notices must retain the display of the "Powered by
  33. * SugarCRM" logo. If the display of the logo is not reasonably feasible for
  34. * technical reasons, the Appropriate Legal Notices must display the words
  35. * "Powered by SugarCRM".
  36. ********************************************************************************/
  37. /*********************************************************************************
  38. * Description:
  39. * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc. All Rights
  40. * Reserved. Contributor(s): ______________________________________..
  41. *********************************************************************************/
  42. require_once('include/SugarPHPMailer.php');
  43. require_once('include/Pear/HTML_Safe/Safe.php');
  44. class Email extends SugarBean {
  45. /* SugarBean schema */
  46. var $id;
  47. var $date_entered;
  48. var $date_modified;
  49. var $assigned_user_id;
  50. var $assigned_user_name;
  51. var $modified_user_id;
  52. var $created_by;
  53. var $deleted;
  54. var $from_addr;
  55. var $reply_to_addr;
  56. var $to_addrs;
  57. var $cc_addrs;
  58. var $bcc_addrs;
  59. var $message_id;
  60. /* Bean Attributes */
  61. var $name;
  62. var $type = 'archived';
  63. var $date_sent;
  64. var $status;
  65. var $intent;
  66. var $mailbox_id;
  67. var $from_name;
  68. var $reply_to_status;
  69. var $reply_to_name;
  70. var $reply_to_email;
  71. var $description;
  72. var $description_html;
  73. var $raw_source;
  74. var $parent_id;
  75. var $parent_type;
  76. /* link attributes */
  77. var $parent_name;
  78. /* legacy */
  79. var $date_start; // legacy
  80. var $time_start; // legacy
  81. var $from_addr_name;
  82. var $to_addrs_arr;
  83. var $cc_addrs_arr;
  84. var $bcc_addrs_arr;
  85. var $to_addrs_ids;
  86. var $to_addrs_names;
  87. var $to_addrs_emails;
  88. var $cc_addrs_ids;
  89. var $cc_addrs_names;
  90. var $cc_addrs_emails;
  91. var $bcc_addrs_ids;
  92. var $bcc_addrs_names;
  93. var $bcc_addrs_emails;
  94. var $contact_id;
  95. var $contact_name;
  96. /* Archive Email attrs */
  97. var $duration_hours;
  98. var $new_schema = true;
  99. var $table_name = 'emails';
  100. var $module_dir = 'Emails';
  101. var $object_name = 'Email';
  102. var $db;
  103. /* private attributes */
  104. 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";
  105. var $cachePath;
  106. var $cacheFile = 'robin.cache.php';
  107. var $replyDelimiter = "> ";
  108. var $emailDescription;
  109. var $emailDescriptionHTML;
  110. var $emailRawSource;
  111. var $link_action;
  112. var $emailAddress;
  113. var $attachments = array();
  114. /* to support Email 2.0 */
  115. var $isDuplicate;
  116. var $uid;
  117. var $to;
  118. var $flagged;
  119. var $answered;
  120. var $seen;
  121. var $draft;
  122. var $relationshipMap = array(
  123. 'Contacts' => 'emails_contacts_rel',
  124. 'Accounts' => 'emails_accounts_rel',
  125. 'Leads' => 'emails_leads_rel',
  126. 'Users' => 'emails_users_rel',
  127. 'Prospects' => 'emails_prospects_rel',
  128. );
  129. /* public */
  130. var $et; // EmailUI object
  131. /**
  132. * sole constructor
  133. */
  134. function Email() {
  135. $this->cachePath = $GLOBALS['sugar_config']['cache_dir'].'modules/Emails';
  136. parent::SugarBean();
  137. $this->safe = new HTML_Safe();
  138. $this->safe->clear();
  139. $this->emailAddress = new SugarEmailAddress();
  140. }
  141. function email2init() {
  142. require_once('modules/Emails/EmailUI.php');
  143. $this->et = new EmailUI();
  144. }
  145. function bean_implements($interface){
  146. switch($interface){
  147. case 'ACL': return true;
  148. default: return false;
  149. }
  150. }
  151. /**
  152. * Presaves one attachment for new email 2.0 spec
  153. * DOES NOT CREATE A NOTE
  154. * @return string ID of note associated with the attachment
  155. */
  156. function email2saveAttachment() {
  157. global $sugar_config;
  158. $filesError = array(
  159. 0 => 'UPLOAD_ERR_OK - There is no error, the file uploaded with success.',
  160. 1 => 'UPLOAD_ERR_INI_SIZE - The uploaded file exceeds the upload_max_filesize directive in php.ini.',
  161. 2 => 'UPLOAD_ERR_FORM_SIZE - The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.',
  162. 3 => 'UPLOAD_ERR_PARTIAL - The uploaded file was only partially uploaded.',
  163. 4 => 'UPLOAD_ERR_NO_FILE - No file was uploaded.',
  164. 5 => 'UNKNOWN ERROR',
  165. 6 => 'UPLOAD_ERR_NO_TMP_DIR - Missing a temporary folder. Introduced in PHP 4.3.10 and PHP 5.0.3.',
  166. 7 => 'UPLOAD_ERR_CANT_WRITE - Failed to write file to disk. Introduced in PHP 5.1.0.',
  167. );
  168. // cn: Bug 5995 - rudimentary error checking
  169. if($_FILES['email_attachment']['error'] != 0 && $_FILES['email_attachment']['error'] != 4) {
  170. $GLOBALS['log']->debug('Email Attachment could not be attach due to error: '.$filesError[$_FILES['email_attachment']['error']]);
  171. return array();
  172. }
  173. if(isset($_FILES['email_attachment']) && is_uploaded_file($_FILES['email_attachment']['tmp_name'])) {
  174. $guid = create_guid();
  175. $cleanAttachmentFileName = from_html($_FILES['email_attachment']['name']);
  176. $GLOBALS['log']->debug("Email Attachment [ {$cleanAttachmentFileName} ] ");
  177. $cleanAttachmentFileName = str_replace("\\", "", $cleanAttachmentFileName);
  178. $GLOBALS['log']->debug("Email Attachment [ {$cleanAttachmentFileName} ] ");
  179. //$destination = clean_path("{$this->et->userCacheDir}/{$guid}{$cleanAttachmentFileName}");
  180. $destination = clean_path("{$this->et->userCacheDir}/{$guid}");
  181. $badExt = $this->safeAttachmentName($cleanAttachmentFileName);
  182. if ($badExt) {
  183. //$destination = $destination . ".txt";
  184. } // if
  185. $fileName = $badExt ? $cleanAttachmentFileName . ".txt" : $cleanAttachmentFileName;
  186. if(move_uploaded_file($_FILES['email_attachment']['tmp_name'], $destination)) {
  187. return array(
  188. 'guid' => $guid,
  189. 'name' => $GLOBALS['db']->helper->escape_quote($fileName),
  190. 'nameForDisplay' => $fileName
  191. );
  192. } else {
  193. $GLOBALS['log']->debug("Email Attachment [ {$cleanAttachmentFileName} ] could not be moved to cache dir");
  194. return array();
  195. }
  196. }
  197. }
  198. function safeAttachmentName($filename) {
  199. global $sugar_config;
  200. $badExtension = false;
  201. //get position of last "." in file name
  202. $file_ext_beg = strrpos($filename, ".");
  203. $file_ext = "";
  204. //get file extension
  205. if($file_ext_beg !== false) {
  206. $file_ext = substr($filename, $file_ext_beg + 1);
  207. }
  208. //check to see if this is a file with extension located in "badext"
  209. foreach($sugar_config['upload_badext'] as $badExt) {
  210. if(strtolower($file_ext) == strtolower($badExt)) {
  211. //if found, then append with .txt and break out of lookup
  212. $filename = $filename . ".txt";
  213. $badExtension = true;
  214. break; // no need to look for more
  215. } // if
  216. } // foreach
  217. return $badExtension;
  218. } // fn
  219. /**
  220. * takes output from email 2.0 to/cc/bcc fields and returns appropriate arrays for usage by PHPMailer
  221. * @param string addresses
  222. * @return array
  223. */
  224. function email2ParseAddresses($addresses) {
  225. $addresses = from_html($addresses);
  226. $addresses = $this->et->unifyEmailString($addresses);
  227. $pattern = '/@.*,/U';
  228. preg_match_all($pattern, $addresses, $matchs);
  229. if (!empty($matchs[0])){
  230. $total = $matchs[0];
  231. foreach ($total as $match) {
  232. $convertedPattern = str_replace(',', '::;::', $match);
  233. $addresses = str_replace($match, $convertedPattern, $addresses);
  234. } //foreach
  235. }
  236. $exAddr = explode("::;::", $addresses);
  237. $ret = array();
  238. $clean = array("<", ">");
  239. $dirty = array("&lt;", "&gt;");
  240. foreach($exAddr as $addr) {
  241. $name = '';
  242. $addr = str_replace($dirty, $clean, $addr);
  243. if((strpos($addr, "<") === false) && (strpos($addr, ">") === false)) {
  244. $address = $addr;
  245. } else {
  246. $address = substr($addr, strpos($addr, "<") + 1, strpos($addr, ">") - 1 - strpos($addr, "<"));
  247. $name = substr($addr, 0, strpos($addr, "<"));
  248. }
  249. $addrTemp = array();
  250. $addrTemp['email'] = trim($address);
  251. $addrTemp['display'] = trim($name);
  252. $ret[] = $addrTemp;
  253. }
  254. return $ret;
  255. }
  256. /**
  257. * takes output from email 2.0 to/cc/bcc fields and returns appropriate arrays for usage by PHPMailer
  258. * @param string addresses
  259. * @return array
  260. */
  261. function email2ParseAddressesForAddressesOnly($addresses) {
  262. $addresses = from_html($addresses);
  263. $pattern = '/@.*,/U';
  264. preg_match_all($pattern, $addresses, $matchs);
  265. if (!empty($matchs[0])){
  266. $total = $matchs[0];
  267. foreach ($total as $match) {
  268. $convertedPattern = str_replace(',', '::;::', $match);
  269. $addresses = str_replace($match, $convertedPattern, $addresses);
  270. } //foreach
  271. }
  272. $exAddr = explode("::;::", $addresses);
  273. $ret = array();
  274. $clean = array("<", ">");
  275. $dirty = array("&lt;", "&gt;");
  276. foreach($exAddr as $addr) {
  277. $name = '';
  278. $addr = str_replace($dirty, $clean, $addr);
  279. if(strpos($addr, "<") && strpos($addr, ">")) {
  280. $address = substr($addr, strpos($addr, "<") + 1, strpos($addr, ">") - 1 - strpos($addr, "<"));
  281. } else {
  282. $address = $addr;
  283. }
  284. $ret[] = trim($address);
  285. }
  286. return $ret;
  287. }
  288. /**
  289. * Determines MIME-type encoding as possible.
  290. * @param string $fileLocation relative path to file
  291. * @return string MIME-type
  292. */
  293. function email2GetMime($fileLocation) {
  294. if(!is_readable($fileLocation)) {
  295. return 'application/octet-stream';
  296. }
  297. if(function_exists('mime_content_type')) {
  298. $mime = mime_content_type($fileLocation);
  299. } elseif(function_exists('ext2mime')) {
  300. $mime = ext2mime($fileLocation);
  301. } else {
  302. $mime = 'application/octet-stream';
  303. }
  304. return $mime;
  305. }
  306. function sendEmailTest($mailserver_url, $port, $ssltls, $smtp_auth_req, $smtp_username, $smtppassword, $fromaddress, $toaddress, $mail_sendtype = 'smtp', $fromname = '') {
  307. global $current_user,$app_strings;
  308. $mod_strings = return_module_language($GLOBALS['current_language'], 'Emails'); //Called from EmailMan as well.
  309. $mail = new SugarPHPMailer();
  310. $mail->Mailer = strtolower($mail_sendtype);
  311. if($mail->Mailer == 'smtp')
  312. {
  313. $mail->Host = $mailserver_url;
  314. $mail->Port = $port;
  315. if (isset($ssltls) && !empty($ssltls)) {
  316. $mail->protocol = "ssl://";
  317. if ($ssltls == 1) {
  318. $mail->SMTPSecure = 'ssl';
  319. } // if
  320. if ($ssltls == 2) {
  321. $mail->SMTPSecure = 'tls';
  322. } // if
  323. } else {
  324. $mail->protocol = "tcp://";
  325. }
  326. if ($smtp_auth_req) {
  327. $mail->SMTPAuth = TRUE;
  328. $mail->Username = $smtp_username;
  329. $mail->Password = $smtppassword;
  330. }
  331. }
  332. else
  333. $mail->Mailer = 'sendmail';
  334. $mail->Subject = from_html($mod_strings['LBL_TEST_EMAIL_SUBJECT']);
  335. $mail->From = $fromaddress;
  336. if ($fromname != '') {
  337. $mail->FromName = html_entity_decode($fromname,ENT_QUOTES);
  338. } else {
  339. $mail->FromName = $current_user->name;
  340. }
  341. $mail->Sender = $mail->From;
  342. $mail->AddAddress($toaddress);
  343. $mail->Body = $mod_strings['LBL_TEST_EMAIL_BODY'];
  344. $return = array();
  345. if(!$mail->Send()) {
  346. ob_clean();
  347. $return['status'] = false;
  348. $return['errorMessage'] = $app_strings['LBL_EMAIL_ERROR_PREPEND']. $mail->ErrorInfo;
  349. return $return;
  350. } // if
  351. $return['status'] = true;
  352. return $return;
  353. } // fn
  354. function decodeDuringSend($htmlData) {
  355. $htmlData = str_replace("sugarLessThan", "&lt;", $htmlData);
  356. $htmlData = str_replace("sugarGreaterThan", "&gt;", $htmlData);
  357. return $htmlData;
  358. }
  359. /**
  360. * Returns true or false if this email is a draft.
  361. *
  362. * @param array $request
  363. * @return bool True indicates this email is a draft.
  364. */
  365. function isDraftEmail($request)
  366. {
  367. return ( isset($request['saveDraft']) || ($this->type == 'draft' && $this->status == 'draft') );
  368. }
  369. /**
  370. * Sends Email for Email 2.0
  371. */
  372. function email2Send($request) {
  373. global $mod_strings;
  374. global $app_strings;
  375. global $current_user;
  376. global $sugar_config;
  377. global $locale;
  378. global $timedate;
  379. global $beanList;
  380. global $beanFiles;
  381. $OBCharset = $locale->getPrecedentPreference('default_email_charset');
  382. /**********************************************************************
  383. * Sugar Email PREP
  384. */
  385. /* preset GUID */
  386. $orignialId = "";
  387. if(!empty($this->id)) {
  388. $orignialId = $this->id;
  389. } // if
  390. if(empty($this->id)) {
  391. $this->id = create_guid();
  392. $this->new_with_id = true;
  393. }
  394. /* satisfy basic HTML email requirements */
  395. $this->name = $request['sendSubject'];
  396. $this->description_html = '&lt;html&gt;&lt;body&gt;'.$request['sendDescription'].'&lt;/body&gt;&lt;/html&gt;';
  397. /**********************************************************************
  398. * PHPMAILER PREP
  399. */
  400. $mail = new SugarPHPMailer();
  401. $mail = $this->setMailer($mail, '', $_REQUEST['fromAccount']);
  402. if (empty($mail->Host) && !$this->isDraftEmail($request))
  403. {
  404. $this->status = 'send_error';
  405. if ($mail->oe->type == 'system')
  406. echo($app_strings['LBL_EMAIL_ERROR_PREPEND']. $app_strings['LBL_EMAIL_INVALID_SYSTEM_OUTBOUND']);
  407. else
  408. echo($app_strings['LBL_EMAIL_ERROR_PREPEND']. $app_strings['LBL_EMAIL_INVALID_PERSONAL_OUTBOUND']);
  409. return false;
  410. }
  411. $subject = $this->name;
  412. $mail->Subject = from_html($this->name);
  413. // work-around legacy code in SugarPHPMailer
  414. if($_REQUEST['setEditor'] == 1) {
  415. $_REQUEST['description_html'] = $_REQUEST['sendDescription'];
  416. $this->description_html = $_REQUEST['description_html'];
  417. } else {
  418. $this->description_html = '';
  419. $this->description = $_REQUEST['sendDescription'];
  420. }
  421. // end work-around
  422. if ( $this->isDraftEmail($request) )
  423. {
  424. if($this->type != 'draft' && $this->status != 'draft') {
  425. $this->id = create_guid();
  426. $this->new_with_id = true;
  427. $this->date_entered = "";
  428. } // if
  429. $q1 = "update emails_email_addr_rel set deleted = 1 WHERE email_id = '{$this->id}'";
  430. $r1 = $this->db->query($q1);
  431. } // if
  432. if (isset($request['saveDraft'])) {
  433. $this->type = 'draft';
  434. $this->status = 'draft';
  435. $forceSave = true;
  436. } else {
  437. /* Apply Email Templates */
  438. // do not parse email templates if the email is being saved as draft....
  439. $toAddresses = $this->email2ParseAddresses($_REQUEST['sendTo']);
  440. $sea = new SugarEmailAddress();
  441. $object_arr = array();
  442. if( isset($_REQUEST['parent_type']) && !empty($_REQUEST['parent_type']) &&
  443. isset($_REQUEST['parent_id']) && !empty($_REQUEST['parent_id']) &&
  444. ($_REQUEST['parent_type'] == 'Accounts' ||
  445. $_REQUEST['parent_type'] == 'Contacts' ||
  446. $_REQUEST['parent_type'] == 'Leads' ||
  447. $_REQUEST['parent_type'] == 'Users' ||
  448. $_REQUEST['parent_type'] == 'Prospects')) {
  449. if(isset($beanList[$_REQUEST['parent_type']]) && !empty($beanList[$_REQUEST['parent_type']])) {
  450. $className = $beanList[$_REQUEST['parent_type']];
  451. if(isset($beanFiles[$className]) && !empty($beanFiles[$className])) {
  452. if(!class_exists($className)) {
  453. require_once($beanFiles[$className]);
  454. }
  455. $bean = new $className();
  456. $bean->retrieve($_REQUEST['parent_id']);
  457. $object_arr[$bean->module_dir] = $bean->id;
  458. } // if
  459. } // if
  460. }
  461. foreach($toAddresses as $addrMeta) {
  462. $addr = $addrMeta['email'];
  463. $beans = $sea->getBeansByEmailAddress($addr);
  464. foreach($beans as $bean) {
  465. if (!isset($object_arr[$bean->module_dir])) {
  466. $object_arr[$bean->module_dir] = $bean->id;
  467. }
  468. }
  469. }
  470. /* template parsing */
  471. if (empty($object_arr)) {
  472. $object_arr= array('Contacts' => '123');
  473. }
  474. $object_arr['Users'] = $current_user->id;
  475. $this->description_html = EmailTemplate::parse_template($this->description_html, $object_arr);
  476. $this->name = EmailTemplate::parse_template($this->name, $object_arr);
  477. $this->description = EmailTemplate::parse_template($this->description, $object_arr);
  478. $this->description = html_entity_decode($this->description,ENT_COMPAT,'UTF-8');
  479. if($this->type != 'draft' && $this->status != 'draft') {
  480. $this->id = create_guid();
  481. $this->date_entered = "";
  482. $this->new_with_id = true;
  483. $this->type = 'out';
  484. $this->status = 'sent';
  485. }
  486. }
  487. if(isset($_REQUEST['parent_type']) && empty($_REQUEST['parent_type']) &&
  488. isset($_REQUEST['parent_id']) && empty($_REQUEST['parent_id']) ) {
  489. $this->parent_id = "";
  490. $this->parent_type = "";
  491. } // if
  492. $mail->Subject = $this->name;
  493. $mail = $this->handleBody($mail);
  494. $mail->Subject = $this->name;
  495. $this->description_html = from_html($this->description_html);
  496. $this->description_html = $this->decodeDuringSend($this->description_html);
  497. $this->description = $this->decodeDuringSend($this->description);
  498. /* from account */
  499. $replyToAddress = $current_user->emailAddress->getReplyToAddress($current_user);
  500. $replyToName = "";
  501. if(empty($request['fromAccount'])) {
  502. $defaults = $current_user->getPreferredEmail();
  503. $mail->From = $defaults['email'];
  504. $mail->FromName = $defaults['name'];
  505. $replyToName = $mail->FromName;
  506. //$replyToAddress = $current_user->emailAddress->getReplyToAddress($current_user);
  507. } else {
  508. // passed -> user -> system default
  509. $ie = new InboundEmail();
  510. $ie->retrieve($request['fromAccount']);
  511. $storedOptions = unserialize(base64_decode($ie->stored_options));
  512. $fromName = "";
  513. $fromAddress = "";
  514. $replyToName = "";
  515. //$replyToAddress = "";
  516. if (!empty($storedOptions)) {
  517. $fromAddress = $storedOptions['from_addr'];
  518. $fromName = from_html($storedOptions['from_name']);
  519. $replyToAddress = (isset($storedOptions['reply_to_addr']) ? $storedOptions['reply_to_addr'] : "");
  520. $replyToName = (isset($storedOptions['reply_to_name']) ? from_html($storedOptions['reply_to_name']) : "");
  521. } // if
  522. $defaults = $current_user->getPreferredEmail();
  523. // Personal Account doesn't have reply To Name and Reply To Address. So add those columns on UI
  524. // After adding remove below code
  525. // code to remove
  526. if ($ie->is_personal)
  527. {
  528. if (empty($replyToAddress))
  529. {
  530. $replyToAddress = $current_user->emailAddress->getReplyToAddress($current_user);
  531. } // if
  532. if (empty($replyToName))
  533. {
  534. $replyToName = $defaults['name'];
  535. } // if
  536. //Personal accounts can have a reply_address, which should
  537. //overwrite the users set default.
  538. if( !empty($storedOptions['reply_to_addr']) )
  539. $replyToAddress = $storedOptions['reply_to_addr'];
  540. }
  541. // end of code to remove
  542. $mail->From = (!empty($fromAddress)) ? $fromAddress : $defaults['email'];
  543. $mail->FromName = (!empty($fromName)) ? $fromName : $defaults['name'];
  544. $replyToName = (!empty($replyToName)) ? $replyToName : $mail->FromName;
  545. }
  546. $mail->Sender = $mail->From; /* set Return-Path field in header to reduce spam score in emails sent via Sugar's Email module */
  547. if (!empty($replyToAddress)) {
  548. $mail->AddReplyTo($replyToAddress,$locale->translateCharsetMIME(trim( $replyToName), 'UTF-8', $OBCharset));
  549. } else {
  550. $mail->AddReplyTo($mail->From,$locale->translateCharsetMIME(trim( $mail->FromName), 'UTF-8', $OBCharset));
  551. } // else
  552. $emailAddressCollection = array(); // used in linking to beans below
  553. // handle to/cc/bcc
  554. foreach($this->email2ParseAddresses($request['sendTo']) as $addr_arr) {
  555. if(empty($addr_arr['email'])) continue;
  556. if(empty($addr_arr['display'])) {
  557. $mail->AddAddress($addr_arr['email'], "");
  558. } else {
  559. $mail->AddAddress($addr_arr['email'],$locale->translateCharsetMIME(trim( $addr_arr['display']), 'UTF-8', $OBCharset));
  560. }
  561. $emailAddressCollection[] = $addr_arr['email'];
  562. }
  563. foreach($this->email2ParseAddresses($request['sendCc']) as $addr_arr) {
  564. if(empty($addr_arr['email'])) continue;
  565. if(empty($addr_arr['display'])) {
  566. $mail->AddCC($addr_arr['email'], "");
  567. } else {
  568. $mail->AddCC($addr_arr['email'],$locale->translateCharsetMIME(trim( $addr_arr['display']), 'UTF-8', $OBCharset));
  569. }
  570. $emailAddressCollection[] = $addr_arr['email'];
  571. }
  572. foreach($this->email2ParseAddresses($request['sendBcc']) as $addr_arr) {
  573. if(empty($addr_arr['email'])) continue;
  574. if(empty($addr_arr['display'])) {
  575. $mail->AddBCC($addr_arr['email'], "");
  576. } else {
  577. $mail->AddBCC($addr_arr['email'],$locale->translateCharsetMIME(trim( $addr_arr['display']), 'UTF-8', $OBCharset));
  578. }
  579. $emailAddressCollection[] = $addr_arr['email'];
  580. }
  581. /* parse remove attachments array */
  582. $removeAttachments = array();
  583. if(!empty($request['templateAttachmentsRemove'])) {
  584. $exRemove = explode("::", $request['templateAttachmentsRemove']);
  585. foreach($exRemove as $file) {
  586. $removeAttachments = substr($file, 0, 36);
  587. }
  588. }
  589. /* handle attachments */
  590. if(!empty($request['attachments'])) {
  591. $exAttachments = explode("::", $request['attachments']);
  592. foreach($exAttachments as $file) {
  593. $file = trim(from_html($file));
  594. $file = str_replace("\\", "", $file);
  595. if(!empty($file)) {
  596. //$fileLocation = $this->et->userCacheDir."/{$file}";
  597. $fileGUID = substr($file, 0, 36);
  598. $fileLocation = $this->et->userCacheDir."/{$fileGUID}";
  599. $filename = substr($file, 36, strlen($file)); // strip GUID for PHPMailer class to name outbound file
  600. $mail->AddAttachment($fileLocation,$filename, 'base64', $this->email2GetMime($fileLocation));
  601. //$mail->AddAttachment($fileLocation, $filename, 'base64');
  602. // only save attachments if we're archiving or drafting
  603. if((($this->type == 'draft') && !empty($this->id)) || (isset($request['saveToSugar']) && $request['saveToSugar'] == 1)) {
  604. $note = new Note();
  605. $note->id = create_guid();
  606. $note->new_with_id = true; // duplicating the note with files
  607. $note->parent_id = $this->id;
  608. $note->parent_type = $this->module_dir;
  609. $note->name = $filename;
  610. $note->filename = $filename;
  611. $noteFile = "{$sugar_config['upload_dir']}{$note->id}";
  612. $note->file_mime_type = $this->email2GetMime($fileLocation);
  613. if(!copy($fileLocation, $noteFile)) {
  614. $GLOBALS['log']->debug("EMAIL 2.0: could not copy attachment file to cache/upload [ {$fileLocation} ]");
  615. }
  616. $note->save();
  617. }
  618. }
  619. }
  620. }
  621. /* handle sugar documents */
  622. if(!empty($request['documents'])) {
  623. $exDocs = explode("::", $request['documents']);
  624. foreach($exDocs as $docId) {
  625. $docId = trim($docId);
  626. if(!empty($docId)) {
  627. $doc = new Document();
  628. $docRev = new DocumentRevision();
  629. $doc->retrieve($docId);
  630. $docRev->retrieve($doc->document_revision_id);
  631. $filename = $docRev->filename;
  632. $fileLocation = "{$sugar_config['upload_dir']}{$docRev->id}";
  633. $mime_type = $docRev->file_mime_type;
  634. $mail->AddAttachment($fileLocation,$locale->translateCharsetMIME(trim($filename), 'UTF-8', $OBCharset), 'base64', $mime_type);
  635. // only save attachments if we're archiving or drafting
  636. if((($this->type == 'draft') && !empty($this->id)) || (isset($request['saveToSugar']) && $request['saveToSugar'] == 1)) {
  637. $note = new Note();
  638. $note->id = create_guid();
  639. $note->new_with_id = true; // duplicating the note with files
  640. $note->parent_id = $this->id;
  641. $note->parent_type = $this->module_dir;
  642. $note->name = $filename;
  643. $note->filename = $filename;
  644. $note->file_mime_type = $mime_type;
  645. $noteFile = "{$sugar_config['upload_dir']}{$note->id}";
  646. if(!copy($fileLocation, $noteFile)) {
  647. $GLOBALS['log']->debug("EMAIL 2.0: could not copy SugarDocument revision file to {$sugar_config['upload_dir']} [ {$fileLocation} ]");
  648. }
  649. $note->save();
  650. }
  651. }
  652. }
  653. }
  654. /* handle template attachments */
  655. if(!empty($request['templateAttachments'])) {
  656. $exNotes = explode("::", $request['templateAttachments']);
  657. foreach($exNotes as $noteId) {
  658. $noteId = trim($noteId);
  659. if(!empty($noteId)) {
  660. $note = new Note();
  661. $note->retrieve($noteId);
  662. if (!empty($note->id)) {
  663. $filename = $note->filename;
  664. $fileLocation = "{$sugar_config['upload_dir']}{$note->id}";
  665. $mime_type = $note->file_mime_type;
  666. if (!$note->embed_flag) {
  667. $mail->AddAttachment($fileLocation,$filename, 'base64', $mime_type);
  668. // only save attachments if we're archiving or drafting
  669. if((($this->type == 'draft') && !empty($this->id)) || (isset($request['saveToSugar']) && $request['saveToSugar'] == 1)) {
  670. if ($note->parent_id != $this->id)
  671. $this->saveTempNoteAttachments($filename,$fileLocation, $mime_type);
  672. } // if
  673. } // if
  674. } else {
  675. //$fileLocation = $this->et->userCacheDir."/{$file}";
  676. $fileGUID = substr($noteId, 0, 36);
  677. $fileLocation = $this->et->userCacheDir."/{$fileGUID}";
  678. //$fileLocation = $this->et->userCacheDir."/{$noteId}";
  679. $filename = substr($noteId, 36, strlen($noteId)); // strip GUID for PHPMailer class to name outbound file
  680. $mail->AddAttachment($fileLocation,$locale->translateCharsetMIME(trim($filename), 'UTF-8', $OBCharset), 'base64', $this->email2GetMime($fileLocation));
  681. //If we are saving an email we were going to forward we need to save the attachments as well.
  682. if( (($this->type == 'draft') && !empty($this->id))
  683. || (isset($request['saveToSugar']) && $request['saveToSugar'] == 1))
  684. {
  685. $mimeType = $this->email2GetMime($fileLocation);
  686. $this->saveTempNoteAttachments($filename,$fileLocation, $mimeType);
  687. } // if
  688. }
  689. }
  690. }
  691. }
  692. /**********************************************************************
  693. * Final Touches
  694. */
  695. /* save email to sugar? */
  696. $forceSave = false;
  697. if($this->type == 'draft' && !isset($request['saveDraft'])) {
  698. // sending a draft email
  699. $this->type = 'out';
  700. $this->status = 'sent';
  701. $forceSave = true;
  702. } elseif(isset($request['saveDraft'])) {
  703. $this->type = 'draft';
  704. $this->status = 'draft';
  705. $forceSave = true;
  706. }
  707. /**********************************************************************
  708. * SEND EMAIL (finally!)
  709. */
  710. $mailSent = false;
  711. if ($this->type != 'draft') {
  712. $mail->prepForOutbound();
  713. $mail->Body = $this->decodeDuringSend($mail->Body);
  714. $mail->AltBody = $this->decodeDuringSend($mail->AltBody);
  715. if (!$mail->Send()) {
  716. $this->status = 'send_error';
  717. ob_clean();
  718. echo($app_strings['LBL_EMAIL_ERROR_PREPEND']. $mail->ErrorInfo);
  719. return false;
  720. }
  721. }
  722. if ((!(empty($orignialId) || isset($request['saveDraft']) || ($this->type == 'draft' && $this->status == 'draft'))) &&
  723. (($_REQUEST['composeType'] == 'reply') || ($_REQUEST['composeType'] == 'replyAll') || ($_REQUEST['composeType'] == 'replyCase')) && ($orignialId != $this->id)) {
  724. $originalEmail = new Email();
  725. $originalEmail->retrieve($orignialId);
  726. $originalEmail->reply_to_status = 1;
  727. $originalEmail->save();
  728. $this->reply_to_status = 0;
  729. } // if
  730. if ($_REQUEST['composeType'] == 'reply' || $_REQUEST['composeType'] == 'replyCase') {
  731. if (isset($_REQUEST['ieId']) && isset($_REQUEST['mbox'])) {
  732. $emailFromIe = new InboundEmail();
  733. $emailFromIe->retrieve($_REQUEST['ieId']);
  734. $emailFromIe->mailbox = $_REQUEST['mbox'];
  735. if (isset($emailFromIe->id) && $emailFromIe->is_personal) {
  736. if ($emailFromIe->isPop3Protocol()) {
  737. $emailFromIe->mark_answered($this->uid, 'pop3');
  738. }
  739. elseif ($emailFromIe->connectMailserver() == 'true') {
  740. $emailFromIe->markEmails($this->uid, 'answered');
  741. $emailFromIe->mark_answered($this->uid);
  742. }
  743. }
  744. }
  745. }
  746. if( $forceSave ||
  747. $this->type == 'draft' ||
  748. (isset($request['saveToSugar']) && $request['saveToSugar'] == 1)) {
  749. // saving a draft OR saving a sent email
  750. $decodedFromName = mb_decode_mimeheader($mail->FromName);
  751. $this->from_addr = "{$decodedFromName} <{$mail->From}>";
  752. $this->from_addr_name = $this->from_addr;
  753. $this->to_addrs = $_REQUEST['sendTo'];
  754. $this->to_addrs_names = $_REQUEST['sendTo'];
  755. $this->cc_addrs = $_REQUEST['sendCc'];
  756. $this->cc_addrs_names = $_REQUEST['sendCc'];
  757. $this->bcc_addrs = $_REQUEST['sendBcc'];
  758. $this->bcc_addrs_names = $_REQUEST['sendBcc'];
  759. $this->assigned_user_id = $current_user->id;
  760. $this->date_sent = $timedate->now();
  761. ///////////////////////////////////////////////////////////////////
  762. //// LINK EMAIL TO SUGARBEANS BASED ON EMAIL ADDY
  763. if( isset($_REQUEST['parent_type']) && !empty($_REQUEST['parent_type']) &&
  764. isset($_REQUEST['parent_id']) && !empty($_REQUEST['parent_id']) ) {
  765. $this->parent_id = $_REQUEST['parent_id'];
  766. $this->parent_type = $_REQUEST['parent_type'];
  767. $q = "SELECT count(*) c FROM emails_beans WHERE email_id = '{$this->id}' AND bean_id = '{$_REQUEST['parent_id']}' AND bean_module = '{$_REQUEST['parent_type']}'";
  768. $r = $this->db->query($q);
  769. $a = $this->db->fetchByAssoc($r);
  770. if($a['c'] <= 0) {
  771. if(isset($beanList[$_REQUEST['parent_type']]) && !empty($beanList[$_REQUEST['parent_type']])) {
  772. $className = $beanList[$_REQUEST['parent_type']];
  773. if(isset($beanFiles[$className]) && !empty($beanFiles[$className])) {
  774. if(!class_exists($className)) {
  775. require_once($beanFiles[$className]);
  776. }
  777. $bean = new $className();
  778. $bean->retrieve($_REQUEST['parent_id']);
  779. if($bean->load_relationship('emails')) {
  780. $bean->emails->add($this->id);
  781. } // if
  782. } // if
  783. } // if
  784. } // if
  785. } else {
  786. if(!class_exists('aCase')) {
  787. }
  788. else{
  789. $c = new aCase();
  790. if($caseId = InboundEmail::getCaseIdFromCaseNumber($mail->Subject, $c)) {
  791. $c->retrieve($caseId);
  792. $c->load_relationship('emails');
  793. $c->emails->add($this->id);
  794. $this->parent_type = "Cases";
  795. $this->parent_id = $caseId;
  796. } // if
  797. }
  798. } // else
  799. //// LINK EMAIL TO SUGARBEANS BASED ON EMAIL ADDY
  800. ///////////////////////////////////////////////////////////////////
  801. $this->save();
  802. }
  803. if(!empty($request['fromAccount'])) {
  804. if (isset($ie->id) && !$ie->isPop3Protocol()) {
  805. $sentFolder = $ie->get_stored_options("sentFolder");
  806. if (!empty($sentFolder)) {
  807. $data = $mail->CreateHeader() . "\r\n" . $mail->CreateBody() . "\r\n";
  808. $ie->mailbox = $sentFolder;
  809. if ($ie->connectMailserver() == 'true') {
  810. $connectString = $ie->getConnectString($ie->getServiceString(), $ie->mailbox);
  811. $returnData = imap_append($ie->conn,$connectString, $data, "\\Seen");
  812. if (!$returnData) {
  813. $GLOBALS['log']->debug("could not copy email to {$ie->mailbox} for {$ie->name}");
  814. } // if
  815. } else {
  816. $GLOBALS['log']->debug("could not connect to mail serve for folder {$ie->mailbox} for {$ie->name}");
  817. } // else
  818. } else {
  819. $GLOBALS['log']->debug("could not copy email to {$ie->mailbox} sent folder as its empty");
  820. } // else
  821. } // if
  822. } // if
  823. return true;
  824. } // end email2send
  825. /**
  826. * Generates a comma sperated name and addresses to be used in compose email screen for contacts or leads
  827. * from listview
  828. *
  829. * @param $module string module name
  830. * @param $idsArray array of record ids to get the email address for
  831. * @return string comma delimited list of email addresses
  832. */
  833. public function getNamePlusEmailAddressesForCompose($module, $idsArray)
  834. {
  835. global $locale;
  836. global $db;
  837. $table = SugarModule::get($module)->loadBean()->table_name;
  838. $returndata = array();
  839. $idsString = "";
  840. foreach($idsArray as $id) {
  841. if ($idsString != "") {
  842. $idsString = $idsString . ",";
  843. } // if
  844. $idsString = $idsString . "'" . $id . "'";
  845. } // foreach
  846. $where = "({$table}.deleted = 0 AND {$table}.id in ({$idsString}))";
  847. if ($module == 'Users' || $module == 'Employees') {
  848. $selectColumn = "{$table}.first_name, {$table}.last_name, {$table}.title";
  849. }
  850. elseif (SugarModule::get($module)->moduleImplements('Person')) {
  851. $selectColumn = "{$table}.first_name, {$table}.last_name, {$table}.salutation, {$table}.title";
  852. }
  853. else {
  854. $selectColumn = "{$table}.name";
  855. }
  856. $query = "SELECT {$table}.id, {$selectColumn}, eabr.primary_address, ea.email_address";
  857. $query .= " FROM {$table} ";
  858. $query .= "JOIN email_addr_bean_rel eabr ON ({$table}.id = eabr.bean_id and eabr.deleted=0) ";
  859. $query .= "JOIN email_addresses ea ON (eabr.email_address_id = ea.id) ";
  860. $query .= " WHERE ({$where}) ORDER BY eabr.primary_address DESC";
  861. $r = $this->db->query($query);
  862. while($a = $this->db->fetchByAssoc($r)) {
  863. if (!isset($returndata[$a['id']])) {
  864. if ($module == 'Users' || $module == 'Employees') {
  865. $full_name = from_html($locale->getLocaleFormattedName($a['first_name'], $a['last_name'], '', $a['title']));
  866. $returndata[$a['id']] = "{$full_name} <".from_html($a['email_address']).">";
  867. }
  868. elseif (SugarModule::get($module)->moduleImplements('Person')) {
  869. $full_name = from_html($locale->getLocaleFormattedName($a['first_name'], $a['last_name'], $a['salutation'], $a['title']));
  870. $returndata[$a['id']] = "{$full_name} <".from_html($a['email_address']).">";
  871. }
  872. else {
  873. $returndata[$a['id']] = from_html($a['name']) . " <".from_html($a['email_address']).">";
  874. } // else
  875. }
  876. }
  877. return join(",", array_values($returndata));
  878. }
  879. /**
  880. * Overrides
  881. */
  882. ///////////////////////////////////////////////////////////////////////////
  883. //// SAVERS
  884. function save($check_notify = false) {
  885. if($this->isDuplicate) {
  886. $GLOBALS['log']->debug("EMAIL - tried to save a duplicate Email record");
  887. } else {
  888. if(empty($this->id)) {
  889. $this->id = create_guid();
  890. $this->new_with_id = true;
  891. }
  892. $this->from_addr_name = $this->cleanEmails($this->from_addr_name);
  893. $this->to_addrs_names = $this->cleanEmails($this->to_addrs_names);
  894. $this->cc_addrs_names = $this->cleanEmails($this->cc_addrs_names);
  895. $this->bcc_addrs_names = $this->cleanEmails($this->bcc_addrs_names);
  896. $this->reply_to_addr = $this->cleanEmails($this->reply_to_addr);
  897. $this->description = to_html($this->safeText(from_html($this->description)));
  898. $this->description_html = $this->safeText($this->description_html);
  899. $this->saveEmailText();
  900. $this->saveEmailAddresses();
  901. $GLOBALS['log']->debug('-------------------------------> Email called save()');
  902. // handle legacy concatenation of date and time fields
  903. if(empty($this->date_sent)) $this->date_sent = $this->date_start." ".$this->time_start;
  904. parent::save($check_notify);
  905. if(!empty($this->parent_type) && !empty($this->parent_id)) {
  906. if(!empty($this->fetched_row) && !empty($this->fetched_row['parent_id']) && !empty($this->fetched_row['parent_type'])) {
  907. if($this->fetched_row['parent_id'] != $this->parent_id || $this->fetched_row['parent_type'] != $this->parent_type) {
  908. $mod = strtolower($this->fetched_row['parent_type']);
  909. $rel = array_key_exists($mod, $this->field_defs) ? $mod : $mod . "_activities_emails"; //Custom modules rel name
  910. if($this->load_relationship($rel) ) {
  911. $this->$rel->delete($this->id, $this->fetched_row['parent_id']);
  912. }
  913. } else {
  914. // we already have this relationship, don't add it
  915. return;
  916. }
  917. }
  918. $mod = strtolower($this->parent_type);
  919. $rel = array_key_exists($mod, $this->field_defs) ? $mod : $mod . "_activities_emails"; //Custom modules rel name
  920. if($this->load_relationship($rel) ) {
  921. $this->$rel->add($this->parent_id);
  922. }
  923. }
  924. }
  925. }
  926. /**
  927. * Helper function to save temporary attachments assocaited to an email as note.
  928. *
  929. * @param string $filename
  930. * @param string $fileLocation
  931. * @param string $mimeType
  932. */
  933. function saveTempNoteAttachments($filename,$fileLocation, $mimeType)
  934. {
  935. global $sugar_config;
  936. $tmpNote = new Note();
  937. $tmpNote->id = create_guid();
  938. $tmpNote->new_with_id = true;
  939. $tmpNote->parent_id = $this->id;
  940. $tmpNote->parent_type = $this->module_dir;
  941. $tmpNote->name = $filename;
  942. $tmpNote->filename = $filename;
  943. $tmpNote->file_mime_type = $mimeType;
  944. $noteFile = "{$sugar_config['upload_dir']}{$tmpNote->id}";
  945. if(!copy($fileLocation, $noteFile))
  946. $GLOBALS['log']->fatal("EMAIL 2.0: could not copy SugarDocument revision file to {$sugar_config['upload_dir']} [ {$fileLocation} ]");
  947. $tmpNote->save();
  948. }
  949. /**
  950. * Handles normalization of Email Addressess
  951. */
  952. function saveEmailAddresses() {
  953. // from, single address
  954. $fromId = $this->emailAddress->getEmailGUID(from_html($this->from_addr));
  955. if(!empty($fromId)){
  956. $this->linkEmailToAddress($fromId, 'from');
  957. }
  958. // to, multiple
  959. $replace = array(",",";");
  960. $toaddrs = str_replace($replace, "::", from_html($this->to_addrs));
  961. $exToAddrs = explode("::", $toaddrs);
  962. if(!empty($exToAddrs)) {
  963. foreach($exToAddrs as $toaddr) {
  964. $toaddr = trim($toaddr);
  965. if(!empty($toaddr)) {
  966. $toId = $this->emailAddress->getEmailGUID($toaddr);
  967. $this->linkEmailToAddress($toId, 'to');
  968. }
  969. }
  970. }
  971. // cc, multiple
  972. $ccAddrs = str_replace($replace, "::", from_html($this->cc_addrs));
  973. $exccAddrs = explode("::", $ccAddrs);
  974. if(!empty($exccAddrs)) {
  975. foreach($exccAddrs as $ccAddr) {
  976. $ccAddr = trim($ccAddr);
  977. if(!empty($ccAddr)) {
  978. $ccId = $this->emailAddress->getEmailGUID($ccAddr);
  979. $this->linkEmailToAddress($ccId, 'cc');
  980. }
  981. }
  982. }
  983. // bcc, multiple
  984. $bccAddrs = str_replace($replace, "::", from_html($this->bcc_addrs));
  985. $exbccAddrs = explode("::", $bccAddrs);
  986. if(!empty($exbccAddrs)) {
  987. foreach($exbccAddrs as $bccAddr) {
  988. $bccAddr = trim($bccAddr);
  989. if(!empty($bccAddr)) {
  990. $bccId = $this->emailAddress->getEmailGUID($bccAddr);
  991. $this->linkEmailToAddress($bccId, 'bcc');
  992. }
  993. }
  994. }
  995. }
  996. function linkEmailToAddress($id, $type) {
  997. // TODO: make this update?
  998. $q1 = "SELECT * FROM emails_email_addr_rel WHERE email_id = '{$this->id}' AND email_address_id = '{$id}' AND address_type = '{$type}' AND deleted = 0";
  999. $r1 = $this->db->query($q1);
  1000. $a1 = $this->db->fetchByAssoc($r1);
  1001. if(!empty($a1) && !empty($a1['id'])) {
  1002. return $a1['id'];
  1003. } else {
  1004. $guid = create_guid();
  1005. $q2 = "INSERT INTO emails_email_addr_rel VALUES('{$guid}', '{$this->id}', '{$type}', '{$id}', 0)";
  1006. $r2 = $this->db->query($q2);
  1007. }
  1008. return $guid;
  1009. }
  1010. function cleanEmails($emails)
  1011. {
  1012. $emails = str_replace(array(",",";"), "::", from_html($emails));
  1013. $addrs = explode("::", $emails);
  1014. $res = array();
  1015. foreach($addrs as $addr) {
  1016. $parts = $this->emailAddress->splitEmailAddress($addr);
  1017. if(empty($parts["email"])) {
  1018. continue;
  1019. }
  1020. if(!empty($parts["name"])) {
  1021. $res[] = "{$parts["name"]} <{$parts["email"]}>";
  1022. } else {
  1023. $res[] .= $parts["email"];
  1024. }
  1025. }
  1026. return join(", ", $res);
  1027. }
  1028. function saveEmailText() {
  1029. $isOracle = ($this->db->dbType == "oci8") ? true : false;
  1030. if ($isOracle) {
  1031. } else {
  1032. $description = $this->db->quote(trim($this->description));
  1033. $description_html = $this->db->quoteForEmail(trim($this->description_html));
  1034. $raw_source = $this->db->quote(trim($this->raw_source));
  1035. $fromAddressName = $this->db->helper->escape_quote($this->from_addr_name);
  1036. $toAddressName = $this->db->helper->escape_quote($this->to_addrs_names);
  1037. $ccAddressName = $this->db->helper->escape_quote($this->cc_addrs_names);
  1038. $bccAddressName = $this->db->helper->escape_quote($this->bcc_addrs_names);
  1039. $replyToAddrName = $this->db->helper->escape_quote($this->reply_to_addr);
  1040. if(!$this->new_with_id) {
  1041. $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}'";
  1042. } else {
  1043. $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)";
  1044. }
  1045. $this->db->query($q);
  1046. } // else
  1047. }
  1048. ///////////////////////////////////////////////////////////////////////////
  1049. //// RETRIEVERS
  1050. function retrieve($id, $encoded=true, $deleted=true) {
  1051. // cn: bug 11915, return SugarBean's retrieve() call bean instead of $this
  1052. $ret = parent::retrieve($id, $encoded, $deleted);
  1053. if($ret) {
  1054. $ret->retrieveEmailText();
  1055. $ret->retrieveEmailAddresses();
  1056. $ret->raw_source = to_html($ret->safeText(from_html($ret->raw_source)));
  1057. $ret->description = to_html($ret->safeText(from_html($ret->description)));
  1058. $ret->description_html = $ret->safeText($ret->description_html);
  1059. $ret->date_start = '';
  1060. $ret->time_start = '';
  1061. $dateSent = explode(' ', $ret->date_sent);
  1062. if (!empty($dateSent)) {
  1063. $ret->date_start = $dateSent[0];
  1064. if ( isset($dateSent[1]) )
  1065. $ret->time_start = $dateSent[1];
  1066. }
  1067. // for Email 2.0
  1068. foreach($ret as $k => $v) {
  1069. $this->$k = $v;
  1070. }
  1071. }
  1072. return $ret;
  1073. }
  1074. /**
  1075. * Retrieves email addresses from GUIDs
  1076. */
  1077. function retrieveEmailAddresses() {
  1078. $return = array();
  1079. $q = "SELECT email_address, address_type
  1080. FROM emails_email_addr_rel eam
  1081. JOIN email_addresses ea ON ea.id = eam.email_address_id
  1082. WHERE eam.email_id = '{$this->id}' AND eam.deleted=0";
  1083. $r = $this->db->query($q);
  1084. while($a = $this->db->fetchByAssoc($r)) {
  1085. if(!isset($return[$a['address_type']])) {
  1086. $return[$a['address_type']] = array();
  1087. }
  1088. $return[$a['address_type']][] = $a['email_address'];
  1089. }
  1090. if(count($return) > 0) {
  1091. if(isset($return['from'])) {
  1092. $this->from_addr = implode(", ", $return['from']);
  1093. }
  1094. if(isset($return['to'])) {
  1095. $this->to_addrs = implode(", ", $return['to']);
  1096. }
  1097. if(isset($return['cc'])) {
  1098. $this->cc_addrs = implode(", ", $return['cc']);
  1099. }
  1100. if(isset($return['bcc'])) {
  1101. $this->bcc_addrs = implode(", ", $return['bcc']);
  1102. }
  1103. }
  1104. }
  1105. /**
  1106. * Handles longtext fields
  1107. */
  1108. function retrieveEmailText() {
  1109. $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}'";
  1110. $r = $this->db->query($q);
  1111. $a = $this->db->fetchByAssoc($r, -1, false);
  1112. $this->description = $a['description'];
  1113. $this->description_html = $a['description_html'];
  1114. $this->raw_source = $a['raw_source'];
  1115. $this->from_addr_name = $a['from_addr'];
  1116. $this->reply_to_addr = $a['reply_to_addr'];
  1117. $this->to_addrs_names = $a['to_addrs'];
  1118. $this->cc_addrs_names = $a['cc_addrs'];
  1119. $this->bcc_addrs_names = $a['bcc_addrs'];
  1120. }
  1121. function delete($id='') {
  1122. if(empty($id))
  1123. $id = $this->id;
  1124. $q = "UPDATE emails SET deleted = 1 WHERE id = '{$id}'";
  1125. $qt = "UPDATE emails_text SET deleted = 1 WHERE email_id = '{$id}'";
  1126. $r = $this->db->query($q);
  1127. $rt = $this->db->query($qt);
  1128. }
  1129. /**
  1130. * creates the standard "Forward" info at the top of the forwarded message
  1131. * @return string
  1132. */
  1133. function getForwardHeader() {
  1134. global $mod_strings;
  1135. global $current_user;
  1136. //$from = str_replace(array("&gt;","&lt;"), array(")","("), $this->from_name);
  1137. $from = to_html($this->from_name);
  1138. $subject = to_html($this->name);
  1139. $ret = "<br /><br />";
  1140. $ret .= $this->replyDelimiter."{$mod_strings['LBL_FROM']} {$from}<br />";
  1141. $ret .= $this->replyDelimiter."{$mod_strings['LBL_DATE_SENT']} {$this->date_sent}<br />";
  1142. $ret .= $this->replyDelimiter."{$mod_strings['LBL_TO']} {$this->to_addrs}<br />";
  1143. $ret .= $this->replyDelimiter."{$mod_strings['LBL_CC']} {$this->cc_addrs}<br />";
  1144. $ret .= $this->replyDelimiter."{$mod_strings['LBL_SUBJECT']} {$subject}<br />";
  1145. $ret .= $this->replyDelimiter."<br />";
  1146. return $ret;
  1147. //return from_html($ret);
  1148. }
  1149. /**
  1150. * retrieves Notes that belong to this Email and stuffs them into the "attachments" attribute
  1151. */
  1152. function getNotes($id, $duplicate=false) {
  1153. if(!class_exists('Note')) {
  1154. }
  1155. $exRemoved = array();
  1156. if(isset($_REQUEST['removeAttachment'])) {
  1157. $exRemoved = explode('::', $_REQUEST['removeAttachment']);
  1158. }
  1159. $noteArray = array();
  1160. $q = "SELECT id FROM notes WHERE parent_id = '".$id."'";
  1161. $r = $this->db->query($q);
  1162. while($a = $this->db->fetchByAssoc($r)) {
  1163. if(!in_array($a['id'], $exRemoved)) {
  1164. $note = new Note();
  1165. $note->retrieve($a['id']);
  1166. // duplicate actual file when creating forwards
  1167. if($duplicate) {
  1168. if(!class_exists('UploadFile')) {
  1169. require_once('include/upload_file.php');
  1170. }
  1171. // save a brand new Note
  1172. $noteDupe->id = create_guid();
  1173. $noteDupe->new_with_id = true;
  1174. $noteDupe->parent_id = $this->id;
  1175. $noteDupe->parent_type = $this->module_dir;
  1176. $noteFile = new UploadFile('none');
  1177. $noteFile->duplicate_file($a['id'], $note->id, $note->filename);
  1178. $note->save();
  1179. }
  1180. // add Note to attachments array
  1181. $this->attachments[] = $note;
  1182. }
  1183. }
  1184. }
  1185. /**
  1186. * creates the standard "Reply" info at the top of the forwarded message
  1187. * @return string
  1188. */
  1189. function getReplyHeader() {
  1190. global $mod_strings;
  1191. global $current_user;
  1192. $from = str_replace(array("&gt;","&lt;", ">","<"), array(")","(",")","("), $this->from_name);
  1193. $ret = "<br>{$mod_strings['LBL_REPLY_HEADER_1']} {$this->date_start}, {$this->time_start}, {$from} {$mod_strings['LBL_REPLY_HEADER_2']}";
  1194. return from_html($ret);
  1195. }
  1196. /**
  1197. * Quotes plain-text email text
  1198. * @param string $text
  1199. * @return string
  1200. */
  1201. function quotePlainTextEmail($text) {
  1202. $quoted = "\n";
  1203. // plain-text
  1204. $desc = nl2br(trim($text));
  1205. $exDesc = explode('<br />', $desc);
  1206. foreach($exDesc as $k => $line) {
  1207. $quoted .= '> '.trim($line)."\r";
  1208. }
  1209. return $quoted;
  1210. }
  1211. /**
  1212. * "quotes" (i.e., "> my text yadda" the HTML part of an email
  1213. * @param string $text HTML text to quote
  1214. * @return string
  1215. */
  1216. function quoteHtmlEmail($text) {
  1217. $text = trim(from_html($text));
  1218. if(empty($text)) {
  1219. return '';
  1220. }
  1221. $out = "<div style='border-left:1px solid #00c; padding:5px; margin-left:10px;'>{$text}</div>";
  1222. return $out;
  1223. }
  1224. /**
  1225. * "quotes" (i.e., "> my text yadda" the HTML part of an email
  1226. * @param string $text HTML text to quote
  1227. * @return string
  1228. */
  1229. function quoteHtmlEmailForNewEmailUI($text) {
  1230. $text = trim($text);
  1231. if(empty($text)) {
  1232. return '';
  1233. }
  1234. $text = str_replace("\n", "\n<BR/>", $text);
  1235. $out = "<div style='border-left:1px solid #00c; padding:5px; margin-left:10px;'>{$text}</div>";
  1236. return $out;
  1237. }
  1238. ///////////////////////////////////////////////////////////////////////////
  1239. //// LEGACY CODE
  1240. /**
  1241. * Safes description text (both HTML and Plain Text) for display
  1242. * @param string str The text to safe
  1243. * @return string Safed text
  1244. */
  1245. function safeText($str) {
  1246. // Safe_HTML
  1247. $this->safe->clear();
  1248. $ret = $this->safe->parse($str);
  1249. // Julian's XSS cleaner
  1250. $potentials = clean_xss($str, false);
  1251. if(is_array($potentials) && !empty($potentials)) {
  1252. //_ppl($potentials);
  1253. foreach($potentials as $bad) {
  1254. $ret = str_replace($bad, "", $ret);
  1255. }
  1256. }
  1257. // clean <HTML> and <BODY> tags
  1258. $html = '#<\\\\\?HTML[\w =\'\"\&]*>#sim';
  1259. $body = '#<\\\\\?BODY[\w =\'\"\&]*>#sim';
  1260. $ret = preg_replace($html, "", $ret);
  1261. $ret = preg_replace($body, "", $ret);
  1262. return $ret;
  1263. }
  1264. /**
  1265. * Ensures that the user is able to send outbound emails
  1266. */
  1267. function check_email_settings() {
  1268. global $current_user;
  1269. $mail_fromaddress = $current_user->emailAddre

Large files files are truncated, but you can click here to view the full file