3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.6 |
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
42 * @param bool $urlOnly
43 * If we need to return only image url.
44 * @param int $contactId
46 * @param bool $addProfileOverlay
47 * If profile overlay class should be added.
51 public static function getImage($contactType, $urlOnly = FALSE, $contactId = NULL, $addProfileOverlay = TRUE) {
52 static $imageInfo = array();
54 $contactType = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, trim($contactType, CRM_Core_DAO
::VALUE_SEPARATOR
));
55 $contactType = $contactType[0];
57 if (!array_key_exists($contactType, $imageInfo)) {
58 $imageInfo[$contactType] = array();
61 $params = array('name' => $contactType);
62 CRM_Contact_BAO_ContactType
::retrieve($params, $typeInfo);
64 if (!empty($typeInfo['image_URL'])) {
65 $imageUrl = $typeInfo['image_URL'];
66 $config = CRM_Core_Config
::singleton();
68 if (!preg_match("/^(\/|(http(s)?:)).+$/i", $imageUrl)) {
69 $imageUrl = $config->resourceBase
. $imageUrl;
71 $imageInfo[$contactType]['image'] = "<div class=\"icon crm-icon {$typeInfo['name']}-icon\" style=\"background: url('{$imageUrl}')\" title=\"{$contactType}\"></div>";
72 $imageInfo[$contactType]['url'] = $imageUrl;
75 $isSubtype = (array_key_exists('parent_id', $typeInfo) &&
76 $typeInfo['parent_id']
80 $type = CRM_Contact_BAO_ContactType
::getBasicType($typeInfo['name']) . '-subtype';
83 $type = CRM_Utils_Array
::value('name', $typeInfo);
86 // do not add title since it hides contact name
87 if ($addProfileOverlay) {
88 $imageInfo[$contactType]['image'] = "<div class=\"icon crm-icon {$type}-icon\"></div>";
91 $imageInfo[$contactType]['image'] = "<div class=\"icon crm-icon {$type}-icon\" title=\"{$contactType}\"></div>";
93 $imageInfo[$contactType]['url'] = NULL;
97 if ($addProfileOverlay) {
98 static $summaryOverlayProfileId = NULL;
99 if (!$summaryOverlayProfileId) {
100 $summaryOverlayProfileId = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_UFGroup', 'summary_overlay', 'id', 'name');
103 $profileURL = CRM_Utils_System
::url('civicrm/profile/view',
104 "reset=1&gid={$summaryOverlayProfileId}&id={$contactId}&snippet=4"
107 $imageInfo[$contactType]['summary-link'] = '<a href="' . $profileURL . '" class="crm-summary-link">' . $imageInfo[$contactType]['image'] . '</a>';
110 $imageInfo[$contactType]['summary-link'] = $imageInfo[$contactType]['image'];
113 return $urlOnly ?
$imageInfo[$contactType]['url'] : $imageInfo[$contactType]['summary-link'];
117 * Function check for mix contact ids(individual+household etc...)
119 * @param array $contactIds
120 * Array of contact ids.
123 * true if mix contact array else false
126 public static function checkContactType(&$contactIds) {
127 if (empty($contactIds)) {
131 $idString = implode(',', $contactIds);
133 SELECT count( DISTINCT contact_type )
135 WHERE id IN ( $idString )
137 $count = CRM_Core_DAO
::singleValueQuery($query,
138 CRM_Core_DAO
::$_nullArray
140 return $count > 1 ?
TRUE : FALSE;
144 * Generate a checksum for a $entityId of type $entityType
146 * @param int $entityId
148 * Timestamp that checksum was generated.
150 * Life of this checksum in hours/ 'inf' for infinite.
151 * @param string $hash
152 * Contact hash, if sent, prevents a query in inner loop.
154 * @param string $entityType
155 * @param null $hashSize
158 * ( $cs, $ts, $live )
160 public static function generateChecksum($entityId, $ts = NULL, $live = NULL, $hash = NULL, $entityType = 'contact', $hashSize = NULL) {
161 // return a warning message if we dont get a entityId
162 // this typically happens when we do a message preview
163 // or an anon mailing view - CRM-8298
165 return 'invalidChecksum';
169 if ($entityType == 'contact') {
170 $hash = CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_Contact',
174 elseif ($entityType == 'mailing') {
175 $hash = CRM_Core_DAO
::getFieldValue('CRM_Mailing_DAO_Mailing',
182 $hash = md5(uniqid(rand(), TRUE));
184 $hash = substr($hash, 0, $hashSize);
187 if ($entityType == 'contact') {
188 CRM_Core_DAO
::setFieldValue('CRM_Contact_DAO_Contact',
193 elseif ($entityType == 'mailing') {
194 CRM_Core_DAO
::setFieldValue('CRM_Mailing_DAO_Mailing',
206 $days = CRM_Core_BAO_Setting
::getItem(CRM_Core_BAO_Setting
::SYSTEM_PREFERENCES_NAME
,
214 $cs = md5("{$hash}_{$entityId}_{$ts}_{$live}");
215 return "{$cs}_{$ts}_{$live}";
219 * Make sure the checksum is valid for the passed in contactID
221 * @param int $contactID
222 * @param string $inputCheck
223 * Checksum to match against.
226 * true if valid, else false
228 public static function validChecksum($contactID, $inputCheck) {
230 $input = CRM_Utils_System
::explode('_', $inputCheck, 3);
232 $inputCS = CRM_Utils_Array
::value(0, $input);
233 $inputTS = CRM_Utils_Array
::value(1, $input);
234 $inputLF = CRM_Utils_Array
::value(2, $input);
236 $check = self
::generateChecksum($contactID, $inputTS, $inputLF);
238 if ($check != $inputCheck) {
242 // no life limit for checksum
243 if ($inputLF == 'inf') {
247 // checksum matches so now check timestamp
249 return ($inputTS +
($inputLF * 60 * 60) >= $now);
253 * Get the count of contact loctions
255 * @param int $contactId
259 * max locations for the contact
261 public static function maxLocations($contactId) {
262 $contactLocations = array();
264 // find number of location blocks for this contact and adjust value accordinly
265 // get location type from email
267 ( SELECT location_type_id FROM civicrm_email WHERE contact_id = {$contactId} )
269 ( SELECT location_type_id FROM civicrm_phone WHERE contact_id = {$contactId} )
271 ( SELECT location_type_id FROM civicrm_im WHERE contact_id = {$contactId} )
273 ( SELECT location_type_id FROM civicrm_address WHERE contact_id = {$contactId} )
275 $dao = CRM_Core_DAO
::executeQuery($query, CRM_Core_DAO
::$_nullArray);
280 * Create Current employer relationship for a individual
282 * @param int $contactID
283 * Contact id of the individual.
284 * @param $organization
286 * @param int $previousEmployerID
287 * @param bool $newContact
290 public static function createCurrentEmployerRelationship($contactID, $organization, $previousEmployerID = NULL, $newContact = FALSE) {
291 //if organization name is passed. CRM-15368,CRM-15547
292 if ($organization && !is_numeric($organization)) {
293 $organizationParams['organization_name'] = $organization;
294 $dedupeParams = CRM_Dedupe_Finder
::formatParams($organizationParams, 'Organization');
296 $dedupeParams['check_permission'] = FALSE;
297 $dupeIDs = CRM_Dedupe_Finder
::dupesByParams($dedupeParams, 'Organization', 'Unsupervised');
299 if (is_array($dupeIDs) && !empty($dupeIDs)) {
300 // we should create relationship only w/ first org CRM-4193
301 foreach ($dupeIDs as $orgId) {
302 $organization = $orgId;
307 //create new organization
309 'contact_type' => 'Organization',
310 'organization_name' => $organization,
312 $org = CRM_Contact_BAO_Contact
::create($newOrg);
313 $organization = $org->id
;
317 if ($organization && is_numeric($organization)) {
318 $cid = array('contact' => $contactID);
320 // get the relationship type id of "Employee of"
321 $relTypeId = CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_RelationshipType', 'Employee of', 'id', 'name_a_b');
323 CRM_Core_Error
::fatal(ts("You seem to have deleted the relationship type 'Employee of'"));
326 // create employee of relationship
327 $relationshipParams = array(
329 'relationship_type_id' => $relTypeId . '_a_b',
330 'contact_check' => array($organization => TRUE),
332 list($valid, $invalid, $duplicate,
333 $saved, $relationshipIds
334 ) = CRM_Contact_BAO_Relationship
::createMultiple($relationshipParams, $cid);
337 // In case we change employer, clean previous employer related records.
338 if (!$previousEmployerID && !$newContact) {
339 $previousEmployerID = CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_Contact', $contactID, 'employer_id');
341 if ($previousEmployerID &&
342 $previousEmployerID != $organization
344 self
::clearCurrentEmployer($contactID, $previousEmployerID);
347 // set current employer
348 self
::setCurrentEmployer(array($contactID => $organization));
350 $relationshipParams['relationship_ids'] = $relationshipIds;
351 // handle related meberships. CRM-3792
352 self
::currentEmployerRelatedMembership($contactID, $organization, $relationshipParams, $duplicate, $previousEmployerID);
357 * Create related memberships for current employer
359 * @param int $contactID
360 * Contact id of the individual.
361 * @param int $employerID
362 * Contact id of the organization.
363 * @param array $relationshipParams
364 * Relationship params.
365 * @param bool $duplicate
366 * Are we triggered existing relationship.
368 * @param int $previousEmpID
370 * @throws CiviCRM_API3_Exception
372 public static function currentEmployerRelatedMembership($contactID, $employerID, $relationshipParams, $duplicate = FALSE, $previousEmpID = NULL) {
374 $action = CRM_Core_Action
::ADD
;
376 //we do not know that triggered relationship record is active.
378 $relationship = new CRM_Contact_DAO_Relationship();
379 $relationship->contact_id_a
= $contactID;
380 $relationship->contact_id_b
= $employerID;
381 $relationship->relationship_type_id
= $relationshipParams['relationship_type_id'];
382 if ($relationship->find(TRUE)) {
383 $action = CRM_Core_Action
::UPDATE
;
384 $ids['contact'] = $contactID;
385 $ids['contactTarget'] = $employerID;
386 $ids['relationship'] = $relationship->id
;
387 CRM_Contact_BAO_Relationship
::setIsActive($relationship->id
, TRUE);
389 $relationship->free();
392 //need to handle related meberships. CRM-3792
393 if ($previousEmpID != $employerID) {
394 CRM_Contact_BAO_Relationship
::relatedMemberships($contactID, $relationshipParams, $ids, $action);
399 * Set current employer id and organization name
401 * @param array $currentEmployerParams
402 * Associated array of contact id and its employer id.
404 public static function setCurrentEmployer($currentEmployerParams) {
405 foreach ($currentEmployerParams as $contactId => $orgId) {
406 $query = "UPDATE civicrm_contact contact_a,civicrm_contact contact_b
407 SET contact_a.employer_id=contact_b.id, contact_a.organization_name=contact_b.organization_name
408 WHERE contact_a.id ={$contactId} AND contact_b.id={$orgId}; ";
410 //FIXME : currently civicrm mysql_query support only single statement
411 //execution, though mysql 5.0 support multiple statement execution.
412 $dao = CRM_Core_DAO
::executeQuery($query);
417 * Update cached current employer name
419 * @param int $organizationId
420 * Current employer id.
422 public static function updateCurrentEmployer($organizationId) {
423 $query = "UPDATE civicrm_contact contact_a,civicrm_contact contact_b
424 SET contact_a.organization_name=contact_b.organization_name
425 WHERE contact_a.employer_id=contact_b.id AND contact_b.id={$organizationId}; ";
427 $dao = CRM_Core_DAO
::executeQuery($query);
431 * Clear cached current employer name
433 * @param int $contactId
434 * Contact id ( mostly individual contact id).
435 * @param int $employerId
436 * Contact id ( mostly organization contact id).
438 public static function clearCurrentEmployer($contactId, $employerId = NULL) {
439 $query = "UPDATE civicrm_contact
440 SET organization_name=NULL, employer_id = NULL
441 WHERE id={$contactId}; ";
443 $dao = CRM_Core_DAO
::executeQuery($query);
445 // need to handle related meberships. CRM-3792
447 //1. disable corresponding relationship.
448 //2. delete related membership.
450 //get the relationship type id of "Employee of"
451 $relTypeId = CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_RelationshipType', 'Employee of', 'id', 'name_a_b');
453 CRM_Core_Error
::fatal(ts("You seem to have deleted the relationship type 'Employee of'"));
455 $relMembershipParams['relationship_type_id'] = $relTypeId . '_a_b';
456 $relMembershipParams['contact_check'][$employerId] = 1;
458 //get relationship id.
459 if (CRM_Contact_BAO_Relationship
::checkDuplicateRelationship($relMembershipParams, $contactId, $employerId)) {
460 $relationship = new CRM_Contact_DAO_Relationship();
461 $relationship->contact_id_a
= $contactId;
462 $relationship->contact_id_b
= $employerId;
463 $relationship->relationship_type_id
= $relTypeId;
465 if ($relationship->find(TRUE)) {
466 CRM_Contact_BAO_Relationship
::setIsActive($relationship->id
, FALSE);
467 CRM_Contact_BAO_Relationship
::relatedMemberships($contactId, $relMembershipParams,
469 CRM_Core_Action
::DELETE
472 $relationship->free();
478 * Build form for related contacts / on behalf of organization.
480 * @param CRM_Core_Form $form
481 * @param string $contactType
483 * @param int $countryID
484 * @param int $stateID
485 * @param string $title
489 public static function buildOnBehalfForm(&$form, $contactType, $countryID, $stateID, $title) {
491 $config = CRM_Core_Config
::singleton();
493 $form->assign('contact_type', $contactType);
494 $form->assign('fieldSetTitle', $title);
495 $form->assign('contactEditMode', TRUE);
497 $attributes = CRM_Core_DAO
::getAttribute('CRM_Contact_DAO_Contact');
498 if ($form->_contactId
) {
499 $form->assign('orgId', $form->_contactId
);
502 switch ($contactType) {
504 $form->add('text', 'organization_name', ts('Organization Name'), $attributes['organization_name'], TRUE);
508 $form->add('text', 'household_name', ts('Household Name'), $attributes['household_name']);
513 $form->addElement('select', 'prefix_id', ts('Prefix'),
514 array('' => ts('- prefix -')) + CRM_Core_PseudoConstant
::get('CRM_Contact_DAO_Contact', 'prefix_id')
516 $form->addElement('text', 'first_name', ts('First Name'),
517 $attributes['first_name']
519 $form->addElement('text', 'middle_name', ts('Middle Name'),
520 $attributes['middle_name']
522 $form->addElement('text', 'last_name', ts('Last Name'),
523 $attributes['last_name']
525 $form->addElement('select', 'suffix_id', ts('Suffix'),
526 array('' => ts('- suffix -')) + CRM_Core_PseudoConstant
::get('CRM_Contact_DAO_Contact', 'suffix_id')
530 $addressSequence = $config->addressSequence();
531 $form->assign('addressSequence', array_fill_keys($addressSequence, 1));
534 $form->addElement('text',
537 CRM_Core_DAO
::getAttribute('CRM_Core_DAO_Phone',
542 $form->addElement('text',
545 CRM_Core_DAO
::getAttribute('CRM_Core_DAO_Email',
549 //build the address block
550 CRM_Contact_Form_Edit_Address
::buildQuickForm($form);
554 * Clear cache employer name and employer id
555 * of all employee when employer get deleted.
557 * @param int $employerId
558 * Contact id of employer ( organization id ).
560 public static function clearAllEmployee($employerId) {
562 UPDATE civicrm_contact
563 SET organization_name=NULL, employer_id = NULL
564 WHERE employer_id={$employerId}; ";
566 $dao = CRM_Core_DAO
::executeQuery($query, CRM_Core_DAO
::$_nullArray);
570 * Given an array of contact ids this function will return array with links to view contact page
572 * @param array $contactIDs
573 * Associated contact id's.
574 * @param bool $addViewLink
575 * @param bool $addEditLink
576 * @param int $originalId
577 * Associated with the contact which is edited.
581 * returns array with links to contact view
583 public static function formatContactIDSToLinks($contactIDs, $addViewLink = TRUE, $addEditLink = TRUE, $originalId = NULL) {
584 $contactLinks = array();
585 if (!is_array($contactIDs) ||
empty($contactIDs)) {
586 return $contactLinks;
589 // does contact has sufficient permissions.
590 $permissions = array(
591 'view' => 'view all contacts',
592 'edit' => 'edit all contacts',
593 'merge' => 'merge duplicate contacts',
596 $permissionedContactIds = array();
597 foreach ($permissions as $task => $permission) {
599 if (CRM_Core_Permission
::check($permission)) {
600 foreach ($contactIDs as $contactId) {
601 $permissionedContactIds[$contactId][$task] = TRUE;
606 // check permission on acl basis.
607 if (in_array($task, array(
611 $aclPermission = CRM_Core_Permission
::VIEW
;
612 if ($task == 'edit') {
613 $aclPermission = CRM_Core_Permission
::EDIT
;
615 foreach ($contactIDs as $contactId) {
616 if (CRM_Contact_BAO_Contact_Permission
::allow($contactId, $aclPermission)) {
617 $permissionedContactIds[$contactId][$task] = TRUE;
623 // retrieve display names for all contacts
625 SELECT c.id, c.display_name, c.contact_type, ce.email
626 FROM civicrm_contact c
627 LEFT JOIN civicrm_email ce ON ( ce.contact_id=c.id AND ce.is_primary = 1 )
628 WHERE c.id IN (' . implode(',', $contactIDs) . ' ) LIMIT 20';
630 $dao = CRM_Core_DAO
::executeQuery($query);
632 $contactLinks['msg'] = NULL;
634 while ($dao->fetch()) {
636 $contactLinks['rows'][$i]['display_name'] = $dao->display_name
;
637 $contactLinks['rows'][$i]['primary_email'] = $dao->email
;
639 // get the permission for current contact id.
640 $hasPermissions = CRM_Utils_Array
::value($dao->id
, $permissionedContactIds);
641 if (!is_array($hasPermissions) ||
empty($hasPermissions)) {
646 // do check for view.
647 if (array_key_exists('view', $hasPermissions)) {
648 $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>';
649 if (!$contactLinks['msg']) {
650 $contactLinks['msg'] = 'view';
653 if (array_key_exists('edit', $hasPermissions)) {
654 $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>';
655 if (!$contactLinks['msg'] ||
$contactLinks['msg'] != 'merge') {
656 $contactLinks['msg'] = 'edit';
659 if (!empty($originalId) && array_key_exists('merge', $hasPermissions)) {
660 $rgBao = new CRM_Dedupe_BAO_RuleGroup();
661 $rgBao->contact_type
= $dao->contact_type
;
662 $rgBao->used
= 'Supervised';
663 if ($rgBao->find(TRUE)) {
666 if ($rgid && isset($dao->id
)) {
667 //get an url to merge the contact
668 $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>';
669 $contactLinks['msg'] = 'merge';
676 return $contactLinks;
680 * This function retrieve component related contact information.
682 * @param array $componentIds
683 * Array of component Ids.
684 * @param string $componentName
685 * @param array $returnProperties
686 * Array of return elements.
689 * array of contact info.
691 public static function contactDetails($componentIds, $componentName, $returnProperties = array()) {
692 $contactDetails = array();
693 if (empty($componentIds) ||
694 !in_array($componentName, array('CiviContribute', 'CiviMember', 'CiviEvent', 'Activity'))
696 return $contactDetails;
699 if (empty($returnProperties)) {
700 $autocompleteContactSearch = CRM_Core_BAO_Setting
::valueOptions(CRM_Core_BAO_Setting
::SYSTEM_PREFERENCES_NAME
,
701 'contact_autocomplete_options'
703 $returnProperties = array_fill_keys(array_merge(array('sort_name'),
704 array_keys($autocompleteContactSearch)
709 if ($componentName == 'CiviContribute') {
710 $compTable = 'civicrm_contribution';
712 elseif ($componentName == 'CiviMember') {
713 $compTable = 'civicrm_membership';
715 elseif ($componentName == 'Activity') {
716 $compTable = 'civicrm_activity';
717 $activityContacts = CRM_Core_OptionGroup
::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name');
720 $compTable = 'civicrm_participant';
723 $select = $from = array();
724 foreach ($returnProperties as $property => $ignore) {
725 $value = (in_array($property, array(
728 ))) ?
'address' : $property;
731 if ($componentName == 'Activity') {
732 $sourceID = CRM_Utils_Array
::key('Activity Source', $activityContacts);
733 $select[] = "contact.$property as $property";
735 INNER JOIN civicrm_activity_contact acs ON (acs.activity_id = {$compTable}.id AND acs.record_type_id = {$sourceID})
736 INNER JOIN civicrm_contact contact ON ( contact.id = acs.contact_id )";
739 $select[] = "$property as $property";
740 $from[$value] = "INNER JOIN civicrm_contact contact ON ( contact.id = $compTable.contact_id )";
744 case 'target_sort_name':
745 $targetID = CRM_Utils_Array
::key('Activity Targets', $activityContacts);
746 $select[] = "contact_target.sort_name as $property";
748 INNER JOIN civicrm_activity_contact act ON (act.activity_id = {$compTable}.id AND act.record_type_id = {$targetID})
749 INNER JOIN civicrm_contact contact_target ON ( contact_target.id = act.contact_id )";
755 case 'street_address':
756 $select[] = "$property as $property";
757 // Grab target contact properties if this is for activity
758 if ($componentName == 'Activity') {
759 $from[$value] = "LEFT JOIN civicrm_{$value} {$value} ON ( contact_target.id = {$value}.contact_id AND {$value}.is_primary = 1 ) ";
762 $from[$value] = "LEFT JOIN civicrm_{$value} {$value} ON ( contact.id = {$value}.contact_id AND {$value}.is_primary = 1 ) ";
767 case 'state_province':
768 $select[] = "{$property}.name as $property";
769 if (!in_array('address', $from)) {
770 // Grab target contact properties if this is for activity
771 if ($componentName == 'Activity') {
772 $from['address'] = 'LEFT JOIN civicrm_address address ON ( contact_target.id = address.contact_id AND address.is_primary = 1) ';
775 $from['address'] = 'LEFT JOIN civicrm_address address ON ( contact.id = address.contact_id AND address.is_primary = 1) ';
778 $from[$value] = " LEFT JOIN civicrm_{$value} {$value} ON ( address.{$value}_id = {$value}.id ) ";
783 //finally retrieve contact details.
784 if (!empty($select) && !empty($from)) {
785 $fromClause = implode(' ', $from);
786 $selectClause = implode(', ', $select);
787 $whereClause = "{$compTable}.id IN (" . implode(',', $componentIds) . ')';
790 SELECT contact.id as contactId, $compTable.id as componentId, $selectClause
791 FROM $compTable as $compTable $fromClause
793 Group By componentId";
795 $contact = CRM_Core_DAO
::executeQuery($query);
796 while ($contact->fetch()) {
797 $contactDetails[$contact->componentId
]['contact_id'] = $contact->contactId
;
798 foreach ($returnProperties as $property => $ignore) {
799 $contactDetails[$contact->componentId
][$property] = $contact->$property;
805 return $contactDetails;
809 * Function handles shared contact address processing
810 * In this function we just modify submitted values so that new address created for the user
811 * has same address as shared contact address. We copy the address so that search etc will be
814 * @param array $address
815 * This is associated array which contains submitted form values.
819 public static function processSharedAddress(&$address) {
820 if (!is_array($address)) {
824 // Sharing contact address during create mode is pretty straight forward.
825 // In update mode we should check following:
826 // - We should check if user has uncheck shared contact address
827 // - If yes then unset the master_id or may be just delete the address that copied master
828 // Normal update process will automatically create new address with submitted values
830 // 1. loop through entire subnitted address array
831 $masterAddress = array();
832 $skipFields = array('is_primary', 'location_type_id', 'is_billing', 'master_id');
833 foreach ($address as & $values) {
834 // 2. check if master id exists, if not continue
835 if (empty($values['master_id']) ||
empty($values['use_shared_address'])) {
836 // we should unset master id when use uncheck share address for existing address
837 $values['master_id'] = 'null';
841 // 3. get the address details for master_id
842 $masterAddress = new CRM_Core_BAO_Address();
843 $masterAddress->id
= CRM_Utils_Array
::value('master_id', $values);
844 $masterAddress->find(TRUE);
846 // 4. modify submitted params and update it with shared contact address
847 // make sure you preserve specific form values like location type, is_primary_ is_billing, master_id
848 // CRM-10336: Also empty any fields from the existing address block if they don't exist in master (otherwise they will persist)
849 foreach ($values as $field => $submittedValue) {
850 if (!in_array($field, $skipFields)) {
851 if (isset($masterAddress->$field)) {
852 $values[$field] = $masterAddress->$field;
855 $values[$field] = '';
863 * Get the list of contact name give address associated array
865 * @param array $addresses
866 * Associated array of.
869 * associated array of contact names
871 public static function getAddressShareContactNames(&$addresses) {
872 $contactNames = array();
873 // get the list of master id's for address
874 $masterAddressIds = array();
875 foreach ($addresses as $key => $addressValue) {
876 if (!empty($addressValue['master_id'])) {
877 $masterAddressIds[] = $addressValue['master_id'];
881 if (!empty($masterAddressIds)) {
882 $query = 'SELECT ca.id, cc.display_name, cc.id as cid, cc.is_deleted
883 FROM civicrm_contact cc
884 INNER JOIN civicrm_address ca ON cc.id = ca.contact_id
885 WHERE ca.id IN ( ' . implode(',', $masterAddressIds) . ')';
886 $dao = CRM_Core_DAO
::executeQuery($query);
888 while ($dao->fetch()) {
889 $contactViewUrl = CRM_Utils_System
::url('civicrm/contact/view', "reset=1&cid={$dao->cid}");
890 $contactNames[$dao->id
] = array(
891 'name' => "<a href='{$contactViewUrl}'>{$dao->display_name}</a>",
892 'is_deleted' => $dao->is_deleted
,
893 'contact_id' => $dao->cid
,
897 return $contactNames;
901 * Clear the contact cache so things are kosher. We started off being super aggressive with clearing
902 * caches, but are backing off from this with every release. Compromise between ease of coding versus
903 * performance versus being accurate at that very instant
906 * The contactID that was edited / deleted.
910 public static function clearContactCaches($contactID = NULL) {
911 // clear acl cache if any.
912 CRM_ACL_BAO_Cache
::resetCache();
914 if (empty($contactID)) {
915 // also clear prev/next dedupe cache - if no contactID passed in
916 CRM_Core_BAO_PrevNextCache
::deleteItem();
919 // reset the group contact cache for this group
920 CRM_Contact_BAO_GroupContactCache
::remove();
924 * @param array $params
928 public static function updateGreeting($params) {
929 $contactType = $params['ct'];
930 $greeting = $params['gt'];
931 $valueID = $id = CRM_Utils_Array
::value('id', $params);
932 $force = CRM_Utils_Array
::value('force', $params);
933 $limit = CRM_Utils_Array
::value('limit', $params);
935 // if valueID is not passed use default value
937 $valueID = $id = self
::defaultGreeting($contactType, $greeting);
941 'contact_type' => $contactType,
942 'greeting_type' => $greeting,
945 $allGreetings = CRM_Core_PseudoConstant
::greeting($filter);
946 $originalGreetingString = $greetingString = CRM_Utils_Array
::value($valueID, $allGreetings);
947 if (!$greetingString) {
948 CRM_Core_Error
::fatal(ts('Incorrect greeting value id %1, or no default greeting for this contact type and greeting type.', array(1 => $valueID)));
951 // build return properties based on tokens
952 $greetingTokens = CRM_Utils_Token
::getTokens($greetingString);
953 $tokens = CRM_Utils_Array
::value('contact', $greetingTokens);
954 $greetingsReturnProperties = array();
955 if (is_array($tokens)) {
956 $greetingsReturnProperties = array_fill_keys(array_values($tokens), 1);
959 // Process ALL contacts only when force=1 or force=2 is passed. Else only contacts with NULL greeting or addressee value are updated.
960 $processAll = $processOnlyIdSet = FALSE;
964 elseif ($force == 2) {
965 $processOnlyIdSet = TRUE;
968 //FIXME : apiQuery should handle these clause.
969 $filterContactFldIds = $filterIds = array();
970 $idFldName = $displayFldName = NULL;
971 if (in_array($greeting, CRM_Contact_BAO_Contact
::$_greetingTypes)) {
972 $idFldName = $greeting . '_id';
973 $displayFldName = $greeting . '_display';
977 $queryParams = array(1 => array($contactType, 'String'));
979 // if $force == 1 then update all contacts else only
980 // those with NULL greeting or addressee value CRM-9476
982 $sql = "SELECT DISTINCT id, $idFldName FROM civicrm_contact WHERE contact_type = %1 ";
986 SELECT DISTINCT id, $idFldName
988 WHERE contact_type = %1
989 AND ({$idFldName} IS NULL
990 OR ( {$idFldName} IS NOT NULL AND ({$displayFldName} IS NULL OR {$displayFldName} = '')) )";
994 $sql .= " LIMIT 0, %2";
995 $queryParams +
= array(2 => array($limit, 'Integer'));
998 $dao = CRM_Core_DAO
::executeQuery($sql, $queryParams);
999 while ($dao->fetch()) {
1000 $filterContactFldIds[$dao->id
] = $dao->$idFldName;
1002 if (!CRM_Utils_System
::isNull($dao->$idFldName)) {
1003 $filterIds[$dao->id
] = $dao->$idFldName;
1008 if (empty($filterContactFldIds)) {
1009 $filterContactFldIds[] = 0;
1012 // retrieve only required contact information
1013 $extraParams[] = array('contact_type', '=', $contactType, 0, 0);
1014 // we do token replacement in the replaceGreetingTokens hook
1015 list($greetingDetails) = CRM_Utils_Token
::getTokenDetails(array_keys($filterContactFldIds),
1016 $greetingsReturnProperties,
1017 FALSE, FALSE, $extraParams
1019 // perform token replacement and build update SQL
1020 $contactIds = array();
1021 $cacheFieldQuery = "UPDATE civicrm_contact SET {$greeting}_display = CASE id ";
1022 foreach ($greetingDetails as $contactID => $contactDetails) {
1024 !array_key_exists($contactID, $filterContactFldIds)
1029 if ($processOnlyIdSet && !array_key_exists($contactID, $filterIds)) {
1034 $greetingString = $originalGreetingString;
1035 $contactIds[] = $contactID;
1038 if ($greetingBuffer = CRM_Utils_Array
::value($filterContactFldIds[$contactID], $allGreetings)) {
1039 $greetingString = $greetingBuffer;
1043 self
::processGreetingTemplate($greetingString, $contactDetails, $contactID, 'CRM_UpdateGreeting');
1044 $greetingString = CRM_Core_DAO
::escapeString($greetingString);
1045 $cacheFieldQuery .= " WHEN {$contactID} THEN '{$greetingString}' ";
1047 $allContactIds[] = $contactID;
1050 if (!empty($allContactIds)) {
1051 $cacheFieldQuery .= " ELSE {$greeting}_display
1053 if (!empty($contactIds)) {
1054 // need to update greeting _id field.
1055 // reset greeting _custom
1056 $resetCustomGreeting = '';
1057 if ($valueID != 4) {
1058 $resetCustomGreeting = ", {$greeting}_custom = NULL ";
1062 UPDATE civicrm_contact
1063 SET {$greeting}_id = {$valueID}
1064 {$resetCustomGreeting}
1065 WHERE id IN (" . implode(',', $contactIds) . ")";
1066 CRM_Core_DAO
::executeQuery($queryString);
1069 // now update cache field
1070 CRM_Core_DAO
::executeQuery($cacheFieldQuery);
1075 * Fetch the default greeting for a given contact type
1077 * @param string $contactType
1079 * @param string $greetingType
1085 public static function defaultGreeting($contactType, $greetingType) {
1086 $contactTypeFilters = array('Individual' => 1, 'Household' => 2, 'Organization' => 3);
1087 if (!isset($contactTypeFilters[$contactType])) {
1090 $filter = $contactTypeFilters[$contactType];
1092 $id = CRM_Core_OptionGroup
::values($greetingType, NULL, NULL, NULL,
1093 " AND is_default = 1 AND (filter = {$filter} OR filter = 0)",
1097 return current($id);
1102 * Process a greeting template string to produce the individualised greeting text.
1104 * This works just like message templates for mailings:
1105 * the template is processed with the token substitution mechanism,
1106 * to supply the individual contact data;
1107 * and it is also processed with Smarty,
1108 * to allow for conditionals etc. based on the contact data.
1110 * Note: We don't pass any variables to Smarty --
1111 * all variable data is inserted into the input string
1112 * by the token substitution mechanism,
1113 * before Smarty is invoked.
1115 * @param string $templateString
1116 * The greeting template string with contact tokens + Smarty syntax.
1118 * @param $contactDetails
1119 * @param int $contactID
1120 * @param string $className
1124 public static function processGreetingTemplate(&$templateString, $contactDetails, $contactID, $className) {
1125 CRM_Utils_Token
::replaceGreetingTokens($templateString, $contactDetails, $contactID, $className, TRUE);
1127 $smarty = CRM_Core_Smarty
::singleton();
1128 $templateString = $smarty->fetch("string:$templateString");