Merge pull request #14279 from eileenmcnaughton/db_test2
[civicrm-core.git] / CRM / Contact / Import / Parser.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
fee14197 4 | CiviCRM version 5 |
6a488035 5 +--------------------------------------------------------------------+
6b83d5bd 6 | Copyright CiviCRM LLC (c) 2004-2019 |
6a488035
TO
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 */
6a488035
TO
27
28/**
29 *
30 * @package CRM
6b83d5bd 31 * @copyright CiviCRM LLC (c) 2004-2019
6a488035 32 */
ec3811b1 33abstract class CRM_Contact_Import_Parser extends CRM_Import_Parser {
6a488035
TO
34
35 protected $_tableName;
36
6a488035 37 /**
100fef9d 38 * Total number of lines in file
4815ab3d 39 *
40 * @var integer
6a488035
TO
41 */
42 protected $_rowCount;
43
6a488035 44 /**
4815ab3d 45 * Running total number of un-matched Contacts.
69078420 46 * @var int
6a488035
TO
47 */
48 protected $_unMatchCount;
49
50 /**
100fef9d 51 * Array of unmatched lines
69078420 52 * @var array
6a488035
TO
53 */
54 protected $_unMatch;
55
6a488035 56 /**
100fef9d 57 * Total number of contacts with unparsed addresses
69078420 58 * @var int
6a488035
TO
59 */
60 protected $_unparsedAddressCount;
61
6a488035 62 /**
100fef9d 63 * Filename of mismatch data
6a488035
TO
64 *
65 * @var string
66 */
67 protected $_misMatchFilemName;
68
69 protected $_primaryKeyName;
70 protected $_statusFieldName;
71
0b3f2e42 72 protected $fieldMetadata = [];
6a488035 73 /**
100fef9d 74 * On duplicate
6a488035
TO
75 *
76 * @var int
77 */
78 public $_onDuplicate;
79
80 /**
100fef9d 81 * Dedupe rule group id to use if set
6a488035
TO
82 *
83 * @var int
84 */
03e04002 85 public $_dedupeRuleGroupID = NULL;
6a488035 86
86538308 87 /**
4815ab3d 88 * Run import.
89 *
100fef9d 90 * @param string $tableName
4815ab3d 91 * @param array $mapper
86538308
EM
92 * @param int $mode
93 * @param int $contactType
94 * @param string $primaryKeyName
95 * @param string $statusFieldName
96 * @param int $onDuplicate
100fef9d 97 * @param int $statusID
8fd37b20 98 * @param int $totalRowCount
86538308
EM
99 * @param bool $doGeocodeAddress
100 * @param int $timeout
8fd37b20 101 * @param string $contactSubType
100fef9d 102 * @param int $dedupeRuleGroupID
86538308
EM
103 *
104 * @return mixed
105 */
ae5ffbb7 106 public function run(
51ccfbbe 107 $tableName,
6a488035 108 &$mapper,
52892e8b
CW
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,
6a488035
TO
119 $dedupeRuleGroupID = NULL
120 ) {
121
122 // TODO: Make the timeout actually work
123 $this->_onDuplicate = $onDuplicate;
124 $this->_dedupeRuleGroupID = $dedupeRuleGroupID;
125
126 switch ($contactType) {
a05662ef 127 case CRM_Import_Parser::CONTACT_INDIVIDUAL:
6a488035
TO
128 $this->_contactType = 'Individual';
129 break;
130
a05662ef 131 case CRM_Import_Parser::CONTACT_HOUSEHOLD:
6a488035
TO
132 $this->_contactType = 'Household';
133 break;
134
a05662ef 135 case CRM_Import_Parser::CONTACT_ORGANIZATION:
6a488035
TO
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
be2fb01f
CW
147 $this->_errors = [];
148 $this->_warnings = [];
149 $this->_conflicts = [];
150 $this->_unparsedAddresses = [];
6a488035 151
6a488035
TO
152 $this->_tableName = $tableName;
153 $this->_primaryKeyName = $primaryKeyName;
154 $this->_statusFieldName = $statusFieldName;
155
156 if ($mode == self::MODE_MAPFIELD) {
be2fb01f 157 $this->_rows = [];
6a488035
TO
158 }
159 else {
160 $this->_activeFieldCount = count($this->_activeFields);
161 }
162
163 if ($mode == self::MODE_IMPORT) {
164 //get the key of email field
165 foreach ($mapper as $key => $value) {
166 if (strtolower($value) == 'email') {
167 $emailKey = $key;
168 break;
169 }
170 }
171 }
172
173 if ($statusID) {
8cebffb2 174 $this->progressImport($statusID);
6a488035
TO
175 $startTimestamp = $currTimestamp = $prevTimestamp = time();
176 }
6a488035
TO
177 // get the contents of the temp. import table
178 $query = "SELECT * FROM $tableName";
179 if ($mode == self::MODE_IMPORT) {
180 $query .= " WHERE $statusFieldName = 'NEW'";
181 }
52892e8b
CW
182 $dao = new CRM_Core_DAO();
183 $db = $dao->getDatabaseConnection();
6a488035
TO
184 $result = $db->query($query);
185
186 while ($values = $result->fetchRow(DB_FETCHMODE_ORDERED)) {
187 $this->_rowCount++;
188
189 /* trim whitespace around the values */
6a488035
TO
190 foreach ($values as $k => $v) {
191 $values[$k] = trim($v, " \t\r\n");
192 }
193 if (CRM_Utils_System::isNull($values)) {
194 continue;
195 }
196
197 $this->_totalCount++;
198
199 if ($mode == self::MODE_MAPFIELD) {
200 $returnCode = $this->mapField($values);
201 }
202 elseif ($mode == self::MODE_PREVIEW) {
203 $returnCode = $this->preview($values);
204 }
205 elseif ($mode == self::MODE_SUMMARY) {
206 $returnCode = $this->summary($values);
207 }
208 elseif ($mode == self::MODE_IMPORT) {
209 //print "Running parser in import mode<br/>\n";
210 $returnCode = $this->import($onDuplicate, $values, $doGeocodeAddress);
8cebffb2
JP
211 if ($statusID && (($this->_rowCount % 50) == 0)) {
212 $prevTimestamp = $this->progressImport($statusID, FALSE, $startTimestamp, $prevTimestamp, $totalRowCount);
6a488035 213 }
6a488035
TO
214 }
215 else {
216 $returnCode = self::ERROR;
217 }
218
219 // note that a line could be valid but still produce a warning
220 if ($returnCode & self::VALID) {
221 $this->_validCount++;
222 if ($mode == self::MODE_MAPFIELD) {
223 $this->_rows[] = $values;
224 $this->_activeFieldCount = max($this->_activeFieldCount, count($values));
225 }
226 }
227
228 if ($returnCode & self::WARNING) {
229 $this->_warningCount++;
230 if ($this->_warningCount < $this->_maxWarningCount) {
231 $this->_warningCount[] = $line;
232 }
233 }
234
235 if ($returnCode & self::ERROR) {
236 $this->_invalidRowCount++;
ca2057ea
SM
237 array_unshift($values, $this->_rowCount);
238 $this->_errors[] = $values;
6a488035
TO
239 }
240
241 if ($returnCode & self::CONFLICT) {
242 $this->_conflictCount++;
243 array_unshift($values, $this->_rowCount);
244 $this->_conflicts[] = $values;
245 }
246
247 if ($returnCode & self::NO_MATCH) {
248 $this->_unMatchCount++;
249 array_unshift($values, $this->_rowCount);
250 $this->_unMatch[] = $values;
251 }
252
253 if ($returnCode & self::DUPLICATE) {
254 if ($returnCode & self::MULTIPLE_DUPE) {
255 /* TODO: multi-dupes should be counted apart from singles
e70a7fc0 256 * on non-skip action */
6a488035
TO
257 }
258 $this->_duplicateCount++;
259 array_unshift($values, $this->_rowCount);
260 $this->_duplicates[] = $values;
261 if ($onDuplicate != self::DUPLICATE_SKIP) {
262 $this->_validCount++;
263 }
264 }
265
266 if ($returnCode & self::UNPARSED_ADDRESS_WARNING) {
267 $this->_unparsedAddressCount++;
268 array_unshift($values, $this->_rowCount);
269 $this->_unparsedAddresses[] = $values;
270 }
271 // we give the derived class a way of aborting the process
272 // note that the return code could be multiple code or'ed together
273 if ($returnCode & self::STOP) {
274 break;
275 }
276
277 // if we are done processing the maxNumber of lines, break
278 if ($this->_maxLinesToProcess > 0 && $this->_validCount >= $this->_maxLinesToProcess) {
279 break;
280 }
281
6a488035
TO
282 // see if we've hit our timeout yet
283 /* if ( $the_thing_with_the_stuff ) {
e70a7fc0
TO
284 do_something( );
285 } */
6a488035
TO
286 }
287
6a488035
TO
288 if ($mode == self::MODE_PREVIEW || $mode == self::MODE_IMPORT) {
289 $customHeaders = $mapper;
290
291 $customfields = CRM_Core_BAO_CustomField::getFields($this->_contactType);
292 foreach ($customHeaders as $key => $value) {
293 if ($id = CRM_Core_BAO_CustomField::getKeyID($value)) {
294 $customHeaders[$key] = $customfields[$id][0];
295 }
296 }
297
298 if ($this->_invalidRowCount) {
299 // removed view url for invlaid contacts
be2fb01f 300 $headers = array_merge([
69078420
SL
301 ts('Line Number'),
302 ts('Reason'),
303 ], $customHeaders);
6a488035
TO
304 $this->_errorFileName = self::errorFileName(self::ERROR);
305 self::exportCSV($this->_errorFileName, $headers, $this->_errors);
306 }
307 if ($this->_conflictCount) {
be2fb01f 308 $headers = array_merge([
69078420
SL
309 ts('Line Number'),
310 ts('Reason'),
311 ], $customHeaders);
6a488035
TO
312 $this->_conflictFileName = self::errorFileName(self::CONFLICT);
313 self::exportCSV($this->_conflictFileName, $headers, $this->_conflicts);
314 }
315 if ($this->_duplicateCount) {
be2fb01f 316 $headers = array_merge([
69078420
SL
317 ts('Line Number'),
318 ts('View Contact URL'),
319 ], $customHeaders);
6a488035
TO
320
321 $this->_duplicateFileName = self::errorFileName(self::DUPLICATE);
322 self::exportCSV($this->_duplicateFileName, $headers, $this->_duplicates);
323 }
324 if ($this->_unMatchCount) {
be2fb01f 325 $headers = array_merge([
69078420
SL
326 ts('Line Number'),
327 ts('Reason'),
328 ], $customHeaders);
6a488035
TO
329
330 $this->_misMatchFilemName = self::errorFileName(self::NO_MATCH);
331 self::exportCSV($this->_misMatchFilemName, $headers, $this->_unMatch);
332 }
333 if ($this->_unparsedAddressCount) {
be2fb01f 334 $headers = array_merge([
69078420
SL
335 ts('Line Number'),
336 ts('Contact Edit URL'),
337 ], $customHeaders);
6a488035
TO
338 $this->_errorFileName = self::errorFileName(self::UNPARSED_ADDRESS_WARNING);
339 self::exportCSV($this->_errorFileName, $headers, $this->_unparsedAddresses);
340 }
341 }
342 //echo "$this->_totalCount,$this->_invalidRowCount,$this->_conflictCount,$this->_duplicateCount";
343 return $this->fini();
344 }
345
6a488035 346 /**
fe482240 347 * Given a list of the importable field keys that the user has selected.
6a488035
TO
348 * set the active fields array to this list
349 *
ae5ffbb7
TO
350 * @param array $fieldKeys
351 * Mapped array of values.
6a488035 352 */
00be9182 353 public function setActiveFields($fieldKeys) {
6a488035
TO
354 $this->_activeFieldCount = count($fieldKeys);
355 foreach ($fieldKeys as $key) {
356 if (empty($this->_fields[$key])) {
719a6fec 357 $this->_activeFields[] = new CRM_Contact_Import_Field('', ts('- do not import -'));
6a488035
TO
358 }
359 else {
360 $this->_activeFields[] = clone($this->_fields[$key]);
361 }
362 }
363 }
364
86538308
EM
365 /**
366 * @param $elements
367 */
00be9182 368 public function setActiveFieldLocationTypes($elements) {
6a488035
TO
369 for ($i = 0; $i < count($elements); $i++) {
370 $this->_activeFields[$i]->_hasLocationType = $elements[$i];
371 }
372 }
373
86538308
EM
374 /**
375 * @param $elements
376 */
69078420 377
86538308
EM
378 /**
379 * @param $elements
380 */
00be9182 381 public function setActiveFieldPhoneTypes($elements) {
6a488035
TO
382 for ($i = 0; $i < count($elements); $i++) {
383 $this->_activeFields[$i]->_phoneType = $elements[$i];
384 }
385 }
386
86538308
EM
387 /**
388 * @param $elements
389 */
00be9182 390 public function setActiveFieldWebsiteTypes($elements) {
6a488035
TO
391 for ($i = 0; $i < count($elements); $i++) {
392 $this->_activeFields[$i]->_websiteType = $elements[$i];
393 }
394 }
395
396 /**
fe482240 397 * Set IM Service Provider type fields.
6a488035 398 *
77c5b619
TO
399 * @param array $elements
400 * IM service provider type ids.
6a488035 401 */
00be9182 402 public function setActiveFieldImProviders($elements) {
6a488035
TO
403 for ($i = 0; $i < count($elements); $i++) {
404 $this->_activeFields[$i]->_imProvider = $elements[$i];
405 }
406 }
407
86538308
EM
408 /**
409 * @param $elements
410 */
00be9182 411 public function setActiveFieldRelated($elements) {
6a488035
TO
412 for ($i = 0; $i < count($elements); $i++) {
413 $this->_activeFields[$i]->_related = $elements[$i];
414 }
415 }
416
86538308
EM
417 /**
418 * @param $elements
419 */
00be9182 420 public function setActiveFieldRelatedContactType($elements) {
6a488035
TO
421 for ($i = 0; $i < count($elements); $i++) {
422 $this->_activeFields[$i]->_relatedContactType = $elements[$i];
423 }
424 }
425
86538308
EM
426 /**
427 * @param $elements
428 */
00be9182 429 public function setActiveFieldRelatedContactDetails($elements) {
6a488035
TO
430 for ($i = 0; $i < count($elements); $i++) {
431 $this->_activeFields[$i]->_relatedContactDetails = $elements[$i];
432 }
433 }
434
86538308
EM
435 /**
436 * @param $elements
437 */
00be9182 438 public function setActiveFieldRelatedContactLocType($elements) {
6a488035
TO
439 for ($i = 0; $i < count($elements); $i++) {
440 $this->_activeFields[$i]->_relatedContactLocType = $elements[$i];
441 }
442 }
443
86538308 444 /**
4815ab3d 445 * Set active field for related contact's phone type.
446 *
447 * @param array $elements
86538308 448 */
00be9182 449 public function setActiveFieldRelatedContactPhoneType($elements) {
6a488035
TO
450 for ($i = 0; $i < count($elements); $i++) {
451 $this->_activeFields[$i]->_relatedContactPhoneType = $elements[$i];
452 }
453 }
454
86538308
EM
455 /**
456 * @param $elements
457 */
00be9182 458 public function setActiveFieldRelatedContactWebsiteType($elements) {
6a488035
TO
459 for ($i = 0; $i < count($elements); $i++) {
460 $this->_activeFields[$i]->_relatedContactWebsiteType = $elements[$i];
461 }
462 }
463
464 /**
fe482240 465 * Set IM Service Provider type fields for related contacts.
6a488035 466 *
77c5b619
TO
467 * @param array $elements
468 * IM service provider type ids of related contact.
6a488035 469 */
00be9182 470 public function setActiveFieldRelatedContactImProvider($elements) {
6a488035
TO
471 for ($i = 0; $i < count($elements); $i++) {
472 $this->_activeFields[$i]->_relatedContactImProvider = $elements[$i];
473 }
474 }
475
476 /**
fe482240 477 * Format the field values for input to the api.
6a488035 478 *
a6c01b45
CW
479 * @return array
480 * (reference ) associative array of name/value pairs
6a488035 481 */
00be9182 482 public function &getActiveFieldParams() {
be2fb01f 483 $params = [];
6a488035 484
6a488035
TO
485 for ($i = 0; $i < $this->_activeFieldCount; $i++) {
486 if ($this->_activeFields[$i]->_name == 'do_not_import') {
487 continue;
488 }
489
490 if (isset($this->_activeFields[$i]->_value)) {
491 if (isset($this->_activeFields[$i]->_hasLocationType)) {
492 if (!isset($params[$this->_activeFields[$i]->_name])) {
be2fb01f 493 $params[$this->_activeFields[$i]->_name] = [];
6a488035
TO
494 }
495
be2fb01f 496 $value = [
ae5ffbb7
TO
497 $this->_activeFields[$i]->_name => $this->_activeFields[$i]->_value,
498 'location_type_id' => $this->_activeFields[$i]->_hasLocationType,
be2fb01f 499 ];
6a488035
TO
500
501 if (isset($this->_activeFields[$i]->_phoneType)) {
502 $value['phone_type_id'] = $this->_activeFields[$i]->_phoneType;
503 }
504
505 // get IM service Provider type id
506 if (isset($this->_activeFields[$i]->_imProvider)) {
507 $value['provider_id'] = $this->_activeFields[$i]->_imProvider;
508 }
509
510 $params[$this->_activeFields[$i]->_name][] = $value;
511 }
512 elseif (isset($this->_activeFields[$i]->_websiteType)) {
be2fb01f 513 $value = [
6a488035
TO
514 $this->_activeFields[$i]->_name => $this->_activeFields[$i]->_value,
515 'website_type_id' => $this->_activeFields[$i]->_websiteType,
be2fb01f 516 ];
6a488035
TO
517
518 $params[$this->_activeFields[$i]->_name][] = $value;
519 }
520
521 if (!isset($params[$this->_activeFields[$i]->_name])) {
522 if (!isset($this->_activeFields[$i]->_related)) {
523 $params[$this->_activeFields[$i]->_name] = $this->_activeFields[$i]->_value;
524 }
525 }
526
527 //minor fix for CRM-4062
528 if (isset($this->_activeFields[$i]->_related)) {
529 if (!isset($params[$this->_activeFields[$i]->_related])) {
be2fb01f 530 $params[$this->_activeFields[$i]->_related] = [];
6a488035
TO
531 }
532
533 if (!isset($params[$this->_activeFields[$i]->_related]['contact_type']) && !empty($this->_activeFields[$i]->_relatedContactType)) {
534 $params[$this->_activeFields[$i]->_related]['contact_type'] = $this->_activeFields[$i]->_relatedContactType;
535 }
536
537 if (isset($this->_activeFields[$i]->_relatedContactLocType) && !empty($this->_activeFields[$i]->_value)) {
7c7c2ddb 538 if (!empty($params[$this->_activeFields[$i]->_related][$this->_activeFields[$i]->_relatedContactDetails]) &&
353ffa53
TO
539 !is_array($params[$this->_activeFields[$i]->_related][$this->_activeFields[$i]->_relatedContactDetails])
540 ) {
be2fb01f 541 $params[$this->_activeFields[$i]->_related][$this->_activeFields[$i]->_relatedContactDetails] = [];
6a488035 542 }
be2fb01f 543 $value = [
6a488035
TO
544 $this->_activeFields[$i]->_relatedContactDetails => $this->_activeFields[$i]->_value,
545 'location_type_id' => $this->_activeFields[$i]->_relatedContactLocType,
be2fb01f 546 ];
6a488035
TO
547
548 if (isset($this->_activeFields[$i]->_relatedContactPhoneType)) {
549 $value['phone_type_id'] = $this->_activeFields[$i]->_relatedContactPhoneType;
550 }
551
552 // get IM service Provider type id for related contact
553 if (isset($this->_activeFields[$i]->_relatedContactImProvider)) {
554 $value['provider_id'] = $this->_activeFields[$i]->_relatedContactImProvider;
555 }
556
557 $params[$this->_activeFields[$i]->_related][$this->_activeFields[$i]->_relatedContactDetails][] = $value;
558 }
559 elseif (isset($this->_activeFields[$i]->_relatedContactWebsiteType)) {
be2fb01f 560 $params[$this->_activeFields[$i]->_related][$this->_activeFields[$i]->_relatedContactDetails][] = [
6a488035
TO
561 'url' => $this->_activeFields[$i]->_value,
562 'website_type_id' => $this->_activeFields[$i]->_relatedContactWebsiteType,
be2fb01f 563 ];
6a488035
TO
564 }
565 else {
566 $params[$this->_activeFields[$i]->_related][$this->_activeFields[$i]->_relatedContactDetails] = $this->_activeFields[$i]->_value;
567 }
568 }
569 }
570 }
571
572 return $params;
573 }
574
86538308
EM
575 /**
576 * @return array
577 */
00be9182 578 public function getColumnPatterns() {
be2fb01f 579 $values = [];
6a488035
TO
580 foreach ($this->_fields as $name => $field) {
581 $values[$name] = $field->_columnPattern;
582 }
583 return $values;
584 }
585
86538308 586 /**
100fef9d 587 * @param string $name
86538308
EM
588 * @param $title
589 * @param int $type
590 * @param string $headerPattern
591 * @param string $dataPattern
592 * @param bool $hasLocationType
593 */
ae5ffbb7 594 public function addField(
51ccfbbe 595 $name, $title, $type = CRM_Utils_Type::T_INT,
6a488035
TO
596 $headerPattern = '//', $dataPattern = '//',
597 $hasLocationType = FALSE
598 ) {
719a6fec 599 $this->_fields[$name] = new CRM_Contact_Import_Field($name, $title, $type, $headerPattern, $dataPattern, $hasLocationType);
6a488035 600 if (empty($name)) {
719a6fec 601 $this->_fields['doNotImport'] = new CRM_Contact_Import_Field($name, $title, $type, $headerPattern, $dataPattern, $hasLocationType);
6a488035
TO
602 }
603 }
604
6a488035 605 /**
fe482240 606 * Store parser values.
6a488035
TO
607 *
608 * @param CRM_Core_Session $store
609 *
2a6da8d7 610 * @param int $mode
6a488035 611 */
00be9182 612 public function set($store, $mode = self::MODE_SUMMARY) {
6a488035
TO
613 $store->set('rowCount', $this->_rowCount);
614 $store->set('fields', $this->getSelectValues());
615 $store->set('fieldTypes', $this->getSelectTypes());
616
617 $store->set('columnPatterns', $this->getColumnPatterns());
618 $store->set('dataPatterns', $this->getDataPatterns());
619 $store->set('columnCount', $this->_activeFieldCount);
620
621 $store->set('totalRowCount', $this->_totalCount);
622 $store->set('validRowCount', $this->_validCount);
623 $store->set('invalidRowCount', $this->_invalidRowCount);
624 $store->set('conflictRowCount', $this->_conflictCount);
625 $store->set('unMatchCount', $this->_unMatchCount);
626
627 switch ($this->_contactType) {
628 case 'Individual':
a05662ef 629 $store->set('contactType', CRM_Import_Parser::CONTACT_INDIVIDUAL);
6a488035
TO
630 break;
631
632 case 'Household':
a05662ef 633 $store->set('contactType', CRM_Import_Parser::CONTACT_HOUSEHOLD);
6a488035
TO
634 break;
635
636 case 'Organization':
a05662ef 637 $store->set('contactType', CRM_Import_Parser::CONTACT_ORGANIZATION);
6a488035
TO
638 }
639
640 if ($this->_invalidRowCount) {
641 $store->set('errorsFileName', $this->_errorFileName);
642 }
643 if ($this->_conflictCount) {
644 $store->set('conflictsFileName', $this->_conflictFileName);
645 }
646 if (isset($this->_rows) && !empty($this->_rows)) {
647 $store->set('dataValues', $this->_rows);
648 }
649
650 if ($this->_unMatchCount) {
651 $store->set('mismatchFileName', $this->_misMatchFilemName);
652 }
653
654 if ($mode == self::MODE_IMPORT) {
655 $store->set('duplicateRowCount', $this->_duplicateCount);
656 $store->set('unparsedAddressCount', $this->_unparsedAddressCount);
657 if ($this->_duplicateCount) {
658 $store->set('duplicatesFileName', $this->_duplicateFileName);
659 }
660 if ($this->_unparsedAddressCount) {
661 $store->set('errorsFileName', $this->_errorFileName);
662 }
663 }
664 //echo "$this->_totalCount,$this->_invalidRowCount,$this->_conflictCount,$this->_duplicateCount";
665 }
666
667 /**
fe482240 668 * Export data to a CSV file.
6a488035 669 *
100fef9d 670 * @param string $fileName
6a488035 671 * @param array $header
c490a46a 672 * @param array $data
4815ab3d 673 */
00be9182 674 public static function exportCSV($fileName, $header, $data) {
6a488035
TO
675
676 if (file_exists($fileName) && !is_writable($fileName)) {
677 CRM_Core_Error::movedSiteError($fileName);
678 }
679 //hack to remove '_status', '_statusMsg' and '_id' from error file
be2fb01f
CW
680 $errorValues = [];
681 $dbRecordStatus = ['IMPORTED', 'ERROR', 'DUPLICATE', 'INVALID', 'NEW'];
6a488035
TO
682 foreach ($data as $rowCount => $rowValues) {
683 $count = 0;
684 foreach ($rowValues as $key => $val) {
685 if (in_array($val, $dbRecordStatus) && $count == (count($rowValues) - 3)) {
686 break;
687 }
688 $errorValues[$rowCount][$key] = $val;
689 $count++;
690 }
691 }
692 $data = $errorValues;
693
be2fb01f 694 $output = [];
6a488035
TO
695 $fd = fopen($fileName, 'w');
696
697 foreach ($header as $key => $value) {
698 $header[$key] = "\"$value\"";
699 }
700 $config = CRM_Core_Config::singleton();
701 $output[] = implode($config->fieldSeparator, $header);
702
703 foreach ($data as $datum) {
704 foreach ($datum as $key => $value) {
705 $datum[$key] = "\"$value\"";
706 }
707 $output[] = implode($config->fieldSeparator, $datum);
708 }
709 fwrite($fd, implode("\n", $output));
710 fclose($fd);
711 }
712
713 /**
4815ab3d 714 * Update the record with PK $id in the import database table.
6a488035
TO
715 *
716 * @param int $id
717 * @param array $params
6a488035
TO
718 */
719 public function updateImportRecord($id, &$params) {
720 $statusFieldName = $this->_statusFieldName;
721 $primaryKeyName = $this->_primaryKeyName;
722
723 if ($statusFieldName && $primaryKeyName) {
724 $dao = new CRM_Core_DAO();
725 $db = $dao->getDatabaseConnection();
726
727 $query = "UPDATE $this->_tableName
52892e8b 728 SET $statusFieldName = ?,
6a488035 729 ${statusFieldName}Msg = ?
52892e8b 730 WHERE $primaryKeyName = ?";
be2fb01f 731 $args = [
6a488035
TO
732 $params[$statusFieldName],
733 CRM_Utils_Array::value("${statusFieldName}Msg", $params),
734 $id,
be2fb01f 735 ];
6a488035
TO
736
737 //print "Running query: $query<br/>With arguments: ".$params[$statusFieldName].", ".$params["${statusFieldName}Msg"].", $id<br/>";
738
739 $db->query($query, $args);
740 }
741 }
742
5dc4d424 743 /**
744 * Format common params data to proper format to store.
745 *
746 * @param array $params
747 * Contain record values.
748 * @param array $formatted
749 * Array of formatted data.
750 * @param array $contactFields
751 * Contact DAO fields.
752 */
753 public function formatCommonData($params, &$formatted, &$contactFields) {
be2fb01f 754 $csType = [
5dc4d424 755 CRM_Utils_Array::value('contact_type', $formatted),
be2fb01f 756 ];
5dc4d424 757
758 //CRM-5125
759 //add custom fields for contact sub type
760 if (!empty($this->_contactSubType)) {
761 $csType = $this->_contactSubType;
762 }
763
764 if ($relCsType = CRM_Utils_Array::value('contact_sub_type', $formatted)) {
765 $csType = $relCsType;
766 }
767
768 $customFields = CRM_Core_BAO_CustomField::getFields($formatted['contact_type'], FALSE, FALSE, $csType);
769
770 $addressCustomFields = CRM_Core_BAO_CustomField::getFields('Address');
771 $customFields = $customFields + $addressCustomFields;
772
773 //if a Custom Email Greeting, Custom Postal Greeting or Custom Addressee is mapped, and no "Greeting / Addressee Type ID" is provided, then automatically set the type = Customized, CRM-4575
be2fb01f 774 $elements = [
5dc4d424 775 'email_greeting_custom' => 'email_greeting',
776 'postal_greeting_custom' => 'postal_greeting',
777 'addressee_custom' => 'addressee',
be2fb01f 778 ];
5dc4d424 779 foreach ($elements as $k => $v) {
780 if (array_key_exists($k, $params) && !(array_key_exists($v, $params))) {
781 $label = key(CRM_Core_OptionGroup::values($v, TRUE, NULL, NULL, 'AND v.name = "Customized"'));
782 $params[$v] = $label;
783 }
784 }
785
786 //format date first
787 $session = CRM_Core_Session::singleton();
788 $dateType = $session->get("dateTypes");
789 foreach ($params as $key => $val) {
790 $customFieldID = CRM_Core_BAO_CustomField::getKeyID($key);
791 if ($customFieldID &&
792 !array_key_exists($customFieldID, $addressCustomFields)
793 ) {
794 //we should not update Date to null, CRM-4062
795 if ($val && ($customFields[$customFieldID]['data_type'] == 'Date')) {
22161818 796 //CRM-21267
160a5de1 797 CRM_Contact_Import_Parser_Contact::formatCustomDate($params, $formatted, $dateType, $key);
5dc4d424 798 }
799 elseif ($customFields[$customFieldID]['data_type'] == 'Boolean') {
800 if (empty($val) && !is_numeric($val) && $this->_onDuplicate == CRM_Import_Parser::DUPLICATE_FILL) {
801 //retain earlier value when Import mode is `Fill`
802 unset($params[$key]);
803 }
804 else {
805 $params[$key] = CRM_Utils_String::strtoboolstr($val);
806 }
807 }
808 }
809
810 if ($key == 'birth_date' && $val) {
811 CRM_Utils_Date::convertToDefaultDate($params, $dateType, $key);
812 }
813 elseif ($key == 'deceased_date' && $val) {
814 CRM_Utils_Date::convertToDefaultDate($params, $dateType, $key);
815 $params['is_deceased'] = 1;
816 }
817 elseif ($key == 'is_deceased' && $val) {
818 $params[$key] = CRM_Utils_String::strtoboolstr($val);
819 }
820 elseif ($key == 'gender') {
821 //CRM-4360
822 $params[$key] = $this->checkGender($val);
823 }
824 }
825
826 //now format custom data.
827 foreach ($params as $key => $field) {
828 if (is_array($field)) {
829 $isAddressCustomField = FALSE;
830 foreach ($field as $value) {
831 $break = FALSE;
832 if (is_array($value)) {
833 foreach ($value as $name => $testForEmpty) {
834 if ($addressCustomFieldID = CRM_Core_BAO_CustomField::getKeyID($name)) {
835 $isAddressCustomField = TRUE;
836 break;
837 }
838 // check if $value does not contain IM provider or phoneType
839 if (($name !== 'phone_type_id' || $name !== 'provider_id') && ($testForEmpty === '' || $testForEmpty == NULL)) {
840 $break = TRUE;
841 break;
842 }
843 }
844 }
845 else {
846 $break = TRUE;
847 }
848
849 if (!$break) {
9afab633 850 if (!empty($value['location_type_id'])) {
851 $this->formatLocationBlock($value, $formatted);
852 }
853 else {
854 CRM_Core_Error::deprecatedFunctionWarning('this is not expected to be reachable now');
855 $this->formatContactParameters($value, $formatted);
856 }
5dc4d424 857 }
858 }
859 if (!$isAddressCustomField) {
860 continue;
861 }
862 }
863
be2fb01f 864 $formatValues = [
5dc4d424 865 $key => $field,
be2fb01f 866 ];
5dc4d424 867
868 if (($key !== 'preferred_communication_method') && (array_key_exists($key, $contactFields))) {
869 // due to merging of individual table and
870 // contact table, we need to avoid
871 // preferred_communication_method forcefully
872 $formatValues['contact_type'] = $formatted['contact_type'];
873 }
874
875 if ($key == 'id' && isset($field)) {
876 $formatted[$key] = $field;
877 }
878 $this->formatContactParameters($formatValues, $formatted);
879
880 //Handling Custom Data
881 // note: Address custom fields will be handled separately inside formatContactParameters
882 if (($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) &&
883 array_key_exists($customFieldID, $customFields) &&
884 !array_key_exists($customFieldID, $addressCustomFields)
885 ) {
886
887 $extends = CRM_Utils_Array::value('extends', $customFields[$customFieldID]);
888 $htmlType = CRM_Utils_Array::value('html_type', $customFields[$customFieldID]);
889 switch ($htmlType) {
890 case 'Select':
891 case 'Radio':
892 case 'Autocomplete-Select':
893 if ($customFields[$customFieldID]['data_type'] == 'String' || $customFields[$customFieldID]['data_type'] == 'Int') {
894 $customOption = CRM_Core_BAO_CustomOption::getCustomOption($customFieldID, TRUE);
895 foreach ($customOption as $customValue) {
896 $val = CRM_Utils_Array::value('value', $customValue);
897 $label = CRM_Utils_Array::value('label', $customValue);
898 $label = strtolower($label);
899 $value = strtolower(trim($formatted[$key]));
900 if (($value == $label) || ($value == strtolower($val))) {
901 $params[$key] = $formatted[$key] = $val;
902 }
903 }
904 }
905 break;
906
907 case 'CheckBox':
5dc4d424 908 case 'Multi-Select':
909
910 if (!empty($formatted[$key]) && !empty($params[$key])) {
911 $mulValues = explode(',', $formatted[$key]);
912 $customOption = CRM_Core_BAO_CustomOption::getCustomOption($customFieldID, TRUE);
be2fb01f
CW
913 $formatted[$key] = [];
914 $params[$key] = [];
5dc4d424 915 foreach ($mulValues as $v1) {
916 foreach ($customOption as $v2) {
917 if ((strtolower($v2['label']) == strtolower(trim($v1))) ||
918 (strtolower($v2['value']) == strtolower(trim($v1)))
919 ) {
920 if ($htmlType == 'CheckBox') {
921 $params[$key][$v2['value']] = $formatted[$key][$v2['value']] = 1;
922 }
923 else {
924 $params[$key][] = $formatted[$key][] = $v2['value'];
925 }
926 }
927 }
928 }
929 }
930 break;
931 }
932 }
933 }
934
935 if (!empty($key) && ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) && array_key_exists($customFieldID, $customFields) &&
936 !array_key_exists($customFieldID, $addressCustomFields)
937 ) {
938 // @todo calling api functions directly is not supported
939 _civicrm_api3_custom_format_params($params, $formatted, $extends);
940 }
941
942 // to check if not update mode and unset the fields with empty value.
943 if (!$this->_updateWithId && array_key_exists('custom', $formatted)) {
944 foreach ($formatted['custom'] as $customKey => $customvalue) {
945 if (empty($formatted['custom'][$customKey][-1]['is_required'])) {
946 $formatted['custom'][$customKey][-1]['is_required'] = $customFields[$customKey]['is_required'];
947 }
948 $emptyValue = CRM_Utils_Array::value('value', $customvalue[-1]);
949 if (!isset($emptyValue)) {
950 unset($formatted['custom'][$customKey]);
951 }
952 }
953 }
954
955 // parse street address, CRM-5450
956 if ($this->_parseStreetAddress) {
957 if (array_key_exists('address', $formatted) && is_array($formatted['address'])) {
958 foreach ($formatted['address'] as $instance => & $address) {
959 $streetAddress = CRM_Utils_Array::value('street_address', $address);
960 if (empty($streetAddress)) {
961 continue;
962 }
963 // parse address field.
964 $parsedFields = CRM_Core_BAO_Address::parseStreetAddress($streetAddress);
965
966 //street address consider to be parsed properly,
967 //If we get street_name and street_number.
968 if (empty($parsedFields['street_name']) || empty($parsedFields['street_number'])) {
969 $parsedFields = array_fill_keys(array_keys($parsedFields), '');
970 }
971
972 // merge parse address w/ main address block.
973 $address = array_merge($address, $parsedFields);
974 }
975 }
976 }
977 }
978
979 /**
980 * Format contact parameters.
981 *
982 * @todo this function needs re-writing & re-merging into the main function.
983 *
984 * Here be dragons.
985 *
986 * @param array $values
987 * @param array $params
988 *
989 * @return bool
990 */
991 protected function formatContactParameters(&$values, &$params) {
992 // Crawl through the possible classes:
993 // Contact
994 // Individual
995 // Household
996 // Organization
997 // Location
998 // Address
999 // Email
1000 // Phone
1001 // IM
1002 // Note
1003 // Custom
1004
1005 // Cache the various object fields
be2fb01f 1006 static $fields = [];
5dc4d424 1007
1008 // first add core contact values since for other Civi modules they are not added
1009 $contactFields = CRM_Contact_DAO_Contact::fields();
1010 _civicrm_api3_store_values($contactFields, $values, $params);
1011
1012 if (isset($values['contact_type'])) {
1013 // we're an individual/household/org property
1014
1015 $fields[$values['contact_type']] = CRM_Contact_DAO_Contact::fields();
1016
1017 _civicrm_api3_store_values($fields[$values['contact_type']], $values, $params);
1018 return TRUE;
1019 }
1020
1021 if (isset($values['individual_prefix'])) {
1022 if (!empty($params['prefix_id'])) {
1023 $prefixes = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'prefix_id');
1024 $params['prefix'] = $prefixes[$params['prefix_id']];
1025 }
1026 else {
1027 $params['prefix'] = $values['individual_prefix'];
1028 }
1029 return TRUE;
1030 }
1031
1032 if (isset($values['individual_suffix'])) {
1033 if (!empty($params['suffix_id'])) {
1034 $suffixes = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'suffix_id');
1035 $params['suffix'] = $suffixes[$params['suffix_id']];
1036 }
1037 else {
1038 $params['suffix'] = $values['individual_suffix'];
1039 }
1040 return TRUE;
1041 }
1042
1043 // CRM-4575
1044 if (isset($values['email_greeting'])) {
1045 if (!empty($params['email_greeting_id'])) {
be2fb01f 1046 $emailGreetingFilter = [
5dc4d424 1047 'contact_type' => CRM_Utils_Array::value('contact_type', $params),
1048 'greeting_type' => 'email_greeting',
be2fb01f 1049 ];
5dc4d424 1050 $emailGreetings = CRM_Core_PseudoConstant::greeting($emailGreetingFilter);
1051 $params['email_greeting'] = $emailGreetings[$params['email_greeting_id']];
1052 }
1053 else {
1054 $params['email_greeting'] = $values['email_greeting'];
1055 }
1056
1057 return TRUE;
1058 }
1059
1060 if (isset($values['postal_greeting'])) {
1061 if (!empty($params['postal_greeting_id'])) {
be2fb01f 1062 $postalGreetingFilter = [
5dc4d424 1063 'contact_type' => CRM_Utils_Array::value('contact_type', $params),
1064 'greeting_type' => 'postal_greeting',
be2fb01f 1065 ];
5dc4d424 1066 $postalGreetings = CRM_Core_PseudoConstant::greeting($postalGreetingFilter);
1067 $params['postal_greeting'] = $postalGreetings[$params['postal_greeting_id']];
1068 }
1069 else {
1070 $params['postal_greeting'] = $values['postal_greeting'];
1071 }
1072 return TRUE;
1073 }
1074
1075 if (isset($values['addressee'])) {
1076 if (!empty($params['addressee_id'])) {
be2fb01f 1077 $addresseeFilter = [
5dc4d424 1078 'contact_type' => CRM_Utils_Array::value('contact_type', $params),
1079 'greeting_type' => 'addressee',
be2fb01f 1080 ];
5dc4d424 1081 $addressee = CRM_Core_PseudoConstant::addressee($addresseeFilter);
1082 $params['addressee'] = $addressee[$params['addressee_id']];
1083 }
1084 else {
1085 $params['addressee'] = $values['addressee'];
1086 }
1087 return TRUE;
1088 }
1089
1090 if (isset($values['gender'])) {
1091 if (!empty($params['gender_id'])) {
1092 $genders = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'gender_id');
1093 $params['gender'] = $genders[$params['gender_id']];
1094 }
1095 else {
1096 $params['gender'] = $values['gender'];
1097 }
1098 return TRUE;
1099 }
1100
1101 if (!empty($values['preferred_communication_method'])) {
be2fb01f 1102 $comm = [];
5dc4d424 1103 $pcm = array_change_key_case(array_flip(CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'preferred_communication_method')), CASE_LOWER);
1104
1105 $preffComm = explode(',', $values['preferred_communication_method']);
1106 foreach ($preffComm as $v) {
1107 $v = strtolower(trim($v));
1108 if (array_key_exists($v, $pcm)) {
1109 $comm[$pcm[$v]] = 1;
1110 }
1111 }
1112
1113 $params['preferred_communication_method'] = $comm;
1114 return TRUE;
1115 }
1116
1117 // format the website params.
1118 if (!empty($values['url'])) {
1119 static $websiteFields;
1120 if (!is_array($websiteFields)) {
1121 $websiteFields = CRM_Core_DAO_Website::fields();
1122 }
1123 if (!array_key_exists('website', $params) ||
1124 !is_array($params['website'])
1125 ) {
be2fb01f 1126 $params['website'] = [];
5dc4d424 1127 }
1128
1129 $websiteCount = count($params['website']);
1130 _civicrm_api3_store_values($websiteFields, $values,
1131 $params['website'][++$websiteCount]
1132 );
1133
1134 return TRUE;
1135 }
1136
1137 // get the formatted location blocks into params - w/ 3.0 format, CRM-4605
1138 if (!empty($values['location_type_id'])) {
9afab633 1139 CRM_Core_Error::deprecatedFunctionWarning('this is not expected to be reachable now');
0b3f2e42 1140 return $this->formatLocationBlock($values, $params);
5dc4d424 1141 }
1142
1143 if (isset($values['note'])) {
1144 // add a note field
1145 if (!isset($params['note'])) {
be2fb01f 1146 $params['note'] = [];
5dc4d424 1147 }
1148 $noteBlock = count($params['note']) + 1;
1149
be2fb01f 1150 $params['note'][$noteBlock] = [];
5dc4d424 1151 if (!isset($fields['Note'])) {
1152 $fields['Note'] = CRM_Core_DAO_Note::fields();
1153 }
1154
1155 // get the current logged in civicrm user
1156 $session = CRM_Core_Session::singleton();
1157 $userID = $session->get('userID');
1158
1159 if ($userID) {
1160 $values['contact_id'] = $userID;
1161 }
1162
1163 _civicrm_api3_store_values($fields['Note'], $values, $params['note'][$noteBlock]);
1164
1165 return TRUE;
1166 }
1167
1168 // Check for custom field values
1169
1170 if (empty($fields['custom'])) {
1171 $fields['custom'] = &CRM_Core_BAO_CustomField::getFields(CRM_Utils_Array::value('contact_type', $values),
1172 FALSE, FALSE, NULL, NULL, FALSE, FALSE, FALSE
1173 );
1174 }
1175
1176 foreach ($values as $key => $value) {
1177 if ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) {
1178 // check if it's a valid custom field id
1179
1180 if (!array_key_exists($customFieldID, $fields['custom'])) {
1181 return civicrm_api3_create_error('Invalid custom field ID');
1182 }
1183 else {
1184 $params[$key] = $value;
1185 }
1186 }
1187 }
1188 return TRUE;
1189 }
1190
c4c7a315 1191 /**
1192 * Format location block ready for importing.
1193 *
1194 * There is some test coverage for this in CRM_Contact_Import_Parser_ContactTest
1195 * e.g. testImportPrimaryAddress.
1196 *
1197 * @param array $values
1198 * @param array $params
c4c7a315 1199 *
1200 * @return bool
1201 */
0b3f2e42 1202 protected function formatLocationBlock(&$values, &$params) {
c4c7a315 1203 $blockTypes = [
1204 'phone' => 'Phone',
1205 'email' => 'Email',
1206 'im' => 'IM',
1207 'openid' => 'OpenID',
1208 'phone_ext' => 'Phone',
1209 ];
1210 foreach ($blockTypes as $blockFieldName => $block) {
1211 if (!array_key_exists($blockFieldName, $values)) {
1212 continue;
1213 }
1214
1215 // block present in value array.
1216 if (!array_key_exists($blockFieldName, $params) || !is_array($params[$blockFieldName])) {
1217 $params[$blockFieldName] = [];
1218 }
1219
0b3f2e42 1220 $fields[$block] = $this->getMetadataForEntity($block);
c4c7a315 1221
c4c7a315 1222 // copy value to dao field name.
1223 if ($blockFieldName == 'im') {
1224 $values['name'] = $values[$blockFieldName];
1225 }
1226
1227 _civicrm_api3_store_values($fields[$block], $values,
2ab10f80 1228 $params[$blockFieldName][$values['location_type_id']]
c4c7a315 1229 );
1230
2ab10f80 1231 $this->fillPrimary($params[$blockFieldName][$values['location_type_id']], $values, $block, CRM_Utils_Array::value('id', $params));
c4c7a315 1232
2ab10f80 1233 if (empty($params['id']) && (count($params[$blockFieldName]) == 1)) {
1234 $params[$blockFieldName][$values['location_type_id']]['is_primary'] = TRUE;
c4c7a315 1235 }
1236
1237 // we only process single block at a time.
1238 return TRUE;
1239 }
1240
1241 // handle address fields.
1242 if (!array_key_exists('address', $params) || !is_array($params['address'])) {
1243 $params['address'] = [];
1244 }
1245
c4c7a315 1246 // Note: we doing multiple value formatting here for address custom fields, plus putting into right format.
1247 // The actual formatting (like date, country ..etc) for address custom fields is taken care of while saving
1248 // the address in CRM_Core_BAO_Address::create method
1249 if (!empty($values['location_type_id'])) {
1250 static $customFields = [];
1251 if (empty($customFields)) {
1252 $customFields = CRM_Core_BAO_CustomField::getFields('Address');
1253 }
1254 // make a copy of values, as we going to make changes
1255 $newValues = $values;
1256 foreach ($values as $key => $val) {
1257 $customFieldID = CRM_Core_BAO_CustomField::getKeyID($key);
1258 if ($customFieldID && array_key_exists($customFieldID, $customFields)) {
c4c7a315 1259
1260 $htmlType = CRM_Utils_Array::value('html_type', $customFields[$customFieldID]);
1261 switch ($htmlType) {
1262 case 'CheckBox':
1263 case 'Multi-Select':
1264 if ($val) {
1265 $mulValues = explode(',', $val);
1266 $customOption = CRM_Core_BAO_CustomOption::getCustomOption($customFieldID, TRUE);
1267 $newValues[$key] = [];
1268 foreach ($mulValues as $v1) {
1269 foreach ($customOption as $v2) {
1270 if ((strtolower($v2['label']) == strtolower(trim($v1))) ||
1271 (strtolower($v2['value']) == strtolower(trim($v1)))
1272 ) {
1273 if ($htmlType == 'CheckBox') {
1274 $newValues[$key][$v2['value']] = 1;
1275 }
1276 else {
1277 $newValues[$key][] = $v2['value'];
1278 }
1279 }
1280 }
1281 }
1282 }
1283 break;
1284 }
1285 }
1286 }
1287 // consider new values
1288 $values = $newValues;
1289 }
1290
0b3f2e42 1291 $fields['Address'] = $this->getMetadataForEntity('Address');
1292 // @todo this is kinda replicated below....
c4c7a315 1293 _civicrm_api3_store_values($fields['Address'], $values, $params['address'][$values['location_type_id']]);
1294
1295 $addressFields = [
1296 'county',
1297 'country',
1298 'state_province',
1299 'supplemental_address_1',
1300 'supplemental_address_2',
1301 'supplemental_address_3',
1302 'StateProvince.name',
1303 ];
0b3f2e42 1304 foreach (array_keys($customFields) as $customFieldID) {
1305 $addressFields[] = 'custom_' . $customFieldID;
1306 }
c4c7a315 1307
1308 foreach ($addressFields as $field) {
1309 if (array_key_exists($field, $values)) {
1310 if (!array_key_exists('address', $params)) {
1311 $params['address'] = [];
1312 }
1313 $params['address'][$values['location_type_id']][$field] = $values[$field];
1314 }
1315 }
1316
f6953904 1317 $this->fillPrimary($params['address'][$values['location_type_id']], $values, 'address', CRM_Utils_Array::value('id', $params));
c4c7a315 1318 return TRUE;
1319 }
1320
0b3f2e42 1321 /**
1322 * Get the field metadata for the relevant entity.
1323 *
1324 * @param string $entity
1325 *
1326 * @return array
1327 */
1328 protected function getMetadataForEntity($entity) {
1329 if (!isset($this->fieldMetadata[$entity])) {
1330 $className = "CRM_Core_DAO_$entity";
1331 $this->fieldMetadata[$entity] = $className::fields();
1332 }
1333 return $this->fieldMetadata[$entity];
1334 }
1335
2ab10f80 1336 /**
1337 * Fill in the primary location.
1338 *
1339 * If the contact has a primary address we update it. Otherwise
1340 * we add an address of the default location type.
1341 *
1342 * @param array $params
1343 * Address block parameters
1344 * @param array $values
1345 * Input values
1346 * @param string $entity
1347 * - address, email, phone
1348 * @param int|NULL $contactID
1349 */
1350 protected function fillPrimary(&$params, $values, $entity, $contactID) {
1351 if ($values['location_type_id'] === 'Primary') {
1352 if ($contactID) {
1353 $primary = civicrm_api3($entity, 'get', [
1354 'return' => 'location_type_id',
1355 'contact_id' => $contactID,
1356 'is_primary' => 1,
1357 'sequential' => 1
1358 ]);
1359 }
1360 $defaultLocationType = CRM_Core_BAO_LocationType::getDefault();
1361 $params['location_type_id'] = (int) (isset($primary) && $primary['count']) ? $primary['values'][0]['location_type_id'] : $defaultLocationType->id;
1362 $params['is_primary'] = 1;
1363 }
1364 }
1365
6a488035 1366}