Merge pull request #18084 from civicrm/5.28
[civicrm-core.git] / CRM / Contact / Import / Form / Preview.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
10 */
11
12 /**
13 *
14 * @package CRM
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
16 */
17
18 /**
19 * This class previews the uploaded file and returns summary statistics.
20 */
21 class CRM_Contact_Import_Form_Preview extends CRM_Import_Form_Preview {
22
23 /**
24 * Whether USPS validation should be disabled during import.
25 *
26 * @var bool
27 */
28 protected $_disableUSPS;
29
30 /**
31 * Set variables up before form is built.
32 */
33 public function preProcess() {
34 //get the data from the session
35 $dataValues = $this->get('dataValues');
36 $mapper = $this->get('mapper');
37 $invalidRowCount = $this->get('invalidRowCount');
38 $conflictRowCount = $this->get('conflictRowCount');
39 $mismatchCount = $this->get('unMatchCount');
40 $columnNames = $this->get('columnNames');
41 $this->_disableUSPS = $this->get('disableUSPS');
42
43 //assign column names
44 $this->assign('columnNames', $columnNames);
45
46 //get the mapping name displayed if the mappingId is set
47 $mappingId = $this->get('loadMappingId');
48 if ($mappingId) {
49 $mapDAO = new CRM_Core_DAO_Mapping();
50 $mapDAO->id = $mappingId;
51 $mapDAO->find(TRUE);
52 $this->assign('loadedMapping', $mappingId);
53 $this->assign('savedName', $mapDAO->name);
54 }
55
56 $this->assign('rowDisplayCount', 2);
57
58 $groups = CRM_Core_PseudoConstant::nestedGroup();
59 $this->set('groups', $groups);
60
61 $tag = CRM_Core_PseudoConstant::get('CRM_Core_DAO_EntityTag', 'tag_id', array('onlyActive' => FALSE));
62 if ($tag) {
63 $this->set('tag', $tag);
64 }
65
66 if ($invalidRowCount) {
67 $urlParams = 'type=' . CRM_Import_Parser::ERROR . '&parser=CRM_Contact_Import_Parser';
68 $this->set('downloadErrorRecordsUrl', CRM_Utils_System::url('civicrm/export', $urlParams));
69 }
70
71 if ($conflictRowCount) {
72 $urlParams = 'type=' . CRM_Import_Parser::CONFLICT . '&parser=CRM_Contact_Import_Parser';
73 $this->set('downloadConflictRecordsUrl', CRM_Utils_System::url('civicrm/export', $urlParams));
74 }
75
76 if ($mismatchCount) {
77 $urlParams = 'type=' . CRM_Import_Parser::NO_MATCH . '&parser=CRM_Contact_Import_Parser';
78 $this->set('downloadMismatchRecordsUrl', CRM_Utils_System::url('civicrm/export', $urlParams));
79 }
80
81 $properties = array(
82 'mapper',
83 'locations',
84 'phones',
85 'ims',
86 'dataValues',
87 'columnCount',
88 'totalRowCount',
89 'validRowCount',
90 'invalidRowCount',
91 'conflictRowCount',
92 'downloadErrorRecordsUrl',
93 'downloadConflictRecordsUrl',
94 'downloadMismatchRecordsUrl',
95 'related',
96 'relatedContactDetails',
97 'relatedContactLocType',
98 'relatedContactPhoneType',
99 'relatedContactImProvider',
100 'websites',
101 'relatedContactWebsiteType',
102 );
103
104 foreach ($properties as $property) {
105 $this->assign($property, $this->get($property));
106 }
107
108 $this->setStatusUrl();
109
110 $showColNames = TRUE;
111 if ('CRM_Import_DataSource_CSV' == $this->get('dataSource') &&
112 !$this->get('skipColumnHeader')
113 ) {
114 $showColNames = FALSE;
115 }
116 $this->assign('showColNames', $showColNames);
117 }
118
119 /**
120 * Build the form object.
121 */
122 public function buildQuickForm() {
123 $this->addElement('text', 'newGroupName', ts('Name for new group'), CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Group', 'title'));
124 $this->addElement('text', 'newGroupDesc', ts('Description of new group'));
125 $groupTypes = CRM_Core_OptionGroup::values('group_type', TRUE);
126 if (!empty($groupTypes)) {
127 $this->addCheckBox('newGroupType',
128 ts('Group Type'),
129 $groupTypes,
130 NULL, NULL, NULL, NULL, '&nbsp;&nbsp;&nbsp;'
131 );
132 }
133
134 $groups = $this->get('groups');
135
136 if (!empty($groups)) {
137 $this->addElement('select', 'groups', ts('Add imported records to existing group(s)'), $groups, array(
138 'multiple' => "multiple",
139 'class' => 'crm-select2',
140 ));
141 }
142
143 //display new tag
144 $this->addElement('text', 'newTagName', ts('Tag'), CRM_Core_DAO::getAttribute('CRM_Core_DAO_Tag', 'name'));
145 $this->addElement('text', 'newTagDesc', ts('Description'), CRM_Core_DAO::getAttribute('CRM_Core_DAO_Tag', 'description'));
146
147 $tag = $this->get('tag');
148 if (!empty($tag)) {
149 foreach ($tag as $tagID => $tagName) {
150 $this->addElement('checkbox', "tag[$tagID]", NULL, $tagName);
151 }
152 }
153
154 $this->addFormRule(array('CRM_Contact_Import_Form_Preview', 'formRule'), $this);
155
156 parent::buildQuickForm();
157 }
158
159 /**
160 * Global validation rules for the form.
161 *
162 * @param array $fields
163 * Posted values of the form.
164 *
165 * @param $files
166 * @param $self
167 *
168 * @return array
169 * list of errors to be posted back to the form
170 */
171 public static function formRule($fields, $files, $self) {
172 $errors = [];
173 $invalidTagName = $invalidGroupName = FALSE;
174
175 if (!empty($fields['newTagName'])) {
176 if (!CRM_Utils_Rule::objectExists(trim($fields['newTagName']),
177 array('CRM_Core_DAO_Tag')
178 )
179 ) {
180 $errors['newTagName'] = ts('Tag \'%1\' already exists.',
181 array(1 => $fields['newTagName'])
182 );
183 $invalidTagName = TRUE;
184 }
185 }
186
187 if (!empty($fields['newGroupName'])) {
188 $title = trim($fields['newGroupName']);
189 $name = CRM_Utils_String::titleToVar($title);
190 $query = 'select count(*) from civicrm_group where name like %1 OR title like %2';
191 $grpCnt = CRM_Core_DAO::singleValueQuery(
192 $query,
193 array(
194 1 => array($name, 'String'),
195 2 => array($title, 'String'),
196 )
197 );
198 if ($grpCnt) {
199 $invalidGroupName = TRUE;
200 $errors['newGroupName'] = ts('Group \'%1\' already exists.', array(1 => $fields['newGroupName']));
201 }
202 }
203
204 $self->assign('invalidTagName', $invalidTagName);
205 $self->assign('invalidGroupName', $invalidGroupName);
206
207 return empty($errors) ? TRUE : $errors;
208 }
209
210 /**
211 * Process the mapped fields and map it into the uploaded file.
212 */
213 public function postProcess() {
214
215 $importJobParams = array(
216 'doGeocodeAddress' => $this->controller->exportValue('DataSource', 'doGeocodeAddress'),
217 'invalidRowCount' => $this->get('invalidRowCount'),
218 'conflictRowCount' => $this->get('conflictRowCount'),
219 'onDuplicate' => $this->get('onDuplicate'),
220 'dedupe' => $this->get('dedupe'),
221 'newGroupName' => $this->controller->exportValue($this->_name, 'newGroupName'),
222 'newGroupDesc' => $this->controller->exportValue($this->_name, 'newGroupDesc'),
223 'newGroupType' => $this->controller->exportValue($this->_name, 'newGroupType'),
224 'groups' => $this->controller->exportValue($this->_name, 'groups'),
225 'allGroups' => $this->get('groups'),
226 'newTagName' => $this->controller->exportValue($this->_name, 'newTagName'),
227 'newTagDesc' => $this->controller->exportValue($this->_name, 'newTagDesc'),
228 'tag' => $this->controller->exportValue($this->_name, 'tag'),
229 'allTags' => $this->get('tag'),
230 'mapper' => $this->controller->exportValue('MapField', 'mapper'),
231 'mapFields' => $this->get('fields'),
232 'contactType' => $this->get('contactType'),
233 'contactSubType' => $this->get('contactSubType'),
234 'primaryKeyName' => $this->get('primaryKeyName'),
235 'statusFieldName' => $this->get('statusFieldName'),
236 'statusID' => $this->get('statusID'),
237 'totalRowCount' => $this->get('totalRowCount'),
238 );
239
240 $tableName = $this->get('importTableName');
241 $importJob = new CRM_Contact_Import_ImportJob($tableName);
242 $importJob->setJobParams($importJobParams);
243
244 // If ACL applies to the current user, update cache before running the import.
245 if (!CRM_Core_Permission::check('view all contacts')) {
246 $userID = CRM_Core_Session::getLoggedInContactID();
247 CRM_ACL_BAO_Cache::deleteEntry($userID);
248 CRM_ACL_BAO_Cache::deleteContactCacheEntry($userID);
249 }
250
251 CRM_Utils_Address_USPS::disable($this->_disableUSPS);
252
253 // run the import
254 $importJob->runImport($this);
255
256 // Clear all caches, forcing any searches to recheck the ACLs or group membership as the import
257 // may have changed it.
258 CRM_Contact_BAO_Contact_Utils::clearContactCaches(TRUE);
259
260 // add all the necessary variables to the form
261 $importJob->setFormVariables($this);
262
263 // check if there is any error occurred
264 $errorStack = CRM_Core_Error::singleton();
265 $errors = $errorStack->getErrors();
266 $errorMessage = [];
267
268 if (is_array($errors)) {
269 foreach ($errors as $key => $value) {
270 $errorMessage[] = $value['message'];
271 }
272
273 // there is no fileName since this is a sql import
274 // so fudge it
275 $config = CRM_Core_Config::singleton();
276 $errorFile = $config->uploadDir . "sqlImport.error.log";
277 if ($fd = fopen($errorFile, 'w')) {
278 fwrite($fd, implode('\n', $errorMessage));
279 }
280 fclose($fd);
281
282 $this->set('errorFile', $errorFile);
283
284 $urlParams = 'type=' . CRM_Import_Parser::ERROR . '&parser=CRM_Contact_Import_Parser';
285 $this->set('downloadErrorRecordsUrl', CRM_Utils_System::url('civicrm/export', $urlParams));
286
287 $urlParams = 'type=' . CRM_Import_Parser::CONFLICT . '&parser=CRM_Contact_Import_Parser';
288 $this->set('downloadConflictRecordsUrl', CRM_Utils_System::url('civicrm/export', $urlParams));
289
290 $urlParams = 'type=' . CRM_Import_Parser::NO_MATCH . '&parser=CRM_Contact_Import_Parser';
291 $this->set('downloadMismatchRecordsUrl', CRM_Utils_System::url('civicrm/export', $urlParams));
292 }
293
294 //hack to clean db
295 //if job complete drop table.
296 $importJob->isComplete(TRUE);
297 }
298
299 /**
300 * Process the mapped fields and map it into the uploaded file.
301 */
302 public function postProcessOld() {
303
304 $doGeocodeAddress = $this->controller->exportValue('DataSource', 'doGeocodeAddress');
305 $invalidRowCount = $this->get('invalidRowCount');
306 $conflictRowCount = $this->get('conflictRowCount');
307 $onDuplicate = $this->get('onDuplicate');
308 $newGroupName = $this->controller->exportValue($this->_name, 'newGroupName');
309 $newGroupDesc = $this->controller->exportValue($this->_name, 'newGroupDesc');
310 $newGroupType = $this->controller->exportValue($this->_name, 'newGroupType');
311 $groups = $this->controller->exportValue($this->_name, 'groups');
312 $allGroups = $this->get('groups');
313 $newTagName = $this->controller->exportValue($this->_name, 'newTagName');
314 $newTagDesc = $this->controller->exportValue($this->_name, 'newTagDesc');
315 $tag = $this->controller->exportValue($this->_name, 'tag');
316 $allTags = $this->get('tag');
317
318 $mapper = $this->controller->exportValue('MapField', 'mapper');
319
320 $mapperKeys = [];
321 $mapperLocTypes = [];
322 $mapperPhoneTypes = [];
323 $mapperRelated = [];
324 $mapperRelatedContactType = [];
325 $mapperRelatedContactDetails = [];
326 $mapperRelatedContactLocType = [];
327 $mapperRelatedContactPhoneType = [];
328
329 foreach ($mapper as $key => $value) {
330 $mapperKeys[$key] = $mapper[$key][0];
331 if (is_numeric($mapper[$key][1])) {
332 $mapperLocTypes[$key] = $mapper[$key][1];
333 }
334 else {
335 $mapperLocTypes[$key] = NULL;
336 }
337
338 if (CRM_Utils_Array::value($key, $mapperKeys) == 'phone') {
339 $mapperPhoneTypes[$key] = $mapper[$key][2];
340 }
341 else {
342 $mapperPhoneTypes[$key] = NULL;
343 }
344
345 list($id, $first, $second) = explode('_', $mapper[$key][0]);
346 if (($first == 'a' && $second == 'b') || ($first == 'b' && $second == 'a')) {
347 $relationType = new CRM_Contact_DAO_RelationshipType();
348 $relationType->id = $id;
349 $relationType->find(TRUE);
350 $fieldName = "contact_type_$second";
351 $mapperRelatedContactType[$key] = $relationType->$fieldName;
352 $mapperRelated[$key] = $mapper[$key][0];
353 $mapperRelatedContactDetails[$key] = $mapper[$key][1];
354 $mapperRelatedContactLocType[$key] = $mapper[$key][2];
355 $mapperRelatedContactPhoneType[$key] = $mapper[$key][3];
356 }
357 else {
358 $mapperRelated[$key] = NULL;
359 $mapperRelatedContactType[$key] = NULL;
360 $mapperRelatedContactDetails[$key] = NULL;
361 $mapperRelatedContactLocType[$key] = NULL;
362 $mapperRelatedContactPhoneType[$key] = NULL;
363 }
364 }
365
366 $parser = new CRM_Contact_Import_Parser_Contact($mapperKeys, $mapperLocTypes,
367 $mapperPhoneTypes, $mapperRelated, $mapperRelatedContactType,
368 $mapperRelatedContactDetails, $mapperRelatedContactLocType,
369 $mapperRelatedContactPhoneType
370 );
371
372 $mapFields = $this->get('fields');
373
374 $locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id');
375 $phoneTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Phone', 'phone_type_id');
376
377 foreach ($mapper as $key => $value) {
378 $header = [];
379 list($id, $first, $second) = explode('_', $mapper[$key][0]);
380 if (($first == 'a' && $second == 'b') || ($first == 'b' && $second == 'a')) {
381 $relationType = new CRM_Contact_DAO_RelationshipType();
382 $relationType->id = $id;
383 $relationType->find(TRUE);
384
385 $header[] = $relationType->name_a_b;
386 $header[] = ucwords(str_replace("_", " ", $mapper[$key][1]));
387
388 if (isset($mapper[$key][2])) {
389 $header[] = $locationTypes[$mapper[$key][2]];
390 }
391 if (isset($mapper[$key][3])) {
392 $header[] = $phoneTypes[$mapper[$key][3]];
393 }
394 }
395 else {
396 if (isset($mapFields[$mapper[$key][0]])) {
397 $header[] = $mapFields[$mapper[$key][0]];
398 if (isset($mapper[$key][1])) {
399 $header[] = $locationTypes[$mapper[$key][1]];
400 }
401 if (isset($mapper[$key][2])) {
402 $header[] = $phoneTypes[$mapper[$key][2]];
403 }
404 }
405 }
406 $mapperFields[] = implode(' - ', $header);
407 }
408
409 $tableName = $this->get('importTableName');
410 //print "Running parser on table: $tableName<br/>";
411 $parser->run($tableName, $mapperFields,
412 CRM_Import_Parser::MODE_IMPORT,
413 $this->get('contactType'),
414 $this->get('primaryKeyName'),
415 $this->get('statusFieldName'),
416 $onDuplicate,
417 $this->get('statusID'),
418 $this->get('totalRowCount'),
419 $doGeocodeAddress,
420 CRM_Contact_Import_Parser::DEFAULT_TIMEOUT,
421 $this->get('contactSubType'),
422 $this->get('dedupe')
423 );
424
425 // add the new contacts to selected groups
426 $contactIds = &$parser->getImportedContacts();
427
428 // add the new related contacts to selected groups
429 $relatedContactIds = &$parser->getRelatedImportedContacts();
430
431 $this->set('relatedCount', count($relatedContactIds));
432 $newGroupId = NULL;
433
434 //changed below if-statement "if ($newGroup) {" to "if ($newGroupName) {"
435 if ($newGroupName) {
436 /* Create a new group */
437
438 $gParams = array(
439 'name' => $newGroupName,
440 'title' => $newGroupName,
441 'description' => $newGroupDesc,
442 'group_type' => $newGroupType,
443 'is_active' => TRUE,
444 );
445 $group = CRM_Contact_BAO_Group::create($gParams);
446 $groups[] = $newGroupId = $group->id;
447 }
448
449 if (is_array($groups)) {
450 $groupAdditions = [];
451 foreach ($groups as $groupId) {
452 $addCount = CRM_Contact_BAO_GroupContact::addContactsToGroup($contactIds, $groupId);
453 if (!empty($relatedContactIds)) {
454 $addRelCount = CRM_Contact_BAO_GroupContact::addContactsToGroup($relatedContactIds, $groupId);
455 }
456 $totalCount = $addCount[1] + $addRelCount[1];
457 if ($groupId == $newGroupId) {
458 $name = $newGroupName;
459 $new = TRUE;
460 }
461 else {
462 $name = $allGroups[$groupId];
463 $new = FALSE;
464 }
465 $groupAdditions[] = array(
466 'url' => CRM_Utils_System::url('civicrm/group/search',
467 'reset=1&force=1&context=smog&gid=' . $groupId
468 ),
469 'name' => $name,
470 'added' => $totalCount,
471 'notAdded' => $addCount[2] + $addRelCount[2],
472 'new' => $new,
473 );
474 }
475 $this->set('groupAdditions', $groupAdditions);
476 }
477
478 $newTagId = NULL;
479 if ($newTagName) {
480 /* Create a new Tag */
481
482 $tagParams = array(
483 'name' => $newTagName,
484 'title' => $newTagName,
485 'description' => $newTagDesc,
486 'is_active' => TRUE,
487 );
488 $addedTag = CRM_Core_BAO_Tag::add($tagParams);
489 $tag[$addedTag->id] = 1;
490 }
491 //add Tag to Import
492
493 if (is_array($tag)) {
494
495 $tagAdditions = [];
496 foreach ($tag as $tagId => $val) {
497 $addTagCount = CRM_Core_BAO_EntityTag::addContactsToTag($contactIds, $tagId);
498 if (!empty($relatedContactIds)) {
499 $addRelTagCount = CRM_Core_BAO_EntityTag::addContactsToTag($relatedContactIds, $tagId);
500 }
501 $totalTagCount = $addTagCount[1] + $addRelTagCount[1];
502 if ($tagId == $addedTag->id) {
503 $tagName = $newTagName;
504 $new = TRUE;
505 }
506 else {
507 $tagName = $allTags[$tagId];
508 $new = FALSE;
509 }
510 $tagAdditions[] = array(
511 'url' => CRM_Utils_System::url('civicrm/contact/search',
512 'reset=1&force=1&context=smog&id=' . $tagId
513 ),
514 'name' => $tagName,
515 'added' => $totalTagCount,
516 'notAdded' => $addTagCount[2] + $addRelTagCount[2],
517 'new' => $new,
518 );
519 }
520 $this->set('tagAdditions', $tagAdditions);
521 }
522
523 // add all the necessary variables to the form
524 $parser->set($this, CRM_Import_Parser::MODE_IMPORT);
525
526 // check if there is any error occurred
527
528 $errorStack = CRM_Core_Error::singleton();
529 $errors = $errorStack->getErrors();
530 $errorMessage = [];
531
532 if (is_array($errors)) {
533 foreach ($errors as $key => $value) {
534 $errorMessage[] = $value['message'];
535 }
536
537 // there is no fileName since this is a sql import
538 // so fudge it
539 $config = CRM_Core_Config::singleton();
540 $errorFile = $config->uploadDir . "sqlImport.error.log";
541 if ($fd = fopen($errorFile, 'w')) {
542 fwrite($fd, implode('\n', $errorMessage));
543 }
544 fclose($fd);
545
546 $this->set('errorFile', $errorFile);
547
548 $urlParams = 'type=' . CRM_Import_Parser::ERROR . '&parser=CRM_Contact_Import_Parser';
549 $this->set('downloadErrorRecordsUrl', CRM_Utils_System::url('civicrm/export', $urlparams));
550
551 $urlParams = 'type=' . CRM_Import_Parser::CONFLICT . '&parser=CRM_Contact_Import_Parser';
552 $this->set('downloadConflictRecordsUrl', CRM_Utils_System::url('civicrm/export', $urlParams));
553
554 $urlParams = 'type=' . CRM_Import_Parser::NO_MATCH . '&parser=CRM_Contact_Import_Parser';
555 $this->set('downloadMismatchRecordsUrl', CRM_Utils_System::url('civicrm/export', $urlParams));
556 }
557 }
558
559 }