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