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

dev/core#4836 Fix mishandling of multiple fields with serialize #29116

Merged
merged 1 commit into from
Jan 28, 2024
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
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