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