_mapperKeys = &$mapperKeys; $this->_mapperLocType = &$mapperLocType; $this->_mapperPhoneType = &$mapperPhoneType; $this->_mapperWebsiteType = $mapperWebsiteType; // get IM service provider type id for contact $this->_mapperImProvider = &$mapperImProvider; $this->_mapperRelated = &$mapperRelated; $this->_mapperRelatedContactType = &$mapperRelatedContactType; $this->_mapperRelatedContactDetails = &$mapperRelatedContactDetails; $this->_mapperRelatedContactLocType = &$mapperRelatedContactLocType; $this->_mapperRelatedContactPhoneType = &$mapperRelatedContactPhoneType; $this->_mapperRelatedContactWebsiteType = $mapperRelatedContactWebsiteType; // get IM service provider type id for related contact $this->_mapperRelatedContactImProvider = &$mapperRelatedContactImProvider; } /** * The initializer code, called before processing. */ public function init() { $contactFields = CRM_Contact_BAO_Contact::importableFields($this->_contactType); // exclude the address options disabled in the Address Settings $fields = CRM_Core_BAO_Address::validateAddressOptions($contactFields); //CRM-5125 //supporting import for contact subtypes $csType = NULL; if (!empty($this->_contactSubType)) { //custom fields for sub type $subTypeFields = CRM_Core_BAO_CustomField::getFieldsForImport($this->_contactSubType); if (!empty($subTypeFields)) { foreach ($subTypeFields as $customSubTypeField => $details) { $fields[$customSubTypeField] = $details; } } } //Relationship importables $this->_relationships = $relations = CRM_Contact_BAO_Relationship::getContactRelationshipType( NULL, NULL, NULL, $this->_contactType, FALSE, 'label', TRUE, $this->_contactSubType ); asort($relations); foreach ($relations as $key => $var) { list($type) = explode('_', $key); $relationshipType[$key]['title'] = $var; $relationshipType[$key]['headerPattern'] = '/' . preg_quote($var, '/') . '/'; $relationshipType[$key]['import'] = TRUE; $relationshipType[$key]['relationship_type_id'] = $type; $relationshipType[$key]['related'] = TRUE; } if (!empty($relationshipType)) { $fields = array_merge($fields, array( 'related' => array( 'title' => ts('- related contact info -'), ), ), $relationshipType); } foreach ($fields as $name => $field) { $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)); } $this->_newContacts = array(); $this->setActiveFields($this->_mapperKeys); $this->setActiveFieldLocationTypes($this->_mapperLocType); $this->setActiveFieldPhoneTypes($this->_mapperPhoneType); $this->setActiveFieldWebsiteTypes($this->_mapperWebsiteType); //set active fields of IM provider of contact $this->setActiveFieldImProviders($this->_mapperImProvider); //related info $this->setActiveFieldRelated($this->_mapperRelated); $this->setActiveFieldRelatedContactType($this->_mapperRelatedContactType); $this->setActiveFieldRelatedContactDetails($this->_mapperRelatedContactDetails); $this->setActiveFieldRelatedContactLocType($this->_mapperRelatedContactLocType); $this->setActiveFieldRelatedContactPhoneType($this->_mapperRelatedContactPhoneType); $this->setActiveFieldRelatedContactWebsiteType($this->_mapperRelatedContactWebsiteType); //set active fields of IM provider of related contact $this->setActiveFieldRelatedContactImProvider($this->_mapperRelatedContactImProvider); $this->_phoneIndex = -1; $this->_emailIndex = -1; $this->_firstNameIndex = -1; $this->_lastNameIndex = -1; $this->_householdNameIndex = -1; $this->_organizationNameIndex = -1; $this->_externalIdentifierIndex = -1; $index = 0; foreach ($this->_mapperKeys as $key) { if (substr($key, 0, 5) == 'email' && substr($key, 0, 14) != 'email_greeting') { $this->_emailIndex = $index; $this->_allEmails = array(); } if (substr($key, 0, 5) == 'phone') { $this->_phoneIndex = $index; } if ($key == 'first_name') { $this->_firstNameIndex = $index; } if ($key == 'last_name') { $this->_lastNameIndex = $index; } if ($key == 'household_name') { $this->_householdNameIndex = $index; } if ($key == 'organization_name') { $this->_organizationNameIndex = $index; } if ($key == 'external_identifier') { $this->_externalIdentifierIndex = $index; $this->_allExternalIdentifiers = array(); } $index++; } $this->_updateWithId = FALSE; if (in_array('id', $this->_mapperKeys) || ($this->_externalIdentifierIndex >= 0 && in_array($this->_onDuplicate, array( CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::DUPLICATE_FILL, ))) ) { $this->_updateWithId = TRUE; } $this->_parseStreetAddress = CRM_Utils_Array::value('street_address_parsing', CRM_Core_BAO_Setting::valueOptions(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'address_options'), FALSE); } /** * Handle the values in mapField mode. * * @param array $values * The array of values belonging to this line. * * @return bool */ public function mapField(&$values) { return CRM_Import_Parser::VALID; } /** * Handle the values in preview mode. * * @param array $values * The array of values belonging to this line. * * @return bool * the result of this processing */ public function preview(&$values) { return $this->summary($values); } /** * Handle the values in summary mode. * * @param array $values * The array of values belonging to this line. * * @return bool * the result of this processing */ public function summary(&$values) { $erroneousField = NULL; $response = $this->setActiveFieldValues($values, $erroneousField); $errorMessage = NULL; $errorRequired = FALSE; switch ($this->_contactType) { case 'Individual': $missingNames = array(); if ($this->_firstNameIndex < 0 || empty($values[$this->_firstNameIndex])) { $errorRequired = TRUE; $missingNames[] = ts('First Name'); } if ($this->_lastNameIndex < 0 || empty($values[$this->_lastNameIndex])) { $errorRequired = TRUE; $missingNames[] = ts('Last Name'); } if ($errorRequired) { $and = ' ' . ts('and') . ' '; $errorMessage = ts('Missing required fields:') . ' ' . implode($and, $missingNames); } break; case 'Household': if ($this->_householdNameIndex < 0 || empty($values[$this->_householdNameIndex])) { $errorRequired = TRUE; $errorMessage = ts('Missing required fields:') . ' ' . ts('Household Name'); } break; case 'Organization': if ($this->_organizationNameIndex < 0 || empty($values[$this->_organizationNameIndex])) { $errorRequired = TRUE; $errorMessage = ts('Missing required fields:') . ' ' . ts('Organization Name'); } break; } $statusFieldName = $this->_statusFieldName; if ($this->_emailIndex >= 0) { /* If we don't have the required fields, bail */ if ($this->_contactType == 'Individual' && !$this->_updateWithId) { if ($errorRequired && empty($values[$this->_emailIndex])) { if ($errorMessage) { $errorMessage .= ' ' . ts('OR') . ' ' . ts('Email Address'); } else { $errorMessage = ts('Missing required field:') . ' ' . ts('Email Address'); } array_unshift($values, $errorMessage); $importRecordParams = array( $statusFieldName => 'ERROR', "${statusFieldName}Msg" => $errorMessage, ); $this->updateImportRecord($values[count($values) - 1], $importRecordParams); return CRM_Import_Parser::ERROR; } } $email = CRM_Utils_Array::value($this->_emailIndex, $values); if ($email) { /* If the email address isn't valid, bail */ if (!CRM_Utils_Rule::email($email)) { $errorMessage = ts('Invalid Email address'); array_unshift($values, $errorMessage); $importRecordParams = array( $statusFieldName => 'ERROR', "${statusFieldName}Msg" => $errorMessage, ); $this->updateImportRecord($values[count($values) - 1], $importRecordParams); return CRM_Import_Parser::ERROR; } /* otherwise, count it and move on */ $this->_allEmails[$email] = $this->_lineCount; } } elseif ($errorRequired && !$this->_updateWithId) { if ($errorMessage) { $errorMessage .= ' ' . ts('OR') . ' ' . ts('Email Address'); } else { $errorMessage = ts('Missing required field:') . ' ' . ts('Email Address'); } array_unshift($values, $errorMessage); $importRecordParams = array( $statusFieldName => 'ERROR', "${statusFieldName}Msg" => $errorMessage, ); $this->updateImportRecord($values[count($values) - 1], $importRecordParams); return CRM_Import_Parser::ERROR; } //check for duplicate external Identifier $externalID = CRM_Utils_Array::value($this->_externalIdentifierIndex, $values); if ($externalID) { /* If it's a dupe,external Identifier */ if ($externalDupe = CRM_Utils_Array::value($externalID, $this->_allExternalIdentifiers)) { $errorMessage = ts('External ID conflicts with record %1', array(1 => $externalDupe)); array_unshift($values, $errorMessage); $importRecordParams = array( $statusFieldName => 'ERROR', "${statusFieldName}Msg" => $errorMessage, ); $this->updateImportRecord($values[count($values) - 1], $importRecordParams); return CRM_Import_Parser::ERROR; } //otherwise, count it and move on $this->_allExternalIdentifiers[$externalID] = $this->_lineCount; } //Checking error in custom data $params = &$this->getActiveFieldParams(); $params['contact_type'] = $this->_contactType; //date-format part ends $errorMessage = NULL; //CRM-5125 //add custom fields for contact sub type $csType = NULL; if (!empty($this->_contactSubType)) { $csType = $this->_contactSubType; } //checking error in custom data $this->isErrorInCustomData($params, $errorMessage, $csType, $this->_relationships); //checking error in core data $this->isErrorInCoreData($params, $errorMessage); if ($errorMessage) { $tempMsg = "Invalid value for field(s) : $errorMessage"; // put the error message in the import record in the DB $importRecordParams = array( $statusFieldName => 'ERROR', "${statusFieldName}Msg" => $tempMsg, ); $this->updateImportRecord($values[count($values) - 1], $importRecordParams); array_unshift($values, $tempMsg); $errorMessage = NULL; return CRM_Import_Parser::ERROR; } //if user correcting errors by walking back //need to reset status ERROR msg to null //now currently we are having valid data. $importRecordParams = array( $statusFieldName => 'NEW', ); $this->updateImportRecord($values[count($values) - 1], $importRecordParams); return CRM_Import_Parser::VALID; } /** * Handle the values in import mode. * * @param int $onDuplicate * The code for what action to take on duplicates. * @param array $values * The array of values belonging to this line. * * @param bool $doGeocodeAddress * * @return bool * the result of this processing */ public function import($onDuplicate, &$values, $doGeocodeAddress = FALSE) { $config = CRM_Core_Config::singleton(); $this->_unparsedStreetAddressContacts = array(); if (!$doGeocodeAddress) { // CRM-5854, reset the geocode method to null to prevent geocoding $config->geocodeMethod = ''; } // first make sure this is a valid line //$this->_updateWithId = false; $response = $this->summary($values); $statusFieldName = $this->_statusFieldName; if ($response != CRM_Import_Parser::VALID) { $importRecordParams = array( $statusFieldName => 'INVALID', "${statusFieldName}Msg" => "Invalid (Error Code: $response)", ); $this->updateImportRecord($values[count($values) - 1], $importRecordParams); return $response; } $params = &$this->getActiveFieldParams(); $formatted = array( 'contact_type' => $this->_contactType, ); static $contactFields = NULL; if ($contactFields == NULL) { $contactFields = CRM_Contact_DAO_Contact::import(); } //check if external identifier exists in database if (!empty($params['external_identifier']) && (!empty($params['id']) || in_array($onDuplicate, array( CRM_Import_Parser::DUPLICATE_SKIP, CRM_Import_Parser::DUPLICATE_NOCHECK, ))) ) { if ($internalCid = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $params['external_identifier'], 'id', 'external_identifier', TRUE)) { if ($internalCid != CRM_Utils_Array::value('id', $params)) { $errorMessage = ts('External ID already exists in Database.'); array_unshift($values, $errorMessage); $importRecordParams = array( $statusFieldName => 'ERROR', "${statusFieldName}Msg" => $errorMessage, ); $this->updateImportRecord($values[count($values) - 1], $importRecordParams); return CRM_Import_Parser::DUPLICATE; } } } if (!empty($this->_contactSubType)) { $params['contact_sub_type'] = $this->_contactSubType; } if ($subType = CRM_Utils_Array::value('contact_sub_type', $params)) { if (CRM_Contact_BAO_ContactType::isExtendsContactType($subType, $this->_contactType, FALSE, 'label')) { $subTypes = CRM_Contact_BAO_ContactType::subTypePairs($this->_contactType, FALSE, NULL); $params['contact_sub_type'] = array_search($subType, $subTypes); } elseif (!CRM_Contact_BAO_ContactType::isExtendsContactType($subType, $this->_contactType)) { $message = "Mismatched or Invalid Contact Subtype."; array_unshift($values, $message); return CRM_Import_Parser::NO_MATCH; } } // Get contact id to format common data in update/fill mode, // prioritising a dedupe rule check over an external_identifier check, but falling back on ext id. if ($this->_updateWithId && empty($params['id'])) { $possibleMatches = $this->getPossibleContactMatches($params); foreach ($possibleMatches as $possibleID) { $params['id'] = $formatted['id'] = $possibleID; } } //format common data, CRM-4062 $this->formatCommonData($params, $formatted, $contactFields); $relationship = FALSE; $createNewContact = TRUE; // Support Match and Update Via Contact ID if ($this->_updateWithId && isset($params['id'])) { $createNewContact = FALSE; // @todo - it feels like all the rows from here to the end of the IF // could be removed in favour of a simple check for whether the contact_type & id match // the call to the deprecated function seems to add no value other that to do an additional // check for the contact_id & type. $error = _civicrm_api3_deprecated_duplicate_formatted_contact($formatted); if (CRM_Core_Error::isAPIError($error, CRM_Core_ERROR::DUPLICATE_CONTACT)) { if (is_array($error['error_message']['params'][0])) { $matchedIDs = $error['error_message']['params'][0]; } else { $matchedIDs = explode(',', $error['error_message']['params'][0]); } if (count($matchedIDs) >= 1) { $updateflag = TRUE; foreach ($matchedIDs as $contactId) { if ($params['id'] == $contactId) { $contactType = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $params['id'], 'contact_type'); if ($formatted['contact_type'] == $contactType) { //validation of subtype for update mode //CRM-5125 $contactSubType = NULL; if (!empty($params['contact_sub_type'])) { $contactSubType = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $params['id'], 'contact_sub_type'); } if (!empty($contactSubType) && (!CRM_Contact_BAO_ContactType::isAllowEdit($params['id'], $contactSubType) && $contactSubType != CRM_Utils_Array::value('contact_sub_type', $formatted))) { $message = "Mismatched contact SubTypes :"; array_unshift($values, $message); $updateflag = FALSE; $this->_retCode = CRM_Import_Parser::NO_MATCH; } else { $updateflag = FALSE; $this->_retCode = CRM_Import_Parser::VALID; } } else { $message = "Mismatched contact Types :"; array_unshift($values, $message); $updateflag = FALSE; $this->_retCode = CRM_Import_Parser::NO_MATCH; } } } if ($updateflag) { $message = "Mismatched contact IDs OR Mismatched contact Types :"; array_unshift($values, $message); $this->_retCode = CRM_Import_Parser::NO_MATCH; } } } else { $contactType = NULL; if (!empty($params['id'])) { $contactType = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $params['id'], 'contact_type'); if ($contactType) { if ($formatted['contact_type'] == $contactType) { //validation of subtype for update mode //CRM-5125 $contactSubType = NULL; if (!empty($params['contact_sub_type'])) { $contactSubType = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $params['id'], 'contact_sub_type'); } if (!empty($contactSubType) && (!CRM_Contact_BAO_ContactType::isAllowEdit($params['id'], $contactSubType) && $contactSubType != CRM_Utils_Array::value('contact_sub_type', $formatted))) { $message = "Mismatched contact SubTypes :"; array_unshift($values, $message); $this->_retCode = CRM_Import_Parser::NO_MATCH; } else { $newContact = $this->createContact($formatted, $contactFields, $onDuplicate, $params['id'], FALSE, $this->_dedupeRuleGroupID); $this->_retCode = CRM_Import_Parser::VALID; } } else { $message = "Mismatched contact Types :"; array_unshift($values, $message); $this->_retCode = CRM_Import_Parser::NO_MATCH; } } else { // we should avoid multiple errors for single record // since we have already retCode and we trying to force again. if ($this->_retCode != CRM_Import_Parser::NO_MATCH) { $message = "No contact found for this contact ID:" . $params['id']; array_unshift($values, $message); $this->_retCode = CRM_Import_Parser::NO_MATCH; } } } else { //CRM-4148 //now we want to create new contact on update/fill also. $createNewContact = TRUE; } } if (isset($newContact) && is_a($newContact, 'CRM_Contact_BAO_Contact')) { $relationship = TRUE; } elseif (is_a($error, 'CRM_Core_Error')) { $newContact = $error; $relationship = TRUE; } } //fixed CRM-4148 //now we create new contact in update/fill mode also. $contactID = NULL; if ($createNewContact || ($this->_retCode != CRM_Import_Parser::NO_MATCH && $this->_updateWithId)) { //CRM-4430, don't carry if not submitted. foreach (array('prefix_id', 'suffix_id', 'gender_id') as $name) { if (!empty($formatted[$name])) { $options = CRM_Contact_BAO_Contact::buildOptions($name, 'get'); if (!isset($options[$formatted[$name]])) { $formatted[$name] = CRM_Utils_Array::key((string) $formatted[$name], $options); } } } if ($this->_updateWithId && !empty($params['id'])) { $contactID = $params['id']; } $newContact = $this->createContact($formatted, $contactFields, $onDuplicate, $contactID, TRUE, $this->_dedupeRuleGroupID); } if (isset($newContact) && is_object($newContact) && ($newContact instanceof CRM_Contact_BAO_Contact)) { $relationship = TRUE; $newContact = clone($newContact); $contactID = $newContact->id; $this->_newContacts[] = $contactID; //get return code if we create new contact in update mode, CRM-4148 if ($this->_updateWithId) { $this->_retCode = CRM_Import_Parser::VALID; } } elseif (isset($newContact) && CRM_Core_Error::isAPIError($newContact, CRM_Core_Error::DUPLICATE_CONTACT)) { // if duplicate, no need of further processing if ($onDuplicate == CRM_Import_Parser::DUPLICATE_SKIP) { $errorMessage = "Skipping duplicate record"; array_unshift($values, $errorMessage); $importRecordParams = array( $statusFieldName => 'DUPLICATE', "${statusFieldName}Msg" => $errorMessage, ); $this->updateImportRecord($values[count($values) - 1], $importRecordParams); return CRM_Import_Parser::DUPLICATE; } $relationship = TRUE; // CRM-10433/CRM-20739 - IDs could be string or array; handle accordingly if (!is_array($dupeContactIDs = $newContact['error_message']['params'][0])) { $dupeContactIDs = explode(',', $dupeContactIDs); } $dupeCount = count($dupeContactIDs); $contactID = array_pop($dupeContactIDs); // check to see if we had more than one duplicate contact id. // if we have more than one, the record will be rejected below if ($dupeCount == 1) { // there was only one dupe, we will continue normally... if (!in_array($contactID, $this->_newContacts)) { $this->_newContacts[] = $contactID; } } } if ($contactID) { // call import hook $currentImportID = end($values); $hookParams = array( 'contactID' => $contactID, 'importID' => $currentImportID, 'importTempTable' => $this->_tableName, 'fieldHeaders' => $this->_mapperKeys, 'fields' => $this->_activeFields, ); CRM_Utils_Hook::import('Contact', 'process', $this, $hookParams); } if ($relationship) { $primaryContactId = NULL; if (CRM_Core_Error::isAPIError($newContact, CRM_Core_ERROR::DUPLICATE_CONTACT)) { if ($dupeCount == 1 && CRM_Utils_Rule::integer($contactID)) { $primaryContactId = $contactID; } } else { $primaryContactId = $newContact->id; } if ((CRM_Core_Error::isAPIError($newContact, CRM_Core_ERROR::DUPLICATE_CONTACT) || is_a($newContact, 'CRM_Contact_BAO_Contact')) && $primaryContactId) { //relationship contact insert foreach ($params as $key => $field) { list($id, $first, $second) = CRM_Utils_System::explode('_', $key, 3); if (!($first == 'a' && $second == 'b') && !($first == 'b' && $second == 'a')) { continue; } $relationType = new CRM_Contact_DAO_RelationshipType(); $relationType->id = $id; $relationType->find(TRUE); $direction = "contact_sub_type_$second"; $formatting = array( 'contact_type' => $params[$key]['contact_type'], ); //set subtype for related contact CRM-5125 if (isset($relationType->$direction)) { //validation of related contact subtype for update mode if ($relCsType = CRM_Utils_Array::value('contact_sub_type', $params[$key]) && $relCsType != $relationType->$direction) { $errorMessage = ts("Mismatched or Invalid contact subtype found for this related contact."); array_unshift($values, $errorMessage); return CRM_Import_Parser::NO_MATCH; } else { $formatting['contact_sub_type'] = $relationType->$direction; } } $relationType->free(); $contactFields = NULL; $contactFields = CRM_Contact_DAO_Contact::import(); //Relation on the basis of External Identifier. if (empty($params[$key]['id']) && !empty($params[$key]['external_identifier'])) { $params[$key]['id'] = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $params[$key]['external_identifier'], 'id', 'external_identifier'); } // check for valid related contact id in update/fill mode, CRM-4424 if (in_array($onDuplicate, array( CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::DUPLICATE_FILL, )) && !empty($params[$key]['id']) ) { $relatedContactType = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $params[$key]['id'], 'contact_type'); if (!$relatedContactType) { $errorMessage = ts("No contact found for this related contact ID: %1", array(1 => $params[$key]['id'])); array_unshift($values, $errorMessage); return CRM_Import_Parser::NO_MATCH; } else { //validation of related contact subtype for update mode //CRM-5125 $relatedCsType = NULL; if (!empty($formatting['contact_sub_type'])) { $relatedCsType = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $params[$key]['id'], 'contact_sub_type'); } if (!empty($relatedCsType) && (!CRM_Contact_BAO_ContactType::isAllowEdit($params[$key]['id'], $relatedCsType) && $relatedCsType != CRM_Utils_Array::value('contact_sub_type', $formatting)) ) { $errorMessage = ts("Mismatched or Invalid contact subtype found for this related contact.") . ' ' . ts("ID: %1", array(1 => $params[$key]['id'])); array_unshift($values, $errorMessage); return CRM_Import_Parser::NO_MATCH; } else { // get related contact id to format data in update/fill mode, //if external identifier is present, CRM-4423 $formatting['id'] = $params[$key]['id']; } } } //format common data, CRM-4062 $this->formatCommonData($field, $formatting, $contactFields); //do we have enough fields to create related contact. $allowToCreate = $this->checkRelatedContactFields($key, $formatting); if (!$allowToCreate) { $errorMessage = ts('Related contact required fields are missing.'); array_unshift($values, $errorMessage); return CRM_Import_Parser::NO_MATCH; } //fixed for CRM-4148 if (!empty($params[$key]['id'])) { $contact = array( 'contact_id' => $params[$key]['id'], ); $defaults = array(); $relatedNewContact = CRM_Contact_BAO_Contact::retrieve($contact, $defaults); } else { $relatedNewContact = $this->createContact($formatting, $contactFields, $onDuplicate, NULL, FALSE); } if (is_object($relatedNewContact) || ($relatedNewContact instanceof CRM_Contact_BAO_Contact)) { $relatedNewContact = clone($relatedNewContact); } $matchedIDs = array(); // To update/fill contact, get the matching contact Ids if duplicate contact found // otherwise get contact Id from object of related contact if (is_array($relatedNewContact) && civicrm_error($relatedNewContact)) { if (CRM_Core_Error::isAPIError($relatedNewContact, CRM_Core_ERROR::DUPLICATE_CONTACT)) { $matchedIDs = $relatedNewContact['error_message']['params'][0]; if (!is_array($matchedIDs)) { $matchedIDs = explode(',', $matchedIDs); } } else { $errorMessage = $relatedNewContact['error_message']; array_unshift($values, $errorMessage); $importRecordParams = array( $statusFieldName => 'ERROR', "${statusFieldName}Msg" => $errorMessage, ); $this->updateImportRecord($values[count($values) - 1], $importRecordParams); return CRM_Import_Parser::ERROR; } } else { $matchedIDs[] = $relatedNewContact->id; } // update/fill related contact after getting matching Contact Ids, CRM-4424 if (in_array($onDuplicate, array( CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::DUPLICATE_FILL, ))) { //validation of related contact subtype for update mode //CRM-5125 $relatedCsType = NULL; if (!empty($formatting['contact_sub_type'])) { $relatedCsType = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $matchedIDs[0], 'contact_sub_type'); } if (!empty($relatedCsType) && (!CRM_Contact_BAO_ContactType::isAllowEdit($matchedIDs[0], $relatedCsType) && $relatedCsType != CRM_Utils_Array::value('contact_sub_type', $formatting))) { $errorMessage = ts("Mismatched or Invalid contact subtype found for this related contact."); array_unshift($values, $errorMessage); return CRM_Import_Parser::NO_MATCH; } else { $updatedContact = $this->createContact($formatting, $contactFields, $onDuplicate, $matchedIDs[0]); } } static $relativeContact = array(); if (CRM_Core_Error::isAPIError($relatedNewContact, CRM_Core_ERROR::DUPLICATE_CONTACT)) { if (count($matchedIDs) >= 1) { $relContactId = $matchedIDs[0]; //add relative contact to count during update & fill mode. //logic to make count distinct by contact id. if ($this->_newRelatedContacts || !empty($relativeContact)) { $reContact = array_keys($relativeContact, $relContactId); if (empty($reContact)) { $this->_newRelatedContacts[] = $relativeContact[] = $relContactId; } } else { $this->_newRelatedContacts[] = $relativeContact[] = $relContactId; } } } else { $relContactId = $relatedNewContact->id; $this->_newRelatedContacts[] = $relativeContact[] = $relContactId; } if (CRM_Core_Error::isAPIError($relatedNewContact, CRM_Core_ERROR::DUPLICATE_CONTACT) || ($relatedNewContact instanceof CRM_Contact_BAO_Contact)) { //fix for CRM-1993.Checks for duplicate related contacts if (count($matchedIDs) >= 1) { //if more than one duplicate contact //found, create relationship with first contact // now create the relationship record $relationParams = array(); $relationParams = array( 'relationship_type_id' => $key, 'contact_check' => array( $relContactId => 1, ), 'is_active' => 1, 'skipRecentView' => TRUE, ); // we only handle related contact success, we ignore failures for now // at some point wold be nice to have related counts as separate $relationIds = array( 'contact' => $primaryContactId, ); list($valid, $invalid, $duplicate, $saved, $relationshipIds) = CRM_Contact_BAO_Relationship::legacyCreateMultiple($relationParams, $relationIds); if ($valid || $duplicate) { $relationIds['contactTarget'] = $relContactId; $action = ($duplicate) ? CRM_Core_Action::UPDATE : CRM_Core_Action::ADD; CRM_Contact_BAO_Relationship::relatedMemberships($primaryContactId, $relationParams, $relationIds, $action); } //handle current employer, CRM-3532 if ($valid) { $allRelationships = CRM_Core_PseudoConstant::relationshipType('name'); $relationshipTypeId = str_replace(array( '_a_b', '_b_a', ), array( '', '', ), $key); $relationshipType = str_replace($relationshipTypeId . '_', '', $key); $orgId = $individualId = NULL; if ($allRelationships[$relationshipTypeId]["name_{$relationshipType}"] == 'Employee of') { $orgId = $relContactId; $individualId = $primaryContactId; } elseif ($allRelationships[$relationshipTypeId]["name_{$relationshipType}"] == 'Employer of') { $orgId = $primaryContactId; $individualId = $relContactId; } if ($orgId && $individualId) { $currentEmpParams[$individualId] = $orgId; CRM_Contact_BAO_Contact_Utils::setCurrentEmployer($currentEmpParams); } } } } } } } if ($this->_updateWithId) { //return warning if street address is unparsed, CRM-5886 return $this->processMessage($values, $statusFieldName, $this->_retCode); } //dupe checking if (is_array($newContact) && civicrm_error($newContact)) { $code = NULL; if (($code = CRM_Utils_Array::value('code', $newContact['error_message'])) && ($code == CRM_Core_Error::DUPLICATE_CONTACT)) { $urls = array(); // need to fix at some stage and decide if the error will return an // array or string, crude hack for now if (is_array($newContact['error_message']['params'][0])) { $cids = $newContact['error_message']['params'][0]; } else { $cids = explode(',', $newContact['error_message']['params'][0]); } foreach ($cids as $cid) { $urls[] = CRM_Utils_System::url('civicrm/contact/view', 'reset=1&cid=' . $cid, TRUE); } $url_string = implode("\n", $urls); // If we duplicate more than one record, skip no matter what if (count($cids) > 1) { $errorMessage = ts('Record duplicates multiple contacts'); $importRecordParams = array( $statusFieldName => 'ERROR', "${statusFieldName}Msg" => $errorMessage, ); //combine error msg to avoid mismatch between error file columns. $errorMessage .= "\n" . $url_string; array_unshift($values, $errorMessage); $this->updateImportRecord($values[count($values) - 1], $importRecordParams); return CRM_Import_Parser::ERROR; } // Params only had one id, so shift it out $contactId = array_shift($cids); $cid = NULL; $vals = array('contact_id' => $contactId); if ($onDuplicate == CRM_Import_Parser::DUPLICATE_REPLACE) { civicrm_api('contact', 'delete', $vals); $cid = CRM_Contact_BAO_Contact::createProfileContact($formatted, $contactFields, $contactId, NULL, NULL, $formatted['contact_type']); } elseif ($onDuplicate == CRM_Import_Parser::DUPLICATE_UPDATE) { $newContact = $this->createContact($formatted, $contactFields, $onDuplicate, $contactId); } elseif ($onDuplicate == CRM_Import_Parser::DUPLICATE_FILL) { $newContact = $this->createContact($formatted, $contactFields, $onDuplicate, $contactId); } // else skip does nothing and just returns an error code. if ($cid) { $contact = array( 'contact_id' => $cid, ); $defaults = array(); $newContact = CRM_Contact_BAO_Contact::retrieve($contact, $defaults); } if (civicrm_error($newContact)) { if (empty($newContact['error_message']['params'])) { // different kind of error other than DUPLICATE $errorMessage = $newContact['error_message']; array_unshift($values, $errorMessage); $importRecordParams = array( $statusFieldName => 'ERROR', "${statusFieldName}Msg" => $errorMessage, ); $this->updateImportRecord($values[count($values) - 1], $importRecordParams); return CRM_Import_Parser::ERROR; } $contactID = $newContact['error_message']['params'][0]; if (is_array($contactID)) { $contactID = array_pop($contactID); } if (!in_array($contactID, $this->_newContacts)) { $this->_newContacts[] = $contactID; } } //CRM-262 No Duplicate Checking if ($onDuplicate == CRM_Import_Parser::DUPLICATE_SKIP) { array_unshift($values, $url_string); $importRecordParams = array( $statusFieldName => 'DUPLICATE', "${statusFieldName}Msg" => "Skipping duplicate record", ); $this->updateImportRecord($values[count($values) - 1], $importRecordParams); return CRM_Import_Parser::DUPLICATE; } $importRecordParams = array( $statusFieldName => 'IMPORTED', ); $this->updateImportRecord($values[count($values) - 1], $importRecordParams); //return warning if street address is not parsed, CRM-5886 return $this->processMessage($values, $statusFieldName, CRM_Import_Parser::VALID); } else { // Not a dupe, so we had an error $errorMessage = $newContact['error_message']; array_unshift($values, $errorMessage); $importRecordParams = array( $statusFieldName => 'ERROR', "${statusFieldName}Msg" => $errorMessage, ); $this->updateImportRecord($values[count($values) - 1], $importRecordParams); return CRM_Import_Parser::ERROR; } } // sleep(3); return $this->processMessage($values, $statusFieldName, CRM_Import_Parser::VALID); } /** * Get the array of successfully imported contact id's * * @return array */ public function &getImportedContacts() { return $this->_newContacts; } /** * Get the array of successfully imported related contact id's * * @return array */ public function &getRelatedImportedContacts() { return $this->_newRelatedContacts; } /** * The initializer code, called before the processing. */ public function fini() { } /** * Check if an error in custom data. * * @param array $params * @param string $errorMessage * A string containing all the error-fields. * * @param null $csType * @param null $relationships */ public static function isErrorInCustomData($params, &$errorMessage, $csType = NULL, $relationships = NULL) { $session = CRM_Core_Session::singleton(); $dateType = $session->get("dateTypes"); if (!empty($params['contact_sub_type'])) { $csType = CRM_Utils_Array::value('contact_sub_type', $params); } if (empty($params['contact_type'])) { $params['contact_type'] = 'Individual'; } // get array of subtypes - CRM-18708 if (in_array($csType, array('Individual', 'Organization', 'Household'))) { $csType = self::getSubtypes($params['contact_type']); } if (is_array($csType)) { // fetch custom fields for every subtype and add it to $customFields array // CRM-18708 $customFields = array(); foreach ($csType as $cType) { $customFields += CRM_Core_BAO_CustomField::getFields($params['contact_type'], FALSE, FALSE, $cType); } } else { $customFields = CRM_Core_BAO_CustomField::getFields($params['contact_type'], FALSE, FALSE, $csType); } $addressCustomFields = CRM_Core_BAO_CustomField::getFields('Address'); $customFields = $customFields + $addressCustomFields; foreach ($params as $key => $value) { if ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) { /* check if it's a valid custom field id */ if (!array_key_exists($customFieldID, $customFields)) { self::addToErrorMsg(ts('field ID'), $errorMessage); } // validate null values for required custom fields of type boolean if (!empty($customFields[$customFieldID]['is_required']) && (empty($params['custom_' . $customFieldID]) && !is_numeric($params['custom_' . $customFieldID])) && $customFields[$customFieldID]['data_type'] == 'Boolean') { self::addToErrorMsg($customFields[$customFieldID]['label'] . '::' . $customFields[$customFieldID]['groupTitle'], $errorMessage); } //For address custom fields, we do get actual custom field value as an inner array of //values so need to modify if (array_key_exists($customFieldID, $addressCustomFields)) { $value = $value[0][$key]; } /* validate the data against the CF type */ if ($value) { if ($customFields[$customFieldID]['data_type'] == 'Date') { if (array_key_exists($customFieldID, $addressCustomFields) && CRM_Utils_Date::convertToDefaultDate($params[$key][0], $dateType, $key)) { $value = $params[$key][0][$key]; } elseif (CRM_Utils_Date::convertToDefaultDate($params, $dateType, $key)) { $value = $params[$key]; } else { self::addToErrorMsg($customFields[$customFieldID]['label'], $errorMessage); } } elseif ($customFields[$customFieldID]['data_type'] == 'Boolean') { if (CRM_Utils_String::strtoboolstr($value) === FALSE) { self::addToErrorMsg($customFields[$customFieldID]['label'] . '::' . $customFields[$customFieldID]['groupTitle'], $errorMessage); } } // need not check for label filed import $htmlType = array( 'CheckBox', 'Multi-Select', 'AdvMulti-Select', 'Select', 'Radio', 'Multi-Select State/Province', 'Multi-Select Country', ); if (!in_array($customFields[$customFieldID]['html_type'], $htmlType) || $customFields[$customFieldID]['data_type'] == 'Boolean' || $customFields[$customFieldID]['data_type'] == 'ContactReference') { $valid = CRM_Core_BAO_CustomValue::typecheck($customFields[$customFieldID]['data_type'], $value); if (!$valid) { self::addToErrorMsg($customFields[$customFieldID]['label'], $errorMessage); } } // check for values for custom fields for checkboxes and multiselect if ($customFields[$customFieldID]['html_type'] == 'CheckBox' || $customFields[$customFieldID]['html_type'] == 'AdvMulti-Select' || $customFields[$customFieldID]['html_type'] == 'Multi-Select') { $value = trim($value); $value = str_replace('|', ',', $value); $mulValues = explode(',', $value); $customOption = CRM_Core_BAO_CustomOption::getCustomOption($customFieldID, TRUE); foreach ($mulValues as $v1) { if (strlen($v1) == 0) { continue; } $flag = FALSE; foreach ($customOption as $v2) { if ((strtolower(trim($v2['label'])) == strtolower(trim($v1))) || (strtolower(trim($v2['value'])) == strtolower(trim($v1)))) { $flag = TRUE; } } if (!$flag) { self::addToErrorMsg($customFields[$customFieldID]['label'], $errorMessage); } } } elseif ($customFields[$customFieldID]['html_type'] == 'Select' || ($customFields[$customFieldID]['html_type'] == 'Radio' && $customFields[$customFieldID]['data_type'] != 'Boolean')) { $customOption = CRM_Core_BAO_CustomOption::getCustomOption($customFieldID, TRUE); $flag = FALSE; foreach ($customOption as $v2) { if ((strtolower(trim($v2['label'])) == strtolower(trim($value))) || (strtolower(trim($v2['value'])) == strtolower(trim($value)))) { $flag = TRUE; } } if (!$flag) { self::addToErrorMsg($customFields[$customFieldID]['label'], $errorMessage); } } elseif ($customFields[$customFieldID]['html_type'] == 'Multi-Select State/Province') { $mulValues = explode(',', $value); foreach ($mulValues as $stateValue) { if ($stateValue) { if (self::in_value(trim($stateValue), CRM_Core_PseudoConstant::stateProvinceAbbreviation()) || self::in_value(trim($stateValue), CRM_Core_PseudoConstant::stateProvince())) { continue; } else { self::addToErrorMsg($customFields[$customFieldID]['label'], $errorMessage); } } } } elseif ($customFields[$customFieldID]['html_type'] == 'Multi-Select Country') { $mulValues = explode(',', $value); foreach ($mulValues as $countryValue) { if ($countryValue) { CRM_Core_PseudoConstant::populate($countryNames, 'CRM_Core_DAO_Country', TRUE, 'name', 'is_active'); CRM_Core_PseudoConstant::populate($countryIsoCodes, 'CRM_Core_DAO_Country', TRUE, 'iso_code'); $limitCodes = CRM_Core_BAO_Country::countryLimit(); $error = TRUE; foreach (array( $countryNames, $countryIsoCodes, $limitCodes, ) as $values) { if (in_array(trim($countryValue), $values)) { $error = FALSE; break; } } if ($error) { self::addToErrorMsg($customFields[$customFieldID]['label'], $errorMessage); } } } } } } elseif (is_array($params[$key]) && isset($params[$key]["contact_type"])) { //CRM-5125 //supporting custom data of related contact subtypes $relation = NULL; if ($relationships) { if (array_key_exists($key, $relationships)) { $relation = $key; } elseif (CRM_Utils_Array::key($key, $relationships)) { $relation = CRM_Utils_Array::key($key, $relationships); } } if (!empty($relation)) { list($id, $first, $second) = CRM_Utils_System::explode('_', $relation, 3); $direction = "contact_sub_type_$second"; $relationshipType = new CRM_Contact_BAO_RelationshipType(); $relationshipType->id = $id; if ($relationshipType->find(TRUE)) { if (isset($relationshipType->$direction)) { $params[$key]['contact_sub_type'] = $relationshipType->$direction; } } $relationshipType->free(); } self::isErrorInCustomData($params[$key], $errorMessage, $csType, $relationships); } } } /** * Check if value present in all genders or. * as a substring of any gender value, if yes than return corresponding gender. * eg value might be m/M, ma/MA, mal/MAL, male return 'Male' * but if value is 'maleabc' than return false * * @param string $gender * Check this value across gender values. * * retunr gender value / false * * @return bool */ public function checkGender($gender) { $gender = trim($gender, '.'); if (!$gender) { return FALSE; } $allGenders = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'gender_id'); foreach ($allGenders as $key => $value) { if (strlen($gender) > strlen($value)) { continue; } if ($gender == $value) { return $value; } if (substr_compare($value, $gender, 0, strlen($gender), TRUE) === 0) { return $value; } } return FALSE; } /** * Check if an error in Core( non-custom fields ) field * * @param array $params * @param string $errorMessage * A string containing all the error-fields. */ public function isErrorInCoreData($params, &$errorMessage) { foreach ($params as $key => $value) { if ($value) { $session = CRM_Core_Session::singleton(); $dateType = $session->get("dateTypes"); switch ($key) { case 'birth_date': if (CRM_Utils_Date::convertToDefaultDate($params, $dateType, $key)) { if (!CRM_Utils_Rule::date($params[$key])) { self::addToErrorMsg(ts('Birth Date'), $errorMessage); } } else { self::addToErrorMsg(ts('Birth-Date'), $errorMessage); } break; case 'deceased_date': if (CRM_Utils_Date::convertToDefaultDate($params, $dateType, $key)) { if (!CRM_Utils_Rule::date($params[$key])) { self::addToErrorMsg(ts('Deceased Date'), $errorMessage); } } else { self::addToErrorMsg(ts('Deceased Date'), $errorMessage); } break; case 'is_deceased': if (CRM_Utils_String::strtoboolstr($value) === FALSE) { self::addToErrorMsg(ts('Deceased'), $errorMessage); } break; case 'gender': case 'gender_id': if (!self::checkGender($value)) { self::addToErrorMsg(ts('Gender'), $errorMessage); } break; case 'preferred_communication_method': $preffComm = array(); $preffComm = explode(',', $value); foreach ($preffComm as $v) { if (!self::in_value(trim($v), CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'preferred_communication_method'))) { self::addToErrorMsg(ts('Preferred Communication Method'), $errorMessage); } } break; case 'preferred_mail_format': if (!array_key_exists(strtolower($value), array_change_key_case(CRM_Core_SelectValues::pmf(), CASE_LOWER))) { self::addToErrorMsg(ts('Preferred Mail Format'), $errorMessage); } break; case 'individual_prefix': case 'prefix_id': if (!self::in_value($value, CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'prefix_id'))) { self::addToErrorMsg(ts('Individual Prefix'), $errorMessage); } break; case 'individual_suffix': case 'suffix_id': if (!self::in_value($value, CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'suffix_id'))) { self::addToErrorMsg(ts('Individual Suffix'), $errorMessage); } break; case 'state_province': if (!empty($value)) { foreach ($value as $stateValue) { if ($stateValue['state_province']) { if (self::in_value($stateValue['state_province'], CRM_Core_PseudoConstant::stateProvinceAbbreviation()) || self::in_value($stateValue['state_province'], CRM_Core_PseudoConstant::stateProvince()) ) { continue; } else { self::addToErrorMsg(ts('State/Province'), $errorMessage); } } } } break; case 'country': if (!empty($value)) { foreach ($value as $stateValue) { if ($stateValue['country']) { CRM_Core_PseudoConstant::populate($countryNames, 'CRM_Core_DAO_Country', TRUE, 'name', 'is_active'); CRM_Core_PseudoConstant::populate($countryIsoCodes, 'CRM_Core_DAO_Country', TRUE, 'iso_code'); $limitCodes = CRM_Core_BAO_Country::countryLimit(); //If no country is selected in //localization then take all countries if (empty($limitCodes)) { $limitCodes = $countryIsoCodes; } if (self::in_value($stateValue['country'], $limitCodes) || self::in_value($stateValue['country'], CRM_Core_PseudoConstant::country())) { continue; } else { if (self::in_value($stateValue['country'], $countryIsoCodes) || self::in_value($stateValue['country'], $countryNames)) { 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); } else { 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); } } } } } break; case 'county': if (!empty($value)) { foreach ($value as $county) { if ($county['county']) { $countyNames = CRM_Core_PseudoConstant::county(); if (!empty($county['county']) && !in_array($county['county'], $countyNames)) { 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); } } } } break; case 'geo_code_1': if (!empty($value)) { foreach ($value as $codeValue) { if (!empty($codeValue['geo_code_1'])) { if (CRM_Utils_Rule::numeric($codeValue['geo_code_1'])) { continue; } else { self::addToErrorMsg(ts('Geo code 1'), $errorMessage); } } } } break; case 'geo_code_2': if (!empty($value)) { foreach ($value as $codeValue) { if (!empty($codeValue['geo_code_2'])) { if (CRM_Utils_Rule::numeric($codeValue['geo_code_2'])) { continue; } else { self::addToErrorMsg(ts('Geo code 2'), $errorMessage); } } } } break; //check for any error in email/postal greeting, addressee, //custom email/postal greeting, custom addressee, CRM-4575 case 'email_greeting': $emailGreetingFilter = array( 'contact_type' => $this->_contactType, 'greeting_type' => 'email_greeting', ); if (!self::in_value($value, CRM_Core_PseudoConstant::greeting($emailGreetingFilter))) { 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); } break; case 'postal_greeting': $postalGreetingFilter = array( 'contact_type' => $this->_contactType, 'greeting_type' => 'postal_greeting', ); if (!self::in_value($value, CRM_Core_PseudoConstant::greeting($postalGreetingFilter))) { 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); } break; case 'addressee': $addresseeFilter = array( 'contact_type' => $this->_contactType, 'greeting_type' => 'addressee', ); if (!self::in_value($value, CRM_Core_PseudoConstant::greeting($addresseeFilter))) { self::addToErrorMsg(ts('Addressee must be one of the configured format options. Check Administer >> System Settings >> Option Groups >> Addressee for valid values'), $errorMessage); } break; case 'email_greeting_custom': if (array_key_exists('email_greeting', $params)) { $emailGreetingLabel = key(CRM_Core_OptionGroup::values('email_greeting', TRUE, NULL, NULL, 'AND v.name = "Customized"')); if (CRM_Utils_Array::value('email_greeting', $params) != $emailGreetingLabel) { self::addToErrorMsg(ts('Email Greeting - Custom'), $errorMessage); } } break; case 'postal_greeting_custom': if (array_key_exists('postal_greeting', $params)) { $postalGreetingLabel = key(CRM_Core_OptionGroup::values('postal_greeting', TRUE, NULL, NULL, 'AND v.name = "Customized"')); if (CRM_Utils_Array::value('postal_greeting', $params) != $postalGreetingLabel) { self::addToErrorMsg(ts('Postal Greeting - Custom'), $errorMessage); } } break; case 'addressee_custom': if (array_key_exists('addressee', $params)) { $addresseeLabel = key(CRM_Core_OptionGroup::values('addressee', TRUE, NULL, NULL, 'AND v.name = "Customized"')); if (CRM_Utils_Array::value('addressee', $params) != $addresseeLabel) { self::addToErrorMsg(ts('Addressee - Custom'), $errorMessage); } } break; case 'url': if (is_array($value)) { foreach ($value as $values) { if (!empty($values['url']) && !CRM_Utils_Rule::url($values['url'])) { self::addToErrorMsg(ts('Website'), $errorMessage); break; } } } break; case 'do_not_email': case 'do_not_phone': case 'do_not_mail': case 'do_not_sms': case 'do_not_trade': if (CRM_Utils_Rule::boolean($value) == FALSE) { $key = ucwords(str_replace("_", " ", $key)); self::addToErrorMsg($key, $errorMessage); } break; case 'email': if (is_array($value)) { foreach ($value as $values) { if (!empty($values['email']) && !CRM_Utils_Rule::email($values['email'])) { self::addToErrorMsg($key, $errorMessage); break; } } } break; default: if (is_array($params[$key]) && isset($params[$key]["contact_type"])) { //check for any relationship data ,FIX ME self::isErrorInCoreData($params[$key], $errorMessage); } } } } } /** * Ckeck a value present or not in a array. * * @param $value * @param $valueArray * * @return bool */ public static function in_value($value, $valueArray) { foreach ($valueArray as $key => $v) { //fix for CRM-1514 if (strtolower(trim($v, ".")) == strtolower(trim($value, "."))) { return TRUE; } } return FALSE; } /** * Build error-message containing error-fields * * @param string $errorName * A string containing error-field name. * @param string $errorMessage * A string containing all the error-fields, where the new errorName is concatenated. * */ public static function addToErrorMsg($errorName, &$errorMessage) { if ($errorMessage) { $errorMessage .= "; $errorName"; } else { $errorMessage = $errorName; } } /** * Method for creating contact. * * @param array $formatted * @param array $contactFields * @param int $onDuplicate * @param int $contactId * @param bool $requiredCheck * @param int $dedupeRuleGroupID * * @return array|bool|\CRM_Contact_BAO_Contact|\CRM_Core_Error|null */ public function createContact(&$formatted, &$contactFields, $onDuplicate, $contactId = NULL, $requiredCheck = TRUE, $dedupeRuleGroupID = NULL) { $dupeCheck = FALSE; $newContact = NULL; if (is_null($contactId) && ($onDuplicate != CRM_Import_Parser::DUPLICATE_NOCHECK)) { $dupeCheck = (bool) ($onDuplicate); } //get the prefix id etc if exists CRM_Contact_BAO_Contact::resolveDefaults($formatted, TRUE); //@todo direct call to API function not supported. // setting required check to false, CRM-2839 // plus we do our own required check in import $error = _civicrm_api3_deprecated_contact_check_params($formatted, $dupeCheck, $dedupeRuleGroupID); if ((is_null($error)) && (civicrm_error(_civicrm_api3_deprecated_validate_formatted_contact($formatted)))) { $error = _civicrm_api3_deprecated_validate_formatted_contact($formatted); } $newContact = $error; if (is_null($error)) { if ($contactId) { $this->formatParams($formatted, $onDuplicate, (int) $contactId); } // pass doNotResetCache flag since resetting and rebuilding cache could be expensive. $config = CRM_Core_Config::singleton(); $config->doNotResetCache = 1; $cid = CRM_Contact_BAO_Contact::createProfileContact($formatted, $contactFields, $contactId, NULL, NULL, $formatted['contact_type']); $config->doNotResetCache = 0; $contact = array( 'contact_id' => $cid, ); $defaults = array(); $newContact = CRM_Contact_BAO_Contact::retrieve($contact, $defaults); } //get the id of the contact whose street address is not parsable, CRM-5886 if ($this->_parseStreetAddress && is_object($newContact) && property_exists($newContact, 'address') && $newContact->address) { foreach ($newContact->address as $address) { if (!empty($address['street_address']) && (empty($address['street_number']) || empty($address['street_name']))) { $this->_unparsedStreetAddressContacts[] = array( 'id' => $newContact->id, 'streetAddress' => $address['street_address'], ); } } } return $newContact; } /** * Format params for update and fill mode. * * @param array $params * reference to an array containing all the. * values for import * @param int $onDuplicate * @param int $cid * contact id. */ public function formatParams(&$params, $onDuplicate, $cid) { if ($onDuplicate == CRM_Import_Parser::DUPLICATE_SKIP) { return; } $contactParams = array( 'contact_id' => $cid, ); $defaults = array(); $contactObj = CRM_Contact_BAO_Contact::retrieve($contactParams, $defaults); $modeUpdate = $modeFill = FALSE; if ($onDuplicate == CRM_Import_Parser::DUPLICATE_UPDATE) { $modeUpdate = TRUE; } if ($onDuplicate == CRM_Import_Parser::DUPLICATE_FILL) { $modeFill = TRUE; } $groupTree = CRM_Core_BAO_CustomGroup::getTree($params['contact_type'], NULL, $cid, 0, NULL); CRM_Core_BAO_CustomGroup::setDefaults($groupTree, $defaults, FALSE, FALSE); $locationFields = array( 'email' => 'email', 'phone' => 'phone', 'im' => 'name', 'website' => 'website', 'address' => 'address', ); $contact = get_object_vars($contactObj); foreach ($params as $key => $value) { if ($key == 'id' || $key == 'contact_type') { continue; } if (array_key_exists($key, $locationFields)) { continue; } elseif (in_array($key, array( 'email_greeting', 'postal_greeting', 'addressee', ))) { // CRM-4575, need to null custom if ($params["{$key}_id"] != 4) { $params["{$key}_custom"] = 'null'; } unset($params[$key]); } elseif ($customFieldId = CRM_Core_BAO_CustomField::getKeyID($key)) { $custom = TRUE; } else { $getValue = CRM_Utils_Array::retrieveValueRecursive($contact, $key); if ($key == 'contact_source') { $params['source'] = $params[$key]; unset($params[$key]); } if ($modeFill && isset($getValue)) { unset($params[$key]); } } } foreach ($locationFields as $locKeys) { if (is_array(CRM_Utils_Array::value($locKeys, $params))) { foreach ($params[$locKeys] as $key => $value) { if ($modeFill) { $getValue = CRM_Utils_Array::retrieveValueRecursive($contact, $locKeys); if (isset($getValue)) { foreach ($getValue as $cnt => $values) { if ($locKeys == 'website') { if (($getValue[$cnt]['website_type_id'] == $params[$locKeys][$key]['website_type_id'])) { unset($params[$locKeys][$key]); } } else { 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']) { unset($params[$locKeys][$key]); } } } } } } if (count($params[$locKeys]) == 0) { unset($params[$locKeys]); } } } } /** * Convert any given date string to default date array. * * @param array $params * Has given date-format. * @param array $formatted * Store formatted date in this array. * @param int $dateType * Type of date. * @param string $dateParam * Index of params. */ public static function formatCustomDate(&$params, &$formatted, $dateType, $dateParam) { //fix for CRM-2687 CRM_Utils_Date::convertToDefaultDate($params, $dateType, $dateParam); $formatted[$dateParam] = CRM_Utils_Date::processDate($params[$dateParam]); } /** * Format common params data to proper format to store. * * @param array $params * Contain record values. * @param array $formatted * Array of formatted data. * @param array $contactFields * Contact DAO fields. */ public function formatCommonData($params, &$formatted, &$contactFields) { $csType = array( CRM_Utils_Array::value('contact_type', $formatted), ); //CRM-5125 //add custom fields for contact sub type if (!empty($this->_contactSubType)) { $csType = $this->_contactSubType; } if ($relCsType = CRM_Utils_Array::value('contact_sub_type', $formatted)) { $csType = $relCsType; } $customFields = CRM_Core_BAO_CustomField::getFields($formatted['contact_type'], FALSE, FALSE, $csType); $addressCustomFields = CRM_Core_BAO_CustomField::getFields('Address'); $customFields = $customFields + $addressCustomFields; //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 $elements = array( 'email_greeting_custom' => 'email_greeting', 'postal_greeting_custom' => 'postal_greeting', 'addressee_custom' => 'addressee', ); foreach ($elements as $k => $v) { if (array_key_exists($k, $params) && !(array_key_exists($v, $params))) { $label = key(CRM_Core_OptionGroup::values($v, TRUE, NULL, NULL, 'AND v.name = "Customized"')); $params[$v] = $label; } } //format date first $session = CRM_Core_Session::singleton(); $dateType = $session->get("dateTypes"); foreach ($params as $key => $val) { $customFieldID = CRM_Core_BAO_CustomField::getKeyID($key); if ($customFieldID && !array_key_exists($customFieldID, $addressCustomFields) ) { //we should not update Date to null, CRM-4062 if ($val && ($customFields[$customFieldID]['data_type'] == 'Date')) { self::formatCustomDate($params, $formatted, $dateType, $key); } elseif ($customFields[$customFieldID]['data_type'] == 'Boolean') { if (empty($val) && !is_numeric($val) && $this->_onDuplicate == CRM_Import_Parser::DUPLICATE_FILL) { //retain earlier value when Import mode is `Fill` unset($params[$key]); } else { $params[$key] = CRM_Utils_String::strtoboolstr($val); } } } if ($key == 'birth_date' && $val) { CRM_Utils_Date::convertToDefaultDate($params, $dateType, $key); } elseif ($key == 'deceased_date' && $val) { CRM_Utils_Date::convertToDefaultDate($params, $dateType, $key); $params['is_deceased'] = 1; } elseif ($key == 'is_deceased' && $val) { $params[$key] = CRM_Utils_String::strtoboolstr($val); } elseif ($key == 'gender') { //CRM-4360 $params[$key] = $this->checkGender($val); } } //now format custom data. foreach ($params as $key => $field) { if (is_array($field)) { $isAddressCustomField = FALSE; foreach ($field as $value) { $break = FALSE; if (is_array($value)) { foreach ($value as $name => $testForEmpty) { if ($addressCustomFieldID = CRM_Core_BAO_CustomField::getKeyID($name)) { $isAddressCustomField = TRUE; break; } // check if $value does not contain IM provider or phoneType if (($name !== 'phone_type_id' || $name !== 'provider_id') && ($testForEmpty === '' || $testForEmpty == NULL)) { $break = TRUE; break; } } } else { $break = TRUE; } if (!$break) { $this->formatContactParameters($value, $formatted); } } if (!$isAddressCustomField) { continue; } } $formatValues = array( $key => $field, ); if (($key !== 'preferred_communication_method') && (array_key_exists($key, $contactFields))) { // due to merging of individual table and // contact table, we need to avoid // preferred_communication_method forcefully $formatValues['contact_type'] = $formatted['contact_type']; } if ($key == 'id' && isset($field)) { $formatted[$key] = $field; } $this->formatContactParameters($formatValues, $formatted); //Handling Custom Data // note: Address custom fields will be handled separately inside formatContactParameters if (($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) && array_key_exists($customFieldID, $customFields) && !array_key_exists($customFieldID, $addressCustomFields) ) { $extends = CRM_Utils_Array::value('extends', $customFields[$customFieldID]); $htmlType = CRM_Utils_Array::value('html_type', $customFields[$customFieldID]); switch ($htmlType) { case 'Select': case 'Radio': case 'Autocomplete-Select': if ($customFields[$customFieldID]['data_type'] == 'String' || $customFields[$customFieldID]['data_type'] == 'Int') { $customOption = CRM_Core_BAO_CustomOption::getCustomOption($customFieldID, TRUE); foreach ($customOption as $customValue) { $val = CRM_Utils_Array::value('value', $customValue); $label = CRM_Utils_Array::value('label', $customValue); $label = strtolower($label); $value = strtolower(trim($formatted[$key])); if (($value == $label) || ($value == strtolower($val))) { $params[$key] = $formatted[$key] = $val; } } } break; case 'CheckBox': case 'AdvMulti-Select': case 'Multi-Select': if (!empty($formatted[$key]) && !empty($params[$key])) { $mulValues = explode(',', $formatted[$key]); $customOption = CRM_Core_BAO_CustomOption::getCustomOption($customFieldID, TRUE); $formatted[$key] = array(); $params[$key] = array(); foreach ($mulValues as $v1) { foreach ($customOption as $v2) { if ((strtolower($v2['label']) == strtolower(trim($v1))) || (strtolower($v2['value']) == strtolower(trim($v1))) ) { if ($htmlType == 'CheckBox') { $params[$key][$v2['value']] = $formatted[$key][$v2['value']] = 1; } else { $params[$key][] = $formatted[$key][] = $v2['value']; } } } } } break; } } } if (!empty($key) && ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) && array_key_exists($customFieldID, $customFields) && !array_key_exists($customFieldID, $addressCustomFields) ) { // @todo calling api functions directly is not supported _civicrm_api3_custom_format_params($params, $formatted, $extends); } // to check if not update mode and unset the fields with empty value. if (!$this->_updateWithId && array_key_exists('custom', $formatted)) { foreach ($formatted['custom'] as $customKey => $customvalue) { if (empty($formatted['custom'][$customKey][-1]['is_required'])) { $formatted['custom'][$customKey][-1]['is_required'] = $customFields[$customKey]['is_required']; } $emptyValue = CRM_Utils_Array::value('value', $customvalue[-1]); if (!isset($emptyValue)) { unset($formatted['custom'][$customKey]); } } } // parse street address, CRM-5450 if ($this->_parseStreetAddress) { if (array_key_exists('address', $formatted) && is_array($formatted['address'])) { foreach ($formatted['address'] as $instance => & $address) { $streetAddress = CRM_Utils_Array::value('street_address', $address); if (empty($streetAddress)) { continue; } // parse address field. $parsedFields = CRM_Core_BAO_Address::parseStreetAddress($streetAddress); //street address consider to be parsed properly, //If we get street_name and street_number. if (empty($parsedFields['street_name']) || empty($parsedFields['street_number'])) { $parsedFields = array_fill_keys(array_keys($parsedFields), ''); } // merge parse address w/ main address block. $address = array_merge($address, $parsedFields); } } } } /** * Generate status and error message for unparsed street address records. * * @param array $values * The array of values belonging to each row. * @param array $statusFieldName * Store formatted date in this array. * @param $returnCode * * @return int */ public function processMessage(&$values, $statusFieldName, $returnCode) { if (empty($this->_unparsedStreetAddressContacts)) { $importRecordParams = array( $statusFieldName => 'IMPORTED', ); } else { $errorMessage = ts("Record imported successfully but unable to parse the street address: "); foreach ($this->_unparsedStreetAddressContacts as $contactInfo => $contactValue) { $contactUrl = CRM_Utils_System::url('civicrm/contact/add', 'reset=1&action=update&cid=' . $contactValue['id'], TRUE, NULL, FALSE); $errorMessage .= "\n Contact ID:" . $contactValue['id'] . " " . $contactValue['streetAddress'] . ""; } array_unshift($values, $errorMessage); $importRecordParams = array( $statusFieldName => 'ERROR', "${statusFieldName}Msg" => $errorMessage, ); $returnCode = CRM_Import_Parser::UNPARSED_ADDRESS_WARNING; } $this->updateImportRecord($values[count($values) - 1], $importRecordParams); return $returnCode; } /** * @param $relKey * @param array $params * * @return bool */ public function checkRelatedContactFields($relKey, $params) { //avoid blank contact creation. $allowToCreate = FALSE; //build the mapper field array. static $relatedContactFields = array(); if (!isset($relatedContactFields[$relKey])) { foreach ($this->_mapperRelated as $key => $name) { if (!$name) { continue; } if (!empty($relatedContactFields[$name]) && !is_array($relatedContactFields[$name])) { $relatedContactFields[$name] = array(); } $fldName = CRM_Utils_Array::value($key, $this->_mapperRelatedContactDetails); if ($fldName == 'url') { $fldName = 'website'; } if ($fldName) { $relatedContactFields[$name][] = $fldName; } } } //validate for passed data. if (is_array($relatedContactFields[$relKey])) { foreach ($relatedContactFields[$relKey] as $fld) { if (!empty($params[$fld])) { $allowToCreate = TRUE; break; } } } return $allowToCreate; } /** * get subtypes given the contact type * * @param string $contactType * @return array $subTypes */ public static function getSubtypes($contactType) { $subTypes = array(); $types = CRM_Contact_BAO_ContactType::subTypeInfo($contactType); if (count($types) > 0) { foreach ($types as $type) { $subTypes[] = $type['name']; } } return $subTypes; } /** * Get the possible contact matches. * * 1) the chosen dedupe rule falling back to * 2) a check for the external ID. * * CRM-17275 * * @param array $params * * @return array * IDs of possible matches. * * @throws \CRM_Core_Exception * @throws \CiviCRM_API3_Exception */ protected function getPossibleContactMatches($params) { $extIDMatch = NULL; if (!empty($params['external_identifier'])) { $extIDContact = civicrm_api3('Contact', 'get', array( 'external_identifier' => $params['external_identifier'], 'return' => 'id', )); if (isset($extIDContact['id'])) { $extIDMatch = $extIDContact['id']; } } $checkParams = array('check_permissions' => FALSE, 'match' => $params); $checkParams['match']['contact_type'] = $this->_contactType; $possibleMatches = civicrm_api3('Contact', 'duplicatecheck', $checkParams); if (!$extIDMatch) { return array_keys($possibleMatches['values']); } if ($possibleMatches['count']) { if (in_array($extIDMatch, array_keys($possibleMatches['values']))) { return array($extIDMatch); } else { throw new CRM_Core_Exception(ts( 'Matching this contact based on the de-dupe rule would cause an external ID conflict')); } } return array($extIDMatch); } /** * Format the form mapping parameters ready for the parser. * * @param int $count * Number of rows. * * @return array $parserParameters */ public static function getParameterForParser($count) { $baseArray = array(); for ($i = 0; $i < $count; $i++) { $baseArray[$i] = NULL; } $parserParameters['mapperLocType'] = $baseArray; $parserParameters['mapperPhoneType'] = $baseArray; $parserParameters['mapperImProvider'] = $baseArray; $parserParameters['mapperWebsiteType'] = $baseArray; $parserParameters['mapperRelated'] = $baseArray; $parserParameters['relatedContactType'] = $baseArray; $parserParameters['relatedContactDetails'] = $baseArray; $parserParameters['relatedContactLocType'] = $baseArray; $parserParameters['relatedContactPhoneType'] = $baseArray; $parserParameters['relatedContactImProvider'] = $baseArray; $parserParameters['relatedContactWebsiteType'] = $baseArray; return $parserParameters; } /** * Format contact parameters. * * @todo this function needs re-writing & re-merging into the main function. * * Here be dragons. * * @param array $values * @param array $params * * @return bool */ protected function formatContactParameters(&$values, &$params) { // Crawl through the possible classes: // Contact // Individual // Household // Organization // Location // Address // Email // Phone // IM // Note // Custom // Cache the various object fields static $fields = array(); // first add core contact values since for other Civi modules they are not added $contactFields = CRM_Contact_DAO_Contact::fields(); _civicrm_api3_store_values($contactFields, $values, $params); if (isset($values['contact_type'])) { // we're an individual/household/org property $fields[$values['contact_type']] = CRM_Contact_DAO_Contact::fields(); _civicrm_api3_store_values($fields[$values['contact_type']], $values, $params); return TRUE; } if (isset($values['individual_prefix'])) { if (!empty($params['prefix_id'])) { $prefixes = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'prefix_id'); $params['prefix'] = $prefixes[$params['prefix_id']]; } else { $params['prefix'] = $values['individual_prefix']; } return TRUE; } if (isset($values['individual_suffix'])) { if (!empty($params['suffix_id'])) { $suffixes = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'suffix_id'); $params['suffix'] = $suffixes[$params['suffix_id']]; } else { $params['suffix'] = $values['individual_suffix']; } return TRUE; } // CRM-4575 if (isset($values['email_greeting'])) { if (!empty($params['email_greeting_id'])) { $emailGreetingFilter = array( 'contact_type' => CRM_Utils_Array::value('contact_type', $params), 'greeting_type' => 'email_greeting', ); $emailGreetings = CRM_Core_PseudoConstant::greeting($emailGreetingFilter); $params['email_greeting'] = $emailGreetings[$params['email_greeting_id']]; } else { $params['email_greeting'] = $values['email_greeting']; } return TRUE; } if (isset($values['postal_greeting'])) { if (!empty($params['postal_greeting_id'])) { $postalGreetingFilter = array( 'contact_type' => CRM_Utils_Array::value('contact_type', $params), 'greeting_type' => 'postal_greeting', ); $postalGreetings = CRM_Core_PseudoConstant::greeting($postalGreetingFilter); $params['postal_greeting'] = $postalGreetings[$params['postal_greeting_id']]; } else { $params['postal_greeting'] = $values['postal_greeting']; } return TRUE; } if (isset($values['addressee'])) { if (!empty($params['addressee_id'])) { $addresseeFilter = array( 'contact_type' => CRM_Utils_Array::value('contact_type', $params), 'greeting_type' => 'addressee', ); $addressee = CRM_Core_PseudoConstant::addressee($addresseeFilter); $params['addressee'] = $addressee[$params['addressee_id']]; } else { $params['addressee'] = $values['addressee']; } return TRUE; } if (isset($values['gender'])) { if (!empty($params['gender_id'])) { $genders = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'gender_id'); $params['gender'] = $genders[$params['gender_id']]; } else { $params['gender'] = $values['gender']; } return TRUE; } if (!empty($values['preferred_communication_method'])) { $comm = array(); $pcm = array_change_key_case(array_flip(CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'preferred_communication_method')), CASE_LOWER); $preffComm = explode(',', $values['preferred_communication_method']); foreach ($preffComm as $v) { $v = strtolower(trim($v)); if (array_key_exists($v, $pcm)) { $comm[$pcm[$v]] = 1; } } $params['preferred_communication_method'] = $comm; return TRUE; } // format the website params. if (!empty($values['url'])) { static $websiteFields; if (!is_array($websiteFields)) { require_once 'CRM/Core/DAO/Website.php'; $websiteFields = CRM_Core_DAO_Website::fields(); } if (!array_key_exists('website', $params) || !is_array($params['website']) ) { $params['website'] = array(); } $websiteCount = count($params['website']); _civicrm_api3_store_values($websiteFields, $values, $params['website'][++$websiteCount] ); return TRUE; } // get the formatted location blocks into params - w/ 3.0 format, CRM-4605 if (!empty($values['location_type_id'])) { $blockTypes = array( 'phone' => 'Phone', 'email' => 'Email', 'im' => 'IM', 'openid' => 'OpenID', 'phone_ext' => 'Phone', ); foreach ($blockTypes as $blockFieldName => $block) { if (!array_key_exists($blockFieldName, $values)) { continue; } // block present in value array. if (!array_key_exists($blockFieldName, $params) || !is_array($params[$blockFieldName])) { $params[$blockFieldName] = array(); } if (!array_key_exists($block, $fields)) { $className = "CRM_Core_DAO_$block"; $fields[$block] = $className::fields(); } $blockCnt = count($params[$blockFieldName]); // copy value to dao field name. if ($blockFieldName == 'im') { $values['name'] = $values[$blockFieldName]; } _civicrm_api3_store_values($fields[$block], $values, $params[$blockFieldName][++$blockCnt] ); if ($values['location_type_id'] === 'Primary') { if (!empty($params['id'])) { $primary = civicrm_api3($block, 'get', array('return' => 'location_type_id', 'contact_id' => $params['id'], 'is_primary' => 1, 'sequential' => 1)); } $defaultLocationType = CRM_Core_BAO_LocationType::getDefault(); $values['location_type_id'] = (isset($primary) && $primary['count']) ? $primary['values'][0]['location_type_id'] : $defaultLocationType->id; $values['is_primary'] = 1; } if (empty($params['id']) && ($blockCnt == 1)) { $params[$blockFieldName][$blockCnt]['is_primary'] = TRUE; } // we only process single block at a time. return TRUE; } // handle address fields. if (!array_key_exists('address', $params) || !is_array($params['address'])) { $params['address'] = array(); } if (!array_key_exists('Address', $fields)) { $fields['Address'] = CRM_Core_DAO_Address::fields(); } // Note: we doing multiple value formatting here for address custom fields, plus putting into right format. // The actual formatting (like date, country ..etc) for address custom fields is taken care of while saving // the address in CRM_Core_BAO_Address::create method if (!empty($values['location_type_id'])) { static $customFields = array(); if (empty($customFields)) { $customFields = CRM_Core_BAO_CustomField::getFields('Address'); } // make a copy of values, as we going to make changes $newValues = $values; foreach ($values as $key => $val) { $customFieldID = CRM_Core_BAO_CustomField::getKeyID($key); if ($customFieldID && array_key_exists($customFieldID, $customFields)) { // mark an entry in fields array since we want the value of custom field to be copied $fields['Address'][$key] = NULL; $htmlType = CRM_Utils_Array::value('html_type', $customFields[$customFieldID]); switch ($htmlType) { case 'CheckBox': case 'AdvMulti-Select': case 'Multi-Select': if ($val) { $mulValues = explode(',', $val); $customOption = CRM_Core_BAO_CustomOption::getCustomOption($customFieldID, TRUE); $newValues[$key] = array(); foreach ($mulValues as $v1) { foreach ($customOption as $v2) { if ((strtolower($v2['label']) == strtolower(trim($v1))) || (strtolower($v2['value']) == strtolower(trim($v1))) ) { if ($htmlType == 'CheckBox') { $newValues[$key][$v2['value']] = 1; } else { $newValues[$key][] = $v2['value']; } } } } } break; } } } // consider new values $values = $newValues; } _civicrm_api3_store_values($fields['Address'], $values, $params['address'][$values['location_type_id']]); $addressFields = array( 'county', 'country', 'state_province', 'supplemental_address_1', 'supplemental_address_2', 'supplemental_address_3', 'StateProvince.name', ); foreach ($addressFields as $field) { if (array_key_exists($field, $values)) { if (!array_key_exists('address', $params)) { $params['address'] = array(); } $params['address'][$values['location_type_id']][$field] = $values[$field]; } } if ($values['location_type_id'] === 'Primary') { if (!empty($params['id'])) { $primary = civicrm_api3('Address', 'get', array('return' => 'location_type_id', 'contact_id' => $params['id'], 'is_primary' => 1, 'sequential' => 1)); } $defaultLocationType = CRM_Core_BAO_LocationType::getDefault(); $params['address'][$values['location_type_id']]['location_type_id'] = (isset($primary) && $primary['count']) ? $primary['values'][0]['location_type_id'] : $defaultLocationType->id; $params['address'][$values['location_type_id']]['is_primary'] = 1; } return TRUE; } if (isset($values['note'])) { // add a note field if (!isset($params['note'])) { $params['note'] = array(); } $noteBlock = count($params['note']) + 1; $params['note'][$noteBlock] = array(); if (!isset($fields['Note'])) { $fields['Note'] = CRM_Core_DAO_Note::fields(); } // get the current logged in civicrm user $session = CRM_Core_Session::singleton(); $userID = $session->get('userID'); if ($userID) { $values['contact_id'] = $userID; } _civicrm_api3_store_values($fields['Note'], $values, $params['note'][$noteBlock]); return TRUE; } // Check for custom field values if (empty($fields['custom'])) { $fields['custom'] = &CRM_Core_BAO_CustomField::getFields(CRM_Utils_Array::value('contact_type', $values), FALSE, FALSE, NULL, NULL, FALSE, FALSE, FALSE ); } foreach ($values as $key => $value) { if ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) { // check if it's a valid custom field id if (!array_key_exists($customFieldID, $fields['custom'])) { return civicrm_api3_create_error('Invalid custom field ID'); } else { $params[$key] = $value; } } } return TRUE; } }