Merge pull request #16429 from ixiam/dev/core#1113
[civicrm-core.git] / CRM / Contact / Import / Form / MapField.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 * This class gets the name of the file to upload.
20 */
21 class CRM_Contact_Import_Form_MapField extends CRM_Import_Form_MapField {
22
23 use CRM_Contact_Import_MetadataTrait;
24
25 /**
26 * An array of all contact fields with
27 * formatted custom field names.
28 *
29 * @var array
30 */
31 protected $_formattedFieldNames;
32
33 /**
34 * On duplicate.
35 *
36 * @var int
37 */
38 public $_onDuplicate;
39
40 protected $_dedupeFields;
41
42 protected static $customFields;
43
44 /**
45 * Attempt to match header labels with our mapper fields.
46 *
47 * FIXME: This is essentially the same function as parent::defaultFromHeader
48 *
49 * @param string $columnName name of column header
50 *
51 * @return string
52 */
53 public function defaultFromColumnName($columnName) {
54
55 if (!preg_match('/^[a-z0-9 ]$/i', $columnName)) {
56 if ($columnKey = array_search($columnName, $this->getFieldTitles())) {
57 $this->_fieldUsed[$columnKey] = TRUE;
58 return $columnKey;
59 }
60 }
61
62 foreach ($this->getHeaderPatterns() as $key => $re) {
63 // Skip empty key/patterns
64 if (!$key || !$re || strlen("$re") < 5) {
65 continue;
66 }
67
68 if (preg_match($re, $columnName)) {
69 $this->_fieldUsed[$key] = TRUE;
70 return $key;
71 }
72 }
73 return '';
74 }
75
76 /**
77 * Set variables up before form is built.
78 */
79 public function preProcess() {
80 $dataSource = $this->get('dataSource');
81 $skipColumnHeader = $this->get('skipColumnHeader');
82 $this->_mapperFields = $this->get('fields');
83 $this->_importTableName = $this->get('importTableName');
84 $this->_onDuplicate = $this->get('onDuplicate');
85 $this->_contactSubType = $this->get('contactSubType');
86 $highlightedFields = [];
87 $highlightedFields[] = 'email';
88 $highlightedFields[] = 'external_identifier';
89 //format custom field names, CRM-2676
90 switch ($this->get('contactType')) {
91 case CRM_Import_Parser::CONTACT_INDIVIDUAL:
92 $contactType = 'Individual';
93 $highlightedFields[] = 'first_name';
94 $highlightedFields[] = 'last_name';
95 break;
96
97 case CRM_Import_Parser::CONTACT_HOUSEHOLD:
98 $contactType = 'Household';
99 $highlightedFields[] = 'household_name';
100 break;
101
102 case CRM_Import_Parser::CONTACT_ORGANIZATION:
103 $contactType = 'Organization';
104 $highlightedFields[] = 'organization_name';
105 break;
106 }
107 $this->_contactType = $contactType;
108 if ($this->_onDuplicate == CRM_Import_Parser::DUPLICATE_SKIP) {
109 unset($this->_mapperFields['id']);
110 }
111 else {
112 $highlightedFields[] = 'id';
113 }
114
115 if ($this->_onDuplicate != CRM_Import_Parser::DUPLICATE_NOCHECK) {
116 //Mark Dedupe Rule Fields as required, since it's used in matching contact
117 foreach (['Individual', 'Household', 'Organization'] as $cType) {
118 $ruleParams = [
119 'contact_type' => $cType,
120 'used' => 'Unsupervised',
121 ];
122 $this->_dedupeFields[$cType] = CRM_Dedupe_BAO_Rule::dedupeRuleFields($ruleParams);
123 }
124
125 //Modify mapper fields title if fields are present in dedupe rule
126 if (is_array($this->_dedupeFields[$contactType])) {
127 foreach ($this->_dedupeFields[$contactType] as $val) {
128 if ($valTitle = CRM_Utils_Array::value($val, $this->_mapperFields)) {
129 $this->_mapperFields[$val] = $valTitle . ' (match to contact)';
130 }
131 }
132 }
133 }
134 // retrieve and highlight required custom fields
135 $formattedFieldNames = $this->formatCustomFieldName($this->_mapperFields);
136 self::$customFields = CRM_Core_BAO_CustomField::getFields($this->_contactType);
137 foreach (self::$customFields as $key => $attr) {
138 if (!empty($attr['is_required'])) {
139 $highlightedFields[] = "custom_$key";
140 }
141 }
142 $this->assign('highlightedFields', $highlightedFields);
143 $this->_formattedFieldNames[$contactType] = $this->_mapperFields = array_merge($this->_mapperFields, $formattedFieldNames);
144
145 $columnNames = [];
146 //get original col headers from csv if present.
147 if ($dataSource == 'CRM_Import_DataSource_CSV' && $skipColumnHeader) {
148 $columnNames = $this->get('originalColHeader');
149 }
150 else {
151 // get the field names from the temp. DB table
152 $dao = new CRM_Core_DAO();
153 $db = $dao->getDatabaseConnection();
154
155 $columnsQuery = "SHOW FIELDS FROM $this->_importTableName
156 WHERE Field NOT LIKE '\_%'";
157 $columnsResult = $db->query($columnsQuery);
158 while ($row = $columnsResult->fetchRow(DB_FETCHMODE_ASSOC)) {
159 $columnNames[] = $row['Field'];
160 }
161 }
162
163 $showColNames = TRUE;
164 if ($dataSource === 'CRM_Import_DataSource_CSV' && !$skipColumnHeader) {
165 $showColNames = FALSE;
166 }
167 $this->assign('showColNames', $showColNames);
168
169 $this->_columnCount = count($columnNames);
170 $this->_columnNames = $columnNames;
171 $this->assign('columnNames', $columnNames);
172 //$this->_columnCount = $this->get( 'columnCount' );
173 $this->assign('columnCount', $this->_columnCount);
174 $this->_dataValues = $this->get('dataValues');
175 $this->assign('dataValues', $this->_dataValues);
176 $this->assign('rowDisplayCount', 2);
177 }
178
179 /**
180 * Build the form object.
181 *
182 * @throws \CiviCRM_API3_Exception
183 */
184 public function buildQuickForm() {
185 $savedMappingID = (int) $this->get('savedMapping');
186 $this->buildSavedMappingFields($savedMappingID);
187
188 $this->addFormRule(['CRM_Contact_Import_Form_MapField', 'formRule']);
189
190 //-------- end of saved mapping stuff ---------
191
192 $defaults = [];
193 $mapperKeys = array_keys($this->_mapperFields);
194 $hasColumnNames = !empty($this->_columnNames);
195 $hasLocationTypes = $this->get('fieldTypes');
196
197 $this->_location_types = ['Primary' => ts('Primary')] + CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id');
198 $defaultLocationType = CRM_Core_BAO_LocationType::getDefault();
199
200 // Pass default location to js
201 if ($defaultLocationType) {
202 $this->assign('defaultLocationType', $defaultLocationType->id);
203 $this->assign('defaultLocationTypeLabel', $this->_location_types[$defaultLocationType->id]);
204 }
205
206 /* Initialize all field usages to false */
207 foreach ($mapperKeys as $key) {
208 $this->_fieldUsed[$key] = FALSE;
209 }
210
211 $sel1 = $this->_mapperFields;
212 $sel2[''] = NULL;
213
214 $phoneTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Phone', 'phone_type_id');
215 $imProviders = CRM_Core_PseudoConstant::get('CRM_Core_DAO_IM', 'provider_id');
216 $websiteTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Website', 'website_type_id');
217
218 foreach ($this->_location_types as $key => $value) {
219 $sel3['phone'][$key] = &$phoneTypes;
220 //build array for IM service provider type for contact
221 $sel3['im'][$key] = &$imProviders;
222 }
223
224 $sel4 = NULL;
225
226 // store and cache all relationship types
227 $contactRelation = new CRM_Contact_DAO_RelationshipType();
228 $contactRelation->find();
229 while ($contactRelation->fetch()) {
230 $contactRelationCache[$contactRelation->id] = [];
231 $contactRelationCache[$contactRelation->id]['contact_type_a'] = $contactRelation->contact_type_a;
232 $contactRelationCache[$contactRelation->id]['contact_sub_type_a'] = $contactRelation->contact_sub_type_a;
233 $contactRelationCache[$contactRelation->id]['contact_type_b'] = $contactRelation->contact_type_b;
234 $contactRelationCache[$contactRelation->id]['contact_sub_type_b'] = $contactRelation->contact_sub_type_b;
235 }
236 $highlightedFields = $highlightedRelFields = [];
237
238 $highlightedFields['email'] = 'All';
239 $highlightedFields['external_identifier'] = 'All';
240 $highlightedFields['first_name'] = 'Individual';
241 $highlightedFields['last_name'] = 'Individual';
242 $highlightedFields['household_name'] = 'Household';
243 $highlightedFields['organization_name'] = 'Organization';
244
245 foreach ($mapperKeys as $key) {
246 // check if there is a _a_b or _b_a in the key
247 if (strpos($key, '_a_b') || strpos($key, '_b_a')) {
248 list($id, $first, $second) = explode('_', $key);
249 }
250 else {
251 $id = $first = $second = NULL;
252 }
253 if (($first === 'a' && $second === 'b') || ($first === 'b' && $second === 'a')) {
254 $cType = $contactRelationCache[$id]["contact_type_{$second}"];
255
256 //CRM-5125 for contact subtype specific relationshiptypes
257 $cSubType = NULL;
258 if (!empty($contactRelationCache[$id]["contact_sub_type_{$second}"])) {
259 $cSubType = $contactRelationCache[$id]["contact_sub_type_{$second}"];
260 }
261
262 if (!$cType) {
263 $cType = 'All';
264 }
265
266 $relatedFields = CRM_Contact_BAO_Contact::importableFields($cType);
267 unset($relatedFields['']);
268 $values = [];
269 foreach ($relatedFields as $name => $field) {
270 $values[$name] = $field['title'];
271 if (isset($hasLocationTypes[$name])) {
272 $sel3[$key][$name] = $this->_location_types;
273 }
274 elseif ($name === 'url') {
275 $sel3[$key][$name] = $websiteTypes;
276 }
277 else {
278 $sel3[$name] = NULL;
279 }
280 }
281
282 //fix to append custom group name to field name, CRM-2676
283 if (empty($this->_formattedFieldNames[$cType]) || $cType == $this->_contactType) {
284 $this->_formattedFieldNames[$cType] = $this->formatCustomFieldName($values);
285 }
286
287 $this->_formattedFieldNames[$cType] = array_merge($values, $this->_formattedFieldNames[$cType]);
288
289 //Modified the Relationship fields if the fields are
290 //present in dedupe rule
291 if ($this->_onDuplicate != CRM_Import_Parser::DUPLICATE_NOCHECK && !empty($this->_dedupeFields[$cType]) &&
292 is_array($this->_dedupeFields[$cType])
293 ) {
294 static $cTypeArray = [];
295 if ($cType != $this->_contactType && !in_array($cType, $cTypeArray)) {
296 foreach ($this->_dedupeFields[$cType] as $val) {
297 if ($valTitle = CRM_Utils_Array::value($val, $this->_formattedFieldNames[$cType])) {
298 $this->_formattedFieldNames[$cType][$val] = $valTitle . ' (match to contact)';
299 }
300 }
301 $cTypeArray[] = $cType;
302 }
303 }
304
305 foreach ($highlightedFields as $k => $v) {
306 if ($v == $cType || $v === 'All') {
307 $highlightedRelFields[$key][] = $k;
308 }
309 }
310 $this->assign('highlightedRelFields', $highlightedRelFields);
311 $sel2[$key] = $this->_formattedFieldNames[$cType];
312
313 if (!empty($cSubType)) {
314 //custom fields for sub type
315 $subTypeFields = CRM_Core_BAO_CustomField::getFieldsForImport($cSubType);
316
317 if (!empty($subTypeFields)) {
318 $subType = NULL;
319 foreach ($subTypeFields as $customSubTypeField => $details) {
320 $subType[$customSubTypeField] = $details['title'];
321 $sel2[$key] = array_merge($sel2[$key], $this->formatCustomFieldName($subType));
322 }
323 }
324 }
325
326 foreach ($this->_location_types as $k => $value) {
327 $sel4[$key]['phone'][$k] = &$phoneTypes;
328 //build array of IM service provider for related contact
329 $sel4[$key]['im'][$k] = &$imProviders;
330 }
331 }
332 else {
333 $options = NULL;
334 if (!empty($hasLocationTypes[$key])) {
335 $options = $this->_location_types;
336 }
337 elseif ($key === 'url') {
338 $options = $websiteTypes;
339 }
340 $sel2[$key] = $options;
341 }
342 }
343
344 $js = "<script type='text/javascript'>\n";
345 $formName = 'document.forms.' . $this->_name;
346 //used to warn for mismatch column count or mismatch mapping
347 CRM_Core_Session::singleton()->setStatus(NULL);
348 $processor = new CRM_Import_ImportProcessor();
349 $processor->setMappingID($savedMappingID);
350 $processor->setFormName($formName);
351 $processor->setMetadata($this->getContactImportMetadata());
352 $processor->setContactTypeByConstant($this->get('contactType'));
353 $processor->setContactSubType($this->get('contactSubType'));
354
355 for ($i = 0; $i < $this->_columnCount; $i++) {
356 $sel = &$this->addElement('hierselect', "mapper[$i]", ts('Mapper for Field %1', [1 => $i]), NULL);
357
358 if ($this->get('savedMapping') && $processor->getFieldName($i)) {
359 $defaults["mapper[$i]"] = $processor->getSavedQuickformDefaultsForColumn($i);
360 $js .= $processor->getQuickFormJSForField($i);
361 }
362 else {
363 $js .= "swapOptions($formName, 'mapper[$i]', 0, 3, 'hs_mapper_0_');\n";
364 if ($hasColumnNames) {
365 // do array search first to see if has mapped key
366 $columnKey = array_search($this->_columnNames[$i], $this->getFieldTitles());
367 if (isset($this->_fieldUsed[$columnKey])) {
368 $defaults["mapper[$i]"] = $columnKey;
369 $this->_fieldUsed[$key] = TRUE;
370 }
371 else {
372 // Infer the default from the column names if we have them
373 $defaults["mapper[$i]"] = [
374 $this->defaultFromColumnName($this->_columnNames[$i]),
375 0,
376 ];
377 }
378 }
379 else {
380 // Otherwise guess the default from the form of the data
381 $defaults["mapper[$i]"] = [
382 $this->defaultFromData($this->getDataPatterns(), $i),
383 // $defaultLocationType->id
384 0,
385 ];
386 }
387 }
388 $sel->setOptions([$sel1, $sel2, $sel3, $sel4]);
389 }
390
391 $js .= "</script>\n";
392 $this->assign('initHideBoxes', $js);
393
394 //set warning if mismatch in more than
395 if (isset($mappingName) &&
396 ($this->_columnCount != count($mappingName))
397 ) {
398 CRM_Core_Session::singleton()->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.'));
399 }
400
401 $this->setDefaults($defaults);
402
403 $this->addButtons([
404 [
405 'type' => 'back',
406 'name' => ts('Previous'),
407 ],
408 [
409 'type' => 'next',
410 'name' => ts('Continue'),
411 'spacing' => '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;',
412 'isDefault' => TRUE,
413 ],
414 [
415 'type' => 'cancel',
416 'name' => ts('Cancel'),
417 ],
418 ]);
419 }
420
421 /**
422 * Global validation rules for the form.
423 *
424 * @param array $fields
425 * Posted values of the form.
426 *
427 * @return array
428 * list of errors to be posted back to the form
429 */
430 public static function formRule($fields) {
431 $errors = [];
432 if (!empty($fields['saveMapping'])) {
433 $nameField = $fields['saveMappingName'] ?? NULL;
434 if (empty($nameField)) {
435 $errors['saveMappingName'] = ts('Name is required to save Import Mapping');
436 }
437 else {
438 $mappingTypeId = CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Mapping', 'mapping_type_id', 'Import Contact');
439 if (CRM_Core_BAO_Mapping::checkMapping($nameField, $mappingTypeId)) {
440 $errors['saveMappingName'] = ts('Duplicate Import Mapping Name');
441 }
442 }
443 }
444 $template = CRM_Core_Smarty::singleton();
445 if (!empty($fields['saveMapping'])) {
446 $template->assign('isCheked', TRUE);
447 }
448
449 if (!empty($errors)) {
450 $_flag = 1;
451 $assignError = new CRM_Core_Page();
452 $assignError->assign('mappingDetailsError', $_flag);
453 return $errors;
454 }
455 else {
456 return TRUE;
457 }
458 }
459
460 /**
461 * Process the mapped fields and map it into the uploaded file.
462 */
463 public function postProcess() {
464 $params = $this->controller->exportValues('MapField');
465
466 //reload the mapfield if load mapping is pressed
467 if (!empty($params['savedMapping'])) {
468 $this->set('savedMapping', $params['savedMapping']);
469 $this->controller->resetPage($this->_name);
470 return;
471 }
472 $mapperKeys = $this->controller->exportValue($this->_name, 'mapper');
473
474 $parser = $this->submit($params, $mapperKeys);
475
476 // add all the necessary variables to the form
477 $parser->set($this);
478 }
479
480 /**
481 * Format custom field name.
482 *
483 * Combine group and field name to avoid conflict.
484 *
485 * @param array $fields
486 *
487 * @return array
488 */
489 public function formatCustomFieldName($fields) {
490 //CRM-2676, replacing the conflict for same custom field name from different custom group.
491 $fieldIds = $formattedFieldNames = [];
492 foreach ($fields as $key => $value) {
493 if ($customFieldId = CRM_Core_BAO_CustomField::getKeyID($key)) {
494 $fieldIds[] = $customFieldId;
495 }
496 }
497
498 if (!empty($fieldIds) && is_array($fieldIds)) {
499 $groupTitles = CRM_Core_BAO_CustomGroup::getGroupTitles($fieldIds);
500
501 if (!empty($groupTitles)) {
502 foreach ($groupTitles as $fId => $values) {
503 $key = "custom_{$fId}";
504 $groupTitle = $values['groupTitle'];
505 $formattedFieldNames[$key] = $fields[$key] . ' :: ' . $groupTitle;
506 }
507 }
508 }
509
510 return $formattedFieldNames;
511 }
512
513 /**
514 * Main submit function.
515 *
516 * Extracted to add testing & start refactoring.
517 *
518 * @param $params
519 * @param $mapperKeys
520 *
521 * @return \CRM_Contact_Import_Parser_Contact
522 * @throws \CiviCRM_API3_Exception
523 */
524 public function submit($params, $mapperKeys) {
525 $mapper = $mapperKeysMain = $locations = [];
526 $parserParameters = CRM_Contact_Import_Parser_Contact::getParameterForParser($this->_columnCount);
527
528 $phoneTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Phone', 'phone_type_id');
529 $imProviders = CRM_Core_PseudoConstant::get('CRM_Core_DAO_IM', 'provider_id');
530 $websiteTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Website', 'website_type_id');
531 $locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id');
532 $locationTypes['Primary'] = ts('Primary');
533
534 for ($i = 0; $i < $this->_columnCount; $i++) {
535
536 $fldName = $mapperKeys[$i][0] ?? NULL;
537 $selOne = $mapperKeys[$i][1] ?? NULL;
538 $selTwo = $mapperKeys[$i][2] ?? NULL;
539 $selThree = $mapperKeys[$i][3] ?? NULL;
540 $mapper[$i] = $this->_mapperFields[$mapperKeys[$i][0]];
541 $mapperKeysMain[$i] = $fldName;
542
543 //need to differentiate non location elements.
544 if ($selOne && (is_numeric($selOne) || $selOne === 'Primary')) {
545 if ($fldName === 'url') {
546 $parserParameters['mapperWebsiteType'][$i] = $websiteTypes[$selOne];
547 }
548 else {
549 $locations[$i] = $locationTypes[$selOne];
550 $parserParameters['mapperLocType'][$i] = $selOne;
551 if ($selTwo && is_numeric($selTwo)) {
552 if ($fldName === 'phone') {
553 $parserParameters['mapperPhoneType'][$i] = $phoneTypes[$selTwo];
554 }
555 elseif ($fldName === 'im') {
556 $parserParameters['mapperImProvider'][$i] = $imProviders[$selTwo];
557 }
558 }
559 }
560 }
561
562 //relationship contact mapper info.
563 list($id, $first, $second) = CRM_Utils_System::explode('_', $fldName, 3);
564 if (($first === 'a' && $second === 'b') ||
565 ($first === 'b' && $second === 'a')
566 ) {
567 $parserParameters['mapperRelated'][$i] = $this->_mapperFields[$fldName];
568 if ($selOne) {
569 if ($selOne === 'url') {
570 $parserParameters['relatedContactWebsiteType'][$i] = $websiteTypes[$selTwo];
571 }
572 else {
573 $parserParameters['relatedContactLocType'][$i] = $locationTypes[$selTwo] ?? NULL;
574 if ($selThree) {
575 if ($selOne === 'phone') {
576 $parserParameters['relatedContactPhoneType'][$i] = $phoneTypes[$selThree];
577 }
578 elseif ($selOne === 'im') {
579 $parserParameters['relatedContactImProvider'][$i] = $imProviders[$selThree];
580 }
581 }
582 }
583
584 //get the related contact type.
585 $relationType = new CRM_Contact_DAO_RelationshipType();
586 $relationType->id = $id;
587 $relationType->find(TRUE);
588 $parserParameters['relatedContactType'][$i] = $relationType->{"contact_type_$second"};
589 $parserParameters['relatedContactDetails'][$i] = $this->_formattedFieldNames[$parserParameters['relatedContactType'][$i]][$selOne];
590 }
591 }
592 }
593
594 $this->set('columnNames', $this->_columnNames);
595 $this->set('websites', $parserParameters['mapperWebsiteType']);
596 $this->set('locations', $locations);
597 $this->set('phones', $parserParameters['mapperPhoneType']);
598 $this->set('ims', $parserParameters['mapperImProvider']);
599 $this->set('related', $parserParameters['mapperRelated']);
600 $this->set('relatedContactType', $parserParameters['relatedContactType']);
601 $this->set('relatedContactDetails', $parserParameters['relatedContactDetails']);
602 $this->set('relatedContactLocType', $parserParameters['relatedContactLocType']);
603 $this->set('relatedContactPhoneType', $parserParameters['relatedContactPhoneType']);
604 $this->set('relatedContactImProvider', $parserParameters['relatedContactImProvider']);
605 $this->set('relatedContactWebsiteType', $parserParameters['relatedContactWebsiteType']);
606 $this->set('mapper', $mapper);
607
608 // store mapping Id to display it in the preview page
609 $this->set('loadMappingId', CRM_Utils_Array::value('mappingId', $params));
610
611 //Updating Mapping Records
612 if (!empty($params['updateMapping'])) {
613
614 $mappingFields = new CRM_Core_DAO_MappingField();
615 $mappingFields->mapping_id = $params['mappingId'];
616 $mappingFields->find();
617
618 $mappingFieldsId = [];
619 while ($mappingFields->fetch()) {
620 if ($mappingFields->id) {
621 $mappingFieldsId[$mappingFields->column_number] = $mappingFields->id;
622 }
623 }
624
625 for ($i = 0; $i < $this->_columnCount; $i++) {
626 $updateMappingFields = new CRM_Core_DAO_MappingField();
627 $updateMappingFields->id = $mappingFieldsId[$i] ?? NULL;
628 $updateMappingFields->mapping_id = $params['mappingId'];
629 $updateMappingFields->column_number = $i;
630
631 $mapperKeyParts = explode('_', $mapperKeys[$i][0], 3);
632 $id = $mapperKeyParts[0] ?? NULL;
633 $first = $mapperKeyParts[1] ?? NULL;
634 $second = $mapperKeyParts[2] ?? NULL;
635 if (($first == 'a' && $second == 'b') || ($first == 'b' && $second == 'a')) {
636 $updateMappingFields->relationship_type_id = $id;
637 $updateMappingFields->relationship_direction = "{$first}_{$second}";
638 $updateMappingFields->name = ucwords(str_replace("_", " ", $mapperKeys[$i][1]));
639 // get phoneType id and provider id separately
640 // before updating mappingFields of phone and IM for related contact, CRM-3140
641 if (CRM_Utils_Array::value('1', $mapperKeys[$i]) == 'url') {
642 $updateMappingFields->website_type_id = $mapperKeys[$i][2] ?? NULL;
643 }
644 else {
645 if (CRM_Utils_Array::value('1', $mapperKeys[$i]) == 'phone') {
646 $updateMappingFields->phone_type_id = $mapperKeys[$i][3] ?? NULL;
647 }
648 elseif (CRM_Utils_Array::value('1', $mapperKeys[$i]) == 'im') {
649 $updateMappingFields->im_provider_id = $mapperKeys[$i][3] ?? NULL;
650 }
651 $updateMappingFields->location_type_id = isset($mapperKeys[$i][2]) && is_numeric($mapperKeys[$i][2]) ? $mapperKeys[$i][2] : NULL;
652 }
653 }
654 else {
655 $updateMappingFields->name = $mapper[$i];
656 $updateMappingFields->relationship_type_id = 'NULL';
657 $updateMappingFields->relationship_type_direction = 'NULL';
658 // to store phoneType id and provider id separately
659 // before updating mappingFields for phone and IM, CRM-3140
660 if (CRM_Utils_Array::value('0', $mapperKeys[$i]) == 'url') {
661 $updateMappingFields->website_type_id = $mapperKeys[$i][1] ?? NULL;
662 }
663 else {
664 if (CRM_Utils_Array::value('0', $mapperKeys[$i]) == 'phone') {
665 $updateMappingFields->phone_type_id = $mapperKeys[$i][2] ?? NULL;
666 }
667 elseif (CRM_Utils_Array::value('0', $mapperKeys[$i]) == 'im') {
668 $updateMappingFields->im_provider_id = $mapperKeys[$i][2] ?? NULL;
669 }
670 $locationTypeID = $parserParameters['mapperLocType'][$i];
671 // location_type_id is NULL for non-location fields, and for Primary location.
672 $updateMappingFields->location_type_id = is_numeric($locationTypeID) ? $locationTypeID : 'null';
673 }
674 }
675 $updateMappingFields->save();
676 }
677 }
678
679 //Saving Mapping Details and Records
680 if (!empty($params['saveMapping'])) {
681 $mappingParams = [
682 'name' => $params['saveMappingName'],
683 'description' => $params['saveMappingDesc'],
684 'mapping_type_id' => 'Import Contact',
685 ];
686
687 $saveMapping = civicrm_api3('Mapping', 'create', $mappingParams);
688
689 $contactType = $this->get('contactType');
690 switch ($contactType) {
691 case CRM_Import_Parser::CONTACT_INDIVIDUAL:
692 $cType = 'Individual';
693 break;
694
695 case CRM_Import_Parser::CONTACT_HOUSEHOLD:
696 $cType = 'Household';
697 break;
698
699 case CRM_Import_Parser::CONTACT_ORGANIZATION:
700 $cType = 'Organization';
701 }
702
703 $mappingID = NULL;
704 for ($i = 0; $i < $this->_columnCount; $i++) {
705 $mappingID = $this->saveMappingField($mapperKeys, $saveMapping, $cType, $i, $mapper, $parserParameters);
706 }
707 $this->set('savedMapping', $mappingID);
708 }
709
710 $parser = new CRM_Contact_Import_Parser_Contact($mapperKeysMain, $parserParameters['mapperLocType'], $parserParameters['mapperPhoneType'],
711 $parserParameters['mapperImProvider'], $parserParameters['mapperRelated'], $parserParameters['relatedContactType'],
712 $parserParameters['relatedContactDetails'], $parserParameters['relatedContactLocType'],
713 $parserParameters['relatedContactPhoneType'], $parserParameters['relatedContactImProvider'],
714 $parserParameters['mapperWebsiteType'], $parserParameters['relatedContactWebsiteType']
715 );
716
717 $primaryKeyName = $this->get('primaryKeyName');
718 $statusFieldName = $this->get('statusFieldName');
719 $parser->run($this->_importTableName,
720 $mapper,
721 CRM_Import_Parser::MODE_PREVIEW,
722 $this->get('contactType'),
723 $primaryKeyName,
724 $statusFieldName,
725 $this->_onDuplicate,
726 NULL, NULL, FALSE,
727 CRM_Contact_Import_Parser::DEFAULT_TIMEOUT,
728 $this->get('contactSubType'),
729 $this->get('dedupe')
730 );
731 return $parser;
732 }
733
734 /**
735 * @param $mapperKeys
736 * @param array $saveMapping
737 * @param string $cType
738 * @param int $i
739 * @param array $mapper
740 * @param array $parserParameters
741 *
742 * @return int
743 */
744 protected function saveMappingField($mapperKeys, array $saveMapping, string $cType, int $i, array $mapper, array $parserParameters): int {
745 $saveMappingFields = new CRM_Core_DAO_MappingField();
746 $saveMappingFields->mapping_id = $saveMapping['id'];
747 $saveMappingFields->contact_type = $cType;
748 $saveMappingFields->column_number = $i;
749
750 $mapperKeyParts = explode('_', $mapperKeys[$i][0], 3);
751 $id = $mapperKeyParts[0] ?? NULL;
752 $first = $mapperKeyParts[1] ?? NULL;
753 $second = $mapperKeyParts[2] ?? NULL;
754 if (($first == 'a' && $second == 'b') || ($first == 'b' && $second == 'a')) {
755 $saveMappingFields->name = ucwords(str_replace("_", " ", $mapperKeys[$i][1]));
756 $saveMappingFields->relationship_type_id = $id;
757 $saveMappingFields->relationship_direction = "{$first}_{$second}";
758 // to get phoneType id and provider id separately
759 // before saving mappingFields of phone and IM for related contact, CRM-3140
760 if (CRM_Utils_Array::value('1', $mapperKeys[$i]) == 'url') {
761 $saveMappingFields->website_type_id = $mapperKeys[$i][2] ?? NULL;
762 }
763 else {
764 if (CRM_Utils_Array::value('1', $mapperKeys[$i]) == 'phone') {
765 $saveMappingFields->phone_type_id = $mapperKeys[$i][3] ?? NULL;
766 }
767 elseif (CRM_Utils_Array::value('1', $mapperKeys[$i]) == 'im') {
768 $saveMappingFields->im_provider_id = $mapperKeys[$i][3] ?? NULL;
769 }
770 $saveMappingFields->location_type_id = (isset($mapperKeys[$i][2]) && $mapperKeys[$i][2] !== 'Primary') ? $mapperKeys[$i][2] : NULL;
771 }
772 }
773 else {
774 $saveMappingFields->name = $mapper[$i];
775 $locationTypeID = $parserParameters['mapperLocType'][$i];
776 // to get phoneType id and provider id separately
777 // before saving mappingFields of phone and IM, CRM-3140
778 if (CRM_Utils_Array::value('0', $mapperKeys[$i]) == 'url') {
779 $saveMappingFields->website_type_id = $mapperKeys[$i][1] ?? NULL;
780 }
781 else {
782 if (CRM_Utils_Array::value('0', $mapperKeys[$i]) == 'phone') {
783 $saveMappingFields->phone_type_id = $mapperKeys[$i][2] ?? NULL;
784 }
785 elseif (CRM_Utils_Array::value('0', $mapperKeys[$i]) == 'im') {
786 $saveMappingFields->im_provider_id = $mapperKeys[$i][2] ?? NULL;
787 }
788 $saveMappingFields->location_type_id = is_numeric($locationTypeID) ? $locationTypeID : NULL;
789 }
790 $saveMappingFields->relationship_type_id = NULL;
791 }
792 $saveMappingFields->save();
793 return $saveMappingFields->mapping_id;
794 }
795
796 }