Merge pull request #17828 from eileenmcnaughton/matt
[civicrm-core.git] / CRM / Event / Import / Form / MapField.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
18/**
19 * This class gets the name of the file to upload
20 */
b26295b8 21class CRM_Event_Import_Form_MapField extends CRM_Import_Form_MapField {
6a488035 22
6a488035 23 /**
fe482240 24 * Set variables up before form is built.
6a488035
TO
25 *
26 * @return void
6a488035
TO
27 */
28 public function preProcess() {
29 $this->_mapperFields = $this->get('fields');
30 asort($this->_mapperFields);
31 unset($this->_mapperFields['participant_is_test']);
32 $this->_columnCount = $this->get('columnCount');
33 $this->assign('columnCount', $this->_columnCount);
34 $this->_dataValues = $this->get('dataValues');
35 $this->assign('dataValues', $this->_dataValues);
36
353ffa53 37 $skipColumnHeader = $this->controller->exportValue('DataSource', 'skipColumnHeader');
6a488035 38 $this->_onDuplicate = $this->get('onDuplicate');
affcc9d2 39 $highlightedFields = [];
6a488035
TO
40 if ($skipColumnHeader) {
41 $this->assign('skipColumnHeader', $skipColumnHeader);
42 $this->assign('rowDisplayCount', 3);
43 /* if we had a column header to skip, stash it for later */
44
45 $this->_columnHeaders = $this->_dataValues[0];
46 }
47 else {
48 $this->assign('rowDisplayCount', 2);
49 }
a05662ef 50 if ($this->_onDuplicate == CRM_Import_Parser::DUPLICATE_UPDATE) {
6a488035
TO
51 $remove = array('participant_contact_id', 'email', 'first_name', 'last_name', 'external_identifier');
52 foreach ($remove as $value) {
53 unset($this->_mapperFields[$value]);
54 }
55 $highlightedFieldsArray = array('participant_id', 'event_id', 'event_title', 'participant_status_id');
56 foreach ($highlightedFieldsArray as $name) {
57 $highlightedFields[] = $name;
58 }
59 }
a05662ef
CW
60 elseif ($this->_onDuplicate == CRM_Import_Parser::DUPLICATE_SKIP ||
61 $this->_onDuplicate == CRM_Import_Parser::DUPLICATE_NOCHECK
6a488035
TO
62 ) {
63 unset($this->_mapperFields['participant_id']);
353ffa53
TO
64 $highlightedFieldsArray = array(
65 'participant_contact_id',
66 'event_id',
67 'email',
68 'first_name',
69 'last_name',
70 'external_identifier',
acb1052e 71 'participant_status_id',
353ffa53 72 );
6a488035
TO
73 foreach ($highlightedFieldsArray as $name) {
74 $highlightedFields[] = $name;
75 }
76 }
77 $this->assign('highlightedFields', $highlightedFields);
78 }
79
80 /**
fe482240 81 * Build the form object.
6a488035
TO
82 *
83 * @return void
6a488035
TO
84 */
85 public function buildQuickForm() {
86
87 //to save the current mappings
88 if (!$this->get('savedMapping')) {
89 $saveDetailsName = ts('Save this field mapping');
90 $this->applyFilter('saveMappingName', 'trim');
91 $this->add('text', 'saveMappingName', ts('Name'));
92 $this->add('text', 'saveMappingDesc', ts('Description'));
93 }
94 else {
95 $savedMapping = $this->get('savedMapping');
96
97 list($mappingName, $mappingContactType, $mappingLocation, $mappingPhoneType, $mappingRelation) = CRM_Core_BAO_Mapping::getMappingFields($savedMapping);
98
353ffa53 99 $mappingName = $mappingName[1];
6a488035 100 $mappingContactType = $mappingContactType[1];
9c1bc317
CW
101 $mappingLocation = $mappingLocation['1'] ?? NULL;
102 $mappingPhoneType = $mappingPhoneType['1'] ?? NULL;
103 $mappingRelation = $mappingRelation['1'] ?? NULL;
6a488035
TO
104
105 //mapping is to be loaded from database
106
353ffa53 107 $params = array('id' => $savedMapping);
affcc9d2 108 $temp = [];
6a488035
TO
109 $mappingDetails = CRM_Core_BAO_Mapping::retrieve($params, $temp);
110
111 $this->assign('loadedMapping', $mappingDetails->name);
112 $this->set('loadedMapping', $savedMapping);
113
114 $getMappingName = new CRM_Core_DAO_Mapping();
115 $getMappingName->id = $savedMapping;
116 $getMappingName->mapping_type = 'Import Participants';
117 $getMappingName->find();
118 while ($getMappingName->fetch()) {
119 $mapperName = $getMappingName->name;
120 }
121
122 $this->assign('savedName', $mapperName);
123
124 $this->add('hidden', 'mappingId', $savedMapping);
125
126 $this->addElement('checkbox', 'updateMapping', ts('Update this field mapping'), NULL);
127 $saveDetailsName = ts('Save as a new field mapping');
128 $this->add('text', 'saveMappingName', ts('Name'));
129 $this->add('text', 'saveMappingDesc', ts('Description'));
130 }
131
132 $this->addElement('checkbox', 'saveMapping', $saveDetailsName, NULL, array('onclick' => "showSaveDetails(this)"));
133
134 $this->addFormRule(array('CRM_Event_Import_Form_MapField', 'formRule'), $this);
135
affcc9d2 136 $defaults = [];
353ffa53
TO
137 $mapperKeys = array_keys($this->_mapperFields);
138 $hasHeaders = !empty($this->_columnHeaders);
139 $headerPatterns = $this->get('headerPatterns');
140 $dataPatterns = $this->get('dataPatterns');
6a488035 141 $hasLocationTypes = $this->get('fieldTypes');
6a488035
TO
142 /* Initialize all field usages to false */
143
144 foreach ($mapperKeys as $key) {
145 $this->_fieldUsed[$key] = FALSE;
146 }
b2b0530a 147 $this->_location_types = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id');
6a488035
TO
148 $sel1 = $this->_mapperFields;
149
150 $sel2[''] = NULL;
353ffa53 151 $js = "<script type='text/javascript'>\n";
6a488035
TO
152 $formName = 'document.forms.' . $this->_name;
153
154 //used to warn for mismatch column count or mismatch mapping
155 $warning = 0;
156 for ($i = 0; $i < $this->_columnCount; $i++) {
157 $sel = &$this->addElement('hierselect', "mapper[$i]", ts('Mapper for Field %1', array(1 => $i)), NULL);
158 $jsSet = FALSE;
159 if ($this->get('savedMapping')) {
160 if (isset($mappingName[$i])) {
161 if ($mappingName[$i] != ts('- do not import -')) {
162
163 $mappingHeader = array_keys($this->_mapperFields, $mappingName[$i]);
164
165 if (!isset($locationId) || !$locationId) {
166 $js .= "{$formName}['mapper[$i][1]'].style.display = 'none';\n";
167 }
168
169 if (!isset($phoneType) || !$phoneType) {
170 $js .= "{$formName}['mapper[$i][2]'].style.display = 'none';\n";
171 }
172
173 $js .= "{$formName}['mapper[$i][3]'].style.display = 'none';\n";
174 $defaults["mapper[$i]"] = array(
175 $mappingHeader[0],
176 (isset($locationId)) ? $locationId : "",
177 (isset($phoneType)) ? $phoneType : "",
178 );
179 $jsSet = TRUE;
180 }
181 else {
affcc9d2 182 $defaults["mapper[$i]"] = [];
6a488035
TO
183 }
184 if (!$jsSet) {
185 for ($k = 1; $k < 4; $k++) {
186 $js .= "{$formName}['mapper[$i][$k]'].style.display = 'none';\n";
187 }
188 }
189 }
190 else {
191 // this load section to help mapping if we ran out of saved columns when doing Load Mapping
192 $js .= "swapOptions($formName, 'mapper[$i]', 0, 3, 'hs_mapper_" . $i . "_');\n";
193
194 if ($hasHeaders) {
195 $defaults["mapper[$i]"] = array($this->defaultFromHeader($this->_columnHeaders[$i], $headerPatterns));
196 }
197 else {
198 $defaults["mapper[$i]"] = array($this->defaultFromData($dataPatterns, $i));
199 }
200 }
201 //end of load mapping
202 }
203 else {
204 $js .= "swapOptions($formName, 'mapper[$i]', 0, 3, 'hs_mapper_" . $i . "_');\n";
205 if ($hasHeaders) {
206 // Infer the default from the skipped headers if we have them
207 $defaults["mapper[$i]"] = array(
208 $this->defaultFromHeader($this->_columnHeaders[$i],
209 $headerPatterns
210 ),
211 // $defaultLocationType->id
212 0,
213 );
214 }
215 else {
216 // Otherwise guess the default from the form of the data
217 $defaults["mapper[$i]"] = array(
218 $this->defaultFromData($dataPatterns, $i),
219 // $defaultLocationType->id
220 0,
221 );
222 }
223 }
224 $sel->setOptions(array($sel1, $sel2, (isset($sel3)) ? $sel3 : "", (isset($sel4)) ? $sel4 : ""));
225 }
226 $js .= "</script>\n";
227 $this->assign('initHideBoxes', $js);
228
229 //set warning if mismatch in more than
230 if (isset($mappingName)) {
231 if (($this->_columnCount != count($mappingName))) {
232 $warning++;
233 }
234 }
235 if ($warning != 0 && $this->get('savedMapping')) {
236 $session = CRM_Core_Session::singleton();
237 $session->setStatus(ts('The data columns in this import file appear to be different from the saved mapping. Please verify that you have selected the correct saved mapping before continuing.'));
238 }
239 else {
240 $session = CRM_Core_Session::singleton();
241 $session->setStatus(NULL);
242 }
243
244 $this->setDefaults($defaults);
245
246 $this->addButtons(array(
90b461f1
SL
247 array(
248 'type' => 'back',
249 'name' => ts('Previous'),
250 ),
251 array(
252 'type' => 'next',
253 'name' => ts('Continue'),
254 'spacing' => '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;',
255 'isDefault' => TRUE,
256 ),
257 array(
258 'type' => 'cancel',
259 'name' => ts('Cancel'),
260 ),
261 ));
6a488035
TO
262 }
263
264 /**
fe482240 265 * Global validation rules for the form.
6a488035 266 *
d4dd1e85
TO
267 * @param array $fields
268 * Posted values of the form.
6a488035 269 *
da6b46f4
EM
270 * @param $files
271 * @param $self
272 *
a6c01b45
CW
273 * @return array
274 * list of errors to be posted back to the form
6a488035 275 */
00be9182 276 public static function formRule($fields, $files, $self) {
affcc9d2 277 $errors = [];
e1a33ae4 278 // define so we avoid notices below
279 $errors['_qf_default'] = '';
6a488035
TO
280 $fieldMessage = NULL;
281 if (!array_key_exists('savedMapping', $fields)) {
affcc9d2 282 $importKeys = [];
6a488035
TO
283 foreach ($fields['mapper'] as $mapperPart) {
284 $importKeys[] = $mapperPart[0];
285 }
286 // FIXME: should use the schema titles, not redeclare them
287 $requiredFields = array(
288 'participant_contact_id' => ts('Contact ID'),
289 'event_id' => ts('Event ID'),
290 );
291
292 $contactTypeId = $self->get('contactType');
293 $contactTypes = array(
a05662ef
CW
294 CRM_Import_Parser::CONTACT_INDIVIDUAL => 'Individual',
295 CRM_Import_Parser::CONTACT_HOUSEHOLD => 'Household',
296 CRM_Import_Parser::CONTACT_ORGANIZATION => 'Organization',
6a488035
TO
297 );
298 $params = array(
353ffa53 299 'used' => 'Unsupervised',
6a488035
TO
300 'contact_type' => $contactTypes[$contactTypeId],
301 );
302 list($ruleFields, $threshold) = CRM_Dedupe_BAO_RuleGroup::dedupeRuleFieldsWeight($params);
303 $weightSum = 0;
304 foreach ($importKeys as $key => $val) {
305 if (array_key_exists($val, $ruleFields)) {
306 $weightSum += $ruleFields[$val];
307 }
308 }
309 foreach ($ruleFields as $field => $weight) {
310 $fieldMessage .= ' ' . $field . '(weight ' . $weight . ')';
311 }
312
313 foreach ($requiredFields as $field => $title) {
314 if (!in_array($field, $importKeys)) {
315 if ($field == 'participant_contact_id') {
316 if ($weightSum >= $threshold || in_array('external_identifier', $importKeys) ||
317 in_array('participant_id', $importKeys)
318 ) {
319 continue;
320 }
a05662ef 321 if ($self->_onDuplicate == CRM_Import_Parser::DUPLICATE_UPDATE) {
ef30e8dc 322 $errors['_qf_default'] .= ts('Missing required field: Provide Participant ID') . '<br />';
6a488035
TO
323 }
324 else {
325 $errors['_qf_default'] .= ts('Missing required contact matching fields.') . " $fieldMessage " . ts('(Sum of all weights should be greater than or equal to threshold: %1).', array(
90b461f1
SL
326 1 => $threshold,
327 )) . ' ' . ts('Or Provide Contact ID or External ID.') . '<br />';
6a488035
TO
328 }
329 }
330 elseif (!in_array('event_title', $importKeys)) {
331 $errors['_qf_default'] .= ts('Missing required field: Provide %1 or %2',
353ffa53
TO
332 array(1 => $title, 2 => 'Event Title')
333 ) . '<br />';
6a488035
TO
334 }
335 }
336 }
337 }
338
a7488080 339 if (!empty($fields['saveMapping'])) {
9c1bc317 340 $nameField = $fields['saveMappingName'] ?? NULL;
6a488035
TO
341 if (empty($nameField)) {
342 $errors['saveMappingName'] = ts('Name is required to save Import Mapping');
343 }
344 else {
95f52e3b 345 if (CRM_Core_BAO_Mapping::checkMapping($nameField, CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Mapping', 'mapping_type_id', 'Import Participant'))) {
6a488035
TO
346 $errors['saveMappingName'] = ts('Duplicate Import Participant Mapping Name');
347 }
348 }
349 }
350
351 //display Error if loaded mapping is not selected
352 if (array_key_exists('loadMapping', $fields)) {
9c1bc317 353 $getMapName = $fields['savedMapping'] ?? NULL;
6a488035
TO
354 if (empty($getMapName)) {
355 $errors['savedMapping'] = ts('Select saved mapping');
356 }
357 }
358
e1a33ae4 359 if (empty($errors['_qf_default'])) {
360 unset($errors['_qf_default']);
361 }
6a488035
TO
362 if (!empty($errors)) {
363 if (!empty($errors['saveMappingName'])) {
364 $_flag = 1;
365 $assignError = new CRM_Core_Page();
366 $assignError->assign('mappingDetailsError', $_flag);
367 }
368 return $errors;
369 }
370
371 return TRUE;
372 }
373
374 /**
375 * Process the mapped fields and map it into the uploaded file
376 * preview the file and extract some summary statistics
377 *
378 * @return void
6a488035
TO
379 */
380 public function postProcess() {
381 $params = $this->controller->exportValues('MapField');
382 //reload the mapfield if load mapping is pressed
383 if (!empty($params['savedMapping'])) {
384 $this->set('savedMapping', $params['savedMapping']);
385 $this->controller->resetPage($this->_name);
386 return;
387 }
388
de7b9b56 389 $fileName = $this->controller->exportValue('DataSource', 'uploadFile');
063338e5 390 $seperator = $this->controller->exportValue('DataSource', 'fieldSeparator');
de7b9b56 391 $skipColumnHeader = $this->controller->exportValue('DataSource', 'skipColumnHeader');
6a488035 392
affcc9d2
CW
393 $mapperKeys = [];
394 $mapper = [];
353ffa53 395 $mapperKeys = $this->controller->exportValue($this->_name, 'mapper');
affcc9d2 396 $mapperKeysMain = [];
6a488035
TO
397
398 for ($i = 0; $i < $this->_columnCount; $i++) {
399 $mapper[$i] = $this->_mapperFields[$mapperKeys[$i][0]];
400 $mapperKeysMain[$i] = $mapperKeys[$i][0];
401 }
402
403 $this->set('mapper', $mapper);
404
405 // store mapping Id to display it in the preview page
406 $this->set('loadMappingId', CRM_Utils_Array::value('mappingId', $params));
407
408 //Updating Mapping Records
a7488080 409 if (!empty($params['updateMapping'])) {
6a488035
TO
410
411 $mappingFields = new CRM_Core_DAO_MappingField();
412 $mappingFields->mapping_id = $params['mappingId'];
413 $mappingFields->find();
414
affcc9d2 415 $mappingFieldsId = [];
6a488035
TO
416 while ($mappingFields->fetch()) {
417 if ($mappingFields->id) {
418 $mappingFieldsId[$mappingFields->column_number] = $mappingFields->id;
419 }
420 }
421
422 for ($i = 0; $i < $this->_columnCount; $i++) {
423 $updateMappingFields = new CRM_Core_DAO_MappingField();
424 $updateMappingFields->id = $mappingFieldsId[$i];
425 $updateMappingFields->mapping_id = $params['mappingId'];
426 $updateMappingFields->column_number = $i;
427
428 $explodedValues = explode('_', $mapperKeys[$i][0]);
9c1bc317
CW
429 $id = $explodedValues[0] ?? NULL;
430 $first = $explodedValues[1] ?? NULL;
431 $second = $explodedValues[2] ?? NULL;
6a488035
TO
432
433 $updateMappingFields->name = $mapper[$i];
434 $updateMappingFields->save();
435 }
436 }
437
438 //Saving Mapping Details and Records
a7488080 439 if (!empty($params['saveMapping'])) {
6a488035
TO
440 $mappingParams = array(
441 'name' => $params['saveMappingName'],
442 'description' => $params['saveMappingDesc'],
95f52e3b 443 'mapping_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Mapping', 'mapping_type_id', 'Import Participant'),
6a488035
TO
444 );
445 $saveMapping = CRM_Core_BAO_Mapping::add($mappingParams);
446
447 for ($i = 0; $i < $this->_columnCount; $i++) {
448 $saveMappingFields = new CRM_Core_DAO_MappingField();
449 $saveMappingFields->mapping_id = $saveMapping->id;
450 $saveMappingFields->column_number = $i;
451
452 $explodedValues = explode('_', $mapperKeys[$i][0]);
9c1bc317
CW
453 $id = $explodedValues[0] ?? NULL;
454 $first = $explodedValues[1] ?? NULL;
455 $second = $explodedValues[2] ?? NULL;
6a488035
TO
456
457 $saveMappingFields->name = $mapper[$i];
458 $saveMappingFields->save();
459 }
460 $this->set('savedMapping', $saveMappingFields->mapping_id);
461 }
462
463 $parser = new CRM_Event_Import_Parser_Participant($mapperKeysMain);
464 $parser->run($fileName, $seperator, $mapper, $skipColumnHeader,
a05662ef 465 CRM_Import_Parser::MODE_PREVIEW, $this->get('contactType')
6a488035
TO
466 );
467 // add all the necessary variables to the form
468 $parser->set($this);
469 }
96025800 470
6a488035 471}