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