drupal-civicrm/sites/all/modules/civicrm/CRM/Contact/BAO/Contact.php
2018-01-14 13:10:16 +00:00

3537 lines
113 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Contact_BAO_Contact extends CRM_Contact_DAO_Contact {
/**
* SQL function used to format the phone_numeric field via trigger.
* @see self::triggerInfo()
*
* Note that this is also used by the 4.3 upgrade script.
* @see CRM_Upgrade_Incremental_php_FourThree
*/
const DROP_STRIP_FUNCTION_43 = "DROP FUNCTION IF EXISTS civicrm_strip_non_numeric";
const CREATE_STRIP_FUNCTION_43 = "
CREATE FUNCTION civicrm_strip_non_numeric(input VARCHAR(255) CHARACTER SET utf8)
RETURNS VARCHAR(255) CHARACTER SET utf8
DETERMINISTIC
NO SQL
BEGIN
DECLARE output VARCHAR(255) CHARACTER SET utf8 DEFAULT '';
DECLARE iterator INT DEFAULT 1;
WHILE iterator < (LENGTH(input) + 1) DO
IF SUBSTRING(input, iterator, 1) IN ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9') THEN
SET output = CONCAT(output, SUBSTRING(input, iterator, 1));
END IF;
SET iterator = iterator + 1;
END WHILE;
RETURN output;
END";
/**
* The types of communication preferences.
*
* @var array
*/
static $_commPrefs = array(
'do_not_phone',
'do_not_email',
'do_not_mail',
'do_not_sms',
'do_not_trade',
);
/**
* Types of greetings.
*
* @var array
*/
static $_greetingTypes = array(
'addressee',
'email_greeting',
'postal_greeting',
);
/**
* Static field for all the contact information that we can potentially import.
*
* @var array
*/
static $_importableFields = array();
/**
* Static field for all the contact information that we can potentially export.
*
* @var array
*/
static $_exportableFields = NULL;
/**
* Class constructor.
*/
public function __construct() {
parent::__construct();
}
/**
* Takes an associative array and creates a contact object.
*
* The function extracts all the params it needs to initialize the create a
* contact object. the params array could contain additional unused name/value
* pairs
*
* @param array $params
* (reference) an assoc array of name/value pairs.
*
* @return CRM_Contact_BAO_Contact|CRM_Core_Error|NULL
* Created or updated contact object or error object.
* (error objects are being phased out in favour of exceptions)
*/
public static function add(&$params) {
$contact = new CRM_Contact_DAO_Contact();
if (empty($params)) {
return NULL;
}
// Fix for validate contact sub type CRM-5143.
if (isset($params['contact_sub_type'])) {
if (empty($params['contact_sub_type'])) {
$params['contact_sub_type'] = 'null';
}
else {
if (!CRM_Contact_BAO_ContactType::isExtendsContactType($params['contact_sub_type'],
$params['contact_type'], TRUE
)
) {
// we'll need to fix tests to handle this
// CRM-7925
CRM_Core_Error::fatal(ts('The Contact Sub Type does not match the Contact type for this record'));
}
$params['contact_sub_type'] = CRM_Utils_Array::implodePadded($params['contact_sub_type']);
}
}
else {
// Reset the value.
// CRM-101XX.
$params['contact_sub_type'] = 'null';
}
// Fixed contact source.
if (isset($params['contact_source'])) {
$params['source'] = $params['contact_source'];
}
if (isset($params['preferred_communication_method']) && is_array($params['preferred_communication_method'])) {
CRM_Utils_Array::formatArrayKeys($params['preferred_communication_method']);
$contact->preferred_communication_method = CRM_Utils_Array::implodePadded($params['preferred_communication_method']);
unset($params['preferred_communication_method']);
}
$allNull = $contact->copyValues($params);
$contact->id = CRM_Utils_Array::value('contact_id', $params);
if ($contact->contact_type == 'Individual') {
$allNull = FALSE;
// Format individual fields.
CRM_Contact_BAO_Individual::format($params, $contact);
}
elseif ($contact->contact_type == 'Household') {
if (isset($params['household_name'])) {
$allNull = FALSE;
$contact->display_name = $contact->sort_name = CRM_Utils_Array::value('household_name', $params, '');
}
}
elseif ($contact->contact_type == 'Organization') {
if (isset($params['organization_name'])) {
$allNull = FALSE;
$contact->display_name = $contact->sort_name = CRM_Utils_Array::value('organization_name', $params, '');
}
}
if (strlen($contact->display_name) > 128) {
$contact->display_name = substr($contact->display_name, 0, 128);
}
if (strlen($contact->sort_name) > 128) {
$contact->sort_name = substr($contact->sort_name, 0, 128);
}
$privacy = CRM_Utils_Array::value('privacy', $params);
if ($privacy &&
is_array($privacy) &&
!empty($privacy)
) {
$allNull = FALSE;
foreach (self::$_commPrefs as $name) {
$contact->$name = CRM_Utils_Array::value($name, $privacy, FALSE);
}
}
// Since hash was required, make sure we have a 0 value for it (CRM-1063).
// @todo - does this mean we can remove this block?
// Fixed in 1.5 by making hash optional, only do this in create mode, not update.
if ((!array_key_exists('hash', $contact) || !$contact->hash) && !$contact->id) {
$allNull = FALSE;
$contact->hash = md5(uniqid(rand(), TRUE));
}
// Even if we don't need $employerId, it's important to call getFieldValue() before
// the contact is saved because we want the existing value to be cached.
// createCurrentEmployerRelationship() needs the old value not the updated one. CRM-10788
$employerId = empty($contact->id) ? NULL : CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $contact->id, 'employer_id');
if (!$allNull) {
$contact->save();
CRM_Core_BAO_Log::register($contact->id,
'civicrm_contact',
$contact->id
);
}
if ($contact->contact_type == 'Individual' && (isset($params['current_employer']) || isset($params['employer_id']))) {
// Create current employer.
$newEmployer = !empty($params['employer_id']) ? $params['employer_id'] : CRM_Utils_Array::value('current_employer', $params);
$newContact = FALSE;
if (empty($params['contact_id'])) {
$newContact = TRUE;
}
if ($newEmployer) {
CRM_Contact_BAO_Contact_Utils::createCurrentEmployerRelationship($contact->id, $newEmployer, $employerId, $newContact);
}
else {
if ($employerId) {
CRM_Contact_BAO_Contact_Utils::clearCurrentEmployer($contact->id, $employerId);
}
}
}
// Update cached employer name.
if ($contact->contact_type == 'Organization') {
CRM_Contact_BAO_Contact_Utils::updateCurrentEmployer($contact->id);
}
return $contact;
}
/**
* Create contact.
*
* takes an associative array and creates a contact object and all the associated
* derived objects (i.e. individual, location, email, phone etc)
*
* This function is invoked from within the web form layer and also from the api layer
*
* @param array $params
* (reference ) an assoc array of name/value pairs.
* @param bool $fixAddress
* If we need to fix address.
* @param bool $invokeHooks
* If we need to invoke hooks.
*
* @param bool $skipDelete
* Unclear parameter, passed to website create
*
* @todo explain this parameter
*
* @throws Exception
* @return CRM_Contact_BAO_Contact|CRM_Core_Error
* Created or updated contribution object. We are deprecating returning an error in
* favour of exceptions
*/
public static function &create(&$params, $fixAddress = TRUE, $invokeHooks = TRUE, $skipDelete = FALSE) {
$contact = NULL;
if (empty($params['contact_type']) && empty($params['contact_id'])) {
return $contact;
}
$isEdit = TRUE;
if ($invokeHooks) {
if (!empty($params['contact_id'])) {
CRM_Utils_Hook::pre('edit', $params['contact_type'], $params['contact_id'], $params);
}
else {
CRM_Utils_Hook::pre('create', $params['contact_type'], NULL, $params);
$isEdit = FALSE;
}
}
$config = CRM_Core_Config::singleton();
// CRM-6942: set preferred language to the current language if its unset (and were creating a contact).
if (empty($params['contact_id'])) {
// A case could be made for checking isset rather than empty but this is more consistent with previous behaviour.
if (empty($params['preferred_language']) && ($language = CRM_Core_I18n::getContactDefaultLanguage()) != FALSE) {
$params['preferred_language'] = $language;
}
// CRM-9739: set greeting & addressee if unset and were creating a contact.
foreach (self::$_greetingTypes as $greeting) {
if (empty($params[$greeting . '_id'])) {
if ($defaultGreetingTypeId
= CRM_Contact_BAO_Contact_Utils::defaultGreeting($params['contact_type'], $greeting)
) {
$params[$greeting . '_id'] = $defaultGreetingTypeId;
}
}
}
}
$transaction = new CRM_Core_Transaction();
$contact = self::add($params);
if (!$contact) {
// Not dying here is stupid, since we get into weird situation and into a bug that
// is impossible to figure out for the user or for us
// CRM-7925
CRM_Core_Error::fatal();
}
$params['contact_id'] = $contact->id;
if (Civi::settings()->get('is_enabled')) {
// Enabling multisite causes the contact to be added to the domain group.
$domainGroupID = CRM_Core_BAO_Domain::getGroupId();
if (!empty($domainGroupID)) {
if (!empty($params['group']) && is_array($params['group'])) {
$params['group'][$domainGroupID] = 1;
}
else {
$params['group'] = array($domainGroupID => 1);
}
}
}
if (array_key_exists('group', $params)) {
$contactIds = array($params['contact_id']);
foreach ($params['group'] as $groupId => $flag) {
if ($flag == 1) {
CRM_Contact_BAO_GroupContact::addContactsToGroup($contactIds, $groupId);
}
elseif ($flag == -1) {
CRM_Contact_BAO_GroupContact::removeContactsFromGroup($contactIds, $groupId);
}
}
}
// Add location Block data.
$blocks = CRM_Core_BAO_Location::create($params, $fixAddress);
foreach ($blocks as $name => $value) {
$contact->$name = $value;
}
if (!empty($params['updateBlankLocInfo'])) {
$skipDelete = TRUE;
}
//add website
CRM_Core_BAO_Website::create($params['website'], $contact->id, $skipDelete);
$userID = CRM_Core_Session::singleton()->get('userID');
// add notes
if (!empty($params['note'])) {
if (is_array($params['note'])) {
foreach ($params['note'] as $note) {
$contactId = $contact->id;
if (isset($note['contact_id'])) {
$contactId = $note['contact_id'];
}
//if logged in user, overwrite contactId
if ($userID) {
$contactId = $userID;
}
$noteParams = array(
'entity_id' => $contact->id,
'entity_table' => 'civicrm_contact',
'note' => $note['note'],
'subject' => CRM_Utils_Array::value('subject', $note),
'contact_id' => $contactId,
);
CRM_Core_BAO_Note::add($noteParams, CRM_Core_DAO::$_nullArray);
}
}
else {
$contactId = $contact->id;
if (isset($note['contact_id'])) {
$contactId = $note['contact_id'];
}
//if logged in user, overwrite contactId
if ($userID) {
$contactId = $userID;
}
$noteParams = array(
'entity_id' => $contact->id,
'entity_table' => 'civicrm_contact',
'note' => $params['note'],
'subject' => CRM_Utils_Array::value('subject', $params),
'contact_id' => $contactId,
);
CRM_Core_BAO_Note::add($noteParams, CRM_Core_DAO::$_nullArray);
}
}
// update the UF user_unique_id if that has changed
CRM_Core_BAO_UFMatch::updateUFName($contact->id);
if (!empty($params['custom']) &&
is_array($params['custom'])
) {
CRM_Core_BAO_CustomValueTable::store($params['custom'], 'civicrm_contact', $contact->id);
}
// make a civicrm_subscription_history entry only on contact create (CRM-777)
if (empty($params['contact_id'])) {
$subscriptionParams = array(
'contact_id' => $contact->id,
'status' => 'Added',
'method' => 'Admin',
);
CRM_Contact_BAO_SubscriptionHistory::create($subscriptionParams);
}
$transaction->commit();
// CRM-6367: fetch the right label for contact types display
$contact->contact_type_display = CRM_Core_DAO::getFieldValue(
'CRM_Contact_DAO_ContactType',
$contact->contact_type,
'label',
'name'
);
CRM_Contact_BAO_Contact_Utils::clearContactCaches();
if ($invokeHooks) {
if ($isEdit) {
CRM_Utils_Hook::post('edit', $params['contact_type'], $contact->id, $contact);
}
else {
CRM_Utils_Hook::post('create', $params['contact_type'], $contact->id, $contact);
}
}
// process greetings CRM-4575, cache greetings
self::processGreetings($contact);
return $contact;
}
/**
* Get the display name and image of a contact.
*
* @param int $id
* The contactId.
*
* @param bool $includeTypeInReturnParameters
* Should type be part of the returned array?
*
* @return array
* the displayName and contactImage for this contact
*/
public static function getDisplayAndImage($id, $includeTypeInReturnParameters = FALSE) {
//CRM-14276 added the * on the civicrm_contact table so that we have all the contact info available
$sql = "
SELECT civicrm_contact.*,
civicrm_email.email as email
FROM civicrm_contact
LEFT JOIN civicrm_email ON civicrm_email.contact_id = civicrm_contact.id
AND civicrm_email.is_primary = 1
WHERE civicrm_contact.id = " . CRM_Utils_Type::escape($id, 'Integer');
$dao = new CRM_Core_DAO();
$dao->query($sql);
if ($dao->fetch()) {
$image = CRM_Contact_BAO_Contact_Utils::getImage($dao->contact_sub_type ?
$dao->contact_sub_type : $dao->contact_type, FALSE, $id
);
$imageUrl = CRM_Contact_BAO_Contact_Utils::getImage($dao->contact_sub_type ?
$dao->contact_sub_type : $dao->contact_type, TRUE, $id
);
// use email if display_name is empty
if (empty($dao->display_name)) {
$displayName = $dao->email;
}
else {
$displayName = $dao->display_name;
}
CRM_Utils_Hook::alterDisplayName($displayName, $id, $dao);
return $includeTypeInReturnParameters ? array(
$displayName,
$image,
$dao->contact_type,
$dao->contact_sub_type,
$imageUrl,
) : array($displayName, $image, $imageUrl);
}
return NULL;
}
/**
* Add billing fields to the params if appropriate.
*
* If we have ANY name fields then we want to ignore all the billing name fields. However, if we
* don't then we should set the name fields to the filling fields AND add the preserveDBName
* parameter (which will tell the BAO only to set those fields if none already exist.
*
* We specifically don't want to set first name from billing and last name form an on-page field. Mixing &
* matching is best done by hipsters.
*
* @param array $params
*/
public static function addBillingNameFieldsIfOtherwiseNotSet(&$params) {
$nameFields = array('first_name', 'middle_name', 'last_name', 'nick_name', 'prefix_id', 'suffix_id');
foreach ($nameFields as $field) {
if (!empty($params[$field])) {
return;
}
}
// There are only 3 - we can iterate through them twice :-)
foreach ($nameFields as $field) {
if (!empty($params['billing_' . $field])) {
$params[$field] = $params['billing_' . $field];
}
$params['preserveDBName'] = TRUE;
}
}
/**
* Create last viewed link to recently updated contact.
*
* @param array $crudLinkSpec
* - action: int, CRM_Core_Action::UPDATE or CRM_Core_Action::VIEW [default: VIEW]
* - entity_table: string, eg "civicrm_contact"
* - entity_id: int
*
* @return array|NULL
* NULL if unavailable, or
* [path: string, query: string, title: string]
* @see CRM_Utils_System::createDefaultCrudLink
*/
public function createDefaultCrudLink($crudLinkSpec) {
switch ($crudLinkSpec['action']) {
case CRM_Core_Action::VIEW:
$result = array(
'title' => $this->display_name,
'path' => 'civicrm/contact/view',
'query' => array(
'reset' => 1,
'cid' => $this->id,
),
);
return $result;
case CRM_Core_Action::UPDATE:
$result = array(
'title' => $this->display_name,
'path' => 'civicrm/contact/add',
'query' => array(
'reset' => 1,
'action' => 'update',
'cid' => $this->id,
),
);
return $result;
}
return NULL;
}
/**
* Get the values for pseudoconstants for name->value and reverse.
*
* @param array $defaults
* (reference) the default values, some of which need to be resolved.
* @param bool $reverse
* True if we want to resolve the values in the reverse direction (value -> name).
*/
public static function resolveDefaults(&$defaults, $reverse = FALSE) {
// Hack for birth_date.
if (!empty($defaults['birth_date'])) {
if (is_array($defaults['birth_date'])) {
$defaults['birth_date'] = CRM_Utils_Date::format($defaults['birth_date'], '-');
}
}
CRM_Utils_Array::lookupValue($defaults, 'prefix', CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'prefix_id'), $reverse);
CRM_Utils_Array::lookupValue($defaults, 'suffix', CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'suffix_id'), $reverse);
CRM_Utils_Array::lookupValue($defaults, 'gender', CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'gender_id'), $reverse);
CRM_Utils_Array::lookupValue($defaults, 'communication_style', CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'communication_style_id'), $reverse);
//lookup value of email/postal greeting, addressee, CRM-4575
foreach (self::$_greetingTypes as $greeting) {
$filterCondition = array(
'contact_type' => CRM_Utils_Array::value('contact_type', $defaults),
'greeting_type' => $greeting,
);
CRM_Utils_Array::lookupValue($defaults, $greeting,
CRM_Core_PseudoConstant::greeting($filterCondition), $reverse
);
}
$blocks = array('address', 'im', 'phone');
foreach ($blocks as $name) {
if (!array_key_exists($name, $defaults) || !is_array($defaults[$name])) {
continue;
}
foreach ($defaults[$name] as $count => & $values) {
//get location type id.
CRM_Utils_Array::lookupValue($values, 'location_type', CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'), $reverse);
if ($name == 'address') {
// FIXME: lookupValue doesn't work for vcard_name
if (!empty($values['location_type_id'])) {
$vcardNames = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id', array('labelColumn' => 'vcard_name'));
$values['vcard_name'] = $vcardNames[$values['location_type_id']];
}
if (!CRM_Utils_Array::lookupValue($values,
'country',
CRM_Core_PseudoConstant::country(),
$reverse
) &&
$reverse
) {
CRM_Utils_Array::lookupValue($values,
'country',
CRM_Core_PseudoConstant::countryIsoCode(),
$reverse
);
}
// CRM-7597
// if we find a country id above, we need to restrict it to that country
// rather than the list of all countries
if (!empty($values['country_id'])) {
$stateProvinceList = CRM_Core_PseudoConstant::stateProvinceForCountry($values['country_id']);
}
else {
$stateProvinceList = CRM_Core_PseudoConstant::stateProvince();
}
if (!CRM_Utils_Array::lookupValue($values,
'state_province',
$stateProvinceList,
$reverse
) &&
$reverse
) {
if (!empty($values['country_id'])) {
$stateProvinceList = CRM_Core_PseudoConstant::stateProvinceForCountry($values['country_id'], 'abbreviation');
}
else {
$stateProvinceList = CRM_Core_PseudoConstant::stateProvinceAbbreviation();
}
CRM_Utils_Array::lookupValue($values,
'state_province',
$stateProvinceList,
$reverse
);
}
if (!empty($values['state_province_id'])) {
$countyList = CRM_Core_PseudoConstant::countyForState($values['state_province_id']);
}
else {
$countyList = CRM_Core_PseudoConstant::county();
}
CRM_Utils_Array::lookupValue($values,
'county',
$countyList,
$reverse
);
}
if ($name == 'im') {
CRM_Utils_Array::lookupValue($values,
'provider',
CRM_Core_PseudoConstant::get('CRM_Core_DAO_IM', 'provider_id'),
$reverse
);
}
if ($name == 'phone') {
CRM_Utils_Array::lookupValue($values,
'phone_type',
CRM_Core_PseudoConstant::get('CRM_Core_DAO_Phone', 'phone_type_id'),
$reverse
);
}
// Kill the reference.
unset($values);
}
}
}
/**
* Fetch object based on array of properties.
*
* @param array $params
* (reference ) an assoc array of name/value pairs.
* @param array $defaults
* (reference ) an assoc array to hold the name / value pairs.
* in a hierarchical manner
* @param bool $microformat
* For location in microformat.
*
* @return CRM_Contact_BAO_Contact
*/
public static function &retrieve(&$params, &$defaults, $microformat = FALSE) {
if (array_key_exists('contact_id', $params)) {
$params['id'] = $params['contact_id'];
}
elseif (array_key_exists('id', $params)) {
$params['contact_id'] = $params['id'];
}
$contact = self::getValues($params, $defaults);
unset($params['id']);
//get the block information for this contact
$entityBlock = array('contact_id' => $params['contact_id']);
$blocks = CRM_Core_BAO_Location::getValues($entityBlock, $microformat);
$defaults = array_merge($defaults, $blocks);
foreach ($blocks as $block => $value) {
$contact->$block = $value;
}
if (!isset($params['noNotes'])) {
$contact->notes = CRM_Core_BAO_Note::getValues($params, $defaults);
}
if (!isset($params['noRelationships'])) {
$contact->relationship = CRM_Contact_BAO_Relationship::getValues($params, $defaults);
}
if (!isset($params['noGroups'])) {
$contact->groupContact = CRM_Contact_BAO_GroupContact::getValues($params, $defaults);
}
if (!isset($params['noWebsite'])) {
$contact->website = CRM_Core_BAO_Website::getValues($params, $defaults);
}
return $contact;
}
/**
* Get the display name of a contact.
*
* @param int $id
* Id of the contact.
*
* @return null|string
* display name of the contact if found
*/
public static function displayName($id) {
$displayName = NULL;
if ($id) {
$displayName = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $id, 'display_name');
}
return $displayName;
}
/**
* Delete a contact and all its associated records.
*
* @param int $id
* Id of the contact to delete.
* @param bool $restore
* Whether to actually restore, not delete.
* @param bool $skipUndelete
* Whether to force contact delete or not.
* @param bool $checkPermissions
*
* @return bool
* Was contact deleted?
*/
public static function deleteContact($id, $restore = FALSE, $skipUndelete = FALSE, $checkPermissions = TRUE) {
if (!$id) {
return FALSE;
}
// If trash is disabled in system settings then we always skip
if (!Civi::settings()->get('contact_undelete')) {
$skipUndelete = TRUE;
}
// make sure we have edit permission for this contact
// before we delete
if ($checkPermissions && (($skipUndelete && !CRM_Core_Permission::check('delete contacts')) ||
($restore && !CRM_Core_Permission::check('access deleted contacts')))
) {
return FALSE;
}
// CRM-12929
// Restrict contact to be delete if contact has financial trxns
$error = NULL;
if ($skipUndelete && CRM_Financial_BAO_FinancialItem::checkContactPresent(array($id), $error)) {
return FALSE;
}
// make sure this contact_id does not have any membership types
$membershipTypeID = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType',
$id,
'id',
'member_of_contact_id'
);
if ($membershipTypeID) {
return FALSE;
}
$contact = new CRM_Contact_DAO_Contact();
$contact->id = $id;
if (!$contact->find(TRUE)) {
return FALSE;
}
$contactType = $contact->contact_type;
// currently we only clear employer cache.
// we are now deleting inherited membership if any.
if ($contact->contact_type == 'Organization') {
$action = $restore ? CRM_Core_Action::ENABLE : CRM_Core_Action::DISABLE;
$relationshipDtls = CRM_Contact_BAO_Relationship::getRelationship($id);
if (!empty($relationshipDtls)) {
foreach ($relationshipDtls as $rId => $details) {
CRM_Contact_BAO_Relationship::disableEnableRelationship($rId, $action);
}
}
CRM_Contact_BAO_Contact_Utils::clearAllEmployee($id);
}
if ($restore) {
return self::contactTrashRestore($contact, TRUE);
}
// start a new transaction
$transaction = new CRM_Core_Transaction();
if ($skipUndelete) {
CRM_Utils_Hook::pre('delete', $contactType, $id, CRM_Core_DAO::$_nullArray);
//delete billing address if exists.
CRM_Contribute_BAO_Contribution::deleteAddress(NULL, $id);
// delete the log entries since we dont have triggers enabled as yet
$logDAO = new CRM_Core_DAO_Log();
$logDAO->entity_table = 'civicrm_contact';
$logDAO->entity_id = $id;
$logDAO->delete();
// delete contact participants CRM-12155
CRM_Event_BAO_Participant::deleteContactParticipant($id);
// delete contact contributions CRM-12155
CRM_Contribute_BAO_Contribution::deleteContactContribution($id);
// do activity cleanup, CRM-5604
CRM_Activity_BAO_Activity::cleanupActivity($id);
// delete all notes related to contact
CRM_Core_BAO_Note::cleanContactNotes($id);
// delete cases related to contact
$contactCases = CRM_Case_BAO_Case::retrieveCaseIdsByContactId($id);
if (!empty($contactCases)) {
foreach ($contactCases as $caseId) {
//check if case is associate with other contact or not.
$caseContactId = CRM_Case_BAO_Case::getCaseClients($caseId);
if (count($caseContactId) <= 1) {
CRM_Case_BAO_Case::deleteCase($caseId);
}
}
}
$contact->delete();
}
else {
self::contactTrashRestore($contact);
}
//delete the contact id from recently view
CRM_Utils_Recent::delContact($id);
self::updateContactCache($id, empty($restore));
// delete any dupe cache entry
CRM_Core_BAO_PrevNextCache::deleteItem($id);
$transaction->commit();
if ($skipUndelete) {
CRM_Utils_Hook::post('delete', $contactType, $contact->id, $contact);
}
// also reset the DB_DO global array so we can reuse the memory
// http://issues.civicrm.org/jira/browse/CRM-4387
CRM_Core_DAO::freeResult();
return TRUE;
}
/**
* Action to update any caches relating to a recently update contact.
*
* I was going to call this from delete as well as from create to ensure the delete is being
* done whenever a contact is set to is_deleted=1 BUT I found create is already over-aggressive in
* that regard so adding it to delete seems to be enough to remove it from CRM_Contact_BAO_Contact_Permission
* where the call involved a subquery that was locking the table.
*
* @param int $contactID
* @param bool $isTrashed
*/
public static function updateContactCache($contactID, $isTrashed = FALSE) {
if ($isTrashed) {
CRM_Contact_BAO_GroupContactCache::removeContact($contactID);
// This has been moved to here from CRM_Contact_BAO_Contact_Permission as that was causing
// a table-locking query. It still seems a bit inadequate as it assumes the acl users can't see deleted
// but this should not cause any change as long as contacts are not being trashed outside the
// main functions for that.
CRM_Core_DAO::executeQuery('DELETE FROM civicrm_acl_contact_cache WHERE contact_id = %1', array(1 => array($contactID, 'Integer')));
}
else {
CRM_Contact_BAO_GroupContactCache::opportunisticCacheFlush();
}
}
/**
* Delete the image of a contact.
*
* @param int $id
* Id of the contact.
*
* @return bool
* Was contact image deleted?
*/
public static function deleteContactImage($id) {
if (!$id) {
return FALSE;
}
$query = "
UPDATE civicrm_contact
SET image_URL=NULL
WHERE id={$id}; ";
CRM_Core_DAO::executeQuery($query);
return TRUE;
}
/**
* Return proportional height and width of the image.
*
* @param int $imageWidth
* Width of image.
*
* @param int $imageHeight
* Height of image.
*
* @return array
* Thumb dimension of image
*/
public static function getThumbSize($imageWidth, $imageHeight) {
$thumbWidth = 100;
if ($imageWidth && $imageHeight) {
$imageRatio = $imageWidth / $imageHeight;
}
else {
$imageRatio = 1;
}
if ($imageRatio > 1) {
$imageThumbWidth = $thumbWidth;
$imageThumbHeight = round($thumbWidth / $imageRatio);
}
else {
$imageThumbHeight = $thumbWidth;
$imageThumbWidth = round($thumbWidth * $imageRatio);
}
return array($imageThumbWidth, $imageThumbHeight);
}
/**
* Validate type of contact image.
*
* @param array $params
* @param string $imageIndex
* Index of image field.
* @param string $statusMsg
* Status message to be set after operation.
* @param string $opType
* Type of operation like fatal, bounce etc.
*
* @return bool
* true if valid image extension
*/
public static function processImageParams(
&$params,
$imageIndex = 'image_URL',
$statusMsg = NULL,
$opType = 'status'
) {
$mimeType = array(
'image/jpeg',
'image/jpg',
'image/png',
'image/bmp',
'image/p-jpeg',
'image/gif',
'image/x-png',
);
if (in_array($params[$imageIndex]['type'], $mimeType)) {
$photo = basename($params[$imageIndex]['name']);
$params[$imageIndex] = CRM_Utils_System::url('civicrm/contact/imagefile', 'photo=' . $photo, TRUE, NULL, TRUE, TRUE);
return TRUE;
}
else {
unset($params[$imageIndex]);
if (!$statusMsg) {
$statusMsg = ts('Image could not be uploaded due to invalid type extension.');
}
if ($opType == 'status') {
CRM_Core_Session::setStatus($statusMsg, 'Sorry', 'error');
}
// FIXME: additional support for fatal, bounce etc could be added.
return FALSE;
}
}
/**
* Extract contact id from url for deleting contact image.
*/
public static function processImage() {
$action = CRM_Utils_Request::retrieve('action', 'String');
$cid = CRM_Utils_Request::retrieve('cid', 'Positive');
// retrieve contact id in case of Profile context
$id = CRM_Utils_Request::retrieve('id', 'Positive');
$cid = $cid ? $cid : $id;
if ($action & CRM_Core_Action::DELETE) {
if (CRM_Utils_Request::retrieve('confirmed', 'Boolean')) {
CRM_Contact_BAO_Contact::deleteContactImage($cid);
CRM_Core_Session::setStatus(ts('Contact image deleted successfully'), ts('Image Deleted'), 'success');
$session = CRM_Core_Session::singleton();
$toUrl = $session->popUserContext();
CRM_Utils_System::redirect($toUrl);
}
}
}
/**
* Function to set is_delete true or restore deleted contact.
*
* @param CRM_Contact_DAO_Contact $contact
* Contact DAO object.
* @param bool $restore
* True to set the is_delete = 1 else false to restore deleted contact,
* i.e. is_delete = 0
*
* @return bool
*/
public static function contactTrashRestore($contact, $restore = FALSE) {
$updateParams = array(
'id' => $contact->id,
'is_deleted' => $restore ? 0 : 1,
);
CRM_Utils_Hook::pre('update', $contact->contact_type, $contact->id, $updateParams);
$params = array(1 => array($contact->id, 'Integer'));
if (!$restore) {
$query = "DELETE FROM civicrm_uf_match WHERE contact_id = %1";
CRM_Core_DAO::executeQuery($query, $params);
}
$contact->copyValues($updateParams);
$contact->save();
CRM_Utils_Hook::post('update', $contact->contact_type, $contact->id, $contact);
return TRUE;
}
/**
* Get contact type for a contact.
*
* @param int $id
* Id of the contact whose contact type is needed.
*
* @return string
* contact_type if $id found else null ""
*/
public static function getContactType($id) {
return CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $id, 'contact_type');
}
/**
* Get contact sub type for a contact.
*
* @param int $id
* Id of the contact whose contact sub type is needed.
*
* @param string $implodeDelimiter
*
* @return string
* contact_sub_type if $id found else null ""
*/
public static function getContactSubType($id, $implodeDelimiter = NULL) {
$subtype = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $id, 'contact_sub_type');
if (!$subtype) {
return $implodeDelimiter ? NULL : array();
}
$subtype = CRM_Utils_Array::explodePadded($subtype);
if ($implodeDelimiter) {
$subtype = implode($implodeDelimiter, $subtype);
}
return $subtype;
}
/**
* Get pair of contact-type and sub-type for a contact.
*
* @param int $id
* Id of the contact whose contact sub/contact type is needed.
*
* @return array
*/
public static function getContactTypes($id) {
$params = array('id' => $id);
$details = array();
$contact = CRM_Core_DAO::commonRetrieve('CRM_Contact_DAO_Contact',
$params,
$details,
array('contact_type', 'contact_sub_type')
);
if ($contact) {
$contactTypes = array();
if ($contact->contact_sub_type) {
$contactTypes = CRM_Utils_Array::explodePadded($contact->contact_sub_type);
}
array_unshift($contactTypes, $contact->contact_type);
return $contactTypes;
}
else {
CRM_Core_Error::fatal();
}
}
/**
* Combine all the importable fields from the lower levels object.
*
* The ordering is important, since currently we do not have a weight
* scheme. Adding weight is super important
*
* @param int|string $contactType contact Type
* @param bool $status
* Status is used to manipulate first title.
* @param bool $showAll
* If true returns all fields (includes disabled fields).
* @param bool $isProfile
* If its profile mode.
* @param bool $checkPermission
* If false, do not include permissioning clause (for custom data).
*
* @param bool $withMultiCustomFields
*
* @return array
* array of importable Fields
*/
public static function importableFields(
$contactType = 'Individual',
$status = FALSE,
$showAll = FALSE,
$isProfile = FALSE,
$checkPermission = TRUE,
$withMultiCustomFields = FALSE
) {
if (empty($contactType)) {
$contactType = 'All';
}
$cacheKeyString = "importableFields $contactType";
$cacheKeyString .= $status ? '_1' : '_0';
$cacheKeyString .= $showAll ? '_1' : '_0';
$cacheKeyString .= $isProfile ? '_1' : '_0';
$cacheKeyString .= $checkPermission ? '_1' : '_0';
$fields = CRM_Utils_Array::value($cacheKeyString, self::$_importableFields);
if (!$fields) {
// check if we can retrieve from database cache
$fields = CRM_Core_BAO_Cache::getItem('contact fields', $cacheKeyString);
}
if (!$fields) {
$fields = CRM_Contact_DAO_Contact::import();
// get the fields thar are meant for contact types
if (in_array($contactType, array(
'Individual',
'Household',
'Organization',
'All',
))) {
$fields = array_merge($fields, CRM_Core_OptionValue::getFields('', $contactType));
}
$locationFields = array_merge(CRM_Core_DAO_Address::import(),
CRM_Core_DAO_Phone::import(),
CRM_Core_DAO_Email::import(),
CRM_Core_DAO_IM::import(TRUE),
CRM_Core_DAO_OpenID::import()
);
$locationFields = array_merge($locationFields,
CRM_Core_BAO_CustomField::getFieldsForImport('Address',
FALSE,
FALSE,
FALSE,
FALSE
)
);
foreach ($locationFields as $key => $field) {
$locationFields[$key]['hasLocationType'] = TRUE;
}
$fields = array_merge($fields, $locationFields);
$fields = array_merge($fields, CRM_Contact_DAO_Contact::import());
$fields = array_merge($fields, CRM_Core_DAO_Note::import());
//website fields
$fields = array_merge($fields, CRM_Core_DAO_Website::import());
$fields['url']['hasWebsiteType'] = TRUE;
if ($contactType != 'All') {
$fields = array_merge($fields,
CRM_Core_BAO_CustomField::getFieldsForImport($contactType,
$showAll,
TRUE,
FALSE,
FALSE,
$withMultiCustomFields
)
);
//unset the fields, which are not related to their
//contact type.
$commonValues = array(
'Individual' => array(
'household_name',
'legal_name',
'sic_code',
'organization_name',
),
'Household' => array(
'first_name',
'middle_name',
'last_name',
'formal_title',
'job_title',
'gender_id',
'prefix_id',
'suffix_id',
'birth_date',
'organization_name',
'legal_name',
'legal_identifier',
'sic_code',
'home_URL',
'is_deceased',
'deceased_date',
),
'Organization' => array(
'first_name',
'middle_name',
'last_name',
'formal_title',
'job_title',
'gender_id',
'prefix_id',
'suffix_id',
'birth_date',
'household_name',
'is_deceased',
'deceased_date',
),
);
foreach ($commonValues[$contactType] as $value) {
unset($fields[$value]);
}
}
else {
foreach (array('Individual', 'Household', 'Organization') as $type) {
$fields = array_merge($fields,
CRM_Core_BAO_CustomField::getFieldsForImport($type,
$showAll,
FALSE,
FALSE,
FALSE,
$withMultiCustomFields
)
);
}
}
if ($isProfile) {
$fields = array_merge($fields, array(
'group' => array(
'title' => ts('Group(s)'),
'name' => 'group',
),
'tag' => array(
'title' => ts('Tag(s)'),
'name' => 'tag',
),
'note' => array(
'title' => ts('Note(s)'),
'name' => 'note',
),
'communication_style_id' => array(
'title' => ts('Communication Style'),
'name' => 'communication_style_id',
),
));
}
//Sorting fields in alphabetical order(CRM-1507)
$fields = CRM_Utils_Array::crmArraySortByField($fields, 'title');
CRM_Core_BAO_Cache::setItem($fields, 'contact fields', $cacheKeyString);
}
self::$_importableFields[$cacheKeyString] = $fields;
if (!$isProfile) {
if (!$status) {
$fields = array_merge(array('do_not_import' => array('title' => ts('- do not import -'))),
self::$_importableFields[$cacheKeyString]
);
}
else {
$fields = array_merge(array('' => array('title' => ts('- Contact Fields -'))),
self::$_importableFields[$cacheKeyString]
);
}
}
return $fields;
}
/**
* Combine all the exportable fields from the lower levels object.
*
* Currently we are using importable fields as exportable fields
*
* @param int|string $contactType contact Type
* @param bool $status
* True while exporting primary contacts.
* @param bool $export
* True when used during export.
* @param bool $search
* True when used during search, might conflict with export param?.
*
* @param bool $withMultiRecord
*
* @return array
* array of exportable Fields
*/
public static function &exportableFields($contactType = 'Individual', $status = FALSE, $export = FALSE, $search = FALSE, $withMultiRecord = FALSE, $checkPermissions = TRUE) {
if (empty($contactType)) {
$contactType = 'All';
}
$cacheKeyString = "exportableFields $contactType";
$cacheKeyString .= $export ? '_1' : '_0';
$cacheKeyString .= $status ? '_1' : '_0';
$cacheKeyString .= $search ? '_1' : '_0';
//CRM-14501 it turns out that the impact of permissioning here is sometimes inconsistent. The field that
//calculates custom fields takes into account the logged in user & caches that for all users
//as an interim fix we will cache the fields by contact
$cacheKeyString .= '_' . CRM_Core_Session::getLoggedInContactID();
if (!self::$_exportableFields || !CRM_Utils_Array::value($cacheKeyString, self::$_exportableFields)) {
if (!self::$_exportableFields) {
self::$_exportableFields = array();
}
// check if we can retrieve from database cache
$fields = CRM_Core_BAO_Cache::getItem('contact fields', $cacheKeyString);
if (!$fields) {
$fields = CRM_Contact_DAO_Contact::export();
// The fields are meant for contact types.
if (in_array($contactType, array(
'Individual',
'Household',
'Organization',
'All',
)
)) {
$fields = array_merge($fields, CRM_Core_OptionValue::getFields('', $contactType));
}
// add current employer for individuals
$fields = array_merge($fields, array(
'current_employer' =>
array(
'name' => 'organization_name',
'title' => ts('Current Employer'),
),
));
$locationType = array(
'location_type' => array(
'name' => 'location_type',
'where' => 'civicrm_location_type.name',
'title' => ts('Location Type'),
),
);
$IMProvider = array(
'im_provider' => array(
'name' => 'im_provider',
'where' => 'civicrm_im.provider_id',
'title' => ts('IM Provider'),
),
);
$locationFields = array_merge($locationType,
CRM_Core_DAO_Address::export(),
CRM_Core_DAO_Phone::export(),
CRM_Core_DAO_Email::export(),
$IMProvider,
CRM_Core_DAO_IM::export(TRUE),
CRM_Core_DAO_OpenID::export()
);
$locationFields = array_merge($locationFields,
CRM_Core_BAO_CustomField::getFieldsForImport('Address')
);
foreach ($locationFields as $key => $field) {
$locationFields[$key]['hasLocationType'] = TRUE;
}
$fields = array_merge($fields, $locationFields);
//add world region
$fields = array_merge($fields,
CRM_Core_DAO_Worldregion::export()
);
$fields = array_merge($fields,
CRM_Contact_DAO_Contact::export()
);
//website fields
$fields = array_merge($fields, CRM_Core_DAO_Website::export());
if ($contactType != 'All') {
$fields = array_merge($fields,
CRM_Core_BAO_CustomField::getFieldsForImport($contactType, $status, FALSE, $search, $checkPermissions, $withMultiRecord)
);
}
else {
foreach (array(
'Individual',
'Household',
'Organization',
) as $type) {
$fields = array_merge($fields,
CRM_Core_BAO_CustomField::getFieldsForImport($type, FALSE, FALSE, $search, $checkPermissions, $withMultiRecord)
);
}
}
$fields['current_employer_id']['title'] = ts('Current Employer ID');
//fix for CRM-791
if ($export) {
$fields = array_merge($fields, array(
'groups' => array(
'title' => ts('Group(s)'),
'name' => 'groups',
),
'tags' => array(
'title' => ts('Tag(s)'),
'name' => 'tags',
),
'notes' => array(
'title' => ts('Note(s)'),
'name' => 'notes',
),
));
}
else {
$fields = array_merge($fields, array(
'group' => array(
'title' => ts('Group(s)'),
'name' => 'group',
),
'tag' => array(
'title' => ts('Tag(s)'),
'name' => 'tag',
),
'note' => array(
'title' => ts('Note(s)'),
'name' => 'note',
),
));
}
//Sorting fields in alphabetical order(CRM-1507)
foreach ($fields as $k => $v) {
$sortArray[$k] = CRM_Utils_Array::value('title', $v);
}
$fields = array_merge($sortArray, $fields);
//unset the field which are not related to their contact type.
if ($contactType != 'All') {
$commonValues = array(
'Individual' => array(
'household_name',
'legal_name',
'sic_code',
'organization_name',
'email_greeting_custom',
'postal_greeting_custom',
'addressee_custom',
),
'Household' => array(
'first_name',
'middle_name',
'last_name',
'formal_title',
'job_title',
'gender_id',
'prefix_id',
'suffix_id',
'birth_date',
'organization_name',
'legal_name',
'legal_identifier',
'sic_code',
'home_URL',
'is_deceased',
'deceased_date',
'current_employer',
'email_greeting_custom',
'postal_greeting_custom',
'addressee_custom',
'prefix_id',
'suffix_id',
),
'Organization' => array(
'first_name',
'middle_name',
'last_name',
'formal_title',
'job_title',
'gender_id',
'prefix_id',
'suffix_id',
'birth_date',
'household_name',
'email_greeting_custom',
'postal_greeting_custom',
'prefix_id',
'suffix_id',
'gender_id',
'addressee_custom',
'is_deceased',
'deceased_date',
'current_employer',
),
);
foreach ($commonValues[$contactType] as $value) {
unset($fields[$value]);
}
}
CRM_Core_BAO_Cache::setItem($fields, 'contact fields', $cacheKeyString);
}
self::$_exportableFields[$cacheKeyString] = $fields;
}
if (!$status) {
$fields = self::$_exportableFields[$cacheKeyString];
}
else {
$fields = array_merge(array('' => array('title' => ts('- Contact Fields -'))),
self::$_exportableFields[$cacheKeyString]
);
}
return $fields;
}
/**
* Get the all contact details (Hierarchical).
*
* @param int $contactId
* Contact id.
* @param array $fields
* Fields array.
*
* @return array
* Contact details
*/
public static function getHierContactDetails($contactId, &$fields) {
$params = array(array('contact_id', '=', $contactId, 0, 0));
$options = array();
$returnProperties = self::makeHierReturnProperties($fields, $contactId);
// We don't know the contents of return properties, but we need the lower
// level ids of the contact so add a few fields.
$returnProperties['first_name'] = 1;
$returnProperties['organization_name'] = 1;
$returnProperties['household_name'] = 1;
$returnProperties['contact_type'] = 1;
$returnProperties['contact_sub_type'] = 1;
return list($query, $options) = CRM_Contact_BAO_Query::apiQuery($params, $returnProperties, $options);
}
/**
* Given a set of flat profile style field names, create a hierarchy.
*
* This is for the query to use, create the right sql.
*
* @param $fields
* @param int $contactId
* Contact id.
*
* @return array
* A hierarchical property tree if appropriate
*/
public static function &makeHierReturnProperties($fields, $contactId = NULL) {
$locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id');
$returnProperties = array();
$multipleFields = array('website' => 'url');
foreach ($fields as $name => $dontCare) {
if (strpos($name, '-') !== FALSE) {
list($fieldName, $id, $type) = CRM_Utils_System::explode('-', $name, 3);
if (!in_array($fieldName, $multipleFields)) {
if ($id == 'Primary') {
$locationTypeName = 1;
}
else {
$locationTypeName = CRM_Utils_Array::value($id, $locationTypes);
if (!$locationTypeName) {
continue;
}
}
if (empty($returnProperties['location'])) {
$returnProperties['location'] = array();
}
if (empty($returnProperties['location'][$locationTypeName])) {
$returnProperties['location'][$locationTypeName] = array();
$returnProperties['location'][$locationTypeName]['location_type'] = $id;
}
if (in_array($fieldName, array(
'phone',
'im',
'email',
'openid',
'phone_ext',
))) {
if ($type) {
$returnProperties['location'][$locationTypeName][$fieldName . '-' . $type] = 1;
}
else {
$returnProperties['location'][$locationTypeName][$fieldName] = 1;
}
}
elseif (substr($fieldName, 0, 14) === 'address_custom') {
$returnProperties['location'][$locationTypeName][substr($fieldName, 8)] = 1;
}
else {
$returnProperties['location'][$locationTypeName][$fieldName] = 1;
}
}
else {
$returnProperties['website'][$id][$fieldName] = 1;
}
}
else {
$returnProperties[$name] = 1;
}
}
return $returnProperties;
}
/**
* Return the primary location type of a contact.
*
* $params int $contactId contact_id
* $params boolean $isPrimaryExist if true, return primary contact location type otherwise null
* $params boolean $skipDefaultPriamry if true, return primary contact location type otherwise null
*
* @param int $contactId
* @param bool $skipDefaultPriamry
* @param null $block
*
* @return int
* $locationType location_type_id
*/
public static function getPrimaryLocationType($contactId, $skipDefaultPriamry = FALSE, $block = NULL) {
if ($block) {
$entityBlock = array('contact_id' => $contactId);
$blocks = CRM_Core_BAO_Location::getValues($entityBlock);
foreach ($blocks[$block] as $key => $value) {
if (!empty($value['is_primary'])) {
$locationType = CRM_Utils_Array::value('location_type_id', $value);
}
}
}
else {
$query = "
SELECT
IF ( civicrm_email.location_type_id IS NULL,
IF ( civicrm_address.location_type_id IS NULL,
IF ( civicrm_phone.location_type_id IS NULL,
IF ( civicrm_im.location_type_id IS NULL,
IF ( civicrm_openid.location_type_id IS NULL, null, civicrm_openid.location_type_id)
,civicrm_im.location_type_id)
,civicrm_phone.location_type_id)
,civicrm_address.location_type_id)
,civicrm_email.location_type_id) as locationType
FROM civicrm_contact
LEFT JOIN civicrm_email ON ( civicrm_email.is_primary = 1 AND civicrm_email.contact_id = civicrm_contact.id )
LEFT JOIN civicrm_address ON ( civicrm_address.is_primary = 1 AND civicrm_address.contact_id = civicrm_contact.id)
LEFT JOIN civicrm_phone ON ( civicrm_phone.is_primary = 1 AND civicrm_phone.contact_id = civicrm_contact.id)
LEFT JOIN civicrm_im ON ( civicrm_im.is_primary = 1 AND civicrm_im.contact_id = civicrm_contact.id)
LEFT JOIN civicrm_openid ON ( civicrm_openid.is_primary = 1 AND civicrm_openid.contact_id = civicrm_contact.id)
WHERE civicrm_contact.id = %1 ";
$params = array(1 => array($contactId, 'Integer'));
$dao = CRM_Core_DAO::executeQuery($query, $params);
$locationType = NULL;
if ($dao->fetch()) {
$locationType = $dao->locationType;
}
}
if (isset($locationType)) {
return $locationType;
}
elseif ($skipDefaultPriamry) {
// if there is no primary contact location then return null
return NULL;
}
else {
// if there is no primart contact location, then return default
// location type of the system
$defaultLocationType = CRM_Core_BAO_LocationType::getDefault();
return $defaultLocationType->id;
}
}
/**
* Get the display name, primary email and location type of a contact.
*
* @param int $id
* Id of the contact.
*
* @return array
* Array of display_name, email if found, do_not_email or (null,null,null)
*/
public static function getContactDetails($id) {
// check if the contact type
$contactType = self::getContactType($id);
$nameFields = ($contactType == 'Individual') ? "civicrm_contact.first_name, civicrm_contact.last_name, civicrm_contact.display_name" : "civicrm_contact.display_name";
$sql = "
SELECT $nameFields, civicrm_email.email, civicrm_contact.do_not_email, civicrm_email.on_hold, civicrm_contact.is_deceased
FROM civicrm_contact LEFT JOIN civicrm_email ON (civicrm_contact.id = civicrm_email.contact_id)
WHERE civicrm_contact.id = %1
ORDER BY civicrm_email.is_primary DESC";
$params = array(1 => array($id, 'Integer'));
$dao = CRM_Core_DAO::executeQuery($sql, $params);
if ($dao->fetch()) {
if ($contactType == 'Individual') {
if ($dao->first_name || $dao->last_name) {
$name = "{$dao->first_name} {$dao->last_name}";
}
else {
$name = $dao->display_name;
}
}
else {
$name = $dao->display_name;
}
$email = $dao->email;
$doNotEmail = $dao->do_not_email ? TRUE : FALSE;
$onHold = $dao->on_hold ? TRUE : FALSE;
$isDeceased = $dao->is_deceased ? TRUE : FALSE;
return array($name, $email, $doNotEmail, $onHold, $isDeceased);
}
return array(NULL, NULL, NULL, NULL, NULL);
}
/**
* Add/edit/register contacts through profile.
*
* @param array $params
* Array of profile fields to be edited/added.
* @param array $fields
* Array of fields from UFGroup.
* @param int $contactID
* Id of the contact to be edited/added.
* @param int $addToGroupID
* Specifies the default group to which contact is added.
* @param int $ufGroupId
* Uf group id (profile id).
* @param string $ctype
* @param bool $visibility
* Basically lets us know where this request is coming from.
* if via a profile from web, we restrict what groups are changed
*
* @return int
* contact id created/edited
*/
public static function createProfileContact(
&$params,
&$fields,
$contactID = NULL,
$addToGroupID = NULL,
$ufGroupId = NULL,
$ctype = NULL,
$visibility = FALSE
) {
// add ufGroupID to params array ( CRM-2012 )
if ($ufGroupId) {
$params['uf_group_id'] = $ufGroupId;
}
self::addBillingNameFieldsIfOtherwiseNotSet($params);
// If a user has logged in, or accessed via a checksum
// Then deliberately 'blanking' a value in the profile should remove it from their record
$session = CRM_Core_Session::singleton();
$params['updateBlankLocInfo'] = TRUE;
if (($session->get('authSrc') & (CRM_Core_Permission::AUTH_SRC_CHECKSUM + CRM_Core_Permission::AUTH_SRC_LOGIN)) == 0) {
$params['updateBlankLocInfo'] = FALSE;
}
if ($contactID) {
$editHook = TRUE;
CRM_Utils_Hook::pre('edit', 'Profile', $contactID, $params);
}
else {
$editHook = FALSE;
CRM_Utils_Hook::pre('create', 'Profile', NULL, $params);
}
list($data, $contactDetails) = self::formatProfileContactParams($params, $fields, $contactID, $ufGroupId, $ctype);
// manage is_opt_out
if (array_key_exists('is_opt_out', $fields) && array_key_exists('is_opt_out', $params)) {
$wasOptOut = CRM_Utils_Array::value('is_opt_out', $contactDetails, FALSE);
$isOptOut = CRM_Utils_Array::value('is_opt_out', $params, FALSE);
$data['is_opt_out'] = $isOptOut;
// on change, create new civicrm_subscription_history entry
if (($wasOptOut != $isOptOut) && !empty($contactDetails['contact_id'])) {
$shParams = array(
'contact_id' => $contactDetails['contact_id'],
'status' => $isOptOut ? 'Removed' : 'Added',
'method' => 'Web',
);
CRM_Contact_BAO_SubscriptionHistory::create($shParams);
}
}
$contact = self::create($data);
// contact is null if the profile does not have any contact fields
if ($contact) {
$contactID = $contact->id;
}
if (empty($contactID)) {
CRM_Core_Error::fatal('Cannot proceed without a valid contact id');
}
// Process group and tag
if (!empty($fields['group'])) {
$method = 'Admin';
// this for sure means we are coming in via profile since i added it to fix
// removing contacts from user groups -- lobo
if ($visibility) {
$method = 'Web';
}
CRM_Contact_BAO_GroupContact::create($params['group'], $contactID, $visibility, $method);
}
if (!empty($fields['tag'])) {
CRM_Core_BAO_EntityTag::create($params['tag'], 'civicrm_contact', $contactID);
}
//to add profile in default group
if (is_array($addToGroupID)) {
$contactIds = array($contactID);
foreach ($addToGroupID as $groupId) {
CRM_Contact_BAO_GroupContact::addContactsToGroup($contactIds, $groupId);
}
}
elseif ($addToGroupID) {
$contactIds = array($contactID);
CRM_Contact_BAO_GroupContact::addContactsToGroup($contactIds, $addToGroupID);
}
CRM_Contact_BAO_GroupContactCache::opportunisticCacheFlush();
if ($editHook) {
CRM_Utils_Hook::post('edit', 'Profile', $contactID, $params);
}
else {
CRM_Utils_Hook::post('create', 'Profile', $contactID, $params);
}
return $contactID;
}
/**
* Format profile contact parameters.
*
* @param array $params
* @param $fields
* @param int $contactID
* @param int $ufGroupId
* @param null $ctype
* @param bool $skipCustom
*
* @return array
*/
public static function formatProfileContactParams(
&$params,
&$fields,
$contactID = NULL,
$ufGroupId = NULL,
$ctype = NULL,
$skipCustom = FALSE
) {
$data = $contactDetails = array();
// get the contact details (hier)
if ($contactID) {
list($details, $options) = self::getHierContactDetails($contactID, $fields);
$contactDetails = $details[$contactID];
$data['contact_type'] = CRM_Utils_Array::value('contact_type', $contactDetails);
$data['contact_sub_type'] = CRM_Utils_Array::value('contact_sub_type', $contactDetails);
}
else {
//we should get contact type only if contact
if ($ufGroupId) {
$data['contact_type'] = CRM_Core_BAO_UFField::getProfileType($ufGroupId);
//special case to handle profile with only contact fields
if ($data['contact_type'] == 'Contact') {
$data['contact_type'] = 'Individual';
}
elseif (CRM_Contact_BAO_ContactType::isaSubType($data['contact_type'])) {
$data['contact_type'] = CRM_Contact_BAO_ContactType::getBasicType($data['contact_type']);
}
}
elseif ($ctype) {
$data['contact_type'] = $ctype;
}
else {
$data['contact_type'] = 'Individual';
}
}
//fix contact sub type CRM-5125
if (array_key_exists('contact_sub_type', $params) &&
!empty($params['contact_sub_type'])
) {
$data['contact_sub_type'] = CRM_Utils_Array::implodePadded($params['contact_sub_type']);
}
elseif (array_key_exists('contact_sub_type_hidden', $params) &&
!empty($params['contact_sub_type_hidden'])
) {
// if profile was used, and had any subtype, we obtain it from there
//CRM-13596 - add to existing contact types, rather than overwriting
$data_contact_sub_type_arr = CRM_Utils_Array::explodePadded($data['contact_sub_type']);
if (!in_array($params['contact_sub_type_hidden'], $data_contact_sub_type_arr)) {
//CRM-20517 - make sure contact_sub_type gets the correct delimiters
$data['contact_sub_type'] = trim($data['contact_sub_type'], CRM_Core_DAO::VALUE_SEPARATOR);
$data['contact_sub_type'] = CRM_Core_DAO::VALUE_SEPARATOR . $data['contact_sub_type'] . CRM_Utils_Array::implodePadded($params['contact_sub_type_hidden']);
}
}
if ($ctype == 'Organization') {
$data['organization_name'] = CRM_Utils_Array::value('organization_name', $contactDetails);
}
elseif ($ctype == 'Household') {
$data['household_name'] = CRM_Utils_Array::value('household_name', $contactDetails);
}
$locationType = array();
$count = 1;
if ($contactID) {
//add contact id
$data['contact_id'] = $contactID;
$primaryLocationType = self::getPrimaryLocationType($contactID);
}
else {
$defaultLocation = CRM_Core_BAO_LocationType::getDefault();
$defaultLocationId = $defaultLocation->id;
}
$billingLocationTypeId = CRM_Core_BAO_LocationType::getBilling();
$blocks = array('email', 'phone', 'im', 'openid');
$multiplFields = array('url');
// prevent overwritten of formatted array, reset all block from
// params if it is not in valid format (since import pass valid format)
foreach ($blocks as $blk) {
if (array_key_exists($blk, $params) &&
!is_array($params[$blk])
) {
unset($params[$blk]);
}
}
$primaryPhoneLoc = NULL;
$session = CRM_Core_Session::singleton();
foreach ($params as $key => $value) {
list($fieldName, $locTypeId, $typeId) = CRM_Utils_System::explode('-', $key, 3);
if ($locTypeId == 'Primary') {
if ($contactID) {
if (in_array($fieldName, $blocks)) {
$locTypeId = self::getPrimaryLocationType($contactID, FALSE, $fieldName);
}
else {
$locTypeId = self::getPrimaryLocationType($contactID, FALSE, 'address');
}
$primaryLocationType = $locTypeId;
}
else {
$locTypeId = $defaultLocationId;
}
}
if (is_numeric($locTypeId) &&
!in_array($fieldName, $multiplFields) &&
substr($fieldName, 0, 7) != 'custom_'
) {
$index = $locTypeId;
if (is_numeric($typeId)) {
$index .= '-' . $typeId;
}
if (!in_array($index, $locationType)) {
$locationType[$count] = $index;
$count++;
}
$loc = CRM_Utils_Array::key($index, $locationType);
$blockName = in_array($fieldName, $blocks) ? $fieldName : 'address';
$data[$blockName][$loc]['location_type_id'] = $locTypeId;
//set is_billing true, for location type "Billing"
if ($locTypeId == $billingLocationTypeId) {
$data[$blockName][$loc]['is_billing'] = 1;
}
if ($contactID) {
//get the primary location type
if ($locTypeId == $primaryLocationType) {
$data[$blockName][$loc]['is_primary'] = 1;
}
}
elseif ($locTypeId == $defaultLocationId) {
$data[$blockName][$loc]['is_primary'] = 1;
}
if (in_array($fieldName, array('phone'))) {
if ($typeId) {
$data['phone'][$loc]['phone_type_id'] = $typeId;
}
else {
$data['phone'][$loc]['phone_type_id'] = '';
}
$data['phone'][$loc]['phone'] = $value;
//special case to handle primary phone with different phone types
// in this case we make first phone type as primary
if (isset($data['phone'][$loc]['is_primary']) && !$primaryPhoneLoc) {
$primaryPhoneLoc = $loc;
}
if ($loc != $primaryPhoneLoc) {
unset($data['phone'][$loc]['is_primary']);
}
}
elseif ($fieldName == 'phone_ext') {
$data['phone'][$loc]['phone_ext'] = $value;
}
elseif ($fieldName == 'email') {
$data['email'][$loc]['email'] = $value;
if (empty($contactID)) {
$data['email'][$loc]['is_primary'] = 1;
}
}
elseif ($fieldName == 'im') {
if (isset($params[$key . '-provider_id'])) {
$data['im'][$loc]['provider_id'] = $params[$key . '-provider_id'];
}
if (strpos($key, '-provider_id') !== FALSE) {
$data['im'][$loc]['provider_id'] = $params[$key];
}
else {
$data['im'][$loc]['name'] = $value;
}
}
elseif ($fieldName == 'openid') {
$data['openid'][$loc]['openid'] = $value;
}
else {
if ($fieldName === 'state_province') {
// CRM-3393
if (is_numeric($value) && ((int ) $value) >= 1000) {
$data['address'][$loc]['state_province_id'] = $value;
}
elseif (empty($value)) {
$data['address'][$loc]['state_province_id'] = '';
}
else {
$data['address'][$loc]['state_province'] = $value;
}
}
elseif ($fieldName === 'country') {
// CRM-3393
if (is_numeric($value) && ((int ) $value) >= 1000
) {
$data['address'][$loc]['country_id'] = $value;
}
elseif (empty($value)) {
$data['address'][$loc]['country_id'] = '';
}
else {
$data['address'][$loc]['country'] = $value;
}
}
elseif ($fieldName === 'county') {
$data['address'][$loc]['county_id'] = $value;
}
elseif ($fieldName == 'address_name') {
$data['address'][$loc]['name'] = $value;
}
elseif (substr($fieldName, 0, 14) === 'address_custom') {
$data['address'][$loc][substr($fieldName, 8)] = $value;
}
else {
$data['address'][$loc][$fieldName] = $value;
}
}
}
else {
if (substr($key, 0, 4) === 'url-') {
$websiteField = explode('-', $key);
$data['website'][$websiteField[1]]['website_type_id'] = $websiteField[1];
$data['website'][$websiteField[1]]['url'] = $value;
}
elseif (in_array($key, self::$_greetingTypes, TRUE)) {
//save email/postal greeting and addressee values if any, CRM-4575
$data[$key . '_id'] = $value;
}
elseif (!$skipCustom && ($customFieldId = CRM_Core_BAO_CustomField::getKeyID($key))) {
// for autocomplete transfer hidden value instead of label
if ($params[$key] && isset($params[$key . '_id'])) {
$value = $params[$key . '_id'];
}
// we need to append time with date
if ($params[$key] && isset($params[$key . '_time'])) {
$value .= ' ' . $params[$key . '_time'];
}
// if auth source is not checksum / login && $value is blank, do not proceed - CRM-10128
if (($session->get('authSrc') & (CRM_Core_Permission::AUTH_SRC_CHECKSUM + CRM_Core_Permission::AUTH_SRC_LOGIN)) == 0 &&
($value == '' || !isset($value))
) {
continue;
}
$valueId = NULL;
if (!empty($params['customRecordValues'])) {
if (is_array($params['customRecordValues']) && !empty($params['customRecordValues'])) {
foreach ($params['customRecordValues'] as $recId => $customFields) {
if (is_array($customFields) && !empty($customFields)) {
foreach ($customFields as $customFieldName) {
if ($customFieldName == $key) {
$valueId = $recId;
break;
}
}
}
}
}
}
//CRM-13596 - check for contact_sub_type_hidden first
if (array_key_exists('contact_sub_type_hidden', $params)) {
$type = $params['contact_sub_type_hidden'];
}
else {
$type = $data['contact_type'];
if (!empty($data['contact_sub_type'])) {
$type = CRM_Utils_Array::explodePadded($data['contact_sub_type']);
}
}
CRM_Core_BAO_CustomField::formatCustomField($customFieldId,
$data['custom'],
$value,
$type,
$valueId,
$contactID,
FALSE,
FALSE
);
}
elseif ($key == 'edit') {
continue;
}
else {
if ($key == 'location') {
foreach ($value as $locationTypeId => $field) {
foreach ($field as $block => $val) {
if ($block == 'address' && array_key_exists('address_name', $val)) {
$value[$locationTypeId][$block]['name'] = $value[$locationTypeId][$block]['address_name'];
}
}
}
}
if ($key == 'phone' && isset($params['phone_ext'])) {
$data[$key] = $value;
foreach ($value as $cnt => $phoneBlock) {
if ($params[$key][$cnt]['location_type_id'] == $params['phone_ext'][$cnt]['location_type_id']) {
$data[$key][$cnt]['phone_ext'] = CRM_Utils_Array::retrieveValueRecursive($params['phone_ext'][$cnt], 'phone_ext');
}
}
}
elseif (in_array($key,
array(
'nick_name',
'job_title',
'middle_name',
'birth_date',
'gender_id',
'current_employer',
'prefix_id',
'suffix_id',
)) &&
($value == '' || !isset($value)) &&
($session->get('authSrc') & (CRM_Core_Permission::AUTH_SRC_CHECKSUM + CRM_Core_Permission::AUTH_SRC_LOGIN)) == 0 ||
($key == 'current_employer' && empty($params['current_employer']))) {
// CRM-10128: if auth source is not checksum / login && $value is blank, do not fill $data with empty value
// to avoid update with empty values
continue;
}
else {
$data[$key] = $value;
}
}
}
}
if (!isset($data['contact_type'])) {
$data['contact_type'] = 'Individual';
}
//set the values for checkboxes (do_not_email, do_not_mail, do_not_trade, do_not_phone)
$privacy = CRM_Core_SelectValues::privacy();
foreach ($privacy as $key => $value) {
if (array_key_exists($key, $fields)) {
// do not reset values for existing contacts, if fields are added to a profile
if (array_key_exists($key, $params)) {
$data[$key] = $params[$key];
if (empty($params[$key])) {
$data[$key] = 0;
}
}
elseif (!$contactID) {
$data[$key] = 0;
}
}
}
return array($data, $contactDetails);
}
/**
* Find the get contact details.
*
* This function does not respect ACLs for now, which might need to be rectified at some
* stage based on how its used.
*
* @param string $mail
* Primary email address of the contact.
* @param string $ctype
* Contact type.
*
* @return object|null
* $dao contact details
*/
public static function matchContactOnEmail($mail, $ctype = NULL) {
$strtolower = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower';
$mail = $strtolower(trim($mail));
$query = "
SELECT civicrm_contact.id as contact_id,
civicrm_contact.hash as hash,
civicrm_contact.contact_type as contact_type,
civicrm_contact.contact_sub_type as contact_sub_type
FROM civicrm_contact
INNER JOIN civicrm_email ON ( civicrm_contact.id = civicrm_email.contact_id )";
if (Civi::settings()->get('uniq_email_per_site')) {
// try to find a match within a site (multisite).
$groups = CRM_Core_BAO_Domain::getChildGroupIds();
if (!empty($groups)) {
$query .= "
INNER JOIN civicrm_group_contact gc ON
(civicrm_contact.id = gc.contact_id AND gc.status = 'Added' AND gc.group_id IN (" . implode(',', $groups) . "))";
}
}
$query .= "
WHERE civicrm_email.email = %1 AND civicrm_contact.is_deleted=0";
$p = array(1 => array($mail, 'String'));
if ($ctype) {
$query .= " AND civicrm_contact.contact_type = %3";
$p[3] = array($ctype, 'String');
}
$query .= " ORDER BY civicrm_email.is_primary DESC";
$dao = CRM_Core_DAO::executeQuery($query, $p);
if ($dao->fetch()) {
return $dao;
}
return NULL;
}
/**
* Find the contact details associated with an OpenID.
*
* @param string $openId
* OpenId of the contact.
* @param string $ctype
* Contact type.
*
* @return object|null
* $dao contact details
*/
public static function matchContactOnOpenId($openId, $ctype = NULL) {
$strtolower = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower';
$openId = $strtolower(trim($openId));
$query = "
SELECT civicrm_contact.id as contact_id,
civicrm_contact.hash as hash,
civicrm_contact.contact_type as contact_type,
civicrm_contact.contact_sub_type as contact_sub_type
FROM civicrm_contact
INNER JOIN civicrm_openid ON ( civicrm_contact.id = civicrm_openid.contact_id )
WHERE civicrm_openid.openid = %1";
$p = array(1 => array($openId, 'String'));
if ($ctype) {
$query .= " AND civicrm_contact.contact_type = %3";
$p[3] = array($ctype, 'String');
}
$query .= " ORDER BY civicrm_openid.is_primary DESC";
$dao = CRM_Core_DAO::executeQuery($query, $p);
if ($dao->fetch()) {
return $dao;
}
return NULL;
}
/**
* Get primary email of the contact.
*
* @param int $contactID
* Contact id.
*
* @return string
* Email address if present else null
*/
public static function getPrimaryEmail($contactID) {
// fetch the primary email
$query = "
SELECT civicrm_email.email as email
FROM civicrm_contact
LEFT JOIN civicrm_email ON ( civicrm_contact.id = civicrm_email.contact_id )
WHERE civicrm_email.is_primary = 1
AND civicrm_contact.id = %1";
$p = array(1 => array($contactID, 'Integer'));
$dao = CRM_Core_DAO::executeQuery($query, $p);
$email = NULL;
if ($dao->fetch()) {
$email = $dao->email;
}
$dao->free();
return $email;
}
/**
* Function to get primary OpenID of the contact.
*
* @param int $contactID
* Contact id.
*
* @return string
* >openid OpenID if present else null
*/
public static function getPrimaryOpenId($contactID) {
// fetch the primary OpenID
$query = "
SELECT civicrm_openid.openid as openid
FROM civicrm_contact
LEFT JOIN civicrm_openid ON ( civicrm_contact.id = civicrm_openid.contact_id )
WHERE civicrm_contact.id = %1
AND civicrm_openid.is_primary = 1";
$p = array(1 => array($contactID, 'Integer'));
$dao = CRM_Core_DAO::executeQuery($query, $p);
$openId = NULL;
if ($dao->fetch()) {
$openId = $dao->openid;
}
$dao->free();
return $openId;
}
/**
* Fetch the object and store the values in the values array.
*
* @param array $params
* Input parameters to find object.
* @param array $values
* Output values of the object.
*
* @return CRM_Contact_BAO_Contact|null
* The found object or null
*/
public static function getValues(&$params, &$values) {
$contact = new CRM_Contact_BAO_Contact();
$contact->copyValues($params);
if ($contact->find(TRUE)) {
CRM_Core_DAO::storeValues($contact, $values);
$privacy = array();
foreach (self::$_commPrefs as $name) {
if (isset($contact->$name)) {
$privacy[$name] = $contact->$name;
}
}
if (!empty($privacy)) {
$values['privacy'] = $privacy;
}
// communication Prefferance
$preffComm = $comm = array();
$comm = explode(CRM_Core_DAO::VALUE_SEPARATOR,
$contact->preferred_communication_method
);
foreach ($comm as $value) {
$preffComm[$value] = 1;
}
$temp = array('preferred_communication_method' => $contact->preferred_communication_method);
$names = array(
'preferred_communication_method' => array(
'newName' => 'preferred_communication_method_display',
'groupName' => 'preferred_communication_method',
),
);
// @todo This can be figured out from metadata & we can avoid the uncached query.
CRM_Core_OptionGroup::lookupValues($temp, $names, FALSE);
$values['preferred_communication_method'] = $preffComm;
$values['preferred_communication_method_display'] = CRM_Utils_Array::value('preferred_communication_method_display', $temp);
if ($contact->preferred_mail_format) {
$preferredMailingFormat = CRM_Core_SelectValues::pmf();
$values['preferred_mail_format'] = $preferredMailingFormat[$contact->preferred_mail_format];
}
// get preferred languages
if (!empty($contact->preferred_language)) {
$values['preferred_language'] = CRM_Core_PseudoConstant::getLabel('CRM_Contact_DAO_Contact', 'preferred_language', $contact->preferred_language);
}
// Calculating Year difference
if ($contact->birth_date) {
$birthDate = CRM_Utils_Date::customFormat($contact->birth_date, '%Y%m%d');
if ($birthDate < date('Ymd')) {
$age = CRM_Utils_Date::calculateAge($birthDate);
$values['age']['y'] = CRM_Utils_Array::value('years', $age);
$values['age']['m'] = CRM_Utils_Array::value('months', $age);
}
}
$contact->contact_id = $contact->id;
return $contact;
}
return NULL;
}
/**
* Given the component name and returns the count of participation of contact.
*
* @param string $component
* Input component name.
* @param int $contactId
* Input contact id.
* @param string $tableName
* Optional tableName if component is custom group.
*
* @return int
* total number in database
*/
public static function getCountComponent($component, $contactId, $tableName = NULL) {
$object = NULL;
switch ($component) {
case 'tag':
return CRM_Core_BAO_EntityTag::getContactTags($contactId, TRUE);
case 'rel':
$result = CRM_Contact_BAO_Relationship::getRelationship($contactId,
CRM_Contact_BAO_Relationship::CURRENT,
0, 1, 0,
NULL, NULL,
TRUE
);
return $result;
case 'group':
return CRM_Contact_BAO_GroupContact::getContactGroup($contactId, "Added", NULL, TRUE);
case 'log':
if (CRM_Core_BAO_Log::useLoggingReport()) {
return FALSE;
}
return CRM_Core_BAO_Log::getContactLogCount($contactId);
case 'note':
return CRM_Core_BAO_Note::getContactNoteCount($contactId);
case 'contribution':
return CRM_Contribute_BAO_Contribution::contributionCount($contactId);
case 'membership':
return CRM_Member_BAO_Membership::getContactMembershipCount($contactId, TRUE);
case 'participant':
return CRM_Event_BAO_Participant::getContactParticipantCount($contactId);
case 'pledge':
return CRM_Pledge_BAO_Pledge::getContactPledgeCount($contactId);
case 'case':
return CRM_Case_BAO_Case::caseCount($contactId);
case 'grant':
return CRM_Grant_BAO_Grant::getContactGrantCount($contactId);
case 'activity':
$input = array(
'contact_id' => $contactId,
'admin' => FALSE,
'caseId' => NULL,
'context' => 'activity',
);
return CRM_Activity_BAO_Activity::deprecatedGetActivitiesCount($input);
case 'mailing':
$params = array('contact_id' => $contactId);
return CRM_Mailing_BAO_Mailing::getContactMailingsCount($params);
default:
$custom = explode('_', $component);
if ($custom['0'] = 'custom') {
if (!$tableName) {
$tableName = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $custom['1'], 'table_name');
}
$queryString = "SELECT count(id) FROM {$tableName} WHERE entity_id = {$contactId}";
return CRM_Core_DAO::singleValueQuery($queryString);
}
}
}
/**
* Process greetings and cache.
*
* @param object $contact
* Contact object after save.
* @param bool $useDefaults
* Use default greeting values.
*/
public static function processGreetings(&$contact, $useDefaults = FALSE) {
if ($useDefaults) {
//retrieve default greetings
$defaultGreetings = CRM_Core_PseudoConstant::greetingDefaults();
$contactDefaults = $defaultGreetings[$contact->contact_type];
}
// The contact object has not always required the
// fields that are required to calculate greetings
// so we need to retrieve it again.
if ($contact->_query !== FALSE) {
$contact->find(TRUE);
}
// store object values to an array
$contactDetails = array();
CRM_Core_DAO::storeValues($contact, $contactDetails);
$contactDetails = array(array($contact->id => $contactDetails));
$emailGreetingString = $postalGreetingString = $addresseeString = NULL;
$updateQueryString = array();
//cache email and postal greeting to greeting display
if ($contact->email_greeting_custom != 'null' && $contact->email_greeting_custom) {
$emailGreetingString = $contact->email_greeting_custom;
}
elseif ($contact->email_greeting_id != 'null' && $contact->email_greeting_id) {
// the filter value for Individual contact type is set to 1
$filter = array(
'contact_type' => $contact->contact_type,
'greeting_type' => 'email_greeting',
);
$emailGreeting = CRM_Core_PseudoConstant::greeting($filter);
$emailGreetingString = $emailGreeting[$contact->email_greeting_id];
$updateQueryString[] = " email_greeting_custom = NULL ";
}
else {
if ($useDefaults) {
reset($contactDefaults['email_greeting']);
$emailGreetingID = key($contactDefaults['email_greeting']);
$emailGreetingString = $contactDefaults['email_greeting'][$emailGreetingID];
$updateQueryString[] = " email_greeting_id = $emailGreetingID ";
$updateQueryString[] = " email_greeting_custom = NULL ";
}
elseif ($contact->email_greeting_custom) {
$updateQueryString[] = " email_greeting_display = NULL ";
}
}
if ($emailGreetingString) {
CRM_Contact_BAO_Contact_Utils::processGreetingTemplate($emailGreetingString,
$contactDetails,
$contact->id,
'CRM_Contact_BAO_Contact'
);
$emailGreetingString = CRM_Core_DAO::escapeString(CRM_Utils_String::stripSpaces($emailGreetingString));
$updateQueryString[] = " email_greeting_display = '{$emailGreetingString}'";
}
//postal greetings
if ($contact->postal_greeting_custom != 'null' && $contact->postal_greeting_custom) {
$postalGreetingString = $contact->postal_greeting_custom;
}
elseif ($contact->postal_greeting_id != 'null' && $contact->postal_greeting_id) {
$filter = array(
'contact_type' => $contact->contact_type,
'greeting_type' => 'postal_greeting',
);
$postalGreeting = CRM_Core_PseudoConstant::greeting($filter);
$postalGreetingString = $postalGreeting[$contact->postal_greeting_id];
$updateQueryString[] = " postal_greeting_custom = NULL ";
}
else {
if ($useDefaults) {
reset($contactDefaults['postal_greeting']);
$postalGreetingID = key($contactDefaults['postal_greeting']);
$postalGreetingString = $contactDefaults['postal_greeting'][$postalGreetingID];
$updateQueryString[] = " postal_greeting_id = $postalGreetingID ";
$updateQueryString[] = " postal_greeting_custom = NULL ";
}
elseif ($contact->postal_greeting_custom) {
$updateQueryString[] = " postal_greeting_display = NULL ";
}
}
if ($postalGreetingString) {
CRM_Contact_BAO_Contact_Utils::processGreetingTemplate($postalGreetingString,
$contactDetails,
$contact->id,
'CRM_Contact_BAO_Contact'
);
$postalGreetingString = CRM_Core_DAO::escapeString(CRM_Utils_String::stripSpaces($postalGreetingString));
$updateQueryString[] = " postal_greeting_display = '{$postalGreetingString}'";
}
// addressee
if ($contact->addressee_custom != 'null' && $contact->addressee_custom) {
$addresseeString = $contact->addressee_custom;
}
elseif ($contact->addressee_id != 'null' && $contact->addressee_id) {
$filter = array(
'contact_type' => $contact->contact_type,
'greeting_type' => 'addressee',
);
$addressee = CRM_Core_PseudoConstant::greeting($filter);
$addresseeString = $addressee[$contact->addressee_id];
$updateQueryString[] = " addressee_custom = NULL ";
}
else {
if ($useDefaults) {
reset($contactDefaults['addressee']);
$addresseeID = key($contactDefaults['addressee']);
$addresseeString = $contactDefaults['addressee'][$addresseeID];
$updateQueryString[] = " addressee_id = $addresseeID ";
$updateQueryString[] = " addressee_custom = NULL ";
}
elseif ($contact->addressee_custom) {
$updateQueryString[] = " addressee_display = NULL ";
}
}
if ($addresseeString) {
CRM_Contact_BAO_Contact_Utils::processGreetingTemplate($addresseeString,
$contactDetails,
$contact->id,
'CRM_Contact_BAO_Contact'
);
$addresseeString = CRM_Core_DAO::escapeString(CRM_Utils_String::stripSpaces($addresseeString));
$updateQueryString[] = " addressee_display = '{$addresseeString}'";
}
if (!empty($updateQueryString)) {
$updateQueryString = implode(',', $updateQueryString);
$queryString = "UPDATE civicrm_contact SET {$updateQueryString} WHERE id = {$contact->id}";
CRM_Core_DAO::executeQuery($queryString);
}
}
/**
* Retrieve loc block ids w/ given condition.
*
* @param int $contactId
* Contact id.
* @param array $criteria
* Key => value pair which should be.
* fulfill by return record ids.
* @param string $condOperator
* Operator use for grouping multiple conditions.
*
* @return array
* loc block ids which fulfill condition.
*/
public static function getLocBlockIds($contactId, $criteria = array(), $condOperator = 'AND') {
$locBlockIds = array();
if (!$contactId) {
return $locBlockIds;
}
foreach (array('Email', 'OpenID', 'Phone', 'Address', 'IM') as $block) {
$name = strtolower($block);
$className = "CRM_Core_DAO_$block";
$blockDAO = new $className();
// build the condition.
if (is_array($criteria)) {
$fields = $blockDAO->fields();
$conditions = array();
foreach ($criteria as $field => $value) {
if (array_key_exists($field, $fields)) {
$cond = "( $field = $value )";
// value might be zero or null.
if (!$value || strtolower($value) == 'null') {
$cond = "( $field = 0 OR $field IS NULL )";
}
$conditions[] = $cond;
}
}
if (!empty($conditions)) {
$blockDAO->whereAdd(implode(" $condOperator ", $conditions));
}
}
$blockDAO->contact_id = $contactId;
$blockDAO->find();
while ($blockDAO->fetch()) {
$locBlockIds[$name][] = $blockDAO->id;
}
$blockDAO->free();
}
return $locBlockIds;
}
/**
* Build context menu items.
*
* @param int $contactId
*
* @return array
* Array of context menu for logged in user.
*/
public static function contextMenu($contactId = NULL) {
$menu = array(
'view' => array(
'title' => ts('View Contact'),
'weight' => 0,
'ref' => 'view-contact',
'class' => 'no-popup',
'key' => 'view',
'permissions' => array('view all contacts'),
),
'add' => array(
'title' => ts('Edit Contact'),
'weight' => 0,
'ref' => 'edit-contact',
'class' => 'no-popup',
'key' => 'add',
'permissions' => array('edit all contacts'),
),
'delete' => array(
'title' => ts('Delete Contact'),
'weight' => 0,
'ref' => 'delete-contact',
'key' => 'delete',
'permissions' => array('access deleted contacts', 'delete contacts'),
),
'contribution' => array(
'title' => ts('Add Contribution'),
'weight' => 5,
'ref' => 'new-contribution',
'key' => 'contribution',
'tab' => 'contribute',
'component' => 'CiviContribute',
'href' => CRM_Utils_System::url('civicrm/contact/view/contribution',
'reset=1&action=add&context=contribution'
),
'permissions' => array(
'access CiviContribute',
'edit contributions',
),
),
'participant' => array(
'title' => ts('Register for Event'),
'weight' => 10,
'ref' => 'new-participant',
'key' => 'participant',
'tab' => 'participant',
'component' => 'CiviEvent',
'href' => CRM_Utils_System::url('civicrm/contact/view/participant', 'reset=1&action=add&context=participant'),
'permissions' => array(
'access CiviEvent',
'edit event participants',
),
),
'activity' => array(
'title' => ts('Record Activity'),
'weight' => 35,
'ref' => 'new-activity',
'key' => 'activity',
'permissions' => array('edit all contacts'),
),
'pledge' => array(
'title' => ts('Add Pledge'),
'weight' => 15,
'ref' => 'new-pledge',
'key' => 'pledge',
'tab' => 'pledge',
'href' => CRM_Utils_System::url('civicrm/contact/view/pledge',
'reset=1&action=add&context=pledge'
),
'component' => 'CiviPledge',
'permissions' => array(
'access CiviPledge',
'edit pledges',
),
),
'membership' => array(
'title' => ts('Add Membership'),
'weight' => 20,
'ref' => 'new-membership',
'key' => 'membership',
'tab' => 'member',
'component' => 'CiviMember',
'href' => CRM_Utils_System::url('civicrm/contact/view/membership',
'reset=1&action=add&context=membership'
),
'permissions' => array(
'access CiviMember',
'edit memberships',
),
),
'case' => array(
'title' => ts('Add Case'),
'weight' => 25,
'ref' => 'new-case',
'key' => 'case',
'tab' => 'case',
'component' => 'CiviCase',
'href' => CRM_Utils_System::url('civicrm/case/add', 'reset=1&action=add&context=case'),
'permissions' => array('add cases'),
),
'grant' => array(
'title' => ts('Add Grant'),
'weight' => 26,
'ref' => 'new-grant',
'key' => 'grant',
'tab' => 'grant',
'component' => 'CiviGrant',
'href' => CRM_Utils_System::url('civicrm/contact/view/grant',
'reset=1&action=add&context=grant'
),
'permissions' => array('edit grants'),
),
'rel' => array(
'title' => ts('Add Relationship'),
'weight' => 30,
'ref' => 'new-relationship',
'key' => 'rel',
'tab' => 'rel',
'href' => CRM_Utils_System::url('civicrm/contact/view/rel',
'reset=1&action=add'
),
'permissions' => array('edit all contacts'),
),
'note' => array(
'title' => ts('Add Note'),
'weight' => 40,
'ref' => 'new-note',
'key' => 'note',
'tab' => 'note',
'class' => 'medium-popup',
'href' => CRM_Utils_System::url('civicrm/contact/view/note',
'reset=1&action=add'
),
'permissions' => array('edit all contacts'),
),
'email' => array(
'title' => ts('Send an Email'),
'weight' => 45,
'ref' => 'new-email',
'key' => 'email',
'permissions' => array('view all contacts'),
),
'group' => array(
'title' => ts('Add to Group'),
'weight' => 50,
'ref' => 'group-add-contact',
'key' => 'group',
'tab' => 'group',
'permissions' => array('edit groups'),
),
'tag' => array(
'title' => ts('Tag Contact'),
'weight' => 55,
'ref' => 'tag-contact',
'key' => 'tag',
'tab' => 'tag',
'permissions' => array('edit all contacts'),
),
);
$menu['otherActions'] = array(
'print' => array(
'title' => ts('Print Summary'),
'description' => ts('Printer-friendly view of this page.'),
'weight' => 5,
'ref' => 'crm-contact-print',
'key' => 'print',
'tab' => 'print',
'href' => CRM_Utils_System::url('civicrm/contact/view/print',
"reset=1&print=1"
),
'class' => 'print',
'icon' => 'crm-i fa-print',
),
'vcard' => array(
'title' => ts('vCard'),
'description' => ts('vCard record for this contact.'),
'weight' => 10,
'ref' => 'crm-contact-vcard',
'key' => 'vcard',
'tab' => 'vcard',
'href' => CRM_Utils_System::url('civicrm/contact/view/vcard',
"reset=1"
),
'class' => 'vcard',
'icon' => 'crm-i fa-list-alt',
),
);
if (CRM_Core_Permission::check('access Contact Dashboard')) {
$menu['otherActions']['dashboard'] = array(
'title' => ts('Contact Dashboard'),
'description' => ts('Contact Dashboard'),
'weight' => 15,
'ref' => 'crm-contact-dashboard',
'key' => 'dashboard',
'tab' => 'dashboard',
'class' => 'dashboard',
// NOTE: As an alternative you can also build url on CMS specific way
// as CRM_Core_Config::singleton()->userSystem->getUserRecordUrl($contactId)
'href' => CRM_Utils_System::url('civicrm/user', "reset=1&id={$contactId}"),
'icon' => 'crm-i fa-tachometer',
);
}
$uid = CRM_Core_BAO_UFMatch::getUFId($contactId);
if ($uid) {
$menu['otherActions']['user-record'] = array(
'title' => ts('User Record'),
'description' => ts('User Record'),
'weight' => 20,
'ref' => 'crm-contact-user-record',
'key' => 'user-record',
'tab' => 'user-record',
'class' => 'user-record',
'href' => CRM_Core_Config::singleton()->userSystem->getUserRecordUrl($contactId),
'icon' => 'crm-i fa-user',
);
}
elseif (CRM_Core_Config::singleton()->userSystem->checkPermissionAddUser()) {
$menu['otherActions']['user-add'] = array(
'title' => ts('Create User Record'),
'description' => ts('Create User Record'),
'weight' => 25,
'ref' => 'crm-contact-user-add',
'key' => 'user-add',
'tab' => 'user-add',
'class' => 'user-add',
'href' => CRM_Utils_System::url('civicrm/contact/view/useradd', 'reset=1&action=add&cid=' . $contactId),
'icon' => 'crm-i fa-user-plus',
);
}
CRM_Utils_Hook::summaryActions($menu, $contactId);
//1. check for component is active.
//2. check for user permissions.
//3. check for acls.
//3. edit and view contact are directly accessible to user.
$aclPermissionedTasks = array(
'view-contact',
'edit-contact',
'new-activity',
'new-email',
'group-add-contact',
'tag-contact',
'delete-contact',
);
$corePermission = CRM_Core_Permission::getPermission();
$contextMenu = array();
foreach ($menu as $key => $values) {
if ($key != 'otherActions') {
// user does not have necessary permissions.
if (!self::checkUserMenuPermissions($aclPermissionedTasks, $corePermission, $values)) {
continue;
}
// build directly accessible action menu.
if (in_array($values['ref'], array(
'view-contact',
'edit-contact',
))) {
$contextMenu['primaryActions'][$key] = array(
'title' => $values['title'],
'ref' => $values['ref'],
'class' => CRM_Utils_Array::value('class', $values),
'key' => $values['key'],
);
continue;
}
// finally get menu item for -more- action widget.
$contextMenu['moreActions'][$values['weight']] = array(
'title' => $values['title'],
'ref' => $values['ref'],
'href' => CRM_Utils_Array::value('href', $values),
'tab' => CRM_Utils_Array::value('tab', $values),
'class' => CRM_Utils_Array::value('class', $values),
'key' => $values['key'],
);
}
else {
foreach ($values as $value) {
// user does not have necessary permissions.
if (!self::checkUserMenuPermissions($aclPermissionedTasks, $corePermission, $value)) {
continue;
}
// finally get menu item for -more- action widget.
$contextMenu['otherActions'][$value['weight']] = array(
'title' => $value['title'],
'ref' => $value['ref'],
'href' => CRM_Utils_Array::value('href', $value),
'tab' => CRM_Utils_Array::value('tab', $value),
'class' => CRM_Utils_Array::value('class', $value),
'icon' => CRM_Utils_Array::value('icon', $value),
'key' => $value['key'],
);
}
}
}
ksort($contextMenu['moreActions']);
ksort($contextMenu['otherActions']);
return $contextMenu;
}
/**
* Check if user has permissions to access items in action menu.
*
* @param array $aclPermissionedTasks
* Array containing ACL related tasks.
* @param string $corePermission
* The permission of the user (edit or view or null).
* @param array $menuOptions
* Array containing params of the menu (title, href, etc).
*
* @return bool
* TRUE if user has all permissions, FALSE if otherwise.
*/
public static function checkUserMenuPermissions($aclPermissionedTasks, $corePermission, $menuOptions) {
$componentName = CRM_Utils_Array::value('component', $menuOptions);
// if component action - make sure component is enable.
if ($componentName && !in_array($componentName, CRM_Core_Config::singleton()->enableComponents)) {
return FALSE;
}
// make sure user has all required permissions.
$hasAllPermissions = FALSE;
$permissions = CRM_Utils_Array::value('permissions', $menuOptions);
if (!is_array($permissions) || empty($permissions)) {
$hasAllPermissions = TRUE;
}
// iterate for required permissions in given permissions array.
if (!$hasAllPermissions) {
$hasPermissions = 0;
foreach ($permissions as $permission) {
if (CRM_Core_Permission::check($permission)) {
$hasPermissions++;
}
}
if (count($permissions) == $hasPermissions) {
$hasAllPermissions = TRUE;
}
// if still user does not have required permissions, check acl.
if (!$hasAllPermissions && $menuOptions['ref'] != 'delete-contact') {
if (in_array($menuOptions['ref'], $aclPermissionedTasks) &&
$corePermission == CRM_Core_Permission::EDIT
) {
$hasAllPermissions = TRUE;
}
elseif (in_array($menuOptions['ref'], array(
'new-email',
))) {
// grant permissions for these tasks.
$hasAllPermissions = TRUE;
}
}
}
return $hasAllPermissions;
}
/**
* Retrieve display name of contact that address is shared.
*
* This is based on $masterAddressId or $contactId .
*
* @param int $masterAddressId
* Master id.
* @param int $contactId
* Contact id.
*
* @return string|null
* the found display name or null.
*/
public static function getMasterDisplayName($masterAddressId = NULL, $contactId = NULL) {
$masterDisplayName = NULL;
$sql = NULL;
if (!$masterAddressId && !$contactId) {
return $masterDisplayName;
}
if ($masterAddressId) {
$sql = "
SELECT display_name from civicrm_contact
LEFT JOIN civicrm_address ON ( civicrm_address.contact_id = civicrm_contact.id )
WHERE civicrm_address.id = " . $masterAddressId;
}
elseif ($contactId) {
$sql = "
SELECT display_name from civicrm_contact cc, civicrm_address add1
LEFT JOIN civicrm_address add2 ON ( add1.master_id = add2.id )
WHERE cc.id = add2.contact_id AND add1.contact_id = " . $contactId;
}
$masterDisplayName = CRM_Core_DAO::singleValueQuery($sql);
return $masterDisplayName;
}
/**
* Get the creation/modification times for a contact.
*
* @param int $contactId
*
* @return array
* Dates - ('created_date' => $, 'modified_date' => $)
*/
public static function getTimestamps($contactId) {
$timestamps = CRM_Core_DAO::executeQuery(
'SELECT created_date, modified_date
FROM civicrm_contact
WHERE id = %1',
array(
1 => array($contactId, 'Integer'),
)
);
if ($timestamps->fetch()) {
return array(
'created_date' => $timestamps->created_date,
'modified_date' => $timestamps->modified_date,
);
}
else {
return NULL;
}
}
/**
* Get a list of triggers for the contact table.
*
* @see hook_civicrm_triggerInfo
* @see CRM_Core_DAO::triggerRebuild
* @see http://issues.civicrm.org/jira/browse/CRM-10554
*
* @param $info
* @param null $tableName
*/
public static function triggerInfo(&$info, $tableName = NULL) {
//during upgrade, first check for valid version and then create triggers
//i.e the columns created_date and modified_date are introduced in 4.3.alpha1 so dont create triggers for older version
if (CRM_Core_Config::isUpgradeMode()) {
$currentVer = CRM_Core_BAO_Domain::version(TRUE);
//if current version is less than 4.3.alpha1 dont create below triggers
if (version_compare($currentVer, '4.3.alpha1') < 0) {
return;
}
}
// Modifications to these records should update the contact timestamps.
\Civi\Core\SqlTrigger\TimestampTriggers::create('civicrm_contact', 'Contact')
->setRelations(array(
array('table' => 'civicrm_address', 'column' => 'contact_id'),
array('table' => 'civicrm_email', 'column' => 'contact_id'),
array('table' => 'civicrm_im', 'column' => 'contact_id'),
array('table' => 'civicrm_phone', 'column' => 'contact_id'),
array('table' => 'civicrm_website', 'column' => 'contact_id'),
)
)
->alterTriggerInfo($info, $tableName);
// Update phone table to populate phone_numeric field
if (!$tableName || $tableName == 'civicrm_phone') {
// Define stored sql function needed for phones
$sqlTriggers = Civi::service('sql_triggers');
$sqlTriggers->enqueueQuery(self::DROP_STRIP_FUNCTION_43);
$sqlTriggers->enqueueQuery(self::CREATE_STRIP_FUNCTION_43);
$info[] = array(
'table' => array('civicrm_phone'),
'when' => 'BEFORE',
'event' => array('INSERT', 'UPDATE'),
'sql' => "\nSET NEW.phone_numeric = civicrm_strip_non_numeric(NEW.phone);\n",
);
}
}
/**
* Check if contact is being used in civicrm_domain based on $contactId.
*
* @param int $contactId
* Contact id.
*
* @return bool
* true if present else false.
*/
public static function checkDomainContact($contactId) {
if (!$contactId) {
return FALSE;
}
$domainId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_Domain', $contactId, 'id', 'contact_id');
if ($domainId) {
return TRUE;
}
else {
return FALSE;
}
}
/**
* Get options for a given contact field.
*
* @see CRM_Core_DAO::buildOptions
*
* TODO: Should we always assume chainselect? What fn should be responsible for controlling that flow?
* TODO: In context of chainselect, what to return if e.g. a country has no states?
*
* @param string $fieldName
* @param string $context
* @see CRM_Core_DAO::buildOptionsContext
* @param array $props
* whatever is known about this dao object.
*
* @return array|bool
*/
public static function buildOptions($fieldName, $context = NULL, $props = array()) {
$params = array();
// Special logic for fields whose options depend on context or properties
switch ($fieldName) {
case 'contact_sub_type':
if (!empty($props['contact_type'])) {
$params['condition'] = "parent_id = (SELECT id FROM civicrm_contact_type WHERE name='{$props['contact_type']}')";
}
break;
case 'contact_type':
if ($context == 'search') {
// CRM-15495 - EntityRef filters and basic search forms expect this format
// FIXME: Search builder does not
return CRM_Contact_BAO_ContactType::getSelectElements();
}
break;
// The contact api supports some related entities so we'll honor that by fetching their options
case 'group_id':
case 'group':
return CRM_Contact_BAO_GroupContact::buildOptions('group_id', $context, $props);
case 'tag_id':
case 'tag':
$props['entity_table'] = 'civicrm_contact';
return CRM_Core_BAO_EntityTag::buildOptions('tag_id', $context, $props);
case 'state_province_id':
case 'state_province':
case 'state_province_name':
case 'country_id':
case 'country':
case 'county_id':
case 'worldregion':
case 'worldregion_id':
return CRM_Core_BAO_Address::buildOptions($fieldName, 'get', $props);
}
return CRM_Core_PseudoConstant::get(__CLASS__, $fieldName, $params, $context);
}
/**
* Delete a contact-related object that has an 'is_primary' field.
*
* Ensures that is_primary gets assigned to another object if available
* Also calls pre/post hooks
*
* @param string $type
* @param int $id
* @return bool
*/
public static function deleteObjectWithPrimary($type, $id) {
if (!$id || !is_numeric($id)) {
return FALSE;
}
$daoName = "CRM_Core_DAO_$type";
$obj = new $daoName();
$obj->id = $id;
$obj->find();
if ($obj->fetch()) {
CRM_Utils_Hook::pre('delete', $type, $id, CRM_Core_DAO::$_nullArray);
$contactId = $obj->contact_id;
$obj->delete();
}
else {
return FALSE;
}
// is_primary is only relavent if this field belongs to a contact
if ($contactId) {
$dao = new $daoName();
$dao->contact_id = $contactId;
$dao->is_primary = 1;
// Pick another record to be primary (if one isn't already)
if (!$dao->find(TRUE)) {
$dao->is_primary = 0;
$dao->find();
if ($dao->fetch()) {
$dao->is_primary = 1;
$dao->save();
}
}
$dao->free();
}
CRM_Utils_Hook::post('delete', $type, $id, $obj);
$obj->free();
return TRUE;
}
/**
* @inheritDoc
*/
public function addSelectWhereClause() {
// We always return an array with these keys, even if they are empty,
// because this tells the query builder that we have considered these fields for acls
$clauses = array(
'id' => (array) CRM_Contact_BAO_Contact_Permission::cacheSubquery(),
'is_deleted' => CRM_Core_Permission::check('access deleted contacts') ? array() : array('!= 1'),
);
CRM_Utils_Hook::selectWhereClause($this, $clauses);
return $clauses;
}
/**
* Get any existing duplicate contacts based on the input parameters.
*
* @param array $input
* Input parameters to be matched.
* @param string $contactType
* @param string $rule
* - Supervised
* - Unsupervised
* @param $excludedContactIDs
* An array of ids not to be included in the results.
* @param bool $checkPermissions
* @param int $ruleGroupID
* ID of the rule group to be used if an override is desirable.
*
* @return array
*/
public static function getDuplicateContacts($input, $contactType, $rule = 'Unsupervised', $excludedContactIDs = array(), $checkPermissions = TRUE, $ruleGroupID = NULL) {
$dedupeParams = CRM_Dedupe_Finder::formatParams($input, $contactType);
$dedupeParams['check_permission'] = $checkPermissions;
$ids = CRM_Dedupe_Finder::dupesByParams($dedupeParams, $contactType, $rule, $excludedContactIDs, $ruleGroupID);
return $ids;
}
/**
* Get the first duplicate contacts based on the input parameters.
*
* @param array $input
* Input parameters to be matched.
* @param string $contactType
* @param string $rule
* - Supervised
* - Unsupervised
* @param $excludedContactIDs
* An array of ids not to be included in the results.
* @param bool $checkPermissions
* @param int $ruleGroupID
* ID of the rule group to be used if an override is desirable.
*
* @return int|NULL
*/
public static function getFirstDuplicateContact($input, $contactType, $rule = 'Unsupervised', $excludedContactIDs = array(), $checkPermissions = TRUE, $ruleGroupID = NULL) {
$ids = self::getDuplicateContacts($input, $contactType, $rule, $excludedContactIDs, $checkPermissions, $ruleGroupID);
if (empty($ids)) {
return NULL;
}
return $ids[0];
}
/**
* Check if a field is associated with an entity that has a location type.
*
* (ie. is an address, phone, email etc field).
*
* @param string $fieldTitle
* Title of the field (not the name - create a new function for that if required).
*
* @return bool
*/
public static function isFieldHasLocationType($fieldTitle) {
foreach (CRM_Contact_BAO_Contact::importableFields() as $key => $field) {
if ($field['title'] === $fieldTitle) {
return CRM_Utils_Array::value('hasLocationType', $field);
}
}
return FALSE;
}
}