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.
7 * This program is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU Affero General Public License version 3 as published by the
9 * Free Software Foundation with the addition of the following permission added
10 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
19 * You should have received a copy of the GNU Affero General Public License along with
20 * this program; if not, see http://www.gnu.org/licenses or write to the Free
21 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
27 * The interactive user interfaces in modified source and object code versions
28 * of this program must display Appropriate Legal Notices, as required under
29 * Section 5 of the GNU Affero General Public License version 3.
31 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32 * these Appropriate Legal Notices must retain the display of the "Powered by
33 * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34 * technical reasons, the Appropriate Legal Notices must display the words
35 * "Powered by SugarCRM".
36 ********************************************************************************/
38 /*********************************************************************************
41 * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc. All Rights
42 * Reserved. Contributor(s): ______________________________________..
43 *********************************************************************************/
46 require_once("include/JSON.php");
49 class SugarEmailAddress extends SugarBean {
50 var $table_name = 'email_addresses';
51 var $module_name = "EmailAddresses";
52 var $module_dir = 'EmailAddresses';
53 var $object_name = 'EmailAddress';
55 //bug 40068, According to rules in page 6 of http://www.apps.ietf.org/rfc/rfc3696.html#sec-3,
56 //allowed special characters ! # $ % & ' * + - / = ? ^ _ ` . { | } ~ in local part
57 // FG - Bug 44338 - Changed RegEx for optimizations
58 // Old Regex : var $regex = "/^(['\.\-\+&'#!\$\*=\?\^_`\{\}~\/\w]+)*@((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|\w+([\.-]?\w+)*(\.[\w-]{2,})+)\$/";
59 // Changes : 1) Removed character "'", since it appears twice
60 // 2) Added "?:" after every open parenthesis, since we don't need to catch groups
61 // 3) Removed the "*" just before "@", since it double the work of the previous "+", slowing down the evaluation
62 var $regex = "/^(?:['\.\-\+&#!\$\*=\?\^_`\{\}~\/\w]+)@(?:(?:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|\w+(?:[\.-]*\w+)*(?:\.[\w-]{2,})+)\$/";
63 var $disable_custom_fields = true;
66 var $addresses = array(); // array of emails
74 function SugarEmailAddress() {
76 $this->index = self::$count;
81 * Legacy email address handling. This is to allow support for SOAP or customizations
83 * @param string $module
85 function handleLegacySave($bean, $prefix = "") {
86 if(!isset($_REQUEST) || !isset($_REQUEST['useEmailWidget'])) {
87 if (empty($this->addresses) || !isset($_REQUEST['massupdate'])) {
88 $this->addresses = array();
89 $optOut = (isset($bean->email_opt_out) && $bean->email_opt_out == "1") ? true : false;
90 $invalid = (isset($bean->invalid_email) && $bean->invalid_email == "1") ? true : false;
93 for($i = 1; $i <= 10; $i++){
95 if(isset($bean->$email) && !empty($bean->$email)){
96 $opt_out_field = $email.'_opt_out';
97 $invalid_field = $email.'_invalid';
98 $field_optOut = (isset($bean->$opt_out_field)) ? $bean->$opt_out_field : $optOut;
99 $field_invalid = (isset($bean->$invalid_field)) ? $bean->$invalid_field : $invalid;
100 $this->addAddress($bean->$email, $isPrimary, false, $field_invalid, $field_optOut);
106 $this->populateAddresses($bean->id, $bean->module_dir, array(),'');
107 if(isset($_REQUEST) && isset($_REQUEST['useEmailWidget'])) {
108 $this->populateLegacyFields($bean);
113 * Fills standard email1 legacy fields
115 * @param string module
118 function handleLegacyRetrieve(&$bean) {
119 $module_dir = $this->getCorrectedModule($bean->module_dir);
120 $this->addresses = $this->getAddressesByGUID($bean->id, $module_dir);
121 $this->populateLegacyFields($bean);
122 // add email1 to fetched_row so it can be audited properly later (by DBHelper::getDataChanges)
123 if (isset($bean->email1) && !isset($bean->fetched_row['email1'])) {
124 $bean->fetched_row['email1'] = $bean->email1;
130 function populateLegacyFields(&$bean){
131 $primary_found = false;
132 $alternate_found = false;
133 $alternate2_found = false;
134 foreach($this->addresses as $k=>$address) {
135 if ($primary_found && $alternate_found)
137 if ($address['primary_address'] == 1 && !$primary_found) {
139 $primary_found = true;
140 } elseif (!$alternate_found) {
141 $alternate_index = $k;
142 $alternate_found = true;
143 } elseif (!$alternate2_found){
144 $alternate2_index = $k;
145 $alternate2_found = true;
149 if ($primary_found) {
150 $bean->email1 = $this->addresses[$primary_index]['email_address'];
151 $bean->email_opt_out = $this->addresses[$primary_index]['opt_out'];
152 $bean->invalid_email = $this->addresses[$primary_index]['invalid_email'];
153 if ($alternate_found) {
154 $bean->email2 = $this->addresses[$alternate_index]['email_address'];
156 } elseif ($alternate_found) {
157 // Use the first found alternate as email1.
158 $bean->email1 = $this->addresses[$alternate_index]['email_address'];
159 $bean->email_opt_out = $this->addresses[$alternate_index]['opt_out'];
160 $bean->invalid_email = $this->addresses[$alternate_index]['invalid_email'];
161 if ($alternate2_found) {
162 $bean->email2 = $this->addresses[$alternate2_index]['email_address'];
168 * Saves email addresses for a parent bean
169 * @param string $id Parent bean ID
170 * @param string $module Parent bean's module
171 * @param array $addresses Override of $_REQUEST vars, used to handle non-standard bean saves
172 * @param string $primary GUID of primary address
173 * @param string $replyTo GUID of reply-to address
174 * @param string $invalid GUID of invalid address
176 function save($id, $module, $new_addrs=array(), $primary='', $replyTo='', $invalid='', $optOut='', $in_workflow=false) {
177 if(empty($this->addresses) || $in_workflow){
178 $this->populateAddresses($id, $module, $new_addrs,$primary);
181 //find all email addresses..
182 $current_links=array();
183 // Need to correct this to handle the Employee/User split
184 $module = $this->getCorrectedModule($module);
185 $q2="select * from email_addr_bean_rel eabr WHERE eabr.bean_id = '{$id}' AND eabr.bean_module = '{$module}' and eabr.deleted=0";
186 $r2 = $this->db->query($q2);
187 while(($row2=$this->db->fetchByAssoc($r2)) != null ) {
188 $current_links[$row2['email_address_id']]=$row2;
191 if (!empty($this->addresses)) {
192 // insert new relationships and create email address record, if they don't exist
193 foreach($this->addresses as $address) {
194 if(!empty($address['email_address'])) {
195 $guid = create_guid();
196 $emailId = $this->AddUpdateEmailAddress($address['email_address'],$address['invalid_email'],$address['opt_out']);// this will save the email address if not found
198 //verify linkage and flags.
200 if (isset($current_links[$emailId])) {
201 if ($address['primary_address'] != $current_links[$emailId]['primary_address'] or $address['reply_to_address'] != $current_links[$emailId]['reply_to_address'] ) {
202 $upd_eabr="UPDATE email_addr_bean_rel SET primary_address='{$address['primary_address']}', reply_to_address='{$address['reply_to_address']}' WHERE id='{$current_links[$emailId]['id']}'";
205 unset($current_links[$emailId]);
207 $now = TimeDate::getInstance()->nowDb();
208 $upd_eabr = "INSERT INTO email_addr_bean_rel (id, email_address_id,bean_id, bean_module,primary_address,reply_to_address,date_created,date_modified,deleted) VALUES('{$guid}', '{$emailId}', '{$id}', '{$module}', {$address['primary_address']}, {$address['reply_to_address']}, '$now', '$now', 0)";
211 if (!empty($upd_eabr)) {
212 $r2 = $this->db->query($upd_eabr);
218 //delete link to dropped email address.
219 if (!empty($current_links)) {
222 foreach ($current_links as $eabr) {
224 $delete.=empty($delete) ? "'".$eabr['id'] . "' " : ",'" . $eabr['id'] . "'";
227 $eabr_unlink="update email_addr_bean_rel set deleted=1 where id in ({$delete})";
228 $this->db->query($eabr_unlink);
234 * returns the number of email addresses found for a specifed bean
236 * @param string $email Address to match
237 * @param object $bean Bean to query against
238 * @param string $addresstype Optional, pass a 1 to query against the primary address, 0 for the other addresses
239 * @return int Count of records found
241 function getCountEmailAddressByBean(
247 $emailCaps = strtoupper(trim($email));
248 if(empty($emailCaps))
252 FROM email_addr_bean_rel eabl JOIN email_addresses ea
253 ON (ea.id = eabl.email_address_id)
254 JOIN {$bean->table_name} bean
255 ON (eabl.bean_id = bean.id)
256 WHERE ea.email_address_caps = '{$emailCaps}'
257 and eabl.bean_module = '{$bean->module_dir}'
258 and eabl.primary_address = '{$addresstype}'
259 and eabl.deleted=0 ";
261 $r = $this->db->query($q);
263 // do it this way to make the count accurate in oracle
265 while ($this->db->fetchByAssoc($r)) ++$i;
271 * This function returns a contact or user ID if a matching email is found
272 * @param $email the email address to match
273 * @param $table which table to query
275 function getRelatedId($email, $module) {
276 $email = trim(strtoupper($email));
277 $module = ucfirst($module);
279 $q = "SELECT bean_id FROM email_addr_bean_rel eabr
280 JOIN email_addresses ea ON (eabr.email_address_id = ea.id)
281 WHERE bean_module = '{$module}' AND ea.email_address_caps = '{$email}' AND eabr.deleted=0";
283 $r = $this->db->query($q, true);
286 while($a = $this->db->fetchByAssoc($r)) {
287 $retArr[] = $a['bean_id'];
289 if(count($retArr) > 0) {
297 * returns a collection of beans matching the email address
298 * @param string $email Address to match
301 function getBeansByEmailAddress($email) {
307 $email = trim($email);
313 $emailCaps = strtoupper($email);
314 $q = "SELECT * FROM email_addr_bean_rel eabl JOIN email_addresses ea ON (ea.id = eabl.email_address_id)
315 WHERE ea.email_address_caps = '{$emailCaps}' and eabl.deleted=0 ";
316 $r = $this->db->query($q);
318 while($a = $this->db->fetchByAssoc($r)) {
319 if(isset($beanList[$a['bean_module']]) && !empty($beanList[$a['bean_module']])) {
320 $className = $beanList[$a['bean_module']];
322 if(isset($beanFiles[$className]) && !empty($beanFiles[$className])) {
323 if(!class_exists($className)) {
324 require_once($beanFiles[$className]);
327 $bean = new $className();
328 $bean->retrieve($a['bean_id']);
332 $GLOBALS['log']->fatal("SUGAREMAILADDRESS: could not find valid class file for [ {$className} ]");
335 $GLOBALS['log']->fatal("SUGAREMAILADDRESS: could not find valid class [ {$a['bean_module']} ]");
343 * Saves email addresses for a parent bean
344 * @param string $id Parent bean ID
345 * @param string $module Parent bean's module
346 * @param array $addresses Override of $_REQUEST vars, used to handle non-standard bean saves
347 * @param string $primary GUID of primary address
348 * @param string $replyTo GUID of reply-to address
349 * @param string $invalid GUID of invalid address
351 function populateAddresses($id, $module, $new_addrs=array(), $primary='', $replyTo='', $invalid='', $optOut='') {
352 $module = $this->getCorrectedModule($module);
353 //One last check for the ConvertLead action in which case we need to change $module to 'Leads'
354 $module = (isset($_REQUEST) && isset($_REQUEST['action']) && $_REQUEST['action'] == 'ConvertLead') ? 'Leads' : $module;
356 $post_from_email_address_widget = (isset($_REQUEST) && isset($_REQUEST['emailAddressWidget'])) ? true : false;
357 $primaryValue = $primary;
359 $hasEmailValue = false;
361 if (isset($_REQUEST) && isset($_REQUEST[$module .'_email_widget_id'])) {
363 $fromRequest = false;
364 // determine which array to process
365 foreach($_REQUEST as $k => $v) {
366 if(strpos($k, 'emailAddress') !== false) {
371 $widget_id = $_REQUEST[$module .'_email_widget_id'];
374 //Iterate over the widgets for this module, in case there are multiple email widgets for this module
375 while(isset($_REQUEST[$module . $widget_id . "emailAddress" . $widgetCount]))
377 if (empty($_REQUEST[$module . $widget_id . "emailAddress" . $widgetCount])) {
382 $hasEmailValue = true;
384 $eId = $module . $widget_id;
385 if(isset($_REQUEST[$eId . 'emailAddressPrimaryFlag'])) {
386 $primaryValue = $_REQUEST[$eId . 'emailAddressPrimaryFlag'];
387 } else if(isset($_REQUEST[$module . 'emailAddressPrimaryFlag'])) {
388 $primaryValue = $_REQUEST[$module . 'emailAddressPrimaryFlag'];
391 $optOutValues = array();
392 if(isset($_REQUEST[$eId .'emailAddressOptOutFlag'])) {
393 $optOutValues = $_REQUEST[$eId .'emailAddressOptOutFlag'];
394 } else if(isset($_REQUEST[$module . 'emailAddressOptOutFlag'])) {
395 $optOutValues = $_REQUEST[$module . 'emailAddressOptOutFlag'];
398 $invalidValues = array();
399 if(isset($_REQUEST[$eId .'emailAddressInvalidFlag'])) {
400 $invalidValues = $_REQUEST[$eId .'emailAddressInvalidFlag'];
401 } else if(isset($_REQUEST[$module . 'emailAddressInvalidFlag'])) {
402 $invalidValues = $_REQUEST[$module . 'emailAddressInvalidFlag'];
405 $deleteValues = array();
406 if(isset($_REQUEST[$eId .'emailAddressDeleteFlag'])) {
407 $deleteValues = $_REQUEST[$eId .'emailAddressDeleteFlag'];
408 } else if(isset($_REQUEST[$module . 'emailAddressDeleteFlag'])) {
409 $deleteValues = $_REQUEST[$module . 'emailAddressDeleteFlag'];
412 // prep from form save
413 $primaryField = $primary;
417 if($fromRequest && empty($primary) && isset($primaryValue)) {
418 $primaryField = $primaryValue;
421 if($fromRequest && empty($replyTo)) {
422 if(isset($_REQUEST[$eId .'emailAddressReplyToFlag'])) {
423 $replyToField = $_REQUEST[$eId .'emailAddressReplyToFlag'];
424 } else if(isset($_REQUEST[$module . 'emailAddressReplyToFlag'])) {
425 $replyToField = $_REQUEST[$module . 'emailAddressReplyToFlag'];
428 if($fromRequest && empty($new_addrs)) {
429 foreach($_REQUEST as $k => $v) {
430 if(preg_match('/'.$eId.'emailAddress[0-9]+$/i', $k) && !empty($v)) {
436 if($fromRequest && empty($new_addrs)) {
437 foreach($_REQUEST as $k => $v) {
438 if(preg_match('/'.$eId.'emailAddressVerifiedValue[0-9]+$/i', $k) && !empty($v)) {
439 $validateFlag = str_replace("Value", "Flag", $k);
440 if (isset($_REQUEST[$validateFlag]) && $_REQUEST[$validateFlag] == "true")
446 //empty the addresses array if the post happened from email address widget.
447 if($post_from_email_address_widget) {
448 $this->addresses=array(); //this gets populated during retrieve of the contact bean.
450 $optOutValues = array();
451 $invalidValues = array();
452 foreach($new_addrs as $k=>$email) {
453 preg_match('/emailAddress([0-9])+$/', $k, $matches);
454 $count = $matches[1];
455 $result = $this->db->query("SELECT opt_out, invalid_email from email_addresses where email_address_caps = '" . strtoupper($email) . "'");
456 if(!empty($result)) {
457 $row=$this->db->fetchByAssoc($result);
458 if(!empty($row['opt_out'])) {
459 $optOutValues[$k] = "emailAddress$count";
461 if(!empty($row['invalid_email'])) {
462 $invalidValues[$k] = "emailAddress$count";
467 // Re-populate the addresses class variable if we have new address(es).
468 if (!empty($new_addrs)) {
469 foreach($new_addrs as $k => $reqVar) {
470 //$key = preg_match("/^$eId/s", $k) ? substr($k, strlen($eId)) : $k;
471 $reqVar = trim($reqVar);
472 if(strpos($k, 'emailAddress') !== false) {
473 if(!empty($reqVar) && !in_array($k, $deleteValues)) {
474 $primary = ($k == $primaryValue) ? true : false;
475 $replyTo = ($k == $replyToField) ? true : false;
476 $invalid = (in_array($k, $invalidValues)) ? true : false;
477 $optOut = (in_array($k, $optOutValues)) ? true : false;
478 $this->addAddress(trim($new_addrs[$k]), $primary, $replyTo, $invalid, $optOut);
485 }//End of Widget for loop
488 //If no widgets, set addresses array to empty
489 if($post_from_email_address_widget && !$hasEmailValue) {
490 $this->addresses = array();
495 * Preps internal array structure for email addresses
496 * @param string $addr Email address
497 * @param bool $primary Default false
498 * @param bool $replyTo Default false
500 function addAddress($addr, $primary=false, $replyTo=false, $invalid=false, $optOut=false) {
501 $addr = html_entity_decode($addr, ENT_QUOTES);
502 if(preg_match($this->regex, $addr)) {
503 $primaryFlag = ($primary) ? '1' : '0';
504 $replyToFlag = ($replyTo) ? '1' : '0';
505 $invalidFlag = ($invalid) ? '1' : '0';
506 $optOutFlag = ($optOut) ? '1' : '0';
510 // If we have such address already, remove it and add new one in.
511 foreach ($this->addresses as $k=>$address) {
512 if ($address['email_address'] == $addr) {
513 unset($this->addresses[$k]);
514 } elseif ($primary && $address['primary_address'] == '1') {
515 // We should only have one primary. If we are adding a primary but
516 // we find an existing primary, reset this one's primary flag.
517 $address['primary_address'] = '0';
521 $this->addresses[] = array(
522 'email_address' => $addr,
523 'primary_address' => $primaryFlag,
524 'reply_to_address' => $replyToFlag,
525 'invalid_email' => $invalidFlag,
526 'opt_out' => $optOutFlag,
529 $GLOBALS['log']->fatal("SUGAREMAILADDRESS: address did not validate [ {$addr} ]");
534 * Updates invalid_email and opt_out flags for each address
536 function updateFlags() {
537 if(!empty($this->addresses)) {
538 foreach($this->addresses as $addressMeta) {
539 if(isset($addressMeta['email_address']) && !empty($addressMeta['email_address'])) {
540 $address = $this->_cleanAddress($addressMeta['email_address']);
542 $q = "SELECT * FROM email_addresses WHERE email_address = '{$address}'";
543 $r = $this->db->query($q);
544 $a = $this->db->fetchByAssoc($r);
547 if(isset($a['invalid_email']) && isset($addressMeta['invalid_email']) && isset($addressMeta['opt_out']) && $a['invalid_email'] != $addressMeta['invalid_email'] || $a['opt_out'] != $addressMeta['opt_out']) {
548 $qUpdate = "UPDATE email_addresses SET invalid_email = {$addressMeta['invalid_email']}, opt_out = {$addressMeta['opt_out']}, date_modified = '".TimeDate::getInstance()->nowDb()."' WHERE id = '{$a['id']}'";
549 $rUpdate = $this->db->query($qUpdate);
557 public function splitEmailAddress($addr)
559 $email = $this->_cleanAddress($addr);
560 if(!preg_match($this->regex, $email)) {
561 $email = ''; // remove bad email addr
563 $name = trim(str_replace(array($email, '<', '>', '"', "'"), '', $addr));
564 return array("name" => $name, "email" => strtolower($email));
569 * Normalizes an RFC-clean email address, returns a string that is the email address only
570 * @param string $addr Dirty email address
571 * @return string clean email address
573 function _cleanAddress($addr) {
574 $addr = trim(from_html($addr));
576 if(strpos($addr, "<") !== false && strpos($addr, ">") !== false) {
577 $address = trim(substr($addr, strrpos($addr, "<") +1, strrpos($addr, ">") - strrpos($addr, "<") -1));
579 $address = trim($addr);
586 * preps a passed email address for email address storage
587 * @param array $addr Address in focus, must be RFC compliant
588 * @return string $id email_addresses ID
590 function getEmailGUID($addr) {
591 $address = $this->_cleanAddress($addr);
592 $addressCaps = strtoupper($address);
594 $q = "SELECT id FROM email_addresses WHERE email_address_caps = '{$addressCaps}'";
595 $r = $this->db->query($q);
596 $a = $this->db->fetchByAssoc($r);
598 if(!empty($a) && !empty($a['id'])) {
602 if(!empty($address)){
603 $guid = create_guid();
604 $address = $GLOBALS['db']->quote($address);
605 $addressCaps = $GLOBALS['db']->quote($addressCaps);
606 $now = TimeDate::getInstance()->nowDb();
607 $qa = "INSERT INTO email_addresses (id, email_address, email_address_caps, date_created, date_modified, deleted)
608 VALUES('{$guid}', '{$address}', '{$addressCaps}', '$now', '$now', 0)";
609 $ra = $this->db->query($qa);
615 function AddUpdateEmailAddress($addr,$invalid=0,$opt_out=0) {
617 $address = $this->_cleanAddress($addr);
618 $addressCaps = strtoupper($this->db->quoteForEmail($address));
620 $q = "SELECT * FROM email_addresses WHERE email_address_caps = '{$addressCaps}' and deleted=0";
621 $r = $this->db->query($q);
622 $a = $this->db->fetchByAssoc($r);
623 if(!empty($a) && !empty($a['id'])) {
624 //verify the opt out and invalid flags.
625 //bug# 39378- did not allow change of case of an email address
626 if ($a['invalid_email'] != $invalid or $a['opt_out'] != $opt_out or strcasecmp(trim($a['email_address']), trim($address))==0) {
627 $upd_q="update email_addresses set email_address='{$address}', invalid_email={$invalid}, opt_out={$opt_out},date_modified = '".gmdate($GLOBALS['timedate']->get_db_date_time_format())."' where id='{$a['id']}'";
628 $upd_r= $this->db->query($upd_q);
633 if(!empty($address)){
634 $guid = create_guid();
635 $address = $GLOBALS['db']->quote($address);
636 $addressCaps = $GLOBALS['db']->quote($addressCaps);
637 $now = TimeDate::getInstance()->nowDb();
638 $qa = "INSERT INTO email_addresses (id, email_address, email_address_caps, date_created, date_modified, deleted, invalid_email, opt_out)
639 VALUES('{$guid}', '{$address}', '{$addressCaps}', '$now', '$now', 0 , $invalid, $opt_out)";
640 $this->db->query($qa);
647 * Returns Primary or newest email address
648 * @param object $focus Object in focus
649 * @return string email
651 function getPrimaryAddress($focus,$parent_id=null,$parent_type=null) {
653 $parent_type=empty($parent_type) ? $focus->module_dir : $parent_type;
654 $parent_id=empty($parent_id) ? $focus->id : $parent_id;
656 $q = "SELECT ea.email_address FROM email_addresses ea
657 LEFT JOIN email_addr_bean_rel ear ON ea.id = ear.email_address_id
658 WHERE ear.bean_module = '{$parent_type}'
659 AND ear.bean_id = '{$parent_id}'
661 AND ea.invalid_email = 0
662 ORDER BY ear.primary_address DESC";
663 $r = $this->db->limitQuery($q, 0, 1);
664 $a = $this->db->fetchByAssoc($r);
666 if(isset($a['email_address'])) {
667 return $a['email_address'];
672 function getReplyToAddress($focus) {
673 $q = "SELECT ea.email_address FROM email_addresses ea
674 LEFT JOIN email_addr_bean_rel ear ON ea.id = ear.email_address_id
675 WHERE ear.bean_module = '{$focus->module_dir}'
676 AND ear.bean_id = '{$focus->id}'
678 AND ea.invalid_email = 0
679 ORDER BY ear.reply_to_address DESC";
680 $r = $this->db->query($q);
681 $a = $this->db->fetchByAssoc($r);
683 if(isset($a['email_address'])) {
684 return $a['email_address'];
690 * Returns all email addresses by parent's GUID
691 * @param string $id Parent's GUID
692 * @param string $module Parent's module
695 function getAddressesByGUID($id, $module) {
697 $module = $this->getCorrectedModule($module);
699 $q = "SELECT ea.email_address, ea.email_address_caps, ea.invalid_email, ea.opt_out, ea.date_created, ea.date_modified,
700 ear.id, ear.email_address_id, ear.bean_id, ear.bean_module, ear.primary_address, ear.reply_to_address, ear.deleted
701 FROM email_addresses ea LEFT JOIN email_addr_bean_rel ear ON ea.id = ear.email_address_id
702 WHERE ear.bean_module = '{$module}'
703 AND ear.bean_id = '{$id}'
705 ORDER BY ear.reply_to_address, ear.primary_address DESC";
706 $r = $this->db->query($q);
708 while($a = $this->db->fetchByAssoc($r)) {
716 * Returns the HTML/JS for the EmailAddress widget
717 * @param string $parent_id ID of parent bean, generally $focus
718 * @param string $module $focus' module
719 * @param bool asMetadata Default false
720 * @return string HTML/JS for widget
722 function getEmailAddressWidgetEditView($id, $module, $asMetadata=false, $tpl='',$tabindex='')
724 if ( !($this->smarty instanceOf Sugar_Smarty ) )
725 $this->smarty = new Sugar_Smarty();
727 global $app_strings, $dictionary, $beanList;
731 $prefillData = 'new Object()';
732 $passedModule = $module;
733 $module = $this->getCorrectedModule($module);
734 $saveModule = $module;
735 if(isset($_POST['is_converted']) && $_POST['is_converted']==true){
736 $id=$_POST['return_id'];
737 $module=$_POST['return_module'];
739 $prefillDataArr = array();
741 $prefillDataArr = $this->getAddressesByGUID($id, $module);
742 //When coming from convert leads, sometimes module is Contacts while the id is for a lead.
743 if (empty($prefillDataArr) && $module == "Contacts")
744 $prefillDataArr = $this->getAddressesByGUID($id, "Leads");
745 } else if(isset($_REQUEST['full_form']) && !empty($_REQUEST['emailAddressWidget'])){
746 $widget_id = isset($_REQUEST[$module . '_email_widget_id']) ? $_REQUEST[$module . '_email_widget_id'] : '0';
748 $key = $module . $widget_id . 'emailAddress'.$count;
749 while(isset($_REQUEST[$key])) {
750 $email = $_REQUEST[$key];
751 $prefillDataArr[] = array('email_address'=>$email,
752 'primary_address'=>isset($_REQUEST['emailAddressPrimaryFlag']) && $_REQUEST['emailAddressPrimaryFlag'] == $key,
753 'invalid_email'=>isset($_REQUEST['emailAddressInvalidFlag']) && in_array($key, $_REQUEST['emailAddressInvalidFlag']),
754 'opt_out'=>isset($_REQUEST['emailAddressOptOutFlag']) && in_array($key, $_REQUEST['emailAddressOptOutFlag']),
755 'reply_to_address'=>false
757 $key = $module . $widget_id . 'emailAddress' . ++$count;
761 if(!empty($prefillDataArr)) {
762 $json = new JSON(JSON_LOOSE_TYPE);
763 $prefillData = $json->encode($prefillDataArr);
764 $prefill = !empty($prefillDataArr) ? 'true' : 'false';
768 $vardefs = $dictionary[$beanList[$passedModule]]['fields'];
769 if (!empty($vardefs['email1']) && isset($vardefs['email1']['required']) && $vardefs['email1']['required'])
771 $this->smarty->assign('required', $required);
773 $this->smarty->assign('module', $saveModule);
774 $this->smarty->assign('index', $this->index);
775 $this->smarty->assign('app_strings', $app_strings);
776 $this->smarty->assign('prefillEmailAddresses', $prefill);
777 $this->smarty->assign('prefillData', $prefillData);
778 $this->smarty->assign('tabindex', $tabindex);
779 //Set addDefaultAddress flag (do not add if it's from the Email module)
780 $this->smarty->assign('addDefaultAddress', (isset($_REQUEST['module']) && $_REQUEST['module'] == 'Emails') ? 'false' : 'true');
783 if ($this->view == "QuickCreate")
784 $form = 'form_'.$this->view .'_'.$module;
785 $this->smarty->assign('emailView', $form);
787 if($module == 'Users') {
788 $this->smarty->assign('useReplyTo', true);
790 $this->smarty->assign('useOptOut', true);
791 $this->smarty->assign('useInvalid', true);
794 $template = empty($tpl) ? "include/SugarEmailAddress/templates/forEditView.tpl" : $tpl;
795 $newEmail = $this->smarty->fetch($template);
801 $ret['prefillData'] = $prefillDataArr;
802 $ret['html'] = $newEmail;
812 * Returns the HTML/JS for the EmailAddress widget
813 * @param object $focus Bean in focus
814 * @return string HTML/JS for widget
816 function getEmailAddressWidgetDetailView($focus, $tpl='')
818 if ( !($this->smarty instanceOf Sugar_Smarty ) )
819 $this->smarty = new Sugar_Smarty();
822 global $current_user;
824 if(empty($focus->id))return '';
825 $prefillData = $this->getAddressesByGUID($focus->id, $focus->module_dir);
827 foreach($prefillData as $addressItem) {
828 $key = ($addressItem['primary_address'] == 1) ? 'primary' : "";
829 $key = ($addressItem['reply_to_address'] == 1) ? 'reply_to' : $key;
830 $key = ($addressItem['opt_out'] == 1) ? 'opt_out' : $key;
831 $key = ($addressItem['invalid_email'] == 1) ? 'invalid' : $key;
832 $key = ($addressItem['opt_out'] == 1) && ($addressItem['invalid_email'] == 1) ? 'opt_out_invalid' : $key;
834 $assign[] = array('key' => $key, 'address' => $current_user->getEmailLink2($addressItem['email_address'], $focus).$addressItem['email_address']."</a>");
838 $this->smarty->assign('app_strings', $app_strings);
839 $this->smarty->assign('emailAddresses', $assign);
840 $templateFile = empty($tpl) ? "include/SugarEmailAddress/templates/forDetailView.tpl" : $tpl;
841 $return = $this->smarty->fetch($templateFile);
847 * getEmailAddressWidgetDuplicatesView($focus)
848 * @param object $focus Bean in focus
849 * @return string HTML that contains hidden input values based off of HTML request
851 function getEmailAddressWidgetDuplicatesView($focus)
853 if ( !($this->smarty instanceOf Sugar_Smarty ) )
854 $this->smarty = new Sugar_Smarty();
861 $mod = isset($focus) ? $focus->module_dir : "";
863 $widget_id = $_POST[$mod .'_email_widget_id'];
864 $this->smarty->assign('email_widget_id',$widget_id);
865 $this->smarty->assign('emailAddressWidget',$_POST['emailAddressWidget']);
867 if(isset($_POST[$mod . $widget_id . 'emailAddressPrimaryFlag'])) {
868 $primary = $_POST[$mod . $widget_id . 'emailAddressPrimaryFlag'];
871 while(isset($_POST[$mod . $widget_id . "emailAddress" . $count])) {
872 $emails[] = $_POST[$mod . $widget_id . 'emailAddress' . $count];
880 if(isset($_POST[$mod . $widget_id . 'emailAddressOptOutFlag'])) {
881 foreach($_POST[$mod . $widget_id . 'emailAddressOptOutFlag'] as $v) {
886 if(isset($_POST[$mod . $widget_id . 'emailAddressInvalidFlag'])) {
887 foreach($_POST[$mod . $widget_id . 'emailAddressInvalidFlag'] as $v) {
892 if(isset($_POST[$mod . $widget_id . 'emailAddressReplyToFlag'])) {
893 foreach($_POST[$mod . $widget_id . 'emailAddressReplyToFlag'] as $v) {
898 if(isset($_POST[$mod . $widget_id . 'emailAddressDeleteFlag'])) {
899 foreach($_POST[$mod . $widget_id . 'emailAddressDeleteFlag'] as $v) {
904 while(isset($_POST[$mod . $widget_id . "emailAddressVerifiedValue" . $count])) {
905 $verified[] = $_POST[$mod . $widget_id . 'emailAddressVerifiedValue' . $count];
909 $this->smarty->assign('emails', $emails);
910 $this->smarty->assign('primary', $primary);
911 $this->smarty->assign('optOut', $optOut);
912 $this->smarty->assign('invalid', $invalid);
913 $this->smarty->assign('replyTo', $invalid);
914 $this->smarty->assign('delete', $invalid);
915 $this->smarty->assign('verified', $invalid);
916 $this->smarty->assign('moduleDir', $mod);
918 return $this->smarty->fetch("include/SugarEmailAddress/templates/forDuplicatesView.tpl");
925 function getFormBaseURL($focus) {
928 $mod = isset($focus) ? $focus->module_dir : "";
930 $widget_id = $_POST[$mod .'_email_widget_id'];
931 $get .= '&' . $mod . '_email_widget_id='. $widget_id;
932 $get .= '&emailAddressWidget='.$_POST['emailAddressWidget'];
934 while(isset($_REQUEST[$mod . $widget_id . 'emailAddress' . $count])) {
935 $get .= "&" . $mod . $widget_id . "emailAddress" . $count . "=" . urlencode($_REQUEST[$mod . $widget_id . 'emailAddress' . $count]);
939 while(isset($_REQUEST[$mod . $widget_id . 'emailAddressVerifiedValue' . $count])) {
940 $get .= "&" . $mod . $widget_id . "emailAddressVerifiedValue" . $count . "=" . urlencode($_REQUEST[$mod . $widget_id . 'emailAddressVerifiedValue' . $count]);
944 $options = array('emailAddressPrimaryFlag', 'emailAddressOptOutFlag', 'emailAddressInvalidFlag', 'emailAddressDeleteFlag', 'emailAddressReplyToFlag');
946 foreach($options as $option) {
948 $optionIdentifier = $mod.$widget_id.$option;
949 if(isset($_REQUEST[$optionIdentifier])) {
950 if(is_array($_REQUEST[$optionIdentifier])) {
951 foreach($_REQUEST[$optionIdentifier] as $optOut) {
952 $get .= "&" . $optionIdentifier . "[" . $count . "]=" . $optOut;
956 $get .= "&" . $optionIdentifier . "=" . $_REQUEST[$optionIdentifier];
964 function setView($view) {
969 * This function is here so the Employees/Users division can be handled cleanly in one place
970 * @param object $focus SugarBean
971 * @return string The value for the bean_module column in the email_addr_bean_rel table
973 function getCorrectedModule(&$module) {
974 return ($module == "Employees")? "Users" : $module;
980 * Convenience function for MVC (Mystique)
981 * @param object $focus SugarBean
982 * @param string $field unused
983 * @param string $value unused
984 * @param string $view DetailView or EditView
987 function getEmailAddressWidget($focus, $field, $value, $view, $tabindex='') {
988 $sea = new SugarEmailAddress();
989 $sea->setView($view);
991 if($view == 'EditView' || $view == 'QuickCreate' || $view == 'ConvertLead') {
992 $module = $focus->module_dir;
993 if ($view == 'ConvertLead' && $module == "Contacts") $module = "Leads";
995 return $sea->getEmailAddressWidgetEditView($focus->id, $module, false,'',$tabindex);
998 return $sea->getEmailAddressWidgetDetailView($focus);