[REF] Remove seemingly unreachable attempt to format activity_date_time field
[civicrm-core.git] / CRM / Activity / Import / Parser / Activity.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
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 +--------------------------------------------------------------------+
10 */
11
12 /**
13 *
14 * @package CRM
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
16 */
17
18
19 /**
20 * Class to parse activity csv files.
21 */
22 class CRM_Activity_Import_Parser_Activity extends CRM_Activity_Import_Parser {
23
24 protected $_mapperKeys;
25
26 private $_contactIdIndex;
27
28 /**
29 * Array of successfully imported activity id's
30 *
31 * @var array
32 */
33 protected $_newActivity;
34
35 /**
36 * Class constructor.
37 *
38 * @param array $mapperKeys
39 */
40 public function __construct($mapperKeys) {
41 parent::__construct();
42 $this->_mapperKeys = $mapperKeys;
43 }
44
45 /**
46 * Function of undocumented functionality required by the interface.
47 */
48 protected function fini() {}
49
50 /**
51 * The initializer code, called before the processing.
52 */
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(),
57 $activityTarget
58 );
59
60 $fields = array_merge($fields, [
61 'source_contact_id' => [
62 'title' => ts('Source Contact'),
63 'headerPattern' => '/Source.Contact?/i',
64 ],
65 'activity_label' => [
66 'title' => ts('Activity Type Label'),
67 'headerPattern' => '/(activity.)?type label?/i',
68 ],
69 ]);
70
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']);
76 }
77
78 $this->_newActivity = [];
79
80 $this->setActiveFields($this->_mapperKeys);
81
82 // FIXME: we should do this in one place together with Form/MapField.php
83 $this->_contactIdIndex = -1;
84
85 $index = 0;
86 foreach ($this->_mapperKeys as $key) {
87 switch ($key) {
88 case 'target_contact_id':
89 case 'external_identifier':
90 $this->_contactIdIndex = $index;
91 break;
92 }
93 $index++;
94 }
95 }
96
97 /**
98 * Handle the values in mapField mode.
99 *
100 * @param array $values
101 * The array of values belonging to this line.
102 *
103 * @return bool
104 */
105 public function mapField(&$values) {
106 return CRM_Import_Parser::VALID;
107 }
108
109 /**
110 * Handle the values in preview mode.
111 *
112 * @param array $values
113 * The array of values belonging to this line.
114 *
115 * @return bool
116 * the result of this processing
117 */
118 public function preview(&$values) {
119 return $this->summary($values);
120 }
121
122 /**
123 * Handle the values in summary mode.
124 *
125 * @param array $values
126 * The array of values belonging to this line.
127 *
128 * @return bool
129 * the result of this processing
130 */
131 public function summary(&$values) {
132 $this->setActiveFieldValues($values);
133
134 try {
135 $this->validateActivityTypeIDAndLabel($values);
136 if (!$this->getFieldValue($values, 'activity_date_time')) {
137 throw new CRM_Core_Exception(ts('Missing required fields'));
138 }
139 }
140 catch (CRM_Core_Exception $e) {
141 return $this->addError($values, [$e->getMessage()]);
142 }
143
144 $params = $this->getActiveFieldParams();
145
146 $errorMessage = NULL;
147
148 // For date-Formats
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');
153 }
154 foreach ($params as $key => $val) {
155 if ($key === 'activity_date_time') {
156 if ($val) {
157 $dateValue = CRM_Utils_Date::formatDate($val, $dateType);
158 if ($dateValue) {
159 $params[$key] = $dateValue;
160 }
161 else {
162 CRM_Contact_Import_Parser_Contact::addToErrorMsg('Activity date', $errorMessage);
163 }
164 }
165 }
166 elseif ($key == 'activity_engagement_level' && $val &&
167 !CRM_Utils_Rule::positiveInteger($val)
168 ) {
169 CRM_Contact_Import_Parser_Contact::addToErrorMsg('Activity Engagement Index', $errorMessage);
170 }
171 }
172 // Date-Format part ends.
173
174 // Checking error in custom data.
175 $params['contact_type'] = $this->_contactType ?? 'Activity';
176
177 CRM_Contact_Import_Parser_Contact::isErrorInCustomData($params, $errorMessage);
178
179 if ($errorMessage) {
180 $tempMsg = "Invalid value for field(s) : $errorMessage";
181 array_unshift($values, $tempMsg);
182 $errorMessage = NULL;
183 return CRM_Import_Parser::ERROR;
184 }
185
186 return CRM_Import_Parser::VALID;
187 }
188
189 /**
190 * Handle the values in import mode.
191 *
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.
196 *
197 * @return bool
198 * the result of this processing
199 * @throws \CRM_Core_Exception
200 */
201 public function import($onDuplicate, &$values) {
202 // First make sure this is a valid line
203 $response = $this->summary($values);
204
205 if ($response != CRM_Import_Parser::VALID) {
206 return $response;
207 }
208 $params = $this->getActiveFieldParams();
209 $activityLabel = array_search('activity_label', $this->_mapperKeys);
210 if ($activityLabel) {
211 $params = array_merge($params, ['activity_label' => $values[$activityLabel]]);
212 }
213 // For date-Formats.
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');
218 }
219
220 $customFields = CRM_Core_BAO_CustomField::getFields('Activity');
221
222 foreach ($params as $key => $val) {
223 if ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) {
224 if (!empty($customFields[$customFieldID]) && $customFields[$customFieldID]['data_type'] == 'Date') {
225 CRM_Contact_Import_Parser_Contact::formatCustomDate($params, $params, $dateType, $key);
226 }
227 elseif (!empty($customFields[$customFieldID]) && $customFields[$customFieldID]['data_type'] == 'Boolean') {
228 $params[$key] = CRM_Utils_String::strtoboolstr($val);
229 }
230 }
231 elseif ($key === 'activity_date_time') {
232 $params[$key] = CRM_Utils_Date::formatDate($val, $dateType);
233 }
234 elseif ($key === 'activity_subject') {
235 $params['subject'] = $val;
236 }
237 }
238 // Date-Format part ends.
239 $formatError = $this->deprecated_activity_formatted_param($params, $params, TRUE);
240
241 if ($formatError) {
242 array_unshift($values, $formatError['error_message']);
243 return CRM_Import_Parser::ERROR;
244 }
245
246 $params['custom'] = CRM_Core_BAO_CustomField::postProcess($params,
247 NULL,
248 'Activity'
249 );
250
251 if ($this->_contactIdIndex < 0) {
252
253 // Retrieve contact id using contact dedupe rule.
254 // Since we are supporting only individual's activity import.
255 $params['contact_type'] = 'Individual';
256 $params['version'] = 3;
257 $error = _civicrm_api3_deprecated_duplicate_formatted_contact($params);
258
259 if (CRM_Core_Error::isAPIError($error, CRM_Core_ERROR::DUPLICATE_CONTACT)) {
260 $matchedIDs = explode(',', $error['error_message']['params'][0]);
261 if (count($matchedIDs) > 1) {
262 array_unshift($values, 'Multiple matching contact records detected for this row. The activity was not imported');
263 return CRM_Import_Parser::ERROR;
264 }
265 $cid = $matchedIDs[0];
266 $params['target_contact_id'] = $cid;
267 $params['version'] = 3;
268 $newActivity = civicrm_api('activity', 'create', $params);
269 if (!empty($newActivity['is_error'])) {
270 array_unshift($values, $newActivity['error_message']);
271 return CRM_Import_Parser::ERROR;
272 }
273
274 $this->_newActivity[] = $newActivity['id'];
275 return CRM_Import_Parser::VALID;
276
277 }
278 // Using new Dedupe rule.
279 $ruleParams = [
280 'contact_type' => 'Individual',
281 'used' => 'Unsupervised',
282 ];
283 $fieldsArray = CRM_Dedupe_BAO_Rule::dedupeRuleFields($ruleParams);
284
285 $disp = NULL;
286 foreach ($fieldsArray as $value) {
287 if (array_key_exists(trim($value), $params)) {
288 $paramValue = $params[trim($value)];
289 if (is_array($paramValue)) {
290 $disp .= $params[trim($value)][0][trim($value)] . " ";
291 }
292 else {
293 $disp .= $params[trim($value)] . " ";
294 }
295 }
296 }
297
298 if (!empty($params['external_identifier'])) {
299 if ($disp) {
300 $disp .= "AND {$params['external_identifier']}";
301 }
302 else {
303 $disp = $params['external_identifier'];
304 }
305 }
306
307 array_unshift($values, 'No matching Contact found for (' . $disp . ')');
308 return CRM_Import_Parser::ERROR;
309 }
310 if (!empty($params['external_identifier'])) {
311 $targetContactId = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact',
312 $params['external_identifier'], 'id', 'external_identifier'
313 );
314
315 if (!empty($params['target_contact_id']) &&
316 $params['target_contact_id'] != $targetContactId
317 ) {
318 array_unshift($values, 'Mismatch of External ID:' . $params['external_identifier'] . ' and Contact Id:' . $params['target_contact_id']);
319 return CRM_Import_Parser::ERROR;
320 }
321 if ($targetContactId) {
322 $params['target_contact_id'] = $targetContactId;
323 }
324 else {
325 array_unshift($values, 'No Matching Contact for External ID:' . $params['external_identifier']);
326 return CRM_Import_Parser::ERROR;
327 }
328 }
329
330 $params['version'] = 3;
331 $newActivity = civicrm_api('activity', 'create', $params);
332 if (!empty($newActivity['is_error'])) {
333 array_unshift($values, $newActivity['error_message']);
334 return CRM_Import_Parser::ERROR;
335 }
336
337 $this->_newActivity[] = $newActivity['id'];
338 return CRM_Import_Parser::VALID;
339 }
340
341 /**
342 * take the input parameter list as specified in the data model and
343 * convert it into the same format that we use in QF and BAO object
344 *
345 * @param array $params
346 * Associative array of property name/value.
347 * pairs to insert in new contact.
348 * @param array $values
349 * The reformatted properties that we can use internally.
350 *
351 * @param array|bool $create Is the formatted Values array going to
352 * be used for CRM_Activity_BAO_Activity::create()
353 *
354 * @return array|CRM_Error
355 */
356 protected function deprecated_activity_formatted_param(&$params, &$values, $create = FALSE) {
357 // copy all the activity fields as is
358 $fields = CRM_Activity_DAO_Activity::fields();
359 _civicrm_api3_store_values($fields, $params, $values);
360
361 require_once 'CRM/Core/OptionGroup.php';
362 $customFields = CRM_Core_BAO_CustomField::getFields('Activity');
363
364 foreach ($params as $key => $value) {
365 // ignore empty values or empty arrays etc
366 if (CRM_Utils_System::isNull($value)) {
367 continue;
368 }
369
370 //Handling Custom Data
371 if ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) {
372 $values[$key] = $value;
373 $type = $customFields[$customFieldID]['html_type'];
374 if (CRM_Core_BAO_CustomField::isSerialized($customFields[$customFieldID])) {
375 $values[$key] = CRM_Import_Parser::unserializeCustomValue($customFieldID, $value, $type);
376 }
377 elseif ($type == 'Select' || $type == 'Radio') {
378 $customOption = CRM_Core_BAO_CustomOption::getCustomOption($customFieldID, TRUE);
379 foreach ($customOption as $customFldID => $customValue) {
380 $val = $customValue['value'] ?? NULL;
381 $label = $customValue['label'] ?? NULL;
382 $label = strtolower($label);
383 $value = strtolower(trim($value));
384 if (($value == $label) || ($value == strtolower($val))) {
385 $values[$key] = $val;
386 }
387 }
388 }
389 }
390
391 if ($key == 'target_contact_id') {
392 if (!CRM_Utils_Rule::integer($value)) {
393 return civicrm_api3_create_error("contact_id not valid: $value");
394 }
395 $contactID = CRM_Core_DAO::singleValueQuery("SELECT id FROM civicrm_contact WHERE id = $value");
396 if (!$contactID) {
397 return civicrm_api3_create_error("Invalid Contact ID: There is no contact record with contact_id = $value.");
398 }
399 }
400 }
401 return NULL;
402 }
403
404 /**
405 *
406 * Get the value for the given field from the row of values.
407 *
408 * @param array $row
409 * @param string $fieldName
410 *
411 * @return null|string
412 */
413 protected function getFieldValue(array $row, string $fieldName) {
414 if (!is_numeric($this->getFieldIndex($fieldName))) {
415 return NULL;
416 }
417 return $row[$this->getFieldIndex($fieldName)] ?? NULL;
418 }
419
420 /**
421 * Get the index for the given field.
422 *
423 * @param string $fieldName
424 *
425 * @return false|int
426 */
427 protected function getFieldIndex(string $fieldName) {
428 return array_search($fieldName, $this->_mapperKeys, TRUE);
429
430 }
431
432 /**
433 * Add an error to the values.
434 *
435 * @param array $values
436 * @param array $error
437 *
438 * @return int
439 */
440 protected function addError(array &$values, array $error): int {
441 array_unshift($values, implode(';', $error));
442 return CRM_Import_Parser::ERROR;
443 }
444
445 /**
446 * Validate that the activity type id does not conflict with the label.
447 *
448 * @param array $values
449 *
450 * @return void
451 * @throws \CRM_Core_Exception
452 */
453 protected function validateActivityTypeIDAndLabel(array $values): void {
454 $activityLabel = $this->getFieldValue($values, 'activity_label');
455 $activityTypeID = $this->getFieldValue($values, 'activity_type_id');
456 if ($activityLabel && $activityTypeID
457 && $activityLabel !== CRM_Core_PseudoConstant::getLabel('CRM_Activity_BAO_Activity', 'activity_type_id', $activityTypeID)) {
458 throw new CRM_Core_Exception(ts('Activity type label and Activity type ID are in conflict'));
459 }
460 if (!$activityLabel && !$activityTypeID) {
461 throw new CRM_Core_Exception(ts('Missing required fields: Activity type label or Activity type ID'));
462 }
463 }
464
465 }