PageRenderTime 39ms CodeModel.GetById 19ms 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
  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->emailAddress->getPrimaryAddress($current_user);
  1270. $replyToName = $current_user->getPreference('mail_fromname');
  1271. $mail_fromname = (!empty($replyToName)) ? $current_user->getPreference('mail_fromname') : $current_user->full_name;
  1272. if(empty($mail_fromaddress)) {
  1273. return false;
  1274. }
  1275. if(empty($mail_fromname)) {
  1276. return false;
  1277. }
  1278. $send_type = $current_user->getPreference('mail_sendtype') ;
  1279. if (!empty($send_type) && $send_type == "SMTP") {
  1280. $mail_smtpserver = $current_user->getPreference('mail_smtpserver');
  1281. $mail_smtpport = $current_user->getPreference('mail_smtpport');
  1282. $mail_smtpauth_req = $current_user->getPreference('mail_smtpauth_req');
  1283. $mail_smtpuser = $current_user->getPreference('mail_smtpuser');
  1284. $mail_smtppass = $current_user->getPreference('mail_smtppass');
  1285. if (empty($mail_smtpserver) ||
  1286. empty($mail_smtpport) ||
  1287. (!empty($mail_smtpauth_req) && ( empty($mail_smtpuser) || empty($mail_smtppass)))
  1288. ) {
  1289. return false;
  1290. }
  1291. }
  1292. return true;
  1293. }
  1294. /**
  1295. * outputs JS to set fields in the MassUpdate form in the "My Inbox" view
  1296. */
  1297. function js_set_archived() {
  1298. global $mod_strings;
  1299. $script = '
  1300. <script type="text/javascript" language="JavaScript"><!-- Begin
  1301. function setArchived() {
  1302. var form = document.getElementById("MassUpdate");
  1303. var status = document.getElementById("mass_status");
  1304. var ok = false;
  1305. for(var i=0; i < form.elements.length; i++) {
  1306. if(form.elements[i].name == "mass[]") {
  1307. if(form.elements[i].checked == true) {
  1308. ok = true;
  1309. }
  1310. }
  1311. }
  1312. if(ok == true) {
  1313. var user = document.getElementById("mass_assigned_user_name");
  1314. var team = document.getElementById("team");
  1315. user.value = "";
  1316. for(var j=0; j<status.length; j++) {
  1317. if(status.options[j].value == "archived") {
  1318. status.options[j].selected = true;
  1319. status.selectedIndex = j; // for IE
  1320. }
  1321. }
  1322. form.submit();
  1323. } else {
  1324. alert("'.$mod_strings['ERR_ARCHIVE_EMAIL'].'");
  1325. }
  1326. }
  1327. // End --></script>';
  1328. return $script;
  1329. }
  1330. /**
  1331. * replaces the javascript in utils.php - more specialized
  1332. */
  1333. function u_get_clear_form_js($type='', $group='', $assigned_user_id='') {
  1334. $uType = '';
  1335. $uGroup = '';
  1336. $uAssigned_user_id = '';
  1337. if(!empty($type)) { $uType = '&type='.$type; }
  1338. if(!empty($group)) { $uGroup = '&group='.$group; }
  1339. if(!empty($assigned_user_id)) { $uAssigned_user_id = '&assigned_user_id='.$assigned_user_id; }
  1340. $the_script = '
  1341. <script type="text/javascript" language="JavaScript"><!-- Begin
  1342. function clear_form(form) {
  1343. var newLoc = "index.php?action=" + form.action.value + "&module=" + form.module.value + "&query=true&clear_query=true'.$uType.$uGroup.$uAssigned_user_id.'";
  1344. if(typeof(form.advanced) != "undefined"){
  1345. newLoc += "&advanced=" + form.advanced.value;
  1346. }
  1347. document.location.href= newLoc;
  1348. }
  1349. // End --></script>';
  1350. return $the_script;
  1351. }
  1352. function pickOneButton() {
  1353. global $theme;
  1354. global $mod_strings;
  1355. $out = '<div><input title="'.$mod_strings['LBL_BUTTON_GRAB_TITLE'].'"
  1356. accessKey="'.$mod_strings['LBL_BUTTON_GRAB_KEY'].'"
  1357. class="button"
  1358. type="button" name="button"
  1359. onClick="window.location=\'index.php?module=Emails&action=Grab\';"
  1360. style="margin-bottom:2px"
  1361. value=" '.$mod_strings['LBL_BUTTON_GRAB'].' "></div>';
  1362. return $out;
  1363. }
  1364. /**
  1365. * Determines what Editor (HTML or Plain-text) the current_user uses;
  1366. * @return string Editor type
  1367. */
  1368. function getUserEditorPreference() {
  1369. global $sugar_config;
  1370. global $current_user;
  1371. $editor = '';
  1372. if(!isset($sugar_config['email_default_editor'])) {
  1373. $sugar_config = $current_user->setDefaultsInConfig();
  1374. }
  1375. $userEditor = $current_user->getPreference('email_editor_option');
  1376. $systemEditor = $sugar_config['email_default_editor'];
  1377. if($userEditor != '') {
  1378. $editor = $userEditor;
  1379. } else {
  1380. $editor = $systemEditor;
  1381. }
  1382. return $editor;
  1383. }
  1384. /**
  1385. * takes the mess we pass from EditView and tries to create some kind of order
  1386. * @param array addrs
  1387. * @param array addrs_ids (from contacts)
  1388. * @param array addrs_names (from contacts);
  1389. * @param array addrs_emails (from contacts);
  1390. * @return array Parsed assoc array to feed to PHPMailer
  1391. */
  1392. function parse_addrs($addrs, $addrs_ids, $addrs_names, $addrs_emails) {
  1393. // cn: bug 9406 - enable commas to separate email addresses
  1394. $addrs = str_replace(",", ";", $addrs);
  1395. $ltgt = array('&lt;','&gt;');
  1396. $gtlt = array('<','>');
  1397. $return = array();
  1398. $addrs = str_replace($ltgt, '', $addrs);
  1399. $addrs_arr = explode(";",$addrs);
  1400. $addrs_arr = $this->remove_empty_fields($addrs_arr);
  1401. $addrs_ids_arr = explode(";",$addrs_ids);
  1402. $addrs_ids_arr = $this->remove_empty_fields($addrs_ids_arr);
  1403. $addrs_emails_arr = explode(";",$addrs_emails);
  1404. $addrs_emails_arr = $this->remove_empty_fields($addrs_emails_arr);
  1405. $addrs_names_arr = explode(";",$addrs_names);
  1406. $addrs_names_arr = $this->remove_empty_fields($addrs_names_arr);
  1407. ///////////////////////////////////////////////////////////////////////
  1408. //// HANDLE EMAILS HAND-WRITTEN
  1409. $contactRecipients = array();
  1410. $knownEmails = array();
  1411. foreach($addrs_arr as $i => $v) {
  1412. if(trim($v) == "")
  1413. continue; // skip any "blanks" - will always have 1
  1414. $recipient = array();
  1415. //// get the email to see if we're dealing with a dupe
  1416. //// what crappy coding
  1417. preg_match("/[A-Z0-9._%-\']+@[A-Z0-9.-]+\.[A-Z]{2,}/i",$v, $match);
  1418. if(!empty($match[0]) && !in_array(trim($match[0]), $knownEmails)) {
  1419. $knownEmails[] = $match[0];
  1420. $recipient['email'] = $match[0];
  1421. //// handle the Display name
  1422. $display = trim(str_replace($match[0], '', $v));
  1423. //// only trigger a "displayName" <email@address> when necessary
  1424. if(isset($addrs_names_arr[$i])){
  1425. $recipient['display'] = $addrs_names_arr[$i];
  1426. }
  1427. else if(!empty($display)) {
  1428. $recipient['display'] = $display;
  1429. }
  1430. if(isset($addrs_ids_arr[$i]) && $addrs_emails_arr[$i] == $match[0]){
  1431. $recipient['contact_id'] = $addrs_ids_arr[$i];
  1432. }
  1433. $return[] = $recipient;
  1434. }
  1435. }
  1436. return $return;
  1437. }
  1438. function remove_empty_fields(&$arr) {
  1439. $newarr = array();
  1440. foreach($arr as $field) {
  1441. $field = trim($field);
  1442. if(empty($field)) {
  1443. continue;
  1444. }
  1445. array_push($newarr,$field);
  1446. }
  1447. return $newarr;
  1448. }
  1449. /**
  1450. * handles attachments of various kinds when sending email
  1451. */
  1452. function handleAttachments() {
  1453. global $mod_strings;
  1454. ///////////////////////////////////////////////////////////////////////////
  1455. //// ATTACHMENTS FROM DRAFTS
  1456. if(($this->type == 'out' || $this->type == 'draft') && $this->status == 'draft' && isset($_REQUEST['record'])) {
  1457. $this->getNotes($_REQUEST['record']); // cn: get notes from OLD email for use in new email
  1458. }
  1459. //// END ATTACHMENTS FROM DRAFTS
  1460. ///////////////////////////////////////////////////////////////////////////
  1461. ///////////////////////////////////////////////////////////////////////////
  1462. //// ATTACHMENTS FROM FORWARDS
  1463. // Bug 8034 Jenny - Need the check for type 'draft' here to handle cases where we want to save
  1464. // forwarded messages as drafts. We still need to save the original message's attachments.
  1465. if(($this->type == 'out' || $this->type == 'draft') &&
  1466. isset($_REQUEST['origType']) && $_REQUEST['origType'] == 'forward' &&
  1467. isset($_REQUEST['return_id']) && !empty($_REQUEST['return_id'])
  1468. ) {
  1469. $this->getNotes($_REQUEST['return_id'], true);
  1470. }
  1471. // cn: bug 8034 - attachments from forward/replies lost when saving in draft
  1472. if(isset($_REQUEST['prior_attachments']) && !empty($_REQUEST['prior_attachments']) && $this->new_with_id == true) {
  1473. $exIds = explode(",", $_REQUEST['prior_attachments']);
  1474. if(!isset($_REQUEST['template_attachment'])) {
  1475. $_REQUEST['template_attachment'] = array();
  1476. }
  1477. $_REQUEST['template_attachment'] = array_merge($_REQUEST['template_attachment'], $exIds);
  1478. }
  1479. //// END ATTACHMENTS FROM FORWARDS
  1480. ///////////////////////////////////////////////////////////////////////////
  1481. ///////////////////////////////////////////////////////////////////////////
  1482. //// ATTACHMENTS FROM TEMPLATES
  1483. // to preserve individual email integrity, we must dupe Notes and associated files
  1484. // for each outbound email - good for integrity, bad for filespace
  1485. if(isset($_REQUEST['template_attachment']) && !empty($_REQUEST['template_attachment'])) {
  1486. $removeArr = array();
  1487. $noteArray = array();
  1488. if(isset($_REQUEST['temp_remove_attachment']) && !empty($_REQUEST['temp_remove_attachment'])) {
  1489. $removeArr = $_REQUEST['temp_remove_attachment'];
  1490. }
  1491. foreach($_REQUEST['template_attachment'] as $noteId) {
  1492. if(in_array($noteId, $removeArr)) {
  1493. continue;
  1494. }
  1495. $noteTemplate = new Note();
  1496. $noteTemplate->retrieve($noteId);
  1497. $noteTemplate->id = create_guid();
  1498. $noteTemplate->new_with_id = true; // duplicating the note with files
  1499. $noteTemplate->parent_id = $this->id;
  1500. $noteTemplate->parent_type = $this->module_dir;
  1501. $noteTemplate->date_entered = '';
  1502. $noteTemplate->save();
  1503. $noteFile = new UploadFile('none');
  1504. $noteFile->duplicate_file($noteId, $noteTemplate->id, $noteTemplate->filename);
  1505. $noteArray[] = $noteTemplate;
  1506. }
  1507. $this->attachments = array_merge($this->attachments, $noteArray);
  1508. }
  1509. //// END ATTACHMENTS FROM TEMPLATES
  1510. ///////////////////////////////////////////////////////////////////////////
  1511. ///////////////////////////////////////////////////////////////////////////
  1512. //// ADDING NEW ATTACHMENTS
  1513. $max_files_upload = 10;
  1514. // Jenny - Bug 8211 Since attachments for drafts have already been processed,
  1515. // we don't need to re-process them.
  1516. if($this->status != "draft") {
  1517. $notes_list = array();
  1518. if(!empty($this->id) && !$this->new_with_id) {
  1519. $note = new Note();
  1520. $where = "notes.parent_id='{$this->id}'";
  1521. $notes_list = $note->get_full_list("", $where, true);
  1522. }
  1523. $this->attachments = array_merge($this->attachments, $notes_list);
  1524. }
  1525. // cn: Bug 5995 - rudimentary error checking
  1526. $filesError = array(
  1527. 0 => 'UPLOAD_ERR_OK - There is no error, the file uploaded with success.',
  1528. 1 => 'UPLOAD_ERR_INI_SIZE - The uploaded file exceeds the upload_max_filesize directive in php.ini.',
  1529. 2 => 'UPLOAD_ERR_FORM_SIZE - The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.',
  1530. 3 => 'UPLOAD_ERR_PARTIAL - The uploaded file was only partially uploaded.',
  1531. 4 => 'UPLOAD_ERR_NO_FILE - No file was uploaded.',
  1532. 5 => 'UNKNOWN ERROR',
  1533. 6 => 'UPLOAD_ERR_NO_TMP_DIR - Missing a temporary folder. Introduced in PHP 4.3.10 and PHP 5.0.3.',
  1534. 7 => 'UPLOAD_ERR_CANT_WRITE - Failed to write file to disk. Introduced in PHP 5.1.0.',
  1535. );
  1536. for($i = 0; $i < $max_files_upload; $i++) {
  1537. // cn: Bug 5995 - rudimentary error checking
  1538. if (!isset($_FILES["email_attachment{$i}"])) {
  1539. $GLOBALS['log']->debug("Email Attachment {$i} does not exist.");
  1540. continue;
  1541. }
  1542. if($_FILES['email_attachment'.$i]['error'] != 0 && $_FILES['email_attachment'.$i]['error'] != 4) {
  1543. $GLOBALS['log']->debug('Email Attachment could not be attach due to error: '.$filesError[$_FILES['email_attachment'.$i]['error']]);
  1544. continue;
  1545. }
  1546. $note = new Note();
  1547. $note->parent_id = $this->id;
  1548. $note->parent_type = $this->module_dir;
  1549. $upload_file = new UploadFile('email_attachment'.$i);
  1550. if(empty($upload_file)) {
  1551. continue;
  1552. }
  1553. if(isset($_FILES['email_attachment'.$i]) && $upload_file->confirm_upload()) {
  1554. $note->filename = $upload_file->get_stored_file_name();
  1555. $note->file = $upload_file;
  1556. $note->name = $mod_strings['LBL_EMAIL_ATTACHMENT'].': '.$note->file->original_file_name;
  1557. $this->attachments[] = $note;
  1558. }
  1559. }
  1560. $this->saved_attachments = array();
  1561. foreach($this->attachments as $note) {
  1562. if(!empty($note->id)) {
  1563. array_push($this->saved_attachments, $note);
  1564. continue;
  1565. }
  1566. $note->parent_id = $this->id;
  1567. $note->parent_type = 'Emails';
  1568. $note->file_mime_type = $note->file->mime_type;
  1569. $note_id = $note->save();
  1570. $this->saved_attachments[] = $note;
  1571. $note->id = $note_id;
  1572. $note->file->final_move($note->id);
  1573. }
  1574. //// END NEW ATTACHMENTS
  1575. ///////////////////////////////////////////////////////////////////////////
  1576. ///////////////////////////////////////////////////////////////////////////
  1577. //// ATTACHMENTS FROM DOCUMENTS
  1578. for($i=0; $i<10; $i++) {
  1579. if(isset($_REQUEST['documentId'.$i]) && !empty($_REQUEST['documentId'.$i])) {
  1580. $doc = new Document();
  1581. $docRev = new DocumentRevision();
  1582. $docNote = new Note();
  1583. $noteFile = new UploadFile('none');
  1584. $doc->retrieve($_REQUEST['documentId'.$i]);
  1585. $docRev->retrieve($doc->document_revision_id);
  1586. $this->saved_attachments[] = $docRev;
  1587. // cn: bug 9723 - Emails with documents send GUID instead of Doc name
  1588. $docNote->name = $docRev->getDocumentRevisionNameForDisplay();
  1589. $docNote->filename = $docRev->filename;
  1590. $docNote->description = $doc->description;
  1591. $docNote->parent_id = $this->id;
  1592. $docNote->parent_type = 'Emails';
  1593. $docNote->file_mime_type = $docRev->file_mime_type;
  1594. $docId = $docNote = $docNote->save();
  1595. $noteFile->duplicate_file($docRev->id, $docId, $docRev->filename);
  1596. }
  1597. }
  1598. //// END ATTACHMENTS FROM DOCUMENTS
  1599. ///////////////////////////////////////////////////////////////////////////
  1600. ///////////////////////////////////////////////////////////////////////////
  1601. //// REMOVE ATTACHMENTS
  1602. if(isset($_REQUEST['remove_attachment']) && !empty($_REQUEST['remove_attachment'])) {
  1603. foreach($_REQUEST['remove_attachment'] as $noteId) {
  1604. $q = 'UPDATE notes SET deleted = 1 WHERE id = \''.$noteId.'\'';
  1605. $this->db->query($q);
  1606. }
  1607. }
  1608. //this will remove attachments that have been selected to be removed from drafts.
  1609. if(isset($_REQUEST['removeAttachment']) && !empty($_REQUEST['removeAttachment'])) {
  1610. $exRemoved = explode('::', $_REQUEST['removeAttachment']);
  1611. foreach($exRemoved as $noteId) {
  1612. $q = 'UPDATE notes SET deleted = 1 WHERE id = \''.$noteId.'\'';
  1613. $this->db->query($q);
  1614. }
  1615. }
  1616. //// END REMOVE ATTACHMENTS
  1617. ///////////////////////////////////////////////////////////////////////////
  1618. }
  1619. /**
  1620. * Determines if an email body (HTML or Plain) has a User signature already in the content
  1621. * @param array Array of signatures
  1622. * @return bool
  1623. */
  1624. function hasSignatureInBody($sig) {
  1625. // strpos can't handle line breaks - normalize
  1626. $html = $this->removeAllNewlines($this->description_html);
  1627. $htmlSig = $this->removeAllNewlines($sig['signature_html']);
  1628. $plain = $this->removeAllNewlines($this->description);
  1629. $plainSig = $this->removeAllNewlines($sig['signature']);
  1630. // cn: bug 11621 - empty sig triggers notice error
  1631. if(!empty($htmlSig) && false !== strpos($html, $htmlSig)) {
  1632. return true;
  1633. } elseif(!empty($plainSig) && false !== strpos($plain, $plainSig)) {
  1634. return true;
  1635. } else {
  1636. return false;
  1637. }
  1638. }
  1639. /**
  1640. * internal helper
  1641. * @param string String to be normalized
  1642. * @return string
  1643. */
  1644. function removeAllNewlines($str) {
  1645. $bad = array("\r\n", "\n\r", "\n", "\r");
  1646. $good = array('', '', '', '');
  1647. return str_replace($bad, $good, strip_tags(br2nl(from_html($str))));
  1648. }
  1649. /**
  1650. * Set navigation anchors to aid DetailView record navigation (VCR buttons)
  1651. * @param string uri The URI from the referring page (always ListView)
  1652. * @return array start Array of the URI broken down with a special "current_view" for My Inbox Navs
  1653. */
  1654. function getStartPage($uri) {
  1655. if(strpos($uri, '&')) { // "&" to ensure that we can explode the GET vars - else we're gonna trigger a Notice error
  1656. $serial = substr($uri, (strpos($uri, '?')+1), strlen($uri));
  1657. $exUri = explode('&', $serial);
  1658. $start = array('module' => '', 'action' => '', 'group' => '', 'record' => '', 'type' => '');
  1659. foreach($exUri as $k => $pair) {
  1660. $exPair = explode('=', $pair);
  1661. $start[$exPair[0]] = $exPair[1];
  1662. }
  1663. // specific views for current_user
  1664. if(isset($start['assigned_user_id'])) {
  1665. $start['current_view'] = "{$start['action']}&module={$start['module']}&assigned_user_id={$start['assigned_user_id']}&type={$start['type']}";
  1666. }
  1667. return $start;
  1668. } else {
  1669. return array();
  1670. }
  1671. }
  1672. /**
  1673. * preps SMTP info for email transmission
  1674. * @param object mail SugarPHPMailer object
  1675. * @param string mailer_id
  1676. * @param string ieId
  1677. * @return object mail SugarPHPMailer object
  1678. */
  1679. function setMailer($mail, $mailer_id='', $ieId='') {
  1680. global $current_user;
  1681. require_once("include/OutboundEmail/OutboundEmail.php");
  1682. $oe = new OutboundEmail();
  1683. $oe = $oe->getInboundMailerSettings($current_user, $mailer_id, $ieId);
  1684. // ssl or tcp - keeping outside isSMTP b/c a default may inadvertantly set ssl://
  1685. $mail->protocol = ($oe->mail_smtpssl) ? "ssl://" : "tcp://";
  1686. if($oe->mail_sendtype == "SMTP")
  1687. {
  1688. //Set mail send type information
  1689. $mail->Mailer = "smtp";
  1690. $mail->Host = $oe->mail_smtpserver;
  1691. $mail->Port = $oe->mail_smtpport;
  1692. if ($oe->mail_smtpssl == 1) {
  1693. $mail->SMTPSecure = 'ssl';
  1694. } // if
  1695. if ($oe->mail_smtpssl == 2) {
  1696. $mail->SMTPSecure = 'tls';
  1697. } // if
  1698. if($oe->mail_smtpauth_req) {
  1699. $mail->SMTPAuth = TRUE;
  1700. $mail->Username = $oe->mail_smtpuser;
  1701. $mail->Password = $oe->mail_smtppass;
  1702. }
  1703. }
  1704. else
  1705. $mail->Mailer = "sendmail";
  1706. $mail->oe = $oe;
  1707. return $mail;
  1708. }
  1709. /**
  1710. * preps SugarPHPMailer object for HTML or Plain text sends
  1711. * @param object SugarPHPMailer instance
  1712. */
  1713. function handleBody($mail) {
  1714. global $current_user;
  1715. global $sugar_config;
  1716. ///////////////////////////////////////////////////////////////////////
  1717. //// HANDLE EMAIL FORMAT PREFERENCE
  1718. // the if() below is HIGHLY dependent on the Javascript unchecking the Send HTML Email box
  1719. // HTML email
  1720. if( (isset($_REQUEST['setEditor']) /* from Email EditView navigation */
  1721. && $_REQUEST['setEditor'] == 1
  1722. && trim($_REQUEST['description_html']) != '')
  1723. || trim($this->description_html) != '' /* from email templates */
  1724. && $current_user->getPreference('email_editor_option', 'global') !== 'plain' //user preference is not set to plain text
  1725. ) {
  1726. $this->handleBodyInHTMLformat($mail);
  1727. } else {
  1728. // plain text only
  1729. $this->description_html = '';
  1730. $mail->IsHTML(false);
  1731. $plainText = from_html($this->description);
  1732. $plainText = str_replace("&nbsp;", " ", $plainText);
  1733. $plainText = str_replace("</p>", "</p><br />", $plainText);
  1734. $plainText = strip_tags(br2nl($plainText));
  1735. $plainText = str_replace("&amp;", "&", $plainText);
  1736. $plainText = str_replace("&#39;", "'", $plainText);
  1737. $mail->Body = wordwrap($plainText, 996);
  1738. $mail->Body = $this->decodeDuringSend($mail->Body);
  1739. $this->description = $mail->Body;
  1740. }
  1741. // wp: if plain text version has lines greater than 998, use base64 encoding
  1742. foreach(explode("\n", ($mail->ContentType == "text/html") ? $mail->AltBody : $mail->Body) as $line) {
  1743. if(strlen($line) > 998) {
  1744. $mail->Encoding = 'base64';
  1745. break;
  1746. }
  1747. }
  1748. //// HANDLE EMAIL FORMAT PREFERENCE
  1749. ///////////////////////////////////////////////////////////////////////
  1750. return $mail;
  1751. }
  1752. /**
  1753. * Retrieve function from handlebody() to unit test easily
  1754. * @param $mail
  1755. * @return formatted $mail body
  1756. */
  1757. function handleBodyInHTMLformat($mail) {
  1758. global $current_user;
  1759. global $sugar_config;
  1760. // wp: if body is html, then insert new lines at 996 characters. no effect on client side
  1761. // due to RFC 2822 which limits email lines to 998
  1762. $mail->IsHTML(true);
  1763. $body = from_html(wordwrap($this->description_html, 996));
  1764. $mail->Body = $body;
  1765. // cn: bug 9725
  1766. // new plan is to use the selected type (html or plain) to fill the other
  1767. $plainText = from_html($this->description_html);
  1768. $plainText = strip_tags(br2nl($plainText));
  1769. $mail->AltBody = $plainText;
  1770. $this->description = $plainText;
  1771. $fileBasePath = "{$sugar_config['cache_dir']}images/";
  1772. $filePatternSearch = "{$sugar_config['cache_dir']}";
  1773. $filePatternSearch = str_replace("/", "\/", $filePatternSearch);
  1774. $filePatternSearch = $filePatternSearch . "images\/";
  1775. if(strpos($mail->Body, "\"{$fileBasePath}") !== FALSE)
  1776. { //cache/images
  1777. $matches = array();
  1778. preg_match_all("/{$filePatternSearch}.+?\"/i", $mail->Body, $matches);
  1779. foreach($matches[0] as $match) {
  1780. $filename = str_replace($fileBasePath, '', $match);
  1781. $filename = urldecode(substr($filename, 0, -1));
  1782. $cid = $filename;
  1783. $file_location = clean_path(getcwd()."/{$sugar_config['cache_dir']}images/{$filename}");
  1784. $mime_type = "image/".strtolower(substr($filename, strrpos($filename, ".")+1, strlen($filename)));
  1785. if(file_exists($file_location)) {
  1786. $mail->AddEmbeddedImage($file_location, $cid, $filename, 'base64', $mime_type);
  1787. }
  1788. }
  1789. //replace references to cache with cid tag
  1790. $mail->Body = str_replace("/" . $fileBasePath,'cid:',$mail->Body);
  1791. $mail->Body = str_replace($fileBasePath,'cid:',$mail->Body);
  1792. // remove bad img line from outbound email
  1793. $regex = '#<img[^>]+src[^=]*=\"\/([^>]*?[^>]*)>#sim';
  1794. $mail->Body = preg_replace($regex, '', $mail->Body);
  1795. }
  1796. $fileBasePath = "{$sugar_config['upload_dir']}";
  1797. $filePatternSearch = "{$sugar_config['upload_dir']}";
  1798. $filePatternSearch = str_replace("/", "\/", $filePatternSearch);
  1799. if(strpos($mail->Body, "\"{$fileBasePath}") !== FALSE)
  1800. {
  1801. $matches = array();
  1802. preg_match_all("/{$filePatternSearch}.+?\"/i", $mail->Body, $matches);
  1803. foreach($matches[0] as $match) {
  1804. $filename = str_replace($fileBasePath, '', $match);
  1805. $filename = urldecode(substr($filename, 0, -1));
  1806. $cid = $filename;
  1807. $file_location = clean_path(getcwd()."/{$sugar_config['upload_dir']}{$filename}");
  1808. $mime_type = "image/".strtolower(substr($filename, strrpos($filename, ".")+1, strlen($filename)));
  1809. if(file_exists($file_location)) {
  1810. $mail->AddEmbeddedImage($file_location, $cid, $filename, 'base64', $mime_type);
  1811. }
  1812. }
  1813. //replace references to cache with cid tag
  1814. $mail->Body = str_replace("/" . $fileBasePath,'cid:',$mail->Body);
  1815. $mail->Body = str_replace($fileBasePath,'cid:',$mail->Body);
  1816. // remove bad img line from outbound email
  1817. $regex = '#<img[^>]+src[^=]*=\"\/([^>]*?[^>]*)>#sim';
  1818. $mail->Body = preg_replace($regex, '', $mail->Body);
  1819. }
  1820. //Replace any embeded images using the secure entryPoint for src url.
  1821. $noteImgRegex = "/<img[^>]*[\s]+src[^=]*=\"index.php\?entryPoint=download\&amp;id=([^\&]*)[^>]*>/im";
  1822. $embededImageMatches = array();
  1823. preg_match_all($noteImgRegex, $mail->Body, $embededImageMatches,PREG_SET_ORDER);
  1824. foreach ($embededImageMatches as $singleMatch )
  1825. {
  1826. $fullMatch = $singleMatch[0];
  1827. $noteId = $singleMatch[1];
  1828. $cid = $noteId;
  1829. $filename = $noteId;
  1830. //Retrieve note for mimetype
  1831. $tmpNote = new Note();
  1832. $tmpNote->retrieve($noteId);
  1833. //Replace the src part of img tag with new cid tag
  1834. $cidRegex = "/src=\"([^\"]*)\"/im";
  1835. $replaceMatch = preg_replace($cidRegex, "src=\"cid:$noteId\"", $fullMatch);
  1836. //Replace the body, old tag for new tag
  1837. $mail->Body = str_replace($fullMatch, $replaceMatch, $mail->Body);
  1838. //Attach the file
  1839. $file_location = clean_path(getcwd()."/{$sugar_config['upload_dir']}{$noteId}");
  1840. if(file_exists($file_location))
  1841. $mail->AddEmbeddedImage($file_location, $cid, $filename, 'base64', $tmpNote->file_mime_type);
  1842. }
  1843. //End Replace
  1844. $mail->Body = from_html($mail->Body);
  1845. }
  1846. /**
  1847. * Sends Email
  1848. * @return bool True on success
  1849. */
  1850. function send() {
  1851. global $mod_strings,$app_strings;
  1852. global $current_user;
  1853. global $sugar_config;
  1854. global $locale;
  1855. $OBCharset = $locale->getPrecedentPreference('default_email_charset');
  1856. $mail = new SugarPHPMailer();
  1857. foreach ($this->to_addrs_arr as $addr_arr) {
  1858. if ( empty($addr_arr['display'])) {
  1859. $mail->AddAddress($addr_arr['email'], "");
  1860. } else {
  1861. $mail->AddAddress($addr_arr['email'],$locale->translateCharsetMIME(trim( $addr_arr['display']), 'UTF-8', $OBCharset));
  1862. }
  1863. }
  1864. foreach ($this->cc_addrs_arr as $addr_arr) {
  1865. if ( empty($addr_arr['display'])) {
  1866. $mail->AddCC($addr_arr['email'], "");
  1867. } else {
  1868. $mail->AddCC($addr_arr['email'],$locale->translateCharsetMIME(trim($addr_arr['display']), 'UTF-8', $OBCharset));
  1869. }
  1870. }
  1871. foreach ($this->bcc_addrs_arr as $addr_arr) {
  1872. if ( empty($addr_arr['display'])) {
  1873. $mail->AddBCC($addr_arr['email'], "");
  1874. } else {
  1875. $mail->AddBCC($addr_arr['email'],$locale->translateCharsetMIME(trim($addr_arr['display']), 'UTF-8', $OBCharset));
  1876. }
  1877. }
  1878. $mail = $this->setMailer($mail);
  1879. // FROM ADDRESS
  1880. if(!empty($this->from_addr)) {
  1881. $mail->From = $this->from_addr;
  1882. } else {
  1883. $mail->From = $current_user->getPreference('mail_fromaddress');
  1884. $this->from_addr = $mail->From;
  1885. }
  1886. // FROM NAME
  1887. if(!empty($this->from_name)) {
  1888. $mail->FromName = $this->from_name;
  1889. } else {
  1890. $mail->FromName = $current_user->getPreference('mail_fromname');
  1891. $this->from_name = $mail->FromName;
  1892. }
  1893. //Reply to information for case create and autoreply.
  1894. if(!empty($this->reply_to_name)) {
  1895. $ReplyToName = $this->reply_to_name;
  1896. } else {
  1897. $ReplyToName = $mail->FromName;
  1898. }
  1899. if(!empty($this->reply_to_addr)) {
  1900. $ReplyToAddr = $this->reply_to_addr;
  1901. } else {
  1902. $ReplyToAddr = $mail->From;
  1903. }
  1904. $mail->Sender = $mail->From; /* set Return-Path field in header to reduce spam score in emails sent via Sugar's Email module */
  1905. $mail->AddReplyTo($ReplyToAddr,$locale->translateCharsetMIME(trim($ReplyToName), 'UTF-8', $OBCharset));
  1906. //$mail->Subject = html_entity_decode($this->name, ENT_QUOTES, 'UTF-8');
  1907. $mail->Subject = $this->name;
  1908. ///////////////////////////////////////////////////////////////////////
  1909. //// ATTACHMENTS
  1910. foreach($this->saved_attachments as $note) {
  1911. $mime_type = 'text/plain';
  1912. if($note->object_name == 'Note') {
  1913. if(!empty($note->file->temp_file_location) && is_file($note->file->temp_file_location)) { // brandy-new file upload/attachment
  1914. $file_location = $sugar_config['upload_dir'].$note->id;
  1915. $filename = $note->file->original_file_name;
  1916. $mime_type = $note->file->mime_type;
  1917. } else { // attachment coming from template/forward
  1918. $file_location = rawurldecode(UploadFile::get_file_path($note->filename,$note->id));
  1919. // cn: bug 9723 - documents from EmailTemplates sent with Doc Name, not file name.
  1920. $filename = !empty($note->filename) ? $note->filename : $note->name;
  1921. $mime_type = $note->file_mime_type;
  1922. }
  1923. } elseif($note->object_name == 'DocumentRevision') { // from Documents
  1924. $filePathName = $note->id;
  1925. // cn: bug 9723 - Emails with documents send GUID instead of Doc name
  1926. $filename = $note->getDocumentRevisionNameForDisplay();
  1927. $file_location = getcwd().'/'.$GLOBALS['sugar_config']['upload_dir'].$filePathName;
  1928. $mime_type = $note->file_mime_type;
  1929. }
  1930. // strip out the "Email attachment label if exists
  1931. $filename = str_replace($mod_strings['LBL_EMAIL_ATTACHMENT'].': ', '', $filename);
  1932. //is attachment in our list of bad files extensions? If so, append .txt to file location
  1933. //get position of last "." in file name
  1934. $file_ext_beg = strrpos($file_location,".");
  1935. $file_ext = "";
  1936. //get file extension
  1937. if($file_ext_beg >0){
  1938. $file_ext = substr($file_location, $file_ext_beg+1 );
  1939. }
  1940. //check to see if this is a file with extension located in "badext"
  1941. foreach($sugar_config['upload_badext'] as $badExt) {
  1942. if(strtolower($file_ext) == strtolower($badExt)) {
  1943. //if found, then append with .txt to filename and break out of lookup
  1944. //this will make sure that the file goes out with right extension, but is stored
  1945. //as a text in db.
  1946. $file_location = $file_location . ".txt";
  1947. break; // no need to look for more
  1948. }
  1949. }
  1950. $mail->AddAttachment($file_location,$locale->translateCharsetMIME(trim($filename), 'UTF-8', $OBCharset), 'base64', $mime_type);
  1951. // embedded Images
  1952. if($note->embed_flag == true) {
  1953. $cid = $filename;
  1954. $mail->AddEmbeddedImage($file_location, $cid, $filename, 'base64',$mime_type);
  1955. }
  1956. }
  1957. //// END ATTACHMENTS
  1958. ///////////////////////////////////////////////////////////////////////
  1959. $mail = $this->handleBody($mail);
  1960. $GLOBALS['log']->debug('Email sending --------------------- ');
  1961. ///////////////////////////////////////////////////////////////////////
  1962. //// I18N TRANSLATION
  1963. $mail->prepForOutbound();
  1964. //// END I18N TRANSLATION
  1965. ///////////////////////////////////////////////////////////////////////
  1966. if($mail->Send()) {
  1967. ///////////////////////////////////////////////////////////////////
  1968. //// INBOUND EMAIL HANDLING
  1969. // mark replied
  1970. if(!empty($_REQUEST['inbound_email_id'])) {
  1971. $ieMail = new Email();
  1972. $ieMail->retrieve($_REQUEST['inbound_email_id']);
  1973. $ieMail->status = 'replied';
  1974. $ieMail->save();
  1975. }
  1976. $GLOBALS['log']->debug(' --------------------- buh bye -- sent successful');
  1977. //// END INBOUND EMAIL HANDLING
  1978. ///////////////////////////////////////////////////////////////////
  1979. return true;
  1980. }
  1981. $GLOBALS['log']->debug($app_strings['LBL_EMAIL_ERROR_PREPEND'].$mail->ErrorInfo);
  1982. return false;
  1983. }
  1984. function listviewACLHelper(){
  1985. $array_assign = parent::listviewACLHelper();
  1986. $is_owner = false;
  1987. if(!empty($this->parent_name)){
  1988. if(!empty($this->parent_name_owner)){
  1989. global $current_user;
  1990. $is_owner = $current_user->id == $this->parent_name_owner;
  1991. }
  1992. }
  1993. if(!ACLController::moduleSupportsACL($this->parent_type) || ACLController::checkAccess($this->parent_type, 'view', $is_owner)){
  1994. $array_assign['PARENT'] = 'a';
  1995. } else {
  1996. $array_assign['PARENT'] = 'span';
  1997. }
  1998. $is_owner = false;
  1999. if(!empty($this->contact_name)) {
  2000. if(!empty($this->contact_name_owner)) {
  2001. global $current_user;
  2002. $is_owner = $current_user->id == $this->contact_name_owner;
  2003. }
  2004. }
  2005. if(ACLController::checkAccess('Contacts', 'view', $is_owner)) {
  2006. $array_assign['CONTACT'] = 'a';
  2007. } else {
  2008. $array_assign['CONTACT'] = 'span';
  2009. }
  2010. return $array_assign;
  2011. }
  2012. function getSystemDefaultEmail() {
  2013. $email = array();
  2014. $r1 = $this->db->query('SELECT config.value FROM config WHERE name=\'fromaddress\'');
  2015. $r2 = $this->db->query('SELECT config.value FROM config WHERE name=\'fromname\'');
  2016. $a1 = $this->db->fetchByAssoc($r1);
  2017. $a2 = $this->db->fetchByAssoc($r2);
  2018. $email['email'] = $a1['value'];
  2019. $email['name'] = $a2['value'];
  2020. return $email;
  2021. }
  2022. function create_new_list_query($order_by, $where,$filter=array(),$params=array(), $show_deleted = 0,$join_type='', $return_array = false,$parentbean=null, $singleSelect = false) {
  2023. if ($return_array) {
  2024. return parent::create_new_list_query($order_by, $where,$filter,$params, $show_deleted,$join_type, $return_array,$parentbean, $singleSelect);
  2025. }
  2026. $custom_join = $this->custom_fields->getJOIN();
  2027. $query = "SELECT ".$this->table_name.".*, users.user_name as assigned_user_name\n";
  2028. if($custom_join){
  2029. $query .= $custom_join['select'];
  2030. }
  2031. $query .= " FROM emails\n";
  2032. if ($where != "" && (strpos($where, "contacts.first_name") > 0)) {
  2033. $query .= " LEFT JOIN emails_beans ON emails.id = emails_beans.email_id\n";
  2034. }
  2035. $query .= " LEFT JOIN users ON emails.assigned_user_id=users.id \n";
  2036. if ($where != "" && (strpos($where, "contacts.first_name") > 0)) {
  2037. $query .= " JOIN contacts ON contacts.id= emails_beans.bean_id AND emails_beans.bean_module='Contacts' and contacts.deleted=0 \n";
  2038. }
  2039. if($custom_join){
  2040. $query .= $custom_join['join'];
  2041. }
  2042. if($show_deleted == 0) {
  2043. $where_auto = " emails.deleted=0 \n";
  2044. }else if($show_deleted == 1){
  2045. $where_auto = " emails.deleted=1 \n";
  2046. }
  2047. if($where != "")
  2048. $query .= "WHERE $where AND ".$where_auto;
  2049. else
  2050. $query .= "WHERE ".$where_auto;
  2051. if($order_by != "")
  2052. $query .= " ORDER BY $order_by";
  2053. else
  2054. $query .= " ORDER BY date_sent DESC";
  2055. return $query;
  2056. } // fn
  2057. function fill_in_additional_list_fields() {
  2058. global $timedate;
  2059. $this->fill_in_additional_detail_fields();
  2060. $this->link_action = 'DetailView';
  2061. ///////////////////////////////////////////////////////////////////////
  2062. //populate attachment_image, used to display attachment icon.
  2063. $query = "select 1 from notes where notes.parent_id = '$this->id' and notes.deleted = 0";
  2064. $result =$this->db->query($query,true," Error filling in additional list fields: ");
  2065. $row = $this->db->fetchByAssoc($result);
  2066. if ($row !=null) {
  2067. $this->attachment_image = SugarThemeRegistry::current()->getImage('attachment',"","","");
  2068. } else {
  2069. $this->attachment_image = SugarThemeRegistry::current()->getImage('blank',"","","");
  2070. }
  2071. ///////////////////////////////////////////////////////////////////////
  2072. if(empty($this->contact_id) && !empty($this->parent_id) && !empty($this->parent_type) && $this->parent_type === 'Contacts' && !empty($this->parent_name) ){
  2073. $this->contact_id = $this->parent_id;
  2074. $this->contact_name = $this->parent_name;
  2075. }
  2076. }
  2077. function fill_in_additional_detail_fields() {
  2078. global $app_list_strings,$mod_strings;
  2079. // Fill in the assigned_user_name
  2080. $this->assigned_user_name = get_assigned_user_name($this->assigned_user_id, '');
  2081. //if ($this->parent_type == 'Contacts') {
  2082. $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 ";
  2083. $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";
  2084. if(!empty($this->parent_id)){
  2085. $query .= " AND contacts.id= '".$this->parent_id."' ";
  2086. }else if(!empty($_REQUEST['record'])){
  2087. $query .= " AND contacts.id= '".$_REQUEST['record']."' ";
  2088. }
  2089. $result =$this->db->query($query,true," Error filling in additional detail fields: ");
  2090. // Get the id and the name.
  2091. $row = $this->db->fetchByAssoc($result);
  2092. if($row != null)
  2093. {
  2094. $contact = new Contact();
  2095. $contact->retrieve($row['id']);
  2096. $this->contact_name = $contact->full_name;
  2097. $this->contact_phone = $row['phone_work'];
  2098. $this->contact_id = $row['id'];
  2099. $this->contact_email = $contact->emailAddress->getPrimaryAddress($contact);
  2100. $this->contact_name_owner = $row['contact_name_owner'];
  2101. $this->contact_name_mod = $row['contact_name_mod'];
  2102. $GLOBALS['log']->debug("Call($this->id): contact_name = $this->contact_name");
  2103. $GLOBALS['log']->debug("Call($this->id): contact_phone = $this->contact_phone");
  2104. $GLOBALS['log']->debug("Call($this->id): contact_id = $this->contact_id");
  2105. $GLOBALS['log']->debug("Call($this->id): contact_email1 = $this->contact_email");
  2106. }
  2107. else {
  2108. $this->contact_name = '';
  2109. $this->contact_phone = '';
  2110. $this->contact_id = '';
  2111. $this->contact_email = '';
  2112. $this->contact_name_owner = '';
  2113. $this->contact_name_mod = '';
  2114. $GLOBALS['log']->debug("Call($this->id): contact_name = $this->contact_name");
  2115. $GLOBALS['log']->debug("Call($this->id): contact_phone = $this->contact_phone");
  2116. $GLOBALS['log']->debug("Call($this->id): contact_id = $this->contact_id");
  2117. $GLOBALS['log']->debug("Call($this->id): contact_email1 = $this->contact_email");
  2118. }
  2119. //}
  2120. $this->created_by_name = get_assigned_user_name($this->created_by);
  2121. $this->modified_by_name = get_assigned_user_name($this->modified_user_id);
  2122. $this->link_action = 'DetailView';
  2123. if(!empty($this->type)) {
  2124. if($this->type == 'out' && $this->status == 'send_error') {
  2125. $this->type_name = $mod_strings['LBL_NOT_SENT'];
  2126. } else {
  2127. $this->type_name = $app_list_strings['dom_email_types'][$this->type];
  2128. }
  2129. if(($this->type == 'out' && $this->status == 'send_error') || $this->type == 'draft') {
  2130. $this->link_action = 'EditView';
  2131. }
  2132. }
  2133. //todo this isset( $app_list_strings['dom_email_status'][$this->status]) is hack for 3261.
  2134. if(!empty($this->status) && isset( $app_list_strings['dom_email_status'][$this->status])) {
  2135. $this->status_name = $app_list_strings['dom_email_status'][$this->status];
  2136. }
  2137. if ( empty($this->name ) && empty($_REQUEST['record'])) {
  2138. $this->name = $mod_strings['LBL_NO_SUBJECT'];
  2139. }
  2140. $this->fill_in_additional_parent_fields();
  2141. }
  2142. function create_export_query(&$order_by, &$where) {
  2143. $contact_required = stristr($where, "contacts");
  2144. $custom_join = $this->custom_fields->getJOIN(true, true,$where);
  2145. if($contact_required) {
  2146. $query = "SELECT emails.*, contacts.first_name, contacts.last_name";
  2147. if($custom_join) {
  2148. $query .= $custom_join['select'];
  2149. }
  2150. $query .= " FROM contacts, emails, emails_contacts ";
  2151. $where_auto = "emails_contacts.contact_id = contacts.id AND emails_contacts.email_id = emails.id AND emails.deleted=0 AND contacts.deleted=0";
  2152. } else {
  2153. $query = 'SELECT emails.*';
  2154. if($custom_join) {
  2155. $query .= $custom_join['select'];
  2156. }
  2157. $query .= ' FROM emails ';
  2158. $where_auto = "emails.deleted=0";
  2159. }
  2160. if($custom_join){
  2161. $query .= $custom_join['join'];
  2162. }
  2163. if($where != "")
  2164. $query .= "where $where AND ".$where_auto;
  2165. else
  2166. $query .= "where ".$where_auto;
  2167. if($order_by != "")
  2168. $query .= " ORDER BY $order_by";
  2169. else
  2170. $query .= " ORDER BY emails.name";
  2171. return $query;
  2172. }
  2173. function get_list_view_data() {
  2174. global $app_list_strings;
  2175. global $theme;
  2176. global $current_user;
  2177. global $timedate;
  2178. global $mod_strings;
  2179. $email_fields = $this->get_list_view_array();
  2180. $this->retrieveEmailText();
  2181. $email_fields['FROM_ADDR'] = $this->from_addr_name;
  2182. $mod_strings = return_module_language($GLOBALS['current_language'], 'Emails'); // hard-coding for Home screen ListView
  2183. if($this->status != 'replied') {
  2184. $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>';
  2185. $email_fields['STATUS'] = ($email_fields['REPLY_TO_STATUS'] == 1 ? $mod_strings['LBL_REPLIED'] : $email_fields['STATUS']);
  2186. } else {
  2187. $email_fields['QUICK_REPLY'] = $mod_strings['LBL_REPLIED'];
  2188. }
  2189. if(!empty($this->parent_type)) {
  2190. $email_fields['PARENT_MODULE'] = $this->parent_type;
  2191. } else {
  2192. switch($this->intent) {
  2193. case 'support':
  2194. $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>';
  2195. break;
  2196. case 'sales':
  2197. $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>';
  2198. break;
  2199. case 'contact':
  2200. $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>';
  2201. break;
  2202. case 'bug':
  2203. $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>';
  2204. break;
  2205. case 'task':
  2206. $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>';
  2207. break;
  2208. case 'bounce':
  2209. break;
  2210. case 'pick':
  2211. // break;
  2212. case 'info':
  2213. //break;
  2214. default:
  2215. $email_fields['CREATE_RELATED'] = $this->quickCreateForm();
  2216. break;
  2217. }
  2218. }
  2219. //BUG 17098 - MFH changed $this->from_addr to $this->to_addrs
  2220. $email_fields['CONTACT_NAME'] = empty($this->contact_name) ? '</a>'.$this->trimLongTo($this->to_addrs).'<a>' : $this->contact_name;
  2221. $email_fields['CONTACT_ID'] = empty($this->contact_id) ? '' : $this->contact_id;
  2222. $email_fields['ATTACHMENT_IMAGE'] = $this->attachment_image;
  2223. $email_fields['LINK_ACTION'] = $this->link_action;
  2224. if(isset($this->type_name))
  2225. $email_fields['TYPE_NAME'] = $this->type_name;
  2226. return $email_fields;
  2227. }
  2228. function quickCreateForm() {
  2229. global $mod_strings, $app_strings, $currentModule, $current_language;
  2230. // Coming from the home page via Dashlets
  2231. if($currentModule != 'Email')
  2232. $mod_strings = return_module_language($current_language, 'Emails');
  2233. return $mod_strings['LBL_QUICK_CREATE']."&nbsp;<a id='$this->id' onclick='return quick_create_overlib(\"{$this->id}\", \"".SugarThemeRegistry::current()->__toString()."\");' href=\"#\" >".SugarThemeRegistry::current()->getImage("advanced_search","alt='".$mod_strings['LBL_QUICK_CREATE']."' border='0' align='absmiddle'")."</a>";
  2234. }
  2235. /**
  2236. * Searches all imported emails and returns the result set as an array.
  2237. *
  2238. */
  2239. function searchImportedEmails($sort = '', $direction='')
  2240. {
  2241. require_once('include/TimeDate.php');
  2242. global $timedate;
  2243. global $current_user;
  2244. global $beanList;
  2245. global $sugar_config;
  2246. global $app_strings;
  2247. $emailSettings = $current_user->getPreference('emailSettings', 'Emails');
  2248. // cn: default to a low number until user specifies otherwise
  2249. if(empty($emailSettings['showNumInList']))
  2250. $pageSize = 20;
  2251. else
  2252. $pageSize = $emailSettings['showNumInList'];
  2253. if( isset($_REQUEST['start']) && isset($_REQUEST['limit']) )
  2254. $page = ceil($_REQUEST['start'] / $_REQUEST['limit']) + 1;
  2255. else
  2256. $page = 1;
  2257. //Determine sort ordering
  2258. //Sort ordering parameters in the request do not coincide with actual column names
  2259. //so we need to remap them.
  2260. $hrSortLocal = array(
  2261. 'flagged' => 'type',
  2262. 'status' => 'reply_to_status',
  2263. 'from' => 'emails_text.from_addr',
  2264. 'subject' => 'name',
  2265. 'date' => 'date_sent',
  2266. 'AssignedTo' => 'assigned_user_id',
  2267. 'flagged' => 'flagged'
  2268. );
  2269. $sort = !empty($_REQUEST['sort']) ? $_REQUEST['sort'] : "";
  2270. $direction = !empty($_REQUEST['dir']) ? $_REQUEST['dir'] : "";
  2271. $order = ( !empty($sort) && !empty($direction) ) ? " ORDER BY {$hrSortLocal[$sort]} {$direction}" : "";
  2272. //Get our main query.
  2273. $fullQuery = $this->_genereateSearchImportedEmailsQuery();
  2274. //Perform a count query needed for pagination.
  2275. $countQuery = $this->create_list_count_query($fullQuery);
  2276. $count_rs = $this->db->query($countQuery, false, 'Error executing count query for imported emails search');
  2277. $count_row = $this->db->fetchByAssoc($count_rs);
  2278. $total_count = ($count_row != null) ? $count_row['c'] : 0;
  2279. $start = ($page - 1) * $pageSize;
  2280. //Execute the query
  2281. $rs = $this->db->limitQuery($fullQuery . $order, $start, $pageSize);
  2282. $return = array();
  2283. while($a = $this->db->fetchByAssoc($rs)) {
  2284. $temp = array();
  2285. $temp['flagged'] = (is_null($a['flagged']) || $a['flagged'] == '0') ? '' : 1;
  2286. $temp['status'] = (is_null($a['reply_to_status']) || $a['reply_to_status'] == '0') ? '' : 1;
  2287. $temp['subject'] = $a['name'];
  2288. $temp['date'] = $timedate->to_display_date_time($a['date_sent']);
  2289. $temp['uid'] = $a['id'];
  2290. $temp['ieId'] = $a['mailbox_id'];
  2291. $temp['site_url'] = $sugar_config['site_url'];
  2292. $temp['seen'] = ($a['status'] == 'unread') ? 0 : 1;
  2293. $temp['type'] = $a['type'];
  2294. $temp['mbox'] = 'sugar::Emails';
  2295. $temp['hasAttach'] = $this->doesImportedEmailHaveAttachment($a['id']);
  2296. //To and from addresses may be stored in emails_text, if nothing is found, revert to
  2297. //regular email addresses.
  2298. $temp['to_addrs'] = preg_replace('/[\x00-\x08\x0B-\x1F]/', '', $a['to_addrs']);
  2299. $temp['from'] = preg_replace('/[\x00-\x08\x0B-\x1F]/', '', $a['from_addr']);
  2300. if( empty($temp['from']) || empty($temp['to_addrs']) )
  2301. {
  2302. //Retrieve email addresses seperatly.
  2303. $tmpEmail = new Email();
  2304. $tmpEmail->id = $a['id'];
  2305. $tmpEmail->retrieveEmailAddresses();
  2306. $temp['from'] = $tmpEmail->from_addr;
  2307. $temp['to_addrs'] = $tmpEmail->to_addrs;
  2308. }
  2309. $return[] = $temp;
  2310. }
  2311. $metadata = array();
  2312. $metadata['totalCount'] = $total_count;
  2313. $metadata['out'] = $return;
  2314. return $metadata;
  2315. }
  2316. /**
  2317. * Determine if an imported email has an attachment by examining the relationship to notes.
  2318. *
  2319. * @param string $id
  2320. * @return boolean
  2321. */
  2322. function doesImportedEmailHaveAttachment($id)
  2323. {
  2324. $hasAttachment = FALSE;
  2325. $query = "SELECT id FROM notes where parent_id='$id' AND parent_type='Emails' AND file_mime_type is not null AND deleted=0";
  2326. $rs = $this->db->limitQuery($query, 0, 1);
  2327. $row = $this->db->fetchByAssoc($rs);
  2328. if( !empty($row['id']) )
  2329. $hasAttachment = TRUE;
  2330. return (int) $hasAttachment;
  2331. }
  2332. /**
  2333. * Generate the query used for searching imported emails.
  2334. *
  2335. * @return String Query to be executed.
  2336. */
  2337. function _genereateSearchImportedEmailsQuery()
  2338. {
  2339. global $timedate;
  2340. $additionalWhereClause = $this->_generateSearchImportWhereClause();
  2341. $query = array();
  2342. $fullQuery = "";
  2343. $query['select'] = "emails.id , emails.mailbox_id, emails.name, emails.date_sent, emails.status, emails.type, emails.flagged, emails.reply_to_status,
  2344. emails_text.from_addr, emails_text.to_addrs FROM emails ";
  2345. $query['joins'] = " JOIN emails_text on emails.id = emails_text.email_id ";
  2346. //Handle from and to addr joins
  2347. if( !empty($_REQUEST['from_addr']) )
  2348. {
  2349. $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
  2350. AND er_from.address_type='from' AND ea_from.email_address='" . strtolower($_REQUEST['from_addr']) . "'";
  2351. }
  2352. if( !empty($_REQUEST['to_addrs']) )
  2353. {
  2354. $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
  2355. AND er_to.address_type='to' AND ea_to.email_address='" . strtolower($_REQUEST['to_addrs']) . "'";
  2356. }
  2357. $query['where'] = " WHERE (emails.type= 'inbound' OR emails.type='archived' OR emails.type='out') AND emails.deleted = 0 ";
  2358. if( !empty($additionalWhereClause) )
  2359. $query['where'] .= "AND $additionalWhereClause";
  2360. //If we are explicitly looking for attachments. Do not use a distinct query as the to_addr is defined
  2361. //as a text which equals clob in oracle and the distinct query can not be executed correctly.
  2362. $addDistinctKeyword = "";
  2363. if( !empty($_REQUEST['attachmentsSearch']) && $_REQUEST['attachmentsSearch'] == 1) //1 indicates yes
  2364. $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 )";
  2365. else if( !empty($_REQUEST['attachmentsSearch']) && $_REQUEST['attachmentsSearch'] == 2 )
  2366. $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 )";
  2367. $fullQuery = "SELECT " . $query['select'] . " " . $query['joins'] . " " . $query['where'];
  2368. return $fullQuery;
  2369. }
  2370. /**
  2371. * Generate the where clause for searching imported emails.
  2372. *
  2373. */
  2374. function _generateSearchImportWhereClause()
  2375. {
  2376. global $timedate;
  2377. //The clear button was removed so if a user removes the asisgned user name, do not process the id.
  2378. if( empty($_REQUEST['assigned_user_name']) && !empty($_REQUEST['assigned_user_id']) )
  2379. unset($_REQUEST['assigned_user_id']);
  2380. $availableSearchParam = array('name' => array('table_name' =>'emails'),
  2381. 'data_parent_id_search' => array('table_name' =>'emails','db_key' => 'parent_id','opp' => '='),
  2382. 'assigned_user_id' => array('table_name' => 'emails', 'opp' => '=') );
  2383. $additionalWhereClause = array();
  2384. foreach ($availableSearchParam as $key => $properties)
  2385. {
  2386. if( !empty($_REQUEST[$key]) )
  2387. {
  2388. $db_key = isset($properties['db_key']) ? $properties['db_key'] : $key;
  2389. $searchValue = $_REQUEST[$key];
  2390. $opp = isset($properties['opp']) ? $properties['opp'] : 'like';
  2391. if($opp == 'like')
  2392. $searchValue = $searchValue . "%";
  2393. $additionalWhereClause[] = "{$properties['table_name']}.$db_key $opp '$searchValue' ";
  2394. }
  2395. }
  2396. $isDateFromSearchSet = !empty($_REQUEST['searchDateFrom']);
  2397. $isdateToSearchSet = !empty($_REQUEST['searchDateTo']);
  2398. $bothDateRangesSet = $isDateFromSearchSet & $isdateToSearchSet;
  2399. //Hanlde date from and to seperately
  2400. if($bothDateRangesSet)
  2401. {
  2402. $dbFormatDateFrom = $timedate->to_db_date($_REQUEST['searchDateFrom'], false);
  2403. $dbFormatDateFrom = db_convert("'" . $dbFormatDateFrom . "'",'datetime');
  2404. $dbFormatDateTo = $timedate->to_db_date($_REQUEST['searchDateTo'], false);
  2405. $dbFormatDateTo = db_convert("'" . $dbFormatDateTo . "'",'datetime');
  2406. $additionalWhereClause[] = "( emails.date_sent >= $dbFormatDateFrom AND
  2407. emails.date_sent <= $dbFormatDateTo )";
  2408. }
  2409. elseif ($isdateToSearchSet)
  2410. {
  2411. $dbFormatDateTo = $timedate->to_db_date($_REQUEST['searchDateTo'], false);
  2412. $dbFormatDateTo = db_convert("'" . $dbFormatDateTo . "'",'datetime');
  2413. $additionalWhereClause[] = "emails.date_sent <= $dbFormatDateTo ";
  2414. }
  2415. elseif ($isDateFromSearchSet)
  2416. {
  2417. $dbFormatDateFrom = $timedate->to_db_date($_REQUEST['searchDateFrom'], false);
  2418. $dbFormatDateFrom = db_convert("'" . $dbFormatDateFrom . "'",'datetime');
  2419. $additionalWhereClause[] = "emails.date_sent >= $dbFormatDateFrom ";
  2420. }
  2421. $additionalWhereClause = implode(" AND ", $additionalWhereClause);
  2422. return $additionalWhereClause;
  2423. }
  2424. /**
  2425. * takes a long TO: string of emails and returns the first appended by an
  2426. * elipse
  2427. */
  2428. function trimLongTo($str) {
  2429. if(strpos($str, ',')) {
  2430. $exStr = explode(',', $str);
  2431. return $exStr[0].'...';
  2432. } elseif(strpos($str, ';')) {
  2433. $exStr = explode(';', $str);
  2434. return $exStr[0].'...';
  2435. } else {
  2436. return $str;
  2437. }
  2438. }
  2439. function get_summary_text() {
  2440. return $this->name;
  2441. }
  2442. function distributionForm($where) {
  2443. global $app_list_strings;
  2444. global $app_strings;
  2445. global $mod_strings;
  2446. global $theme;
  2447. global $current_user;
  2448. $distribution = get_select_options_with_id($app_list_strings['dom_email_distribution'], '');
  2449. $_SESSION['distribute_where'] = $where;
  2450. $out = '<form name="Distribute" id="Distribute">';
  2451. $out .= get_form_header($mod_strings['LBL_DIST_TITLE'], '', false);
  2452. $out .=<<<eoq
  2453. <script>
  2454. enableQS(true);
  2455. </script>
  2456. eoq;
  2457. $out .= '
  2458. <table cellpadding="0" cellspacing="0" width="100%" border="0">
  2459. <tr>
  2460. <td>
  2461. <script type="text/javascript">
  2462. function checkDeps(form) {
  2463. return;
  2464. }
  2465. function mySubmit() {
  2466. var assform = document.getElementById("Distribute");
  2467. var select = document.getElementById("userSelect");
  2468. var assign1 = assform.r1.checked;
  2469. var assign2 = assform.r2.checked;
  2470. var dist = assform.dm.value;
  2471. var assign = false;
  2472. var users = false;
  2473. var rules = false;
  2474. var warn1 = "'.$mod_strings['LBL_WARN_NO_USERS'].'";
  2475. var warn2 = "";
  2476. if(assign1 || assign2) {
  2477. assign = true;
  2478. }
  2479. for(i=0; i<select.options.length; i++) {
  2480. if(select.options[i].selected == true) {
  2481. users = true;
  2482. warn1 = "";
  2483. }
  2484. }
  2485. if(dist != "") {
  2486. rules = true;
  2487. } else {
  2488. warn2 = "'.$mod_strings['LBL_WARN_NO_DIST'].'";
  2489. }
  2490. if(assign && users && rules) {
  2491. if(document.getElementById("r1").checked) {
  2492. var mu = document.getElementById("MassUpdate");
  2493. var grabbed = "";
  2494. for(i=0; i<mu.elements.length; i++) {
  2495. if(mu.elements[i].type == "checkbox" && mu.elements[i].checked && mu.elements[i].name.value != "massall") {
  2496. if(grabbed != "") { grabbed += "::"; }
  2497. grabbed += mu.elements[i].value;
  2498. }
  2499. }
  2500. var formgrab = document.getElementById("grabbed");
  2501. formgrab.value = grabbed;
  2502. }
  2503. assform.submit();
  2504. } else {
  2505. alert("'.$mod_strings['LBL_ASSIGN_WARN'].'" + "\n" + warn1 + "\n" + warn2);
  2506. }
  2507. }
  2508. function submitDelete() {
  2509. if(document.getElementById("r1").checked) {
  2510. var mu = document.getElementById("MassUpdate");
  2511. var grabbed = "";
  2512. for(i=0; i<mu.elements.length; i++) {
  2513. if(mu.elements[i].type == "checkbox" && mu.elements[i].checked && mu.elements[i].name != "massall") {
  2514. if(grabbed != "") { grabbed += "::"; }
  2515. grabbed += mu.elements[i].value;
  2516. }
  2517. }
  2518. var formgrab = document.getElementById("grabbed");
  2519. formgrab.value = grabbed;
  2520. }
  2521. if(grabbed == "") {
  2522. alert("'.$mod_strings['LBL_MASS_DELETE_ERROR'].'");
  2523. } else {
  2524. document.getElementById("Distribute").submit();
  2525. }
  2526. }
  2527. </script>
  2528. <input type="hidden" name="module" value="Emails">
  2529. <input type="hidden" name="action" id="action">
  2530. <input type="hidden" name="grabbed" id="grabbed">
  2531. <table cellpadding="1" cellspacing="0" width="100%" border="0" class="edit view">
  2532. <tr height="20">
  2533. <td scope="col" scope="row" NOWRAP align="center">
  2534. &nbsp;'.$mod_strings['LBL_ASSIGN_SELECTED_RESULTS_TO'].'&nbsp;';
  2535. $out .= $this->userSelectTable();
  2536. $out .= '</td>
  2537. <td scope="col" scope="row" NOWRAP align="left">
  2538. &nbsp;'.$mod_strings['LBL_USING_RULES'].'&nbsp;
  2539. <select name="distribute_method" id="dm" onChange="checkDeps(this.form);">'.$distribution.'</select>
  2540. </td>';
  2541. $out .= '</td>
  2542. </tr>';
  2543. $out .= '<tr>
  2544. <td scope="col" width="50%" scope="row" NOWRAP align="right" colspan="2">
  2545. <input title="'.$mod_strings['LBL_BUTTON_DISTRIBUTE_TITLE'].'"
  2546. id="dist_button"
  2547. accessKey="'.$mod_strings['LBL_BUTTON_DISTRIBUTE_KEY'].'"
  2548. class="button" onClick="AjaxObject.detailView.handleAssignmentDialogAssignAction();"
  2549. type="button" name="button"
  2550. value=" '.$mod_strings['LBL_BUTTON_DISTRIBUTE'].' ">';
  2551. $out .= '</tr>
  2552. </table>
  2553. </td>
  2554. </tr>
  2555. </table>
  2556. </form>';
  2557. return $out;
  2558. }
  2559. function userSelectTable() {
  2560. global $theme;
  2561. global $mod_strings;
  2562. $colspan = 1;
  2563. $setTeamUserFunction = '';
  2564. // get users
  2565. $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");
  2566. $userTable = '<table cellpadding="0" cellspacing="0" border="0">';
  2567. $userTable .= '<tr><td colspan="2"><b>'.$mod_strings['LBL_USER_SELECT'].'</b></td></tr>';
  2568. $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>';
  2569. $userTable .= '<tr><td colspan="2"><select style="visibility:hidden;" name="users[]" id="userSelect" multiple size="12">';
  2570. while($a = $this->db->fetchByAssoc($r)) {
  2571. $userTable .= '<option value="'.$a['id'].'" id="'.$a['id'].'">'.$a['first_name'].' '.$a['last_name'].'</option>';
  2572. }
  2573. $userTable .= '</select></td></tr>';
  2574. $userTable .= '</table>';
  2575. $out = '<script type="text/javascript">';
  2576. $out .= $setTeamUserFunction;
  2577. $out .= '
  2578. function setCheckMark() {
  2579. var select = document.getElementById("userSelect");
  2580. for(i=0 ; i<select.options.length; i++) {
  2581. if(select.options[i].selected == true) {
  2582. document.getElementById("checkMark").style.display="";
  2583. return;
  2584. }
  2585. }
  2586. document.getElementById("checkMark").style.display="none";
  2587. return;
  2588. }
  2589. function showUserSelect() {
  2590. var targetTable = document.getElementById("user_select");
  2591. targetTable.style.visibility="visible";
  2592. var userSelectTable = document.getElementById("userSelect");
  2593. userSelectTable.style.visibility="visible";
  2594. return;
  2595. }
  2596. function hideUserSelect() {
  2597. var targetTable = document.getElementById("user_select");
  2598. targetTable.style.visibility="hidden";
  2599. var userSelectTable = document.getElementById("userSelect");
  2600. userSelectTable.style.visibility="hidden";
  2601. return;
  2602. }
  2603. function toggleAll(toggle) {
  2604. if(toggle.checked) {
  2605. var stat = true;
  2606. } else {
  2607. var stat = false;
  2608. }
  2609. var form = document.getElementById("userSelect");
  2610. for(i=0; i<form.options.length; i++) {
  2611. form.options[i].selected = stat;
  2612. }
  2613. }
  2614. </script>
  2615. <span id="showUsersDiv" style="position:relative;">
  2616. <a href="#" id="showUsers" onClick="javascript:showUserSelect();">
  2617. <img border="0" src="'.SugarThemeRegistry::current()->getImageURL('Users.gif').'"></a>&nbsp;
  2618. <a href="#" id="showUsers" onClick="javascript:showUserSelect();">
  2619. <span style="display:none;" id="checkMark"><img border="0" src="'.SugarThemeRegistry::current()->getImageURL('check_inline.gif').'"></span>
  2620. </a>
  2621. <div id="user_select" style="width:200px;position:absolute;left:2;top:2;visibility:hidden;z-index:1000;">
  2622. <table cellpadding="0" cellspacing="0" border="0" class="list view">
  2623. <tr height="20">
  2624. <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\';">
  2625. <a href="#" onClick="javascript:hideUserSelect();"><img border="0" src="'.SugarThemeRegistry::current()->getImageURL('close.gif').'"></a>
  2626. '.$mod_strings['LBL_USER_SELECT'].'
  2627. </td>
  2628. </tr>
  2629. <tr>';
  2630. //<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\';">
  2631. $out .= ' <td style="padding:5px" class="oddListRowS1" bgcolor="#fdfdfd" valign="top" align="left" style="left:0;top:0;">
  2632. '.$userTable.'
  2633. </td>
  2634. </tr>
  2635. </table></div>
  2636. </span>';
  2637. return $out;
  2638. }
  2639. function checkInbox($type) {
  2640. global $theme;
  2641. global $mod_strings;
  2642. $out = '<div><input title="'.$mod_strings['LBL_BUTTON_CHECK_TITLE'].'"
  2643. accessKey="'.$mod_strings['LBL_BUTTON_CHECK_KEY'].'"
  2644. class="button"
  2645. type="button" name="button"
  2646. onClick="window.location=\'index.php?module=Emails&action=Check&type='.$type.'\';"
  2647. style="margin-bottom:2px"
  2648. value=" '.$mod_strings['LBL_BUTTON_CHECK'].' "></div>';
  2649. return $out;
  2650. }
  2651. /**
  2652. * Guesses Primary Parent id from From: email address. Cascades guesses from Accounts to Contacts to Leads to
  2653. * Users. This will not affect the many-to-many relationships already constructed as this is, at best,
  2654. * informational linking.
  2655. */
  2656. function fillPrimaryParentFields() {
  2657. if(empty($this->from_addr))
  2658. return;
  2659. $GLOBALS['log']->debug("*** Email trying to guess Primary Parent from address [ {$this->from_addr} ]");
  2660. $tables = array('accounts');
  2661. $ret = array();
  2662. // loop through types to get hits
  2663. foreach($tables as $table) {
  2664. $q = "SELECT name, id FROM {$table} WHERE email1 = '{$this->from_addr}' OR email2 = '{$this->from_addr}' AND deleted = 0";
  2665. $r = $this->db->query($q);
  2666. while($a = $this->db->fetchByAssoc($r)) {
  2667. if(!empty($a['name']) && !empty($a['id'])) {
  2668. $this->parent_type = ucwords($table);
  2669. $this->parent_id = $a['id'];
  2670. $this->parent_name = $a['name'];
  2671. return;
  2672. }
  2673. }
  2674. }
  2675. }
  2676. } // end class def