$title = $userRecordUrl = '';
$config = CRM_Core_Config::singleton();
+ // @todo - this user url stuff is only needed for the form layer - move to CRM_Contact_Form_Merge
if ($config->userSystem->is_drupal) {
$userRecordUrl = CRM_Utils_System::url('user/%ufid');
$title = ts('%1 User: %2; user id: %3', [
*/
public static function skipMerge($mainId, $otherId, &$migrationInfo, $mode = 'safe', &$conflicts = []) {
- $conflicts = self::getConflicts($migrationInfo, $mainId, $otherId, $mode);
+ $conflicts = self::getConflicts($migrationInfo, $mainId, $otherId, $mode)['conflicts'];
// A hook could have set skip_merge in order to alter merge behaviour.
// This is a something we might ideally deprecate since they really 'should'
// mess with the conflicts array instead.
$locations = ['main' => [], 'other' => []];
foreach ($locationBlocks as $blockName => $blockInfo) {
-
- // Collect existing fields from both 'main' and 'other' contacts first
- // This allows us to match up location/types when building the table rows
- $locations['main'][$blockName] = self::buildLocationBlockForContact($mainId, $blockInfo, $blockName);
- $locations['other'][$blockName] = self::buildLocationBlockForContact($otherId, $blockInfo, $blockName);
-
- // Now, build the table rows appropriately, based off the information on
- // the 'other' contact
- if (!empty($locations['other']) && !empty($locations['other'][$blockName])) {
- foreach ($locations['other'][$blockName] as $count => $value) {
-
- $displayValue = $value[$blockInfo['displayField']];
-
- // Add this value to the table rows
- $rows["move_location_{$blockName}_{$count}"]['other'] = $displayValue;
-
- // CRM-17556 Only display 'main' contact value if it's the same location + type
- // Look it up from main values...
-
- $lookupLocation = FALSE;
- if ($blockInfo['hasLocation']) {
- $lookupLocation = $value['location_type_id'];
- }
-
- $lookupType = FALSE;
- if ($blockInfo['hasType']) {
- $lookupType = CRM_Utils_Array::value($blockInfo['hasType'], $value);
- }
-
- // Hold ID of main contact's matching block
- $mainContactBlockId = 0;
-
- if (!empty($locations['main'][$blockName])) {
- foreach ($locations['main'][$blockName] as $mainValueCheck) {
- // No location/type, or matching location and type
- if (
- (empty($lookupLocation) || $lookupLocation == $mainValueCheck['location_type_id'])
- && (empty($lookupType) || $lookupType == $mainValueCheck[$blockInfo['hasType']])
- ) {
- // Set this value as the default against the 'other' contact value
- $rows["move_location_{$blockName}_{$count}"]['main'] = $mainValueCheck[$blockInfo['displayField']];
- $rows["move_location_{$blockName}_{$count}"]['main_is_primary'] = $mainValueCheck['is_primary'];
- $rows["move_location_{$blockName}_{$count}"]['location_entity'] = $blockName;
- $mainContactBlockId = $mainValueCheck['id'];
- break;
- }
- }
- }
-
- // Add checkbox to migrate data from 'other' to 'main'
- $elements[] = ['advcheckbox', "move_location_{$blockName}_{$count}"];
-
- // Add checkbox to set the 'other' location as primary
- $elements[] = [
- 'advcheckbox',
- "location_blocks[$blockName][$count][set_other_primary]",
- NULL,
- ts('Set as primary'),
- ];
-
- // Flag up this field to skipMerge function (@todo: do we need to?)
- $migrationInfo["move_location_{$blockName}_{$count}"] = 1;
-
- // Add a hidden field to store the ID of the target main contact block
- $elements[] = [
- 'hidden',
- "location_blocks[$blockName][$count][mainContactBlockId]",
- $mainContactBlockId,
- ];
-
- // Setup variables
- $thisTypeId = FALSE;
- $thisLocId = FALSE;
-
- // Provide a select drop-down for the location's location type
- // eg: Home, Work...
-
- if ($blockInfo['hasLocation']) {
-
- // Load the location options for this entity
- $locationOptions = civicrm_api3($blockName, 'getoptions', ['field' => 'location_type_id']);
-
- $thisLocId = $value['location_type_id'];
-
- // Put this field's location type at the top of the list
- $tmpIdList = $locationOptions['values'];
- $defaultLocId = [$thisLocId => $tmpIdList[$thisLocId]];
- unset($tmpIdList[$thisLocId]);
-
- // Add the element
- $elements[] = [
- 'select',
- "location_blocks[$blockName][$count][locTypeId]",
- NULL,
- $defaultLocId + $tmpIdList,
- ];
-
- // Add the relevant information to the $migrationInfo
- // Keep location-type-id same as that of other-contact
- // @todo Check this logic out
- $migrationInfo['location_blocks'][$blockName][$count]['locTypeId'] = $thisLocId;
- if ($blockName != 'address') {
- $elements[] = [
- 'advcheckbox',
- "location_blocks[{$blockName}][$count][operation]",
- NULL,
- ts('Add new'),
- ];
- // always use add operation
- $migrationInfo['location_blocks'][$blockName][$count]['operation'] = 1;
- }
-
- }
-
- // Provide a select drop-down for the location's type/provider
- // eg websites: Facebook...
-
- if ($blockInfo['hasType']) {
-
- // Load the type options for this entity
- $typeOptions = civicrm_api3($blockName, 'getoptions', ['field' => $blockInfo['hasType']]);
-
- $thisTypeId = CRM_Utils_Array::value($blockInfo['hasType'], $value);
-
- // Put this field's location type at the top of the list
- $tmpIdList = $typeOptions['values'];
- $defaultTypeId = [$thisTypeId => CRM_Utils_Array::value($thisTypeId, $tmpIdList)];
- unset($tmpIdList[$thisTypeId]);
-
- // Add the element
- $elements[] = [
- 'select',
- "location_blocks[$blockName][$count][typeTypeId]",
- NULL,
- $defaultTypeId + $tmpIdList,
- ];
-
- // Add the information to the migrationInfo
- $migrationInfo['location_blocks'][$blockName][$count]['typeTypeId'] = $thisTypeId;
-
- }
-
- // Set the label for this row
- $rowTitle = $blockInfo['label'] . ' ' . ($count + 1);
- if (!empty($thisLocId)) {
- $rowTitle .= ' (' . $locationOptions['values'][$thisLocId] . ')';
- }
- if (!empty($thisTypeId)) {
- $rowTitle .= ' (' . $typeOptions['values'][$thisTypeId] . ')';
- }
- $rows["move_location_{$blockName}_$count"]['title'] = $rowTitle;
-
- } // End loop through 'other' locations of this type
-
- } // End if 'other' location for this type exists
-
+ list($locations, $rows, $elements, $migrationInfo) = self::addLocationFieldInfo($mainId, $otherId, $blockInfo, $blockName, $locations, $rows, $elements, $migrationInfo);
} // End loop through each location block entity
// add the related tables and unset the ones that don't sport any of the duplicate contact's info
- $config = CRM_Core_Config::singleton();
- $mainUfId = CRM_Core_BAO_UFMatch::getUFId($mainId);
- $mainUser = NULL;
- if ($mainUfId) {
- // d6 compatible
- if ($config->userSystem->is_drupal == '1' && function_exists($mainUser)) {
- $mainUser = user_load($mainUfId);
- }
- elseif ($config->userFramework == 'Joomla') {
- $mainUser = JFactory::getUser($mainUfId);
- }
- }
- $otherUfId = CRM_Core_BAO_UFMatch::getUFId($otherId);
- $otherUser = NULL;
- if ($otherUfId) {
- // d6 compatible
- if ($config->userSystem->is_drupal == '1' && function_exists($mainUser)) {
- $otherUser = user_load($otherUfId);
- }
- elseif ($config->userFramework == 'Joomla') {
- $otherUser = JFactory::getUser($otherUfId);
- }
- }
-
$mergeHandler = new CRM_Dedupe_MergeHandler((int) $mainId, (int) $otherId);
$relTables = $mergeHandler->getTablesRelatedToTheMergePair();
foreach ($relTables as $name => $null) {
$relTables[$name]['main_url'] = str_replace('$cid', $mainId, $relTables[$name]['url']);
$relTables[$name]['other_url'] = str_replace('$cid', $otherId, $relTables[$name]['url']);
- if ($name == 'rel_table_users') {
- $relTables[$name]['main_url'] = str_replace('%ufid', $mainUfId, $relTables[$name]['url']);
- $relTables[$name]['other_url'] = str_replace('%ufid', $otherUfId, $relTables[$name]['url']);
- $find = ['$ufid', '$ufname'];
- if ($mainUser) {
- $replace = [$mainUfId, $mainUser->name];
- $relTables[$name]['main_title'] = str_replace($find, $replace, $relTables[$name]['title']);
- }
- if ($otherUser) {
- $replace = [$otherUfId, $otherUser->name];
- $relTables[$name]['other_title'] = str_replace($find, $replace, $relTables[$name]['title']);
- }
+ if ($name === 'rel_table_users') {
+ // @todo - this user url stuff is only needed for the form layer - move to CRM_Contact_Form_Merge
+ $relTables[$name]['main_url'] = str_replace('%ufid', CRM_Core_BAO_UFMatch::getUFId($otherId), $relTables[$name]['url']);
+ $relTables[$name]['other_url'] = str_replace('%ufid', CRM_Core_BAO_UFMatch::getUFId($otherId), $relTables[$name]['url']);
}
if ($name == 'rel_table_memberships') {
//Enable 'add new' checkbox if main contact does not contain any membership similar to duplicate contact.
);
foreach ($otherTree as $gid => $group) {
- $foundField = FALSE;
if (!isset($group['fields'])) {
continue;
}
foreach ($group['fields'] as $fid => $field) {
+ $mainContactValue = $mainTree[$gid]['fields'][$fid]['customValue'] ?? NULL;
+ $otherContactValue = $otherTree[$gid]['fields'][$fid]['customValue'] ?? NULL;
if (in_array($fid, $compareFields['custom'])) {
- if (!$foundField) {
- $rows["custom_group_$gid"]['title'] = $group['title'];
- $foundField = TRUE;
- }
- if (!empty($mainTree[$gid]['fields'][$fid]['customValue'])) {
- foreach ($mainTree[$gid]['fields'][$fid]['customValue'] as $valueId => $values) {
+ $rows["custom_group_$gid"]['title'] = $rows["custom_group_$gid"]['title'] ?? $group['title'];
+
+ if ($mainContactValue) {
+ foreach ($mainContactValue as $valueId => $values) {
$rows["move_custom_$fid"]['main'] = CRM_Core_BAO_CustomField::displayValue($values['data'], $fid);
}
}
- $value = "null";
- if (!empty($otherTree[$gid]['fields'][$fid]['customValue'])) {
- foreach ($otherTree[$gid]['fields'][$fid]['customValue'] as $valueId => $values) {
+ $value = 'null';
+ if ($otherContactValue) {
+ foreach ($otherContactValue as $valueId => $values) {
$rows["move_custom_$fid"]['other'] = CRM_Core_BAO_CustomField::displayValue($values['data'], $fid);
if ($values['data'] === 0 || $values['data'] === '0') {
$values['data'] = $qfZeroBug;
$rows["move_custom_$fid"]['title'] = $field['label'];
$elements[] = [
- 'advcheckbox',
- "move_custom_$fid",
- NULL,
- NULL,
- NULL,
- $value,
+ 0 => 'advcheckbox',
+ 1 => "move_custom_$fid",
+ 2 => NULL,
+ 3 => NULL,
+ 4 => NULL,
+ 5 => $value,
+ 'is_checked' => (!isset($rows["move_custom_$fid"]['main']) || $rows["move_custom_$fid"]['main'] === ''),
];
$migrationInfo["move_custom_$fid"] = $value;
}
// allow hook to override / manipulate migrationInfo as well
$migrationInfo = $migrationData['migration_info'];
foreach ($conflicts as $key => $val) {
- if ($val !== NULL || $mode !== 'safe') {
- // copy over the resolved values
- $migrationInfo[$key] = $val;
- unset($conflicts[$key]);
- }
+ // Copy over the resolved values. If we are in aggressive mode we update to null
+ // so as not to copy over. Why it's different to safe mode is a bit murky.
+ // Working theory is it doesn't matter what we do in safe mode here if $val is NULL.
+ // as the merge is not gonna happen if $val == NULL
+ $migrationInfo[$key] = $val ?? ($mode === 'safe' ? $migrationInfo[$key] : NULL);
}
- return self::formatConflictArray($conflicts, $migrationInfo['rows'], $migrationInfo['main_details']['location_blocks'], $migrationInfo['other_details']['location_blocks'], $mainId, $otherId);
+ return self::formatConflictArray($conflicts, $migrationInfo['rows'], $migrationInfo['main_details']['location_blocks'], $migrationInfo['other_details']['location_blocks'], $mainId, $otherId, $mode);
}
/**
* @param $toRemoveContactLocationBlocks
* @param $toKeepID
* @param $toRemoveID
+ * @param string $mode
*
* @return mixed
* @throws \CRM_Core_Exception
*/
- protected static function formatConflictArray($conflicts, $migrationInfo, $toKeepContactLocationBlocks, $toRemoveContactLocationBlocks, $toKeepID, $toRemoveID) {
+ protected static function formatConflictArray($conflicts, $migrationInfo, $toKeepContactLocationBlocks, $toRemoveContactLocationBlocks, $toKeepID, $toRemoveID, $mode) {
$return = [];
+ $resolved = [];
+ foreach ($conflicts as $key => $val) {
+ if ($val !== NULL) {
+ // copy over the resolved values
+ $resolved[$key] = $val;
+ unset($conflicts[$key]);
+ }
+ elseif ($mode === 'aggressive') {
+ unset($conflicts[$key]);
+ if (strpos($key, 'move_location_') !== 0) {
+ // @todo - just handling plain contact fields for now because I think I need a bigger refactor
+ // of the below to handle locations & will do as a follow up.
+ $resolved['contact'][substr($key, 5)] = $migrationInfo[$key]['main'];
+ }
+ }
+ }
foreach (array_keys($conflicts) as $index) {
if (substr($index, 0, 14) === 'move_location_') {
$parts = explode('_', $index);
throw new CRM_Core_Exception(ts('Unknown parameter') . $index);
}
}
- return $return;
+ return ['conflicts' => $return, 'resolved' => $resolved];
}
/**
}
}
+ /**
+ * @param $mainId
+ * @param $otherId
+ * @param $blockInfo
+ * @param $blockName
+ * @param array $locations
+ * @param array $rows
+ * @param array $elements
+ * @param array $migrationInfo
+ *
+ * @return array
+ * @throws \CiviCRM_API3_Exception
+ */
+ protected static function addLocationFieldInfo($mainId, $otherId, $blockInfo, $blockName, array $locations, array $rows, array $elements, array $migrationInfo): array {
+ // Collect existing fields from both 'main' and 'other' contacts first
+ // This allows us to match up location/types when building the table rows
+ $locations['main'][$blockName] = self::buildLocationBlockForContact($mainId, $blockInfo, $blockName);
+ $locations['other'][$blockName] = self::buildLocationBlockForContact($otherId, $blockInfo, $blockName);
+
+ // Now, build the table rows appropriately, based off the information on
+ // the 'other' contact
+ if (!empty($locations['other']) && !empty($locations['other'][$blockName])) {
+ foreach ($locations['other'][$blockName] as $count => $value) {
+
+ $displayValue = $value[$blockInfo['displayField']];
+
+ // Add this value to the table rows
+ $rows["move_location_{$blockName}_{$count}"]['other'] = $displayValue;
+
+ // CRM-17556 Only display 'main' contact value if it's the same location + type
+ // Look it up from main values...
+
+ $lookupLocation = FALSE;
+ if ($blockInfo['hasLocation']) {
+ $lookupLocation = $value['location_type_id'];
+ }
+
+ $lookupType = FALSE;
+ if ($blockInfo['hasType']) {
+ $lookupType = CRM_Utils_Array::value($blockInfo['hasType'], $value);
+ }
+
+ // Hold ID of main contact's matching block
+ $mainContactBlockId = 0;
+
+ if (!empty($locations['main'][$blockName])) {
+ foreach ($locations['main'][$blockName] as $mainValueCheck) {
+ // No location/type, or matching location and type
+ if (
+ (empty($lookupLocation) || $lookupLocation == $mainValueCheck['location_type_id'])
+ && (empty($lookupType) || $lookupType == $mainValueCheck[$blockInfo['hasType']])
+ ) {
+ // Set this value as the default against the 'other' contact value
+ $rows["move_location_{$blockName}_{$count}"]['main'] = $mainValueCheck[$blockInfo['displayField']];
+ $rows["move_location_{$blockName}_{$count}"]['main_is_primary'] = $mainValueCheck['is_primary'];
+ $rows["move_location_{$blockName}_{$count}"]['location_entity'] = $blockName;
+ $mainContactBlockId = $mainValueCheck['id'];
+ break;
+ }
+ }
+ }
+
+ // Add checkbox to migrate data from 'other' to 'main'
+ $elements[] = ['advcheckbox', "move_location_{$blockName}_{$count}"];
+
+ // Add checkbox to set the 'other' location as primary
+ $elements[] = [
+ 'advcheckbox',
+ "location_blocks[$blockName][$count][set_other_primary]",
+ NULL,
+ ts('Set as primary'),
+ ];
+
+ // Flag up this field to skipMerge function (@todo: do we need to?)
+ $migrationInfo["move_location_{$blockName}_{$count}"] = 1;
+
+ // Add a hidden field to store the ID of the target main contact block
+ $elements[] = [
+ 'hidden',
+ "location_blocks[$blockName][$count][mainContactBlockId]",
+ $mainContactBlockId,
+ ];
+
+ // Setup variables
+ $thisTypeId = FALSE;
+ $thisLocId = FALSE;
+
+ // Provide a select drop-down for the location's location type
+ // eg: Home, Work...
+
+ if ($blockInfo['hasLocation']) {
+
+ // Load the location options for this entity
+ $locationOptions = civicrm_api3($blockName, 'getoptions', ['field' => 'location_type_id']);
+
+ $thisLocId = $value['location_type_id'];
+
+ // Put this field's location type at the top of the list
+ $tmpIdList = $locationOptions['values'];
+ $defaultLocId = [$thisLocId => $tmpIdList[$thisLocId]];
+ unset($tmpIdList[$thisLocId]);
+
+ // Add the element
+ $elements[] = [
+ 'select',
+ "location_blocks[$blockName][$count][locTypeId]",
+ NULL,
+ $defaultLocId + $tmpIdList,
+ ];
+
+ // Add the relevant information to the $migrationInfo
+ // Keep location-type-id same as that of other-contact
+ // @todo Check this logic out
+ $migrationInfo['location_blocks'][$blockName][$count]['locTypeId'] = $thisLocId;
+ if ($blockName != 'address') {
+ $elements[] = [
+ 'advcheckbox',
+ "location_blocks[{$blockName}][$count][operation]",
+ NULL,
+ ts('Add new'),
+ ];
+ // always use add operation
+ $migrationInfo['location_blocks'][$blockName][$count]['operation'] = 1;
+ }
+
+ }
+
+ // Provide a select drop-down for the location's type/provider
+ // eg websites: Facebook...
+
+ if ($blockInfo['hasType']) {
+
+ // Load the type options for this entity
+ $typeOptions = civicrm_api3($blockName, 'getoptions', ['field' => $blockInfo['hasType']]);
+
+ $thisTypeId = CRM_Utils_Array::value($blockInfo['hasType'], $value);
+
+ // Put this field's location type at the top of the list
+ $tmpIdList = $typeOptions['values'];
+ $defaultTypeId = [$thisTypeId => CRM_Utils_Array::value($thisTypeId, $tmpIdList)];
+ unset($tmpIdList[$thisTypeId]);
+
+ // Add the element
+ $elements[] = [
+ 'select',
+ "location_blocks[$blockName][$count][typeTypeId]",
+ NULL,
+ $defaultTypeId + $tmpIdList,
+ ];
+
+ // Add the information to the migrationInfo
+ $migrationInfo['location_blocks'][$blockName][$count]['typeTypeId'] = $thisTypeId;
+
+ }
+
+ // Set the label for this row
+ $rowTitle = $blockInfo['label'] . ' ' . ($count + 1);
+ if (!empty($thisLocId)) {
+ $rowTitle .= ' (' . $locationOptions['values'][$thisLocId] . ')';
+ }
+ if (!empty($thisTypeId)) {
+ $rowTitle .= ' (' . $typeOptions['values'][$thisTypeId] . ')';
+ }
+ $rows["move_location_{$blockName}_$count"]['title'] = $rowTitle;
+
+ } // End loop through 'other' locations of this type
+
+ }
+ return [$locations, $rows, $elements, $migrationInfo];
+ }
+
}