protected $_mapperKeys;
- private $_contactIdIndex;
-
/**
* Array of successfully imported activity id's
*
*/
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.
*
* 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, '//');
$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++;
- }
}
/**
/**
* 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;
}
/**
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.
*
$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.
*
* @param int $onDuplicate
* @param int $statusID
* @param int $totalRowCount
- *
- * @return mixed
- * @throws Exception
*/
public function run(
array $fileName,
$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 = [];
}
$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) {
$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);
- }
}
}
*
* @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.
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;
+ }
+ }
+
}