X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=CRM%2FActivity%2FImport%2FParser%2FActivity.php;h=cd186dbfb3186d8893e10824a6e83a0352974543;hb=7eebbdaa9a8ccca6b1abf42cbf4ebaf4f20a718a;hp=5e95f929eeb393f43ac5ce37c3c1e97b823d38e1;hpb=c819a4cc76c91ca51e038b0b5debaf95e94811dc;p=civicrm-core.git diff --git a/CRM/Activity/Import/Parser/Activity.php b/CRM/Activity/Import/Parser/Activity.php index 5e95f929ee..cd186dbfb3 100644 --- a/CRM/Activity/Import/Parser/Activity.php +++ b/CRM/Activity/Import/Parser/Activity.php @@ -23,8 +23,6 @@ class CRM_Activity_Import_Parser_Activity extends CRM_Import_Parser { protected $_mapperKeys; - private $_contactIdIndex; - /** * Array of successfully imported activity id's * @@ -32,33 +30,12 @@ class CRM_Activity_Import_Parser_Activity extends CRM_Import_Parser { */ protected $_newActivity; - protected $_fileName; - - /** - * Imported file size. - * @var int - */ - protected $_fileSize; - - /** - * Separator being used. - * @var string - */ - protected $_separator; - /** * Total number of lines in file. * @var int */ protected $_lineCount; - /** - * Whether the file has a column header or not. - * - * @var bool - */ - protected $_haveColumnHeader; - /** * Class constructor. * @@ -73,24 +50,9 @@ class CRM_Activity_Import_Parser_Activity extends CRM_Import_Parser { * The initializer code, called before the processing. */ public function init() { - $activityContact = CRM_Activity_BAO_ActivityContact::import(); - $activityTarget['target_contact_id'] = $activityContact['contact_id']; - $fields = array_merge(CRM_Activity_BAO_Activity::importableFields(), - $activityTarget - ); - - $fields = array_merge($fields, [ - 'source_contact_id' => [ - 'title' => ts('Source Contact'), - 'headerPattern' => '/Source.Contact?/i', - ], - 'activity_label' => [ - 'title' => ts('Activity Type Label'), - 'headerPattern' => '/(activity.)?type label?/i', - ], - ]); - - foreach ($fields as $name => $field) { + $this->setFieldMetadata(); + + foreach ($this->importableFieldsMetadata as $name => $field) { $field['type'] = CRM_Utils_Array::value('type', $field, CRM_Utils_Type::T_INT); $field['dataPattern'] = CRM_Utils_Array::value('dataPattern', $field, '//'); $field['headerPattern'] = CRM_Utils_Array::value('headerPattern', $field, '//'); @@ -103,20 +65,6 @@ class CRM_Activity_Import_Parser_Activity extends CRM_Import_Parser { $this->_newActivity = []; $this->setActiveFields($this->_mapperKeys); - - // FIXME: we should do this in one place together with Form/MapField.php - $this->_contactIdIndex = -1; - - $index = 0; - foreach ($this->_mapperKeys as $key) { - switch ($key) { - case 'target_contact_id': - case 'external_identifier': - $this->_contactIdIndex = $index; - break; - } - $index++; - } } /** @@ -143,137 +91,126 @@ class CRM_Activity_Import_Parser_Activity extends CRM_Import_Parser { /** * Handle the values in import mode. * - * @param int $onDuplicate - * The code for what action to take on duplicates. * @param array $values * The array of values belonging to this line. - * - * @return int - * CRM_Import_Parser::VALID for success or - * CRM_Import_Parser::ERROR for error. - * - * @throws \CRM_Core_Exception */ - public function import($onDuplicate, &$values) { + public function import($values) { + $rowNumber = (int) ($values[array_key_last($values)]); // First make sure this is a valid line try { - $this->validateValues($values); - } - catch (CRM_Core_Exception $e) { - return $this->addError($values, [$e->getMessage()]); - } - $params = $this->getApiReadyParams($values); - // For date-Formats. - $session = CRM_Core_Session::singleton(); - $dateType = $session->get('dateTypes'); + $params = $this->getMappedRow($values); - $customFields = CRM_Core_BAO_CustomField::getFields('Activity'); + if (empty($params['external_identifier']) && empty($params['target_contact_id'])) { - foreach ($params as $key => $val) { - if ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) { - if (!empty($customFields[$customFieldID]) && $customFields[$customFieldID]['data_type'] == 'Date') { - CRM_Contact_Import_Parser_Contact::formatCustomDate($params, $params, $dateType, $key); - } - elseif (!empty($customFields[$customFieldID]) && $customFields[$customFieldID]['data_type'] == 'Boolean') { - $params[$key] = CRM_Utils_String::strtoboolstr($val); - } - } - elseif ($key === 'activity_date_time') { - $params[$key] = CRM_Utils_Date::formatDate($val, $dateType); - } - elseif ($key === 'activity_subject') { - $params['subject'] = $val; - } - } + // Retrieve contact id using contact dedupe rule. + // Since we are supporting only individual's activity import. + $params['contact_type'] = 'Individual'; + $params['version'] = 3; + $matchedIDs = CRM_Contact_BAO_Contact::getDuplicateContacts($params, 'Individual'); - if ($this->_contactIdIndex < 0) { + if (!empty($matchedIDs)) { + if (count($matchedIDs) > 1) { + throw new CRM_Core_Exception('Multiple matching contact records detected for this row. The activity was not imported'); + } + $cid = $matchedIDs[0]; + $params['target_contact_id'] = $cid; + $params['version'] = 3; + $newActivity = civicrm_api('activity', 'create', $params); + if (!empty($newActivity['is_error'])) { + throw new CRM_Core_Exception($newActivity['error_message']); + } - // Retrieve contact id using contact dedupe rule. - // Since we are supporting only individual's activity import. - $params['contact_type'] = 'Individual'; - $params['version'] = 3; - $error = _civicrm_api3_deprecated_duplicate_formatted_contact($params); + $this->_newActivity[] = $newActivity['id']; + $this->setImportStatus($rowNumber, 'IMPORTED', '', $newActivity['id']); + return; - if (CRM_Core_Error::isAPIError($error, CRM_Core_ERROR::DUPLICATE_CONTACT)) { - $matchedIDs = explode(',', $error['error_message']['params'][0]); - if (count($matchedIDs) > 1) { - array_unshift($values, 'Multiple matching contact records detected for this row. The activity was not imported'); - return CRM_Import_Parser::ERROR; } - $cid = $matchedIDs[0]; - $params['target_contact_id'] = $cid; - $params['version'] = 3; - $newActivity = civicrm_api('activity', 'create', $params); - if (!empty($newActivity['is_error'])) { - array_unshift($values, $newActivity['error_message']); - return CRM_Import_Parser::ERROR; + // Using new Dedupe rule. + $ruleParams = [ + 'contact_type' => 'Individual', + 'used' => 'Unsupervised', + ]; + $fieldsArray = CRM_Dedupe_BAO_DedupeRule::dedupeRuleFields($ruleParams); + + $disp = NULL; + foreach ($fieldsArray as $value) { + if (array_key_exists(trim($value), $params)) { + $paramValue = $params[trim($value)]; + if (is_array($paramValue)) { + $disp .= $params[trim($value)][0][trim($value)] . " "; + } + else { + $disp .= $params[trim($value)] . " "; + } + } } - $this->_newActivity[] = $newActivity['id']; - return CRM_Import_Parser::VALID; - - } - // Using new Dedupe rule. - $ruleParams = [ - 'contact_type' => 'Individual', - 'used' => 'Unsupervised', - ]; - $fieldsArray = CRM_Dedupe_BAO_DedupeRule::dedupeRuleFields($ruleParams); - - $disp = NULL; - foreach ($fieldsArray as $value) { - if (array_key_exists(trim($value), $params)) { - $paramValue = $params[trim($value)]; - if (is_array($paramValue)) { - $disp .= $params[trim($value)][0][trim($value)] . " "; + if (!empty($params['external_identifier'])) { + if ($disp) { + $disp .= "AND {$params['external_identifier']}"; } else { - $disp .= $params[trim($value)] . " "; + $disp = $params['external_identifier']; } } - } + throw new CRM_Core_Exception('No matching Contact found for (' . $disp . ')'); + } if (!empty($params['external_identifier'])) { - if ($disp) { - $disp .= "AND {$params['external_identifier']}"; + $targetContactId = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', + $params['external_identifier'], 'id', 'external_identifier' + ); + + if (!empty($params['target_contact_id']) && + $params['target_contact_id'] != $targetContactId + ) { + throw new CRM_Core_Exception('Mismatch of External ID:' . $params['external_identifier'] . ' and Contact Id:' . $params['target_contact_id']); + } + if ($targetContactId) { + $params['target_contact_id'] = $targetContactId; } else { - $disp = $params['external_identifier']; + throw new CRM_Core_Exception('No Matching Contact for External ID:' . $params['external_identifier']); } } - array_unshift($values, 'No matching Contact found for (' . $disp . ')'); - return CRM_Import_Parser::ERROR; + $params['version'] = 3; + $newActivity = civicrm_api('activity', 'create', $params); + if (!empty($newActivity['is_error'])) { + throw new CRM_Core_Exception($newActivity['error_message']); + } } - if (!empty($params['external_identifier'])) { - $targetContactId = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', - $params['external_identifier'], 'id', 'external_identifier' - ); + catch (CRM_Core_Exception $e) { + $this->setImportStatus($rowNumber, 'ERROR', $e->getMessage()); + return; + } + $this->_newActivity[] = $newActivity['id']; + $this->setImportStatus($rowNumber, 'IMPORTED', '', $newActivity['id']); + } - if (!empty($params['target_contact_id']) && - $params['target_contact_id'] != $targetContactId - ) { - array_unshift($values, 'Mismatch of External ID:' . $params['external_identifier'] . ' and Contact Id:' . $params['target_contact_id']); - return CRM_Import_Parser::ERROR; - } - if ($targetContactId) { - $params['target_contact_id'] = $targetContactId; + /** + * Get the row from the csv mapped to our parameters. + * + * @param array $values + * + * @return array + * @throws \API_Exception + */ + public function getMappedRow(array $values): array { + $params = []; + foreach ($this->getFieldMappings() as $i => $mappedField) { + if ($mappedField['name'] === 'do_not_import') { + continue; } - else { - array_unshift($values, 'No Matching Contact for External ID:' . $params['external_identifier']); - return CRM_Import_Parser::ERROR; + if ($mappedField['name']) { + $fieldName = $this->getFieldMetadata($mappedField['name'])['name']; + if (in_array($mappedField['name'], ['target_contact_id', 'source_contact_id'])) { + $fieldName = $mappedField['name']; + } + $params[$fieldName] = $this->getTransformedFieldValue($mappedField['name'], $values[$i]); } } - - $params['version'] = 3; - $newActivity = civicrm_api('activity', 'create', $params); - if (!empty($newActivity['is_error'])) { - array_unshift($values, $newActivity['error_message']); - return CRM_Import_Parser::ERROR; - } - - $this->_newActivity[] = $newActivity['id']; - return CRM_Import_Parser::VALID; + return $params; } /** @@ -292,6 +229,13 @@ class CRM_Activity_Import_Parser_Activity extends CRM_Import_Parser { return $row[$this->getFieldIndex($fieldName)] ?? NULL; } + /** + * @return array + */ + protected function getRequiredFields(): array { + return [['activity_type_id' => ts('Activity Type'), 'activity_date_time' => ts('Activity Date')]]; + } + /** * Get the index for the given field. * @@ -375,47 +319,12 @@ class CRM_Activity_Import_Parser_Activity extends CRM_Import_Parser { $errorMessage = NULL; // Checking error in custom data. $params['contact_type'] = 'Activity'; - CRM_Contact_Import_Parser_Contact::isErrorInCustomData($params, $errorMessage); + $this->isErrorInCustomData($params, $errorMessage); if ($errorMessage) { throw new CRM_Core_Exception('Invalid value for field(s) : ' . $errorMessage); } } - /** - * @param array $values - * - * @throws \CRM_Core_Exception - */ - protected function validateValues(array $values): void { - // Check required fields if this is not an update. - if (!$this->getFieldValue($values, 'activity_id')) { - if (!$this->getFieldValue($values, 'activity_label') - && !$this->getFieldValue($values, 'activity_type_id')) { - throw new CRM_Core_Exception(ts('Missing required fields: Activity type label or Activity type ID')); - } - if (!$this->getFieldValue($values, 'activity_date_time')) { - throw new CRM_Core_Exception(ts('Missing required fields')); - } - } - - $this->validateActivityTypeIDAndLabel($values); - if ($this->getFieldValue($values, 'activity_date_time') - && !$this->isValidDate($this->getFieldValue($values, 'activity_date_time'))) { - throw new CRM_Core_Exception(ts('Invalid Activity Date')); - } - - if ($this->getFieldValue($values, 'activity_engagement_level') - && !CRM_Utils_Rule::positiveInteger($this->getFieldValue($values, 'activity_engagement_level'))) { - throw new CRM_Core_Exception(ts('Activity Engagement Index')); - } - - $targetContactID = $this->getFieldValue($values, 'target_contact_id'); - if ($targetContactID && !$this->isValidContactID($targetContactID)) { - throw new CRM_Core_Exception("Invalid Contact ID: There is no contact record with contact_id = " . CRM_Utils_Type::escape($targetContactID, 'String')); - } - $this->validateCustomFields($values); - } - /** * Get array of parameters formatted for the api from the submitted values. * @@ -445,9 +354,6 @@ class CRM_Activity_Import_Parser_Activity extends CRM_Import_Parser { * @param int $onDuplicate * @param int $statusID * @param int $totalRowCount - * - * @return mixed - * @throws Exception */ public function run( array $fileName, @@ -459,29 +365,14 @@ class CRM_Activity_Import_Parser_Activity extends CRM_Import_Parser { $statusID = NULL, $totalRowCount = NULL ) { - - $fileName = $fileName['name']; - $this->init(); - $this->_haveColumnHeader = $skipColumnHeader; - - $this->_separator = $separator; - - $fd = fopen($fileName, "r"); - if (!$fd) { - return FALSE; - } - $this->_lineCount = 0; $this->_invalidRowCount = $this->_validCount = 0; $this->_totalCount = 0; $this->_errors = []; $this->_warnings = []; - - $this->_fileSize = number_format(filesize($fileName) / 1024.0, 2); - if ($mode == self::MODE_MAPFIELD) { $this->_rows = []; } @@ -493,33 +384,11 @@ class CRM_Activity_Import_Parser_Activity extends CRM_Import_Parser { $startTimestamp = $currTimestamp = $prevTimestamp = time(); } - while (!feof($fd)) { + $dataSource = $this->getDataSourceObject(); + $dataSource->setStatuses(['new']); + while ($row = $dataSource->getRow()) { $this->_lineCount++; - - $values = fgetcsv($fd, 8192, $separator); - if (!$values) { - continue; - } - - self::encloseScrub($values); - - // skip column header if we're not in mapfield mode - if ($mode != self::MODE_MAPFIELD && $skipColumnHeader) { - $skipColumnHeader = FALSE; - continue; - } - - // Trim whitespace around the values. - - $empty = TRUE; - foreach ($values as $k => $v) { - $values[$k] = trim($v, " \t\r\n"); - } - - if (CRM_Utils_System::isNull($values)) { - continue; - } - + $values = array_values($row); $this->_totalCount++; if ($mode == self::MODE_MAPFIELD) { @@ -530,60 +399,11 @@ class CRM_Activity_Import_Parser_Activity extends CRM_Import_Parser { $returnCode = $this->summary($values); } elseif ($mode == self::MODE_IMPORT) { - $returnCode = $this->import($onDuplicate, $values); + $this->import($values); if ($statusID && (($this->_lineCount % 50) == 0)) { $prevTimestamp = $this->progressImport($statusID, FALSE, $startTimestamp, $prevTimestamp, $totalRowCount); } } - else { - $returnCode = self::ERROR; - } - - // note that a line could be valid but still produce a warning - if ($returnCode & self::VALID) { - $this->_validCount++; - if ($mode == self::MODE_MAPFIELD) { - $this->_rows[] = $values; - $this->_activeFieldCount = max($this->_activeFieldCount, count($values)); - } - } - - if ($returnCode & self::ERROR) { - $this->_invalidRowCount++; - $recordNumber = $this->_lineCount; - if ($this->_haveColumnHeader) { - $recordNumber--; - } - array_unshift($values, $recordNumber); - $this->_errors[] = $values; - } - - // if we are done processing the maxNumber of lines, break - if ($this->_maxLinesToProcess > 0 && $this->_validCount >= $this->_maxLinesToProcess) { - break; - } - } - - fclose($fd); - - if ($mode == self::MODE_PREVIEW || $mode == self::MODE_IMPORT) { - $customHeaders = $mapper; - - $customfields = CRM_Core_BAO_CustomField::getFields('Activity'); - foreach ($customHeaders as $key => $value) { - if ($id = CRM_Core_BAO_CustomField::getKeyID($value)) { - $customHeaders[$key] = $customfields[$id][0]; - } - } - if ($this->_invalidRowCount) { - // removed view url for invlaid contacts - $headers = array_merge( - [ts('Line Number'), ts('Reason')], - $customHeaders - ); - $this->_errorFileName = self::errorFileName(self::ERROR); - self::exportCSV($this->_errorFileName, $headers, $this->_errors); - } } } @@ -632,28 +452,7 @@ class CRM_Activity_Import_Parser_Activity extends CRM_Import_Parser { * * @param CRM_Core_Session $store */ - public function set($store) { - $store->set('fileSize', $this->_fileSize); - $store->set('lineCount', $this->_lineCount); - $store->set('separator', $this->_separator); - $store->set('fields', $this->getSelectValues()); - - $store->set('headerPatterns', $this->getHeaderPatterns()); - $store->set('dataPatterns', $this->getDataPatterns()); - $store->set('columnCount', $this->_activeFieldCount); - - $store->set('totalRowCount', $this->_totalCount); - $store->set('validRowCount', $this->_validCount); - $store->set('invalidRowCount', $this->_invalidRowCount); - - if ($this->_invalidRowCount) { - $store->set('errorsFileName', $this->_errorFileName); - } - - if (isset($this->_rows) && !empty($this->_rows)) { - $store->set('dataValues', $this->_rows); - } - } + public function set($store) {} /** * Export data to a CSV file. @@ -682,4 +481,25 @@ class CRM_Activity_Import_Parser_Activity extends CRM_Import_Parser { fclose($fd); } + /** + * Ensure metadata is loaded. + */ + protected function setFieldMetadata(): void { + if (empty($this->importableFieldsMetadata)) { + $activityContact = CRM_Activity_BAO_ActivityContact::import(); + $activityTarget['target_contact_id'] = $activityContact['contact_id']; + $fields = array_merge(CRM_Activity_BAO_Activity::importableFields(), + $activityTarget + ); + + $fields = array_merge($fields, [ + 'source_contact_id' => [ + 'title' => ts('Source Contact'), + 'headerPattern' => '/Source.Contact?/i', + ], + ]); + $this->importableFieldsMetadata = $fields; + } + } + }