Merge pull request #10770 from jitendrapurohit/CRM-18177-rc
[civicrm-core.git] / CRM / Import / Parser.php
CommitLineData
ec3811b1
CW
1<?php
2/*
3 +--------------------------------------------------------------------+
7e9e8871 4 | CiviCRM version 4.7 |
ec3811b1 5 +--------------------------------------------------------------------+
0f03f337 6 | Copyright CiviCRM LLC (c) 2004-2017 |
ec3811b1
CW
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 +--------------------------------------------------------------------+
d25dd0ee 26 */
ec3811b1
CW
27
28/**
29 *
30 * @package CRM
0f03f337 31 * @copyright CiviCRM LLC (c) 2004-2017
ec3811b1 32 */
ec3811b1
CW
33abstract class CRM_Import_Parser {
34 /**
35 * Settings
36 */
7da04cde 37 const MAX_ERRORS = 250, MAX_WARNINGS = 25, DEFAULT_TIMEOUT = 30;
ec3811b1
CW
38
39 /**
40 * Return codes
41 */
7da04cde 42 const VALID = 1, WARNING = 2, ERROR = 4, CONFLICT = 8, STOP = 16, DUPLICATE = 32, MULTIPLE_DUPE = 64, NO_MATCH = 128, UNPARSED_ADDRESS_WARNING = 256;
ec3811b1
CW
43
44 /**
45 * Parser modes
46 */
7da04cde 47 const MODE_MAPFIELD = 1, MODE_PREVIEW = 2, MODE_SUMMARY = 4, MODE_IMPORT = 8;
ec3811b1
CW
48
49 /**
50 * Codes for duplicate record handling
51 */
7da04cde 52 const DUPLICATE_SKIP = 1, DUPLICATE_REPLACE = 2, DUPLICATE_UPDATE = 4, DUPLICATE_FILL = 8, DUPLICATE_NOCHECK = 16;
ec3811b1
CW
53
54 /**
55 * Contact types
56 */
7da04cde 57 const CONTACT_INDIVIDUAL = 1, CONTACT_HOUSEHOLD = 2, CONTACT_ORGANIZATION = 4;
69a4c20a
CW
58
59
60 /**
100fef9d 61 * Total number of non empty lines
69a4c20a
CW
62 */
63 protected $_totalCount;
64
65 /**
100fef9d 66 * Running total number of valid lines
69a4c20a
CW
67 */
68 protected $_validCount;
69
70 /**
100fef9d 71 * Running total number of invalid rows
69a4c20a
CW
72 */
73 protected $_invalidRowCount;
74
75 /**
100fef9d 76 * Maximum number of non-empty/comment lines to process
69a4c20a
CW
77 *
78 * @var int
79 */
80 protected $_maxLinesToProcess;
81
82 /**
100fef9d 83 * Maximum number of invalid rows to store
69a4c20a
CW
84 */
85 protected $_maxErrorCount;
86
87 /**
100fef9d 88 * Array of error lines, bounded by MAX_ERROR
69a4c20a
CW
89 */
90 protected $_errors;
91
92 /**
100fef9d 93 * Total number of conflict lines
69a4c20a
CW
94 */
95 protected $_conflictCount;
96
97 /**
100fef9d 98 * Array of conflict lines
69a4c20a
CW
99 */
100 protected $_conflicts;
101
102 /**
100fef9d 103 * Total number of duplicate (from database) lines
69a4c20a
CW
104 */
105 protected $_duplicateCount;
106
107 /**
100fef9d 108 * Array of duplicate lines
69a4c20a
CW
109 */
110 protected $_duplicates;
111
112 /**
100fef9d 113 * Running total number of warnings
69a4c20a
CW
114 */
115 protected $_warningCount;
116
117 /**
100fef9d 118 * Maximum number of warnings to store
69a4c20a
CW
119 */
120 protected $_maxWarningCount = self::MAX_WARNINGS;
121
122 /**
100fef9d 123 * Array of warning lines, bounded by MAX_WARNING
69a4c20a
CW
124 */
125 protected $_warnings;
126
127 /**
100fef9d 128 * Array of all the fields that could potentially be part
69a4c20a
CW
129 * of this import process
130 * @var array
131 */
132 protected $_fields;
133
134 /**
100fef9d 135 * Array of the fields that are actually part of the import process
69a4c20a
CW
136 * the position in the array also dictates their position in the import
137 * file
138 * @var array
139 */
140 protected $_activeFields;
141
142 /**
100fef9d 143 * Cache the count of active fields
69a4c20a
CW
144 *
145 * @var int
146 */
147 protected $_activeFieldCount;
148
149 /**
100fef9d 150 * Cache of preview rows
69a4c20a
CW
151 *
152 * @var array
153 */
154 protected $_rows;
155
156 /**
100fef9d 157 * Filename of error data
69a4c20a
CW
158 *
159 * @var string
160 */
161 protected $_errorFileName;
162
163 /**
100fef9d 164 * Filename of conflict data
69a4c20a
CW
165 *
166 * @var string
167 */
168 protected $_conflictFileName;
169
170 /**
100fef9d 171 * Filename of duplicate data
69a4c20a
CW
172 *
173 * @var string
174 */
175 protected $_duplicateFileName;
176
177 /**
100fef9d 178 * Contact type
69a4c20a
CW
179 *
180 * @var int
181 */
182 public $_contactType;
e87ff4ce 183 /**
184 * Contact sub-type
185 *
186 * @var int
187 */
188 public $_contactSubType;
69a4c20a
CW
189
190 /**
e87ff4ce 191 * Class constructor.
69a4c20a 192 */
00be9182 193 public function __construct() {
69a4c20a
CW
194 $this->_maxLinesToProcess = 0;
195 $this->_maxErrorCount = self::MAX_ERRORS;
196 }
197
198 /**
fe482240 199 * Abstract function definitions.
69a4c20a 200 */
bed98343 201 abstract protected function init();
e0ef6999
EM
202
203 /**
204 * @return mixed
205 */
bed98343 206 abstract protected function fini();
e0ef6999
EM
207
208 /**
2b4bc760 209 * Map field.
210 *
211 * @param array $values
e0ef6999
EM
212 *
213 * @return mixed
214 */
bed98343 215 abstract protected function mapField(&$values);
e0ef6999
EM
216
217 /**
2b4bc760 218 * Preview.
219 *
220 * @param array $values
e0ef6999
EM
221 *
222 * @return mixed
223 */
bed98343 224 abstract protected function preview(&$values);
e0ef6999
EM
225
226 /**
227 * @param $values
228 *
229 * @return mixed
230 */
bed98343 231 abstract protected function summary(&$values);
e0ef6999
EM
232
233 /**
234 * @param $onDuplicate
235 * @param $values
236 *
237 * @return mixed
238 */
bed98343 239 abstract protected function import($onDuplicate, &$values);
69a4c20a
CW
240
241 /**
fe482240 242 * Set and validate field values.
69a4c20a 243 *
5a4f6742 244 * @param array $elements
16b10e64 245 * array.
6f69cc11 246 * @param $erroneousField
16b10e64 247 * reference.
77b97be7
EM
248 *
249 * @return int
69a4c20a 250 */
00be9182 251 public function setActiveFieldValues($elements, &$erroneousField) {
69a4c20a
CW
252 $maxCount = count($elements) < $this->_activeFieldCount ? count($elements) : $this->_activeFieldCount;
253 for ($i = 0; $i < $maxCount; $i++) {
254 $this->_activeFields[$i]->setValue($elements[$i]);
255 }
256
257 // reset all the values that we did not have an equivalent import element
258 for (; $i < $this->_activeFieldCount; $i++) {
259 $this->_activeFields[$i]->resetValue();
260 }
261
262 // now validate the fields and return false if error
263 $valid = self::VALID;
264 for ($i = 0; $i < $this->_activeFieldCount; $i++) {
265 if (!$this->_activeFields[$i]->validate()) {
266 // no need to do any more validation
267 $erroneousField = $i;
268 $valid = self::ERROR;
269 break;
270 }
271 }
272 return $valid;
273 }
274
275 /**
fe482240 276 * Format the field values for input to the api.
69a4c20a 277 *
a6c01b45
CW
278 * @return array
279 * (reference) associative array of name/value pairs
69a4c20a 280 */
00be9182 281 public function &getActiveFieldParams() {
69a4c20a
CW
282 $params = array();
283 for ($i = 0; $i < $this->_activeFieldCount; $i++) {
284 if (isset($this->_activeFields[$i]->_value)
285 && !isset($params[$this->_activeFields[$i]->_name])
286 && !isset($this->_activeFields[$i]->_related)
287 ) {
288
289 $params[$this->_activeFields[$i]->_name] = $this->_activeFields[$i]->_value;
290 }
291 }
292 return $params;
293 }
294
e0ef6999
EM
295 /**
296 * @return array
297 */
00be9182 298 public function getSelectValues() {
69a4c20a
CW
299 $values = array();
300 foreach ($this->_fields as $name => $field) {
301 $values[$name] = $field->_title;
302 }
303 return $values;
304 }
305
e0ef6999
EM
306 /**
307 * @return array
308 */
00be9182 309 public function getSelectTypes() {
69a4c20a
CW
310 $values = array();
311 foreach ($this->_fields as $name => $field) {
312 if (isset($field->_hasLocationType)) {
313 $values[$name] = $field->_hasLocationType;
314 }
315 }
316 return $values;
317 }
318
e0ef6999
EM
319 /**
320 * @return array
321 */
00be9182 322 public function getHeaderPatterns() {
69a4c20a
CW
323 $values = array();
324 foreach ($this->_fields as $name => $field) {
325 if (isset($field->_headerPattern)) {
326 $values[$name] = $field->_headerPattern;
327 }
328 }
329 return $values;
330 }
331
e0ef6999
EM
332 /**
333 * @return array
334 */
00be9182 335 public function getDataPatterns() {
69a4c20a
CW
336 $values = array();
337 foreach ($this->_fields as $name => $field) {
338 $values[$name] = $field->_dataPattern;
339 }
340 return $values;
341 }
342
343 /**
2b4bc760 344 * Remove single-quote enclosures from a value array (row).
69a4c20a
CW
345 *
346 * @param array $values
347 * @param string $enclosure
348 *
349 * @return void
69a4c20a 350 */
00be9182 351 public static function encloseScrub(&$values, $enclosure = "'") {
69a4c20a
CW
352 if (empty($values)) {
353 return;
354 }
355
356 foreach ($values as $k => $v) {
357 $values[$k] = preg_replace("/^$enclosure(.*)$enclosure$/", '$1', $v);
358 }
359 }
360
361 /**
fe482240 362 * Setter function.
69a4c20a
CW
363 *
364 * @param int $max
365 *
366 * @return void
69a4c20a 367 */
00be9182 368 public function setMaxLinesToProcess($max) {
69a4c20a
CW
369 $this->_maxLinesToProcess = $max;
370 }
371
372 /**
fe482240 373 * Determines the file extension based on error code.
69a4c20a
CW
374 *
375 * @var $type error code constant
376 * @return string
69a4c20a 377 */
00be9182 378 public static function errorFileName($type) {
69a4c20a
CW
379 $fileName = NULL;
380 if (empty($type)) {
381 return $fileName;
382 }
383
384 $config = CRM_Core_Config::singleton();
385 $fileName = $config->uploadDir . "sqlImport";
386 switch ($type) {
387 case self::ERROR:
388 $fileName .= '.errors';
389 break;
390
391 case self::CONFLICT:
392 $fileName .= '.conflicts';
393 break;
394
395 case self::DUPLICATE:
396 $fileName .= '.duplicates';
397 break;
398
399 case self::NO_MATCH:
400 $fileName .= '.mismatch';
401 break;
402
403 case self::UNPARSED_ADDRESS_WARNING:
404 $fileName .= '.unparsedAddress';
405 break;
406 }
407
408 return $fileName;
409 }
410
411 /**
fe482240 412 * Determines the file name based on error code.
69a4c20a
CW
413 *
414 * @var $type error code constant
415 * @return string
69a4c20a 416 */
00be9182 417 public static function saveFileName($type) {
69a4c20a
CW
418 $fileName = NULL;
419 if (empty($type)) {
420 return $fileName;
421 }
422 switch ($type) {
423 case self::ERROR:
424 $fileName = 'Import_Errors.csv';
425 break;
426
427 case self::CONFLICT:
428 $fileName = 'Import_Conflicts.csv';
429 break;
430
431 case self::DUPLICATE:
432 $fileName = 'Import_Duplicates.csv';
433 break;
434
435 case self::NO_MATCH:
436 $fileName = 'Import_Mismatch.csv';
437 break;
438
439 case self::UNPARSED_ADDRESS_WARNING:
440 $fileName = 'Import_Unparsed_Address.csv';
441 break;
442 }
443
444 return $fileName;
445 }
446
56316747 447 /**
448 * Check if contact is a duplicate .
449 *
450 * @param array $formatValues
451 *
452 * @return array
453 */
454 protected function checkContactDuplicate(&$formatValues) {
455 //retrieve contact id using contact dedupe rule
456 $formatValues['contact_type'] = $this->_contactType;
457 $formatValues['version'] = 3;
458 require_once 'CRM/Utils/DeprecatedUtils.php';
459 $error = _civicrm_api3_deprecated_check_contact_dedupe($formatValues);
460 return $error;
461 }
462
ec3811b1 463}