Merge pull request #23267 from civicrm/5.49
[civicrm-core.git] / CRM / Event / Import / Parser / Participant.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
bc77d7c0
TO
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 |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035
TO
11
12/**
13 *
14 * @package CRM
ca5cec67 15 * @copyright CiviCRM LLC https://civicrm.org/licensing
6a488035
TO
16 */
17
18require_once 'CRM/Utils/DeprecatedUtils.php';
19
20/**
21 * class to parse membership csv files
22 */
d60cd664 23class CRM_Event_Import_Parser_Participant extends CRM_Import_Parser {
6a488035
TO
24 protected $_mapperKeys;
25
26 private $_contactIdIndex;
6a488035
TO
27 private $_eventIndex;
28 private $_participantStatusIndex;
29 private $_participantRoleIndex;
30 private $_eventTitleIndex;
31
32 /**
ceb10dc7 33 * Array of successfully imported participants id's
6a488035 34 *
90b461f1 35 * @var array
6a488035
TO
36 */
37 protected $_newParticipants;
38
d60cd664
EM
39
40 protected $_fileName;
41
42 /**
43 * Imported file size.
44 *
45 * @var int
46 */
47 protected $_fileSize;
48
49 /**
50 * Separator being used.
51 *
52 * @var string
53 */
54 protected $_separator;
55
56 /**
57 * Total number of lines in file.
58 *
59 * @var int
60 */
61 protected $_lineCount;
62
63 /**
64 * Whether the file has a column header or not
65 *
66 * @var bool
67 */
68 protected $_haveColumnHeader;
69
6a488035 70 /**
fe482240 71 * Class constructor.
54957108 72 *
73 * @param array $mapperKeys
6a488035 74 */
5e21e0f3 75 public function __construct(&$mapperKeys) {
6a488035
TO
76 parent::__construct();
77 $this->_mapperKeys = &$mapperKeys;
78 }
79
80 /**
54957108 81 * The initializer code, called before the processing.
6a488035 82 */
00be9182 83 public function init() {
6a488035
TO
84 $fields = CRM_Event_BAO_Participant::importableFields($this->_contactType, FALSE);
85 $fields['event_id']['title'] = 'Event ID';
86 $eventfields = &CRM_Event_BAO_Event::fields();
87 $fields['event_title'] = $eventfields['event_title'];
88
89 foreach ($fields as $name => $field) {
90 $field['type'] = CRM_Utils_Array::value('type', $field, CRM_Utils_Type::T_INT);
91 $field['dataPattern'] = CRM_Utils_Array::value('dataPattern', $field, '//');
92 $field['headerPattern'] = CRM_Utils_Array::value('headerPattern', $field, '//');
93 $this->addField($name, $field['title'], $field['type'], $field['headerPattern'], $field['dataPattern']);
94 }
95
be2fb01f 96 $this->_newParticipants = [];
6a488035
TO
97 $this->setActiveFields($this->_mapperKeys);
98
99 // FIXME: we should do this in one place together with Form/MapField.php
100 $this->_contactIdIndex = -1;
101 $this->_eventIndex = -1;
102 $this->_participantStatusIndex = -1;
103 $this->_participantRoleIndex = -1;
104 $this->_eventTitleIndex = -1;
105
106 $index = 0;
107 foreach ($this->_mapperKeys as $key) {
108
109 switch ($key) {
110 case 'participant_contact_id':
111 $this->_contactIdIndex = $index;
112 break;
113
114 case 'event_id':
115 $this->_eventIndex = $index;
116 break;
117
118 case 'participant_status':
119 case 'participant_status_id':
120 $this->_participantStatusIndex = $index;
121 break;
122
123 case 'participant_role_id':
124 $this->_participantRoleIndex = $index;
125 break;
126
127 case 'event_title':
128 $this->_eventTitleIndex = $index;
129 break;
130 }
131 $index++;
132 }
133 }
134
6a488035 135 /**
fe482240 136 * Handle the values in preview mode.
6a488035 137 *
d4dd1e85
TO
138 * @param array $values
139 * The array of values belonging to this line.
6a488035 140 *
79d7553f 141 * @return bool
a6c01b45 142 * the result of this processing
6a488035 143 */
00be9182 144 public function preview(&$values) {
6a488035
TO
145 return $this->summary($values);
146 }
147
148 /**
fe482240 149 * Handle the values in summary mode.
6a488035 150 *
d4dd1e85
TO
151 * @param array $values
152 * The array of values belonging to this line.
6a488035 153 *
79d7553f 154 * @return bool
a6c01b45 155 * the result of this processing
6a488035 156 */
00be9182 157 public function summary(&$values) {
6a488035
TO
158 $erroneousField = NULL;
159
353ffa53 160 $response = $this->setActiveFieldValues($values, $erroneousField);
6a488035 161 $errorRequired = FALSE;
353ffa53 162 $index = -1;
6a488035
TO
163
164 if ($this->_eventIndex > -1 && $this->_eventTitleIndex > -1) {
165 array_unshift($values, ts('Select either EventID OR Event Title'));
a05662ef 166 return CRM_Import_Parser::ERROR;
6a488035
TO
167 }
168 elseif ($this->_eventTitleIndex > -1) {
169 $index = $this->_eventTitleIndex;
170 }
171 elseif ($this->_eventIndex > -1) {
172 $index = $this->_eventIndex;
173 }
174 $params = &$this->getActiveFieldParams();
175
176 if (!(($index < 0) || ($this->_participantStatusIndex < 0))) {
177 $errorRequired = !CRM_Utils_Array::value($this->_participantStatusIndex, $values);
8cc574cf 178 if (empty($params['event_id']) && empty($params['event_title'])) {
719a6fec 179 CRM_Contact_Import_Parser_Contact::addToErrorMsg('Event', $missingField);
6a488035 180 }
a7488080 181 if (empty($params['participant_status_id'])) {
719a6fec 182 CRM_Contact_Import_Parser_Contact::addToErrorMsg('Participant Status', $missingField);
6a488035
TO
183 }
184 }
185 else {
186 $errorRequired = TRUE;
187 $missingField = NULL;
188 if ($index < 0) {
719a6fec 189 CRM_Contact_Import_Parser_Contact::addToErrorMsg('Event', $missingField);
6a488035
TO
190 }
191 if ($this->_participantStatusIndex < 0) {
719a6fec 192 CRM_Contact_Import_Parser_Contact::addToErrorMsg('Participant Status', $missingField);
6a488035
TO
193 }
194 }
195
196 if ($errorRequired) {
197 array_unshift($values, ts('Missing required field(s) :') . $missingField);
a05662ef 198 return CRM_Import_Parser::ERROR;
6a488035
TO
199 }
200
201 $errorMessage = NULL;
202
203 //for date-Formats
204 $session = CRM_Core_Session::singleton();
205 $dateType = $session->get('dateTypes');
206
207 foreach ($params as $key => $val) {
208 if ($val && ($key == 'participant_register_date')) {
209 if ($dateValue = CRM_Utils_Date::formatDate($params[$key], $dateType)) {
210 $params[$key] = $dateValue;
211 }
212 else {
719a6fec 213 CRM_Contact_Import_Parser_Contact::addToErrorMsg('Register Date', $errorMessage);
6a488035
TO
214 }
215 }
216 elseif ($val && ($key == 'participant_role_id' || $key == 'participant_role')) {
217 $roleIDs = CRM_Event_PseudoConstant::participantRole();
218 $val = explode(',', $val);
219 if ($key == 'participant_role_id') {
220 foreach ($val as $role) {
c3f7ab62 221 if (!array_key_exists(trim($role), $roleIDs)) {
719a6fec 222 CRM_Contact_Import_Parser_Contact::addToErrorMsg('Participant Role Id', $errorMessage);
6a488035
TO
223 break;
224 }
225 }
226 }
227 else {
228 foreach ($val as $role) {
719a6fec 229 if (!CRM_Contact_Import_Parser_Contact::in_value(trim($role), $roleIDs)) {
353ffa53 230 CRM_Contact_Import_Parser_Contact::addToErrorMsg('Participant Role', $errorMessage);
6a488035
TO
231 break;
232 }
233 }
234 }
235 }
236 elseif ($val && (($key == 'participant_status_id') || ($key == 'participant_status'))) {
237 $statusIDs = CRM_Event_PseudoConstant::participantStatus();
238 if ($key == 'participant_status_id') {
c3f7ab62 239 if (!array_key_exists(trim($val), $statusIDs)) {
719a6fec 240 CRM_Contact_Import_Parser_Contact::addToErrorMsg('Participant Status Id', $errorMessage);
6a488035
TO
241 break;
242 }
243 }
719a6fec
CW
244 elseif (!CRM_Contact_Import_Parser_Contact::in_value($val, $statusIDs)) {
245 CRM_Contact_Import_Parser_Contact::addToErrorMsg('Participant Status', $errorMessage);
6a488035
TO
246 break;
247 }
248 }
249 }
250 //date-Format part ends
251
252 $params['contact_type'] = 'Participant';
253 //checking error in custom data
719a6fec 254 CRM_Contact_Import_Parser_Contact::isErrorInCustomData($params, $errorMessage);
6a488035
TO
255
256 if ($errorMessage) {
257 $tempMsg = "Invalid value for field(s) : $errorMessage";
258 array_unshift($values, $tempMsg);
259 $errorMessage = NULL;
a05662ef 260 return CRM_Import_Parser::ERROR;
6a488035 261 }
a05662ef 262 return CRM_Import_Parser::VALID;
6a488035
TO
263 }
264
265 /**
fe482240 266 * Handle the values in import mode.
6a488035 267 *
d4dd1e85
TO
268 * @param int $onDuplicate
269 * The code for what action to take on duplicates.
270 * @param array $values
271 * The array of values belonging to this line.
6a488035 272 *
79d7553f 273 * @return bool
a6c01b45 274 * the result of this processing
6a488035 275 */
00be9182 276 public function import($onDuplicate, &$values) {
6a488035
TO
277
278 // first make sure this is a valid line
279 $response = $this->summary($values);
a05662ef 280 if ($response != CRM_Import_Parser::VALID) {
6a488035
TO
281 return $response;
282 }
353ffa53
TO
283 $params = &$this->getActiveFieldParams();
284 $session = CRM_Core_Session::singleton();
285 $dateType = $session->get('dateTypes');
be2fb01f 286 $formatted = ['version' => 3];
1828a237 287 $customFields = CRM_Core_BAO_CustomField::getFields('Participant');
6a488035
TO
288
289 // don't add to recent items, CRM-4399
290 $formatted['skipRecentView'] = TRUE;
291
292 foreach ($params as $key => $val) {
293 if ($val) {
294 if ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) {
295 if ($customFields[$customFieldID]['data_type'] == 'Date') {
719a6fec 296 CRM_Contact_Import_Parser_Contact::formatCustomDate($params, $formatted, $dateType, $key);
6a488035
TO
297 unset($params[$key]);
298 }
299 elseif ($customFields[$customFieldID]['data_type'] == 'Boolean') {
300 $params[$key] = CRM_Utils_String::strtoboolstr($val);
301 }
302 }
22e263ad 303 if ($key == 'participant_register_date') {
39d87d1e
JM
304 CRM_Utils_Date::convertToDefaultDate($params, $dateType, 'participant_register_date');
305 $formatted['participant_register_date'] = CRM_Utils_Date::processDate($params['participant_register_date']);
306 }
6a488035
TO
307 }
308 }
309
8cc574cf 310 if (!(!empty($params['participant_role_id']) || !empty($params['participant_role']))) {
a7488080 311 if (!empty($params['event_id'])) {
6a488035
TO
312 $params['participant_role_id'] = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Event', $params['event_id'], 'default_role_id');
313 }
314 else {
315 $eventTitle = $params['event_title'];
d4d1df5b 316 $params['participant_role_id'] = CRM_Core_DAO::singleValueQuery('SELECT default_role_id FROM civicrm_event WHERE title = %1', [
64e1d541 317 1 => [$eventTitle, 'String'],
d4d1df5b 318 ]);
6a488035
TO
319 }
320 }
321
322 //date-Format part ends
323 static $indieFields = NULL;
324 if ($indieFields == NULL) {
325 $indieFields = CRM_Event_BAO_Participant::import();
326 }
327
be2fb01f 328 $formatValues = [];
6a488035
TO
329 foreach ($params as $key => $field) {
330 if ($field == NULL || $field === '') {
331 continue;
332 }
333
334 $formatValues[$key] = $field;
335 }
336
c3fc9a44 337 $formatError = $this->formatValues($formatted, $formatValues);
6a488035
TO
338
339 if ($formatError) {
340 array_unshift($values, $formatError['error_message']);
a05662ef 341 return CRM_Import_Parser::ERROR;
6a488035
TO
342 }
343
344 if (!CRM_Utils_Rule::integer($formatted['event_id'])) {
345 array_unshift($values, ts('Invalid value for Event ID'));
a05662ef 346 return CRM_Import_Parser::ERROR;
6a488035
TO
347 }
348
a05662ef 349 if ($onDuplicate != CRM_Import_Parser::DUPLICATE_UPDATE) {
6a488035 350 $formatted['custom'] = CRM_Core_BAO_CustomField::postProcess($formatted,
6a488035
TO
351 NULL,
352 'Participant'
353 );
354 }
355 else {
356 if ($formatValues['participant_id']) {
357 $dao = new CRM_Event_BAO_Participant();
358 $dao->id = $formatValues['participant_id'];
359
360 $formatted['custom'] = CRM_Core_BAO_CustomField::postProcess($formatted,
6a488035
TO
361 $formatValues['participant_id'],
362 'Participant'
363 );
364 if ($dao->find(TRUE)) {
be2fb01f 365 $ids = [
6a488035
TO
366 'participant' => $formatValues['participant_id'],
367 'userId' => $session->get('userID'),
be2fb01f
CW
368 ];
369 $participantValues = [];
6a488035 370 //@todo calling api functions directly is not supported
1a5d4d8a 371 $newParticipant = $this->deprecated_participant_check_params($formatted, $participantValues, FALSE);
6a488035
TO
372 if ($newParticipant['error_message']) {
373 array_unshift($values, $newParticipant['error_message']);
a05662ef 374 return CRM_Import_Parser::ERROR;
6a488035
TO
375 }
376 $newParticipant = CRM_Event_BAO_Participant::create($formatted, $ids);
a7488080 377 if (!empty($formatted['fee_level'])) {
be2fb01f 378 $otherParams = [
6a488035 379 'fee_label' => $formatted['fee_level'],
21dfd5f5 380 'event_id' => $newParticipant->event_id,
be2fb01f 381 ];
6a488035
TO
382 CRM_Price_BAO_LineItem::syncLineItems($newParticipant->id, 'civicrm_participant', $newParticipant->fee_amount, $otherParams);
383 }
384
385 $this->_newParticipant[] = $newParticipant->id;
a05662ef 386 return CRM_Import_Parser::VALID;
6a488035
TO
387 }
388 else {
389 array_unshift($values, 'Matching Participant record not found for Participant ID ' . $formatValues['participant_id'] . '. Row was skipped.');
a05662ef 390 return CRM_Import_Parser::ERROR;
6a488035
TO
391 }
392 }
393 }
394
395 if ($this->_contactIdIndex < 0) {
56316747 396 $error = $this->checkContactDuplicate($formatValues);
6a488035
TO
397
398 if (CRM_Core_Error::isAPIError($error, CRM_Core_ERROR::DUPLICATE_CONTACT)) {
399 $matchedIDs = explode(',', $error['error_message']['params'][0]);
400 if (count($matchedIDs) >= 1) {
401 foreach ($matchedIDs as $contactId) {
402 $formatted['contact_id'] = $contactId;
403 $formatted['version'] = 3;
880585a2 404 $newParticipant = $this->deprecated_create_participant_formatted($formatted, $onDuplicate);
6a488035
TO
405 }
406 }
407 }
408 else {
409 // Using new Dedupe rule.
be2fb01f 410 $ruleParams = [
6a488035 411 'contact_type' => $this->_contactType,
353ffa53 412 'used' => 'Unsupervised',
be2fb01f 413 ];
61194d45 414 $fieldsArray = CRM_Dedupe_BAO_DedupeRule::dedupeRuleFields($ruleParams);
6a488035
TO
415
416 $disp = '';
417 foreach ($fieldsArray as $value) {
418 if (array_key_exists(trim($value), $params)) {
419 $paramValue = $params[trim($value)];
420 if (is_array($paramValue)) {
421 $disp .= $params[trim($value)][0][trim($value)] . " ";
422 }
423 else {
424 $disp .= $params[trim($value)] . " ";
425 }
426 }
427 }
428
a7488080 429 if (!empty($params['external_identifier'])) {
6a488035
TO
430 if ($disp) {
431 $disp .= "AND {$params['external_identifier']}";
432 }
433 else {
434 $disp = $params['external_identifier'];
435 }
436 }
437
438 array_unshift($values, 'No matching Contact found for (' . $disp . ')');
a05662ef 439 return CRM_Import_Parser::ERROR;
6a488035
TO
440 }
441 }
442 else {
a7488080 443 if (!empty($formatValues['external_identifier'])) {
6a488035
TO
444 $checkCid = new CRM_Contact_DAO_Contact();
445 $checkCid->external_identifier = $formatValues['external_identifier'];
446 $checkCid->find(TRUE);
447 if ($checkCid->id != $formatted['contact_id']) {
d79be26c 448 array_unshift($values, 'Mismatch of External ID:' . $formatValues['external_identifier'] . ' and Contact Id:' . $formatted['contact_id']);
a05662ef 449 return CRM_Import_Parser::ERROR;
6a488035
TO
450 }
451 }
452
880585a2 453 $newParticipant = $this->deprecated_create_participant_formatted($formatted, $onDuplicate);
6a488035
TO
454 }
455
456 if (is_array($newParticipant) && civicrm_error($newParticipant)) {
a05662ef 457 if ($onDuplicate == CRM_Import_Parser::DUPLICATE_SKIP) {
6a488035 458
9c1bc317
CW
459 $contactID = $newParticipant['contactID'] ?? NULL;
460 $participantID = $newParticipant['participantID'] ?? NULL;
353ffa53 461 $url = CRM_Utils_System::url('civicrm/contact/view/participant',
6a488035
TO
462 "reset=1&id={$participantID}&cid={$contactID}&action=view", TRUE
463 );
464 if (is_array($newParticipant['error_message']) &&
465 ($participantID == $newParticipant['error_message']['params'][0])
466 ) {
467 array_unshift($values, $url);
a05662ef 468 return CRM_Import_Parser::DUPLICATE;
6a488035
TO
469 }
470 elseif ($newParticipant['error_message']) {
471 array_unshift($values, $newParticipant['error_message']);
a05662ef 472 return CRM_Import_Parser::ERROR;
6a488035 473 }
a05662ef 474 return CRM_Import_Parser::ERROR;
6a488035
TO
475 }
476 }
477
478 if (!(is_array($newParticipant) && civicrm_error($newParticipant))) {
9c1bc317 479 $this->_newParticipants[] = $newParticipant['id'] ?? NULL;
6a488035
TO
480 }
481
a05662ef 482 return CRM_Import_Parser::VALID;
6a488035
TO
483 }
484
485 /**
fe482240 486 * Get the array of successfully imported Participation ids.
6a488035
TO
487 *
488 * @return array
6a488035 489 */
00be9182 490 public function &getImportedParticipations() {
6a488035
TO
491 return $this->_newParticipants;
492 }
493
c3fc9a44 494 /**
495 * Format values
496 *
497 * @todo lots of tidy up needed here - very old function relocated.
498 *
499 * @param array $values
500 * @param array $params
501 *
502 * @return array|null
503 */
504 protected function formatValues(&$values, $params) {
505 $fields = CRM_Event_DAO_Participant::fields();
506 _civicrm_api3_store_values($fields, $params, $values);
507
508 $customFields = CRM_Core_BAO_CustomField::getFields('Participant', FALSE, FALSE, NULL, NULL, FALSE, FALSE, FALSE);
509
510 foreach ($params as $key => $value) {
511 // ignore empty values or empty arrays etc
512 if (CRM_Utils_System::isNull($value)) {
513 continue;
514 }
515
516 // Handling Custom Data
517 if ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) {
518 $values[$key] = $value;
519 $type = $customFields[$customFieldID]['html_type'];
726e45e7 520 if (CRM_Core_BAO_CustomField::isSerialized($customFields[$customFieldID])) {
be40742b 521 $values[$key] = self::unserializeCustomValue($customFieldID, $value, $type);
c3fc9a44 522 }
523 elseif ($type == 'Select' || $type == 'Radio') {
524 $customOption = CRM_Core_BAO_CustomOption::getCustomOption($customFieldID, TRUE);
525 foreach ($customOption as $customFldID => $customValue) {
9c1bc317
CW
526 $val = $customValue['value'] ?? NULL;
527 $label = $customValue['label'] ?? NULL;
c3fc9a44 528 $label = strtolower($label);
529 $value = strtolower(trim($value));
530 if (($value == $label) || ($value == strtolower($val))) {
531 $values[$key] = $val;
532 }
533 }
534 }
535 }
536
537 switch ($key) {
538 case 'participant_contact_id':
539 if (!CRM_Utils_Rule::integer($value)) {
540 return civicrm_api3_create_error("contact_id not valid: $value");
541 }
08c6b770 542 if (!CRM_Core_DAO::singleValueQuery("SELECT id FROM civicrm_contact WHERE id = $value")) {
c3fc9a44 543 return civicrm_api3_create_error("Invalid Contact ID: There is no contact record with contact_id = $value.");
544 }
545 $values['contact_id'] = $values['participant_contact_id'];
546 unset($values['participant_contact_id']);
547 break;
548
549 case 'participant_register_date':
550 if (!CRM_Utils_Rule::dateTime($value)) {
551 return civicrm_api3_create_error("$key not a valid date: $value");
552 }
553 break;
554
555 case 'event_title':
556 $id = CRM_Core_DAO::getFieldValue("CRM_Event_DAO_Event", $value, 'id', 'title');
557 $values['event_id'] = $id;
558 break;
559
560 case 'event_id':
561 if (!CRM_Utils_Rule::integer($value)) {
562 return civicrm_api3_create_error("Event ID is not valid: $value");
563 }
d4d1df5b 564 $svq = CRM_Core_DAO::singleValueQuery('SELECT id FROM civicrm_event WHERE id = %1', [
64e1d541 565 1 => [$value, 'Integer'],
d4d1df5b 566 ]);
c3fc9a44 567 if (!$svq) {
568 return civicrm_api3_create_error("Invalid Event ID: There is no event record with event_id = $value.");
569 }
570 break;
571
572 case 'participant_status_id':
573 if (!CRM_Utils_Rule::integer($value)) {
574 return civicrm_api3_create_error("Event Status ID is not valid: $value");
575 }
576 break;
577
578 case 'participant_status':
579 $status = CRM_Event_PseudoConstant::participantStatus();
fe7f4414 580 $values['participant_status_id'] = CRM_Utils_Array::key($value, $status);
c3fc9a44 581 break;
582
583 case 'participant_role_id':
584 case 'participant_role':
585 $role = CRM_Event_PseudoConstant::participantRole();
586 $participantRoles = explode(",", $value);
587 foreach ($participantRoles as $k => $v) {
588 $v = trim($v);
589 if ($key == 'participant_role') {
590 $participantRoles[$k] = CRM_Utils_Array::key($v, $role);
591 }
592 else {
593 $participantRoles[$k] = $v;
594 }
595 }
596 $values['role_id'] = implode(CRM_Core_DAO::VALUE_SEPARATOR, $participantRoles);
597 unset($values[$key]);
598 break;
599
600 default:
601 break;
602 }
603 }
604
605 if (array_key_exists('participant_note', $params)) {
606 $values['participant_note'] = $params['participant_note'];
607 }
608
609 // CRM_Event_BAO_Participant::create() handles register_date,
610 // status_id and source. So, if $values contains
611 // participant_register_date, participant_status_id or participant_source,
612 // convert it to register_date, status_id or source
be2fb01f 613 $changes = [
c3fc9a44 614 'participant_register_date' => 'register_date',
615 'participant_source' => 'source',
616 'participant_status_id' => 'status_id',
617 'participant_role_id' => 'role_id',
618 'participant_fee_level' => 'fee_level',
619 'participant_fee_amount' => 'fee_amount',
620 'participant_id' => 'id',
be2fb01f 621 ];
c3fc9a44 622
623 foreach ($changes as $orgVal => $changeVal) {
624 if (isset($values[$orgVal])) {
625 $values[$changeVal] = $values[$orgVal];
626 unset($values[$orgVal]);
627 }
628 }
629
630 return NULL;
631 }
632
880585a2 633 /**
880585a2 634 * @param array $params
635 * @param $onDuplicate
636 *
637 * @return array|bool
638 * <type>
4d935ea5 639 * @throws \CiviCRM_API3_Exception
640 * @deprecated - this is part of the import parser not the API & needs to be
641 * moved on out
642 *
880585a2 643 */
644 protected function deprecated_create_participant_formatted($params, $onDuplicate) {
645 if ($onDuplicate != CRM_Import_Parser::DUPLICATE_NOCHECK) {
646 CRM_Core_Error::reset();
1a5d4d8a 647 $error = $this->deprecated_participant_check_params($params, TRUE);
880585a2 648 if (civicrm_error($error)) {
649 return $error;
650 }
651 }
4d935ea5 652 return civicrm_api3('Participant', 'create', $params);
880585a2 653 }
654
1a5d4d8a 655 /**
656 * Formatting that was written a long time ago and may not make sense now.
657 *
658 * @param array $params
659 *
660 * @param bool $checkDuplicate
661 *
662 * @return array|bool
663 */
664 protected function deprecated_participant_check_params($params, $checkDuplicate = FALSE) {
665
666 // check if participant id is valid or not
667 if (!empty($params['id'])) {
668 $participant = new CRM_Event_BAO_Participant();
669 $participant->id = $params['id'];
670 if (!$participant->find(TRUE)) {
671 return civicrm_api3_create_error(ts('Participant id is not valid'));
672 }
673 }
674
675 // check if contact id is valid or not
676 if (!empty($params['contact_id'])) {
677 $contact = new CRM_Contact_BAO_Contact();
678 $contact->id = $params['contact_id'];
679 if (!$contact->find(TRUE)) {
680 return civicrm_api3_create_error(ts('Contact id is not valid'));
681 }
682 }
683
684 // check that event id is not an template
685 if (!empty($params['event_id'])) {
686 $isTemplate = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Event', $params['event_id'], 'is_template');
687 if (!empty($isTemplate)) {
688 return civicrm_api3_create_error(ts('Event templates are not meant to be registered.'));
689 }
690 }
691
692 $result = [];
693 if ($checkDuplicate) {
694 if (CRM_Event_BAO_Participant::checkDuplicate($params, $result)) {
695 $participantID = array_pop($result);
696
697 $error = CRM_Core_Error::createError("Found matching participant record.",
698 CRM_Core_Error::DUPLICATE_PARTICIPANT,
699 'Fatal', $participantID
700 );
701
702 return civicrm_api3_create_error($error->pop(),
703 [
704 'contactID' => $params['contact_id'],
705 'participantID' => $participantID,
706 ]
707 );
708 }
709 }
710 return TRUE;
711 }
712
d60cd664
EM
713 /**
714 * @param string $fileName
715 * @param string $separator
716 * @param $mapper
717 * @param bool $skipColumnHeader
718 * @param int $mode
719 * @param int $contactType
720 * @param int $onDuplicate
721 *
722 * @return mixed
723 * @throws Exception
724 */
725 public function run(
726 $fileName,
727 $separator,
728 $mapper,
729 $skipColumnHeader = FALSE,
730 $mode = self::MODE_PREVIEW,
731 $contactType = self::CONTACT_INDIVIDUAL,
732 $onDuplicate = self::DUPLICATE_SKIP
733 ) {
734 if (!is_array($fileName)) {
735 throw new CRM_Core_Exception('Unable to determine import file');
736 }
737 $fileName = $fileName['name'];
738
739 switch ($contactType) {
740 case self::CONTACT_INDIVIDUAL:
741 $this->_contactType = 'Individual';
742 break;
743
744 case self::CONTACT_HOUSEHOLD:
745 $this->_contactType = 'Household';
746 break;
747
748 case self::CONTACT_ORGANIZATION:
749 $this->_contactType = 'Organization';
750 }
751
752 $this->init();
753
754 $this->_haveColumnHeader = $skipColumnHeader;
755
756 $this->_separator = $separator;
757
758 $fd = fopen($fileName, "r");
759 if (!$fd) {
760 return FALSE;
761 }
762
06ef1cdc 763 $this->_lineCount = 0;
d60cd664
EM
764 $this->_invalidRowCount = $this->_validCount = 0;
765 $this->_totalCount = $this->_conflictCount = 0;
766
767 $this->_errors = [];
768 $this->_warnings = [];
769 $this->_conflicts = [];
770
771 $this->_fileSize = number_format(filesize($fileName) / 1024.0, 2);
772
773 if ($mode == self::MODE_MAPFIELD) {
774 $this->_rows = [];
775 }
776 else {
777 $this->_activeFieldCount = count($this->_activeFields);
778 }
779
780 while (!feof($fd)) {
781 $this->_lineCount++;
782
783 $values = fgetcsv($fd, 8192, $separator);
784 if (!$values) {
785 continue;
786 }
787
788 self::encloseScrub($values);
789
790 // skip column header if we're not in mapfield mode
791 if ($mode != self::MODE_MAPFIELD && $skipColumnHeader) {
792 $skipColumnHeader = FALSE;
793 continue;
794 }
795
796 /* trim whitespace around the values */
797
798 $empty = TRUE;
799 foreach ($values as $k => $v) {
800 $values[$k] = trim($v, " \t\r\n");
801 }
802
803 if (CRM_Utils_System::isNull($values)) {
804 continue;
805 }
806
807 $this->_totalCount++;
808
809 if ($mode == self::MODE_MAPFIELD) {
4ad623fc 810 $returnCode = CRM_Import_Parser::VALID;
d60cd664
EM
811 }
812 elseif ($mode == self::MODE_PREVIEW) {
813 $returnCode = $this->preview($values);
814 }
815 elseif ($mode == self::MODE_SUMMARY) {
816 $returnCode = $this->summary($values);
817 }
818 elseif ($mode == self::MODE_IMPORT) {
819 $returnCode = $this->import($onDuplicate, $values);
820 }
821 else {
822 $returnCode = self::ERROR;
823 }
824
825 // note that a line could be valid but still produce a warning
826 if ($returnCode & self::VALID) {
827 $this->_validCount++;
828 if ($mode == self::MODE_MAPFIELD) {
829 $this->_rows[] = $values;
830 $this->_activeFieldCount = max($this->_activeFieldCount, count($values));
831 }
832 }
833
d60cd664
EM
834 if ($returnCode & self::ERROR) {
835 $this->_invalidRowCount++;
836 $recordNumber = $this->_lineCount;
837 if ($this->_haveColumnHeader) {
838 $recordNumber--;
839 }
840 array_unshift($values, $recordNumber);
841 $this->_errors[] = $values;
842 }
843
844 if ($returnCode & self::CONFLICT) {
845 $this->_conflictCount++;
846 $recordNumber = $this->_lineCount;
847 if ($this->_haveColumnHeader) {
848 $recordNumber--;
849 }
850 array_unshift($values, $recordNumber);
851 $this->_conflicts[] = $values;
852 }
853
854 if ($returnCode & self::DUPLICATE) {
855 $this->_duplicateCount++;
856 $recordNumber = $this->_lineCount;
857 if ($this->_haveColumnHeader) {
858 $recordNumber--;
859 }
860 array_unshift($values, $recordNumber);
861 $this->_duplicates[] = $values;
862 if ($onDuplicate != self::DUPLICATE_SKIP) {
863 $this->_validCount++;
864 }
865 }
866
867 // we give the derived class a way of aborting the process
868 // note that the return code could be multiple code or'ed together
869 if ($returnCode & self::STOP) {
870 break;
871 }
872
873 // if we are done processing the maxNumber of lines, break
874 if ($this->_maxLinesToProcess > 0 && $this->_validCount >= $this->_maxLinesToProcess) {
875 break;
876 }
877 }
878
879 fclose($fd);
880
881 if ($mode == self::MODE_PREVIEW || $mode == self::MODE_IMPORT) {
882 $customHeaders = $mapper;
883
884 $customfields = CRM_Core_BAO_CustomField::getFields('Participant');
885 foreach ($customHeaders as $key => $value) {
886 if ($id = CRM_Core_BAO_CustomField::getKeyID($value)) {
887 $customHeaders[$key] = $customfields[$id][0];
888 }
889 }
890
891 if ($this->_invalidRowCount) {
892 // removed view url for invlaid contacts
893 $headers = array_merge([
894 ts('Line Number'),
895 ts('Reason'),
896 ], $customHeaders);
897 $this->_errorFileName = self::errorFileName(self::ERROR);
898 self::exportCSV($this->_errorFileName, $headers, $this->_errors);
899 }
900 if ($this->_conflictCount) {
901 $headers = array_merge([
902 ts('Line Number'),
903 ts('Reason'),
904 ], $customHeaders);
905 $this->_conflictFileName = self::errorFileName(self::CONFLICT);
906 self::exportCSV($this->_conflictFileName, $headers, $this->_conflicts);
907 }
908 if ($this->_duplicateCount) {
909 $headers = array_merge([
910 ts('Line Number'),
911 ts('View Participant URL'),
912 ], $customHeaders);
913
914 $this->_duplicateFileName = self::errorFileName(self::DUPLICATE);
915 self::exportCSV($this->_duplicateFileName, $headers, $this->_duplicates);
916 }
917 }
d60cd664
EM
918 }
919
920 /**
921 * Given a list of the importable field keys that the user has selected
922 * set the active fields array to this list
923 *
924 * @param array $fieldKeys mapped array of values
925 *
926 * @return void
927 */
928 public function setActiveFields($fieldKeys) {
929 $this->_activeFieldCount = count($fieldKeys);
930 foreach ($fieldKeys as $key) {
931 if (empty($this->_fields[$key])) {
932 $this->_activeFields[] = new CRM_Event_Import_Field('', ts('- do not import -'));
933 }
934 else {
935 $this->_activeFields[] = clone($this->_fields[$key]);
936 }
937 }
938 }
939
940 /**
941 * @param string $name
942 * @param $title
943 * @param int $type
944 * @param string $headerPattern
945 * @param string $dataPattern
946 */
947 public function addField($name, $title, $type = CRM_Utils_Type::T_INT, $headerPattern = '//', $dataPattern = '//') {
948 if (empty($name)) {
949 $this->_fields['doNotImport'] = new CRM_Event_Import_Field($name, $title, $type, $headerPattern, $dataPattern);
950 }
951 else {
952
953 //$tempField = CRM_Contact_BAO_Contact::importableFields('Individual', null );
954 $tempField = CRM_Contact_BAO_Contact::importableFields('All', NULL);
955 if (!array_key_exists($name, $tempField)) {
956 $this->_fields[$name] = new CRM_Event_Import_Field($name, $title, $type, $headerPattern, $dataPattern);
957 }
958 else {
959 $this->_fields[$name] = new CRM_Contact_Import_Field($name, $title, $type, $headerPattern, $dataPattern,
960 CRM_Utils_Array::value('hasLocationType', $tempField[$name])
961 );
962 }
963 }
964 }
965
966 /**
967 * Store parser values.
968 *
969 * @param CRM_Core_Session $store
970 *
971 * @param int $mode
972 *
973 * @return void
974 */
975 public function set($store, $mode = self::MODE_SUMMARY) {
976 $store->set('fileSize', $this->_fileSize);
977 $store->set('lineCount', $this->_lineCount);
978 $store->set('separator', $this->_separator);
979 $store->set('fields', $this->getSelectValues());
980 $store->set('fieldTypes', $this->getSelectTypes());
981
982 $store->set('headerPatterns', $this->getHeaderPatterns());
983 $store->set('dataPatterns', $this->getDataPatterns());
984 $store->set('columnCount', $this->_activeFieldCount);
985
986 $store->set('totalRowCount', $this->_totalCount);
987 $store->set('validRowCount', $this->_validCount);
988 $store->set('invalidRowCount', $this->_invalidRowCount);
989 $store->set('conflictRowCount', $this->_conflictCount);
990
991 switch ($this->_contactType) {
992 case 'Individual':
993 $store->set('contactType', CRM_Import_Parser::CONTACT_INDIVIDUAL);
994 break;
995
996 case 'Household':
997 $store->set('contactType', CRM_Import_Parser::CONTACT_HOUSEHOLD);
998 break;
999
1000 case 'Organization':
1001 $store->set('contactType', CRM_Import_Parser::CONTACT_ORGANIZATION);
1002 }
1003
1004 if ($this->_invalidRowCount) {
1005 $store->set('errorsFileName', $this->_errorFileName);
1006 }
1007 if ($this->_conflictCount) {
1008 $store->set('conflictsFileName', $this->_conflictFileName);
1009 }
1010 if (isset($this->_rows) && !empty($this->_rows)) {
1011 $store->set('dataValues', $this->_rows);
1012 }
1013
1014 if ($mode == self::MODE_IMPORT) {
1015 $store->set('duplicateRowCount', $this->_duplicateCount);
1016 if ($this->_duplicateCount) {
1017 $store->set('duplicatesFileName', $this->_duplicateFileName);
1018 }
1019 }
1020 }
1021
1022 /**
1023 * Export data to a CSV file.
1024 *
1025 * @param string $fileName
1026 * @param array $header
1027 * @param array $data
1028 *
1029 * @return void
1030 */
1031 public static function exportCSV($fileName, $header, $data) {
1032 $output = [];
1033 $fd = fopen($fileName, 'w');
1034
1035 foreach ($header as $key => $value) {
1036 $header[$key] = "\"$value\"";
1037 }
1038 $config = CRM_Core_Config::singleton();
1039 $output[] = implode($config->fieldSeparator, $header);
1040
1041 foreach ($data as $datum) {
1042 foreach ($datum as $key => $value) {
1043 if (is_array($value)) {
1044 foreach ($value[0] as $k1 => $v1) {
1045 if ($k1 == 'location_type_id') {
1046 continue;
1047 }
1048 $datum[$k1] = $v1;
1049 }
1050 }
1051 else {
1052 $datum[$key] = "\"$value\"";
1053 }
1054 }
1055 $output[] = implode($config->fieldSeparator, $datum);
1056 }
1057 fwrite($fd, implode("\n", $output));
1058 fclose($fd);
1059 }
1060
6a488035 1061}