Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CRM-21773 Fix merging multi-value custom fields #11691

Merged
merged 3 commits into from
Mar 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 35 additions & 23 deletions CRM/Dedupe/Merger.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,25 +39,25 @@ class CRM_Dedupe_Merger {
* @return array
*/
public static function relTables() {
static $relTables;

// Setting these merely prevents enotices - but it may be more appropriate not to add the user table below
// if the url can't be retrieved. A more standardised way to retrieve them is.
// CRM_Core_Config::singleton()->userSystem->getUserRecordUrl() - however that function takes a contact_id &
// we may need a different function when it is not known.
$title = $userRecordUrl = '';
if (!isset(Civi::$statics[__CLASS__]['relTables'])) {

$config = CRM_Core_Config::singleton();
if ($config->userSystem->is_drupal) {
$userRecordUrl = CRM_Utils_System::url('user/%ufid');
$title = ts('%1 User: %2; user id: %3', array(1 => $config->userFramework, 2 => '$ufname', 3 => '$ufid'));
}
elseif ($config->userFramework == 'Joomla') {
$userRecordUrl = $config->userSystem->getVersion() > 1.5 ? $config->userFrameworkBaseURL . "index.php?option=com_users&view=user&task=user.edit&id=" . '%ufid' : $config->userFrameworkBaseURL . "index2.php?option=com_users&view=user&task=edit&id[]=" . '%ufid';
$title = ts('%1 User: %2; user id: %3', array(1 => $config->userFramework, 2 => '$ufname', 3 => '$ufid'));
}
// Setting these merely prevents enotices - but it may be more appropriate not to add the user table below
// if the url can't be retrieved. A more standardised way to retrieve them is.
// CRM_Core_Config::singleton()->userSystem->getUserRecordUrl() - however that function takes a contact_id &
// we may need a different function when it is not known.
$title = $userRecordUrl = '';

$config = CRM_Core_Config::singleton();
if ($config->userSystem->is_drupal) {
$userRecordUrl = CRM_Utils_System::url('user/%ufid');
$title = ts('%1 User: %2; user id: %3', array(1 => $config->userFramework, 2 => '$ufname', 3 => '$ufid'));
}
elseif ($config->userFramework == 'Joomla') {
$userRecordUrl = $config->userSystem->getVersion() > 1.5 ? $config->userFrameworkBaseURL . "index.php?option=com_users&view=user&task=user.edit&id=" . '%ufid' : $config->userFrameworkBaseURL . "index2.php?option=com_users&view=user&task=edit&id[]=" . '%ufid';
$title = ts('%1 User: %2; user id: %3', array(1 => $config->userFramework, 2 => '$ufname', 3 => '$ufid'));
}

if (!$relTables) {
$relTables = array(
'rel_table_contributions' => array(
'title' => ts('Contributions'),
Expand Down Expand Up @@ -155,8 +155,12 @@ public static function relTables() {

// Allow hook_civicrm_merge() to adjust $relTables
CRM_Utils_Hook::merge('relTables', $relTables);

// Cache the results in a static variable
Civi::$statics[__CLASS__]['relTables'] = $relTables;
}
return $relTables;

return Civi::$statics[__CLASS__]['relTables'];
}

/**
Expand Down Expand Up @@ -273,11 +277,12 @@ public static function locTables() {
* We treat multi-valued custom sets as "related tables" similar to activities, contributions, etc.
* @param string $request
* 'relTables' or 'cidRefs'.
* @return array
* @see CRM-13836
*/
public static function getMultiValueCustomSets($request) {
static $data = NULL;
if ($data === NULL) {

if (!isset(Civi::$statics[__CLASS__]['multiValueCustomSets'])) {
$data = array(
'relTables' => array(),
'cidRefs' => array(),
Expand All @@ -296,8 +301,12 @@ public static function getMultiValueCustomSets($request) {
'url' => CRM_Utils_System::url('civicrm/contact/view', 'reset=1&force=1&cid=$cid' . $urlSuffix),
);
}

// Store the result in a static variable cache
Civi::$statics[__CLASS__]['multiValueCustomSets'] = $data;
}
return $data[$request];

return Civi::$statics[__CLASS__]['multiValueCustomSets'][$request];
}

/**
Expand Down Expand Up @@ -467,12 +476,15 @@ public static function moveContactBelongings($mainId, $otherId, $tables = FALSE,

$mainId = (int) $mainId;
$otherId = (int) $otherId;
$multi_value_tables = array_keys(CRM_Dedupe_Merger::getMultiValueCustomSets('cidRefs'));

$sqls = array();
foreach ($affected as $table) {
// skipping non selected custom table's value migration
if ($customTableToCopyFrom !== NULL && in_array($table, $customTables) && !in_array($table, $customTableToCopyFrom)) {
continue;
// skipping non selected single-value custom table's value migration
if (!in_array($table, $multi_value_tables)) {
if ($customTableToCopyFrom !== NULL && in_array($table, $customTables) && !in_array($table, $customTableToCopyFrom)) {
continue;
}
}

// Call custom processing function for objects that require it
Expand Down
29 changes: 29 additions & 0 deletions tests/phpunit/CRM/Dedupe/MergerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,14 @@ public function testMigrationOfUnselectedCustomDataOnEmptyCustomRecord() {
$createGroup = $this->setupCustomGroupForIndividual();
$customField1 = $this->setupCustomField('TestField', $createGroup);

// Create multi-value custom field
$multiGroup = $this->CustomGroupMultipleCreateByParams();
$multiField = $this->customFieldCreate(array(
'custom_group_id' => $multiGroup['id'],
'label' => 'field_1' . $multiGroup['id'],
'in_selector' => 1,
));

// Contacts setup
$this->setupMatchData();
$originalContactID = $this->contacts[0]['id'];
Expand All @@ -558,17 +566,24 @@ public function testMigrationOfUnselectedCustomDataOnEmptyCustomRecord() {
$this->callAPISuccess('Contact', 'create', array(
'id' => $duplicateContactID,
"custom_{$customField1['id']}" => 'abc',
"custom_{$multiField['id']}" => 'def',
));
$this->assertCustomFieldValue($duplicateContactID, 'abc', "custom_{$customField1['id']}");
$this->assertCustomFieldValue($duplicateContactID, 'def', "custom_{$multiField['id']}");

// Merge, and ensure that no value was migrated
$this->mergeContacts($originalContactID, $duplicateContactID, array(
"move_custom_{$customField1['id']}" => NULL,
"move_rel_table_custom_{$multiGroup['id']}" => NULL,
));
$this->assertCustomFieldValue($originalContactID, '', "custom_{$customField1['id']}");
$this->assertCustomFieldValue($originalContactID, '', "custom_{$multiField['id']}");

// cleanup created custom set
$this->callAPISuccess('CustomField', 'delete', array('id' => $customField1['id']));
$this->callAPISuccess('CustomGroup', 'delete', array('id' => $createGroup['id']));
$this->callAPISuccess('CustomField', 'delete', array('id' => $multiField['id']));
$this->callAPISuccess('CustomGroup', 'delete', array('id' => $multiGroup['id']));
}

/**
Expand All @@ -583,6 +598,14 @@ public function testMigrationOfSomeCustomDataOnEmptyCustomRecord() {
$customField1 = $this->setupCustomField('Test1', $createGroup);
$customField2 = $this->setupCustomField('Test2', $createGroup);

// Create multi-value custom field
$multiGroup = $this->CustomGroupMultipleCreateByParams();
$multiField = $this->customFieldCreate(array(
'custom_group_id' => $multiGroup['id'],
'label' => 'field_1' . $multiGroup['id'],
'in_selector' => 1,
));

// Contacts setup
$this->setupMatchData();
$originalContactID = $this->contacts[0]['id'];
Expand All @@ -593,22 +616,28 @@ public function testMigrationOfSomeCustomDataOnEmptyCustomRecord() {
'id' => $duplicateContactID,
"custom_{$customField1['id']}" => 'abc',
"custom_{$customField2['id']}" => 'def',
"custom_{$multiField['id']}" => 'ghi',
));
$this->assertCustomFieldValue($duplicateContactID, 'abc', "custom_{$customField1['id']}");
$this->assertCustomFieldValue($duplicateContactID, 'def', "custom_{$customField2['id']}");
$this->assertCustomFieldValue($duplicateContactID, 'ghi', "custom_{$multiField['id']}");

// Perform merge
$this->mergeContacts($originalContactID, $duplicateContactID, array(
"move_custom_{$customField1['id']}" => NULL,
"move_custom_{$customField2['id']}" => 'def',
"move_rel_table_custom_{$multiGroup['id']}" => '1',
));
$this->assertCustomFieldValue($originalContactID, '', "custom_{$customField1['id']}");
$this->assertCustomFieldValue($originalContactID, 'def', "custom_{$customField2['id']}");
$this->assertCustomFieldValue($originalContactID, 'ghi', "custom_{$multiField['id']}");

// cleanup created custom set
$this->callAPISuccess('CustomField', 'delete', array('id' => $customField1['id']));
$this->callAPISuccess('CustomField', 'delete', array('id' => $customField2['id']));
$this->callAPISuccess('CustomGroup', 'delete', array('id' => $createGroup['id']));
$this->callAPISuccess('CustomField', 'delete', array('id' => $multiField['id']));
$this->callAPISuccess('CustomGroup', 'delete', array('id' => $multiGroup['id']));
}

/**
Expand Down