3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.7 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2017 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
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. |
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. |
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 +--------------------------------------------------------------------+
27 require_once 'CRM/Utils/DeprecatedUtils.php';
28 require_once 'api/v3/utils.php';
33 * @copyright CiviCRM LLC (c) 2004-2017
37 * class to parse contact csv files
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;
53 protected $_emailIndex;
54 protected $_firstNameIndex;
55 protected $_lastNameIndex;
57 protected $_householdNameIndex;
58 protected $_organizationNameIndex;
60 protected $_allEmails;
62 protected $_phoneIndex;
65 * Is update only permitted on an id match.
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.
73 protected $_updateWithId;
76 protected $_externalIdentifierIndex;
77 protected $_allExternalIdentifiers;
78 protected $_parseStreetAddress;
81 * Array of successfully imported contact id's
85 protected $_newContacts;
92 protected $_lineCount;
95 * Array of successfully imported related contact id's
99 protected $_newRelatedContacts;
102 * Array of all the contacts whose street addresses are not parsed.
103 * of this import process
106 protected $_unparsedStreetAddressContacts;
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
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
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;
146 * The initializer code, called before processing.
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);
154 //supporting import for contact subtypes
156 if (!empty($this->_contactSubType
)) {
157 //custom fields for sub type
158 $subTypeFields = CRM_Core_BAO_CustomField
::getFieldsForImport($this->_contactSubType
);
160 if (!empty($subTypeFields)) {
161 foreach ($subTypeFields as $customSubTypeField => $details) {
162 $fields[$customSubTypeField] = $details;
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
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;
184 if (!empty($relationshipType)) {
185 $fields = array_merge($fields, array(
187 'title' => ts('- related contact info -'),
189 ), $relationshipType);
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));
196 $this->_newContacts
= array();
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
);
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
);
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;
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();
229 if (substr($key, 0, 5) == 'phone') {
230 $this->_phoneIndex
= $index;
232 if ($key == 'first_name') {
233 $this->_firstNameIndex
= $index;
235 if ($key == 'last_name') {
236 $this->_lastNameIndex
= $index;
238 if ($key == 'household_name') {
239 $this->_householdNameIndex
= $index;
241 if ($key == 'organization_name') {
242 $this->_organizationNameIndex
= $index;
245 if ($key == 'external_identifier') {
246 $this->_externalIdentifierIndex
= $index;
247 $this->_allExternalIdentifiers
= array();
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
,
258 $this->_updateWithId
= TRUE;
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);
265 * Handle the values in mapField mode.
267 * @param array $values
268 * The array of values belonging to this line.
272 public function mapField(&$values) {
273 return CRM_Import_Parser
::VALID
;
277 * Handle the values in preview mode.
279 * @param array $values
280 * The array of values belonging to this line.
283 * the result of this processing
285 public function preview(&$values) {
286 return $this->summary($values);
290 * Handle the values in summary mode.
292 * @param array $values
293 * The array of values belonging to this line.
296 * the result of this processing
298 public function summary(&$values) {
299 $erroneousField = NULL;
300 $response = $this->setActiveFieldValues($values, $erroneousField);
302 $errorMessage = NULL;
303 $errorRequired = FALSE;
304 switch ($this->_contactType
) {
306 $missingNames = array();
307 if ($this->_firstNameIndex
< 0 ||
empty($values[$this->_firstNameIndex
])) {
308 $errorRequired = TRUE;
309 $missingNames[] = ts('First Name');
311 if ($this->_lastNameIndex
< 0 ||
empty($values[$this->_lastNameIndex
])) {
312 $errorRequired = TRUE;
313 $missingNames[] = ts('Last Name');
315 if ($errorRequired) {
316 $and = ' ' . ts('and') . ' ';
317 $errorMessage = ts('Missing required fields:') . ' ' . implode($and, $missingNames);
322 if ($this->_householdNameIndex
< 0 ||
empty($values[$this->_householdNameIndex
])) {
323 $errorRequired = TRUE;
324 $errorMessage = ts('Missing required fields:') . ' ' . ts('Household Name');
329 if ($this->_organizationNameIndex
< 0 ||
empty($values[$this->_organizationNameIndex
])) {
330 $errorRequired = TRUE;
331 $errorMessage = ts('Missing required fields:') . ' ' . ts('Organization Name');
336 $statusFieldName = $this->_statusFieldName
;
338 if ($this->_emailIndex
>= 0) {
339 /* If we don't have the required fields, bail */
341 if ($this->_contactType
== 'Individual' && !$this->_updateWithId
) {
342 if ($errorRequired && empty($values[$this->_emailIndex
])) {
344 $errorMessage .= ' ' . ts('OR') . ' ' . ts('Email Address');
347 $errorMessage = ts('Missing required field:') . ' ' . ts('Email Address');
349 array_unshift($values, $errorMessage);
350 $importRecordParams = array(
351 $statusFieldName => 'ERROR',
352 "${statusFieldName}Msg" => $errorMessage,
354 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
356 return CRM_Import_Parser
::ERROR
;
360 $email = CRM_Utils_Array
::value($this->_emailIndex
, $values);
362 /* If the email address isn't valid, bail */
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,
371 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
373 return CRM_Import_Parser
::ERROR
;
376 /* otherwise, count it and move on */
377 $this->_allEmails
[$email] = $this->_lineCount
;
380 elseif ($errorRequired && !$this->_updateWithId
) {
382 $errorMessage .= ' ' . ts('OR') . ' ' . ts('Email Address');
385 $errorMessage = ts('Missing required field:') . ' ' . ts('Email Address');
387 array_unshift($values, $errorMessage);
388 $importRecordParams = array(
389 $statusFieldName => 'ERROR',
390 "${statusFieldName}Msg" => $errorMessage,
392 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
394 return CRM_Import_Parser
::ERROR
;
397 //check for duplicate external Identifier
398 $externalID = CRM_Utils_Array
::value($this->_externalIdentifierIndex
, $values);
400 /* If it's a dupe,external Identifier */
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,
409 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
410 return CRM_Import_Parser
::ERROR
;
412 //otherwise, count it and move on
413 $this->_allExternalIdentifiers
[$externalID] = $this->_lineCount
;
416 //Checking error in custom data
417 $params = &$this->getActiveFieldParams();
418 $params['contact_type'] = $this->_contactType
;
419 //date-format part ends
421 $errorMessage = NULL;
424 //add custom fields for contact sub type
426 if (!empty($this->_contactSubType
)) {
427 $csType = $this->_contactSubType
;
430 //checking error in custom data
431 $this->isErrorInCustomData($params, $errorMessage, $csType, $this->_relationships
);
433 //checking error in core data
434 $this->isErrorInCoreData($params, $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,
442 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
443 array_unshift($values, $tempMsg);
444 $errorMessage = NULL;
445 return CRM_Import_Parser
::ERROR
;
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',
454 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
456 return CRM_Import_Parser
::VALID
;
460 * Handle the values in import mode.
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.
467 * @param bool $doGeocodeAddress
470 * the result of this processing
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
= '';
480 // first make sure this is a valid line
481 //$this->_updateWithId = false;
482 $response = $this->summary($values);
483 $statusFieldName = $this->_statusFieldName
;
485 if ($response != CRM_Import_Parser
::VALID
) {
486 $importRecordParams = array(
487 $statusFieldName => 'INVALID',
488 "${statusFieldName}Msg" => "Invalid (Error Code: $response)",
490 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
494 $params = &$this->getActiveFieldParams();
496 'contact_type' => $this->_contactType
,
499 static $contactFields = NULL;
500 if ($contactFields == NULL) {
501 $contactFields = CRM_Contact_DAO_Contact
::import();
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
,
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,
519 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
520 return CRM_Import_Parser
::DUPLICATE
;
525 if (!empty($this->_contactSubType
)) {
526 $params['contact_sub_type'] = $this->_contactSubType
;
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);
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
;
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;
549 //format common data, CRM-4062
550 $this->formatCommonData($params, $formatted, $contactFields);
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) {
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
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');
577 if (!empty($contactSubType) && (!CRM_Contact_BAO_ContactType
::isAllowEdit($params['id'], $contactSubType) && $contactSubType != CRM_Utils_Array
::value('contact_sub_type', $formatted))) {
579 $message = "Mismatched contact SubTypes :";
580 array_unshift($values, $message);
582 $this->_retCode
= CRM_Import_Parser
::NO_MATCH
;
586 $this->_retCode
= CRM_Import_Parser
::VALID
;
590 $message = "Mismatched contact Types :";
591 array_unshift($values, $message);
593 $this->_retCode
= CRM_Import_Parser
::NO_MATCH
;
598 $message = "Mismatched contact IDs OR Mismatched contact Types :";
599 array_unshift($values, $message);
600 $this->_retCode
= CRM_Import_Parser
::NO_MATCH
;
606 if (!empty($params['id'])) {
607 $contactType = CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_Contact', $params['id'], 'contact_type');
609 if ($formatted['contact_type'] == $contactType) {
610 //validation of subtype for update mode
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');
617 if (!empty($contactSubType) && (!CRM_Contact_BAO_ContactType
::isAllowEdit($params['id'], $contactSubType) && $contactSubType != CRM_Utils_Array
::value('contact_sub_type', $formatted))) {
619 $message = "Mismatched contact SubTypes :";
620 array_unshift($values, $message);
621 $this->_retCode
= CRM_Import_Parser
::NO_MATCH
;
624 $newContact = $this->createContact($formatted, $contactFields, $onDuplicate, $params['id'], FALSE, $this->_dedupeRuleGroupID
);
625 $this->_retCode
= CRM_Import_Parser
::VALID
;
629 $message = "Mismatched contact Types :";
630 array_unshift($values, $message);
631 $this->_retCode
= CRM_Import_Parser
::NO_MATCH
;
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
;
646 //now we want to create new contact on update/fill also.
647 $createNewContact = TRUE;
651 if (isset($newContact) && is_a($newContact, 'CRM_Contact_BAO_Contact')) {
652 $relationship = TRUE;
654 elseif (is_a($error, 'CRM_Core_Error')) {
655 $newContact = $error;
656 $relationship = TRUE;
661 //now we create new contact in update/fill mode also.
663 if ($createNewContact ||
($this->_retCode
!= CRM_Import_Parser
::NO_MATCH
&& $this->_updateWithId
)) {
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);
674 if ($this->_updateWithId
&& !empty($params['id'])) {
675 $contactID = $params['id'];
677 $newContact = $this->createContact($formatted, $contactFields, $onDuplicate, $contactID, TRUE, $this->_dedupeRuleGroupID
);
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;
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
;
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,
700 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
701 return CRM_Import_Parser
::DUPLICATE
;
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);
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;
723 $currentImportID = end($values);
726 'contactID' => $contactID,
727 'importID' => $currentImportID,
728 'importTempTable' => $this->_tableName
,
729 'fieldHeaders' => $this->_mapperKeys
,
730 'fields' => $this->_activeFields
,
733 CRM_Utils_Hook
::import('Contact', 'process', $this, $hookParams);
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];
744 $primaryContactId = $newContact->id
;
747 if ((CRM_Core_Error
::isAPIError($newContact, CRM_Core_ERROR
::DUPLICATE_CONTACT
) ||
is_a($newContact, 'CRM_Contact_BAO_Contact')) && $primaryContactId) {
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')) {
756 $relationType = new CRM_Contact_DAO_RelationshipType();
757 $relationType->id
= $id;
758 $relationType->find(TRUE);
759 $direction = "contact_sub_type_$second";
762 'contact_type' => $params[$key]['contact_type'],
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
;
774 $formatting['contact_sub_type'] = $relationType->$direction;
777 $relationType->free();
779 $contactFields = NULL;
780 $contactFields = CRM_Contact_DAO_Contact
::import();
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');
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'])
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
;
799 //validation of related contact subtype for update mode
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');
806 if (!empty($relatedCsType) && (!CRM_Contact_BAO_ContactType
::isAllowEdit($params[$key]['id'], $relatedCsType) &&
807 $relatedCsType != CRM_Utils_Array
::value('contact_sub_type', $formatting))
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
;
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'];
821 //format common data, CRM-4062
822 $this->formatCommonData($field, $formatting, $contactFields);
824 //do we have enough fields to create related contact.
825 $allowToCreate = $this->checkRelatedContactFields($key, $formatting);
827 if (!$allowToCreate) {
828 $errorMessage = ts('Related contact required fields are missing.');
829 array_unshift($values, $errorMessage);
830 return CRM_Import_Parser
::NO_MATCH
;
834 if (!empty($params[$key]['id'])) {
836 'contact_id' => $params[$key]['id'],
839 $relatedNewContact = CRM_Contact_BAO_Contact
::retrieve($contact, $defaults);
842 $relatedNewContact = $this->createContact($formatting, $contactFields, $onDuplicate, NULL, FALSE);
845 if (is_object($relatedNewContact) ||
($relatedNewContact instanceof CRM_Contact_BAO_Contact
)) {
846 $relatedNewContact = clone($relatedNewContact);
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]);
857 $errorMessage = $relatedNewContact['error_message'];
858 array_unshift($values, $errorMessage);
859 $importRecordParams = array(
860 $statusFieldName => 'ERROR',
861 "${statusFieldName}Msg" => $errorMessage,
863 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
864 return CRM_Import_Parser
::ERROR
;
868 $matchedIDs[] = $relatedNewContact->id
;
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
,
875 //validation of related contact subtype for update mode
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');
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
;
888 $updatedContact = $this->createContact($formatting, $contactFields, $onDuplicate, $matchedIDs[0]);
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);
900 if (empty($reContact)) {
901 $this->_newRelatedContacts
[] = $relativeContact[] = $relContactId;
905 $this->_newRelatedContacts
[] = $relativeContact[] = $relContactId;
910 $relContactId = $relatedNewContact->id
;
911 $this->_newRelatedContacts
[] = $relativeContact[] = $relContactId;
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(
927 'skipRecentView' => TRUE,
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,
936 list($valid, $invalid, $duplicate, $saved, $relationshipIds) = CRM_Contact_BAO_Relationship
::legacyCreateMultiple($relationParams, $relationIds);
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);
944 //handle current employer, CRM-3532
946 $allRelationships = CRM_Core_PseudoConstant
::relationshipType('name');
947 $relationshipTypeId = str_replace(array(
954 $relationshipType = str_replace($relationshipTypeId . '_', '', $key);
955 $orgId = $individualId = NULL;
956 if ($allRelationships[$relationshipTypeId]["name_{$relationshipType}"] == 'Employee of') {
957 $orgId = $relContactId;
958 $individualId = $primaryContactId;
960 elseif ($allRelationships[$relationshipTypeId]["name_{$relationshipType}"] == 'Employer of') {
961 $orgId = $primaryContactId;
962 $individualId = $relContactId;
964 if ($orgId && $individualId) {
965 $currentEmpParams[$individualId] = $orgId;
966 CRM_Contact_BAO_Contact_Utils
::setCurrentEmployer($currentEmpParams);
974 if ($this->_updateWithId
) {
975 //return warning if street address is unparsed, CRM-5886
976 return $this->processMessage($values, $statusFieldName, $this->_retCode
);
979 if (is_array($newContact) && civicrm_error($newContact)) {
982 if (($code = CRM_Utils_Array
::value('code', $newContact['error_message'])) && ($code == CRM_Core_Error
::DUPLICATE_CONTACT
)) {
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];
990 $cids = explode(',', $newContact['error_message']['params'][0]);
993 foreach ($cids as $cid) {
994 $urls[] = CRM_Utils_System
::url('civicrm/contact/view', 'reset=1&cid=' . $cid, TRUE);
997 $url_string = implode("\n", $urls);
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,
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
;
1014 // Params only had one id, so shift it out
1015 $contactId = array_shift($cids);
1018 $vals = array('contact_id' => $contactId);
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']);
1024 elseif ($onDuplicate == CRM_Import_Parser
::DUPLICATE_UPDATE
) {
1025 $newContact = $this->createContact($formatted, $contactFields, $onDuplicate, $contactId);
1027 elseif ($onDuplicate == CRM_Import_Parser
::DUPLICATE_FILL
) {
1028 $newContact = $this->createContact($formatted, $contactFields, $onDuplicate, $contactId);
1030 // else skip does nothing and just returns an error code.
1033 'contact_id' => $cid,
1035 $defaults = array();
1036 $newContact = CRM_Contact_BAO_Contact
::retrieve($contact, $defaults);
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,
1048 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
1049 return CRM_Import_Parser
::ERROR
;
1052 $contactID = $newContact['error_message']['params'][0];
1053 if (!in_array($contactID, $this->_newContacts
)) {
1054 $this->_newContacts
[] = $contactID;
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",
1064 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
1065 return CRM_Import_Parser
::DUPLICATE
;
1068 $importRecordParams = array(
1069 $statusFieldName => 'IMPORTED',
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
);
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,
1083 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
1084 return CRM_Import_Parser
::ERROR
;
1088 return $this->processMessage($values, $statusFieldName, CRM_Import_Parser
::VALID
);
1092 * Get the array of successfully imported contact id's
1096 public function &getImportedContacts() {
1097 return $this->_newContacts
;
1101 * Get the array of successfully imported related contact id's
1105 public function &getRelatedImportedContacts() {
1106 return $this->_newRelatedContacts
;
1110 * The initializer code, called before the processing.
1112 public function fini() {
1116 * Check if an error in custom data.
1118 * @param array $params
1119 * @param string $errorMessage
1120 * A string containing all the error-fields.
1122 * @param null $csType
1123 * @param null $relationships
1125 public static function isErrorInCustomData($params, &$errorMessage, $csType = NULL, $relationships = NULL) {
1126 $session = CRM_Core_Session
::singleton();
1127 $dateType = $session->get("dateTypes");
1129 if (!empty($params['contact_sub_type'])) {
1130 $csType = CRM_Utils_Array
::value('contact_sub_type', $params);
1133 if (empty($params['contact_type'])) {
1134 $params['contact_type'] = 'Individual';
1137 // get array of subtypes - CRM-18708
1138 if (in_array($csType, array('Individual', 'Organization', 'Household'))) {
1139 $csType = self
::getSubtypes($params['contact_type']);
1142 if (is_array($csType)) {
1143 // fetch custom fields for every subtype and add it to $customFields array
1145 $customFields = array();
1146 foreach ($csType as $cType) {
1147 $customFields +
= CRM_Core_BAO_CustomField
::getFields($params['contact_type'], FALSE, FALSE, $cType);
1151 $customFields = CRM_Core_BAO_CustomField
::getFields($params['contact_type'], FALSE, FALSE, $csType);
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 */
1160 if (!array_key_exists($customFieldID, $customFields)) {
1161 self
::addToErrorMsg(ts('field ID'), $errorMessage);
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);
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];
1173 /* validate the data against the CF type */
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];
1180 elseif (CRM_Utils_Date
::convertToDefaultDate($params, $dateType, $key)) {
1181 $value = $params[$key];
1184 self
::addToErrorMsg($customFields[$customFieldID]['label'], $errorMessage);
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);
1192 // need not check for label filed import
1199 'Multi-Select State/Province',
1200 'Multi-Select Country',
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);
1205 self
::addToErrorMsg($customFields[$customFieldID]['label'], $errorMessage);
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) {
1221 foreach ($customOption as $v2) {
1222 if ((strtolower(trim($v2['label'])) == strtolower(trim($v1))) ||
(strtolower(trim($v2['value'])) == strtolower(trim($v1)))) {
1228 self
::addToErrorMsg($customFields[$customFieldID]['label'], $errorMessage);
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);
1235 foreach ($customOption as $v2) {
1236 if ((strtolower(trim($v2['label'])) == strtolower(trim($value))) ||
(strtolower(trim($v2['value'])) == strtolower(trim($value)))) {
1241 self
::addToErrorMsg($customFields[$customFieldID]['label'], $errorMessage);
1244 elseif ($customFields[$customFieldID]['html_type'] == 'Multi-Select State/Province') {
1245 $mulValues = explode(',', $value);
1246 foreach ($mulValues as $stateValue) {
1248 if (self
::in_value(trim($stateValue), CRM_Core_PseudoConstant
::stateProvinceAbbreviation()) || self
::in_value(trim($stateValue), CRM_Core_PseudoConstant
::stateProvince())) {
1252 self
::addToErrorMsg($customFields[$customFieldID]['label'], $errorMessage);
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();
1271 if (in_array(trim($countryValue), $values)) {
1278 self
::addToErrorMsg($customFields[$customFieldID]['label'], $errorMessage);
1285 elseif (is_array($params[$key]) && isset($params[$key]["contact_type"])) {
1287 //supporting custom data of related contact subtypes
1289 if ($relationships) {
1290 if (array_key_exists($key, $relationships)) {
1293 elseif (CRM_Utils_Array
::key($key, $relationships)) {
1294 $relation = CRM_Utils_Array
::key($key, $relationships);
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;
1307 $relationshipType->free();
1310 self
::isErrorInCustomData($params[$key], $errorMessage, $csType, $relationships);
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
1321 * @param string $gender
1322 * Check this value across gender values.
1324 * retunr gender value / false
1328 public function checkGender($gender) {
1329 $gender = trim($gender, '.');
1334 $allGenders = CRM_Core_PseudoConstant
::get('CRM_Contact_DAO_Contact', 'gender_id');
1335 foreach ($allGenders as $key => $value) {
1336 if (strlen($gender) > strlen($value)) {
1339 if ($gender == $value) {
1342 if (substr_compare($value, $gender, 0, strlen($gender), TRUE) === 0) {
1351 * Check if an error in Core( non-custom fields ) field
1353 * @param array $params
1354 * @param string $errorMessage
1355 * A string containing all the error-fields.
1357 public function isErrorInCoreData($params, &$errorMessage) {
1358 foreach ($params as $key => $value) {
1360 $session = CRM_Core_Session
::singleton();
1361 $dateType = $session->get("dateTypes");
1365 if (CRM_Utils_Date
::convertToDefaultDate($params, $dateType, $key)) {
1366 if (!CRM_Utils_Rule
::date($params[$key])) {
1367 self
::addToErrorMsg(ts('Birth Date'), $errorMessage);
1371 self
::addToErrorMsg(ts('Birth-Date'), $errorMessage);
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);
1382 self
::addToErrorMsg(ts('Deceased Date'), $errorMessage);
1387 if (CRM_Utils_String
::strtoboolstr($value) === FALSE) {
1388 self
::addToErrorMsg(ts('Deceased'), $errorMessage);
1394 if (!self
::checkGender($value)) {
1395 self
::addToErrorMsg(ts('Gender'), $errorMessage);
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);
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);
1415 case 'individual_prefix':
1417 if (!self
::in_value($value, CRM_Core_PseudoConstant
::get('CRM_Contact_DAO_Contact', 'prefix_id'))) {
1418 self
::addToErrorMsg(ts('Individual Prefix'), $errorMessage);
1422 case 'individual_suffix':
1424 if (!self
::in_value($value, CRM_Core_PseudoConstant
::get('CRM_Contact_DAO_Contact', 'suffix_id'))) {
1425 self
::addToErrorMsg(ts('Individual Suffix'), $errorMessage);
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())
1439 self
::addToErrorMsg(ts('State/Province'), $errorMessage);
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;
1459 if (self
::in_value($stateValue['country'], $limitCodes) || self
::in_value($stateValue['country'], CRM_Core_PseudoConstant
::country())) {
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);
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);
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);
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'])) {
1496 self
::addToErrorMsg(ts('Geo code 1'), $errorMessage);
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'])) {
1511 self
::addToErrorMsg(ts('Geo code 2'), $errorMessage);
1518 //check for any error in email/postal greeting, addressee,
1519 //custom email/postal greeting, custom addressee, CRM-4575
1521 case 'email_greeting':
1522 $emailGreetingFilter = array(
1523 'contact_type' => $this->_contactType
,
1524 'greeting_type' => 'email_greeting',
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);
1531 case 'postal_greeting':
1532 $postalGreetingFilter = array(
1533 'contact_type' => $this->_contactType
,
1534 'greeting_type' => 'postal_greeting',
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);
1542 $addresseeFilter = array(
1543 'contact_type' => $this->_contactType
,
1544 'greeting_type' => 'addressee',
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);
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);
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);
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);
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);
1589 case 'do_not_email':
1590 case 'do_not_phone':
1593 case 'do_not_trade':
1594 if (CRM_Utils_Rule
::boolean($value) == FALSE) {
1595 $key = ucwords(str_replace("_", " ", $key));
1596 self
::addToErrorMsg($key, $errorMessage);
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);
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);
1622 * Ckeck a value present or not in a array.
1625 * @param $valueArray
1629 public static function in_value($value, $valueArray) {
1630 foreach ($valueArray as $key => $v) {
1632 if (strtolower(trim($v, ".")) == strtolower(trim($value, "."))) {
1640 * Build error-message containing error-fields
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.
1648 public static function addToErrorMsg($errorName, &$errorMessage) {
1649 if ($errorMessage) {
1650 $errorMessage .= "; $errorName";
1653 $errorMessage = $errorName;
1658 * Method for creating contact.
1660 * @param array $formatted
1661 * @param array $contactFields
1662 * @param int $onDuplicate
1663 * @param int $contactId
1664 * @param bool $requiredCheck
1665 * @param int $dedupeRuleGroupID
1667 * @return array|bool|\CRM_Contact_BAO_Contact|\CRM_Core_Error|null
1669 public function createContact(&$formatted, &$contactFields, $onDuplicate, $contactId = NULL, $requiredCheck = TRUE, $dedupeRuleGroupID = NULL) {
1673 if (is_null($contactId) && ($onDuplicate != CRM_Import_Parser
::DUPLICATE_NOCHECK
)) {
1674 $dupeCheck = (bool) ($onDuplicate);
1677 //get the prefix id etc if exists
1678 CRM_Contact_BAO_Contact
::resolveDefaults($formatted, TRUE);
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);
1685 if ((is_null($error)) && (civicrm_error(_civicrm_api3_deprecated_validate_formatted_contact($formatted)))) {
1686 $error = _civicrm_api3_deprecated_validate_formatted_contact($formatted);
1689 $newContact = $error;
1691 if (is_null($error)) {
1693 $this->formatParams($formatted, $onDuplicate, (int) $contactId);
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;
1703 'contact_id' => $cid,
1706 $defaults = array();
1707 $newContact = CRM_Contact_BAO_Contact
::retrieve($contact, $defaults);
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'],
1725 * Format params for update and fill mode.
1727 * @param array $params
1728 * reference to an array containing all the.
1730 * @param int $onDuplicate
1734 public function formatParams(&$params, $onDuplicate, $cid) {
1735 if ($onDuplicate == CRM_Import_Parser
::DUPLICATE_SKIP
) {
1739 $contactParams = array(
1740 'contact_id' => $cid,
1743 $defaults = array();
1744 $contactObj = CRM_Contact_BAO_Contact
::retrieve($contactParams, $defaults);
1746 $modeUpdate = $modeFill = FALSE;
1748 if ($onDuplicate == CRM_Import_Parser
::DUPLICATE_UPDATE
) {
1752 if ($onDuplicate == CRM_Import_Parser
::DUPLICATE_FILL
) {
1756 $groupTree = CRM_Core_BAO_CustomGroup
::getTree($params['contact_type'], NULL, $cid, 0, NULL);
1757 CRM_Core_BAO_CustomGroup
::setDefaults($groupTree, $defaults, FALSE, FALSE);
1759 $locationFields = array(
1763 'website' => 'website',
1764 'address' => 'address',
1767 $contact = get_object_vars($contactObj);
1769 foreach ($params as $key => $value) {
1770 if ($key == 'id' ||
$key == 'contact_type') {
1774 if (array_key_exists($key, $locationFields)) {
1777 elseif (in_array($key, array(
1782 // CRM-4575, need to null custom
1783 if ($params["{$key}_id"] != 4) {
1784 $params["{$key}_custom"] = 'null';
1786 unset($params[$key]);
1788 elseif ($customFieldId = CRM_Core_BAO_CustomField
::getKeyID($key)) {
1792 $getValue = CRM_Utils_Array
::retrieveValueRecursive($contact, $key);
1794 if ($key == 'contact_source') {
1795 $params['source'] = $params[$key];
1796 unset($params[$key]);
1799 if ($modeFill && isset($getValue)) {
1800 unset($params[$key]);
1805 foreach ($locationFields as $locKeys) {
1806 if (is_array(CRM_Utils_Array
::value($locKeys, $params))) {
1807 foreach ($params[$locKeys] as $key => $value) {
1809 $getValue = CRM_Utils_Array
::retrieveValueRecursive($contact, $locKeys);
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]);
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]);
1827 if (count($params[$locKeys]) == 0) {
1828 unset($params[$locKeys]);
1835 * Convert any given date string to default date array.
1837 * @param array $params
1838 * Has given date-format.
1839 * @param array $formatted
1840 * Store formatted date in this array.
1841 * @param int $dateType
1843 * @param string $dateParam
1846 public static function formatCustomDate(&$params, &$formatted, $dateType, $dateParam) {
1848 CRM_Utils_Date
::convertToDefaultDate($params, $dateType, $dateParam);
1849 $formatted[$dateParam] = CRM_Utils_Date
::processDate($params[$dateParam]);
1853 * Format common params data to proper format to store.
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.
1862 public function formatCommonData($params, &$formatted, &$contactFields) {
1864 CRM_Utils_Array
::value('contact_type', $formatted),
1868 //add custom fields for contact sub type
1869 if (!empty($this->_contactSubType
)) {
1870 $csType = $this->_contactSubType
;
1873 if ($relCsType = CRM_Utils_Array
::value('contact_sub_type', $formatted)) {
1874 $csType = $relCsType;
1877 $customFields = CRM_Core_BAO_CustomField
::getFields($formatted['contact_type'], FALSE, FALSE, $csType);
1879 $addressCustomFields = CRM_Core_BAO_CustomField
::getFields('Address');
1880 $customFields = $customFields +
$addressCustomFields;
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
1884 'email_greeting_custom' => 'email_greeting',
1885 'postal_greeting_custom' => 'postal_greeting',
1886 'addressee_custom' => 'addressee',
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;
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)
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);
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]);
1913 $params[$key] = CRM_Utils_String
::strtoboolstr($val);
1918 if ($key == 'birth_date' && $val) {
1919 CRM_Utils_Date
::convertToDefaultDate($params, $dateType, $key);
1921 elseif ($key == 'deceased_date' && $val) {
1922 CRM_Utils_Date
::convertToDefaultDate($params, $dateType, $key);
1924 elseif ($key == 'is_deceased' && $val) {
1925 $params[$key] = CRM_Utils_String
::strtoboolstr($val);
1927 elseif ($key == 'gender') {
1929 $params[$key] = $this->checkGender($val);
1933 //now format custom data.
1934 foreach ($params as $key => $field) {
1935 if (is_array($field)) {
1936 $isAddressCustomField = FALSE;
1937 foreach ($field as $value) {
1939 if (is_array($value)) {
1940 foreach ($value as $name => $testForEmpty) {
1941 if ($addressCustomFieldID = CRM_Core_BAO_CustomField
::getKeyID($name)) {
1942 $isAddressCustomField = TRUE;
1945 // check if $value does not contain IM provider or phoneType
1946 if (($name !== 'phone_type_id' ||
$name !== 'provider_id') && ($testForEmpty === '' ||
$testForEmpty == NULL)) {
1957 $this->formatContactParameters($value, $formatted);
1960 if (!$isAddressCustomField) {
1965 $formatValues = array(
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'];
1976 if ($key == 'id' && isset($field)) {
1977 $formatted[$key] = $field;
1979 $this->formatContactParameters($formatValues, $formatted);
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)
1988 $extends = CRM_Utils_Array
::value('extends', $customFields[$customFieldID]);
1989 $htmlType = CRM_Utils_Array
::value('html_type', $customFields[$customFieldID]);
1990 switch ($htmlType) {
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;
2009 case 'AdvMulti-Select':
2010 case 'Multi-Select':
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)))
2022 if ($htmlType == 'CheckBox') {
2023 $params[$key][$v2['value']] = $formatted[$key][$v2['value']] = 1;
2026 $params[$key][] = $formatted[$key][] = $v2['value'];
2037 if (!empty($key) && ($customFieldID = CRM_Core_BAO_CustomField
::getKeyID($key)) && array_key_exists($customFieldID, $customFields) &&
2038 !array_key_exists($customFieldID, $addressCustomFields)
2040 // @todo calling api functions directly is not supported
2041 _civicrm_api3_custom_format_params($params, $formatted, $extends);
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'];
2050 $emptyValue = CRM_Utils_Array
::value('value', $customvalue[-1]);
2051 if (!isset($emptyValue)) {
2052 unset($formatted['custom'][$customKey]);
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)) {
2065 // parse address field.
2066 $parsedFields = CRM_Core_BAO_Address
::parseStreetAddress($streetAddress);
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), '');
2074 // merge parse address w/ main address block.
2075 $address = array_merge($address, $parsedFields);
2082 * Generate status and error message for unparsed street address records.
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
2092 public function processMessage(&$values, $statusFieldName, $returnCode) {
2093 if (empty($this->_unparsedStreetAddressContacts
)) {
2094 $importRecordParams = array(
2095 $statusFieldName => 'IMPORTED',
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>";
2104 array_unshift($values, $errorMessage);
2105 $importRecordParams = array(
2106 $statusFieldName => 'ERROR',
2107 "${statusFieldName}Msg" => $errorMessage,
2109 $returnCode = CRM_Import_Parser
::UNPARSED_ADDRESS_WARNING
;
2111 $this->updateImportRecord($values[count($values) - 1], $importRecordParams);
2117 * @param array $params
2121 public function checkRelatedContactFields($relKey, $params) {
2122 //avoid blank contact creation.
2123 $allowToCreate = FALSE;
2125 //build the mapper field array.
2126 static $relatedContactFields = array();
2127 if (!isset($relatedContactFields[$relKey])) {
2128 foreach ($this->_mapperRelated
as $key => $name) {
2133 if (!empty($relatedContactFields[$name]) && !is_array($relatedContactFields[$name])) {
2134 $relatedContactFields[$name] = array();
2136 $fldName = CRM_Utils_Array
::value($key, $this->_mapperRelatedContactDetails
);
2137 if ($fldName == 'url') {
2138 $fldName = 'website';
2141 $relatedContactFields[$name][] = $fldName;
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;
2156 return $allowToCreate;
2160 * get subtypes given the contact type
2162 * @param string $contactType
2163 * @return array $subTypes
2165 public static function getSubtypes($contactType) {
2166 $subTypes = array();
2167 $types = CRM_Contact_BAO_ContactType
::subTypeInfo($contactType);
2169 if (count($types) > 0) {
2170 foreach ($types as $type) {
2171 $subTypes[] = $type['name'];
2178 * Get the possible contact matches.
2180 * 1) the chosen dedupe rule falling back to
2181 * 2) a check for the external ID.
2185 * @param array $params
2188 * IDs of possible matches.
2190 * @throws \CRM_Core_Exception
2191 * @throws \CiviCRM_API3_Exception
2193 protected function getPossibleContactMatches($params) {
2196 if (!empty($params['external_identifier'])) {
2197 $extIDContact = civicrm_api3('Contact', 'get', array(
2198 'external_identifier' => $params['external_identifier'],
2201 if (isset($extIDContact['id'])) {
2202 $extIDMatch = $extIDContact['id'];
2205 $checkParams = array('check_permissions' => FALSE, 'match' => $params);
2206 $checkParams['match']['contact_type'] = $this->_contactType
;
2208 $possibleMatches = civicrm_api3('Contact', 'duplicatecheck', $checkParams);
2210 return array_keys($possibleMatches['values']);
2212 if ($possibleMatches['count']) {
2213 if (in_array($extIDMatch, array_keys($possibleMatches['values']))) {
2214 return array($extIDMatch);
2217 throw new CRM_Core_Exception(ts(
2218 'Matching this contact based on the de-dupe rule would cause an external ID conflict'));
2221 return array($extIDMatch);
2225 * Format the form mapping parameters ready for the parser.
2230 * @return array $parserParameters
2232 public static function getParameterForParser($count) {
2233 $baseArray = array();
2234 for ($i = 0; $i < $count; $i++
) {
2235 $baseArray[$i] = NULL;
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;
2249 return $parserParameters;
2254 * Format contact parameters.
2256 * @todo this function needs re-writing & re-merging into the main function.
2260 * @param array $values
2261 * @param array $params
2265 protected function formatContactParameters(&$values, &$params) {
2266 // Crawl through the possible classes:
2279 // Cache the various object fields
2280 static $fields = array();
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);
2286 if (isset($values['contact_type'])) {
2287 // we're an individual/household/org property
2289 $fields[$values['contact_type']] = CRM_Contact_DAO_Contact
::fields();
2291 _civicrm_api3_store_values($fields[$values['contact_type']], $values, $params);
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']];
2301 $params['prefix'] = $values['individual_prefix'];
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']];
2312 $params['suffix'] = $values['individual_suffix'];
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',
2324 $emailGreetings = CRM_Core_PseudoConstant
::greeting($emailGreetingFilter);
2325 $params['email_greeting'] = $emailGreetings[$params['email_greeting_id']];
2328 $params['email_greeting'] = $values['email_greeting'];
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',
2340 $postalGreetings = CRM_Core_PseudoConstant
::greeting($postalGreetingFilter);
2341 $params['postal_greeting'] = $postalGreetings[$params['postal_greeting_id']];
2344 $params['postal_greeting'] = $values['postal_greeting'];
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',
2355 $addressee = CRM_Core_PseudoConstant
::addressee($addresseeFilter);
2356 $params['addressee'] = $addressee[$params['addressee_id']];
2359 $params['addressee'] = $values['addressee'];
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']];
2370 $params['gender'] = $values['gender'];
2375 if (!empty($values['preferred_communication_method'])) {
2377 $pcm = array_change_key_case(array_flip(CRM_Core_PseudoConstant
::get('CRM_Contact_DAO_Contact', 'preferred_communication_method')), CASE_LOWER
);
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;
2387 $params['preferred_communication_method'] = $comm;
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();
2398 if (!array_key_exists('website', $params) ||
2399 !is_array($params['website'])
2401 $params['website'] = array();
2404 $websiteCount = count($params['website']);
2405 _civicrm_api3_store_values($websiteFields, $values,
2406 $params['website'][++
$websiteCount]
2412 // get the formatted location blocks into params - w/ 3.0 format, CRM-4605
2413 if (!empty($values['location_type_id'])) {
2414 $blockTypes = array(
2418 'openid' => 'OpenID',
2419 'phone_ext' => 'Phone',
2421 foreach ($blockTypes as $blockFieldName => $block) {
2422 if (!array_key_exists($blockFieldName, $values)) {
2426 // block present in value array.
2427 if (!array_key_exists($blockFieldName, $params) ||
!is_array($params[$blockFieldName])) {
2428 $params[$blockFieldName] = array();
2431 if (!array_key_exists($block, $fields)) {
2432 $className = "CRM_Core_DAO_$block";
2433 $fields[$block] = $className::fields();
2436 $blockCnt = count($params[$blockFieldName]);
2438 // copy value to dao field name.
2439 if ($blockFieldName == 'im') {
2440 $values['name'] = $values[$blockFieldName];
2443 _civicrm_api3_store_values($fields[$block], $values,
2444 $params[$blockFieldName][++
$blockCnt]
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));
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;
2456 if (empty($params['id']) && ($blockCnt == 1)) {
2457 $params[$blockFieldName][$blockCnt]['is_primary'] = TRUE;
2460 // we only process single block at a time.
2464 // handle address fields.
2465 if (!array_key_exists('address', $params) ||
!is_array($params['address'])) {
2466 $params['address'] = array();
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)
2480 if (!array_key_exists('Address', $fields)) {
2481 $fields['Address'] = CRM_Core_DAO_Address
::fields();
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');
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;
2500 $htmlType = CRM_Utils_Array
::value('html_type', $customFields[$customFieldID]);
2501 switch ($htmlType) {
2503 case 'AdvMulti-Select':
2504 case 'Multi-Select':
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)))
2514 if ($htmlType == 'CheckBox') {
2515 $newValues[$key][$v2['value']] = 1;
2518 $newValues[$key][] = $v2['value'];
2528 // consider new values
2529 $values = $newValues;
2532 _civicrm_api3_store_values($fields['Address'], $values, $params['address'][$addressCnt]);
2534 $addressFields = array(
2538 'supplemental_address_1',
2539 'supplemental_address_2',
2540 'supplemental_address_3',
2541 'StateProvince.name',
2544 foreach ($addressFields as $field) {
2545 if (array_key_exists($field, $values)) {
2546 if (!array_key_exists('address', $params)) {
2547 $params['address'] = array();
2549 $params['address'][$addressCnt][$field] = $values[$field];
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));
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;
2563 if ($addressCnt == 1) {
2565 $params['address'][$addressCnt]['is_primary'] = TRUE;
2570 if (isset($values['note'])) {
2572 if (!isset($params['note'])) {
2573 $params['note'] = array();
2575 $noteBlock = count($params['note']) +
1;
2577 $params['note'][$noteBlock] = array();
2578 if (!isset($fields['Note'])) {
2579 $fields['Note'] = CRM_Core_DAO_Note
::fields();
2582 // get the current logged in civicrm user
2583 $session = CRM_Core_Session
::singleton();
2584 $userID = $session->get('userID');
2587 $values['contact_id'] = $userID;
2590 _civicrm_api3_store_values($fields['Note'], $values, $params['note'][$noteBlock]);
2595 // Check for custom field values
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
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
2607 if (!array_key_exists($customFieldID, $fields['custom'])) {
2608 return civicrm_api3_create_error('Invalid custom field ID');
2611 $params[$key] = $value;