3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
31 * @copyright CiviCRM LLC (c) 2004-2014
35 class CRM_Contact_BAO_Contact_Utils
{
38 * given a contact type, get the contact image
40 * @param string $contactType contact type
41 * @param boolean $urlOnly if we need to return only image url
42 * @param int $contactId contact id
43 * @param boolean $addProfileOverlay if profile overlay class should be added
49 static function getImage($contactType, $urlOnly = FALSE, $contactId = NULL, $addProfileOverlay = TRUE) {
50 static $imageInfo = array();
52 $contactType = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, trim($contactType, CRM_Core_DAO
::VALUE_SEPARATOR
));
53 $contactType = $contactType[0];
55 if (!array_key_exists($contactType, $imageInfo)) {
56 $imageInfo[$contactType] = array();
59 $params = array('name' => $contactType);
60 CRM_Contact_BAO_ContactType
::retrieve($params, $typeInfo);
62 if (!empty($typeInfo['image_URL'])) {
63 $imageUrl = $typeInfo['image_URL'];
64 $config = CRM_Core_Config
::singleton();
66 if (!preg_match("/^(\/|(http(s)?:)).+$/i", $imageUrl)) {
67 $imageUrl = $config->resourceBase
. $imageUrl;
69 $imageInfo[$contactType]['image'] = "<div class=\"icon crm-icon {$typeInfo['name']}-icon\" style=\"background: url('{$imageUrl}')\" title=\"{$contactType}\"></div>";
70 $imageInfo[$contactType]['url'] = $imageUrl;
73 $isSubtype = (array_key_exists('parent_id', $typeInfo) &&
74 $typeInfo['parent_id']
78 $type = CRM_Contact_BAO_ContactType
::getBasicType($typeInfo['name']) . '-subtype';
81 $type = CRM_Utils_Array
::value('name', $typeInfo);
84 // do not add title since it hides contact name
85 if ($addProfileOverlay) {
86 $imageInfo[$contactType]['image'] = "<div class=\"icon crm-icon {$type}-icon\"></div>";
89 $imageInfo[$contactType]['image'] = "<div class=\"icon crm-icon {$type}-icon\" title=\"{$contactType}\"></div>";
91 $imageInfo[$contactType]['url'] = NULL;
95 if ($addProfileOverlay) {
96 static $summaryOverlayProfileId = NULL;
97 if (!$summaryOverlayProfileId) {
98 $summaryOverlayProfileId = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_UFGroup', 'summary_overlay', 'id', 'name');
101 $profileURL = CRM_Utils_System
::url('civicrm/profile/view',
102 "reset=1&gid={$summaryOverlayProfileId}&id={$contactId}&snippet=4"
105 $imageInfo[$contactType]['summary-link'] = '<a href="' . $profileURL . '" class="crm-summary-link">' . $imageInfo[$contactType]['image'] . '</a>';
108 $imageInfo[$contactType]['summary-link'] = $imageInfo[$contactType]['image'];
111 return $urlOnly ?
$imageInfo[$contactType]['url'] : $imageInfo[$contactType]['summary-link'];
115 * function check for mix contact ids(individual+household etc...)
117 * @param array $contactIds array of contact ids
119 * @return boolen true or false true if mix contact array else fale
124 public static function checkContactType(&$contactIds) {
125 if (empty($contactIds)) {
129 $idString = implode(',', $contactIds);
131 SELECT count( DISTINCT contact_type )
133 WHERE id IN ( $idString )
135 $count = CRM_Core_DAO
::singleValueQuery($query,
136 CRM_Core_DAO
::$_nullArray
138 return $count > 1 ?
TRUE : FALSE;
142 * Generate a checksum for a contactID
144 * @param int $contactID
145 * @param int $ts timestamp that checksum was generated
146 * @param int $live life of this checksum in hours/ 'inf' for infinite
147 * @param string $hash contact hash, if sent, prevents a query in inner loop
149 * @return array ( $cs, $ts, $live )
153 static function generateChecksum($contactID, $ts = NULL, $live = NULL, $hash = NULL) {
154 // return a warning message if we dont get a contactID
155 // this typically happens when we do a message preview
156 // or an anon mailing view - CRM-8298
158 return 'invalidChecksum';
162 $hash = CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_Contact',
168 $hash = md5(uniqid(rand(), TRUE));
169 CRM_Core_DAO
::setFieldValue('CRM_Contact_DAO_Contact',
180 $days = CRM_Core_BAO_Setting
::getItem(CRM_Core_BAO_Setting
::SYSTEM_PREFERENCES_NAME
,
188 $cs = md5("{$hash}_{$contactID}_{$ts}_{$live}");
189 return "{$cs}_{$ts}_{$live}";
193 * Make sure the checksum is valid for the passed in contactID
195 * @param int $contactID
196 * @param string $inputCheck checksum to match against
198 * @return boolean true if valid, else false
202 static function validChecksum($contactID, $inputCheck) {
204 $input = CRM_Utils_System
::explode('_', $inputCheck, 3);
206 $inputCS = CRM_Utils_Array
::value(0, $input);
207 $inputTS = CRM_Utils_Array
::value(1, $input);
208 $inputLF = CRM_Utils_Array
::value(2, $input);
210 $check = self
::generateChecksum($contactID, $inputTS, $inputLF);
212 if ($check != $inputCheck) {
216 // no life limit for checksum
217 if ($inputLF == 'inf') {
221 // checksum matches so now check timestamp
223 return ($inputTS +
($inputLF * 60 * 60) >= $now);
227 * Function to get the count of contact loctions
229 * @param int $contactId contact id
231 * @return int $locationCount max locations for the contact
235 static function maxLocations($contactId) {
236 $contactLocations = array();
238 // find number of location blocks for this contact and adjust value accordinly
239 // get location type from email
241 ( SELECT location_type_id FROM civicrm_email WHERE contact_id = {$contactId} )
243 ( SELECT location_type_id FROM civicrm_phone WHERE contact_id = {$contactId} )
245 ( SELECT location_type_id FROM civicrm_im WHERE contact_id = {$contactId} )
247 ( SELECT location_type_id FROM civicrm_address WHERE contact_id = {$contactId} )
249 $dao = CRM_Core_DAO
::executeQuery($query, CRM_Core_DAO
::$_nullArray);
254 * Create Current employer relationship for a individual
256 * @param int $contactID contact id of the individual
257 * @param string $organization it can be name or id of organization
262 static function createCurrentEmployerRelationship($contactID, $organizationId, $previousEmployerID = NULL) {
263 if ($organizationId && is_numeric($organizationId)) {
264 $cid = array('contact' => $contactID);
266 // get the relationship type id of "Employee of"
267 $relTypeId = CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_RelationshipType', 'Employee of', 'id', 'name_a_b');
269 CRM_Core_Error
::fatal(ts("You seem to have deleted the relationship type 'Employee of'"));
272 // create employee of relationship
273 $relationshipParams = array(
275 'relationship_type_id' => $relTypeId . '_a_b',
276 'contact_check' => array($organizationId => TRUE),
278 list($valid, $invalid, $duplicate,
279 $saved, $relationshipIds
280 ) = CRM_Contact_BAO_Relationship
::create($relationshipParams, $cid);
283 // In case we change employer, clean prveovious employer related records.
284 if (!$previousEmployerID) {
285 $previousEmployerID = CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_Contact', $contactID, 'employer_id');
287 if ($previousEmployerID &&
288 $previousEmployerID != $organizationId
290 self
::clearCurrentEmployer($contactID, $previousEmployerID);
293 // set current employer
294 self
::setCurrentEmployer(array($contactID => $organizationId));
296 $relationshipParams['relationship_ids'] = $relationshipIds;
297 // handle related meberships. CRM-3792
298 self
::currentEmployerRelatedMembership($contactID, $organizationId, $relationshipParams, $duplicate, $previousEmployerID);
303 * create related memberships for current employer
305 * @param int $contactID contact id of the individual
306 * @param int $employerID contact id of the organization.
307 * @param array $relationshipParams relationship params.
308 * @param boolean $duplicate are we triggered existing relationship.
313 static function currentEmployerRelatedMembership($contactID, $employerID, $relationshipParams, $duplicate = FALSE, $previousEmpID = NULL) {
315 $action = CRM_Core_Action
::ADD
;
317 //we do not know that triggered relationship record is active.
319 $relationship = new CRM_Contact_DAO_Relationship();
320 $relationship->contact_id_a
= $contactID;
321 $relationship->contact_id_b
= $employerID;
322 $relationship->relationship_type_id
= $relationshipParams['relationship_type_id'];
323 if ($relationship->find(TRUE)) {
324 $action = CRM_Core_Action
::UPDATE
;
325 $ids['contact'] = $contactID;
326 $ids['contactTarget'] = $employerID;
327 $ids['relationship'] = $relationship->id
;
328 CRM_Contact_BAO_Relationship
::setIsActive($relationship->id
, TRUE);
330 $relationship->free();
333 //need to handle related meberships. CRM-3792
334 if ($previousEmpID != $employerID) {
335 CRM_Contact_BAO_Relationship
::relatedMemberships($contactID, $relationshipParams, $ids, $action);
340 * Function to set current employer id and organization name
342 * @param array $currentEmployerParams associated array of contact id and its employer id
345 static function setCurrentEmployer($currentEmployerParams) {
346 foreach ($currentEmployerParams as $contactId => $orgId) {
347 $query = "UPDATE civicrm_contact contact_a,civicrm_contact contact_b
348 SET contact_a.employer_id=contact_b.id, contact_a.organization_name=contact_b.organization_name
349 WHERE contact_a.id ={$contactId} AND contact_b.id={$orgId}; ";
351 //FIXME : currently civicrm mysql_query support only single statement
352 //execution, though mysql 5.0 support multiple statement execution.
353 $dao = CRM_Core_DAO
::executeQuery($query);
358 * Function to update cached current employer name
360 * @param int $organizationId current employer id
363 static function updateCurrentEmployer($organizationId) {
364 $query = "UPDATE civicrm_contact contact_a,civicrm_contact contact_b
365 SET contact_a.organization_name=contact_b.organization_name
366 WHERE contact_a.employer_id=contact_b.id AND contact_b.id={$organizationId}; ";
368 $dao = CRM_Core_DAO
::executeQuery($query);
372 * Function to clear cached current employer name
374 * @param int $contactId contact id ( mostly individual contact id)
375 * @param int $employerId contact id ( mostly organization contact id)
378 static function clearCurrentEmployer($contactId, $employerId = NULL) {
379 $query = "UPDATE civicrm_contact
380 SET organization_name=NULL, employer_id = NULL
381 WHERE id={$contactId}; ";
383 $dao = CRM_Core_DAO
::executeQuery($query);
385 // need to handle related meberships. CRM-3792
387 //1. disable corresponding relationship.
388 //2. delete related membership.
390 //get the relationship type id of "Employee of"
391 $relTypeId = CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_RelationshipType', 'Employee of', 'id', 'name_a_b');
393 CRM_Core_Error
::fatal(ts("You seem to have deleted the relationship type 'Employee of'"));
395 $relMembershipParams['relationship_type_id'] = $relTypeId . '_a_b';
396 $relMembershipParams['contact_check'][$employerId] = 1;
398 //get relationship id.
399 if (CRM_Contact_BAO_Relationship
::checkDuplicateRelationship($relMembershipParams, $contactId, $employerId)) {
400 $relationship = new CRM_Contact_DAO_Relationship();
401 $relationship->contact_id_a
= $contactId;
402 $relationship->contact_id_b
= $employerId;
403 $relationship->relationship_type_id
= $relTypeId;
405 if ($relationship->find(TRUE)) {
406 CRM_Contact_BAO_Relationship
::setIsActive($relationship->id
, FALSE);
407 CRM_Contact_BAO_Relationship
::relatedMemberships($contactId, $relMembershipParams,
409 ), CRM_Core_Action
::DELETE
412 $relationship->free();
418 * Function to build form for related contacts / on behalf of organization.
420 * @param $form object invoking Object
421 * @param $contactType string contact type
422 * @param $title string fieldset title
423 * @param $maxLocationBlocks int number of location blocks
428 static function buildOnBehalfForm(&$form,
429 $contactType = 'Individual',
432 $title = 'Contact Information',
433 $contactEditMode = FALSE,
434 $maxLocationBlocks = 1
436 if ($title == 'Contact Information') {
437 $title = ts('Contact Information');
440 $config = CRM_Core_Config
::singleton();
442 $form->assign('contact_type', $contactType);
443 $form->assign('fieldSetTitle', $title);
444 $form->assign('contactEditMode', $contactEditMode);
446 $attributes = CRM_Core_DAO
::getAttribute('CRM_Contact_DAO_Contact');
447 if ($form->_contactId
) {
448 $form->assign('orgId', $form->_contactId
);
451 switch ($contactType) {
453 $session = CRM_Core_Session
::singleton();
454 $contactID = $session->get('userID');
457 $employers = CRM_Contact_BAO_Relationship
::getPermissionedEmployer($contactID);
460 $locDataURL = CRM_Utils_System
::url('civicrm/ajax/permlocation', 'cid=', FALSE, NULL, FALSE);
461 $form->assign('locDataURL', $locDataURL);
463 if (!$contactEditMode && $contactID && (count($employers) >= 1)) {
465 $dataURL = CRM_Utils_System
::url('civicrm/ajax/employer',
469 $form->assign('employerDataURL', $dataURL);
471 $form->add('text', 'organization_id', ts('Select an existing related Organization OR Enter a new one'));
472 $form->add('hidden', 'onbehalfof_id', '', array('id' => 'onbehalfof_id'));
473 $orgOptions = array('0' => ts('Create new organization'),
474 '1' => ts('Select existing organization'),
476 $orgOptionExtra = array('onclick' => "showHideByValue('org_option','true','select_org','table-row','radio',true);showHideByValue('org_option','true','create_org','table-row','radio',false);");
477 $form->addRadio('org_option', ts('options'), $orgOptions, $orgOptionExtra);
478 $form->assign('relatedOrganizationFound', TRUE);
481 $form->add('text', 'organization_name', ts('Organization Name'), $attributes['organization_name'], TRUE);
485 $form->add('text', 'household_name', ts('Household Name'),
486 $attributes['household_name']
492 $form->addElement('select', 'prefix_id', ts('Prefix'),
493 array('' => ts('- prefix -')) + CRM_Core_PseudoConstant
::get('CRM_Contact_DAO_Contact', 'prefix_id')
495 $form->addElement('text', 'first_name', ts('First Name'),
496 $attributes['first_name']
498 $form->addElement('text', 'middle_name', ts('Middle Name'),
499 $attributes['middle_name']
501 $form->addElement('text', 'last_name', ts('Last Name'),
502 $attributes['last_name']
504 $form->addElement('select', 'suffix_id', ts('Suffix'),
505 array('' => ts('- suffix -')) + CRM_Core_PseudoConstant
::get('CRM_Contact_DAO_Contact', 'suffix_id')
509 $addressSequence = $config->addressSequence();
510 $form->assign('addressSequence', array_fill_keys($addressSequence, 1));
513 $form->addElement('text',
516 CRM_Core_DAO
::getAttribute('CRM_Core_DAO_Phone',
521 $form->addElement('text',
524 CRM_Core_DAO
::getAttribute('CRM_Core_DAO_Email',
528 //build the address block
529 CRM_Contact_Form_Edit_Address
::buildQuickForm($form);
531 // also fix the state country selector
532 CRM_Contact_Form_Edit_Address
::fixStateSelect($form,
533 'address[1][country_id]',
534 'address[1][state_province_id]',
535 "address[1][county_id]",
542 * Function to clear cache employer name and employer id
543 * of all employee when employer get deleted.
545 * @param int $employerId contact id of employer ( organization id )
548 static function clearAllEmployee($employerId) {
550 UPDATE civicrm_contact
551 SET organization_name=NULL, employer_id = NULL
552 WHERE employer_id={$employerId}; ";
554 $dao = CRM_Core_DAO
::executeQuery($query, CRM_Core_DAO
::$_nullArray);
558 * Given an array of contact ids this function will return array with links to view contact page
560 * @param array $contactIDs associated contact id's
561 * @param int $originalId associated with the contact which is edited
564 * @return array $contactViewLinks returns array with links to contact view
568 static function formatContactIDSToLinks($contactIDs, $addViewLink = TRUE, $addEditLink = TRUE, $originalId = NULL) {
569 $contactLinks = array();
570 if (!is_array($contactIDs) ||
empty($contactIDs)) {
571 return $contactLinks;
574 // does contact has sufficient permissions.
575 $permissions = array(
576 'view' => 'view all contacts',
577 'edit' => 'edit all contacts',
578 'merge' => 'merge duplicate contacts',
581 $permissionedContactIds = array();
582 foreach ($permissions as $task => $permission) {
584 if (CRM_Core_Permission
::check($permission)) {
585 foreach ($contactIDs as $contactId) {
586 $permissionedContactIds[$contactId][$task] = TRUE;
591 // check permission on acl basis.
592 if (in_array($task, array(
594 $aclPermission = CRM_Core_Permission
::VIEW
;
595 if ($task == 'edit') {
596 $aclPermission = CRM_Core_Permission
::EDIT
;
598 foreach ($contactIDs as $contactId) {
599 if (CRM_Contact_BAO_Contact_Permission
::allow($contactId, $aclPermission)) {
600 $permissionedContactIds[$contactId][$task] = TRUE;
606 // retrieve display names for all contacts
608 SELECT c.id, c.display_name, c.contact_type, ce.email
609 FROM civicrm_contact c
610 LEFT JOIN civicrm_email ce ON ( ce.contact_id=c.id AND ce.is_primary = 1 )
611 WHERE c.id IN (' . implode(',', $contactIDs) . ' ) LIMIT 20';
613 $dao = CRM_Core_DAO
::executeQuery($query);
615 $contactLinks['msg'] = NULL;
617 while ($dao->fetch()) {
619 $contactLinks['rows'][$i]['display_name'] = $dao->display_name
;
620 $contactLinks['rows'][$i]['primary_email'] = $dao->email
;
622 // get the permission for current contact id.
623 $hasPermissions = CRM_Utils_Array
::value($dao->id
, $permissionedContactIds);
624 if (!is_array($hasPermissions) ||
empty($hasPermissions)) {
629 // do check for view.
630 if (array_key_exists('view', $hasPermissions)) {
631 $contactLinks['rows'][$i]['view'] = '<a class="action-item" href="' . CRM_Utils_System
::url('civicrm/contact/view', 'reset=1&cid=' . $dao->id
) . '" target="_blank">' . ts('View') . '</a>';
632 if (!$contactLinks['msg']) {
633 $contactLinks['msg'] = 'view';
636 if (array_key_exists('edit', $hasPermissions)) {
637 $contactLinks['rows'][$i]['edit'] = '<a class="action-item" href="' . CRM_Utils_System
::url('civicrm/contact/add', 'reset=1&action=update&cid=' . $dao->id
) . '" target="_blank">' . ts('Edit') . '</a>';
638 if (!$contactLinks['msg'] ||
$contactLinks['msg'] != 'merge') {
639 $contactLinks['msg'] = 'edit';
642 if (!empty($originalId) && array_key_exists('merge', $hasPermissions)) {
643 $rgBao = new CRM_Dedupe_BAO_RuleGroup();
644 $rgBao->contact_type
= $dao->contact_type
;
645 $rgBao->used
= 'Supervised';
646 if ($rgBao->find(TRUE)) {
649 if ($rgid && isset($dao->id
)) {
650 //get an url to merge the contact
651 $contactLinks['rows'][$i]['merge'] = '<a class="action-item" href="' . CRM_Utils_System
::url('civicrm/contact/merge', "reset=1&cid=" . $originalId . '&oid=' . $dao->id
. '&action=update&rgid=' . $rgid) . '">' . ts('Merge') . '</a>';
652 $contactLinks['msg'] = 'merge';
659 return $contactLinks;
663 * This function retrieve component related contact information.
665 * @param array $componentIds array of component Ids.
666 * @param array $returnProperties array of return elements.
668 * @return $contactDetails array of contact info.
671 static function contactDetails($componentIds, $componentName, $returnProperties = array(
673 $contactDetails = array();
674 if (empty($componentIds) ||
675 !in_array($componentName, array('CiviContribute', 'CiviMember', 'CiviEvent', 'Activity'))
677 return $contactDetails;
680 if (empty($returnProperties)) {
681 $autocompleteContactSearch = CRM_Core_BAO_Setting
::valueOptions(CRM_Core_BAO_Setting
::SYSTEM_PREFERENCES_NAME
,
682 'contact_autocomplete_options'
684 $returnProperties = array_fill_keys(array_merge(array('sort_name'),
685 array_keys($autocompleteContactSearch)
690 if ($componentName == 'CiviContribute') {
691 $compTable = 'civicrm_contribution';
693 elseif ($componentName == 'CiviMember') {
694 $compTable = 'civicrm_membership';
696 elseif ($componentName == 'Activity') {
697 $compTable = 'civicrm_activity';
698 $activityContacts = CRM_Core_OptionGroup
::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name');
701 $compTable = 'civicrm_participant';
704 $select = $from = array();
705 foreach ($returnProperties as $property => $ignore) {
706 $value = (in_array($property, array(
707 'city', 'street_address'))) ?
'address' : $property;
710 if ($componentName == 'Activity') {
711 $sourceID = CRM_Utils_Array
::key('Activity Source', $activityContacts);
712 $select[] = "contact.$property as $property";
714 INNER JOIN civicrm_activity_contact acs ON (acs.activity_id = {$compTable}.id AND acs.record_type_id = {$sourceID})
715 INNER JOIN civicrm_contact contact ON ( contact.id = acs.contact_id )";
718 $select[] = "$property as $property";
719 $from[$value] = "INNER JOIN civicrm_contact contact ON ( contact.id = $compTable.contact_id )";
723 case 'target_sort_name':
724 $targetID = CRM_Utils_Array
::key('Activity Targets', $activityContacts);
725 $select[] = "contact_target.sort_name as $property";
727 INNER JOIN civicrm_activity_contact act ON (act.activity_id = {$compTable}.id AND act.record_type_id = {$targetID})
728 INNER JOIN civicrm_contact contact_target ON ( contact_target.id = act.contact_id )";
734 case 'street_address':
735 $select[] = "$property as $property";
736 // Grab target contact properties if this is for activity
737 if ($componentName == 'Activity') {
738 $from[$value] = "LEFT JOIN civicrm_{$value} {$value} ON ( contact_target.id = {$value}.contact_id AND {$value}.is_primary = 1 ) ";
741 $from[$value] = "LEFT JOIN civicrm_{$value} {$value} ON ( contact.id = {$value}.contact_id AND {$value}.is_primary = 1 ) ";
746 case 'state_province':
747 $select[] = "{$property}.name as $property";
748 if (!in_array('address', $from)) {
749 // Grab target contact properties if this is for activity
750 if ($componentName == 'Activity') {
751 $from['address'] = 'LEFT JOIN civicrm_address address ON ( contact_target.id = address.contact_id AND address.is_primary = 1) ';
754 $from['address'] = 'LEFT JOIN civicrm_address address ON ( contact.id = address.contact_id AND address.is_primary = 1) ';
757 $from[$value] = " LEFT JOIN civicrm_{$value} {$value} ON ( address.{$value}_id = {$value}.id ) ";
762 //finally retrieve contact details.
763 if (!empty($select) && !empty($from)) {
764 $fromClause = implode(' ', $from);
765 $selectClause = implode(', ', $select);
766 $whereClause = "{$compTable}.id IN (" . implode(',', $componentIds) . ')';
769 SELECT contact.id as contactId, $compTable.id as componentId, $selectClause
770 FROM $compTable as $compTable $fromClause
772 Group By componentId";
774 $contact = CRM_Core_DAO
::executeQuery($query);
775 while ($contact->fetch()) {
776 $contactDetails[$contact->componentId
]['contact_id'] = $contact->contactId
;
777 foreach ($returnProperties as $property => $ignore) {
778 $contactDetails[$contact->componentId
][$property] = $contact->$property;
784 return $contactDetails;
788 * Function handles shared contact address processing
789 * In this function we just modify submitted values so that new address created for the user
790 * has same address as shared contact address. We copy the address so that search etc will be
793 * @param array $address this is associated array which contains submitted form values
799 static function processSharedAddress(&$address) {
800 if (!is_array($address)) {
804 // Sharing contact address during create mode is pretty straight forward.
805 // In update mode we should check following:
806 // - We should check if user has uncheck shared contact address
807 // - If yes then unset the master_id or may be just delete the address that copied master
808 // Normal update process will automatically create new address with submitted values
810 // 1. loop through entire subnitted address array
811 $masterAddress = array();
812 $skipFields = array('is_primary', 'location_type_id', 'is_billing', 'master_id');
813 foreach ($address as & $values) {
814 // 2. check if master id exists, if not continue
815 if (empty($values['master_id']) ||
empty($values['use_shared_address'])) {
816 // we should unset master id when use uncheck share address for existing address
817 $values['master_id'] = 'null';
821 // 3. get the address details for master_id
822 $masterAddress = new CRM_Core_BAO_Address();
823 $masterAddress->id
= CRM_Utils_Array
::value('master_id', $values);
824 $masterAddress->find(TRUE);
826 // 4. modify submitted params and update it with shared contact address
827 // make sure you preserve specific form values like location type, is_primary_ is_billing, master_id
828 // CRM-10336: Also empty any fields from the existing address block if they don't exist in master (otherwise they will persist)
829 foreach ($values as $field => $submittedValue) {
830 if (!in_array($field, $skipFields)){
831 if (isset($masterAddress->$field)) {
832 $values[$field] = $masterAddress->$field;
834 $values[$field] = '';
842 * Function to get the list of contact name give address associated array
844 * @param array $addresses associated array of
846 * @return $contactNames associated array of contact names
849 static function getAddressShareContactNames(&$addresses) {
850 $contactNames = array();
851 // get the list of master id's for address
852 $masterAddressIds = array();
853 foreach ($addresses as $key => $addressValue) {
854 if (!empty($addressValue['master_id'])) {
855 $masterAddressIds[] = $addressValue['master_id'];
859 if (!empty($masterAddressIds)) {
860 $query = 'SELECT ca.id, cc.display_name, cc.id as cid, cc.is_deleted
861 FROM civicrm_contact cc
862 INNER JOIN civicrm_address ca ON cc.id = ca.contact_id
863 WHERE ca.id IN ( ' . implode(',', $masterAddressIds) . ')';
864 $dao = CRM_Core_DAO
::executeQuery($query);
866 while ($dao->fetch()) {
867 $contactViewUrl = CRM_Utils_System
::url('civicrm/contact/view', "reset=1&cid={$dao->cid}");
868 $contactNames[$dao->id
] = array(
869 'name' => "<a href='{$contactViewUrl}'>{$dao->display_name}</a>",
870 'is_deleted' => $dao->is_deleted
,
874 return $contactNames;
878 * Clear the contact cache so things are kosher. We started off being super aggressive with clearing
879 * caches, but are backing off from this with every release. Compromise between ease of coding versus
880 * performance versus being accurate at that very instant
882 * @param $contactID - the contactID that was edited / deleted
887 static function clearContactCaches($contactID = NULL) {
888 // clear acl cache if any.
889 CRM_ACL_BAO_Cache
::resetCache();
891 if (empty($contactID)) {
892 // also clear prev/next dedupe cache - if no contactID passed in
893 CRM_Core_BAO_PrevNextCache
::deleteItem();
896 // reset the group contact cache for this group
897 CRM_Contact_BAO_GroupContactCache
::remove();
900 public static function updateGreeting($params) {
901 $contactType = $params['ct'];
902 $greeting = $params['gt'];
903 $valueID = $id = CRM_Utils_Array
::value('id', $params);
904 $force = CRM_Utils_Array
::value('force', $params);
906 // if valueID is not passed use default value
908 $valueID = $id = self
::defaultGreeting($contactType, $greeting);
912 'contact_type' => $contactType,
913 'greeting_type' => $greeting,
916 $allGreetings = CRM_Core_PseudoConstant
::greeting($filter);
917 $originalGreetingString = $greetingString = CRM_Utils_Array
::value($valueID, $allGreetings);
918 if (!$greetingString) {
919 CRM_Core_Error
::fatal(ts('Incorrect greeting value id %1, or no default greeting for this contact type and greeting type.', array(1 => $valueID)));
922 // build return properties based on tokens
923 $greetingTokens = CRM_Utils_Token
::getTokens($greetingString);
924 $tokens = CRM_Utils_Array
::value('contact', $greetingTokens);
925 $greetingsReturnProperties = array();
926 if (is_array($tokens)) {
927 $greetingsReturnProperties = array_fill_keys(array_values($tokens), 1);
930 // Process ALL contacts only when force=1 or force=2 is passed. Else only contacts with NULL greeting or addressee value are updated.
931 $processAll = $processOnlyIdSet = FALSE;
935 elseif ($force == 2) {
936 $processOnlyIdSet = TRUE;
939 //FIXME : apiQuery should handle these clause.
940 $filterContactFldIds = $filterIds = array();
941 $idFldName = $displayFldName = NULL;
942 if (in_array($greeting, CRM_Contact_BAO_Contact
::$_greetingTypes)) {
943 $idFldName = $greeting . '_id';
944 $displayFldName = $greeting . '_display';
948 // if $force == 1 then update all contacts else only
949 // those with NULL greeting or addressee value CRM-9476
951 $sql = "SELECT DISTINCT id, $idFldName FROM civicrm_contact WHERE contact_type = %1 ";
955 SELECT DISTINCT id, $idFldName
957 WHERE contact_type = %1
958 AND ({$idFldName} IS NULL
959 OR ( {$idFldName} IS NOT NULL AND ({$displayFldName} IS NULL OR {$displayFldName} = '')) )";
962 $dao = CRM_Core_DAO
::executeQuery($sql, array(1 => array($contactType, 'String')));
963 while ($dao->fetch()) {
964 $filterContactFldIds[$dao->id
] = $dao->$idFldName;
966 if (!CRM_Utils_System
::isNull($dao->$idFldName)) {
967 $filterIds[$dao->id
] = $dao->$idFldName;
972 if (empty($filterContactFldIds)) {
973 $filterContactFldIds[] = 0;
976 // retrieve only required contact information
977 $extraParams[] = array('contact_type', '=', $contactType, 0, 0);
978 // we do token replacement in the replaceGreetingTokens hook
979 list($greetingDetails) = CRM_Utils_Token
::getTokenDetails(array_keys($filterContactFldIds),
980 $greetingsReturnProperties,
981 FALSE, FALSE, $extraParams
983 // perform token replacement and build update SQL
984 $contactIds = array();
985 $cacheFieldQuery = "UPDATE civicrm_contact SET {$greeting}_display = CASE id ";
986 foreach ($greetingDetails as $contactID => $contactDetails) {
988 !array_key_exists($contactID, $filterContactFldIds)
993 if ($processOnlyIdSet && !array_key_exists($contactID, $filterIds)) {
998 $greetingString = $originalGreetingString;
999 $contactIds[] = $contactID;
1002 if ($greetingBuffer = CRM_Utils_Array
::value($filterContactFldIds[$contactID], $allGreetings)) {
1003 $greetingString = $greetingBuffer;
1007 self
::processGreetingTemplate($greetingString, $contactDetails, $contactID, 'CRM_UpdateGreeting');
1008 $greetingString = CRM_Core_DAO
::escapeString($greetingString);
1009 $cacheFieldQuery .= " WHEN {$contactID} THEN '{$greetingString}' ";
1011 $allContactIds[] = $contactID;
1014 if (!empty($allContactIds)) {
1015 $cacheFieldQuery .= " ELSE {$greeting}_display
1017 if (!empty($contactIds)) {
1018 // need to update greeting _id field.
1019 // reset greeting _custom
1020 $resetCustomGreeting = '';
1021 if ($valueID != 4) {
1022 $resetCustomGreeting = ", {$greeting}_custom = NULL ";
1026 UPDATE civicrm_contact
1027 SET {$greeting}_id = {$valueID}
1028 {$resetCustomGreeting}
1029 WHERE id IN (" . implode(',', $contactIds) . ")";
1030 CRM_Core_DAO
::executeQuery($queryString);
1033 // now update cache field
1034 CRM_Core_DAO
::executeQuery($cacheFieldQuery);
1039 * Fetch the default greeting for a given contact type
1041 * @param string $contactType contact type
1042 * @param string $greetingType greeting type
1044 * @return int or null
1046 static function defaultGreeting($contactType, $greetingType) {
1047 $contactTypeFilters = array('Individual' => 1, 'Household' => 2, 'Organization' => 3);
1048 if (!isset($contactTypeFilters[$contactType])) {
1051 $filter = $contactTypeFilters[$contactType];
1053 $id = CRM_Core_OptionGroup
::values($greetingType, NULL, NULL, NULL,
1054 " AND is_default = 1 AND (filter = {$filter} OR filter = 0)",
1058 return current($id);
1063 * Process a greeting template string to produce the individualised greeting text.
1065 * This works just like message templates for mailings:
1066 * the template is processed with the token substitution mechanism,
1067 * to supply the individual contact data;
1068 * and it is also processed with Smarty,
1069 * to allow for conditionals etc. based on the contact data.
1071 * Note: We don't pass any variables to Smarty --
1072 * all variable data is inserted into the input string
1073 * by the token substitution mechanism,
1074 * before Smarty is invoked.
1076 * @param string $templateString the greeting template string with contact tokens + Smarty syntax
1081 static function processGreetingTemplate(&$templateString, $contactDetails, $contactID, $className) {
1082 CRM_Utils_Token
::replaceGreetingTokens($templateString, $contactDetails, $contactID, $className, TRUE);
1084 $smarty = CRM_Core_Smarty
::singleton();
1085 $templateString = $smarty->fetch("string:$templateString");