X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=CRM%2FDedupe%2FMerger.php;h=8566e7a18344904072fd4fd2c95875ece70b70ad;hb=c30897643e6607912705d3aecd3039446e3a6f0d;hp=3f926f75899895248a1d1743322b0a9132e6f792;hpb=e562e4ebc9c7637002ff32c18c2903e129bb2510;p=civicrm-core.git diff --git a/CRM/Dedupe/Merger.php b/CRM/Dedupe/Merger.php index 3f926f7589..8566e7a183 100644 --- a/CRM/Dedupe/Merger.php +++ b/CRM/Dedupe/Merger.php @@ -1,9 +1,9 @@ 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( @@ -290,7 +291,7 @@ WHERE * 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( @@ -305,9 +306,9 @@ WHERE } /** - * return payment related table. + * Return payment related table. */ - static function paymentTables() { + public static function paymentTables() { static $tables; if (!$tables) { $tables = array('civicrm_pledge', 'civicrm_membership', 'civicrm_participant'); @@ -317,9 +318,9 @@ WHERE } /** - * return payment update Query. + * 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; @@ -363,46 +364,46 @@ INNER JOIN civicrm_participant participant ON ( participant.id = payment.partic } /** - * @param $mainId - * @param $otherId - * @param $tableName + * @param int $mainId + * @param int $otherId + * @param string $tableName * @param array $tableOperations * @param string $mode * * @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, - // failing to meet intended result. Therefore we introduce this additonal query: + // failing to meet intended result. Therefore we introduce this additional query: $sqls[] = "DELETE FROM civicrm_uf_match WHERE contact_id = {$mainId}"; break; } @@ -414,9 +415,8 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m * 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(); @@ -517,13 +517,14 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m /** * 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(), @@ -552,25 +553,23 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m } /** - * Function to batch merge a set of contacts based on rule-group and group. + * 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 - * @internal param array $cacheParams prev-next-cache params based on which next pair of contacts are computed. - * Generally used with batch-merge. - * @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'; @@ -598,26 +597,27 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m } /** - * Function to merge given set of contacts. Performs core operation. + * 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()); @@ -692,18 +692,20 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m * 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, @@ -719,7 +721,8 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m } 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'])) { @@ -746,7 +749,6 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m if (!empty($migrationInfo['main_loc_block']) && array_key_exists("main_address{$mainNewLocTypeId}", $migrationInfo['main_loc_block'])) { // main loc already has some address for the loc-type. Its a overwrite situation. - // look for next available loc-type $newTypeId = NULL; foreach ($allLocationTypes as $typeId => $typeLabel) { @@ -804,19 +806,23 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m /** * 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'])) { @@ -831,9 +837,6 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m 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; @@ -841,37 +844,22 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m $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']; } @@ -889,7 +877,13 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m $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); @@ -924,6 +918,12 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m $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 @@ -1036,7 +1036,7 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m array( 1 => $block, 2 => $count, - 3 => $allLocationTypes[$locTypeId] + 3 => $allLocationTypes[$locTypeId], ) ); @@ -1055,8 +1055,11 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m $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; @@ -1170,9 +1173,9 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m ); if ($values['data'] === 0 || $values['data'] === '0') { $values['data'] = $qfZeroBug; - } + } $value = ($values['data']) ? $values['data'] : $value; - } + } } $rows["move_custom_$fid"]['title'] = $field['label']; @@ -1203,16 +1206,16 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m * 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; } @@ -1225,7 +1228,7 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m $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; @@ -1255,7 +1258,6 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m } } - // **** Do location related migration: if (!empty($locBlocks)) { $locComponent = array( @@ -1291,7 +1293,7 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m } 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) { @@ -1423,10 +1425,13 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m //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; @@ -1434,7 +1439,9 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m } } 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); @@ -1483,18 +1490,19 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m // 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} @@ -1548,6 +1556,15 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m /* } */ + // 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)) { @@ -1557,17 +1574,18 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m 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']; } @@ -1575,16 +1593,29 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m 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]); @@ -1595,12 +1626,12 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m /** * 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; @@ -1618,11 +1649,12 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m '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 } } + }