5007ad6896a4d132ac2622c97ac4c1fa89b9e32d
[civicrm-core.git] / CRM / Contact / Import / Parser / Contact.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.5 |
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 //@todo calling api functions directly is not supported
37 require_once 'api/v3/utils.php';
38 require_once 'api/v3/Contact.php';
39
40 /**
41 * class to parse contact csv files
42 */
43 class CRM_Contact_Import_Parser_Contact extends CRM_Contact_Import_Parser {
44 protected $_mapperKeys;
45 protected $_mapperLocType;
46 protected $_mapperPhoneType;
47 protected $_mapperImProvider;
48 protected $_mapperWebsiteType;
49 protected $_mapperRelated;
50 protected $_mapperRelatedContactType;
51 protected $_mapperRelatedContactDetails;
52 protected $_mapperRelatedContactEmailType;
53 protected $_mapperRelatedContactImProvider;
54 protected $_mapperRelatedContactWebsiteType;
55 protected $_relationships;
56
57 protected $_emailIndex;
58 protected $_firstNameIndex;
59 protected $_lastNameIndex;
60
61 protected $_householdNameIndex;
62 protected $_organizationNameIndex;
63
64 protected $_allEmails;
65
66 protected $_phoneIndex;
67 protected $_updateWithId;
68 protected $_retCode;
69
70 protected $_externalIdentifierIndex;
71 protected $_allExternalIdentifiers;
72 protected $_parseStreetAddress;
73
74 /**
75 * Array of successfully imported contact id's
76 *
77 * @array
78 */
79 protected $_newContacts;
80
81 /**
82 * line count id
83 *
84 * @var int
85 */
86 protected $_lineCount;
87
88 /**
89 * Array of successfully imported related contact id's
90 *
91 * @array
92 */
93 protected $_newRelatedContacts;
94
95 /**
96 * array of all the contacts whose street addresses are not parsed
97 * of this import process
98 * @var array
99 */
100 protected $_unparsedStreetAddressContacts;
101
102 /**
103 * class constructor
104 */
105 function __construct(&$mapperKeys, $mapperLocType = NULL, $mapperPhoneType = NULL, $mapperImProvider = NULL, $mapperRelated = NULL, $mapperRelatedContactType = NULL, $mapperRelatedContactDetails = NULL, $mapperRelatedContactLocType = NULL, $mapperRelatedContactPhoneType = NULL, $mapperRelatedContactImProvider = NULL,
106 $mapperWebsiteType = NULL, $mapperRelatedContactWebsiteType = NULL
107 ) {
108 parent::__construct();
109 $this->_mapperKeys = &$mapperKeys;
110 $this->_mapperLocType = &$mapperLocType;
111 $this->_mapperPhoneType = &$mapperPhoneType;
112 $this->_mapperWebsiteType = $mapperWebsiteType;
113 // get IM service provider type id for contact
114 $this->_mapperImProvider = &$mapperImProvider;
115 $this->_mapperRelated = &$mapperRelated;
116 $this->_mapperRelatedContactType = &$mapperRelatedContactType;
117 $this->_mapperRelatedContactDetails = &$mapperRelatedContactDetails;
118 $this->_mapperRelatedContactLocType = &$mapperRelatedContactLocType;
119 $this->_mapperRelatedContactPhoneType = &$mapperRelatedContactPhoneType;
120 $this->_mapperRelatedContactWebsiteType = $mapperRelatedContactWebsiteType;
121 // get IM service provider type id for related contact
122 $this->_mapperRelatedContactImProvider = &$mapperRelatedContactImProvider;
123 }
124
125 /**
126 * the initializer code, called before the processing
127 *
128 * @return void
129 * @access public
130 */
131 function init() {
132 $contactFields = CRM_Contact_BAO_Contact::importableFields($this->_contactType);
133 // exclude the address options disabled in the Address Settings
134 $fields = CRM_Core_BAO_Address::validateAddressOptions($contactFields);
135
136 //CRM-5125
137 //supporting import for contact subtypes
138 $csType = NULL;
139 if (!empty($this->_contactSubType)) {
140 //custom fields for sub type
141 $subTypeFields = CRM_Core_BAO_CustomField::getFieldsForImport($this->_contactSubType);
142
143 if (!empty($subTypeFields)) {
144 foreach ($subTypeFields as $customSubTypeField => $details) {
145 $fields[$customSubTypeField] = $details;
146 }
147 }
148 }
149
150 //Relationship importables
151 $this->_relationships = $relations =
152 CRM_Contact_BAO_Relationship::getContactRelationshipType(
153 NULL, NULL, NULL, $this->_contactType,
154 FALSE, 'label', TRUE, $this->_contactSubType
155 );
156 asort($relations);
157
158 foreach ($relations as $key => $var) {
159 list($type) = explode('_', $key);
160 $relationshipType[$key]['title'] = $var;
161 $relationshipType[$key]['headerPattern'] = '/' . preg_quote($var, '/') . '/';
162 $relationshipType[$key]['import'] = TRUE;
163 $relationshipType[$key]['relationship_type_id'] = $type;
164 $relationshipType[$key]['related'] = TRUE;
165 }
166
167 if (!empty($relationshipType)) {
168 $fields = array_merge($fields, array(
169 'related' => array(
170 'title' => ts('- related contact info -'),
171 ),
172 ), $relationshipType);
173 }
174
175 foreach ($fields as $name => $field) {
176 $this->addField($name, $field['title'], CRM_Utils_Array::value('type', $field), CRM_Utils_Array::value('headerPattern', $field), CRM_Utils_Array::value('dataPattern', $field), CRM_Utils_Array::value('hasLocationType', $field));
177 }
178
179 $this->_newContacts = array();
180
181 $this->setActiveFields($this->_mapperKeys);
182 $this->setActiveFieldLocationTypes($this->_mapperLocType);
183 $this->setActiveFieldPhoneTypes($this->_mapperPhoneType);
184 $this->setActiveFieldWebsiteTypes($this->_mapperWebsiteType);
185 //set active fields of IM provider of contact
186 $this->setActiveFieldImProviders($this->_mapperImProvider);
187
188 //related info
189 $this->setActiveFieldRelated($this->_mapperRelated);
190 $this->setActiveFieldRelatedContactType($this->_mapperRelatedContactType);
191 $this->setActiveFieldRelatedContactDetails($this->_mapperRelatedContactDetails);
192 $this->setActiveFieldRelatedContactLocType($this->_mapperRelatedContactLocType);
193 $this->setActiveFieldRelatedContactPhoneType($this->_mapperRelatedContactPhoneType);
194 $this->setActiveFieldRelatedContactWebsiteType($this->_mapperRelatedContactWebsiteType);
195 //set active fields of IM provider of related contact
196 $this->setActiveFieldRelatedContactImProvider($this->_mapperRelatedContactImProvider);
197
198 $this->_phoneIndex = -1;
199 $this->_emailIndex = -1;
200 $this->_firstNameIndex = -1;
201 $this->_lastNameIndex = -1;
202 $this->_householdNameIndex = -1;
203 $this->_organizationNameIndex = -1;
204 $this->_externalIdentifierIndex = -1;
205
206 $index = 0;
207 foreach ($this->_mapperKeys as $key) {
208 if (substr($key, 0, 5) == 'email' && substr($key, 0, 14) != 'email_greeting') {
209 $this->_emailIndex = $index;
210 $this->_allEmails = array();
211 }
212 if (substr($key, 0, 5) == 'phone') {
213 $this->_phoneIndex = $index;
214 }
215 if ($key == 'first_name') {
216 $this->_firstNameIndex = $index;
217 }
218 if ($key == 'last_name') {
219 $this->_lastNameIndex = $index;
220 }
221 if ($key == 'household_name') {
222 $this->_householdNameIndex = $index;
223 }
224 if ($key == 'organization_name') {
225 $this->_organizationNameIndex = $index;
226 }
227
228 if ($key == 'external_identifier') {
229 $this->_externalIdentifierIndex = $index;
230 $this->_allExternalIdentifiers = array();
231 }
232 $index++;
233 }
234
235 $this->_updateWithId = FALSE;
236 if (in_array('id', $this->_mapperKeys) || ($this->_externalIdentifierIndex >= 0 && in_array($this->_onDuplicate, array(
237 CRM_Import_Parser::DUPLICATE_UPDATE,
238 CRM_Import_Parser::DUPLICATE_FILL,
239 )))) {
240 $this->_updateWithId = TRUE;
241 }
242
243 $this->_parseStreetAddress = CRM_Utils_Array::value('street_address_parsing', CRM_Core_BAO_Setting::valueOptions(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'address_options'), FALSE);
244 }
245
246 /**
247 * handle the values in mapField mode
248 *
249 * @param array $values the array of values belonging to this line
250 *
251 * @return boolean
252 * @access public
253 */
254 function mapField(&$values) {
255 return CRM_Import_Parser::VALID;
256 }
257
258 /**
259 * handle the values in preview mode
260 *
261 * @param array $values the array of values belonging to this line
262 *
263 * @return boolean the result of this processing
264 * @access public
265 */
266 function preview(&$values) {
267 return $this->summary($values);
268 }
269
270 /**
271 * handle the values in summary mode
272 *
273 * @param array $values the array of values belonging to this line
274 *
275 * @return boolean the result of this processing
276 * @access public
277 */
278 function summary(&$values) {
279 $erroneousField = NULL;
280 $response = $this->setActiveFieldValues($values, $erroneousField);
281
282 $errorMessage = NULL;
283 $errorRequired = FALSE;
284 switch ($this->_contactType) {
285 case 'Individual':
286 $missingNames = array();
287 if ($this->_firstNameIndex < 0 || empty($values[$this->_firstNameIndex])) {
288 $errorRequired = TRUE;
289 $missingNames[] = ts('First Name');
290 }
291 if ($this->_lastNameIndex < 0 || empty($values[$this->_lastNameIndex])) {
292 $errorRequired = TRUE;
293 $missingNames[] = ts('Last Name');
294 }
295 if ($errorRequired) {
296 $and = ' ' . ts('and') . ' ';
297 $errorMessage = ts('Missing required fields:') . ' ' . implode($and, $missingNames);
298 }
299 break;
300
301 case 'Household':
302 if ($this->_householdNameIndex < 0 || empty($values[$this->_householdNameIndex])) {
303 $errorRequired = TRUE;
304 $errorMessage = ts('Missing required fields:') . ' ' . ts('Household Name');
305 }
306 break;
307
308 case 'Organization':
309 if ($this->_organizationNameIndex < 0 || empty($values[$this->_organizationNameIndex])) {
310 $errorRequired = TRUE;
311 $errorMessage = ts('Missing required fields:') . ' ' . ts('Organization Name');
312 }
313 break;
314 }
315
316 $statusFieldName = $this->_statusFieldName;
317
318 if ($this->_emailIndex >= 0) {
319 /* If we don't have the required fields, bail */
320
321 if ($this->_contactType == 'Individual' && !$this->_updateWithId) {
322 if ($errorRequired && empty($values[$this->_emailIndex])) {
323 if ($errorMessage) {
324 $errorMessage .= ' ' . ts('OR') . ' ' . ts('Email Address');
325 }
326 else {
327 $errorMessage = ts('Missing required field:') . ' ' . ts('Email Address');
328 }
329 array_unshift($values, $errorMessage);
330 $importRecordParams = array(
331 $statusFieldName => 'ERROR',
332 "${statusFieldName}Msg" => $errorMessage,
333 );
334 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
335
336 return CRM_Import_Parser::ERROR;
337 }
338 }
339
340 $email = CRM_Utils_Array::value($this->_emailIndex, $values);
341 if ($email) {
342 /* If the email address isn't valid, bail */
343
344 if (!CRM_Utils_Rule::email($email)) {
345 $errorMessage = ts('Invalid Email address');
346 array_unshift($values, $errorMessage);
347 $importRecordParams = array(
348 $statusFieldName => 'ERROR',
349 "${statusFieldName}Msg" => $errorMessage,
350 );
351 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
352
353 return CRM_Import_Parser::ERROR;
354 }
355
356 /* otherwise, count it and move on */
357 $this->_allEmails[$email] = $this->_lineCount;
358 }
359 }
360 elseif ($errorRequired && !$this->_updateWithId) {
361 if ($errorMessage) {
362 $errorMessage .= ' ' . ts('OR') . ' ' . ts('Email Address');
363 }
364 else {
365 $errorMessage = ts('Missing required field:') . ' ' . ts('Email Address');
366 }
367 array_unshift($values, $errorMessage);
368 $importRecordParams = array(
369 $statusFieldName => 'ERROR',
370 "${statusFieldName}Msg" => $errorMessage,
371 );
372 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
373
374 return CRM_Import_Parser::ERROR;
375 }
376
377 //check for duplicate external Identifier
378 $externalID = CRM_Utils_Array::value($this->_externalIdentifierIndex, $values);
379 if ($externalID) {
380 /* If it's a dupe,external Identifier */
381
382 if ($externalDupe = CRM_Utils_Array::value($externalID, $this->_allExternalIdentifiers)) {
383 $errorMessage = ts('External ID conflicts with record %1', array(1 => $externalDupe));
384 array_unshift($values, $errorMessage);
385 $importRecordParams = array(
386 $statusFieldName => 'ERROR',
387 "${statusFieldName}Msg" => $errorMessage,
388 );
389 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
390 return CRM_Import_Parser::ERROR;
391 }
392 //otherwise, count it and move on
393 $this->_allExternalIdentifiers[$externalID] = $this->_lineCount;
394 }
395
396 //Checking error in custom data
397 $params = &$this->getActiveFieldParams();
398 $params['contact_type'] = $this->_contactType;
399 //date-format part ends
400
401 $errorMessage = NULL;
402
403 //CRM-5125
404 //add custom fields for contact sub type
405 $csType = NULL;
406 if (!empty($this->_contactSubType)) {
407 $csType = $this->_contactSubType;
408 }
409
410 //checking error in custom data
411 $this->isErrorInCustomData($params, $errorMessage, $csType, $this->_relationships);
412
413 //checking error in core data
414 $this->isErrorInCoreData($params, $errorMessage);
415 if ($errorMessage) {
416 $tempMsg = "Invalid value for field(s) : $errorMessage";
417 // put the error message in the import record in the DB
418 $importRecordParams = array(
419 $statusFieldName => 'ERROR',
420 "${statusFieldName}Msg" => $tempMsg,
421 );
422 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
423 array_unshift($values, $tempMsg);
424 $errorMessage = NULL;
425 return CRM_Import_Parser::ERROR;
426 }
427
428 //if user correcting errors by walking back
429 //need to reset status ERROR msg to null
430 //now currently we are having valid data.
431 $importRecordParams = array(
432 $statusFieldName => 'NEW',
433 );
434 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
435
436 return CRM_Import_Parser::VALID;
437 }
438
439 /**
440 * handle the values in import mode
441 *
442 * @param int $onDuplicate the code for what action to take on duplicates
443 * @param array $values the array of values belonging to this line
444 *
445 * @param bool $doGeocodeAddress
446 *
447 * @return boolean the result of this processing
448 * @access public
449 */
450 function import($onDuplicate, &$values, $doGeocodeAddress = FALSE) {
451 $config = CRM_Core_Config::singleton();
452 $this->_unparsedStreetAddressContacts = array();
453 if (!$doGeocodeAddress) {
454 // CRM-5854, reset the geocode method to null to prevent geocoding
455 $config->geocodeMethod = NULL;
456 }
457
458 // first make sure this is a valid line
459 //$this->_updateWithId = false;
460 $response = $this->summary($values);
461 $statusFieldName = $this->_statusFieldName;
462
463 if ($response != CRM_Import_Parser::VALID) {
464 $importRecordParams = array(
465 $statusFieldName => 'INVALID',
466 "${statusFieldName}Msg" => "Invalid (Error Code: $response)",
467 );
468 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
469 return $response;
470 }
471
472 $params = &$this->getActiveFieldParams();
473 $formatted = array(
474 'contact_type' => $this->_contactType,
475 );
476
477 static $contactFields = NULL;
478 if ($contactFields == NULL) {
479 $contactFields = CRM_Contact_DAO_Contact::import();
480 }
481
482 //check if external identifier exists in database
483 if (!empty($params['external_identifier']) && (!empty($params['id']) || in_array($onDuplicate, array(
484 CRM_Import_Parser::DUPLICATE_SKIP,
485 CRM_Import_Parser::DUPLICATE_NOCHECK,
486 )))) {
487
488 if ($internalCid = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $params['external_identifier'], 'id', 'external_identifier')) {
489 if ($internalCid != CRM_Utils_Array::value('id', $params)) {
490 $errorMessage = ts('External ID already exists in Database.');
491 array_unshift($values, $errorMessage);
492 $importRecordParams = array(
493 $statusFieldName => 'ERROR',
494 "${statusFieldName}Msg" => $errorMessage,
495 );
496 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
497 return CRM_Import_Parser::DUPLICATE;
498 }
499 }
500 }
501
502 if (!empty($this->_contactSubType)) {
503 $params['contact_sub_type'] = $this->_contactSubType;
504 }
505
506 if ($subType = CRM_Utils_Array::value('contact_sub_type', $params)) {
507 if (CRM_Contact_BAO_ContactType::isExtendsContactType($subType, $this->_contactType, FALSE, 'label')) {
508 $subTypes = CRM_Contact_BAO_ContactType::subTypePairs($this->_contactType, FALSE, NULL);
509 $params['contact_sub_type'] = array_search($subType, $subTypes);
510 }
511 elseif (!CRM_Contact_BAO_ContactType::isExtendsContactType($subType, $this->_contactType)) {
512 $message = "Mismatched or Invalid Contact SubType.";
513 array_unshift($values, $message);
514 return CRM_Import_Parser::NO_MATCH;
515 }
516 }
517
518 //get contact id to format common data in update/fill mode,
519 //if external identifier is present, CRM-4423
520 if ($this->_updateWithId && empty($params['id']) && !empty($params['external_identifier'])) {
521 if ($cid = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $params['external_identifier'], 'id', 'external_identifier')) {
522 $formatted['id'] = $cid;
523 }
524 }
525
526 //format common data, CRM-4062
527 $this->formatCommonData($params, $formatted, $contactFields);
528
529 $relationship = FALSE;
530 $createNewContact = TRUE;
531 // Support Match and Update Via Contact ID
532 if ($this->_updateWithId) {
533 $createNewContact = FALSE;
534 if (empty($params['id']) && !empty($params['external_identifier'])) {
535 if ($cid) {
536 $params['id'] = $cid;
537 }
538 }
539
540 $error = _civicrm_api3_deprecated_duplicate_formatted_contact($formatted);
541 if (CRM_Core_Error::isAPIError($error, CRM_Core_ERROR::DUPLICATE_CONTACT)) {
542 $matchedIDs = explode(',', $error['error_message']['params'][0]);
543 if (count($matchedIDs) >= 1) {
544 $updateflag = TRUE;
545 foreach ($matchedIDs as $contactId) {
546 if ($params['id'] == $contactId) {
547 $contactType = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $params['id'], 'contact_type');
548
549 if ($formatted['contact_type'] == $contactType) {
550 //validation of subtype for update mode
551 //CRM-5125
552 $contactSubType = NULL;
553 if (!empty($params['contact_sub_type'])) {
554 $contactSubType = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $params['id'], 'contact_sub_type');
555 }
556
557 if (!empty($contactSubType) && (!CRM_Contact_BAO_ContactType::isAllowEdit($params['id'], $contactSubType) && $contactSubType != CRM_Utils_Array::value('contact_sub_type', $formatted))) {
558
559 $message = "Mismatched contact SubTypes :";
560 array_unshift($values, $message);
561 $updateflag = FALSE;
562 $this->_retCode = CRM_Import_Parser::NO_MATCH;
563 }
564 else {
565 $newContact = $this->createContact($formatted, $contactFields, $onDuplicate, $contactId, FALSE, $this->_dedupeRuleGroupID);
566 $updateflag = FALSE;
567 $this->_retCode = CRM_Import_Parser::VALID;
568 }
569 }
570 else {
571 $message = "Mismatched contact Types :";
572 array_unshift($values, $message);
573 $updateflag = FALSE;
574 $this->_retCode = CRM_Import_Parser::NO_MATCH;
575 }
576 }
577 }
578 if ($updateflag) {
579 $message = "Mismatched contact IDs OR Mismatched contact Types :";
580 array_unshift($values, $message);
581 $this->_retCode = CRM_Import_Parser::NO_MATCH;
582 }
583 }
584 }
585 else {
586 $contactType = NULL;
587 if (!empty($params['id'])) {
588 $contactType = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $params['id'], 'contact_type');
589 if ($contactType) {
590 if ($formatted['contact_type'] == $contactType) {
591 //validation of subtype for update mode
592 //CRM-5125
593 $contactSubType = NULL;
594 if (!empty($params['contact_sub_type'])) {
595 $contactSubType = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $params['id'], 'contact_sub_type');
596 }
597
598 if (!empty($contactSubType) && (!CRM_Contact_BAO_ContactType::isAllowEdit($params['id'], $contactSubType) && $contactSubType != CRM_Utils_Array::value('contact_sub_type', $formatted))) {
599
600 $message = "Mismatched contact SubTypes :";
601 array_unshift($values, $message);
602 $this->_retCode = CRM_Import_Parser::NO_MATCH;
603 }
604 else {
605 $newContact = $this->createContact($formatted, $contactFields, $onDuplicate, $params['id'], FALSE, $this->_dedupeRuleGroupID);
606 $this->_retCode = CRM_Import_Parser::VALID;
607 }
608 }
609 else {
610 $message = "Mismatched contact Types :";
611 array_unshift($values, $message);
612 $this->_retCode = CRM_Import_Parser::NO_MATCH;
613 }
614 }
615 else {
616 // we should avoid multiple errors for single record
617 // since we have already retCode and we trying to force again.
618 if ($this->_retCode != CRM_Import_Parser::NO_MATCH) {
619 $message = "No contact found for this contact ID:" . $params['id'];
620 array_unshift($values, $message);
621 $this->_retCode = CRM_Import_Parser::NO_MATCH;
622 }
623 }
624 }
625 else {
626 //CRM-4148
627 //now we want to create new contact on update/fill also.
628 $createNewContact = TRUE;
629 }
630 }
631
632 if (isset($newContact) && is_a($newContact, 'CRM_Contact_BAO_Contact')) {
633 $relationship = TRUE;
634 }
635 elseif (is_a($error, 'CRM_Core_Error')) {
636 $newContact = $error;
637 $relationship = TRUE;
638 }
639 }
640
641 //fixed CRM-4148
642 //now we create new contact in update/fill mode also.
643 $contactID = NULL;
644 if ($createNewContact || ($this->_retCode != CRM_Import_Parser::NO_MATCH && $this->_updateWithId)) {
645
646 //CRM-4430, don't carry if not submitted.
647 foreach (array('prefix_id', 'suffix_id', 'gender_id') as $name) {
648 if (!empty($formatted[$name])) {
649 $options = CRM_Contact_BAO_Contact::buildOptions($name, 'get');
650 if (!isset($options[$formatted[$name]])) {
651 $formatted[$name] = CRM_Utils_Array::key((string) $formatted[$name], $options);
652 }
653 }
654 }
655 if ($this->_updateWithId && !empty($params['id'])) {
656 $contactID = $params['id'];
657 }
658 $newContact = $this->createContact($formatted, $contactFields, $onDuplicate, $contactID, TRUE, $this->_dedupeRuleGroupID);
659 }
660
661 if (isset($newContact) && is_object($newContact) && ($newContact instanceof CRM_Contact_BAO_Contact)) {
662 $relationship = TRUE;
663 $newContact = clone($newContact);
664 $contactID = $newContact->id;
665 $this->_newContacts[] = $contactID;
666
667 //get return code if we create new contact in update mode, CRM-4148
668 if ($this->_updateWithId) {
669 $this->_retCode = CRM_Import_Parser::VALID;
670 }
671 }
672 elseif (isset($newContact) && CRM_Core_Error::isAPIError($newContact, CRM_Core_ERROR::DUPLICATE_CONTACT)) {
673 // if duplicate, no need of further processing
674 if ($onDuplicate == CRM_Import_Parser::DUPLICATE_SKIP) {
675 $errorMessage = "Skipping duplicate record";
676 array_unshift($values, $errorMessage);
677 $importRecordParams = array(
678 $statusFieldName => 'DUPLICATE',
679 "${statusFieldName}Msg" => $errorMessage,
680 );
681 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
682 return CRM_Import_Parser::DUPLICATE;
683 }
684
685 $relationship = TRUE;
686 # see CRM-10433 - might return comma separate list of all dupes
687 $dupeContactIDs = explode(',',$newContact['error_message']['params'][0]);
688 $dupeCount = count($dupeContactIDs);
689 $contactID = array_pop($dupeContactIDs);
690 // check to see if we had more than one duplicate contact id.
691 // if we have more than one, the record will be rejected below
692 if ($dupeCount == 1) {
693 // there was only one dupe, we will continue normally...
694 if (!in_array($contactID, $this->_newContacts)) {
695 $this->_newContacts[] = $contactID;
696 }
697 }
698 }
699
700 if ($contactID) {
701 // call import hook
702 $currentImportID = end($values);
703
704 $hookParams = array(
705 'contactID' => $contactID,
706 'importID' => $currentImportID,
707 'importTempTable' => $this->_tableName,
708 'fieldHeaders' => $this->_mapperKeys,
709 'fields' => $this->_activeFields,
710 );
711
712 CRM_Utils_Hook::import('Contact', 'process', $this, $hookParams);
713 }
714
715 if ($relationship) {
716 $primaryContactId = NULL;
717 if (CRM_Core_Error::isAPIError($newContact, CRM_Core_ERROR::DUPLICATE_CONTACT)) {
718 if (CRM_Utils_Rule::integer($newContact['error_message']['params'][0])) {
719 $primaryContactId = $newContact['error_message']['params'][0];
720 }
721 }
722 else {
723 $primaryContactId = $newContact->id;
724 }
725
726 if ((CRM_Core_Error::isAPIError($newContact, CRM_Core_ERROR::DUPLICATE_CONTACT) || is_a($newContact, 'CRM_Contact_BAO_Contact')) && $primaryContactId) {
727
728 //relationship contact insert
729 foreach ($params as $key => $field) {
730 list($id, $first, $second) = CRM_Utils_System::explode('_', $key, 3);
731 if (!($first == 'a' && $second == 'b') && !($first == 'b' && $second == 'a')) {
732 continue;
733 }
734
735 $relationType = new CRM_Contact_DAO_RelationshipType();
736 $relationType->id = $id;
737 $relationType->find(TRUE);
738 $direction = "contact_sub_type_$second";
739
740 $formatting = array(
741 'contact_type' => $params[$key]['contact_type'],
742 );
743
744 //set subtype for related contact CRM-5125
745 if (isset($relationType->$direction)) {
746 //validation of related contact subtype for update mode
747 if ($relCsType = CRM_Utils_Array::value('contact_sub_type', $params[$key]) && $relCsType != $relationType->$direction) {
748 $errorMessage = ts("Mismatched or Invalid contact subtype found for this related contact");
749 array_unshift($values, $errorMessage);
750 return CRM_Import_Parser::NO_MATCH;
751 }
752 else {
753 $formatting['contact_sub_type'] = $relationType->$direction;
754 }
755 }
756 $relationType->free();
757
758 $contactFields = NULL;
759 $contactFields = CRM_Contact_DAO_Contact::import();
760
761 //Relation on the basis of External Identifier.
762 if (empty($params[$key]['id']) && !empty($params[$key]['external_identifier'])) {
763 $params[$key]['id'] = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $params[$key]['external_identifier'], 'id', 'external_identifier');
764 }
765 // check for valid related contact id in update/fill mode, CRM-4424
766 if (in_array($onDuplicate, array(
767 CRM_Import_Parser::DUPLICATE_UPDATE,
768 CRM_Import_Parser::DUPLICATE_FILL,
769 )) && !empty($params[$key]['id'])) {
770 $relatedContactType = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $params[$key]['id'], 'contact_type');
771 if (!$relatedContactType) {
772 $errorMessage = ts("No contact found for this related contact ID: %1", array(1 => $params[$key]['id']));
773 array_unshift($values, $errorMessage);
774 return CRM_Import_Parser::NO_MATCH;
775 }
776 else {
777 //validation of related contact subtype for update mode
778 //CRM-5125
779 $relatedCsType = NULL;
780 if (!empty($formatting['contact_sub_type'])) {
781 $relatedCsType = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $params[$key]['id'], 'contact_sub_type');
782 }
783
784 if (!empty($relatedCsType) && (!CRM_Contact_BAO_ContactType::isAllowEdit($params[$key]['id'], $relatedCsType) &&
785 $relatedCsType != CRM_Utils_Array::value('contact_sub_type', $formatting))) {
786 $errorMessage = ts("Mismatched or Invalid contact subtype found for this related contact ID: %1", array(1 => $params[$key]['id']));
787 array_unshift($values, $errorMessage);
788 return CRM_Import_Parser::NO_MATCH;
789 }
790 else {
791 // get related contact id to format data in update/fill mode,
792 //if external identifier is present, CRM-4423
793 $formatting['id'] = $params[$key]['id'];
794 }
795 }
796 }
797
798 //format common data, CRM-4062
799 $this->formatCommonData($field, $formatting, $contactFields);
800
801 //do we have enough fields to create related contact.
802 $allowToCreate = $this->checkRelatedContactFields($key, $formatting);
803
804 if (!$allowToCreate) {
805 $errorMessage = ts('Related contact required fields are missing.');
806 array_unshift($values, $errorMessage);
807 return CRM_Import_Parser::NO_MATCH;
808 }
809
810 //fixed for CRM-4148
811 if (!empty($params[$key]['id'])) {
812 $contact = array(
813 'contact_id' => $params[$key]['id'],
814 );
815 $defaults = array();
816 $relatedNewContact = CRM_Contact_BAO_Contact::retrieve($contact, $defaults);
817 }
818 else {
819 $relatedNewContact = $this->createContact($formatting, $contactFields, $onDuplicate, NULL, FALSE);
820 }
821
822 if (is_object($relatedNewContact) || ($relatedNewContact instanceof CRM_Contact_BAO_Contact)) {
823 $relatedNewContact = clone($relatedNewContact);
824 }
825
826 $matchedIDs = array();
827 // To update/fill contact, get the matching contact Ids if duplicate contact found
828 // otherwise get contact Id from object of related contact
829 if (is_array($relatedNewContact) && civicrm_error($relatedNewContact)) {
830 if (CRM_Core_Error::isAPIError($relatedNewContact, CRM_Core_ERROR::DUPLICATE_CONTACT)) {
831 $matchedIDs = explode(',', $relatedNewContact['error_message']['params'][0]);
832 }
833 else {
834 $errorMessage = $relatedNewContact['error_message'];
835 array_unshift($values, $errorMessage);
836 $importRecordParams = array(
837 $statusFieldName => 'ERROR',
838 "${statusFieldName}Msg" => $errorMessage,
839 );
840 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
841 return CRM_Import_Parser::ERROR;
842 }
843 }
844 else {
845 $matchedIDs[] = $relatedNewContact->id;
846 }
847 // update/fill related contact after getting matching Contact Ids, CRM-4424
848 if (in_array($onDuplicate, array(
849 CRM_Import_Parser::DUPLICATE_UPDATE,
850 CRM_Import_Parser::DUPLICATE_FILL,
851 ))) {
852 //validation of related contact subtype for update mode
853 //CRM-5125
854 $relatedCsType = NULL;
855 if (!empty($formatting['contact_sub_type'])) {
856 $relatedCsType = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $matchedIDs[0], 'contact_sub_type');
857 }
858
859 if (!empty($relatedCsType) && (!CRM_Contact_BAO_ContactType::isAllowEdit($matchedIDs[0], $relatedCsType) && $relatedCsType != CRM_Utils_Array::value('contact_sub_type', $formatting))) {
860 $errorMessage = ts("Mismatched or Invalid contact subtype found for this related contact.");
861 array_unshift($values, $errorMessage);
862 return CRM_Import_Parser::NO_MATCH;
863 }
864 else {
865 $updatedContact = $this->createContact($formatting, $contactFields, $onDuplicate, $matchedIDs[0]);
866 }
867 }
868 static $relativeContact = array();
869 if (CRM_Core_Error::isAPIError($relatedNewContact, CRM_Core_ERROR::DUPLICATE_CONTACT)) {
870 if (count($matchedIDs) >= 1) {
871 $relContactId = $matchedIDs[0];
872 //add relative contact to count during update & fill mode.
873 //logic to make count distinct by contact id.
874 if ($this->_newRelatedContacts || !empty($relativeContact)) {
875 $reContact = array_keys($relativeContact, $relContactId);
876
877 if (empty($reContact)) {
878 $this->_newRelatedContacts[] = $relativeContact[] = $relContactId;
879 }
880 }
881 else {
882 $this->_newRelatedContacts[] = $relativeContact[] = $relContactId;
883 }
884 }
885 }
886 else {
887 $relContactId = $relatedNewContact->id;
888 $this->_newRelatedContacts[] = $relativeContact[] = $relContactId;
889 }
890
891 if (CRM_Core_Error::isAPIError($relatedNewContact, CRM_Core_ERROR::DUPLICATE_CONTACT) || ($relatedNewContact instanceof CRM_Contact_BAO_Contact)) {
892 //fix for CRM-1993.Checks for duplicate related contacts
893 if (count($matchedIDs) >= 1) {
894 //if more than one duplicate contact
895 //found, create relationship with first contact
896 // now create the relationship record
897 $relationParams = array();
898 $relationParams = array(
899 'relationship_type_id' => $key,
900 'contact_check' => array(
901 $relContactId => 1,
902 ),
903 'is_active' => 1,
904 'skipRecentView' => TRUE,
905 );
906
907 // we only handle related contact success, we ignore failures for now
908 // at some point wold be nice to have related counts as separate
909 $relationIds = array(
910 'contact' => $primaryContactId,
911 );
912
913 list($valid, $invalid, $duplicate, $saved, $relationshipIds) = CRM_Contact_BAO_Relationship::create($relationParams, $relationIds);
914
915 if ($valid || $duplicate) {
916 $relationIds['contactTarget'] = $relContactId;
917 $action = ($duplicate) ? CRM_Core_Action::UPDATE : CRM_Core_Action::ADD;
918 CRM_Contact_BAO_Relationship::relatedMemberships($primaryContactId, $relationParams, $relationIds, $action);
919 }
920
921 //handle current employer, CRM-3532
922 if ($valid) {
923 $allRelationships = CRM_Core_PseudoConstant::relationshipType('name');
924 $relationshipTypeId = str_replace(array(
925 '_a_b',
926 '_b_a',
927 ), array(
928 '',
929 '',
930 ), $key);
931 $relationshipType = str_replace($relationshipTypeId . '_', '', $key);
932 $orgId = $individualId = NULL;
933 if ($allRelationships[$relationshipTypeId]["name_{$relationshipType}"] == 'Employee of') {
934 $orgId = $relContactId;
935 $individualId = $primaryContactId;
936 }
937 elseif ($allRelationships[$relationshipTypeId]["name_{$relationshipType}"] == 'Employer of') {
938 $orgId = $primaryContactId;
939 $individualId = $relContactId;
940 }
941 if ($orgId && $individualId) {
942 $currentEmpParams[$individualId] = $orgId;
943 CRM_Contact_BAO_Contact_Utils::setCurrentEmployer($currentEmpParams);
944 }
945 }
946 }
947 }
948 }
949 }
950 }
951 if ($this->_updateWithId) {
952 //return warning if street address is unparsed, CRM-5886
953 return $this->processMessage($values, $statusFieldName, $this->_retCode);
954 }
955 //dupe checking
956 if (is_array($newContact) && civicrm_error($newContact)) {
957 $code = NULL;
958
959 if (($code = CRM_Utils_Array::value('code', $newContact['error_message'])) && ($code == CRM_Core_Error::DUPLICATE_CONTACT)) {
960 $urls = array();
961 // need to fix at some stage and decide if the error will return an
962 // array or string, crude hack for now
963 if (is_array($newContact['error_message']['params'][0])) {
964 $cids = $newContact['error_message']['params'][0];
965 }
966 else {
967 $cids = explode(',', $newContact['error_message']['params'][0]);
968 }
969
970 foreach ($cids as $cid) {
971 $urls[] = CRM_Utils_System::url('civicrm/contact/view', 'reset=1&cid=' . $cid, TRUE);
972 }
973
974 $url_string = implode("\n", $urls);
975
976 // If we duplicate more than one record, skip no matter what
977 if (count($cids) > 1) {
978 $errorMessage = ts('Record duplicates multiple contacts');
979 $importRecordParams = array(
980 $statusFieldName => 'ERROR',
981 "${statusFieldName}Msg" => $errorMessage,
982 );
983
984 //combine error msg to avoid mismatch between error file columns.
985 $errorMessage .= "\n" . $url_string;
986 array_unshift($values, $errorMessage);
987 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
988 return CRM_Import_Parser::ERROR;
989 }
990
991 // Params only had one id, so shift it out
992 $contactId = array_shift($cids);
993 $cid = NULL;
994
995 $vals = array('contact_id' => $contactId);
996
997 if ($onDuplicate == CRM_Import_Parser::DUPLICATE_REPLACE) {
998 civicrm_api('contact', 'delete', $vals);
999 $cid = CRM_Contact_BAO_Contact::createProfileContact($formatted, $contactFields, $contactId, NULL, NULL, $formatted['contact_type']);
1000 }
1001 elseif ($onDuplicate == CRM_Import_Parser::DUPLICATE_UPDATE) {
1002 $newContact = $this->createContact($formatted, $contactFields, $onDuplicate, $contactId);
1003 }
1004 elseif ($onDuplicate == CRM_Import_Parser::DUPLICATE_FILL) {
1005 $newContact = $this->createContact($formatted, $contactFields, $onDuplicate, $contactId);
1006 }
1007 // else skip does nothing and just returns an error code.
1008
1009
1010 if ($cid) {
1011 $contact = array(
1012 'contact_id' => $cid,
1013 );
1014 $defaults = array();
1015 $newContact = CRM_Contact_BAO_Contact::retrieve($contact, $defaults);
1016 }
1017
1018 if (civicrm_error($newContact)) {
1019 if (empty($newContact['error_message']['params'])) {
1020 // different kind of error other than DUPLICATE
1021 $errorMessage = $newContact['error_message'];
1022 array_unshift($values, $errorMessage);
1023 $importRecordParams = array(
1024 $statusFieldName => 'ERROR',
1025 "${statusFieldName}Msg" => $errorMessage,
1026 );
1027 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
1028 return CRM_Import_Parser::ERROR;
1029 }
1030
1031 $contactID = $newContact['error_message']['params'][0];
1032 if (!in_array($contactID, $this->_newContacts)) {
1033 $this->_newContacts[] = $contactID;
1034 }
1035 }
1036 //CRM-262 No Duplicate Checking
1037 if ($onDuplicate == CRM_Import_Parser::DUPLICATE_SKIP) {
1038 array_unshift($values, $url_string);
1039 $importRecordParams = array(
1040 $statusFieldName => 'DUPLICATE',
1041 "${statusFieldName}Msg" => "Skipping duplicate record",
1042 );
1043 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
1044 return CRM_Import_Parser::DUPLICATE;
1045 }
1046
1047 $importRecordParams = array(
1048 $statusFieldName => 'IMPORTED',
1049 );
1050 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
1051 //return warning if street address is not parsed, CRM-5886
1052 return $this->processMessage($values, $statusFieldName, CRM_Import_Parser::VALID);
1053 }
1054 else {
1055 // Not a dupe, so we had an error
1056 $errorMessage = $newContact['error_message'];
1057 array_unshift($values, $errorMessage);
1058 $importRecordParams = array(
1059 $statusFieldName => 'ERROR',
1060 "${statusFieldName}Msg" => $errorMessage,
1061 );
1062 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
1063 return CRM_Import_Parser::ERROR;
1064 }
1065 }
1066 // sleep(3);
1067 return $this->processMessage($values, $statusFieldName, CRM_Import_Parser::VALID);
1068 }
1069
1070 /**
1071 * Get the array of successfully imported contact id's
1072 *
1073 * @return array
1074 * @access public
1075 */
1076 function &getImportedContacts() {
1077 return $this->_newContacts;
1078 }
1079
1080 /**
1081 * Get the array of successfully imported related contact id's
1082 *
1083 * @return array
1084 * @access public
1085 */
1086 function &getRelatedImportedContacts() {
1087 return $this->_newRelatedContacts;
1088 }
1089
1090 /**
1091 * the initializer code, called before the processing
1092 *
1093 * @return void
1094 * @access public
1095 */
1096 function fini() {}
1097
1098 /**
1099 * function to check if an error in custom data
1100 *
1101 * @param $params
1102 * @param String $errorMessage A string containing all the error-fields.
1103 *
1104 * @param null $csType
1105 * @param null $relationships
1106 *
1107 * @access public
1108 */
1109 static function isErrorInCustomData($params, &$errorMessage, $csType = NULL, $relationships = NULL) {
1110 $session = CRM_Core_Session::singleton();
1111 $dateType = $session->get("dateTypes");
1112
1113 if (!empty($params['contact_sub_type'])) {
1114 $csType = CRM_Utils_Array::value('contact_sub_type', $params);
1115 }
1116
1117 if (empty($params['contact_type'])) {
1118 $params['contact_type'] = 'Individual';
1119 }
1120 $customFields = CRM_Core_BAO_CustomField::getFields($params['contact_type'], FALSE, FALSE, $csType);
1121
1122 $addressCustomFields = CRM_Core_BAO_CustomField::getFields('Address');
1123 $customFields = $customFields + $addressCustomFields;
1124 foreach ($params as $key => $value) {
1125 if ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) {
1126 /* check if it's a valid custom field id */
1127
1128 if (!array_key_exists($customFieldID, $customFields)) {
1129 self::addToErrorMsg(ts('field ID'), $errorMessage);
1130 }
1131 // validate null values for required custom fields of type boolean
1132 if (!empty($customFields[$customFieldID]['is_required']) && (empty($params['custom_'.$customFieldID]) && !is_numeric($params['custom_'.$customFieldID])) && $customFields[$customFieldID]['data_type'] == 'Boolean') {
1133 self::addToErrorMsg($customFields[$customFieldID]['label'].'::'.$customFields[$customFieldID]['groupTitle'], $errorMessage);
1134 }
1135
1136 //For address custom fields, we do get actual custom field value as an inner array of
1137 //values so need to modify
1138 if (array_key_exists($customFieldID, $addressCustomFields)) {
1139 $value = $value[0][$key];
1140 }
1141 /* validate the data against the CF type */
1142
1143 if ($value) {
1144 if ($customFields[$customFieldID]['data_type'] == 'Date') {
1145 if (array_key_exists($customFieldID, $addressCustomFields) && CRM_Utils_Date::convertToDefaultDate($params[$key][0], $dateType, $key)) {
1146 $value = $params[$key][0][$key];
1147 }
1148 else if (CRM_Utils_Date::convertToDefaultDate($params, $dateType, $key)) {
1149 $value = $params[$key];
1150 }
1151 else {
1152 self::addToErrorMsg($customFields[$customFieldID]['label'], $errorMessage);
1153 }
1154 }
1155 elseif ($customFields[$customFieldID]['data_type'] == 'Boolean') {
1156 if (CRM_Utils_String::strtoboolstr($value) === FALSE) {
1157 self::addToErrorMsg($customFields[$customFieldID]['label'].'::'.$customFields[$customFieldID]['groupTitle'], $errorMessage);
1158 }
1159 }
1160 // need not check for label filed import
1161 $htmlType = array(
1162 'CheckBox',
1163 'Multi-Select',
1164 'AdvMulti-Select',
1165 'Select',
1166 'Radio',
1167 'Multi-Select State/Province',
1168 'Multi-Select Country',
1169 );
1170 if (!in_array($customFields[$customFieldID]['html_type'], $htmlType) || $customFields[$customFieldID]['data_type'] == 'Boolean' || $customFields[$customFieldID]['data_type'] == 'ContactReference') {
1171 $valid = CRM_Core_BAO_CustomValue::typecheck($customFields[$customFieldID]['data_type'], $value);
1172 if (!$valid) {
1173 self::addToErrorMsg($customFields[$customFieldID]['label'], $errorMessage);
1174 }
1175 }
1176
1177 // check for values for custom fields for checkboxes and multiselect
1178 if ($customFields[$customFieldID]['html_type'] == 'CheckBox' || $customFields[$customFieldID]['html_type'] == 'AdvMulti-Select' || $customFields[$customFieldID]['html_type'] == 'Multi-Select') {
1179 $value = trim($value);
1180 $value = str_replace('|', ',', $value);
1181 $mulValues = explode(',', $value);
1182 $customOption = CRM_Core_BAO_CustomOption::getCustomOption($customFieldID, TRUE);
1183 foreach ($mulValues as $v1) {
1184 if (strlen($v1) == 0) {
1185 continue;
1186 }
1187
1188 $flag = FALSE;
1189 foreach ($customOption as $v2) {
1190 if ((strtolower(trim($v2['label'])) == strtolower(trim($v1))) || (strtolower(trim($v2['value'])) == strtolower(trim($v1)))) {
1191 $flag = TRUE;
1192 }
1193 }
1194
1195 if (!$flag) {
1196 self::addToErrorMsg($customFields[$customFieldID]['label'], $errorMessage);
1197 }
1198 }
1199 }
1200 elseif ($customFields[$customFieldID]['html_type'] == 'Select' || ($customFields[$customFieldID]['html_type'] == 'Radio' && $customFields[$customFieldID]['data_type'] != 'Boolean')) {
1201 $customOption = CRM_Core_BAO_CustomOption::getCustomOption($customFieldID, TRUE);
1202 $flag = FALSE;
1203 foreach ($customOption as $v2) {
1204 if ((strtolower(trim($v2['label'])) == strtolower(trim($value))) || (strtolower(trim($v2['value'])) == strtolower(trim($value)))) {
1205 $flag = TRUE;
1206 }
1207 }
1208 if (!$flag) {
1209 self::addToErrorMsg($customFields[$customFieldID]['label'], $errorMessage);
1210 }
1211 }
1212 elseif ($customFields[$customFieldID]['html_type'] == 'Multi-Select State/Province') {
1213 $mulValues = explode(',', $value);
1214 foreach ($mulValues as $stateValue) {
1215 if ($stateValue) {
1216 if (self::in_value(trim($stateValue), CRM_Core_PseudoConstant::stateProvinceAbbreviation()) || self::in_value(trim($stateValue), CRM_Core_PseudoConstant::stateProvince())) {
1217 continue;
1218 }
1219 else {
1220 self::addToErrorMsg($customFields[$customFieldID]['label'], $errorMessage);
1221 }
1222 }
1223 }
1224 }
1225 elseif ($customFields[$customFieldID]['html_type'] == 'Multi-Select Country') {
1226 $mulValues = explode(',', $value);
1227 foreach ($mulValues as $countryValue) {
1228 if ($countryValue) {
1229 CRM_Core_PseudoConstant::populate($countryNames, 'CRM_Core_DAO_Country', TRUE, 'name', 'is_active');
1230 CRM_Core_PseudoConstant::populate($countryIsoCodes, 'CRM_Core_DAO_Country', TRUE, 'iso_code');
1231 $config = CRM_Core_Config::singleton();
1232 $limitCodes = $config->countryLimit();
1233
1234 $error = TRUE;
1235 foreach (array(
1236 $countryNames,
1237 $countryIsoCodes,
1238 $limitCodes,
1239 ) as $values) {
1240 if (in_array(trim($countryValue), $values)) {
1241 $error = FALSE;
1242 break;
1243 }
1244 }
1245
1246 if ($error) {
1247 self::addToErrorMsg($customFields[$customFieldID]['label'], $errorMessage);
1248 }
1249 }
1250 }
1251 }
1252 }
1253 }
1254 elseif (is_array($params[$key]) && isset($params[$key]["contact_type"])) {
1255 //CRM-5125
1256 //supporting custom data of related contact subtypes
1257 $relation = NULL;
1258 if ($relationships) {
1259 if (array_key_exists($key, $relationships)) {
1260 $relation = $key;
1261 }
1262 elseif (CRM_Utils_Array::key($key, $relationships)) {
1263 $relation = CRM_Utils_Array::key($key, $relationships);
1264 }
1265 }
1266 if (!empty($relation)) {
1267 list($id, $first, $second) = CRM_Utils_System::explode('_', $relation, 3);
1268 $direction = "contact_sub_type_$second";
1269 $relationshipType = new CRM_Contact_BAO_RelationshipType();
1270 $relationshipType->id = $id;
1271 if ($relationshipType->find(TRUE)) {
1272 if (isset($relationshipType->$direction)) {
1273 $params[$key]['contact_sub_type'] = $relationshipType->$direction;
1274 }
1275 }
1276 $relationshipType->free();
1277 }
1278
1279 self::isErrorInCustomData($params[$key], $errorMessage, $csType, $relationships);
1280 }
1281 }
1282 }
1283
1284 /**
1285 * Check if value present in all genders or
1286 * as a substring of any gender value, if yes than return corresponding gender.
1287 * eg value might be m/M, ma/MA, mal/MAL, male return 'Male'
1288 * but if value is 'maleabc' than return false
1289 *
1290 * @param string $gender check this value across gender values.
1291 *
1292 * retunr gender value / false
1293 *
1294 * @return bool
1295 * @access public
1296 */
1297 public function checkGender($gender) {
1298 $gender = trim($gender, '.');
1299 if (!$gender) {
1300 return FALSE;
1301 }
1302
1303 $allGenders = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'gender_id');
1304 foreach ($allGenders as $key => $value) {
1305 if (strlen($gender) > strlen($value)) {
1306 continue;
1307 }
1308 if ($gender == $value) {
1309 return $value;
1310 }
1311 if (substr_compare($value, $gender, 0, strlen($gender), TRUE) === 0) {
1312 return $value;
1313 }
1314 }
1315
1316 return FALSE;
1317 }
1318
1319 /**
1320 * function to check if an error in Core( non-custom fields ) field
1321 *
1322 * @param $params
1323 * @param String $errorMessage A string containing all the error-fields.
1324 *
1325 * @access public
1326 */
1327 function isErrorInCoreData($params, &$errorMessage) {
1328 foreach ($params as $key => $value) {
1329 if ($value) {
1330 $session = CRM_Core_Session::singleton();
1331 $dateType = $session->get("dateTypes");
1332
1333 switch ($key) {
1334 case 'birth_date':
1335 if (CRM_Utils_Date::convertToDefaultDate($params, $dateType, $key)) {
1336 if (!CRM_Utils_Rule::date($params[$key])) {
1337 self::addToErrorMsg(ts('Birth Date'), $errorMessage);
1338 }
1339 }
1340 else {
1341 self::addToErrorMsg(ts('Birth-Date'), $errorMessage);
1342 }
1343 break;
1344
1345 case 'deceased_date':
1346 if (CRM_Utils_Date::convertToDefaultDate($params, $dateType, $key)) {
1347 if (!CRM_Utils_Rule::date($params[$key])) {
1348 self::addToErrorMsg(ts('Deceased Date'), $errorMessage);
1349 }
1350 }
1351 else {
1352 self::addToErrorMsg(ts('Deceased Date'), $errorMessage);
1353 }
1354 break;
1355
1356 case 'is_deceased':
1357 if (CRM_Utils_String::strtoboolstr($value) === FALSE) {
1358 self::addToErrorMsg(ts('Is Deceased'), $errorMessage);
1359 }
1360 break;
1361
1362 case 'gender':
1363 case 'gender_id':
1364 if (!self::checkGender($value)) {
1365 self::addToErrorMsg(ts('Gender'), $errorMessage);
1366 }
1367 break;
1368
1369 case 'preferred_communication_method':
1370 $preffComm = array();
1371 $preffComm = explode(',', $value);
1372 foreach ($preffComm as $v) {
1373 if (!self::in_value(trim($v), CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'preferred_communication_method'))) {
1374 self::addToErrorMsg(ts('Preferred Communication Method'), $errorMessage);
1375 }
1376 }
1377 break;
1378
1379 case 'preferred_mail_format':
1380 if (!array_key_exists(strtolower($value), array_change_key_case(CRM_Core_SelectValues::pmf(), CASE_LOWER))) {
1381 self::addToErrorMsg(ts('Preferred Mail Format'), $errorMessage);
1382 }
1383 break;
1384
1385 case 'individual_prefix':
1386 case 'prefix_id':
1387 if (!self::in_value($value, CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'prefix_id'))) {
1388 self::addToErrorMsg(ts('Individual Prefix'), $errorMessage);
1389 }
1390 break;
1391
1392 case 'individual_suffix':
1393 case 'suffix_id':
1394 if (!self::in_value($value, CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'suffix_id'))) {
1395 self::addToErrorMsg(ts('Individual Suffix'), $errorMessage);
1396 }
1397 break;
1398
1399 case 'state_province':
1400 if (!empty($value)) {
1401 foreach ($value as $stateValue) {
1402 if ($stateValue['state_province']) {
1403 if (self::in_value($stateValue['state_province'], CRM_Core_PseudoConstant::stateProvinceAbbreviation()) ||
1404 self::in_value($stateValue['state_province'], CRM_Core_PseudoConstant::stateProvince())) {
1405 continue;
1406 }
1407 else {
1408 self::addToErrorMsg(ts('State/Province'), $errorMessage);
1409 }
1410 }
1411 }
1412 }
1413 break;
1414
1415 case 'country':
1416 if (!empty($value)) {
1417 foreach ($value as $stateValue) {
1418 if ($stateValue['country']) {
1419 CRM_Core_PseudoConstant::populate($countryNames, 'CRM_Core_DAO_Country', TRUE, 'name', 'is_active');
1420 CRM_Core_PseudoConstant::populate($countryIsoCodes, 'CRM_Core_DAO_Country', TRUE, 'iso_code');
1421 $config = CRM_Core_Config::singleton();
1422 $limitCodes = $config->countryLimit();
1423 //If no country is selected in
1424 //localization then take all countries
1425 if (empty($limitCodes)) {
1426 $limitCodes = $countryIsoCodes;
1427 }
1428
1429 if (self::in_value($stateValue['country'], $limitCodes) || self::in_value($stateValue['country'], CRM_Core_PseudoConstant::country())) {
1430 continue;
1431 }
1432 else {
1433 if (self::in_value($stateValue['country'], $countryIsoCodes) || self::in_value($stateValue['country'], $countryNames)) {
1434 self::addToErrorMsg(ts('Country input value is in table but not "available": "This Country is valid but is NOT in the list of Available Countries currently configured for your site. This can be viewed and modifed from Administer > Localization > Languages Currency Locations." '), $errorMessage);
1435 }
1436 else {
1437 self::addToErrorMsg(ts('Country input value not in country table: "The Country value appears to be invalid. It does not match any value in CiviCRM table of countries."'), $errorMessage);
1438 }
1439 }
1440 }
1441 }
1442 }
1443 break;
1444
1445 case 'county':
1446 if (!empty($value)) {
1447 foreach ($value as $county) {
1448 if ($county['county']) {
1449 $countyNames = CRM_Core_PseudoConstant::county();
1450 if (!empty($county['county']) && !in_array($county['county'], $countyNames)) {
1451 self::addToErrorMsg(ts('County input value not in county table: The County value appears to be invalid. It does not match any value in CiviCRM table of counties.'), $errorMessage);
1452 }
1453 }
1454 }
1455 }
1456 break;
1457
1458 case 'geo_code_1':
1459 if (!empty($value)) {
1460 foreach ($value as $codeValue) {
1461 if (!empty($codeValue['geo_code_1'])) {
1462 if (CRM_Utils_Rule::numeric($codeValue['geo_code_1'])) {
1463 continue;
1464 }
1465 else {
1466 self::addToErrorMsg(ts('Geo code 1'), $errorMessage);
1467 }
1468 }
1469 }
1470 }
1471 break;
1472
1473 case 'geo_code_2':
1474 if (!empty($value)) {
1475 foreach ($value as $codeValue) {
1476 if (!empty($codeValue['geo_code_2'])) {
1477 if (CRM_Utils_Rule::numeric($codeValue['geo_code_2'])) {
1478 continue;
1479 }
1480 else {
1481 self::addToErrorMsg(ts('Geo code 2'), $errorMessage);
1482 }
1483 }
1484 }
1485 }
1486 break;
1487 //check for any error in email/postal greeting, addressee,
1488 //custom email/postal greeting, custom addressee, CRM-4575
1489
1490 case 'email_greeting':
1491 $emailGreetingFilter = array(
1492 'contact_type' => $this->_contactType,
1493 'greeting_type' => 'email_greeting',
1494 );
1495 if (!self::in_value($value, CRM_Core_PseudoConstant::greeting($emailGreetingFilter))) {
1496 self::addToErrorMsg(ts('Email Greeting must be one of the configured format options. Check Administer >> System Settings >> Option Groups >> Email Greetings for valid values'), $errorMessage);
1497 }
1498 break;
1499
1500 case 'postal_greeting':
1501 $postalGreetingFilter = array(
1502 'contact_type' => $this->_contactType,
1503 'greeting_type' => 'postal_greeting',
1504 );
1505 if (!self::in_value($value, CRM_Core_PseudoConstant::greeting($postalGreetingFilter))) {
1506 self::addToErrorMsg(ts('Postal Greeting must be one of the configured format options. Check Administer >> System Settings >> Option Groups >> Postal Greetings for valid values'), $errorMessage);
1507 }
1508 break;
1509
1510 case 'addressee':
1511 $addresseeFilter = array(
1512 'contact_type' => $this->_contactType,
1513 'greeting_type' => 'addressee',
1514 );
1515 if (!self::in_value($value, CRM_Core_PseudoConstant::greeting($addresseeFilter))) {
1516 self::addToErrorMsg(ts('Addressee must be one of the configured format options. Check Administer >> System Settings >> Option Groups >> Addressee for valid values'), $errorMessage);
1517 }
1518 break;
1519
1520 case 'email_greeting_custom':
1521 if (array_key_exists('email_greeting', $params)) {
1522 $emailGreetingLabel = key(CRM_Core_OptionGroup::values('email_greeting', TRUE, NULL, NULL, 'AND v.name = "Customized"'));
1523 if (CRM_Utils_Array::value('email_greeting', $params) != $emailGreetingLabel) {
1524 self::addToErrorMsg(ts('Email Greeting - Custom'), $errorMessage);
1525 }
1526 }
1527 break;
1528
1529 case 'postal_greeting_custom':
1530 if (array_key_exists('postal_greeting', $params)) {
1531 $postalGreetingLabel = key(CRM_Core_OptionGroup::values('postal_greeting', TRUE, NULL, NULL, 'AND v.name = "Customized"'));
1532 if (CRM_Utils_Array::value('postal_greeting', $params) != $postalGreetingLabel) {
1533 self::addToErrorMsg(ts('Postal Greeting - Custom'), $errorMessage);
1534 }
1535 }
1536 break;
1537
1538 case 'addressee_custom':
1539 if (array_key_exists('addressee', $params)) {
1540 $addresseeLabel = key(CRM_Core_OptionGroup::values('addressee', TRUE, NULL, NULL, 'AND v.name = "Customized"'));
1541 if (CRM_Utils_Array::value('addressee', $params) != $addresseeLabel) {
1542 self::addToErrorMsg(ts('Addressee - Custom'), $errorMessage);
1543 }
1544 }
1545 break;
1546
1547 case 'url':
1548 if (is_array($value)) {
1549 foreach ($value as $values) {
1550 if (!empty($values['url']) && !CRM_Utils_Rule::url($values['url'])) {
1551 self::addToErrorMsg(ts('Website'), $errorMessage);
1552 break;
1553 }
1554 }
1555 }
1556 break;
1557
1558 case 'do_not_email':
1559 case 'do_not_phone':
1560 case 'do_not_mail':
1561 case 'do_not_sms':
1562 case 'do_not_trade':
1563 if (CRM_Utils_Rule::boolean($value) == FALSE) {
1564 $key = ucwords(str_replace("_", " ", $key));
1565 self::addToErrorMsg($key, $errorMessage);
1566 }
1567 break;
1568
1569 case 'email':
1570 if (is_array($value)) {
1571 foreach ($value as $values) {
1572 if (!empty($values['email']) && !CRM_Utils_Rule::email($values['email'])) {
1573 self::addToErrorMsg($key, $errorMessage);
1574 break;
1575 }
1576 }
1577 }
1578 break;
1579
1580 default:
1581 if (is_array($params[$key]) && isset($params[$key]["contact_type"])) {
1582 //check for any relationship data ,FIX ME
1583 self::isErrorInCoreData($params[$key], $errorMessage);
1584 }
1585 }
1586 }
1587 }
1588 }
1589
1590 /**
1591 * function to ckeck a value present or not in a array
1592 *
1593 * @param $value
1594 * @param $valueArray
1595 *
1596 * @return bool
1597 *
1598 * @access public
1599 */
1600 function in_value($value, $valueArray) {
1601 foreach ($valueArray as $key => $v) {
1602 //fix for CRM-1514
1603 if (strtolower(trim($v, ".")) == strtolower(trim($value, "."))) {
1604 return TRUE;
1605 }
1606 }
1607 return FALSE;
1608 }
1609
1610 /**
1611 * function to build error-message containing error-fields
1612 *
1613 * @param String $errorName A string containing error-field name.
1614 * @param String $errorMessage A string containing all the error-fields, where the new errorName is concatenated.
1615 *
1616 * @static
1617 * @access public
1618 */
1619 static function addToErrorMsg($errorName, &$errorMessage) {
1620 if ($errorMessage) {
1621 $errorMessage .= "; $errorName";
1622 }
1623 else {
1624 $errorMessage = $errorName;
1625 }
1626 }
1627
1628 /**
1629 * method for creating contact
1630 *
1631 *
1632 */
1633 function createContact(&$formatted, &$contactFields, $onDuplicate, $contactId = NULL, $requiredCheck = TRUE, $dedupeRuleGroupID = NULL) {
1634 $dupeCheck = FALSE;
1635
1636 $newContact = NULL;
1637
1638 if (is_null($contactId) && ($onDuplicate != CRM_Import_Parser::DUPLICATE_NOCHECK)) {
1639 $dupeCheck = (bool)($onDuplicate);
1640 }
1641
1642 //get the prefix id etc if exists
1643 CRM_Contact_BAO_Contact::resolveDefaults($formatted, TRUE);
1644
1645 require_once 'CRM/Utils/DeprecatedUtils.php';
1646 //@todo direct call to API function not supported.
1647 // setting required check to false, CRM-2839
1648 // plus we do our own required check in import
1649 $error = _civicrm_api3_deprecated_contact_check_params($formatted, $dupeCheck, TRUE, FALSE, $dedupeRuleGroupID);
1650
1651 if ((is_null($error)) && (civicrm_error(_civicrm_api3_deprecated_validate_formatted_contact($formatted)))) {
1652 $error = _civicrm_api3_deprecated_validate_formatted_contact($formatted);
1653 }
1654
1655 $newContact = $error;
1656
1657 if (is_null($error)) {
1658 if ($contactId) {
1659 $this->formatParams($formatted, $onDuplicate, (int) $contactId);
1660 }
1661
1662 // pass doNotResetCache flag since resetting and rebuilding cache could be expensive.
1663 $config = CRM_Core_Config::singleton();
1664 $config->doNotResetCache = 1;
1665 $cid = CRM_Contact_BAO_Contact::createProfileContact($formatted, $contactFields, $contactId, NULL, NULL, $formatted['contact_type']);
1666 $config->doNotResetCache = 0;
1667
1668 $contact = array(
1669 'contact_id' => $cid,
1670 );
1671
1672 $defaults = array();
1673 $newContact = CRM_Contact_BAO_Contact::retrieve($contact, $defaults);
1674 }
1675
1676 //get the id of the contact whose street address is not parsable, CRM-5886
1677 if ($this->_parseStreetAddress && is_object($newContact) && property_exists($newContact, 'address') && $newContact->address) {
1678 foreach ($newContact->address as $address) {
1679 if (!empty($address['street_address']) && (empty($address['street_number']) || empty($address['street_name']))) {
1680 $this->_unparsedStreetAddressContacts[] = array(
1681 'id' => $newContact->id,
1682 'streetAddress' => $address['street_address'],
1683 );
1684 }
1685 }
1686 }
1687 return $newContact;
1688 }
1689
1690 /**
1691 * format params for update and fill mode
1692 *
1693 * @param $params array reference to an array containing all the
1694 * values for import
1695 * @param $onDuplicate int
1696 * @param $cid int contact id
1697 */
1698 function formatParams(&$params, $onDuplicate, $cid) {
1699 if ($onDuplicate == CRM_Import_Parser::DUPLICATE_SKIP) {
1700 return;
1701 }
1702
1703 $contactParams = array(
1704 'contact_id' => $cid,
1705 );
1706
1707 $defaults = array();
1708 $contactObj = CRM_Contact_BAO_Contact::retrieve($contactParams, $defaults);
1709
1710 $modeUpdate = $modeFill = FALSE;
1711
1712 if ($onDuplicate == CRM_Import_Parser::DUPLICATE_UPDATE) {
1713 $modeUpdate = TRUE;
1714 }
1715
1716 if ($onDuplicate == CRM_Import_Parser::DUPLICATE_FILL) {
1717 $modeFill = TRUE;
1718 }
1719
1720 $groupTree = CRM_Core_BAO_CustomGroup::getTree($params['contact_type'], CRM_Core_DAO::$_nullObject, $cid, 0, NULL);
1721 CRM_Core_BAO_CustomGroup::setDefaults($groupTree, $defaults, FALSE, FALSE);
1722
1723 $locationFields = array(
1724 'email' => 'email',
1725 'phone' => 'phone',
1726 'im' => 'name',
1727 'website' => 'website',
1728 'address' => 'address',
1729 );
1730
1731 $contact = get_object_vars($contactObj);
1732
1733 foreach ($params as $key => $value) {
1734 if ($key == 'id' || $key == 'contact_type') {
1735 continue;
1736 }
1737
1738 if (array_key_exists($key, $locationFields)) {
1739 continue;
1740 }
1741 elseif (in_array($key, array(
1742 'email_greeting',
1743 'postal_greeting',
1744 'addressee',
1745 ))) {
1746 // CRM-4575, need to null custom
1747 if ($params["{$key}_id"] != 4) {
1748 $params["{$key}_custom"] = 'null';
1749 }
1750 unset($params[$key]);
1751 }
1752 elseif ($customFieldId = CRM_Core_BAO_CustomField::getKeyID($key)) {
1753 $custom = TRUE;
1754 }
1755 else {
1756 $getValue = CRM_Utils_Array::retrieveValueRecursive($contact, $key);
1757
1758 if ($key == 'contact_source') {
1759 $params['source'] = $params[$key];
1760 unset($params[$key]);
1761 }
1762
1763 if ($modeFill && isset($getValue)) {
1764 unset($params[$key]);
1765 }
1766 }
1767 }
1768
1769 foreach ($locationFields as $locKeys) {
1770 if (is_array(CRM_Utils_Array::value($locKeys, $params))) {
1771 foreach ($params[$locKeys] as $key => $value) {
1772 if ($modeFill) {
1773 $getValue = CRM_Utils_Array::retrieveValueRecursive($contact, $locKeys);
1774
1775 if (isset($getValue)) {
1776 foreach ($getValue as $cnt => $values) {
1777 if ($locKeys == 'website') {
1778 if (($getValue[$cnt]['website_type_id'] == $params[$locKeys][$key]['website_type_id'])) {
1779 unset($params[$locKeys][$key]);
1780 }
1781 }
1782 else {
1783 if ((!empty($getValue[$cnt]['location_type_id']) && !empty($params[$locKeys][$key]['location_type_id'])) && $getValue[$cnt]['location_type_id'] == $params[$locKeys][$key]['location_type_id']) {
1784 unset($params[$locKeys][$key]);
1785 }
1786 }
1787 }
1788 }
1789 }
1790 }
1791 if (count($params[$locKeys]) == 0) {
1792 unset($params[$locKeys]);
1793 }
1794 }
1795 }
1796 }
1797
1798 /**
1799 * convert any given date string to default date array.
1800 *
1801 * @param array $params has given date-format
1802 * @param array $formatted store formatted date in this array
1803 * @param int $dateType type of date
1804 * @param string $dateParam index of params
1805 * @static
1806 */
1807 function formatCustomDate(&$params, &$formatted, $dateType, $dateParam) {
1808 //fix for CRM-2687
1809 CRM_Utils_Date::convertToDefaultDate($params, $dateType, $dateParam);
1810 $formatted[$dateParam] = CRM_Utils_Date::processDate($params[$dateParam]);
1811 }
1812
1813 /**
1814 * format common params data to proper format to store.
1815 *
1816 * @param array $params contain record values.
1817 * @param array $formatted array of formatted data.
1818 * @param array $contactFields contact DAO fields.
1819 * @static
1820 */
1821 function formatCommonData($params, &$formatted, &$contactFields) {
1822 $csType = array(
1823 CRM_Utils_Array::value('contact_type', $formatted),
1824 );
1825
1826 //CRM-5125
1827 //add custom fields for contact sub type
1828 if (!empty($this->_contactSubType)) {
1829 $csType = $this->_contactSubType;
1830 }
1831
1832 if ($relCsType = CRM_Utils_Array::value('contact_sub_type', $formatted)) {
1833 $csType = $relCsType;
1834 }
1835
1836 $customFields = CRM_Core_BAO_CustomField::getFields($formatted['contact_type'], FALSE, FALSE, $csType);
1837
1838 $addressCustomFields = CRM_Core_BAO_CustomField::getFields('Address');
1839 $customFields = $customFields + $addressCustomFields;
1840
1841 //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
1842 $elements = array(
1843 'email_greeting_custom' => 'email_greeting',
1844 'postal_greeting_custom' => 'postal_greeting',
1845 'addressee_custom' => 'addressee',
1846 );
1847 foreach ($elements as $k => $v) {
1848 if (array_key_exists($k, $params) && !(array_key_exists($v, $params))) {
1849 $label = key(CRM_Core_OptionGroup::values($v, TRUE, NULL, NULL, 'AND v.name = "Customized"'));
1850 $params[$v] = $label;
1851 }
1852 }
1853
1854 //format date first
1855 $session = CRM_Core_Session::singleton();
1856 $dateType = $session->get("dateTypes");
1857 foreach ($params as $key => $val) {
1858 $customFieldID = CRM_Core_BAO_CustomField::getKeyID($key);
1859 if ($customFieldID &&
1860 !array_key_exists($customFieldID, $addressCustomFields)) {
1861 //we should not update Date to null, CRM-4062
1862 if ($val && ($customFields[$customFieldID]['data_type'] == 'Date')) {
1863 self::formatCustomDate($params, $formatted, $dateType, $key);
1864 }
1865 elseif ($customFields[$customFieldID]['data_type'] == 'Boolean') {
1866 if (empty($val) && !is_numeric($val) && $this->_onDuplicate == CRM_Import_Parser::DUPLICATE_FILL) {
1867 //retain earlier value when Import mode is `Fill`
1868 unset($params[$key]);
1869 }
1870 else {
1871 $params[$key] = CRM_Utils_String::strtoboolstr($val);
1872 }
1873 }
1874
1875 if ($key == 'birth_date' && $val) {
1876 CRM_Utils_Date::convertToDefaultDate($params, $dateType, $key);
1877 }
1878 elseif ($key == 'deceased_date' && $val) {
1879 CRM_Utils_Date::convertToDefaultDate($params, $dateType, $key);
1880 }
1881 elseif ($key == 'is_deceased' && $val) {
1882 $params[$key] = CRM_Utils_String::strtoboolstr($val);
1883 }
1884 elseif ($key == 'gender') {
1885 //CRM-4360
1886 $params[$key] = $this->checkGender($val);
1887 }
1888 }
1889 }
1890
1891 //now format custom data.
1892 foreach ($params as $key => $field) {
1893 if (is_array($field)) {
1894 $isAddressCustomField = FALSE;
1895 foreach ($field as $value) {
1896 $break = FALSE;
1897 if (is_array($value)) {
1898 foreach ($value as $name => $testForEmpty) {
1899 if ($addressCustomFieldID = CRM_Core_BAO_CustomField::getKeyID($name)) {
1900 $isAddressCustomField = TRUE;
1901 break;
1902 }
1903 // check if $value does not contain IM provider or phoneType
1904 if (($name !== 'phone_type_id' || $name !== 'provider_id') && ($testForEmpty === '' || $testForEmpty == NULL)) {
1905 $break = TRUE;
1906 break;
1907 }
1908 }
1909 }
1910 else {
1911 $break = TRUE;
1912 }
1913
1914 if (!$break) {
1915 require_once 'CRM/Utils/DeprecatedUtils.php';
1916 _civicrm_api3_deprecated_add_formatted_param($value, $formatted);
1917 }
1918 }
1919 if (!$isAddressCustomField) {
1920 continue;
1921 }
1922 }
1923
1924 $formatValues = array(
1925 $key => $field,
1926 );
1927
1928 if (($key !== 'preferred_communication_method') && (array_key_exists($key, $contactFields))) {
1929 // due to merging of individual table and
1930 // contact table, we need to avoid
1931 // preferred_communication_method forcefully
1932 $formatValues['contact_type'] = $formatted['contact_type'];
1933 }
1934
1935 if ($key == 'id' && isset($field)) {
1936 $formatted[$key] = $field;
1937 }
1938 require_once 'CRM/Utils/DeprecatedUtils.php';
1939 _civicrm_api3_deprecated_add_formatted_param($formatValues, $formatted);
1940
1941 //Handling Custom Data
1942 // note: Address custom fields will be handled separately inside _civicrm_api3_deprecated_add_formatted_param
1943 if (($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) &&
1944 array_key_exists($customFieldID, $customFields) &&
1945 !array_key_exists($customFieldID, $addressCustomFields)) {
1946
1947 $extends = CRM_Utils_Array::value('extends', $customFields[$customFieldID]);
1948 $htmlType = CRM_Utils_Array::value( 'html_type', $customFields[$customFieldID] );
1949 switch ( $htmlType ) {
1950 case 'Select':
1951 case 'Radio':
1952 case 'Autocomplete-Select':
1953 if ($customFields[$customFieldID]['data_type'] == 'String') {
1954 $customOption = CRM_Core_BAO_CustomOption::getCustomOption($customFieldID, TRUE);
1955 foreach ($customOption as $customFldID => $customValue) {
1956 $val = CRM_Utils_Array::value('value', $customValue);
1957 $label = CRM_Utils_Array::value('label', $customValue);
1958 $label = strtolower($label);
1959 $value = strtolower(trim($formatted[$key]));
1960 if (($value == $label) || ($value == strtolower($val))) {
1961 $params[$key] = $formatted[$key] = $val;
1962 }
1963 }
1964 }
1965 break;
1966 case 'CheckBox':
1967 case 'AdvMulti-Select':
1968 case 'Multi-Select':
1969
1970 if (!empty($formatted[$key]) && !empty($params[$key])) {
1971 $mulValues = explode( ',', $formatted[$key] );
1972 $customOption = CRM_Core_BAO_CustomOption::getCustomOption( $customFieldID, true );
1973 $formatted[$key] = array( );
1974 $params[$key] = array( );
1975 foreach ( $mulValues as $v1 ) {
1976 foreach ( $customOption as $v2 ) {
1977 if ( ( strtolower( $v2['label'] ) == strtolower( trim( $v1 ) ) ) ||
1978 ( strtolower( $v2['value'] ) == strtolower( trim( $v1 ) ) ) ) {
1979 if ( $htmlType == 'CheckBox' ) {
1980 $params[$key][$v2['value']] = $formatted[$key][$v2['value']] = 1;
1981 } else {
1982 $params[$key][] = $formatted[$key][] = $v2['value'];
1983 }
1984 }
1985 }
1986 }
1987 }
1988 break;
1989 }
1990 }
1991 }
1992
1993 if (($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) && array_key_exists($customFieldID, $customFields) &&
1994 !array_key_exists($customFieldID, $addressCustomFields)) {
1995 // @todo calling api functions directly is not supported
1996 _civicrm_api3_custom_format_params($params, $formatted, $extends);
1997 }
1998
1999 // to check if not update mode and unset the fields with empty value.
2000 if (!$this->_updateWithId && array_key_exists('custom', $formatted)) {
2001 foreach ($formatted['custom'] as $customKey => $customvalue) {
2002 if (empty($formatted['custom'][$customKey][- 1]['is_required'])) {
2003 $formatted['custom'][$customKey][-1]['is_required'] = $customFields[$customKey]['is_required'];
2004 }
2005 $emptyValue = CRM_Utils_Array::value('value', $customvalue[ - 1]);
2006 if (!isset($emptyValue)) {
2007 unset($formatted['custom'][$customKey]);
2008 }
2009 }
2010 }
2011
2012 // parse street address, CRM-5450
2013 if ($this->_parseStreetAddress) {
2014 if (array_key_exists('address', $formatted) && is_array($formatted['address'])) {
2015 foreach ($formatted['address'] as $instance => & $address) {
2016 $streetAddress = CRM_Utils_Array::value('street_address', $address);
2017 if (empty($streetAddress)) {
2018 continue;
2019 }
2020 // parse address field.
2021 $parsedFields = CRM_Core_BAO_Address::parseStreetAddress($streetAddress);
2022
2023 //street address consider to be parsed properly,
2024 //If we get street_name and street_number.
2025 if (empty($parsedFields['street_name']) || empty($parsedFields['street_number'])) {
2026 $parsedFields = array_fill_keys(array_keys($parsedFields), '');
2027 }
2028
2029 // merge parse address w/ main address block.
2030 $address = array_merge($address, $parsedFields);
2031 }
2032 }
2033 }
2034 }
2035
2036 /**
2037 * Function to generate status and error message for unparsed street address records.
2038 *
2039 * @param array $values the array of values belonging to each row
2040 * @param array $statusFieldName store formatted date in this array
2041 * @param $returnCode
2042 *
2043 * @return int
2044 * @access public
2045 */
2046 function processMessage(&$values, $statusFieldName, $returnCode) {
2047 if (empty($this->_unparsedStreetAddressContacts)) {
2048 $importRecordParams = array(
2049 $statusFieldName => 'IMPORTED',
2050 );
2051 }
2052 else {
2053 $errorMessage = ts("Record imported successfully but unable to parse the street address: ");
2054 foreach ($this->_unparsedStreetAddressContacts as $contactInfo => $contactValue) {
2055 $contactUrl = CRM_Utils_System::url('civicrm/contact/add', 'reset=1&action=update&cid=' . $contactValue['id'], TRUE, NULL, FALSE);
2056 $errorMessage .= "\n Contact ID:" . $contactValue['id'] . " <a href=\"$contactUrl\"> " . $contactValue['streetAddress'] . "</a>";
2057 }
2058 array_unshift($values, $errorMessage);
2059 $importRecordParams = array(
2060 $statusFieldName => 'ERROR',
2061 "${statusFieldName}Msg" => $errorMessage,
2062 );
2063 $returnCode = CRM_Import_Parser::UNPARSED_ADDRESS_WARNING;
2064 }
2065 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
2066 return $returnCode;
2067 }
2068
2069 /**
2070 * @param $relKey
2071 * @param $params
2072 *
2073 * @return bool
2074 */
2075 function checkRelatedContactFields($relKey, $params) {
2076 //avoid blank contact creation.
2077 $allowToCreate = FALSE;
2078
2079 //build the mapper field array.
2080 static $relatedContactFields = array();
2081 if (!isset($relatedContactFields[$relKey])) {
2082 foreach ($this->_mapperRelated as $key => $name) {
2083 if (!$name) {
2084 continue;
2085 }
2086
2087 if (!empty($relatedContactFields[$name]) && !is_array($relatedContactFields[$name])) {
2088 $relatedContactFields[$name] = array();
2089 }
2090 $fldName = CRM_Utils_Array::value($key, $this->_mapperRelatedContactDetails);
2091 if ($fldName == 'url') {
2092 $fldName = 'website';
2093 }
2094 if ($fldName) {
2095 $relatedContactFields[$name][] = $fldName;
2096 }
2097 }
2098 }
2099
2100 //validate for passed data.
2101 if (is_array($relatedContactFields[$relKey])) {
2102 foreach ($relatedContactFields[$relKey] as $fld) {
2103 if (!empty($params[$fld])) {
2104 $allowToCreate = TRUE;
2105 break;
2106 }
2107 }
2108 }
2109
2110 return $allowToCreate;
2111 }
2112 }
2113