<?php
/*
+--------------------------------------------------------------------+
- | CiviCRM version 4.5 |
+ | CiviCRM version 4.6 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2014 |
+--------------------------------------------------------------------+
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
-*/
+ */
/**
*
*/
class CRM_Dedupe_Merger {
- // FIXME: consider creating a common structure with cidRefs() and eidRefs()
- // FIXME: the sub-pages references by the URLs should
- // be loaded dynamically on the merge form instead
/**
+ * FIXME: consider creating a common structure with cidRefs() and eidRefs()
+ * FIXME: the sub-pages references by the URLs should
+ * be loaded dynamically on the merge form instead
* @return array
*/
- static function relTables() {
+ public static function relTables() {
static $relTables;
$config = CRM_Core_Config::singleton();
/**
* Returns the related tables groups for which a contact has any info entered
*/
- static function getActiveRelTables($cid) {
+ public static function getActiveRelTables($cid) {
$cid = (int) $cid;
$groups = array();
/**
* Return tables and their fields referencing civicrm_contact.contact_id explicitly
*/
- static function cidRefs() {
+ public static function cidRefs() {
static $cidRefs;
if (!$cidRefs) {
$sql = "
/**
* Return tables and their fields referencing civicrm_contact.contact_id with entity_id
*/
- static function eidRefs() {
+ public static function eidRefs() {
static $eidRefs;
if (!$eidRefs) {
// FIXME: this should be generated dynamically from the schema
return $eidRefs;
}
+ /**
+ * Return tables using locations
+ */
+ public static function locTables() {
+ static $locTables;
+ if (!$locTables) {
+ $locTables = array('civicrm_email', 'civicrm_address', 'civicrm_phone');
+
+ // Allow hook_civicrm_merge() to adjust $locTables
+ CRM_Utils_Hook::merge('locTables', $locTables);
+ }
+ return $locTables;
+ }
+
/**
* We treat multi-valued custom sets as "related tables" similar to activities, contributions, etc.
- * @param string $request 'relTables' or 'cidRefs'
+ * @param string $request
+ * 'relTables' or 'cidRefs'.
* @see CRM-13836
*/
- static function getMultiValueCustomSets($request) {
+ public static function getMultiValueCustomSets($request) {
static $data = NULL;
if ($data === NULL) {
$data = array(
'extends' => array('IN' => array('Individual', 'Organization', 'Household', 'Contact')),
'return' => array('id', 'title', 'table_name', 'style'),
));
- foreach($result['values'] as $custom) {
+ foreach ($result['values'] as $custom) {
$data['cidRefs'][$custom['table_name']] = array('entity_id');
$urlSuffix = $custom['style'] == 'Tab' ? '&selectedChild=custom_' . $custom['id'] : '';
$data['relTables']['rel_table_custom_' . $custom['id']] = array(
* Tables which require custom processing should declare functions to call here.
* Doing so will override normal processing.
*/
- static function cpTables() {
+ public static function cpTables() {
static $tables;
if (!$tables) {
$tables = array(
/**
* Return payment related table.
*/
- static function paymentTables() {
+ public static function paymentTables() {
static $tables;
if (!$tables) {
$tables = array('civicrm_pledge', 'civicrm_membership', 'civicrm_participant');
/**
* Return payment update Query.
*/
- static function paymentSql($tableName, $mainContactId, $otherContactId) {
+ public static function paymentSql($tableName, $mainContactId, $otherContactId) {
$sqls = array();
if (!$tableName || !$mainContactId || !$otherContactId) {
return $sqls;
*
* @return array
*/
- static function operationSql($mainId, $otherId, $tableName, $tableOperations = array(), $mode = 'add') {
+ public static function operationSql($mainId, $otherId, $tableName, $tableOperations = array(), $mode = 'add') {
$sqls = array();
if (!$tableName || !$mainId || !$otherId) {
return $sqls;
}
-
switch ($tableName) {
case 'civicrm_membership':
- if (array_key_exists($tableName, $tableOperations) && $tableOperations[$tableName]['add'])
- break;
- if ($mode == 'add') {
- $sqls[] = "
+ if (array_key_exists($tableName, $tableOperations) && $tableOperations[$tableName]['add']) {
+ break;
+ }
+ if ($mode == 'add') {
+ $sqls[] = "
DELETE membership1.* FROM civicrm_membership membership1
INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = membership2.membership_type_id
AND membership1.contact_id = {$mainId}
AND membership2.contact_id = {$otherId} ";
- }
- if ($mode == 'payment') {
- $sqls[] = "
+ }
+ if ($mode == 'payment') {
+ $sqls[] = "
DELETE contribution.* FROM civicrm_contribution contribution
INNER JOIN civicrm_membership_payment payment ON payment.contribution_id = contribution.id
INNER JOIN civicrm_membership membership1 ON membership1.id = payment.membership_id
AND membership1.contact_id = {$mainId}
INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = membership2.membership_type_id
AND membership2.contact_id = {$otherId}";
- }
- break;
+ }
+ break;
case 'civicrm_uf_match':
// normal queries won't work for uf_match since that will lead to violation of unique constraint,
* Based on the provided two contact_ids and a set of tables, move the
* belongings of the other contact to the main one.
*
- * @static
*/
- static function moveContactBelongings($mainId, $otherId, $tables = FALSE, $tableOperations = array()) {
+ public static function moveContactBelongings($mainId, $otherId, $tables = FALSE, $tableOperations = array()) {
$cidRefs = self::cidRefs();
$eidRefs = self::eidRefs();
$cpTables = self::cpTables();
}
else {
// if there aren't any specific tables, don't affect the ones handled by relTables()
+ // also don't affect tables in locTables() CRM-15658
$relTables = self::relTables();
- $handled = array();
+ $handled = self::locTables();
foreach ($relTables as $params) {
$handled = array_merge($handled, $params['tables']);
}
/**
* Find differences between contacts.
*
- * @param array $main contact details
- * @param array $other contact details
+ * @param array $main
+ * Contact details.
+ * @param array $other
+ * Contact details.
*
* @return array
- * @static
*/
- static function findDifferences($main, $other) {
+ public static function findDifferences($main, $other) {
$result = array(
'contact' => array(),
'custom' => array(),
/**
* Batch merge a set of contacts based on rule-group and group.
*
- * @param int $rgid rule group id
- * @param int $gid group id
- * @param string $mode helps decide how to behave when there are conflicts.
+ * @param int $rgid
+ * Rule group id.
+ * @param int $gid
+ * Group id.
+ * @param string $mode
+ * Helps decide how to behave when there are conflicts.
* A 'safe' value skips the merge if there are any un-resolved conflicts.
* Does a force merge otherwise.
- * @param boolean $autoFlip wether to let api decide which contact to retain and which to delete.
+ * @param bool $autoFlip to let api decide which contact to retain and which to delete.
+ * Wether to let api decide which contact to retain and which to delete.
* @param bool $redirectForPerformance
*
* @return array|bool
- * @static
- * @access public
*/
- static function batchMerge($rgid, $gid = NULL, $mode = 'safe', $autoFlip = TRUE, $redirectForPerformance = FALSE) {
+ public static function batchMerge($rgid, $gid = NULL, $mode = 'safe', $autoFlip = TRUE, $redirectForPerformance = FALSE) {
$contactType = CRM_Core_DAO::getFieldValue('CRM_Dedupe_DAO_RuleGroup', $rgid, 'contact_type');
$cacheKeyString = "merge {$contactType}";
$cacheKeyString .= $rgid ? "_{$rgid}" : '_0';
/**
* Merge given set of contacts. Performs core operation.
*
- * @param array $dupePairs set of pair of contacts for whom merge is to be done.
- * @param array $cacheParams prev-next-cache params based on which next pair of contacts are computed.
+ * @param array $dupePairs
+ * Set of pair of contacts for whom merge is to be done.
+ * @param array $cacheParams
+ * Prev-next-cache params based on which next pair of contacts are computed.
* Generally used with batch-merge.
- * @param string $mode helps decide how to behave when there are conflicts.
+ * @param string $mode
+ * Helps decide how to behave when there are conflicts.
* A 'safe' value skips the merge if there are any un-resolved conflicts.
* Does a force merge otherwise (aggressive mode).
- * @param boolean $autoFlip wether to let api decide which contact to retain and which to delete.
+ * @param bool $autoFlip to let api decide which contact to retain and which to delete.
+ * Wether to let api decide which contact to retain and which to delete.
*
*
* @param bool $redirectForPerformance
*
* @return array|bool
- * @static
- * @access public
*/
- static function merge($dupePairs = array(), $cacheParams = array(), $mode = 'safe',
- $autoFlip = TRUE, $redirectForPerformance = FALSE
+ public static function merge($dupePairs = array(), $cacheParams = array(), $mode = 'safe',
+ $autoFlip = TRUE, $redirectForPerformance = FALSE
) {
$cacheKeyString = CRM_Utils_Array::value('cache_key_string', $cacheParams);
$resultStats = array('merged' => array(), 'skipped' => array());
* A function which uses various rules / algorithms for choosing which contact to bias to
* when there's a conflict (to handle "gotchas"). Plus the safest route to merge.
*
- * @param int $mainId main contact with whom merge has to happen
- * @param int $otherId duplicate contact which would be deleted after merge operation
- * @param array $migrationInfo array of information about which elements to merge.
- * @param string $mode helps decide how to behave when there are conflicts.
+ * @param int $mainId
+ * Main contact with whom merge has to happen.
+ * @param int $otherId
+ * Duplicate contact which would be deleted after merge operation.
+ * @param array $migrationInfo
+ * Array of information about which elements to merge.
+ * @param string $mode
+ * Helps decide how to behave when there are conflicts.
* A 'safe' value skips the merge if there are any un-resolved conflicts.
* Does a force merge otherwise (aggressive mode).
*
* @return bool
- * @static
- * @access public
*/
- static function skipMerge($mainId, $otherId, &$migrationInfo, $mode = 'safe') {
+ public static function skipMerge($mainId, $otherId, &$migrationInfo, $mode = 'safe') {
$conflicts = array();
$migrationData = array(
'old_migration_info' => $migrationInfo,
}
elseif ((in_array(substr($key, 5), CRM_Dedupe_Merger::getContactFields()) or
substr($key, 0, 12) == 'move_custom_'
- ) and $val != NULL) {
+ ) and $val != NULL
+ ) {
// Rule: if both main-contact has other-contact, let $mode decide if to merge a
// particular field or not
if (!empty($migrationInfo['rows'][$key]['main'])) {
if ($fieldName == 'address') {
$mainNewLocTypeId = $migrationInfo['location'][$fieldName][$fieldCount]['locTypeId'];
if (!empty($migrationInfo['main_loc_address']) &&
- array_key_exists("main_{$mainNewLocTypeId}", $migrationInfo['main_loc_address'])) {
+ array_key_exists("main_{$mainNewLocTypeId}", $migrationInfo['main_loc_address'])
+ ) {
// main loc already has some address for the loc-type. Its a overwrite situation.
// look for next available loc-type
/**
* A function to build an array of information required by merge function and the merge UI.
*
- * @param int $mainId main contact with whom merge has to happen
- * @param int $otherId duplicate contact which would be deleted after merge operation
+ * @param int $mainId
+ * Main contact with whom merge has to happen.
+ * @param int $otherId
+ * Duplicate contact which would be deleted after merge operation.
*
* @return array|bool|int
- * @static
- * @access public
*/
- static function getRowsElementsAndInfo($mainId, $otherId) {
+ public static function getRowsElementsAndInfo($mainId, $otherId) {
$qfZeroBug = 'e8cddb72-a257-11dc-b9cc-0016d3330ee9';
// Fetch contacts
foreach (array('main' => $mainId, 'other' => $otherId) as $moniker => $cid) {
- $params = array('contact_id' => $cid, 'version' => 3, 'return' => array_merge(array('display_name'), self::getContactFields()));
+ $params = array(
+ 'contact_id' => $cid,
+ 'version' => 3,
+ 'return' => array_merge(array('display_name'), self::getContactFields()),
+ );
$result = civicrm_api('contact', 'get', $params);
if (empty($result['values'][$cid]['contact_type'])) {
CRM_Core_DAO::freeResult();
}
- // get all contact subtypes
- $contactSubTypes = CRM_Contact_BAO_ContactType::subTypePairs(NULL, TRUE, '');
-
// FIXME: there must be a better way
foreach (array('main', 'other') as $moniker) {
$contact = &$$moniker;
$value = empty($preferred_communication_method) ? array() : $preferred_communication_method;
$specialValues[$moniker] = array(
'preferred_communication_method' => $value,
- 'contact_sub_type' => $value,
'communication_style_id' => $value,
);
- if (!empty($contact['preferred_communication_method'])){
- // api 3 returns pref_comm_method as an array, which breaks the lookup; so we reconstruct
- $prefCommList = is_array($specialValues[$moniker]['preferred_communication_method']) ?
- implode(CRM_Core_DAO::VALUE_SEPARATOR, $specialValues[$moniker]['preferred_communication_method']) :
- $specialValues[$moniker]['preferred_communication_method'];
+ if (!empty($contact['preferred_communication_method'])) {
+ // api 3 returns pref_comm_method as an array, which breaks the lookup; so we reconstruct
+ $prefCommList = is_array($specialValues[$moniker]['preferred_communication_method']) ? implode(CRM_Core_DAO::VALUE_SEPARATOR, $specialValues[$moniker]['preferred_communication_method']) : $specialValues[$moniker]['preferred_communication_method'];
$specialValues[$moniker]['preferred_communication_method'] = CRM_Core_DAO::VALUE_SEPARATOR . $prefCommList . CRM_Core_DAO::VALUE_SEPARATOR;
}
$names = array(
- 'preferred_communication_method' =>
- array(
+ 'preferred_communication_method' => array(
'newName' => 'preferred_communication_method_display',
'groupName' => 'preferred_communication_method',
),
);
CRM_Core_OptionGroup::lookupValues($specialValues[$moniker], $names);
- if (!empty($contact['contact_sub_type'])) {
- $specialValues[$moniker]['contact_sub_type'] = implode(CRM_Core_DAO::VALUE_SEPARATOR, $contact['contact_sub_type']);
-
- // fix contact sub type label for contact with sub type
- $subtypes = array();
- foreach ($contact['contact_sub_type'] as $key => $value) {
- $subtypes[] = CRM_Utils_Array::retrieveValueRecursive($contactSubTypes, $value);
- }
- $contact['contact_sub_type_display'] = $specialValues[$moniker]['contact_sub_type_display'] = implode(', ', $subtypes);
- }
-
if (!empty($contact['communication_style'])) {
$specialValues[$moniker]['communication_style_id_display'] = $contact['communication_style'];
}
$rows = $elements = $relTableElements = $migrationInfo = array();
+ $genders = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'gender_id');
+
foreach ($diffs['contact'] as $field) {
+ if ($field == 'contact_sub_type') {
+ // CRM-15681 don't display sub-types in UI
+ continue;
+ }
foreach (array('main', 'other') as $moniker) {
$contact = &$$moniker;
$value = CRM_Utils_Array::value($field, $contact);
$value = CRM_Utils_Array::value('suffix_id', $contact);
$field = 'suffix_id';
}
+ elseif ($field == 'gender_id' && !empty($value)) {
+ $label = $genders[$value];
+ }
+ elseif ($field == 'current_employer_id' && !empty($value)) {
+ $label = "$value (" . CRM_Contact_BAO_Contact::displayName($value) . ")";
+ }
$rows["move_$field"][$moniker] = $label;
if ($moniker == 'other') {
//CRM-14334
array(
1 => $block,
2 => $count,
- 3 => $allLocationTypes[$locTypeId]
+ 3 => $allLocationTypes[$locTypeId],
)
);
$js = array('onChange' => "mergeBlock('$name', this, $count );");
}
$elements[] = array(
- 'select', "location[{$name}][$count][locTypeId]", NULL,
- $defaultLocType + $locTypeValues, $js,
+ 'select',
+ "location[{$name}][$count][locTypeId]",
+ NULL,
+ $defaultLocType + $locTypeValues,
+ $js,
);
// keep location-type-id same as that of other-contact
$migrationInfo['location'][$name][$count]['locTypeId'] = $locTypeId;
);
if ($values['data'] === 0 || $values['data'] === '0') {
$values['data'] = $qfZeroBug;
- }
+ }
$value = ($values['data']) ? $values['data'] : $value;
- }
+ }
}
$rows["move_custom_$fid"]['title'] = $field['label'];
* other contact to the main one - be it Location / CustomFields or Contact .. related info.
* A superset of moveContactBelongings() function.
*
- * @param int $mainId main contact with whom merge has to happen
- * @param int $otherId duplicate contact which would be deleted after merge operation
+ * @param int $mainId
+ * Main contact with whom merge has to happen.
+ * @param int $otherId
+ * Duplicate contact which would be deleted after merge operation.
*
* @param $migrationInfo
*
* @return bool
- * @static
- * @access public
*/
- static function moveAllBelongings($mainId, $otherId, $migrationInfo) {
+ public static function moveAllBelongings($mainId, $otherId, $migrationInfo) {
if (empty($migrationInfo)) {
return FALSE;
}
$value = '0';
}
if ((in_array(substr($key, 5), CRM_Dedupe_Merger::getContactFields()) ||
- substr($key, 0, 12) == 'move_custom_') &&
+ substr($key, 0, 12) == 'move_custom_') &&
$value != NULL
) {
$submitted[substr($key, 5)] = $value;
}
}
-
// **** Do location related migration:
if (!empty($locBlocks)) {
$locComponent = array(
}
if (isset($migrationInfo['main_details']['loc_block_ids'][$name])) {
- $mainBlockId = CRM_Utils_Array::value($idKey, $migrationInfo['main_details']['loc_block_ids'][$name]);
+ $mainBlockId = CRM_Utils_Array::value($idKey, $migrationInfo['main_details']['loc_block_ids'][$name]);
}
if (!$otherBlockId) {
//keep state and country as array format.
//for checkbox and m-select format w/ VALUE_SEPARATOR
if (in_array($htmlType, array(
- 'CheckBox', 'Multi-Select', 'AdvMulti-Select'))) {
+ 'CheckBox',
+ 'Multi-Select',
+ 'AdvMulti-Select',
+ ))) {
$submitted[$key] = CRM_Core_DAO::VALUE_SEPARATOR . implode(CRM_Core_DAO::VALUE_SEPARATOR,
- $mergeValue
- ) . CRM_Core_DAO::VALUE_SEPARATOR;
+ $mergeValue
+ ) . CRM_Core_DAO::VALUE_SEPARATOR;
}
else {
$submitted[$key] = $mergeValue;
}
}
elseif (in_array($htmlType, array(
- 'Multi-Select Country', 'Multi-Select State/Province'))) {
+ 'Multi-Select Country',
+ 'Multi-Select State/Province',
+ ))) {
//we require submitted values should be in array format
if ($value) {
$mergeValueArray = explode(CRM_Core_DAO::VALUE_SEPARATOR, $value);
// move the other contact's file to main contact
//NYSS need to INSERT or UPDATE depending on whether main contact has an existing record
- if ( CRM_Core_DAO::singleValueQuery("SELECT id FROM {$tableName} WHERE entity_id = {$mainId}") ) {
- $sql = "UPDATE {$tableName} SET {$columnName} = {$fileIds[$otherId]} WHERE entity_id = {$mainId}";
+ if (CRM_Core_DAO::singleValueQuery("SELECT id FROM {$tableName} WHERE entity_id = {$mainId}")) {
+ $sql = "UPDATE {$tableName} SET {$columnName} = {$fileIds[$otherId]} WHERE entity_id = {$mainId}";
}
else {
$sql = "INSERT INTO {$tableName} ( entity_id, {$columnName} ) VALUES ( {$mainId}, {$fileIds[$otherId]} )";
}
CRM_Core_DAO::executeQuery($sql, CRM_Core_DAO::$_nullArray);
- if ( CRM_Core_DAO::singleValueQuery("
+ if (CRM_Core_DAO::singleValueQuery("
SELECT id
FROM civicrm_entity_file
- WHERE entity_table = '{$tableName}' AND file_id = {$fileIds[$otherId]}") ) {
+ WHERE entity_table = '{$tableName}' AND file_id = {$fileIds[$otherId]}")
+ ) {
$sql = "
UPDATE civicrm_entity_file
SET entity_id = {$mainId}
/* } */
+ // CRM-15681 merge sub_types
+ if ($other_sub_types = CRM_Utils_array::value('contact_sub_type', $migrationInfo['other_details'])) {
+ if ($main_sub_types = CRM_Utils_array::value('contact_sub_type', $migrationInfo['main_details'])) {
+ $submitted['contact_sub_type'] = array_unique(array_merge($main_sub_types, $other_sub_types));
+ }
+ else {
+ $submitted['contact_sub_type'] = $other_sub_types;
+ }
+ }
// **** Update contact related info for the main contact
if (!empty($submitted)) {
if ($currentEmloyerId = CRM_Utils_Array::value('current_employer_id', $submitted)) {
if (!CRM_Utils_System::isNull($currentEmloyerId)) {
$submitted['current_employer'] = $submitted['current_employer_id'];
- } else {
+ }
+ else {
$submitted['current_employer'] = '';
}
unset($submitted['current_employer_id']);
}
//CRM-14312 include prefix/suffix from mainId if not overridden for proper construction of display/sort name
- if ( !isset($submitted['prefix_id']) && !empty($migrationInfo['main_details']['prefix_id']) ) {
+ if (!isset($submitted['prefix_id']) && !empty($migrationInfo['main_details']['prefix_id'])) {
$submitted['prefix_id'] = $migrationInfo['main_details']['prefix_id'];
}
- if ( !isset($submitted['suffix_id']) && !empty($migrationInfo['main_details']['suffix_id']) ) {
+ if (!isset($submitted['suffix_id']) && !empty($migrationInfo['main_details']['suffix_id'])) {
$submitted['suffix_id'] = $migrationInfo['main_details']['suffix_id'];
}
unset($submitted);
}
+ CRM_Utils_Hook::post('merge', 'Contact', $mainId, CRM_Core_DAO::$_nullObject);
+
return TRUE;
}
/**
- * @return array of field names which will be compared, so everything except ID.
+ * @return array
+ * Array of field names which will be compared, so everything except ID.
*/
- static function getContactFields() {
+ public static function getContactFields() {
$contactFields = CRM_Contact_DAO_Contact::fields();
- $invalidFields = array('api_key', 'contact_is_deleted', 'created_date', 'display_name', 'hash', 'id', 'modified_date',
- 'primary_contact_id', 'sort_name', 'user_unique_id');
+ $invalidFields = array(
+ 'api_key',
+ 'contact_is_deleted',
+ 'created_date',
+ 'display_name',
+ 'hash',
+ 'id',
+ 'modified_date',
+ 'primary_contact_id',
+ 'sort_name',
+ 'user_unique_id',
+ );
foreach ($contactFields as $field => $value) {
if (in_array($field, $invalidFields)) {
unset($contactFields[$field]);
/**
* Added for CRM-12695
- * Based on the contactId provided
+ * Based on the contactID provided
* add/update membership(s) to related contacts
*
- * @param contactId
+ * @param int $contactID
*/
- static function addMembershipToRealtedContacts($contactID) {
+ public static function addMembershipToRealtedContacts($contactID) {
$dao = new CRM_Member_DAO_Membership();
$dao->contact_id = $contactID;
$dao->is_test = 0;
'start_date' => CRM_Utils_Date::isoToMysql($dao->start_date),
'end_date' => CRM_Utils_Date::isoToMysql($dao->end_date),
'source' => $dao->source,
- 'status_id' => $dao->status_id
+ 'status_id' => $dao->status_id,
);
// create/update membership(s) for related contact(s)
CRM_Member_BAO_Membership::createRelatedMemberships($membershipParams, $dao);
} // end of if relationshipTypeId
}
}
+
}