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