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