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