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