INFRA-132 - CRM/Contact - Misc
[civicrm-core.git] / CRM / Contact / Import / Parser.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.6 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
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-2014
32 * $Id$
33 *
34 */
35
36
37 abstract class CRM_Contact_Import_Parser extends CRM_Import_Parser {
38
39 protected $_tableName;
40
41 /**#@+
42 * @var integer
43 */
44
45 /**
46 * Total number of lines in file
47 */
48 protected $_rowCount;
49
50 /**
51 * Running total number of un matched Conact
52 */
53 protected $_unMatchCount;
54
55 /**
56 * Array of unmatched lines
57 */
58 protected $_unMatch;
59
60 /**
61 * Total number of contacts with unparsed addresses
62 */
63 protected $_unparsedAddressCount;
64
65 /**
66 * Filename of mismatch data
67 *
68 * @var string
69 */
70 protected $_misMatchFilemName;
71
72 protected $_primaryKeyName;
73 protected $_statusFieldName;
74
75 /**
76 * On duplicate
77 *
78 * @var int
79 */
80 public $_onDuplicate;
81
82 /**
83 * Dedupe rule group id to use if set
84 *
85 * @var int
86 */
87 public $_dedupeRuleGroupID = NULL;
88
89 /**
90 * @param string $tableName
91 * @param $mapper
92 * @param int $mode
93 * @param int $contactType
94 * @param string $primaryKeyName
95 * @param string $statusFieldName
96 * @param int $onDuplicate
97 * @param int $statusID
98 * @param null $totalRowCount
99 * @param bool $doGeocodeAddress
100 * @param int $timeout
101 * @param null $contactSubType
102 * @param int $dedupeRuleGroupID
103 *
104 * @return mixed
105 */
106 function run(
107 $tableName,
108 &$mapper,
109 $mode = self::MODE_PREVIEW,
110 $contactType = self::CONTACT_INDIVIDUAL,
111 $primaryKeyName = '_id',
112 $statusFieldName = '_status',
113 $onDuplicate = self::DUPLICATE_SKIP,
114 $statusID = NULL,
115 $totalRowCount = NULL,
116 $doGeocodeAddress = FALSE,
117 $timeout = CRM_Contact_Import_Parser::DEFAULT_TIMEOUT,
118 $contactSubType = NULL,
119 $dedupeRuleGroupID = NULL
120 ) {
121
122 // TODO: Make the timeout actually work
123 $this->_onDuplicate = $onDuplicate;
124 $this->_dedupeRuleGroupID = $dedupeRuleGroupID;
125
126 switch ($contactType) {
127 case CRM_Import_Parser::CONTACT_INDIVIDUAL:
128 $this->_contactType = 'Individual';
129 break;
130
131 case CRM_Import_Parser::CONTACT_HOUSEHOLD:
132 $this->_contactType = 'Household';
133 break;
134
135 case CRM_Import_Parser::CONTACT_ORGANIZATION:
136 $this->_contactType = 'Organization';
137 }
138
139 $this->_contactSubType = $contactSubType;
140
141 $this->init();
142
143 $this->_rowCount = $this->_warningCount = 0;
144 $this->_invalidRowCount = $this->_validCount = 0;
145 $this->_totalCount = $this->_conflictCount = 0;
146
147 $this->_errors = array();
148 $this->_warnings = array();
149 $this->_conflicts = array();
150 $this->_unparsedAddresses = array();
151
152 $status = '';
153
154 $this->_tableName = $tableName;
155 $this->_primaryKeyName = $primaryKeyName;
156 $this->_statusFieldName = $statusFieldName;
157
158 if ($mode == self::MODE_MAPFIELD) {
159 $this->_rows = array();
160 }
161 else {
162 $this->_activeFieldCount = count($this->_activeFields);
163 }
164
165 if ($mode == self::MODE_IMPORT) {
166 //get the key of email field
167 foreach ($mapper as $key => $value) {
168 if (strtolower($value) == 'email') {
169 $emailKey = $key;
170 break;
171 }
172 }
173 }
174
175 if ($statusID) {
176 $skip = 50;
177 // $skip = 1;
178 $config = CRM_Core_Config::singleton();
179 $statusFile = "{$config->uploadDir}status_{$statusID}.txt";
180 $status = "<div class='description'>&nbsp; " . ts('No processing status reported yet.') . "</div>";
181
182 //do not force the browser to display the save dialog, CRM-7640
183 $contents = json_encode(array(0, $status));
184
185 file_put_contents($statusFile, $contents);
186
187 $startTimestamp = $currTimestamp = $prevTimestamp = time();
188 }
189
190 // get the contents of the temp. import table
191 $query = "SELECT * FROM $tableName";
192 if ($mode == self::MODE_IMPORT) {
193 $query .= " WHERE $statusFieldName = 'NEW'";
194 }
195 $dao = new CRM_Core_DAO();
196 $db = $dao->getDatabaseConnection();
197 $result = $db->query($query);
198
199 while ($values = $result->fetchRow(DB_FETCHMODE_ORDERED)) {
200 $this->_rowCount++;
201
202 /* trim whitespace around the values */
203
204 $empty = TRUE;
205 foreach ($values as $k => $v) {
206 $values[$k] = trim($v, " \t\r\n");
207 }
208 if (CRM_Utils_System::isNull($values)) {
209 continue;
210 }
211
212 $this->_totalCount++;
213
214 if ($mode == self::MODE_MAPFIELD) {
215 $returnCode = $this->mapField($values);
216 }
217 elseif ($mode == self::MODE_PREVIEW) {
218 $returnCode = $this->preview($values);
219 }
220 elseif ($mode == self::MODE_SUMMARY) {
221 $returnCode = $this->summary($values);
222 }
223 elseif ($mode == self::MODE_IMPORT) {
224 //print "Running parser in import mode<br/>\n";
225 $returnCode = $this->import($onDuplicate, $values, $doGeocodeAddress);
226 if ($statusID && (($this->_rowCount % $skip) == 0)) {
227 $currTimestamp = time();
228 $totalTime = ($currTimestamp - $startTimestamp);
229 $time = ($currTimestamp - $prevTimestamp);
230 $recordsLeft = $totalRowCount - $this->_rowCount;
231 if ($recordsLeft < 0) {
232 $recordsLeft = 0;
233 }
234 $estimatedTime = ($recordsLeft / $skip) * $time;
235 $estMinutes = floor($estimatedTime / 60);
236 $timeFormatted = '';
237 if ($estMinutes > 1) {
238 $timeFormatted = $estMinutes . ' ' . ts('minutes') . ' ';
239 $estimatedTime = $estimatedTime - ($estMinutes * 60);
240 }
241 $timeFormatted .= round($estimatedTime) . ' ' . ts('seconds');
242 $processedPercent = (int ) (($this->_rowCount * 100) / $totalRowCount);
243 $statusMsg = ts('%1 of %2 records - %3 remaining',
244 array(1 => $this->_rowCount, 2 => $totalRowCount, 3 => $timeFormatted)
245 );
246 $status = "
247 <div class=\"description\">
248 &nbsp; <strong>{$statusMsg}</strong>
249 </div>
250 ";
251
252 $contents = json_encode(array($processedPercent, $status));
253
254 file_put_contents($statusFile, $contents);
255
256 $prevTimestamp = $currTimestamp;
257 }
258 // sleep(1);
259 }
260 else {
261 $returnCode = self::ERROR;
262 }
263
264 // note that a line could be valid but still produce a warning
265 if ($returnCode & self::VALID) {
266 $this->_validCount++;
267 if ($mode == self::MODE_MAPFIELD) {
268 $this->_rows[] = $values;
269 $this->_activeFieldCount = max($this->_activeFieldCount, count($values));
270 }
271 }
272
273 if ($returnCode & self::WARNING) {
274 $this->_warningCount++;
275 if ($this->_warningCount < $this->_maxWarningCount) {
276 $this->_warningCount[] = $line;
277 }
278 }
279
280 if ($returnCode & self::ERROR) {
281 $this->_invalidRowCount++;
282 if ($this->_invalidRowCount < $this->_maxErrorCount) {
283 array_unshift($values, $this->_rowCount);
284 $this->_errors[] = $values;
285 }
286 }
287
288 if ($returnCode & self::CONFLICT) {
289 $this->_conflictCount++;
290 array_unshift($values, $this->_rowCount);
291 $this->_conflicts[] = $values;
292 }
293
294 if ($returnCode & self::NO_MATCH) {
295 $this->_unMatchCount++;
296 array_unshift($values, $this->_rowCount);
297 $this->_unMatch[] = $values;
298 }
299
300 if ($returnCode & self::DUPLICATE) {
301 if ($returnCode & self::MULTIPLE_DUPE) {
302 /* TODO: multi-dupes should be counted apart from singles
303 * on non-skip action */
304 }
305 $this->_duplicateCount++;
306 array_unshift($values, $this->_rowCount);
307 $this->_duplicates[] = $values;
308 if ($onDuplicate != self::DUPLICATE_SKIP) {
309 $this->_validCount++;
310 }
311 }
312
313 if ($returnCode & self::UNPARSED_ADDRESS_WARNING) {
314 $this->_unparsedAddressCount++;
315 array_unshift($values, $this->_rowCount);
316 $this->_unparsedAddresses[] = $values;
317 }
318 // we give the derived class a way of aborting the process
319 // note that the return code could be multiple code or'ed together
320 if ($returnCode & self::STOP) {
321 break;
322 }
323
324 // if we are done processing the maxNumber of lines, break
325 if ($this->_maxLinesToProcess > 0 && $this->_validCount >= $this->_maxLinesToProcess) {
326 break;
327 }
328
329 // clean up memory from dao's
330 CRM_Core_DAO::freeResult();
331
332 // see if we've hit our timeout yet
333 /* if ( $the_thing_with_the_stuff ) {
334 do_something( );
335 } */
336 }
337
338 if ($mode == self::MODE_PREVIEW || $mode == self::MODE_IMPORT) {
339 $customHeaders = $mapper;
340
341 $customfields = CRM_Core_BAO_CustomField::getFields($this->_contactType);
342 foreach ($customHeaders as $key => $value) {
343 if ($id = CRM_Core_BAO_CustomField::getKeyID($value)) {
344 $customHeaders[$key] = $customfields[$id][0];
345 }
346 }
347
348 if ($this->_invalidRowCount) {
349 // removed view url for invlaid contacts
350 $headers = array_merge(array(ts('Line Number'),
351 ts('Reason'),
352 ),
353 $customHeaders
354 );
355 $this->_errorFileName = self::errorFileName(self::ERROR);
356 self::exportCSV($this->_errorFileName, $headers, $this->_errors);
357 }
358 if ($this->_conflictCount) {
359 $headers = array_merge(array(ts('Line Number'),
360 ts('Reason'),
361 ),
362 $customHeaders
363 );
364 $this->_conflictFileName = self::errorFileName(self::CONFLICT);
365 self::exportCSV($this->_conflictFileName, $headers, $this->_conflicts);
366 }
367 if ($this->_duplicateCount) {
368 $headers = array_merge(array(ts('Line Number'),
369 ts('View Contact URL'),
370 ),
371 $customHeaders
372 );
373
374 $this->_duplicateFileName = self::errorFileName(self::DUPLICATE);
375 self::exportCSV($this->_duplicateFileName, $headers, $this->_duplicates);
376 }
377 if ($this->_unMatchCount) {
378 $headers = array_merge(array(ts('Line Number'),
379 ts('Reason'),
380 ),
381 $customHeaders
382 );
383
384 $this->_misMatchFilemName = self::errorFileName(self::NO_MATCH);
385 self::exportCSV($this->_misMatchFilemName, $headers, $this->_unMatch);
386 }
387 if ($this->_unparsedAddressCount) {
388 $headers = array_merge(array(ts('Line Number'),
389 ts('Contact Edit URL'),
390 ),
391 $customHeaders
392 );
393 $this->_errorFileName = self::errorFileName(self::UNPARSED_ADDRESS_WARNING);
394 self::exportCSV($this->_errorFileName, $headers, $this->_unparsedAddresses);
395 }
396 }
397 //echo "$this->_totalCount,$this->_invalidRowCount,$this->_conflictCount,$this->_duplicateCount";
398 return $this->fini();
399 }
400
401 /**
402 * Given a list of the importable field keys that the user has selected
403 * set the active fields array to this list
404 *
405 * @param array mapped array of values
406 *
407 * @return void
408 */
409 public function setActiveFields($fieldKeys) {
410 $this->_activeFieldCount = count($fieldKeys);
411 foreach ($fieldKeys as $key) {
412 if (empty($this->_fields[$key])) {
413 $this->_activeFields[] = new CRM_Contact_Import_Field('', ts('- do not import -'));
414 }
415 else {
416 $this->_activeFields[] = clone($this->_fields[$key]);
417 }
418 }
419 }
420
421 /**
422 * @param $elements
423 */
424 public function setActiveFieldLocationTypes($elements) {
425 for ($i = 0; $i < count($elements); $i++) {
426 $this->_activeFields[$i]->_hasLocationType = $elements[$i];
427 }
428 }
429
430 /**
431 * @param $elements
432 */
433 /**
434 * @param $elements
435 */
436 public function setActiveFieldPhoneTypes($elements) {
437 for ($i = 0; $i < count($elements); $i++) {
438 $this->_activeFields[$i]->_phoneType = $elements[$i];
439 }
440 }
441
442 /**
443 * @param $elements
444 */
445 public function setActiveFieldWebsiteTypes($elements) {
446 for ($i = 0; $i < count($elements); $i++) {
447 $this->_activeFields[$i]->_websiteType = $elements[$i];
448 }
449 }
450
451 /**
452 * Set IM Service Provider type fields
453 *
454 * @param array $elements
455 * IM service provider type ids.
456 *
457 * @return void
458 */
459 public function setActiveFieldImProviders($elements) {
460 for ($i = 0; $i < count($elements); $i++) {
461 $this->_activeFields[$i]->_imProvider = $elements[$i];
462 }
463 }
464
465 /**
466 * @param $elements
467 */
468 public function setActiveFieldRelated($elements) {
469 for ($i = 0; $i < count($elements); $i++) {
470 $this->_activeFields[$i]->_related = $elements[$i];
471 }
472 }
473
474 /**
475 * @param $elements
476 */
477 public function setActiveFieldRelatedContactType($elements) {
478 for ($i = 0; $i < count($elements); $i++) {
479 $this->_activeFields[$i]->_relatedContactType = $elements[$i];
480 }
481 }
482
483 /**
484 * @param $elements
485 */
486 public function setActiveFieldRelatedContactDetails($elements) {
487 for ($i = 0; $i < count($elements); $i++) {
488 $this->_activeFields[$i]->_relatedContactDetails = $elements[$i];
489 }
490 }
491
492 /**
493 * @param $elements
494 */
495 public function setActiveFieldRelatedContactLocType($elements) {
496 for ($i = 0; $i < count($elements); $i++) {
497 $this->_activeFields[$i]->_relatedContactLocType = $elements[$i];
498 }
499 }
500
501 /**
502 * @param $elements
503 */
504 public function setActiveFieldRelatedContactPhoneType($elements) {
505 for ($i = 0; $i < count($elements); $i++) {
506 $this->_activeFields[$i]->_relatedContactPhoneType = $elements[$i];
507 }
508 }
509
510 /**
511 * @param $elements
512 */
513 public function setActiveFieldRelatedContactWebsiteType($elements) {
514 for ($i = 0; $i < count($elements); $i++) {
515 $this->_activeFields[$i]->_relatedContactWebsiteType = $elements[$i];
516 }
517 }
518
519 /**
520 * Set IM Service Provider type fields for related contacts
521 *
522 * @param array $elements
523 * IM service provider type ids of related contact.
524 *
525 * @return void
526 */
527 public function setActiveFieldRelatedContactImProvider($elements) {
528 for ($i = 0; $i < count($elements); $i++) {
529 $this->_activeFields[$i]->_relatedContactImProvider = $elements[$i];
530 }
531 }
532
533 /**
534 * Format the field values for input to the api
535 *
536 * @return array (reference ) associative array of name/value pairs
537 */
538 public function &getActiveFieldParams() {
539 $params = array();
540
541 for ($i = 0; $i < $this->_activeFieldCount; $i++) {
542 if ($this->_activeFields[$i]->_name == 'do_not_import') {
543 continue;
544 }
545
546 if (isset($this->_activeFields[$i]->_value)) {
547 if (isset($this->_activeFields[$i]->_hasLocationType)) {
548 if (!isset($params[$this->_activeFields[$i]->_name])) {
549 $params[$this->_activeFields[$i]->_name] = array();
550 }
551
552 $value = array(
553 $this->_activeFields[$i]->_name =>
554 $this->_activeFields[$i]->_value,
555 'location_type_id' =>
556 $this->_activeFields[$i]->_hasLocationType,
557 );
558
559 if (isset($this->_activeFields[$i]->_phoneType)) {
560 $value['phone_type_id'] = $this->_activeFields[$i]->_phoneType;
561 }
562
563 // get IM service Provider type id
564 if (isset($this->_activeFields[$i]->_imProvider)) {
565 $value['provider_id'] = $this->_activeFields[$i]->_imProvider;
566 }
567
568 $params[$this->_activeFields[$i]->_name][] = $value;
569 }
570 elseif (isset($this->_activeFields[$i]->_websiteType)) {
571 $value = array(
572 $this->_activeFields[$i]->_name => $this->_activeFields[$i]->_value,
573 'website_type_id' => $this->_activeFields[$i]->_websiteType,
574 );
575
576 $params[$this->_activeFields[$i]->_name][] = $value;
577 }
578
579 if (!isset($params[$this->_activeFields[$i]->_name])) {
580 if (!isset($this->_activeFields[$i]->_related)) {
581 $params[$this->_activeFields[$i]->_name] = $this->_activeFields[$i]->_value;
582 }
583 }
584
585 //minor fix for CRM-4062
586 if (isset($this->_activeFields[$i]->_related)) {
587 if (!isset($params[$this->_activeFields[$i]->_related])) {
588 $params[$this->_activeFields[$i]->_related] = array();
589 }
590
591 if (!isset($params[$this->_activeFields[$i]->_related]['contact_type']) && !empty($this->_activeFields[$i]->_relatedContactType)) {
592 $params[$this->_activeFields[$i]->_related]['contact_type'] = $this->_activeFields[$i]->_relatedContactType;
593 }
594
595 if (isset($this->_activeFields[$i]->_relatedContactLocType) && !empty($this->_activeFields[$i]->_value)) {
596 if (!empty($params[$this->_activeFields[$i]->_related][$this->_activeFields[$i]->_relatedContactDetails]) &&
597 !is_array($params[$this->_activeFields[$i]->_related][$this->_activeFields[$i]->_relatedContactDetails])) {
598 $params[$this->_activeFields[$i]->_related][$this->_activeFields[$i]->_relatedContactDetails] = array();
599 }
600 $value = array(
601 $this->_activeFields[$i]->_relatedContactDetails => $this->_activeFields[$i]->_value,
602 'location_type_id' => $this->_activeFields[$i]->_relatedContactLocType,
603 );
604
605 if (isset($this->_activeFields[$i]->_relatedContactPhoneType)) {
606 $value['phone_type_id'] = $this->_activeFields[$i]->_relatedContactPhoneType;
607 }
608
609 // get IM service Provider type id for related contact
610 if (isset($this->_activeFields[$i]->_relatedContactImProvider)) {
611 $value['provider_id'] = $this->_activeFields[$i]->_relatedContactImProvider;
612 }
613
614 $params[$this->_activeFields[$i]->_related][$this->_activeFields[$i]->_relatedContactDetails][] = $value;
615 }
616 elseif (isset($this->_activeFields[$i]->_relatedContactWebsiteType)) {
617 $params[$this->_activeFields[$i]->_related][$this->_activeFields[$i]->_relatedContactDetails][] = array(
618 'url' => $this->_activeFields[$i]->_value,
619 'website_type_id' => $this->_activeFields[$i]->_relatedContactWebsiteType,
620 );
621 }
622 else {
623 $params[$this->_activeFields[$i]->_related][$this->_activeFields[$i]->_relatedContactDetails] = $this->_activeFields[$i]->_value;
624 }
625 }
626 }
627 }
628
629 return $params;
630 }
631
632 /**
633 * @return array
634 */
635 public function getColumnPatterns() {
636 $values = array();
637 foreach ($this->_fields as $name => $field) {
638 $values[$name] = $field->_columnPattern;
639 }
640 return $values;
641 }
642
643 /**
644 * @param string $name
645 * @param $title
646 * @param int $type
647 * @param string $headerPattern
648 * @param string $dataPattern
649 * @param bool $hasLocationType
650 */
651 function addField(
652 $name, $title, $type = CRM_Utils_Type::T_INT,
653 $headerPattern = '//', $dataPattern = '//',
654 $hasLocationType = FALSE
655 ) {
656 $this->_fields[$name] = new CRM_Contact_Import_Field($name, $title, $type, $headerPattern, $dataPattern, $hasLocationType);
657 if (empty($name)) {
658 $this->_fields['doNotImport'] = new CRM_Contact_Import_Field($name, $title, $type, $headerPattern, $dataPattern, $hasLocationType);
659 }
660 }
661
662 /**
663 * Store parser values
664 *
665 * @param CRM_Core_Session $store
666 *
667 * @param int $mode
668 *
669 * @return void
670 */
671 public function set($store, $mode = self::MODE_SUMMARY) {
672 $store->set('rowCount', $this->_rowCount);
673 $store->set('fields', $this->getSelectValues());
674 $store->set('fieldTypes', $this->getSelectTypes());
675
676 $store->set('columnPatterns', $this->getColumnPatterns());
677 $store->set('dataPatterns', $this->getDataPatterns());
678 $store->set('columnCount', $this->_activeFieldCount);
679
680 $store->set('totalRowCount', $this->_totalCount);
681 $store->set('validRowCount', $this->_validCount);
682 $store->set('invalidRowCount', $this->_invalidRowCount);
683 $store->set('conflictRowCount', $this->_conflictCount);
684 $store->set('unMatchCount', $this->_unMatchCount);
685
686 switch ($this->_contactType) {
687 case 'Individual':
688 $store->set('contactType', CRM_Import_Parser::CONTACT_INDIVIDUAL);
689 break;
690
691 case 'Household':
692 $store->set('contactType', CRM_Import_Parser::CONTACT_HOUSEHOLD);
693 break;
694
695 case 'Organization':
696 $store->set('contactType', CRM_Import_Parser::CONTACT_ORGANIZATION);
697 }
698
699 if ($this->_invalidRowCount) {
700 $store->set('errorsFileName', $this->_errorFileName);
701 }
702 if ($this->_conflictCount) {
703 $store->set('conflictsFileName', $this->_conflictFileName);
704 }
705 if (isset($this->_rows) && !empty($this->_rows)) {
706 $store->set('dataValues', $this->_rows);
707 }
708
709 if ($this->_unMatchCount) {
710 $store->set('mismatchFileName', $this->_misMatchFilemName);
711 }
712
713 if ($mode == self::MODE_IMPORT) {
714 $store->set('duplicateRowCount', $this->_duplicateCount);
715 $store->set('unparsedAddressCount', $this->_unparsedAddressCount);
716 if ($this->_duplicateCount) {
717 $store->set('duplicatesFileName', $this->_duplicateFileName);
718 }
719 if ($this->_unparsedAddressCount) {
720 $store->set('errorsFileName', $this->_errorFileName);
721 }
722 }
723 //echo "$this->_totalCount,$this->_invalidRowCount,$this->_conflictCount,$this->_duplicateCount";
724 }
725
726 /**
727 * Export data to a CSV file
728 *
729 * @param string $fileName
730 * @param array $header
731 * @param array $data
732 *
733 * @return void
734 */
735 public static function exportCSV($fileName, $header, $data) {
736
737 if (file_exists($fileName) && !is_writable($fileName)) {
738 CRM_Core_Error::movedSiteError($fileName);
739 }
740 //hack to remove '_status', '_statusMsg' and '_id' from error file
741 $errorValues = array();
742 $dbRecordStatus = array('IMPORTED', 'ERROR', 'DUPLICATE', 'INVALID', 'NEW');
743 foreach ($data as $rowCount => $rowValues) {
744 $count = 0;
745 foreach ($rowValues as $key => $val) {
746 if (in_array($val, $dbRecordStatus) && $count == (count($rowValues) - 3)) {
747 break;
748 }
749 $errorValues[$rowCount][$key] = $val;
750 $count++;
751 }
752 }
753 $data = $errorValues;
754
755 $output = array();
756 $fd = fopen($fileName, 'w');
757
758 foreach ($header as $key => $value) {
759 $header[$key] = "\"$value\"";
760 }
761 $config = CRM_Core_Config::singleton();
762 $output[] = implode($config->fieldSeparator, $header);
763
764 foreach ($data as $datum) {
765 foreach ($datum as $key => $value) {
766 $datum[$key] = "\"$value\"";
767 }
768 $output[] = implode($config->fieldSeparator, $datum);
769 }
770 fwrite($fd, implode("\n", $output));
771 fclose($fd);
772 }
773
774 /**
775 * Update the record with PK $id in the import database table
776 *
777 * @param int $id
778 * @param array $params
779 *
780 * @return void
781 */
782 public function updateImportRecord($id, &$params) {
783 $statusFieldName = $this->_statusFieldName;
784 $primaryKeyName = $this->_primaryKeyName;
785
786 if ($statusFieldName && $primaryKeyName) {
787 $dao = new CRM_Core_DAO();
788 $db = $dao->getDatabaseConnection();
789
790 $query = "UPDATE $this->_tableName
791 SET $statusFieldName = ?,
792 ${statusFieldName}Msg = ?
793 WHERE $primaryKeyName = ?";
794 $args = array(
795 $params[$statusFieldName],
796 CRM_Utils_Array::value("${statusFieldName}Msg", $params),
797 $id,
798 );
799
800 //print "Running query: $query<br/>With arguments: ".$params[$statusFieldName].", ".$params["${statusFieldName}Msg"].", $id<br/>";
801
802 $db->query($query, $args);
803 }
804 }
805
806 }