3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
20 * Class to parse activity csv files.
22 class CRM_Activity_Import_Parser_Activity
extends CRM_Activity_Import_Parser
{
24 protected $_mapperKeys;
26 private $_contactIdIndex;
29 * Array of successfully imported activity id's
33 protected $_newActivity;
38 * @param array $mapperKeys
40 public function __construct($mapperKeys) {
41 parent
::__construct();
42 $this->_mapperKeys
= $mapperKeys;
46 * Function of undocumented functionality required by the interface.
48 protected function fini() {}
51 * The initializer code, called before the processing.
53 public function init() {
54 $activityContact = CRM_Activity_BAO_ActivityContact
::import();
55 $activityTarget['target_contact_id'] = $activityContact['contact_id'];
56 $fields = array_merge(CRM_Activity_BAO_Activity
::importableFields(),
60 $fields = array_merge($fields, [
61 'source_contact_id' => [
62 'title' => ts('Source Contact'),
63 'headerPattern' => '/Source.Contact?/i',
66 'title' => ts('Activity Type Label'),
67 'headerPattern' => '/(activity.)?type label?/i',
71 foreach ($fields as $name => $field) {
72 $field['type'] = CRM_Utils_Array
::value('type', $field, CRM_Utils_Type
::T_INT
);
73 $field['dataPattern'] = CRM_Utils_Array
::value('dataPattern', $field, '//');
74 $field['headerPattern'] = CRM_Utils_Array
::value('headerPattern', $field, '//');
75 $this->addField($name, $field['title'], $field['type'], $field['headerPattern'], $field['dataPattern']);
78 $this->_newActivity
= [];
80 $this->setActiveFields($this->_mapperKeys
);
82 // FIXME: we should do this in one place together with Form/MapField.php
83 $this->_contactIdIndex
= -1;
86 foreach ($this->_mapperKeys
as $key) {
88 case 'target_contact_id':
89 case 'external_identifier':
90 $this->_contactIdIndex
= $index;
98 * Handle the values in mapField mode.
100 * @param array $values
101 * The array of values belonging to this line.
105 public function mapField(&$values) {
106 return CRM_Import_Parser
::VALID
;
110 * Handle the values in preview mode.
112 * @param array $values
113 * The array of values belonging to this line.
116 * the result of this processing
118 public function preview(&$values) {
119 return $this->summary($values);
123 * Handle the values in summary mode.
125 * @param array $values
126 * The array of values belonging to this line.
129 * the result of this processing
131 public function summary(&$values) {
132 $this->setActiveFieldValues($values);
135 $this->validateActivityTypeIDAndLabel($values);
136 if (!$this->getFieldValue($values, 'activity_date_time')) {
137 throw new CRM_Core_Exception(ts('Missing required fields'));
140 catch (CRM_Core_Exception
$e) {
141 return $this->addError($values, [$e->getMessage()]);
144 $params = $this->getActiveFieldParams();
146 $errorMessage = NULL;
149 $session = CRM_Core_Session
::singleton();
150 $dateType = $session->get('dateTypes');
151 if (!isset($params['source_contact_id'])) {
152 $params['source_contact_id'] = $session->get('userID');
154 foreach ($params as $key => $val) {
155 if ($key === 'activity_date_time') {
157 $dateValue = CRM_Utils_Date
::formatDate($val, $dateType);
159 $params[$key] = $dateValue;
162 CRM_Contact_Import_Parser_Contact
::addToErrorMsg('Activity date', $errorMessage);
166 elseif ($key == 'activity_engagement_level' && $val &&
167 !CRM_Utils_Rule
::positiveInteger($val)
169 CRM_Contact_Import_Parser_Contact
::addToErrorMsg('Activity Engagement Index', $errorMessage);
172 // Date-Format part ends.
174 // Checking error in custom data.
175 $params['contact_type'] = $this->_contactType ??
'Activity';
177 CRM_Contact_Import_Parser_Contact
::isErrorInCustomData($params, $errorMessage);
180 $tempMsg = "Invalid value for field(s) : $errorMessage";
181 array_unshift($values, $tempMsg);
182 $errorMessage = NULL;
183 return CRM_Import_Parser
::ERROR
;
186 return CRM_Import_Parser
::VALID
;
190 * Handle the values in import mode.
192 * @param int $onDuplicate
193 * The code for what action to take on duplicates.
194 * @param array $values
195 * The array of values belonging to this line.
198 * the result of this processing
199 * @throws \CRM_Core_Exception
201 public function import($onDuplicate, &$values) {
202 // First make sure this is a valid line
203 $response = $this->summary($values);
205 if ($response != CRM_Import_Parser
::VALID
) {
208 $params = $this->getActiveFieldParams();
209 $activityLabel = array_search('activity_label', $this->_mapperKeys
);
210 if ($activityLabel) {
211 $params = array_merge($params, ['activity_label' => $values[$activityLabel]]);
214 $session = CRM_Core_Session
::singleton();
215 $dateType = $session->get('dateTypes');
216 if (!isset($params['source_contact_id'])) {
217 $params['source_contact_id'] = $session->get('userID');
220 $customFields = CRM_Core_BAO_CustomField
::getFields('Activity');
222 foreach ($params as $key => $val) {
223 if ($customFieldID = CRM_Core_BAO_CustomField
::getKeyID($key)) {
224 if ($key === 'activity_date_time' && $val) {
225 $params[$key] = CRM_Utils_Date
::formatDate($val, $dateType);
227 elseif (!empty($customFields[$customFieldID]) && $customFields[$customFieldID]['data_type'] == 'Date') {
228 CRM_Contact_Import_Parser_Contact
::formatCustomDate($params, $params, $dateType, $key);
230 elseif (!empty($customFields[$customFieldID]) && $customFields[$customFieldID]['data_type'] == 'Boolean') {
231 $params[$key] = CRM_Utils_String
::strtoboolstr($val);
234 elseif ($key === 'activity_date_time') {
235 $params[$key] = CRM_Utils_Date
::formatDate($val, $dateType);
237 elseif ($key === 'activity_subject') {
238 $params['subject'] = $val;
241 // Date-Format part ends.
242 $formatError = $this->deprecated_activity_formatted_param($params, $params, TRUE);
245 array_unshift($values, $formatError['error_message']);
246 return CRM_Import_Parser
::ERROR
;
249 $params['custom'] = CRM_Core_BAO_CustomField
::postProcess($params,
254 if ($this->_contactIdIndex
< 0) {
256 // Retrieve contact id using contact dedupe rule.
257 // Since we are supporting only individual's activity import.
258 $params['contact_type'] = 'Individual';
259 $params['version'] = 3;
260 $error = _civicrm_api3_deprecated_duplicate_formatted_contact($params);
262 if (CRM_Core_Error
::isAPIError($error, CRM_Core_ERROR
::DUPLICATE_CONTACT
)) {
263 $matchedIDs = explode(',', $error['error_message']['params'][0]);
264 if (count($matchedIDs) > 1) {
265 array_unshift($values, 'Multiple matching contact records detected for this row. The activity was not imported');
266 return CRM_Import_Parser
::ERROR
;
268 $cid = $matchedIDs[0];
269 $params['target_contact_id'] = $cid;
270 $params['version'] = 3;
271 $newActivity = civicrm_api('activity', 'create', $params);
272 if (!empty($newActivity['is_error'])) {
273 array_unshift($values, $newActivity['error_message']);
274 return CRM_Import_Parser
::ERROR
;
277 $this->_newActivity
[] = $newActivity['id'];
278 return CRM_Import_Parser
::VALID
;
281 // Using new Dedupe rule.
283 'contact_type' => 'Individual',
284 'used' => 'Unsupervised',
286 $fieldsArray = CRM_Dedupe_BAO_Rule
::dedupeRuleFields($ruleParams);
289 foreach ($fieldsArray as $value) {
290 if (array_key_exists(trim($value), $params)) {
291 $paramValue = $params[trim($value)];
292 if (is_array($paramValue)) {
293 $disp .= $params[trim($value)][0][trim($value)] . " ";
296 $disp .= $params[trim($value)] . " ";
301 if (!empty($params['external_identifier'])) {
303 $disp .= "AND {$params['external_identifier']}";
306 $disp = $params['external_identifier'];
310 array_unshift($values, 'No matching Contact found for (' . $disp . ')');
311 return CRM_Import_Parser
::ERROR
;
313 if (!empty($params['external_identifier'])) {
314 $targetContactId = CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_Contact',
315 $params['external_identifier'], 'id', 'external_identifier'
318 if (!empty($params['target_contact_id']) &&
319 $params['target_contact_id'] != $targetContactId
321 array_unshift($values, 'Mismatch of External ID:' . $params['external_identifier'] . ' and Contact Id:' . $params['target_contact_id']);
322 return CRM_Import_Parser
::ERROR
;
324 if ($targetContactId) {
325 $params['target_contact_id'] = $targetContactId;
328 array_unshift($values, 'No Matching Contact for External ID:' . $params['external_identifier']);
329 return CRM_Import_Parser
::ERROR
;
333 $params['version'] = 3;
334 $newActivity = civicrm_api('activity', 'create', $params);
335 if (!empty($newActivity['is_error'])) {
336 array_unshift($values, $newActivity['error_message']);
337 return CRM_Import_Parser
::ERROR
;
340 $this->_newActivity
[] = $newActivity['id'];
341 return CRM_Import_Parser
::VALID
;
345 * take the input parameter list as specified in the data model and
346 * convert it into the same format that we use in QF and BAO object
348 * @param array $params
349 * Associative array of property name/value.
350 * pairs to insert in new contact.
351 * @param array $values
352 * The reformatted properties that we can use internally.
354 * @param array|bool $create Is the formatted Values array going to
355 * be used for CRM_Activity_BAO_Activity::create()
357 * @return array|CRM_Error
359 protected function deprecated_activity_formatted_param(&$params, &$values, $create = FALSE) {
360 // copy all the activity fields as is
361 $fields = CRM_Activity_DAO_Activity
::fields();
362 _civicrm_api3_store_values($fields, $params, $values);
364 require_once 'CRM/Core/OptionGroup.php';
365 $customFields = CRM_Core_BAO_CustomField
::getFields('Activity');
367 foreach ($params as $key => $value) {
368 // ignore empty values or empty arrays etc
369 if (CRM_Utils_System
::isNull($value)) {
373 //Handling Custom Data
374 if ($customFieldID = CRM_Core_BAO_CustomField
::getKeyID($key)) {
375 $values[$key] = $value;
376 $type = $customFields[$customFieldID]['html_type'];
377 if (CRM_Core_BAO_CustomField
::isSerialized($customFields[$customFieldID])) {
378 $values[$key] = CRM_Import_Parser
::unserializeCustomValue($customFieldID, $value, $type);
380 elseif ($type == 'Select' ||
$type == 'Radio') {
381 $customOption = CRM_Core_BAO_CustomOption
::getCustomOption($customFieldID, TRUE);
382 foreach ($customOption as $customFldID => $customValue) {
383 $val = $customValue['value'] ??
NULL;
384 $label = $customValue['label'] ??
NULL;
385 $label = strtolower($label);
386 $value = strtolower(trim($value));
387 if (($value == $label) ||
($value == strtolower($val))) {
388 $values[$key] = $val;
394 if ($key == 'target_contact_id') {
395 if (!CRM_Utils_Rule
::integer($value)) {
396 return civicrm_api3_create_error("contact_id not valid: $value");
398 $contactID = CRM_Core_DAO
::singleValueQuery("SELECT id FROM civicrm_contact WHERE id = $value");
400 return civicrm_api3_create_error("Invalid Contact ID: There is no contact record with contact_id = $value.");
409 * Get the value for the given field from the row of values.
412 * @param string $fieldName
414 * @return null|string
416 protected function getFieldValue(array $row, string $fieldName) {
417 if (!is_numeric($this->getFieldIndex($fieldName))) {
420 return $row[$this->getFieldIndex($fieldName)] ??
NULL;
424 * Get the index for the given field.
426 * @param string $fieldName
430 protected function getFieldIndex(string $fieldName) {
431 return array_search($fieldName, $this->_mapperKeys
, TRUE);
436 * Add an error to the values.
438 * @param array $values
439 * @param array $error
443 protected function addError(array &$values, array $error): int {
444 array_unshift($values, implode(';', $error));
445 return CRM_Import_Parser
::ERROR
;
449 * Validate that the activity type id does not conflict with the label.
451 * @param array $values
454 * @throws \CRM_Core_Exception
456 protected function validateActivityTypeIDAndLabel(array $values): void
{
457 $activityLabel = $this->getFieldValue($values, 'activity_label');
458 $activityTypeID = $this->getFieldValue($values, 'activity_type_id');
459 if ($activityLabel && $activityTypeID
460 && $activityLabel !== CRM_Core_PseudoConstant
::getLabel('CRM_Activity_BAO_Activity', 'activity_type_id', $activityTypeID)) {
461 throw new CRM_Core_Exception(ts('Activity type label and Activity type ID are in conflict'));
463 if (!$activityLabel && !$activityTypeID) {
464 throw new CRM_Core_Exception(ts('Missing required fields: Activity type label or Activity type ID'));