From f54e87d9cf1e216036adf92f744cdbac3c1fe79e Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Mon, 3 Jan 2022 16:53:29 -0500 Subject: [PATCH] Import - Fix multi-record custom data import PHP errors Fixes fatal PHP error `Declaration of CRM_Custom_Import_Parser::run(...) should be compatible with CRM_Contact_Import_Parser::run(...)` by switching to inherit the base class instead of the contact import class. This simplifies the code in general. --- CRM/Custom/Import/Form/DataSource.php | 7 +- CRM/Custom/Import/Parser.php | 31 ++++++-- CRM/Custom/Import/Parser/Api.php | 105 +++++++++++++++++++++----- CRM/Import/Parser.php | 2 +- 4 files changed, 117 insertions(+), 28 deletions(-) diff --git a/CRM/Custom/Import/Form/DataSource.php b/CRM/Custom/Import/Form/DataSource.php index d36cb68fd5..1fcc6b2323 100644 --- a/CRM/Custom/Import/Form/DataSource.php +++ b/CRM/Custom/Import/Form/DataSource.php @@ -35,9 +35,10 @@ class CRM_Custom_Import_Form_DataSource extends CRM_Import_Form_DataSource { 'multipleCustomData' => $this->_id, ]; - if ($loadeMapping = $this->get('loadedMapping')) { - $this->assign('loadedMapping', $loadeMapping); - $defaults['savedMapping'] = $loadeMapping; + $loadedMapping = $this->get('loadedMapping'); + $this->assign('loadedMapping', $loadedMapping); + if ($loadedMapping) { + $defaults['savedMapping'] = $loadedMapping; } return $defaults; diff --git a/CRM/Custom/Import/Parser.php b/CRM/Custom/Import/Parser.php index 0ee0ccf2e0..68036ecb96 100644 --- a/CRM/Custom/Import/Parser.php +++ b/CRM/Custom/Import/Parser.php @@ -14,7 +14,7 @@ * @package CRM * @copyright CiviCRM LLC https://civicrm.org/licensing */ -abstract class CRM_Custom_Import_Parser extends CRM_Contact_Import_Parser { +abstract class CRM_Custom_Import_Parser extends CRM_Import_Parser { protected $_fileName; @@ -58,7 +58,7 @@ abstract class CRM_Custom_Import_Parser extends CRM_Contact_Import_Parser { */ public function run( $fileName, - $separator = ',', + $separator, &$mapper, $skipColumnHeader = FALSE, $mode = self::MODE_PREVIEW, @@ -167,7 +167,7 @@ abstract class CRM_Custom_Import_Parser extends CRM_Contact_Import_Parser { if ($returnCode & self::WARNING) { $this->_warningCount++; if ($this->_warningCount < $this->_maxWarningCount) { - $this->_warningCount[] = $line; + $this->_warnings[] = $this->_lineCount; } } @@ -238,7 +238,7 @@ abstract class CRM_Custom_Import_Parser extends CRM_Contact_Import_Parser { ts('Reason'), ], $customHeaders); $this->_errorFileName = self::errorFileName(self::ERROR); - self::exportCSV($this->_errorFileName, $headers, $this->_errors); + CRM_Contact_Import_Parser::exportCSV($this->_errorFileName, $headers, $this->_errors); } if ($this->_conflictCount) { $headers = array_merge([ @@ -246,7 +246,7 @@ abstract class CRM_Custom_Import_Parser extends CRM_Contact_Import_Parser { ts('Reason'), ], $customHeaders); $this->_conflictFileName = self::errorFileName(self::CONFLICT); - self::exportCSV($this->_conflictFileName, $headers, $this->_conflicts); + CRM_Contact_Import_Parser::exportCSV($this->_conflictFileName, $headers, $this->_conflicts); } if ($this->_duplicateCount) { $headers = array_merge([ @@ -255,7 +255,7 @@ abstract class CRM_Custom_Import_Parser extends CRM_Contact_Import_Parser { ], $customHeaders); $this->_duplicateFileName = self::errorFileName(self::DUPLICATE); - self::exportCSV($this->_duplicateFileName, $headers, $this->_duplicates); + CRM_Contact_Import_Parser::exportCSV($this->_duplicateFileName, $headers, $this->_duplicates); } } return $this->fini(); @@ -356,4 +356,23 @@ abstract class CRM_Custom_Import_Parser extends CRM_Contact_Import_Parser { } } + /** + * @param string $name + * @param $title + * @param int $type + * @param string $headerPattern + * @param string $dataPattern + * @param bool $hasLocationType + */ + public function addField( + $name, $title, $type = CRM_Utils_Type::T_INT, + $headerPattern = '//', $dataPattern = '//', + $hasLocationType = FALSE + ) { + $this->_fields[$name] = new CRM_Custom_Import_Field($name, $title, $type, $headerPattern, $dataPattern, $hasLocationType); + if (empty($name)) { + $this->_fields['doNotImport'] = new CRM_Custom_Import_Field($name, $title, $type, $headerPattern, $dataPattern, $hasLocationType); + } + } + } diff --git a/CRM/Custom/Import/Parser/Api.php b/CRM/Custom/Import/Parser/Api.php index db3180adb7..bff037f88f 100644 --- a/CRM/Custom/Import/Parser/Api.php +++ b/CRM/Custom/Import/Parser/Api.php @@ -147,23 +147,16 @@ class CRM_Custom_Import_Parser_Api extends CRM_Custom_Import_Parser { public function import($onDuplicate, &$values) { $response = $this->summary($values); if ($response != CRM_Import_Parser::VALID) { - $importRecordParams = [ - $statusFieldName => 'INVALID', - "${statusFieldName}Msg" => "Invalid (Error Code: $response)", - ]; return $response; } $this->_updateWithId = FALSE; $this->_parseStreetAddress = CRM_Utils_Array::value('street_address_parsing', CRM_Core_BAO_Setting::valueOptions(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'address_options'), FALSE); - $params = $this->getActiveFieldParams(); $contactType = $this->_contactType ? $this->_contactType : 'Organization'; $formatted = [ 'contact_type' => $contactType, ]; - $session = CRM_Core_Session::singleton(); - $dateType = $session->get('dateTypes'); if (isset($this->_params['external_identifier']) && !isset($this->_params['contact_id'])) { $checkCid = new CRM_Contact_DAO_Contact(); @@ -174,9 +167,8 @@ class CRM_Custom_Import_Parser_Api extends CRM_Custom_Import_Parser { else { $formatted['id'] = $this->_params['contact_id']; } - $setDateFields = array_intersect_key($this->_params, array_flip($this->_dateFields)); - $this->formatCommonData($this->_params, $formatted, $formatted); + $this->formatCommonData($this->_params, $formatted); foreach ($formatted['custom'] as $key => $val) { $this->_params['custom_' . $key] = $val[-1]['value']; } @@ -194,19 +186,96 @@ class CRM_Custom_Import_Parser_Api extends CRM_Custom_Import_Parser { } /** - * Format Date params. + * Adapted from CRM_Contact_Import_Parser::formatCommonData * - * Although the api will accept any strtotime valid string CiviCRM accepts at least one date format - * not supported by strtotime so we should run this through a conversion + * 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. */ - public function formatDateParams() { + public function formatCommonData($params, &$formatted) { + + $customFields = CRM_Core_BAO_CustomField::getFields(NULL); + + //format date first $session = CRM_Core_Session::singleton(); - $dateType = $session->get('dateTypes'); - $setDateFields = array_intersect_key($this->_params, array_flip($this->_dateFields)); + $dateType = $session->get("dateTypes"); + foreach ($params as $key => $val) { + $customFieldID = CRM_Core_BAO_CustomField::getKeyID($key); + if ($customFieldID) { + //we should not update Date to null, CRM-4062 + if ($val && ($customFields[$customFieldID]['data_type'] == 'Date')) { + //CRM-21267 + CRM_Contact_Import_Parser_Contact::formatCustomDate($params, $formatted, $dateType, $key); + } + elseif ($customFields[$customFieldID]['data_type'] == 'Boolean') { + if (empty($val) && !is_numeric($val)) { + //retain earlier value when Import mode is `Fill` + unset($params[$key]); + } + else { + $params[$key] = CRM_Utils_String::strtoboolstr($val); + } + } + } + } + + //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']; + } + } + } + } + } + } + } - foreach ($setDateFields as $key => $value) { - CRM_Utils_Date::convertToDefaultDate($this->_params, $dateType, $key); - $this->_params[$key] = CRM_Utils_Date::processDate($this->_params[$key]); + 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); } } diff --git a/CRM/Import/Parser.php b/CRM/Import/Parser.php index f49c095acb..68ef35a23d 100644 --- a/CRM/Import/Parser.php +++ b/CRM/Import/Parser.php @@ -442,7 +442,7 @@ abstract class CRM_Import_Parser { /** * Determines the file extension based on error code. * - * @var $type error code constant + * @var int $type error code constant * @return string */ public static function errorFileName($type) { -- 2.25.1