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 /*********************************************************************************
40 * Description: TODO: To be written.
41 * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc.
42 * All Rights Reserved.
43 * Contributor(s): ______________________________________..
44 ********************************************************************************/
46 require_once('include/SugarObjects/templates/person/Person.php');
59 require_once('include/SugarObjects/templates/person/Person.php');
60 // Contact is used to store customer information.
61 class Contact extends Person {
69 var $modified_user_id;
70 var $assigned_user_id;
73 var $modified_by_name;
96 var $primary_address_street;
97 var $primary_address_city;
98 var $primary_address_state;
99 var $primary_address_postalcode;
100 var $primary_address_country;
101 var $alt_address_street;
102 var $alt_address_city;
103 var $alt_address_state;
104 var $alt_address_postalcode;
105 var $alt_address_country;
109 var $contacts_users_id;
110 // These are for related fields
115 var $opportunity_role;
116 var $opportunity_rel_id;
126 var $assigned_user_name;
128 var $accept_status_id;
129 var $accept_status_name;
130 var $alt_address_street_2;
131 var $alt_address_street_3;
132 var $opportunity_role_id;
133 var $portal_password;
134 var $primary_address_street_2;
135 var $primary_address_street_3;
138 var $full_name; // l10n localized name
140 var $table_name = "contacts";
141 var $rel_account_table = "accounts_contacts";
142 //This is needed for upgrade. This table definition moved to Opportunity module.
143 var $rel_opportunity_table = "opportunities_contacts";
145 var $object_name = "Contact";
146 var $module_dir = 'Contacts';
148 var $new_schema = true;
149 var $importable = true;
151 // This is used to retrieve related fields from form posts.
152 var $additional_column_fields = Array('bug_id', 'assigned_user_name', 'account_name', 'account_id', 'opportunity_id', 'case_id', 'task_id', 'note_id', 'meeting_id', 'call_id', 'email_id'
155 var $relationship_fields = Array('account_id'=> 'accounts','bug_id' => 'bugs', 'call_id'=>'calls','case_id'=>'cases','email_id'=>'emails',
156 'meeting_id'=>'meetings','note_id'=>'notes','task_id'=>'tasks', 'opportunity_id'=>'opportunities', 'contacts_users_id' => 'user_sync'
166 function add_list_count_joins(&$query, $where)
169 if(stristr($where, "accounts.name"))
171 // add a join to the accounts table.
173 LEFT JOIN accounts_contacts
174 ON contacts.id=accounts_contacts.contact_id
176 ON accounts_contacts.account_id=accounts.id
179 $custom_join = $this->custom_fields->getJOIN();
181 $query .= $custom_join['join'];
187 function listviewACLHelper(){
188 $array_assign = parent::listviewACLHelper();
190 //MFH BUG 18281; JChi #15255
191 $is_owner = !empty($this->assigned_user_id) && $this->assigned_user_id == $GLOBALS['current_user']->id;
192 if(!ACLController::moduleSupportsACL('Accounts') || ACLController::checkAccess('Accounts', 'view', $is_owner)){
193 $array_assign['ACCOUNT'] = 'a';
195 $array_assign['ACCOUNT'] = 'span';
198 return $array_assign;
201 function create_new_list_query($order_by, $where,$filter=array(),$params=array(), $show_deleted = 0,$join_type='', $return_array = false,$parentbean=null, $singleSelect = false)
203 //if this is from "contact address popup" action, then process popup list query
204 if(isset($_REQUEST['action']) && $_REQUEST['action'] == 'ContactAddressPopup'){
205 return $this->address_popup_create_new_list_query($order_by, $where, $filter, $params, $show_deleted, $join_type, $return_array, $parentbean, $singleSelect);
208 //any other action goes to parent function in sugarbean
209 if(strpos($order_by,'sync_contact') !== false){
210 //we have found that the user is ordering by the sync_contact field, it would be troublesome to sort by this field
211 //and perhaps a performance issue, so just remove it
214 return parent::create_new_list_query($order_by, $where, $filter, $params, $show_deleted, $join_type, $return_array, $parentbean, $singleSelect);
222 function address_popup_create_new_list_query($order_by, $where,$filter=array(),$params=array(), $show_deleted = 0,$join_type='', $return_array = false,$parentbean=null, $singleSelect = false)
224 //if this is any action that is not the contact address popup, then go to parent function in sugarbean
225 if(isset($_REQUEST['action']) && $_REQUEST['action'] !== 'ContactAddressPopup'){
226 return parent::create_new_list_query($order_by, $where, $filter, $params, $show_deleted, $join_type, $return_array, $parentbean, $singleSelect);
229 $custom_join = $this->custom_fields->getJOIN();
230 // MFH - BUG #14208 creates alias name for select
231 $select_query = "SELECT ";
232 $select_query .= db_concat($this->table_name,array('first_name','last_name')) . " name, ";
235 accounts.name as account_name,
236 accounts.id as account_id,
237 accounts.assigned_user_id account_id_owner,
238 users.user_name as assigned_user_name ";
240 $select_query .= $custom_join['select'];
242 $ret_array['select'] = $select_query;
247 $from_query .= "LEFT JOIN users
248 ON contacts.assigned_user_id=users.id
249 LEFT JOIN accounts_contacts
250 ON contacts.id=accounts_contacts.contact_id and accounts_contacts.deleted = 0
252 ON accounts_contacts.account_id=accounts.id AND accounts.deleted=0 ";
253 $from_query .= "LEFT JOIN email_addr_bean_rel eabl ON eabl.bean_id = contacts.id AND eabl.bean_module = 'Contacts' and eabl.primary_address = 1 and eabl.deleted=0 ";
254 $from_query .= "LEFT JOIN email_addresses ea ON (ea.id = eabl.email_address_id) ";
256 $from_query .= $custom_join['join'];
258 $ret_array['from'] = $from_query;
259 $ret_array['from_min'] = 'from contacts';
262 if($show_deleted == 0){
263 $where_auto = " $this->table_name.deleted=0 ";
264 //$where_auto .= " AND accounts.deleted=0 ";
265 }else if($show_deleted == 1){
266 $where_auto = " $this->table_name.deleted=1 ";
271 $where_query = "where ($where) AND ".$where_auto;
273 $where_query = "where ".$where_auto;
277 $ret_array['where'] = $where_query;
279 if(!empty($order_by)){
280 $orderby_query = " ORDER BY ". $this->process_order_by($order_by, null);
282 $ret_array['order_by'] = $orderby_query ;
289 return $ret_array['select'] . $ret_array['from'] . $ret_array['where']. $ret_array['order_by'];
296 function create_export_query(&$order_by, &$where, $relate_link_join='')
298 $custom_join = $this->custom_fields->getJOIN(true, true,$where);
300 $custom_join['join'] .= $relate_link_join;
302 contacts.*,email_addresses.email_address email_address,
303 accounts.name as account_name,
304 users.user_name as assigned_user_name ";
306 $query .= $custom_join['select'];
308 $query .= " FROM contacts ";
309 $query .= "LEFT JOIN users
310 ON contacts.assigned_user_id=users.id ";
311 $query .= "LEFT JOIN accounts_contacts
312 ON ( contacts.id=accounts_contacts.contact_id and (accounts_contacts.deleted is null or accounts_contacts.deleted = 0))
314 ON accounts_contacts.account_id=accounts.id ";
316 //join email address table too.
317 $query .= ' LEFT JOIN email_addr_bean_rel on contacts.id = email_addr_bean_rel.bean_id and email_addr_bean_rel.bean_module=\'Contacts\' and email_addr_bean_rel.deleted=0 and email_addr_bean_rel.primary_address=1 ';
318 $query .= ' LEFT JOIN email_addresses on email_addresses.id = email_addr_bean_rel.email_address_id ' ;
321 $query .= $custom_join['join'];
324 $where_auto = "( accounts.deleted IS NULL OR accounts.deleted=0 )
325 AND contacts.deleted=0 ";
328 $query .= "where ($where) AND ".$where_auto;
330 $query .= "where ".$where_auto;
332 if(!empty($order_by))
333 $query .= " ORDER BY ". $this->process_order_by($order_by, null);
338 function fill_in_additional_list_fields() {
339 parent::fill_in_additional_list_fields();
340 $this->_create_proper_name_field();
341 // cn: bug 8586 - l10n names for Contacts in Email TO: field
342 $this->email_and_name1 = "{$this->full_name} <".$this->email1.">";
343 $this->email_and_name2 = "{$this->full_name} <".$this->email2.">";
345 if($this->force_load_details == true) {
346 $this->fill_in_additional_detail_fields();
350 function fill_in_additional_detail_fields() {
351 parent::fill_in_additional_detail_fields();
352 if(empty($this->id)) return;
354 global $locale, $app_list_strings, $current_user;
356 // retrieve the account information and the information about the person the contact reports to.
357 $query = "SELECT acc.id, acc.name, con_reports_to.first_name, con_reports_to.last_name
359 left join accounts_contacts a_c on a_c.contact_id = '".$this->id."' and a_c.deleted=0
360 left join accounts acc on a_c.account_id = acc.id and acc.deleted=0
361 left join contacts con_reports_to on con_reports_to.id = contacts.reports_to_id
362 where contacts.id = '".$this->id."'";
363 // Bug 43196 - If a contact is related to multiple accounts, make sure we pull the one we are looking for
364 if ( !empty($this->account_id) ) {
365 $query .= " and acc.id = '{$this->account_id}'";
368 $result = $this->db->query($query,true," Error filling in additional detail fields: ");
370 // Get the id and the name.
371 $row = $this->db->fetchByAssoc($result);
375 $this->account_name = $row['name'];
376 $this->account_id = $row['id'];
377 $this->report_to_name = $locale->getLocaleFormattedName($row['first_name'], $row['last_name'],'','','',null,true);
381 $this->account_name = '';
382 $this->account_id = '';
383 $this->report_to_name = '';
385 $this->load_contacts_users_relationship();
386 /** concating this here because newly created Contacts do not have a
387 * 'name' attribute constructed to pass onto related items, such as Tasks
390 $this->name = $locale->getLocaleFormattedName($this->first_name, $this->last_name);
391 if(!empty($this->contacts_users_id)) {
392 $this->sync_contact = true;
395 if(!empty($this->portal_active) && $this->portal_active == 1) {
396 $this->portal_active = true;
398 // Set campaign name if there is a campaign id
399 if( !empty($this->campaign_id)){
401 $camp = new Campaign();
402 $where = "campaigns.id='{$this->campaign_id}'";
403 $campaign_list = $camp->get_full_list("campaigns.name", $where, true);
404 $this->campaign_name = $campaign_list[0]->name;
409 loads the contacts_users relationship to populate a checkbox
410 where a user can select if they would like to sync a particular
413 function load_contacts_users_relationship(){
414 global $current_user;
416 $this->load_relationship("user_sync");
417 $query_array=$this->user_sync->getQuery(true);
419 $query_array['where'] .= " AND users.id = '$current_user->id'";
422 foreach ($query_array as $qstring) {
423 $query.=' '.$qstring;
426 $list = $this->build_related_list($query, new User());
428 //this should only return one possible value so set it
429 $this->contacts_users_id = $list[0]->id;
433 function get_list_view_data($filter_fields = array()) {
434 global $system_config;
435 global $current_user;
437 $this->_create_proper_name_field();
438 $temp_array = $this->get_list_view_array();
439 $temp_array['NAME'] = $this->name;
440 $temp_array['ENCODED_NAME'] = $this->name;
442 if(isset($system_config->settings['system_skypeout_on'])
443 && $system_config->settings['system_skypeout_on'] == 1)
445 if(!empty($temp_array['PHONE_WORK'])
446 && skype_formatted($temp_array['PHONE_WORK']))
448 $temp_array['PHONE_WORK'] = '<a href="callto://'
449 . $temp_array['PHONE_WORK']. '">'
450 . $temp_array['PHONE_WORK']. '</a>' ;
453 if($filter_fields && !empty($filter_fields['sync_contact'])){
454 $this->load_contacts_users_relationship();
455 $temp_array['SYNC_CONTACT'] = !empty($this->contacts_users_id) ? 1 : 0;
457 $temp_array['EMAIL1'] = $this->emailAddress->getPrimaryAddress($this);
458 $this->email1 = $temp_array['EMAIL1'];
459 $temp_array['EMAIL1_LINK'] = $current_user->getEmailLink('email1', $this, '', '', 'ListView');
460 $temp_array['EMAIL_AND_NAME1'] = "{$this->full_name} <".$temp_array['EMAIL1'].">";
465 builds a generic search based on the query string using or
466 do not include any $this-> because this is called on without having the class instantiated
468 function build_generic_where_clause ($the_query_string)
470 $where_clauses = Array();
471 $the_query_string = $this->db->quote($the_query_string);
473 array_push($where_clauses, "contacts.last_name like '$the_query_string%'");
474 array_push($where_clauses, "contacts.first_name like '$the_query_string%'");
475 array_push($where_clauses, "accounts.name like '$the_query_string%'");
476 array_push($where_clauses, "contacts.assistant like '$the_query_string%'");
477 array_push($where_clauses, "ea.email_address like '$the_query_string%'");
479 if (is_numeric($the_query_string))
481 array_push($where_clauses, "contacts.phone_home like '%$the_query_string%'");
482 array_push($where_clauses, "contacts.phone_mobile like '%$the_query_string%'");
483 array_push($where_clauses, "contacts.phone_work like '%$the_query_string%'");
484 array_push($where_clauses, "contacts.phone_other like '%$the_query_string%'");
485 array_push($where_clauses, "contacts.phone_fax like '%$the_query_string%'");
486 array_push($where_clauses, "contacts.assistant_phone like '%$the_query_string%'");
490 foreach($where_clauses as $clause)
492 if($the_where != "") $the_where .= " or ";
493 $the_where .= $clause;
500 function set_notification_body($xtpl, $contact)
504 $xtpl->assign("CONTACT_NAME", trim($locale->getLocaleFormattedName($contact->first_name, $contact->last_name)));
505 $xtpl->assign("CONTACT_DESCRIPTION", $contact->description);
510 function get_contact_id_by_email($email)
512 $email = trim($email);
514 //email is empty, no need to query, return null
518 $where_clause = "(email1='$email' OR email2='$email') AND deleted=0";
520 $query = "SELECT * FROM $this->table_name WHERE $where_clause";
521 $GLOBALS['log']->debug("Retrieve $this->object_name: ".$query);
522 //requireSingleResult has beeen deprecated.
523 //$result = $this->db->requireSingleResult($query, true, "Retrieving record $where_clause:");
524 $result = $this->db->limitQuery($query,0,1,true, "Retrieving record $where_clause:");
531 $row = $this->db->fetchByAssoc($result, -1, true);
536 function save_relationship_changes($is_update) {
538 //if account_id was replaced unlink the previous account_id.
539 //this rel_fields_before_value is populated by sugarbean during the retrieve call.
540 if (!empty($this->account_id) and !empty($this->rel_fields_before_value['account_id']) and
541 (trim($this->account_id) != trim($this->rel_fields_before_value['account_id']))) {
542 //unlink the old record.
543 $this->load_relationship('accounts');
544 $this->accounts->delete($this->id,$this->rel_fields_before_value['account_id']);
546 parent::save_relationship_changes($is_update);
549 function bean_implements($interface){
551 case 'ACL':return true;
555 function get_unlinked_email_query($type=array()) {
557 return get_unlinked_email_query($type, $this);
561 * used by import to add a list of users
563 * Parameter can be one of the following:
564 * - string 'all': add this contact for all users
565 * - comma deliminated lists of teams and/or users
567 * @param string $list_of_user
570 function process_sync_to_outlook(
576 // cache this object since we'll be reusing it a bunch
577 if ( !($focus_user instanceof User) ) {
579 $focus_user = new User();
583 if ( empty($list_of_users) ) {
586 if ( !isset($this->users) ) {
587 $this->load_relationship('user_sync');
590 if ( strtolower($list_of_users) == 'all' ) {
591 // add all non-deleted users
592 $sql = "SELECT id FROM users WHERE deleted=0 AND is_group=0 AND portal_only=0";
593 $result=$this->db->query($sql);
594 while ( $hash = $this->db->fetchByAssoc($result) ) {
595 $this->user_sync->add($hash['id']);
599 $theList = explode(",",$list_of_users);
600 foreach ($theList as $eachItem) {
601 if ( $focus_user->retrieve_user_id($eachItem)
602 || $focus_user->retrieve($eachItem)) {
603 // it is a user, add user
604 $this->user_sync->add($this->id);