Skip to content

Commit

Permalink
dev/core#4836 Fix mishandling of multiple fields with serialize
Browse files Browse the repository at this point in the history
  • Loading branch information
eileenmcnaughton committed Jan 27, 2024
1 parent 3d03103 commit 3cd6ea3
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 116 deletions.
128 changes: 15 additions & 113 deletions CRM/Custom/Import/Parser/Api.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
*/
class CRM_Custom_Import_Parser_Api extends CRM_Import_Parser {

protected $_fields = [];
protected $_multipleCustomData = '';

/**
* Get information about the provided job.
*
Expand Down Expand Up @@ -35,39 +32,18 @@ public static function getUserJobInfo(): array {
* @param array $values
* The array of values belonging to this line.
*/
public function import($values) {
public function import(array $values): void {
$rowNumber = (int) $values[array_key_last($values)];
try {
$params = $this->getMappedRow($values);
$formatted = [];
foreach ($params as $key => $value) {
if ($value !== '') {
$formatted[$key] = $value;
}
}

if (isset($params['external_identifier']) && !isset($params['contact_id'])) {
$checkCid = new CRM_Contact_DAO_Contact();
$checkCid->external_identifier = $params['external_identifier'];
$checkCid->find(TRUE);
$formatted['id'] = $checkCid->id;
}
else {
$formatted['id'] = $params['contact_id'];
}

$this->formatCommonData($params, $formatted);
foreach ($formatted['custom'] as $key => $val) {
$params['custom_' . $key] = $val[-1]['value'];
}
$params['skipRecentView'] = TRUE;
$params['check_permissions'] = TRUE;
$params['entity_id'] = $formatted['id'];
civicrm_api3('custom_value', 'create', $params);
$this->setImportStatus($rowNumber, 'IMPORTED', '', $formatted['id']);
$params['entity_id'] = $params['contact_id'];
civicrm_api3('CustomValue', 'create', $params);
$this->setImportStatus($rowNumber, 'IMPORTED', '', $params['contact_id']);
}
catch (CRM_Core_Exception $e) {
$this->setImportStatus($rowNumber, 'ERROR', $e->getMessage(), $formatted['id']);
$this->setImportStatus($rowNumber, 'ERROR', $e->getMessage(), $params['contact_id'] ?? NULL);
}
}

Expand All @@ -76,7 +52,7 @@ public function import($values) {
*/
public function setFieldMetadata(): void {
if (!$this->importableFieldsMetadata) {
$customGroupID = $this->getSubmittedValue('multipleCustomData');
$customGroupID = $this->getCustomGroupID();
$importableFields = $this->getGroupFieldsForImport($customGroupID);
$this->importableFieldsMetadata = array_merge([
'do_not_import' => ['title' => ts('- do not import -')],
Expand All @@ -95,87 +71,13 @@ public function getRequiredFields(): array {
return [['contact_id'], ['external_identifier']];
}

/**
* Adapted from CRM_Contact_Import_Parser_Contact::formatCommonData
*
* TODO: Is this function even necessary? All values get passed to the api anyway.
*
* @param array $params
* Contain record values.
* @param array $formatted
* Array of formatted data.
*/
private function formatCommonData($params, &$formatted) {

$customFields = CRM_Core_BAO_CustomField::getFields(NULL);

//now format custom data.
foreach ($params as $key => $field) {

if ($key == 'id' && isset($field)) {
$formatted[$key] = $field;
}

//Handling Custom Data
if (($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) &&
array_key_exists($customFieldID, $customFields)
) {

$extends = $customFields[$customFieldID]['extends'] ?? NULL;
$htmlType = $customFields[$customFieldID]['html_type'] ?? NULL;
$dataType = $customFields[$customFieldID]['data_type'] ?? NULL;
$serialized = CRM_Core_BAO_CustomField::isSerialized($customFields[$customFieldID]);

if (!$serialized && in_array($htmlType, ['Select', 'Radio', 'Autocomplete-Select']) && in_array($dataType, ['String', 'Int'])) {
$customOption = CRM_Core_BAO_CustomOption::getCustomOption($customFieldID, TRUE);
foreach ($customOption as $customValue) {
$val = $customValue['value'] ?? NULL;
$label = strtolower($customValue['label'] ?? '');
$value = strtolower(trim($formatted[$key]));
if (($value == $label) || ($value == strtolower($val))) {
$params[$key] = $formatted[$key] = $val;
}
}
}
elseif ($serialized && !empty($formatted[$key]) && !empty($params[$key])) {
$mulValues = explode(',', $formatted[$key]);
$customOption = CRM_Core_BAO_CustomOption::getCustomOption($customFieldID, TRUE);
$formatted[$key] = [];
$params[$key] = [];
foreach ($mulValues as $v1) {
foreach ($customOption as $v2) {
if ((strtolower($v2['label']) == strtolower(trim($v1))) ||
(strtolower($v2['value']) == strtolower(trim($v1)))
) {
if ($htmlType === 'CheckBox') {
$params[$key][$v2['value']] = $formatted[$key][$v2['value']] = 1;
}
else {
$params[$key][] = $formatted[$key][] = $v2['value'];
}
}
}
}
}
}
}

if (!empty($key) && ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) && array_key_exists($customFieldID, $customFields)) {
// @todo calling api functions directly is not supported
_civicrm_api3_custom_format_params($params, $formatted, $extends);
}
}

/**
* Return the field ids and names (with groups) for import purpose.
*
* @param int $customGroupID
* Custom group ID.
*
* @return array
*
* @noinspection PhpDocMissingThrowsInspection
* @noinspection PhpUnhandledExceptionInspection
*/
private function getGroupFieldsForImport(int $customGroupID): array {
$importableFields = [];
Expand All @@ -188,26 +90,26 @@ private function getGroupFieldsForImport(int $customGroupID): array {
/* generate the key for the fields array */
$key = 'custom_' . $values['id'];
$regexp = preg_replace('/[.,;:!?]/', '', $values['label']);
$importableFields[$key] = [
$importableFields[$key] = array_merge($values, [
'name' => $key,
'title' => $values['label'] ?? NULL,
'headerPattern' => '/' . preg_quote($regexp, '/') . '/i',
'import' => 1,
'custom_field_id' => $values['id'],
'options_per_line' => $values['options_per_line'],
'data_type' => $values['data_type'],
'html_type' => $values['html_type'],
'type' => CRM_Core_BAO_CustomField::dataToType()[$values['data_type']],
'is_search_range' => $values['is_search_range'],
'date_format' => $values['date_format'],
'time_format' => $values['time_format'],
'extends' => $customGroup['extends'],
'custom_group_id' => $customGroupID,
'custom_group_id.name' => $customGroup['name'],
'is_multiple' => $customGroup['is_multiple'],
];
]);
}
return $importableFields;
}

/**
* @return int
*/
private function getCustomGroupID(): int {
return (int) $this->getSubmittedValue('multipleCustomData');
}

}
9 changes: 6 additions & 3 deletions tests/phpunit/CRM/Custom/Import/Parser/ApiTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ class CRM_Custom_Import_Parser_ApiTest extends CiviUnitTestCase {
*/
public function testImport(): void {
$this->individualCreate();
$this->createCustomGroupWithFieldOfType(['is_multiple' => TRUE, 'extends' => 'Contact'], 'select', 'level');
$this->createCustomGroupWithFieldOfType(['is_multiple' => TRUE, 'extends' => 'Contact'], 'select', 'level', ['serialize' => 1]);

$customGroupID = $this->ids['CustomGroup']['level'];
$dateFieldID = $this->createDateCustomField(['date_format' => 'yy', 'custom_group_id' => $customGroupID])['id'];
$this->importCSV('custom_data_date_select.csv', [
Expand All @@ -34,11 +35,13 @@ public function testImport(): void {
], ['multipleCustomData' => $customGroupID]);
$dataSource = new CRM_Import_DataSource_CSV($this->userJobID);
$row = $dataSource->getRow();
$this->assertEquals('IMPORTED', $row['_status']);
$this->assertEquals('IMPORTED', $row['_status'], $row['_status_message']);
$row = $dataSource->getRow();
$this->assertEquals('IMPORTED', $row['_status']);
$this->assertEquals('IMPORTED', $row['_status'], $row['_status_message']);
$row = $dataSource->getRow();
$this->assertEquals('ERROR', $row['_status']);
$row = $dataSource->getRow();
$this->assertEquals('IMPORTED', $row['_status'], $row['_status_message']);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ The Contact ID,The Level,The School,The Start Year
3,Red,Ah Ha,2010-12-01
3,R,Bah bah,2022-01-02
3,Aqua,Cah cah,2022-02-03
3,"Red,Yellow ",Hogwarts ,2023-02-03

0 comments on commit 3cd6ea3

Please sign in to comment.