First commit
This commit is contained in:
commit
c6e2478c40
13918 changed files with 2303184 additions and 0 deletions
3536
sites/all/modules/civicrm/CRM/Contact/BAO/Contact.php
Normal file
3536
sites/all/modules/civicrm/CRM/Contact/BAO/Contact.php
Normal file
File diff suppressed because it is too large
Load diff
205
sites/all/modules/civicrm/CRM/Contact/BAO/Contact/Location.php
Normal file
205
sites/all/modules/civicrm/CRM/Contact/BAO/Contact/Location.php
Normal file
|
@ -0,0 +1,205 @@
|
|||
<?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_Location {
|
||||
|
||||
/**
|
||||
* Get the display name, primary email, location type and location id of a contact.
|
||||
*
|
||||
* @param int $id
|
||||
* Id of the contact.
|
||||
*
|
||||
* @param bool $isPrimary
|
||||
* @param int $locationTypeID
|
||||
*
|
||||
* @return array
|
||||
* Array of display_name, email, location type and location id if found, or (null,null,null, null)
|
||||
*/
|
||||
public static function getEmailDetails($id, $isPrimary = TRUE, $locationTypeID = NULL) {
|
||||
$primaryClause = NULL;
|
||||
if ($isPrimary) {
|
||||
$primaryClause = " AND civicrm_email.is_primary = 1";
|
||||
}
|
||||
|
||||
$locationClause = NULL;
|
||||
if ($locationTypeID) {
|
||||
$locationClause = " AND civicrm_email.location_type_id = $locationTypeID";
|
||||
}
|
||||
|
||||
$sql = "
|
||||
SELECT civicrm_contact.display_name,
|
||||
civicrm_email.email,
|
||||
civicrm_email.location_type_id,
|
||||
civicrm_email.id
|
||||
FROM civicrm_contact
|
||||
LEFT JOIN civicrm_email ON ( civicrm_contact.id = civicrm_email.contact_id {$primaryClause} {$locationClause} )
|
||||
WHERE civicrm_contact.id = %1";
|
||||
|
||||
$params = array(1 => array($id, 'Integer'));
|
||||
$dao = CRM_Core_DAO::executeQuery($sql, $params);
|
||||
if ($dao->fetch()) {
|
||||
return array($dao->display_name, $dao->email, $dao->location_type_id, $dao->id);
|
||||
}
|
||||
return array(NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sms number and display name of a contact.
|
||||
*
|
||||
* @param int $id
|
||||
* Id of the contact.
|
||||
*
|
||||
* @param null $type
|
||||
*
|
||||
* @return array
|
||||
* tuple of display_name and sms if found, or (null,null)
|
||||
*/
|
||||
public static function getPhoneDetails($id, $type = NULL) {
|
||||
if (!$id) {
|
||||
return array(NULL, NULL);
|
||||
}
|
||||
|
||||
$cond = NULL;
|
||||
if ($type) {
|
||||
$cond = " AND civicrm_phone.phone_type_id = '$type'";
|
||||
}
|
||||
|
||||
$sql = "
|
||||
SELECT civicrm_contact.display_name, civicrm_phone.phone, civicrm_contact.do_not_sms
|
||||
FROM civicrm_contact
|
||||
LEFT JOIN civicrm_phone ON ( civicrm_phone.contact_id = civicrm_contact.id )
|
||||
WHERE civicrm_phone.is_primary = 1
|
||||
$cond
|
||||
AND civicrm_contact.id = %1";
|
||||
|
||||
$params = array(1 => array($id, 'Integer'));
|
||||
$dao = CRM_Core_DAO::executeQuery($sql, $params);
|
||||
if ($dao->fetch()) {
|
||||
return array($dao->display_name, $dao->phone, $dao->do_not_sms);
|
||||
}
|
||||
return array(NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the information to map a contact.
|
||||
*
|
||||
* @param array $ids
|
||||
* The list of ids for which we want map info.
|
||||
* $param int $locationTypeID
|
||||
*
|
||||
* @param int $locationTypeID
|
||||
* @param bool $imageUrlOnly
|
||||
*
|
||||
* @return null|string
|
||||
* display name of the contact if found
|
||||
*/
|
||||
public static function &getMapInfo($ids, $locationTypeID = NULL, $imageUrlOnly = FALSE) {
|
||||
$idString = ' ( ' . implode(',', $ids) . ' ) ';
|
||||
$sql = "
|
||||
SELECT civicrm_contact.id as contact_id,
|
||||
civicrm_contact.contact_type as contact_type,
|
||||
civicrm_contact.contact_sub_type as contact_sub_type,
|
||||
civicrm_contact.display_name as display_name,
|
||||
civicrm_address.street_address as street_address,
|
||||
civicrm_address.supplemental_address_1 as supplemental_address_1,
|
||||
civicrm_address.supplemental_address_2 as supplemental_address_2,
|
||||
civicrm_address.supplemental_address_3 as supplemental_address_3,
|
||||
civicrm_address.city as city,
|
||||
civicrm_address.postal_code as postal_code,
|
||||
civicrm_address.postal_code_suffix as postal_code_suffix,
|
||||
civicrm_address.geo_code_1 as latitude,
|
||||
civicrm_address.geo_code_2 as longitude,
|
||||
civicrm_state_province.abbreviation as state,
|
||||
civicrm_country.name as country,
|
||||
civicrm_location_type.name as location_type
|
||||
FROM civicrm_contact
|
||||
LEFT JOIN civicrm_address ON civicrm_address.contact_id = civicrm_contact.id
|
||||
LEFT JOIN civicrm_state_province ON civicrm_address.state_province_id = civicrm_state_province.id
|
||||
LEFT JOIN civicrm_country ON civicrm_address.country_id = civicrm_country.id
|
||||
LEFT JOIN civicrm_location_type ON civicrm_location_type.id = civicrm_address.location_type_id
|
||||
WHERE civicrm_address.geo_code_1 IS NOT NULL
|
||||
AND civicrm_address.geo_code_2 IS NOT NULL
|
||||
AND civicrm_contact.id IN $idString ";
|
||||
|
||||
$params = array();
|
||||
if (!$locationTypeID) {
|
||||
$sql .= " AND civicrm_address.is_primary = 1";
|
||||
}
|
||||
else {
|
||||
$sql .= " AND civicrm_address.location_type_id = %1";
|
||||
$params[1] = array($locationTypeID, 'Integer');
|
||||
}
|
||||
|
||||
$dao = CRM_Core_DAO::executeQuery($sql, $params);
|
||||
|
||||
$locations = array();
|
||||
$config = CRM_Core_Config::singleton();
|
||||
|
||||
while ($dao->fetch()) {
|
||||
$location = array();
|
||||
$location['contactID'] = $dao->contact_id;
|
||||
$location['displayName'] = addslashes($dao->display_name);
|
||||
$location['city'] = $dao->city;
|
||||
$location['state'] = $dao->state;
|
||||
$location['postal_code'] = $dao->postal_code;
|
||||
$location['lat'] = $dao->latitude;
|
||||
$location['lng'] = $dao->longitude;
|
||||
$location['marker_class'] = $dao->contact_type;
|
||||
$address = '';
|
||||
|
||||
CRM_Utils_String::append($address, '<br />',
|
||||
array(
|
||||
$dao->street_address,
|
||||
$dao->supplemental_address_1,
|
||||
$dao->supplemental_address_2,
|
||||
$dao->supplemental_address_3,
|
||||
$dao->city,
|
||||
)
|
||||
);
|
||||
CRM_Utils_String::append($address, ', ',
|
||||
array($dao->state, $dao->postal_code)
|
||||
);
|
||||
CRM_Utils_String::append($address, '<br /> ',
|
||||
array($dao->country)
|
||||
);
|
||||
$location['address'] = addslashes($address);
|
||||
$location['displayAddress'] = str_replace('<br />', ', ', addslashes($address));
|
||||
$location['url'] = CRM_Utils_System::url('civicrm/contact/view', 'reset=1&cid=' . $dao->contact_id);
|
||||
$location['location_type'] = $dao->location_type;
|
||||
$location['image'] = CRM_Contact_BAO_Contact_Utils::getImage(isset($dao->contact_sub_type) ? $dao->contact_sub_type : $dao->contact_type, $imageUrlOnly, $dao->contact_id
|
||||
);
|
||||
$locations[] = $location;
|
||||
}
|
||||
return $locations;
|
||||
}
|
||||
|
||||
}
|
190
sites/all/modules/civicrm/CRM/Contact/BAO/Contact/Optimizer.php
Normal file
190
sites/all/modules/civicrm/CRM/Contact/BAO/Contact/Optimizer.php
Normal file
|
@ -0,0 +1,190 @@
|
|||
<?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_Optimizer {
|
||||
/**
|
||||
* Edit function.
|
||||
*
|
||||
* @param array $newValues
|
||||
* @param array $oldValues
|
||||
*/
|
||||
public static function edit(&$newValues, &$oldValues) {
|
||||
// still need to do more work on this
|
||||
// CRM-10192
|
||||
return;
|
||||
|
||||
self::website($newValues, $oldValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $newValues
|
||||
* @param $oldValues
|
||||
*/
|
||||
public static function website(&$newValues, &$oldValues) {
|
||||
$oldWebsiteValues = CRM_Utils_Array::value('website', $oldValues);
|
||||
$newWebsiteValues = CRM_Utils_Array::value('website', $newValues);
|
||||
|
||||
if ($oldWebsiteValues == NULL || $newWebsiteValues == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check if we had a value in the old
|
||||
$oldEmpty = $newEmpty = TRUE;
|
||||
$old = $new = array();
|
||||
|
||||
foreach ($oldWebsiteValues as $idx => $value) {
|
||||
if (!empty($value['url'])) {
|
||||
$oldEmpty = FALSE;
|
||||
$old[] = array('website_type_id' => $value['website_type_id'], 'url' => $value['url']);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($newWebsiteValues as $idx => $value) {
|
||||
if (!empty($value['url'])) {
|
||||
$newEmpty = FALSE;
|
||||
$new[] = array('website_type_id' => $value['website_type_id'], 'url' => $value['url']);
|
||||
}
|
||||
}
|
||||
|
||||
// if both old and new are empty, we can delete new and avoid a write
|
||||
if ($oldEmpty && $newEmpty) {
|
||||
unset($newValues['website']);
|
||||
}
|
||||
|
||||
// if different number of non-empty entries, return
|
||||
if (count($new) != count($old)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// same number of entries, check if they are exactly the same
|
||||
foreach ($old as $oldID => $oldValues) {
|
||||
$found = FALSE;
|
||||
foreach ($new as $newID => $newValues) {
|
||||
if (
|
||||
$old['website_type_id'] == $new['website_type_id'] &&
|
||||
$old['url'] == $new['url']
|
||||
) {
|
||||
$found = TRUE;
|
||||
unset($new[$newID]);
|
||||
break;
|
||||
}
|
||||
if (!$found) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we've come here, this means old and new are the same
|
||||
// we can skip saving new and return
|
||||
unset($newValues['website']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $newValues
|
||||
* @param $oldValues
|
||||
*/
|
||||
public static function email(&$newValues, &$oldValues) {
|
||||
$oldEmailValues = CRM_Utils_Array::value('email', $oldValues);
|
||||
$newEmailValues = CRM_Utils_Array::value('email', $newValues);
|
||||
|
||||
if ($oldEmailValues == NULL || $newEmailValues == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check if we had a value in the old
|
||||
$oldEmpty = $newEmpty = TRUE;
|
||||
$old = $new = array();
|
||||
|
||||
foreach ($oldEmailValues as $idx => $value) {
|
||||
if (!empty($value['email'])) {
|
||||
$oldEmpty = FALSE;
|
||||
$old[] = array(
|
||||
'email' => $value['email'],
|
||||
'location_type_id' => $value['location_type_id'],
|
||||
'on_hold' => $value['on_hold'] ? 1 : 0,
|
||||
'is_primary' => $value['is_primary'] ? 1 : 0,
|
||||
'is_bulkmail' => $value['is_bulkmail'] ? 1 : 0,
|
||||
'signature_text' => $value['signature_text'] ? $value['signature_text'] : '',
|
||||
'signature_html' => $value['signature_html'] ? $value['signature_html'] : '',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($newEmailValues as $idx => $value) {
|
||||
if (!empty($value['email'])) {
|
||||
$newEmpty = FALSE;
|
||||
$new[] = array(
|
||||
'email' => $value['email'],
|
||||
'location_type_id' => $value['location_type_id'],
|
||||
'on_hold' => $value['on_hold'] ? 1 : 0,
|
||||
'is_primary' => $value['is_primary'] ? 1 : 0,
|
||||
'is_bulkmail' => $value['is_bulkmail'] ? 1 : 0,
|
||||
'signature_text' => $value['signature_text'] ? $value['signature_text'] : '',
|
||||
'signature_html' => $value['signature_html'] ? $value['signature_html'] : '',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// if both old and new are empty, we can delete new and avoid a write
|
||||
if ($oldEmpty && $newEmpty) {
|
||||
unset($newValues['email']);
|
||||
}
|
||||
|
||||
// if different number of non-empty entries, return
|
||||
if (count($new) != count($old)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// same number of entries, check if they are exactly the same
|
||||
foreach ($old as $oldID => $oldValues) {
|
||||
$found = FALSE;
|
||||
foreach ($new as $newID => $newValues) {
|
||||
if (
|
||||
$old['email_type_id'] == $new['email_type_id'] &&
|
||||
$old['url'] == $new['url']
|
||||
) {
|
||||
$found = TRUE;
|
||||
unset($new[$newID]);
|
||||
break;
|
||||
}
|
||||
if (!$found) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we've come here, this means old and new are the same
|
||||
// we can skip saving new and return
|
||||
unset($newValues['email']);
|
||||
}
|
||||
|
||||
}
|
488
sites/all/modules/civicrm/CRM/Contact/BAO/Contact/Permission.php
Normal file
488
sites/all/modules/civicrm/CRM/Contact/BAO/Contact/Permission.php
Normal file
|
@ -0,0 +1,488 @@
|
|||
<?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_Permission {
|
||||
|
||||
/**
|
||||
* Check which of the given contact IDs the logged in user
|
||||
* has permissions for the operation type according to:
|
||||
* - general permissions (e.g. 'edit all contacts')
|
||||
* - deletion status (unless you have 'access deleted contacts')
|
||||
* - ACL
|
||||
* - permissions inherited through relationships (also second degree if enabled)
|
||||
*
|
||||
* @param array $contact_ids
|
||||
* Contact IDs.
|
||||
* @param int $type the type of operation (view|edit)
|
||||
*
|
||||
* @see CRM_Contact_BAO_Contact_Permission::allow
|
||||
*
|
||||
* @return array
|
||||
* list of contact IDs the logged in user has the given permission for
|
||||
*/
|
||||
public static function allowList($contact_ids, $type = CRM_Core_Permission::VIEW) {
|
||||
$result_set = array();
|
||||
if (empty($contact_ids)) {
|
||||
// empty contact lists would cause trouble in the SQL. And be pointless.
|
||||
return $result_set;
|
||||
}
|
||||
|
||||
// make sure the the general permissions are given
|
||||
if (CRM_Core_Permission::check('edit all contacts')
|
||||
|| $type == CRM_Core_Permission::VIEW && CRM_Core_Permission::check('view all contacts')
|
||||
) {
|
||||
|
||||
// if the general permission is there, all good
|
||||
if (CRM_Core_Permission::check('access deleted contacts')) {
|
||||
// if user can access deleted contacts -> fine
|
||||
return $contact_ids;
|
||||
}
|
||||
else {
|
||||
// if the user CANNOT access deleted contacts, these need to be filtered
|
||||
$contact_id_list = implode(',', $contact_ids);
|
||||
$filter_query = "SELECT DISTINCT(id) FROM civicrm_contact WHERE id IN ($contact_id_list) AND is_deleted = 0";
|
||||
$query = CRM_Core_DAO::executeQuery($filter_query);
|
||||
while ($query->fetch()) {
|
||||
$result_set[(int) $query->id] = TRUE;
|
||||
}
|
||||
return array_keys($result_set);
|
||||
}
|
||||
}
|
||||
|
||||
// get logged in user
|
||||
$contactID = CRM_Core_Session::getLoggedInContactID();
|
||||
if (empty($contactID)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// make sure the cache is filled
|
||||
self::cache($contactID, $type);
|
||||
|
||||
// compile query
|
||||
$operation = ($type == CRM_Core_Permission::VIEW) ? 'View' : 'Edit';
|
||||
|
||||
// add clause for deleted contacts, if the user doesn't have the permission to access them
|
||||
$LEFT_JOIN_DELETED = $AND_CAN_ACCESS_DELETED = '';
|
||||
if (!CRM_Core_Permission::check('access deleted contacts')) {
|
||||
$LEFT_JOIN_DELETED = "LEFT JOIN civicrm_contact ON civicrm_contact.id = contact_id";
|
||||
$AND_CAN_ACCESS_DELETED = "AND civicrm_contact.is_deleted = 0";
|
||||
}
|
||||
|
||||
// RUN the query
|
||||
$contact_id_list = implode(',', $contact_ids);
|
||||
$query = "
|
||||
SELECT contact_id
|
||||
FROM civicrm_acl_contact_cache
|
||||
{$LEFT_JOIN_DELETED}
|
||||
WHERE contact_id IN ({$contact_id_list})
|
||||
AND user_id = {$contactID}
|
||||
AND operation = '{$operation}'
|
||||
{$AND_CAN_ACCESS_DELETED}";
|
||||
$result = CRM_Core_DAO::executeQuery($query);
|
||||
while ($result->fetch()) {
|
||||
$result_set[(int) $result->contact_id] = TRUE;
|
||||
}
|
||||
|
||||
// if some have been rejected, double check for permissions inherited by relationship
|
||||
if (count($result_set) < count($contact_ids)) {
|
||||
$rejected_contacts = array_diff_key($contact_ids, $result_set);
|
||||
// @todo consider storing these to the acl cache for next time, since we have fetched.
|
||||
$allowed_by_relationship = self::relationshipList($rejected_contacts);
|
||||
foreach ($allowed_by_relationship as $contact_id) {
|
||||
$result_set[(int) $contact_id] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return array_keys($result_set);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the logged in user has permissions for the operation type.
|
||||
*
|
||||
* @param int $id
|
||||
* Contact id.
|
||||
* @param int|string $type the type of operation (view|edit)
|
||||
*
|
||||
* @return bool
|
||||
* true if the user has permission, false otherwise
|
||||
*/
|
||||
public static function allow($id, $type = CRM_Core_Permission::VIEW) {
|
||||
// get logged in user
|
||||
$contactID = CRM_Core_Session::getLoggedInContactID();
|
||||
|
||||
// first: check if contact is trying to view own contact
|
||||
if ($contactID == $id && ($type == CRM_Core_Permission::VIEW && CRM_Core_Permission::check('view my contact')
|
||||
|| $type == CRM_Core_Permission::EDIT && CRM_Core_Permission::check('edit my contact'))
|
||||
) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
# FIXME: push this somewhere below, to not give this permission so many rights
|
||||
$isDeleted = (bool) CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $id, 'is_deleted');
|
||||
if (CRM_Core_Permission::check('access deleted contacts') && $isDeleted) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// short circuit for admin rights here so we avoid unneeeded queries
|
||||
// some duplication of code, but we skip 3-5 queries
|
||||
if (CRM_Core_Permission::check('edit all contacts') ||
|
||||
($type == CRM_ACL_API::VIEW && CRM_Core_Permission::check('view all contacts'))
|
||||
) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// check permission based on relationship, CRM-2963
|
||||
if (self::relationshipList(array($id))) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// We should probably do a cheap check whether it's in the cache first.
|
||||
// check permission based on ACL
|
||||
$tables = array();
|
||||
$whereTables = array();
|
||||
|
||||
$permission = CRM_ACL_API::whereClause($type, $tables, $whereTables, NULL, FALSE, FALSE, TRUE);
|
||||
$from = CRM_Contact_BAO_Query::fromClause($whereTables);
|
||||
|
||||
$query = "
|
||||
SELECT contact_a.id
|
||||
$from
|
||||
WHERE contact_a.id = %1 AND $permission
|
||||
LIMIT 1
|
||||
";
|
||||
|
||||
if (CRM_Core_DAO::singleValueQuery($query, array(1 => array($id, 'Integer')))) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the acl contact cache for this contact id if empty.
|
||||
*
|
||||
* @param int $userID
|
||||
* @param int|string $type the type of operation (view|edit)
|
||||
* @param bool $force
|
||||
* Should we force a recompute.
|
||||
*/
|
||||
public static function cache($userID, $type = CRM_Core_Permission::VIEW, $force = FALSE) {
|
||||
// FIXME: maybe find a better way of keeping track of this. @eileen pointed out
|
||||
// that somebody might flush the cache away from under our feet,
|
||||
// but the alternative would be a SQL call every time this is called,
|
||||
// and a complete rebuild if the result was an empty set...
|
||||
static $_processed = array(
|
||||
CRM_Core_Permission::VIEW => array(),
|
||||
CRM_Core_Permission::EDIT => array());
|
||||
|
||||
if ($type == CRM_Core_Permission::VIEW) {
|
||||
$operationClause = " operation IN ( 'Edit', 'View' ) ";
|
||||
$operation = 'View';
|
||||
}
|
||||
else {
|
||||
$operationClause = " operation = 'Edit' ";
|
||||
$operation = 'Edit';
|
||||
}
|
||||
$queryParams = array(1 => array($userID, 'Integer'));
|
||||
|
||||
if (!$force) {
|
||||
// skip if already calculated
|
||||
if (!empty($_processed[$type][$userID])) {
|
||||
return;
|
||||
}
|
||||
|
||||
// run a query to see if the cache is filled
|
||||
$sql = "
|
||||
SELECT count(*)
|
||||
FROM civicrm_acl_contact_cache
|
||||
WHERE user_id = %1
|
||||
AND $operationClause
|
||||
";
|
||||
$count = CRM_Core_DAO::singleValueQuery($sql, $queryParams);
|
||||
if ($count > 0) {
|
||||
$_processed[$type][$userID] = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$tables = array();
|
||||
$whereTables = array();
|
||||
|
||||
$permission = CRM_ACL_API::whereClause($type, $tables, $whereTables, $userID, FALSE, FALSE, TRUE);
|
||||
|
||||
$from = CRM_Contact_BAO_Query::fromClause($whereTables);
|
||||
CRM_Core_DAO::executeQuery("
|
||||
INSERT INTO civicrm_acl_contact_cache ( user_id, contact_id, operation )
|
||||
SELECT DISTINCT $userID as user_id, contact_a.id as contact_id, '{$operation}' as operation
|
||||
$from
|
||||
LEFT JOIN civicrm_acl_contact_cache ac ON ac.user_id = $userID AND ac.contact_id = contact_a.id AND ac.operation = '{$operation}'
|
||||
WHERE $permission
|
||||
AND ac.user_id IS NULL
|
||||
");
|
||||
|
||||
// Add in a row for the logged in contact. Do not try to combine with the above query or an ugly OR will appear in
|
||||
// the permission clause.
|
||||
if (CRM_Core_Permission::check('edit my contact') ||
|
||||
($type == CRM_Core_Permission::VIEW && CRM_Core_Permission::check('view my contact'))) {
|
||||
if (!CRM_Core_DAO::singleValueQuery("
|
||||
SELECT count(*) FROM civicrm_acl_contact_cache WHERE user_id = %1 AND contact_id = %1 AND operation = '{$operation}' LIMIT 1", $queryParams)) {
|
||||
CRM_Core_DAO::executeQuery("INSERT INTO civicrm_acl_contact_cache ( user_id, contact_id, operation ) VALUES(%1, %1, '{$operation}')", $queryParams);
|
||||
}
|
||||
}
|
||||
$_processed[$type][$userID] = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $contactAlias
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function cacheClause($contactAlias = 'contact_a') {
|
||||
if (CRM_Core_Permission::check('view all contacts') ||
|
||||
CRM_Core_Permission::check('edit all contacts')
|
||||
) {
|
||||
if (is_array($contactAlias)) {
|
||||
$wheres = array();
|
||||
foreach ($contactAlias as $alias) {
|
||||
// CRM-6181
|
||||
$wheres[] = "$alias.is_deleted = 0";
|
||||
}
|
||||
return array(NULL, '(' . implode(' AND ', $wheres) . ')');
|
||||
}
|
||||
else {
|
||||
// CRM-6181
|
||||
return array(NULL, "$contactAlias.is_deleted = 0");
|
||||
}
|
||||
}
|
||||
|
||||
$contactID = (int) CRM_Core_Session::getLoggedInContactID();
|
||||
self::cache($contactID);
|
||||
|
||||
if (is_array($contactAlias) && !empty($contactAlias)) {
|
||||
//More than one contact alias
|
||||
$clauses = array();
|
||||
foreach ($contactAlias as $k => $alias) {
|
||||
$clauses[] = " INNER JOIN civicrm_acl_contact_cache aclContactCache_{$k} ON {$alias}.id = aclContactCache_{$k}.contact_id AND aclContactCache_{$k}.user_id = $contactID ";
|
||||
}
|
||||
|
||||
$fromClause = implode(" ", $clauses);
|
||||
$whereClase = NULL;
|
||||
}
|
||||
else {
|
||||
$fromClause = " INNER JOIN civicrm_acl_contact_cache aclContactCache ON {$contactAlias}.id = aclContactCache.contact_id ";
|
||||
$whereClase = " aclContactCache.user_id = $contactID AND $contactAlias.is_deleted = 0";
|
||||
}
|
||||
|
||||
return array($fromClause, $whereClase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate acl subquery that can be placed in the WHERE clause of a query or the ON clause of a JOIN.
|
||||
*
|
||||
* This is specifically for VIEW operations.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public static function cacheSubquery() {
|
||||
if (!CRM_Core_Permission::check(array(array('view all contacts', 'edit all contacts')))) {
|
||||
$contactID = (int) CRM_Core_Session::getLoggedInContactID();
|
||||
self::cache($contactID);
|
||||
return "IN (SELECT contact_id FROM civicrm_acl_contact_cache WHERE user_id = $contactID)";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter a list of contact_ids by the ones that the
|
||||
* currently active user as a permissioned relationship with
|
||||
*
|
||||
* @param array $contact_ids
|
||||
* List of contact IDs to be filtered
|
||||
*
|
||||
* @return array
|
||||
* List of contact IDs that the user has permissions for
|
||||
*/
|
||||
public static function relationshipList($contact_ids) {
|
||||
$result_set = array();
|
||||
|
||||
// no processing empty lists (avoid SQL errors as well)
|
||||
if (empty($contact_ids)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// get the currently logged in user
|
||||
$contactID = CRM_Core_Session::getLoggedInContactID();
|
||||
if (empty($contactID)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// compile a list of queries (later to UNION)
|
||||
$queries = array();
|
||||
$contact_id_list = implode(',', $contact_ids);
|
||||
|
||||
// add a select statement for each direection
|
||||
$directions = array(array('from' => 'a', 'to' => 'b'), array('from' => 'b', 'to' => 'a'));
|
||||
|
||||
// NORMAL/SINGLE DEGREE RELATIONSHIPS
|
||||
foreach ($directions as $direction) {
|
||||
$user_id_column = "contact_id_{$direction['from']}";
|
||||
$contact_id_column = "contact_id_{$direction['to']}";
|
||||
|
||||
// add clause for deleted contacts, if the user doesn't have the permission to access them
|
||||
$LEFT_JOIN_DELETED = $AND_CAN_ACCESS_DELETED = '';
|
||||
if (!CRM_Core_Permission::check('access deleted contacts')) {
|
||||
$LEFT_JOIN_DELETED = "LEFT JOIN civicrm_contact ON civicrm_contact.id = {$contact_id_column} ";
|
||||
$AND_CAN_ACCESS_DELETED = "AND civicrm_contact.is_deleted = 0";
|
||||
}
|
||||
|
||||
$queries[] = "
|
||||
SELECT civicrm_relationship.{$contact_id_column} AS contact_id
|
||||
FROM civicrm_relationship
|
||||
{$LEFT_JOIN_DELETED}
|
||||
WHERE civicrm_relationship.{$user_id_column} = {$contactID}
|
||||
AND civicrm_relationship.{$contact_id_column} IN ({$contact_id_list})
|
||||
AND civicrm_relationship.is_active = 1
|
||||
AND civicrm_relationship.is_permission_{$direction['from']}_{$direction['to']} = 1
|
||||
$AND_CAN_ACCESS_DELETED";
|
||||
}
|
||||
|
||||
// FIXME: secondDegRelPermissions should be a setting
|
||||
$config = CRM_Core_Config::singleton();
|
||||
if ($config->secondDegRelPermissions) {
|
||||
foreach ($directions as $first_direction) {
|
||||
foreach ($directions as $second_direction) {
|
||||
// add clause for deleted contacts, if the user doesn't have the permission to access them
|
||||
$LEFT_JOIN_DELETED = $AND_CAN_ACCESS_DELETED = '';
|
||||
if (!CRM_Core_Permission::check('access deleted contacts')) {
|
||||
$LEFT_JOIN_DELETED = "LEFT JOIN civicrm_contact first_degree_contact ON first_degree_contact.id = second_degree_relationship.contact_id_{$second_direction['from']}\n";
|
||||
$LEFT_JOIN_DELETED .= "LEFT JOIN civicrm_contact second_degree_contact ON second_degree_contact.id = second_degree_relationship.contact_id_{$second_direction['to']} ";
|
||||
$AND_CAN_ACCESS_DELETED = "AND first_degree_contact.is_deleted = 0\n";
|
||||
$AND_CAN_ACCESS_DELETED .= "AND second_degree_contact.is_deleted = 0 ";
|
||||
}
|
||||
|
||||
$queries[] = "
|
||||
SELECT second_degree_relationship.contact_id_{$second_direction['to']} AS contact_id
|
||||
FROM civicrm_relationship first_degree_relationship
|
||||
LEFT JOIN civicrm_relationship second_degree_relationship ON first_degree_relationship.contact_id_{$first_direction['to']} = second_degree_relationship.contact_id_{$first_direction['from']}
|
||||
{$LEFT_JOIN_DELETED}
|
||||
WHERE first_degree_relationship.contact_id_{$first_direction['from']} = {$contactID}
|
||||
AND second_degree_relationship.contact_id_{$second_direction['to']} IN ({$contact_id_list})
|
||||
AND first_degree_relationship.is_active = 1
|
||||
AND first_degree_relationship.is_permission_{$first_direction['from']}_{$first_direction['to']} = 1
|
||||
AND second_degree_relationship.is_active = 1
|
||||
AND second_degree_relationship.is_permission_{$second_direction['from']}_{$second_direction['to']} = 1
|
||||
$AND_CAN_ACCESS_DELETED";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// finally UNION the queries and call
|
||||
$query = "(" . implode(")\nUNION DISTINCT (", $queries) . ")";
|
||||
$result = CRM_Core_DAO::executeQuery($query);
|
||||
while ($result->fetch()) {
|
||||
$result_set[(int) $result->contact_id] = TRUE;
|
||||
}
|
||||
return array_keys($result_set);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param int $contactID
|
||||
* @param CRM_Core_Form $form
|
||||
* @param bool $redirect
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateOnlyChecksum($contactID, &$form, $redirect = TRUE) {
|
||||
// check if this is of the format cs=XXX
|
||||
if (!CRM_Contact_BAO_Contact_Utils::validChecksum($contactID,
|
||||
CRM_Utils_Request::retrieve('cs', 'String', $form, FALSE)
|
||||
)
|
||||
) {
|
||||
if ($redirect) {
|
||||
// also set a message in the UF framework
|
||||
$message = ts('You do not have permission to edit this contact record. Contact the site administrator if you need assistance.');
|
||||
CRM_Utils_System::setUFMessage($message);
|
||||
|
||||
$config = CRM_Core_Config::singleton();
|
||||
CRM_Core_Error::statusBounce($message,
|
||||
$config->userFrameworkBaseURL
|
||||
);
|
||||
// does not come here, we redirect in the above statement
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// set appropriate AUTH source
|
||||
self::initChecksumAuthSrc(TRUE, $form);
|
||||
|
||||
// so here the contact is posing as $contactID, lets set the logging contact ID variable
|
||||
// CRM-8965
|
||||
CRM_Core_DAO::executeQuery('SET @civicrm_user_id = %1',
|
||||
array(1 => array($contactID, 'Integer'))
|
||||
);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $checkSumValidationResult
|
||||
* @param null $form
|
||||
*/
|
||||
public static function initChecksumAuthSrc($checkSumValidationResult = FALSE, $form = NULL) {
|
||||
$session = CRM_Core_Session::singleton();
|
||||
if ($checkSumValidationResult && $form && CRM_Utils_Request::retrieve('cs', 'String', $form, FALSE)) {
|
||||
// if result is already validated, and url has cs, set the flag.
|
||||
$session->set('authSrc', CRM_Core_Permission::AUTH_SRC_CHECKSUM);
|
||||
}
|
||||
elseif (($session->get('authSrc') & CRM_Core_Permission::AUTH_SRC_CHECKSUM) == CRM_Core_Permission::AUTH_SRC_CHECKSUM) {
|
||||
// if checksum wasn't present in REQUEST OR checksum result validated as FALSE,
|
||||
// and flag was already set exactly as AUTH_SRC_CHECKSUM, unset it.
|
||||
$session->set('authSrc', CRM_Core_Permission::AUTH_SRC_UNKNOWN);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $contactID
|
||||
* @param CRM_Core_Form $form
|
||||
* @param bool $redirect
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateChecksumContact($contactID, &$form, $redirect = TRUE) {
|
||||
if (!self::allow($contactID, CRM_Core_Permission::EDIT)) {
|
||||
// check if this is of the format cs=XXX
|
||||
return self::validateOnlyChecksum($contactID, $form, $redirect);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
}
|
1149
sites/all/modules/civicrm/CRM/Contact/BAO/Contact/Utils.php
Normal file
1149
sites/all/modules/civicrm/CRM/Contact/BAO/Contact/Utils.php
Normal file
File diff suppressed because it is too large
Load diff
948
sites/all/modules/civicrm/CRM/Contact/BAO/ContactType.php
Normal file
948
sites/all/modules/civicrm/CRM/Contact/BAO/ContactType.php
Normal file
|
@ -0,0 +1,948 @@
|
|||
<?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_ContactType extends CRM_Contact_DAO_ContactType {
|
||||
|
||||
/**
|
||||
* 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 flattened values.
|
||||
*
|
||||
* @return CRM_Contact_BAO_ContactType|null
|
||||
* object on success, null otherwise
|
||||
*/
|
||||
public static function retrieve(&$params, &$defaults) {
|
||||
$contactType = new CRM_Contact_DAO_ContactType();
|
||||
$contactType->copyValues($params);
|
||||
if ($contactType->find(TRUE)) {
|
||||
CRM_Core_DAO::storeValues($contactType, $defaults);
|
||||
return $contactType;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this contact type active.
|
||||
*
|
||||
* @param string $contactType
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isActive($contactType) {
|
||||
$contact = self::contactTypeInfo(FALSE);
|
||||
$active = array_key_exists($contactType, $contact) ? TRUE : FALSE;
|
||||
return $active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve basic contact type information.
|
||||
*
|
||||
* @param bool $all
|
||||
*
|
||||
* @return array
|
||||
* Array of basic contact types information.
|
||||
*/
|
||||
public static function basicTypeInfo($all = FALSE) {
|
||||
static $_cache = NULL;
|
||||
|
||||
if ($_cache === NULL) {
|
||||
$_cache = array();
|
||||
}
|
||||
|
||||
$argString = $all ? 'CRM_CT_BTI_1' : 'CRM_CT_BTI_0';
|
||||
if (!array_key_exists($argString, $_cache)) {
|
||||
$cache = CRM_Utils_Cache::singleton();
|
||||
$_cache[$argString] = $cache->get($argString);
|
||||
if (!$_cache[$argString]) {
|
||||
$sql = "
|
||||
SELECT *
|
||||
FROM civicrm_contact_type
|
||||
WHERE parent_id IS NULL
|
||||
";
|
||||
if ($all === FALSE) {
|
||||
$sql .= " AND is_active = 1";
|
||||
}
|
||||
|
||||
$params = array();
|
||||
$dao = CRM_Core_DAO::executeQuery($sql,
|
||||
$params,
|
||||
FALSE,
|
||||
'CRM_Contact_DAO_ContactType'
|
||||
);
|
||||
while ($dao->fetch()) {
|
||||
$value = array();
|
||||
CRM_Core_DAO::storeValues($dao, $value);
|
||||
$_cache[$argString][$dao->name] = $value;
|
||||
}
|
||||
|
||||
$cache->set($argString, $_cache[$argString]);
|
||||
}
|
||||
}
|
||||
return $_cache[$argString];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all basic contact types.
|
||||
*
|
||||
* @param bool $all
|
||||
*
|
||||
* @return array
|
||||
* Array of basic contact types
|
||||
*/
|
||||
public static function basicTypes($all = FALSE) {
|
||||
return array_keys(self::basicTypeInfo($all));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $all
|
||||
* @param string $key
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function basicTypePairs($all = FALSE, $key = 'name') {
|
||||
$subtypes = self::basicTypeInfo($all);
|
||||
|
||||
$pairs = array();
|
||||
foreach ($subtypes as $name => $info) {
|
||||
$index = ($key == 'name') ? $name : $info[$key];
|
||||
$pairs[$index] = $info['label'];
|
||||
}
|
||||
return $pairs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all subtypes Information.
|
||||
*
|
||||
* @param array $contactType
|
||||
* ..
|
||||
* @param bool $all
|
||||
* @param bool $ignoreCache
|
||||
* @param bool $reset
|
||||
*
|
||||
* @return array
|
||||
* Array of sub type information
|
||||
*/
|
||||
public static function subTypeInfo($contactType = NULL, $all = FALSE, $ignoreCache = FALSE, $reset = FALSE) {
|
||||
static $_cache = NULL;
|
||||
|
||||
if ($reset === TRUE) {
|
||||
$_cache = NULL;
|
||||
}
|
||||
|
||||
if ($_cache === NULL) {
|
||||
$_cache = array();
|
||||
}
|
||||
if ($contactType && !is_array($contactType)) {
|
||||
$contactType = array($contactType);
|
||||
}
|
||||
|
||||
$argString = $all ? 'CRM_CT_STI_1_' : 'CRM_CT_STI_0_';
|
||||
if (!empty($contactType)) {
|
||||
$argString .= implode('_', $contactType);
|
||||
}
|
||||
|
||||
if ((!array_key_exists($argString, $_cache)) || $ignoreCache) {
|
||||
$cache = CRM_Utils_Cache::singleton();
|
||||
$_cache[$argString] = $cache->get($argString);
|
||||
if (!$_cache[$argString] || $ignoreCache) {
|
||||
$_cache[$argString] = array();
|
||||
|
||||
$ctWHERE = '';
|
||||
if (!empty($contactType)) {
|
||||
$ctWHERE = " AND parent.name IN ('" . implode("','", $contactType) . "')";
|
||||
}
|
||||
|
||||
$sql = "
|
||||
SELECT subtype.*, parent.name as parent, parent.label as parent_label
|
||||
FROM civicrm_contact_type subtype
|
||||
INNER JOIN civicrm_contact_type parent ON subtype.parent_id = parent.id
|
||||
WHERE subtype.name IS NOT NULL AND subtype.parent_id IS NOT NULL {$ctWHERE}
|
||||
";
|
||||
if ($all === FALSE) {
|
||||
$sql .= " AND subtype.is_active = 1 AND parent.is_active = 1 ORDER BY parent.id";
|
||||
}
|
||||
$dao = CRM_Core_DAO::executeQuery($sql, array(),
|
||||
FALSE, 'CRM_Contact_DAO_ContactType'
|
||||
);
|
||||
while ($dao->fetch()) {
|
||||
$value = array();
|
||||
CRM_Core_DAO::storeValues($dao, $value);
|
||||
$value['parent'] = $dao->parent;
|
||||
$value['parent_label'] = $dao->parent_label;
|
||||
$_cache[$argString][$dao->name] = $value;
|
||||
}
|
||||
|
||||
$cache->set($argString, $_cache[$argString]);
|
||||
}
|
||||
}
|
||||
return $_cache[$argString];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* retrieve all subtypes
|
||||
*
|
||||
* @param array $contactType
|
||||
* ..
|
||||
* @param bool $all
|
||||
* @param string $columnName
|
||||
* @param bool $ignoreCache
|
||||
*
|
||||
* @return array
|
||||
* all subtypes OR list of subtypes associated to
|
||||
* a given basic contact type
|
||||
*/
|
||||
public static function subTypes($contactType = NULL, $all = FALSE, $columnName = 'name', $ignoreCache = FALSE) {
|
||||
if ($columnName == 'name') {
|
||||
return array_keys(self::subTypeInfo($contactType, $all, $ignoreCache));
|
||||
}
|
||||
else {
|
||||
return array_values(self::subTypePairs($contactType, FALSE, NULL, $ignoreCache));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* retrieve subtype pairs with name as 'subtype-name' and 'label' as value
|
||||
*
|
||||
* @param array $contactType
|
||||
* @param bool $all
|
||||
* @param string $labelPrefix
|
||||
* @param bool $ignoreCache
|
||||
*
|
||||
* @return array
|
||||
* list of subtypes with name as 'subtype-name' and 'label' as value
|
||||
*/
|
||||
public static function subTypePairs($contactType = NULL, $all = FALSE, $labelPrefix = '- ', $ignoreCache = FALSE) {
|
||||
$subtypes = self::subTypeInfo($contactType, $all, $ignoreCache);
|
||||
|
||||
$pairs = array();
|
||||
foreach ($subtypes as $name => $info) {
|
||||
$pairs[$name] = $labelPrefix . $info['label'];
|
||||
}
|
||||
return $pairs;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* retrieve list of all types i.e basic + subtypes.
|
||||
*
|
||||
* @param bool $all
|
||||
*
|
||||
* @return array
|
||||
* Array of basic types + all subtypes.
|
||||
*/
|
||||
public static function contactTypes($all = FALSE) {
|
||||
return array_keys(self::contactTypeInfo($all));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve info array about all types i.e basic + subtypes.
|
||||
*
|
||||
* @param bool $all
|
||||
* @param bool $reset
|
||||
*
|
||||
* @return array
|
||||
* Array of basic types + all subtypes.
|
||||
*/
|
||||
public static function contactTypeInfo($all = FALSE, $reset = FALSE) {
|
||||
static $_cache = NULL;
|
||||
|
||||
if ($reset === TRUE) {
|
||||
$_cache = NULL;
|
||||
}
|
||||
|
||||
if ($_cache === NULL) {
|
||||
$_cache = array();
|
||||
}
|
||||
|
||||
$argString = $all ? 'CRM_CT_CTI_1' : 'CRM_CT_CTI_0';
|
||||
if (!array_key_exists($argString, $_cache)) {
|
||||
$cache = CRM_Utils_Cache::singleton();
|
||||
$_cache[$argString] = $cache->get($argString);
|
||||
if (!$_cache[$argString]) {
|
||||
$_cache[$argString] = array();
|
||||
|
||||
$sql = "
|
||||
SELECT type.*, parent.name as parent, parent.label as parent_label
|
||||
FROM civicrm_contact_type type
|
||||
LEFT JOIN civicrm_contact_type parent ON type.parent_id = parent.id
|
||||
WHERE type.name IS NOT NULL
|
||||
";
|
||||
if ($all === FALSE) {
|
||||
$sql .= " AND type.is_active = 1";
|
||||
}
|
||||
|
||||
$dao = CRM_Core_DAO::executeQuery($sql,
|
||||
array(),
|
||||
FALSE,
|
||||
'CRM_Contact_DAO_ContactType'
|
||||
);
|
||||
while ($dao->fetch()) {
|
||||
$value = array();
|
||||
CRM_Core_DAO::storeValues($dao, $value);
|
||||
if (array_key_exists('parent_id', $value)) {
|
||||
$value['parent'] = $dao->parent;
|
||||
$value['parent_label'] = $dao->parent_label;
|
||||
}
|
||||
$_cache[$argString][$dao->name] = $value;
|
||||
}
|
||||
|
||||
$cache->set($argString, $_cache[$argString]);
|
||||
}
|
||||
}
|
||||
|
||||
return $_cache[$argString];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve basic type pairs with name as 'built-in name' and 'label' as value.
|
||||
*
|
||||
* @param bool $all
|
||||
* @param null $typeName
|
||||
* @param null $delimiter
|
||||
*
|
||||
* @return array
|
||||
* Array of basictypes with name as 'built-in name' and 'label' as value
|
||||
*/
|
||||
public static function contactTypePairs($all = FALSE, $typeName = NULL, $delimiter = NULL) {
|
||||
$types = self::contactTypeInfo($all);
|
||||
|
||||
if ($typeName && !is_array($typeName)) {
|
||||
$typeName = explode(CRM_Core_DAO::VALUE_SEPARATOR, trim($typeName, CRM_Core_DAO::VALUE_SEPARATOR));
|
||||
}
|
||||
|
||||
$pairs = array();
|
||||
if ($typeName) {
|
||||
foreach ($typeName as $type) {
|
||||
if (array_key_exists($type, $types)) {
|
||||
$pairs[$type] = $types[$type]['label'];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
foreach ($types as $name => $info) {
|
||||
$pairs[$name] = $info['label'];
|
||||
}
|
||||
}
|
||||
|
||||
return !$delimiter ? $pairs : implode($delimiter, $pairs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of elements for select box.
|
||||
* Note that this used to default to using the hex(01) character - which results in an invalid character being used in form fields
|
||||
* which was not handled well be anything that loaded & resaved the html (outside core)
|
||||
* The use of this separator is now explicit in the calling functions as a step towards it's removal
|
||||
*
|
||||
* @param bool $all
|
||||
* @param bool $isSeparator
|
||||
* @param string $separator
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getSelectElements(
|
||||
$all = FALSE,
|
||||
$isSeparator = TRUE,
|
||||
$separator = '__'
|
||||
) {
|
||||
static $_cache = NULL;
|
||||
|
||||
if ($_cache === NULL) {
|
||||
$_cache = array();
|
||||
}
|
||||
|
||||
$argString = $all ? 'CRM_CT_GSE_1' : 'CRM_CT_GSE_0';
|
||||
$argString .= $isSeparator ? '_1' : '_0';
|
||||
$argString .= $separator;
|
||||
if (!array_key_exists($argString, $_cache)) {
|
||||
$cache = CRM_Utils_Cache::singleton();
|
||||
$_cache[$argString] = $cache->get($argString);
|
||||
|
||||
if (!$_cache[$argString]) {
|
||||
$_cache[$argString] = array();
|
||||
|
||||
$sql = "
|
||||
SELECT c.name as child_name , c.label as child_label , c.id as child_id,
|
||||
p.name as parent_name, p.label as parent_label, p.id as parent_id
|
||||
FROM civicrm_contact_type c
|
||||
LEFT JOIN civicrm_contact_type p ON ( c.parent_id = p.id )
|
||||
WHERE ( c.name IS NOT NULL )
|
||||
";
|
||||
|
||||
if ($all === FALSE) {
|
||||
$sql .= "
|
||||
AND c.is_active = 1
|
||||
AND ( p.is_active = 1 OR p.id IS NULL )
|
||||
";
|
||||
}
|
||||
$sql .= " ORDER BY c.id";
|
||||
|
||||
$values = array();
|
||||
$dao = CRM_Core_DAO::executeQuery($sql);
|
||||
while ($dao->fetch()) {
|
||||
if (!empty($dao->parent_id)) {
|
||||
$key = $isSeparator ? $dao->parent_name . $separator . $dao->child_name : $dao->child_name;
|
||||
$label = "- {$dao->child_label}";
|
||||
$pName = $dao->parent_name;
|
||||
}
|
||||
else {
|
||||
$key = $dao->child_name;
|
||||
$label = $dao->child_label;
|
||||
$pName = $dao->child_name;
|
||||
}
|
||||
|
||||
if (!isset($values[$pName])) {
|
||||
$values[$pName] = array();
|
||||
}
|
||||
$values[$pName][] = array('key' => $key, 'label' => $label);
|
||||
}
|
||||
|
||||
$selectElements = array();
|
||||
foreach ($values as $pName => $elements) {
|
||||
foreach ($elements as $element) {
|
||||
$selectElements[$element['key']] = $element['label'];
|
||||
}
|
||||
}
|
||||
$_cache[$argString] = $selectElements;
|
||||
|
||||
$cache->set($argString, $_cache[$argString]);
|
||||
}
|
||||
}
|
||||
return $_cache[$argString];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given type is a subtype.
|
||||
*
|
||||
* @param string $subType
|
||||
* Contact subType.
|
||||
* @param bool $ignoreCache
|
||||
*
|
||||
* @return bool
|
||||
* true if subType, false otherwise.
|
||||
*/
|
||||
public static function isaSubType($subType, $ignoreCache = FALSE) {
|
||||
return in_array($subType, self::subTypes(NULL, TRUE, 'name', $ignoreCache));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the basic contact type associated with given subType.
|
||||
*
|
||||
* @param array /string $subType contact subType.
|
||||
* @return array/string of basicTypes.
|
||||
*/
|
||||
public static function getBasicType($subType) {
|
||||
static $_cache = NULL;
|
||||
if ($_cache === NULL) {
|
||||
$_cache = array();
|
||||
}
|
||||
|
||||
$isArray = TRUE;
|
||||
if ($subType && !is_array($subType)) {
|
||||
$subType = array($subType);
|
||||
$isArray = FALSE;
|
||||
}
|
||||
$argString = implode('_', $subType);
|
||||
|
||||
if (!array_key_exists($argString, $_cache)) {
|
||||
$_cache[$argString] = array();
|
||||
|
||||
$sql = "
|
||||
SELECT subtype.name as contact_subtype, type.name as contact_type
|
||||
FROM civicrm_contact_type subtype
|
||||
INNER JOIN civicrm_contact_type type ON ( subtype.parent_id = type.id )
|
||||
WHERE subtype.name IN ('" . implode("','", $subType) . "' )";
|
||||
$dao = CRM_Core_DAO::executeQuery($sql);
|
||||
while ($dao->fetch()) {
|
||||
if (!$isArray) {
|
||||
$_cache[$argString] = $dao->contact_type;
|
||||
break;
|
||||
}
|
||||
$_cache[$argString][$dao->contact_subtype] = $dao->contact_type;
|
||||
}
|
||||
}
|
||||
return $_cache[$argString];
|
||||
}
|
||||
|
||||
/**
|
||||
* Suppress all subtypes present in given array.
|
||||
*
|
||||
* @param array $subTypes
|
||||
* Contact subTypes.
|
||||
* @param bool $ignoreCache
|
||||
*
|
||||
* @return array
|
||||
* Array of suppressed subTypes.
|
||||
*/
|
||||
public static function suppressSubTypes(&$subTypes, $ignoreCache = FALSE) {
|
||||
$subTypes = array_diff($subTypes, self::subTypes(NULL, TRUE, 'name', $ignoreCache));
|
||||
return $subTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if a given subtype is associated with a given basic contact type.
|
||||
*
|
||||
* @param string $subType
|
||||
* Contact subType.
|
||||
* @param string $contactType
|
||||
* Contact Type.
|
||||
* @param bool $ignoreCache
|
||||
* @param string $columnName
|
||||
*
|
||||
* @return bool
|
||||
* true if contact extends, false otherwise.
|
||||
*/
|
||||
public static function isExtendsContactType($subType, $contactType, $ignoreCache = FALSE, $columnName = 'name') {
|
||||
$subType = (array) CRM_Utils_Array::explodePadded($subType);
|
||||
$subtypeList = self::subTypes($contactType, TRUE, $columnName, $ignoreCache);
|
||||
$intersection = array_intersect($subType, $subtypeList);
|
||||
return $subType == $intersection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create shortcuts menu for contactTypes.
|
||||
*
|
||||
* @return array
|
||||
* of contactTypes
|
||||
*/
|
||||
public static function getCreateNewList() {
|
||||
$shortCuts = array();
|
||||
//@todo FIXME - using the CRM_Core_DAO::VALUE_SEPARATOR creates invalid html - if you can find the form
|
||||
// this is loaded onto then replace with something like '__' & test
|
||||
$separator = CRM_Core_DAO::VALUE_SEPARATOR;
|
||||
$contactTypes = self::getSelectElements(FALSE, TRUE, $separator);
|
||||
foreach ($contactTypes as $key => $value) {
|
||||
if ($key) {
|
||||
$typeValue = explode(CRM_Core_DAO::VALUE_SEPARATOR, $key);
|
||||
$cType = CRM_Utils_Array::value('0', $typeValue);
|
||||
$typeUrl = 'ct=' . $cType;
|
||||
if ($csType = CRM_Utils_Array::value('1', $typeValue)) {
|
||||
$typeUrl .= "&cst=$csType";
|
||||
}
|
||||
$shortCut = array(
|
||||
'path' => 'civicrm/contact/add',
|
||||
'query' => "$typeUrl&reset=1",
|
||||
'ref' => "new-$value",
|
||||
'title' => $value,
|
||||
);
|
||||
if ($csType = CRM_Utils_Array::value('1', $typeValue)) {
|
||||
$shortCuts[$cType]['shortCuts'][] = $shortCut;
|
||||
}
|
||||
else {
|
||||
$shortCuts[$cType] = $shortCut;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $shortCuts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete Contact SubTypes.
|
||||
*
|
||||
* @param int $contactTypeId
|
||||
* ID of the Contact Subtype to be deleted.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function del($contactTypeId) {
|
||||
|
||||
if (!$contactTypeId) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$params = array('id' => $contactTypeId);
|
||||
self::retrieve($params, $typeInfo);
|
||||
$name = $typeInfo['name'];
|
||||
// check if any custom group
|
||||
$custom = new CRM_Core_DAO_CustomGroup();
|
||||
$custom->whereAdd("extends_entity_column_value LIKE '%" .
|
||||
CRM_Core_DAO::VALUE_SEPARATOR .
|
||||
$name .
|
||||
CRM_Core_DAO::VALUE_SEPARATOR . "%'"
|
||||
);
|
||||
if ($custom->find()) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// remove subtype for existing contacts
|
||||
$sql = "
|
||||
UPDATE civicrm_contact SET contact_sub_type = NULL
|
||||
WHERE contact_sub_type = '$name'";
|
||||
CRM_Core_DAO::executeQuery($sql);
|
||||
|
||||
// remove subtype from contact type table
|
||||
$contactType = new CRM_Contact_DAO_ContactType();
|
||||
$contactType->id = $contactTypeId;
|
||||
$contactType->delete();
|
||||
|
||||
// remove navigation entry if any
|
||||
if ($name) {
|
||||
$sql = "
|
||||
DELETE
|
||||
FROM civicrm_navigation
|
||||
WHERE name = %1";
|
||||
$params = array(1 => array("New $name", 'String'));
|
||||
$dao = CRM_Core_DAO::executeQuery($sql, $params);
|
||||
CRM_Core_BAO_Navigation::resetNavigation();
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add or update Contact SubTypes.
|
||||
*
|
||||
* @param array $params
|
||||
* An assoc array of name/value pairs.
|
||||
*
|
||||
* @return object|void
|
||||
*/
|
||||
public static function add(&$params) {
|
||||
|
||||
// label or name
|
||||
if (empty($params['id']) && empty($params['label'])) {
|
||||
return NULL;
|
||||
}
|
||||
if (!empty($params['parent_id']) &&
|
||||
!CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_ContactType', $params['parent_id'])
|
||||
) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
$contactType = new CRM_Contact_DAO_ContactType();
|
||||
$contactType->copyValues($params);
|
||||
$contactType->id = CRM_Utils_Array::value('id', $params);
|
||||
$contactType->is_active = CRM_Utils_Array::value('is_active', $params, 0);
|
||||
|
||||
$contactType->save();
|
||||
if ($contactType->find(TRUE)) {
|
||||
$contactName = $contactType->name;
|
||||
$contact = ucfirst($contactType->label);
|
||||
$active = $contactType->is_active;
|
||||
}
|
||||
|
||||
if (!empty($params['id'])) {
|
||||
$params = array('name' => "New $contactName");
|
||||
$newParams = array(
|
||||
'label' => "New $contact",
|
||||
'is_active' => $active,
|
||||
);
|
||||
CRM_Core_BAO_Navigation::processUpdate($params, $newParams);
|
||||
}
|
||||
else {
|
||||
$name = self::getBasicType($contactName);
|
||||
if (!$name) {
|
||||
return;
|
||||
}
|
||||
$value = array('name' => "New $name");
|
||||
CRM_Core_BAO_Navigation::retrieve($value, $navinfo);
|
||||
$navigation = array(
|
||||
'label' => "New $contact",
|
||||
'name' => "New $contactName",
|
||||
'url' => "civicrm/contact/add?ct=$name&cst=$contactName&reset=1",
|
||||
'permission' => 'add contacts',
|
||||
'parent_id' => $navinfo['id'],
|
||||
'is_active' => $active,
|
||||
);
|
||||
CRM_Core_BAO_Navigation::add($navigation);
|
||||
}
|
||||
CRM_Core_BAO_Navigation::resetNavigation();
|
||||
|
||||
// reset the cache after adding
|
||||
self::subTypeInfo(NULL, FALSE, FALSE, TRUE);
|
||||
|
||||
return $contactType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the is_active flag in the db.
|
||||
*
|
||||
* @param int $id
|
||||
* Id of the database record.
|
||||
* @param bool $is_active
|
||||
* Value we want to set the is_active field.
|
||||
*
|
||||
* @return Object
|
||||
* DAO object on success, null otherwise
|
||||
*/
|
||||
public static function setIsActive($id, $is_active) {
|
||||
$params = array('id' => $id);
|
||||
self::retrieve($params, $contactinfo);
|
||||
$params = array('name' => "New $contactinfo[name]");
|
||||
$newParams = array('is_active' => $is_active);
|
||||
CRM_Core_BAO_Navigation::processUpdate($params, $newParams);
|
||||
CRM_Core_BAO_Navigation::resetNavigation();
|
||||
return CRM_Core_DAO::setFieldValue('CRM_Contact_DAO_ContactType', $id,
|
||||
'is_active', $is_active
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $typeName
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getLabel($typeName) {
|
||||
$types = self::contactTypeInfo(TRUE);
|
||||
|
||||
if (array_key_exists($typeName, $types)) {
|
||||
return $types[$typeName]['label'];
|
||||
}
|
||||
return $typeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether allow to change any contact's subtype
|
||||
* on the basis of custom data and relationship of specific subtype
|
||||
* currently used in contact/edit form amd in import validation
|
||||
*
|
||||
* @param int $contactId
|
||||
* Contact id.
|
||||
* @param string $subType
|
||||
* Subtype.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isAllowEdit($contactId, $subType = NULL) {
|
||||
|
||||
if (!$contactId) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (empty($subType)) {
|
||||
$subType = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact',
|
||||
$contactId,
|
||||
'contact_sub_type'
|
||||
);
|
||||
}
|
||||
|
||||
if (self::hasCustomData($subType, $contactId) || self::hasRelationships($contactId, $subType)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $contactType
|
||||
* @param int $contactId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function hasCustomData($contactType, $contactId = NULL) {
|
||||
$subTypeClause = '';
|
||||
|
||||
if (self::isaSubType($contactType)) {
|
||||
$subType = $contactType;
|
||||
$contactType = self::getBasicType($subType);
|
||||
|
||||
// check for empty custom data which extends subtype
|
||||
$subTypeValue = CRM_Core_DAO::VALUE_SEPARATOR . $subType . CRM_Core_DAO::VALUE_SEPARATOR;
|
||||
$subTypeClause = " AND extends_entity_column_value LIKE '%{$subTypeValue}%' ";
|
||||
}
|
||||
$query = "SELECT table_name FROM civicrm_custom_group WHERE extends = '{$contactType}' {$subTypeClause}";
|
||||
|
||||
$dao = CRM_Core_DAO::executeQuery($query);
|
||||
while ($dao->fetch()) {
|
||||
$sql = "SELECT count(id) FROM {$dao->table_name}";
|
||||
if ($contactId) {
|
||||
$sql .= " WHERE entity_id = {$contactId}";
|
||||
}
|
||||
$sql .= " LIMIT 1";
|
||||
|
||||
$customDataCount = CRM_Core_DAO::singleValueQuery($sql);
|
||||
if (!empty($customDataCount)) {
|
||||
$dao->free();
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo what does this function do?
|
||||
* @param int $contactId
|
||||
* @param $contactType
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function hasRelationships($contactId, $contactType) {
|
||||
$subTypeClause = NULL;
|
||||
if (self::isaSubType($contactType)) {
|
||||
$subType = $contactType;
|
||||
$contactType = self::getBasicType($subType);
|
||||
$subTypeClause = " AND ( ( crt.contact_type_a = '{$contactType}' AND crt.contact_sub_type_a = '{$subType}') OR
|
||||
( crt.contact_type_b = '{$contactType}' AND crt.contact_sub_type_b = '{$subType}') ) ";
|
||||
}
|
||||
else {
|
||||
$subTypeClause = " AND ( crt.contact_type_a = '{$contactType}' OR crt.contact_type_b = '{$contactType}' ) ";
|
||||
}
|
||||
|
||||
// check relationships for
|
||||
$relationshipQuery = "
|
||||
SELECT count(cr.id) FROM civicrm_relationship cr
|
||||
INNER JOIN civicrm_relationship_type crt ON
|
||||
( cr.relationship_type_id = crt.id {$subTypeClause} )
|
||||
WHERE ( cr.contact_id_a = {$contactId} OR cr.contact_id_b = {$contactId} )
|
||||
LIMIT 1";
|
||||
|
||||
$relationshipCount = CRM_Core_DAO::singleValueQuery($relationshipQuery);
|
||||
|
||||
if (!empty($relationshipCount)) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo what does this function do?
|
||||
* @param $contactType
|
||||
* @param array $subtypeSet
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getSubtypeCustomPair($contactType, $subtypeSet = array()) {
|
||||
if (empty($subtypeSet)) {
|
||||
return $subtypeSet;
|
||||
}
|
||||
|
||||
$customSet = $subTypeClause = array();
|
||||
foreach ($subtypeSet as $subtype) {
|
||||
$subtype = CRM_Utils_Type::escape($subtype, 'String');
|
||||
$subtype = CRM_Core_DAO::VALUE_SEPARATOR . $subtype . CRM_Core_DAO::VALUE_SEPARATOR;
|
||||
$subTypeClause[] = "extends_entity_column_value LIKE '%{$subtype}%' ";
|
||||
}
|
||||
$query = "SELECT table_name
|
||||
FROM civicrm_custom_group
|
||||
WHERE extends = %1 AND " . implode(" OR ", $subTypeClause);
|
||||
$dao = CRM_Core_DAO::executeQuery($query, array(1 => array($contactType, 'String')));
|
||||
while ($dao->fetch()) {
|
||||
$customSet[] = $dao->table_name;
|
||||
}
|
||||
return array_unique($customSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that does something.
|
||||
* @todo what does this function do?
|
||||
*
|
||||
* @param int $contactID
|
||||
* @param $contactType
|
||||
* @param array $oldSubtypeSet
|
||||
* @param array $newSubtypeSet
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function deleteCustomSetForSubtypeMigration(
|
||||
$contactID,
|
||||
$contactType,
|
||||
$oldSubtypeSet = array(),
|
||||
$newSubtypeSet = array()
|
||||
) {
|
||||
$oldCustomSet = self::getSubtypeCustomPair($contactType, $oldSubtypeSet);
|
||||
$newCustomSet = self::getSubtypeCustomPair($contactType, $newSubtypeSet);
|
||||
|
||||
$customToBeRemoved = array_diff($oldCustomSet, $newCustomSet);
|
||||
foreach ($customToBeRemoved as $customTable) {
|
||||
self::deleteCustomRowsForEntityID($customTable, $contactID);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete content / rows of a custom table specific to a subtype for a given custom-group.
|
||||
* This function currently works for contact subtypes only and could be later improved / genralized
|
||||
* to work for other subtypes as well.
|
||||
*
|
||||
* @param int $gID
|
||||
* Custom group id.
|
||||
* @param array $subtypes
|
||||
* List of subtypes related to which entry is to be removed.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function deleteCustomRowsOfSubtype($gID, $subtypes = array(), $subtypesToPreserve = array()) {
|
||||
if (!$gID or empty($subtypes)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$tableName = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $gID, 'table_name');
|
||||
|
||||
// drop triggers CRM-13587
|
||||
CRM_Core_DAO::dropTriggers($tableName);
|
||||
|
||||
foreach ($subtypesToPreserve as $subtypeToPreserve) {
|
||||
$subtypeToPreserve = CRM_Utils_Type::escape($subtypeToPreserve, 'String');
|
||||
$subtypesToPreserveClause[] = "(civicrm_contact.contact_sub_type NOT LIKE '%" . CRM_Core_DAO::VALUE_SEPARATOR . $subtypeToPreserve . CRM_Core_DAO::VALUE_SEPARATOR . "%')";
|
||||
}
|
||||
$subtypesToPreserveClause = implode(' AND ', $subtypesToPreserveClause);
|
||||
|
||||
$subtypeClause = array();
|
||||
foreach ($subtypes as $subtype) {
|
||||
$subtype = CRM_Utils_Type::escape($subtype, 'String');
|
||||
$subtypeClause[] = "( civicrm_contact.contact_sub_type LIKE '%" . CRM_Core_DAO::VALUE_SEPARATOR . $subtype . CRM_Core_DAO::VALUE_SEPARATOR . "%'"
|
||||
. " AND " . $subtypesToPreserveClause . ")";
|
||||
}
|
||||
$subtypeClause = implode(' OR ', $subtypeClause);
|
||||
|
||||
$query = "DELETE custom.*
|
||||
FROM {$tableName} custom
|
||||
INNER JOIN civicrm_contact ON civicrm_contact.id = custom.entity_id
|
||||
WHERE ($subtypeClause)";
|
||||
|
||||
CRM_Core_DAO::singleValueQuery($query);
|
||||
|
||||
// rebuild triggers CRM-13587
|
||||
CRM_Core_DAO::triggerRebuild($tableName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete content / rows of a custom table specific entity-id for a given custom-group table.
|
||||
*
|
||||
* @param int $customTable
|
||||
* Custom table name.
|
||||
* @param int $entityID
|
||||
* Entity id.
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public static function deleteCustomRowsForEntityID($customTable, $entityID) {
|
||||
$customTable = CRM_Utils_Type::escape($customTable, 'String');
|
||||
$query = "DELETE FROM {$customTable} WHERE entity_id = %1";
|
||||
return CRM_Core_DAO::singleValueQuery($query, array(1 => array($entityID, 'Integer')));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?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_DashboardContact extends CRM_Contact_DAO_DashboardContact {
|
||||
}
|
1415
sites/all/modules/civicrm/CRM/Contact/BAO/Group.php
Normal file
1415
sites/all/modules/civicrm/CRM/Contact/BAO/Group.php
Normal file
File diff suppressed because it is too large
Load diff
798
sites/all/modules/civicrm/CRM/Contact/BAO/GroupContact.php
Normal file
798
sites/all/modules/civicrm/CRM/Contact/BAO/GroupContact.php
Normal file
|
@ -0,0 +1,798 @@
|
|||
<?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_GroupContact extends CRM_Contact_DAO_GroupContact {
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an associative array and creates a groupContact object.
|
||||
*
|
||||
* the function extract all the params it needs to initialize the create a
|
||||
* group 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_Group
|
||||
*/
|
||||
public static function add(&$params) {
|
||||
|
||||
$dataExists = self::dataExists($params);
|
||||
if (!$dataExists) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
$groupContact = new CRM_Contact_BAO_GroupContact();
|
||||
$groupContact->copyValues($params);
|
||||
CRM_Contact_BAO_SubscriptionHistory::create($params);
|
||||
$groupContact->save();
|
||||
return $groupContact;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there is data to create the object.
|
||||
*
|
||||
* @param array $params
|
||||
* (reference ) an assoc array of name/value pairs.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function dataExists(&$params) {
|
||||
// return if no data present
|
||||
if ($params['group_id'] == 0) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the list of params in the params array, 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 array
|
||||
* (reference) the values that could be potentially assigned to smarty
|
||||
*/
|
||||
public static function getValues(&$params, &$values) {
|
||||
if (empty($params)) {
|
||||
return NULL;
|
||||
}
|
||||
$values['group']['data'] = CRM_Contact_BAO_GroupContact::getContactGroup($params['contact_id'],
|
||||
'Added',
|
||||
3
|
||||
);
|
||||
|
||||
// get the total count of groups
|
||||
$values['group']['totalCount'] = CRM_Contact_BAO_GroupContact::getContactGroup($params['contact_id'],
|
||||
'Added',
|
||||
NULL,
|
||||
TRUE
|
||||
);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an array of contact ids, add all the contacts to the group
|
||||
*
|
||||
* @param array $contactIds
|
||||
* The array of contact ids to be added.
|
||||
* @param int $groupId
|
||||
* The id of the group.
|
||||
* @param string $method
|
||||
* @param string $status
|
||||
* @param int $tracking
|
||||
*
|
||||
* @return array
|
||||
* (total, added, notAdded) count of contacts added to group
|
||||
*/
|
||||
public static function addContactsToGroup(
|
||||
$contactIds,
|
||||
$groupId,
|
||||
$method = 'Admin',
|
||||
$status = 'Added',
|
||||
$tracking = NULL
|
||||
) {
|
||||
if (empty($contactIds) || empty($groupId)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
CRM_Utils_Hook::pre('create', 'GroupContact', $groupId, $contactIds);
|
||||
|
||||
list($numContactsAdded, $numContactsNotAdded)
|
||||
= self::bulkAddContactsToGroup($contactIds, $groupId, $method, $status, $tracking);
|
||||
|
||||
CRM_Contact_BAO_Contact_Utils::clearContactCaches();
|
||||
|
||||
CRM_Utils_Hook::post('create', 'GroupContact', $groupId, $contactIds);
|
||||
|
||||
return array(count($contactIds), $numContactsAdded, $numContactsNotAdded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an array of contact ids, remove all the contacts from the group
|
||||
*
|
||||
* @param array $contactIds
|
||||
* (reference ) the array of contact ids to be removed.
|
||||
* @param int $groupId
|
||||
* The id of the group.
|
||||
*
|
||||
* @param string $method
|
||||
* @param string $status
|
||||
* @param NULL $tracking
|
||||
*
|
||||
* @return array
|
||||
* (total, removed, notRemoved) count of contacts removed to group
|
||||
*/
|
||||
public static function removeContactsFromGroup(
|
||||
&$contactIds,
|
||||
$groupId,
|
||||
$method = 'Admin',
|
||||
$status = 'Removed',
|
||||
$tracking = NULL
|
||||
) {
|
||||
if (!is_array($contactIds)) {
|
||||
return array(0, 0, 0);
|
||||
}
|
||||
|
||||
if ($status == 'Removed' || $status == 'Deleted') {
|
||||
$op = 'delete';
|
||||
}
|
||||
else {
|
||||
$op = 'edit';
|
||||
}
|
||||
|
||||
CRM_Utils_Hook::pre($op, 'GroupContact', $groupId, $contactIds);
|
||||
|
||||
$date = date('YmdHis');
|
||||
$numContactsRemoved = 0;
|
||||
$numContactsNotRemoved = 0;
|
||||
|
||||
$group = new CRM_Contact_DAO_Group();
|
||||
$group->id = $groupId;
|
||||
$group->find(TRUE);
|
||||
|
||||
foreach ($contactIds as $contactId) {
|
||||
if ($status == 'Deleted') {
|
||||
$query = "DELETE FROM civicrm_group_contact WHERE contact_id=$contactId AND group_id=$groupId";
|
||||
$dao = CRM_Core_DAO::executeQuery($query);
|
||||
$historyParams = array(
|
||||
'group_id' => $groupId,
|
||||
'contact_id' => $contactId,
|
||||
'status' => $status,
|
||||
'method' => $method,
|
||||
'date' => $date,
|
||||
'tracking' => $tracking,
|
||||
);
|
||||
CRM_Contact_BAO_SubscriptionHistory::create($historyParams);
|
||||
}
|
||||
else {
|
||||
$groupContact = new CRM_Contact_DAO_GroupContact();
|
||||
$groupContact->group_id = $groupId;
|
||||
$groupContact->contact_id = $contactId;
|
||||
// check if the selected contact id already a member, or if this is
|
||||
// an opt-out of a smart group.
|
||||
// if not a member remove to groupContact else keep the count of contacts that are not removed
|
||||
if ($groupContact->find(TRUE) || $group->saved_search_id) {
|
||||
// remove the contact from the group
|
||||
$numContactsRemoved++;
|
||||
}
|
||||
else {
|
||||
$numContactsNotRemoved++;
|
||||
}
|
||||
|
||||
//now we grant the negative membership to contact if not member. CRM-3711
|
||||
$historyParams = array(
|
||||
'group_id' => $groupId,
|
||||
'contact_id' => $contactId,
|
||||
'status' => $status,
|
||||
'method' => $method,
|
||||
'date' => $date,
|
||||
'tracking' => $tracking,
|
||||
);
|
||||
CRM_Contact_BAO_SubscriptionHistory::create($historyParams);
|
||||
$groupContact->status = $status;
|
||||
$groupContact->save();
|
||||
}
|
||||
}
|
||||
|
||||
CRM_Contact_BAO_Contact_Utils::clearContactCaches();
|
||||
|
||||
CRM_Utils_Hook::post($op, 'GroupContact', $groupId, $contactIds);
|
||||
|
||||
return array(count($contactIds), $numContactsRemoved, $numContactsNotRemoved);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of all the groups and groups for a contact.
|
||||
*
|
||||
* @param int $contactId
|
||||
* Contact id.
|
||||
*
|
||||
* @param bool $visibility
|
||||
*
|
||||
*
|
||||
* @return array
|
||||
* this array has key-> group id and value group title
|
||||
*/
|
||||
public static function getGroupList($contactId = 0, $visibility = FALSE) {
|
||||
$group = new CRM_Contact_DAO_Group();
|
||||
|
||||
$select = $from = $where = '';
|
||||
|
||||
$select = 'SELECT civicrm_group.id, civicrm_group.title ';
|
||||
$from = ' FROM civicrm_group ';
|
||||
$where = " WHERE civicrm_group.is_active = 1 ";
|
||||
if ($contactId) {
|
||||
$from .= ' , civicrm_group_contact ';
|
||||
$where .= " AND civicrm_group.id = civicrm_group_contact.group_id
|
||||
AND civicrm_group_contact.contact_id = " . CRM_Utils_Type::escape($contactId, 'Integer');
|
||||
}
|
||||
|
||||
if ($visibility) {
|
||||
$where .= " AND civicrm_group.visibility != 'User and User Admin Only'";
|
||||
}
|
||||
$groupBy = " GROUP BY civicrm_group.id";
|
||||
|
||||
$orderby = " ORDER BY civicrm_group.name";
|
||||
$sql = $select . $from . $where . $groupBy . $orderby;
|
||||
|
||||
$group->query($sql);
|
||||
|
||||
$values = array();
|
||||
while ($group->fetch()) {
|
||||
$values[$group->id] = $group->title;
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of groups for contact based on status of group membership.
|
||||
*
|
||||
* @param int $contactId
|
||||
* Contact id.
|
||||
* @param string $status
|
||||
* State of membership.
|
||||
* @param int $numGroupContact
|
||||
* Number of groups for a contact that should be shown.
|
||||
* @param bool $count
|
||||
* True if we are interested only in the count.
|
||||
* @param bool $ignorePermission
|
||||
* True if we should ignore permissions for the current user.
|
||||
* useful in profile where permissions are limited for the user. If left
|
||||
* at false only groups viewable by the current user are returned
|
||||
* @param bool $onlyPublicGroups
|
||||
* True if we want to hide system groups.
|
||||
*
|
||||
* @param bool $excludeHidden
|
||||
*
|
||||
* @param int $groupId
|
||||
*
|
||||
* @param bool $includeSmartGroups
|
||||
* Include or Exclude Smart Group(s)
|
||||
*
|
||||
* @return array|int $values
|
||||
* the relevant data object values for the contact or the total count when $count is TRUE
|
||||
*/
|
||||
public static function getContactGroup(
|
||||
$contactId,
|
||||
$status = NULL,
|
||||
$numGroupContact = NULL,
|
||||
$count = FALSE,
|
||||
$ignorePermission = FALSE,
|
||||
$onlyPublicGroups = FALSE,
|
||||
$excludeHidden = TRUE,
|
||||
$groupId = NULL,
|
||||
$includeSmartGroups = FALSE
|
||||
) {
|
||||
if ($count) {
|
||||
$select = 'SELECT count(DISTINCT civicrm_group_contact.id)';
|
||||
}
|
||||
else {
|
||||
$select = 'SELECT
|
||||
civicrm_group_contact.id as civicrm_group_contact_id,
|
||||
civicrm_group.title as group_title,
|
||||
civicrm_group.visibility as visibility,
|
||||
civicrm_group_contact.status as status,
|
||||
civicrm_group.id as group_id,
|
||||
civicrm_group.is_hidden as is_hidden,
|
||||
civicrm_subscription_history.date as date,
|
||||
civicrm_subscription_history.method as method';
|
||||
}
|
||||
|
||||
$where = " WHERE contact_a.id = %1 AND civicrm_group.is_active = 1";
|
||||
if (!$includeSmartGroups) {
|
||||
$where .= " AND saved_search_id IS NULL";
|
||||
}
|
||||
if ($excludeHidden) {
|
||||
$where .= " AND civicrm_group.is_hidden = 0 ";
|
||||
}
|
||||
$params = array(1 => array($contactId, 'Integer'));
|
||||
if (!empty($status)) {
|
||||
$where .= ' AND civicrm_group_contact.status = %2';
|
||||
$params[2] = array($status, 'String');
|
||||
}
|
||||
if (!empty($groupId)) {
|
||||
$where .= " AND civicrm_group.id = %3 ";
|
||||
$params[3] = array($groupId, 'Integer');
|
||||
}
|
||||
$tables = array(
|
||||
'civicrm_group_contact' => 1,
|
||||
'civicrm_group' => 1,
|
||||
'civicrm_subscription_history' => 1,
|
||||
);
|
||||
$whereTables = array();
|
||||
if ($ignorePermission) {
|
||||
$permission = ' ( 1 ) ';
|
||||
}
|
||||
else {
|
||||
$permission = CRM_Core_Permission::getPermissionedStaticGroupClause(CRM_Core_Permission::VIEW, $tables, $whereTables);
|
||||
}
|
||||
|
||||
$from = CRM_Contact_BAO_Query::fromClause($tables);
|
||||
|
||||
$where .= " AND $permission ";
|
||||
|
||||
if ($onlyPublicGroups) {
|
||||
$where .= " AND civicrm_group.visibility != 'User and User Admin Only' ";
|
||||
}
|
||||
|
||||
$order = $limit = '';
|
||||
if (!$count) {
|
||||
$order = ' ORDER BY civicrm_group.title, civicrm_subscription_history.date ASC';
|
||||
|
||||
if ($numGroupContact) {
|
||||
$limit = " LIMIT 0, $numGroupContact";
|
||||
}
|
||||
}
|
||||
|
||||
$sql = $select . $from . $where . $order . $limit;
|
||||
|
||||
if ($count) {
|
||||
$result = CRM_Core_DAO::singleValueQuery($sql, $params);
|
||||
return $result;
|
||||
}
|
||||
else {
|
||||
$dao = CRM_Core_DAO::executeQuery($sql, $params);
|
||||
$values = array();
|
||||
while ($dao->fetch()) {
|
||||
$id = $dao->civicrm_group_contact_id;
|
||||
$values[$id]['id'] = $id;
|
||||
$values[$id]['group_id'] = $dao->group_id;
|
||||
$values[$id]['title'] = $dao->group_title;
|
||||
$values[$id]['visibility'] = $dao->visibility;
|
||||
$values[$id]['is_hidden'] = $dao->is_hidden;
|
||||
switch ($dao->status) {
|
||||
case 'Added':
|
||||
$prefix = 'in_';
|
||||
break;
|
||||
|
||||
case 'Removed':
|
||||
$prefix = 'out_';
|
||||
break;
|
||||
|
||||
default:
|
||||
$prefix = 'pending_';
|
||||
}
|
||||
$values[$id][$prefix . 'date'] = $dao->date;
|
||||
$values[$id][$prefix . 'method'] = $dao->method;
|
||||
if ($status == 'Removed') {
|
||||
$query = "SELECT `date` as `date_added` FROM civicrm_subscription_history WHERE id = (SELECT max(id) FROM civicrm_subscription_history WHERE contact_id = %1 AND status = \"Added\" AND group_id = $dao->group_id )";
|
||||
$dateDAO = CRM_Core_DAO::executeQuery($query, $params);
|
||||
if ($dateDAO->fetch()) {
|
||||
$values[$id]['date_added'] = $dateDAO->date_added;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns membership details of a contact for a group.
|
||||
*
|
||||
* @param int $contactId
|
||||
* Id of the contact.
|
||||
* @param int $groupID
|
||||
* Id of a particular group.
|
||||
* @param string $method
|
||||
* If we want the subscription history details for a specific method.
|
||||
*
|
||||
* @return object
|
||||
* of group contact
|
||||
*/
|
||||
public static function getMembershipDetail($contactId, $groupID, $method = 'Email') {
|
||||
$leftJoin = $where = $orderBy = NULL;
|
||||
|
||||
if ($method) {
|
||||
//CRM-13341 add group_id clause
|
||||
$leftJoin = "
|
||||
LEFT JOIN civicrm_subscription_history
|
||||
ON ( civicrm_group_contact.contact_id = civicrm_subscription_history.contact_id
|
||||
AND civicrm_subscription_history.group_id = {$groupID} )";
|
||||
$where = "AND civicrm_subscription_history.method ='Email'";
|
||||
$orderBy = "ORDER BY civicrm_subscription_history.id DESC";
|
||||
}
|
||||
$query = "
|
||||
SELECT *
|
||||
FROM civicrm_group_contact
|
||||
$leftJoin
|
||||
WHERE civicrm_group_contact.contact_id = %1
|
||||
AND civicrm_group_contact.group_id = %2
|
||||
$where
|
||||
$orderBy
|
||||
";
|
||||
|
||||
$params = array(
|
||||
1 => array($contactId, 'Integer'),
|
||||
2 => array($groupID, 'Integer'),
|
||||
);
|
||||
$dao = CRM_Core_DAO::executeQuery($query, $params);
|
||||
$dao->fetch();
|
||||
return $dao;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to get Group Id.
|
||||
*
|
||||
* @param int $groupContactID
|
||||
* Id of a particular group.
|
||||
*
|
||||
*
|
||||
* @return groupID
|
||||
*/
|
||||
public static function getGroupId($groupContactID) {
|
||||
$dao = new CRM_Contact_DAO_GroupContact();
|
||||
$dao->id = $groupContactID;
|
||||
$dao->find(TRUE);
|
||||
return $dao->group_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an associative array and creates / removes
|
||||
* contacts from the groups
|
||||
*
|
||||
*
|
||||
* @param array $params
|
||||
* (reference ) an assoc array of name/value pairs.
|
||||
* @param array $contactId
|
||||
* Contact id.
|
||||
*
|
||||
* @param bool $visibility
|
||||
* @param string $method
|
||||
*/
|
||||
public static function create(&$params, $contactId, $visibility = FALSE, $method = 'Admin') {
|
||||
$contactIds = array();
|
||||
$contactIds[] = $contactId;
|
||||
|
||||
//if $visibility is true we are coming in via profile mean $method = 'Web'
|
||||
$ignorePermission = FALSE;
|
||||
if ($visibility) {
|
||||
$ignorePermission = TRUE;
|
||||
}
|
||||
|
||||
if ($contactId) {
|
||||
$contactGroupList = CRM_Contact_BAO_GroupContact::getContactGroup($contactId, 'Added',
|
||||
NULL, FALSE, $ignorePermission
|
||||
);
|
||||
if (is_array($contactGroupList)) {
|
||||
foreach ($contactGroupList as $key) {
|
||||
$groupId = $key['group_id'];
|
||||
$contactGroup[$groupId] = $groupId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get the list of all the groups
|
||||
$allGroup = CRM_Contact_BAO_GroupContact::getGroupList(0, $visibility);
|
||||
|
||||
// this fix is done to prevent warning generated by array_key_exits incase of empty array is given as input
|
||||
if (!is_array($params)) {
|
||||
$params = array();
|
||||
}
|
||||
|
||||
// this fix is done to prevent warning generated by array_key_exits incase of empty array is given as input
|
||||
if (!isset($contactGroup) || !is_array($contactGroup)) {
|
||||
$contactGroup = array();
|
||||
}
|
||||
|
||||
// check which values has to be add/remove contact from group
|
||||
foreach ($allGroup as $key => $varValue) {
|
||||
if (!empty($params[$key]) && !array_key_exists($key, $contactGroup)) {
|
||||
// add contact to group
|
||||
CRM_Contact_BAO_GroupContact::addContactsToGroup($contactIds, $key, $method);
|
||||
}
|
||||
elseif (empty($params[$key]) && array_key_exists($key, $contactGroup)) {
|
||||
// remove contact from group
|
||||
CRM_Contact_BAO_GroupContact::removeContactsFromGroup($contactIds, $key, $method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $contactID
|
||||
* @param int $groupID
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isContactInGroup($contactID, $groupID) {
|
||||
if (!CRM_Utils_Rule::positiveInteger($contactID) ||
|
||||
!CRM_Utils_Rule::positiveInteger($groupID)
|
||||
) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$params = array(
|
||||
array('group', 'IN', array($groupID), 0, 0),
|
||||
array('contact_id', '=', $contactID, 0, 0),
|
||||
);
|
||||
list($contacts, $_) = CRM_Contact_BAO_Query::apiQuery($params, array('contact_id'));
|
||||
|
||||
if (!empty($contacts)) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function merges the groups from otherContactID to mainContactID.
|
||||
* along with subscription history
|
||||
*
|
||||
* @param int $mainContactId
|
||||
* Contact id of main contact record.
|
||||
* @param int $otherContactId
|
||||
* Contact id of record which is going to merge.
|
||||
*
|
||||
* @see CRM_Dedupe_Merger::cpTables()
|
||||
*
|
||||
* TODO: use the 3rd $sqls param to append sql statements rather than executing them here
|
||||
*/
|
||||
public static function mergeGroupContact($mainContactId, $otherContactId) {
|
||||
$params = array(
|
||||
1 => array($mainContactId, 'Integer'),
|
||||
2 => array($otherContactId, 'Integer'),
|
||||
);
|
||||
|
||||
// find all groups that are in otherContactID but not in mainContactID, copy them over
|
||||
$sql = "
|
||||
SELECT cOther.group_id
|
||||
FROM civicrm_group_contact cOther
|
||||
LEFT JOIN civicrm_group_contact cMain ON cOther.group_id = cMain.group_id AND cMain.contact_id = %1
|
||||
WHERE cOther.contact_id = %2
|
||||
AND cMain.contact_id IS NULL
|
||||
";
|
||||
$dao = CRM_Core_DAO::executeQuery($sql, $params);
|
||||
|
||||
$otherGroupIDs = array();
|
||||
while ($dao->fetch()) {
|
||||
$otherGroupIDs[] = $dao->group_id;
|
||||
}
|
||||
|
||||
if (!empty($otherGroupIDs)) {
|
||||
$otherGroupIDString = implode(',', $otherGroupIDs);
|
||||
|
||||
$sql = "
|
||||
UPDATE civicrm_group_contact
|
||||
SET contact_id = %1
|
||||
WHERE contact_id = %2
|
||||
AND group_id IN ( $otherGroupIDString )
|
||||
";
|
||||
CRM_Core_DAO::executeQuery($sql, $params);
|
||||
|
||||
$sql = "
|
||||
UPDATE civicrm_subscription_history
|
||||
SET contact_id = %1
|
||||
WHERE contact_id = %2
|
||||
AND group_id IN ( $otherGroupIDString )
|
||||
";
|
||||
CRM_Core_DAO::executeQuery($sql, $params);
|
||||
}
|
||||
|
||||
$sql = "
|
||||
SELECT cOther.group_id as group_id,
|
||||
cOther.status as group_status
|
||||
FROM civicrm_group_contact cMain
|
||||
INNER JOIN civicrm_group_contact cOther ON cMain.group_id = cOther.group_id
|
||||
WHERE cMain.contact_id = %1
|
||||
AND cOther.contact_id = %2
|
||||
";
|
||||
$dao = CRM_Core_DAO::executeQuery($sql, $params);
|
||||
|
||||
$groupIDs = array();
|
||||
while ($dao->fetch()) {
|
||||
// only copy it over if it has added status and migrate the history
|
||||
if ($dao->group_status == 'Added') {
|
||||
$groupIDs[] = $dao->group_id;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($groupIDs)) {
|
||||
$groupIDString = implode(',', $groupIDs);
|
||||
|
||||
$sql = "
|
||||
UPDATE civicrm_group_contact
|
||||
SET status = 'Added'
|
||||
WHERE contact_id = %1
|
||||
AND group_id IN ( $groupIDString )
|
||||
";
|
||||
CRM_Core_DAO::executeQuery($sql, $params);
|
||||
|
||||
$sql = "
|
||||
UPDATE civicrm_subscription_history
|
||||
SET contact_id = %1
|
||||
WHERE contact_id = %2
|
||||
AND group_id IN ( $groupIDString )
|
||||
";
|
||||
CRM_Core_DAO::executeQuery($sql, $params);
|
||||
}
|
||||
|
||||
// delete all the other group contacts
|
||||
$sql = "
|
||||
DELETE
|
||||
FROM civicrm_group_contact
|
||||
WHERE contact_id = %2
|
||||
";
|
||||
CRM_Core_DAO::executeQuery($sql, $params);
|
||||
|
||||
$sql = "
|
||||
DELETE
|
||||
FROM civicrm_subscription_history
|
||||
WHERE contact_id = %2
|
||||
";
|
||||
CRM_Core_DAO::executeQuery($sql, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an array of contact ids, add all the contacts to the group
|
||||
*
|
||||
* @param array $contactIDs
|
||||
* The array of contact ids to be added.
|
||||
* @param int $groupID
|
||||
* The id of the group.
|
||||
* @param string $method
|
||||
* @param string $status
|
||||
* @param NULL $tracking
|
||||
*
|
||||
* @return array
|
||||
* (total, added, notAdded) count of contacts added to group
|
||||
*/
|
||||
public static function bulkAddContactsToGroup(
|
||||
$contactIDs,
|
||||
$groupID,
|
||||
$method = 'Admin',
|
||||
$status = 'Added',
|
||||
$tracking = NULL
|
||||
) {
|
||||
|
||||
$numContactsAdded = 0;
|
||||
$numContactsNotAdded = 0;
|
||||
|
||||
$contactGroupSQL = "
|
||||
REPLACE INTO civicrm_group_contact ( group_id, contact_id, status )
|
||||
VALUES
|
||||
";
|
||||
$subscriptioHistorySQL = "
|
||||
INSERT INTO civicrm_subscription_history( group_id, contact_id, date, method, status, tracking )
|
||||
VALUES
|
||||
";
|
||||
|
||||
$date = date('YmdHis');
|
||||
|
||||
// to avoid long strings, lets do BULK_INSERT_HIGH_COUNT values at a time
|
||||
while (!empty($contactIDs)) {
|
||||
$input = array_splice($contactIDs, 0, CRM_Core_DAO::BULK_INSERT_HIGH_COUNT);
|
||||
$contactStr = implode(',', $input);
|
||||
|
||||
// lets check their current status
|
||||
$sql = "
|
||||
SELECT GROUP_CONCAT(contact_id) as contactStr
|
||||
FROM civicrm_group_contact
|
||||
WHERE group_id = %1
|
||||
AND status = %2
|
||||
AND contact_id IN ( $contactStr )
|
||||
";
|
||||
$params = array(
|
||||
1 => array($groupID, 'Integer'),
|
||||
2 => array($status, 'String'),
|
||||
);
|
||||
|
||||
$presentIDs = array();
|
||||
$dao = CRM_Core_DAO::executeQuery($sql, $params);
|
||||
if ($dao->fetch()) {
|
||||
$presentIDs = explode(',', $dao->contactStr);
|
||||
$presentIDs = array_flip($presentIDs);
|
||||
}
|
||||
|
||||
$gcValues = $shValues = array();
|
||||
foreach ($input as $cid) {
|
||||
if (isset($presentIDs[$cid])) {
|
||||
$numContactsNotAdded++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$gcValues[] = "( $groupID, $cid, '$status' )";
|
||||
$shValues[] = "( $groupID, $cid, '$date', '$method', '$status', '$tracking' )";
|
||||
$numContactsAdded++;
|
||||
}
|
||||
|
||||
if (!empty($gcValues)) {
|
||||
$cgSQL = $contactGroupSQL . implode(",\n", $gcValues);
|
||||
CRM_Core_DAO::executeQuery($cgSQL);
|
||||
|
||||
$shSQL = $subscriptioHistorySQL . implode(",\n", $shValues);
|
||||
CRM_Core_DAO::executeQuery($shSQL);
|
||||
}
|
||||
}
|
||||
|
||||
return array($numContactsAdded, $numContactsNotAdded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get options for a given field.
|
||||
* @see CRM_Core_DAO::buildOptions
|
||||
*
|
||||
* @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();
|
||||
|
||||
$options = CRM_Core_PseudoConstant::get(__CLASS__, $fieldName, $params, $context);
|
||||
|
||||
// Sort group list by hierarchy
|
||||
// TODO: This will only work when api.entity is "group_contact". What about others?
|
||||
if (($fieldName == 'group' || $fieldName == 'group_id') && ($context == 'search' || $context == 'create')) {
|
||||
$options = CRM_Contact_BAO_Group::getGroupsHierarchy($options, NULL, '- ', TRUE);
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
}
|
766
sites/all/modules/civicrm/CRM/Contact/BAO/GroupContactCache.php
Normal file
766
sites/all/modules/civicrm/CRM/Contact/BAO/GroupContactCache.php
Normal file
|
@ -0,0 +1,766 @@
|
|||
<?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_GroupContactCache extends CRM_Contact_DAO_GroupContactCache {
|
||||
|
||||
static $_alreadyLoaded = array();
|
||||
|
||||
/**
|
||||
* Get a list of caching modes.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getModes() {
|
||||
return array(
|
||||
// Flush expired caches in response to user actions.
|
||||
'opportunistic' => ts('Opportunistic Flush'),
|
||||
|
||||
// Flush expired caches via background cron jobs.
|
||||
'deterministic' => ts('Cron Flush'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if we have cache entries for this group.
|
||||
*
|
||||
* If not, regenerate, else return.
|
||||
*
|
||||
* @param array $groupIDs
|
||||
* Of group that we are checking against.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if we did not regenerate, FALSE if we did
|
||||
*/
|
||||
public static function check($groupIDs) {
|
||||
if (empty($groupIDs)) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return self::loadAll($groupIDs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formulate the query to see which groups needs to be refreshed.
|
||||
*
|
||||
* The calculation is based on their cache date and the smartGroupCacheTimeOut
|
||||
*
|
||||
* @param string $groupIDClause
|
||||
* The clause which limits which groups we need to evaluate.
|
||||
* @param bool $includeHiddenGroups
|
||||
* Hidden groups are excluded by default.
|
||||
*
|
||||
* @return string
|
||||
* the sql query which lists the groups that need to be refreshed
|
||||
*/
|
||||
public static function groupRefreshedClause($groupIDClause = NULL, $includeHiddenGroups = FALSE) {
|
||||
$smartGroupCacheTimeoutDateTime = self::getCacheInvalidDateTime();
|
||||
|
||||
$query = "
|
||||
SELECT g.id
|
||||
FROM civicrm_group g
|
||||
WHERE ( g.saved_search_id IS NOT NULL OR g.children IS NOT NULL )
|
||||
AND g.is_active = 1
|
||||
AND (
|
||||
g.cache_date IS NULL
|
||||
OR cache_date <= $smartGroupCacheTimeoutDateTime
|
||||
OR NOW() >= g.refresh_date
|
||||
)";
|
||||
|
||||
if (!$includeHiddenGroups) {
|
||||
$query .= "AND (g.is_hidden = 0 OR g.is_hidden IS NULL)";
|
||||
}
|
||||
|
||||
if (!empty($groupIDClause)) {
|
||||
$query .= " AND ( $groupIDClause ) ";
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if a group has been refreshed recently.
|
||||
*
|
||||
* This is primarily used in a locking scenario when some other process might have refreshed things underneath
|
||||
* this process
|
||||
*
|
||||
* @param int $groupID
|
||||
* The group ID.
|
||||
* @param bool $includeHiddenGroups
|
||||
* Hidden groups are excluded by default.
|
||||
*
|
||||
* @return string
|
||||
* the sql query which lists the groups that need to be refreshed
|
||||
*/
|
||||
public static function shouldGroupBeRefreshed($groupID, $includeHiddenGroups = FALSE) {
|
||||
$query = self::groupRefreshedClause("g.id = %1", $includeHiddenGroups);
|
||||
$params = array(1 => array($groupID, 'Integer'));
|
||||
|
||||
// if the query returns the group ID, it means the group is a valid candidate for refreshing
|
||||
return CRM_Core_DAO::singleValueQuery($query, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if we have cache entries for this group.
|
||||
*
|
||||
* if not, regenerate, else return
|
||||
*
|
||||
* @param int|array $groupIDs groupIDs of group that we are checking against
|
||||
* if empty, all groups are checked
|
||||
* @param int $limit
|
||||
* Limits the number of groups we evaluate.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if we did not regenerate, FALSE if we did
|
||||
*/
|
||||
public static function loadAll($groupIDs = NULL, $limit = 0) {
|
||||
// ensure that all the smart groups are loaded
|
||||
// this function is expensive and should be sparingly used if groupIDs is empty
|
||||
if (empty($groupIDs)) {
|
||||
$groupIDClause = NULL;
|
||||
$groupIDs = array();
|
||||
}
|
||||
else {
|
||||
if (!is_array($groupIDs)) {
|
||||
$groupIDs = array($groupIDs);
|
||||
}
|
||||
|
||||
// note escapeString is a must here and we can't send the imploded value as second argument to
|
||||
// the executeQuery(), since that would put single quote around the string and such a string
|
||||
// of comma separated integers would not work.
|
||||
$groupIDString = CRM_Core_DAO::escapeString(implode(', ', $groupIDs));
|
||||
|
||||
$groupIDClause = "g.id IN ({$groupIDString})";
|
||||
}
|
||||
|
||||
$query = self::groupRefreshedClause($groupIDClause);
|
||||
|
||||
$limitClause = $orderClause = NULL;
|
||||
if ($limit > 0) {
|
||||
$limitClause = " LIMIT 0, $limit";
|
||||
$orderClause = " ORDER BY g.cache_date, g.refresh_date";
|
||||
}
|
||||
// We ignore hidden groups and disabled groups
|
||||
$query .= "
|
||||
$orderClause
|
||||
$limitClause
|
||||
";
|
||||
|
||||
$dao = CRM_Core_DAO::executeQuery($query);
|
||||
$processGroupIDs = array();
|
||||
$refreshGroupIDs = $groupIDs;
|
||||
while ($dao->fetch()) {
|
||||
$processGroupIDs[] = $dao->id;
|
||||
|
||||
// remove this id from refreshGroupIDs
|
||||
foreach ($refreshGroupIDs as $idx => $gid) {
|
||||
if ($gid == $dao->id) {
|
||||
unset($refreshGroupIDs[$idx]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($refreshGroupIDs)) {
|
||||
$refreshGroupIDString = CRM_Core_DAO::escapeString(implode(', ', $refreshGroupIDs));
|
||||
$time = self::getRefreshDateTime();
|
||||
$query = "
|
||||
UPDATE civicrm_group g
|
||||
SET g.refresh_date = $time
|
||||
WHERE g.id IN ( {$refreshGroupIDString} )
|
||||
AND g.refresh_date IS NULL
|
||||
";
|
||||
CRM_Core_DAO::executeQuery($query);
|
||||
}
|
||||
|
||||
if (empty($processGroupIDs)) {
|
||||
return TRUE;
|
||||
}
|
||||
else {
|
||||
self::add($processGroupIDs);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the smart group cache for given groups.
|
||||
*
|
||||
* @param array $groupIDs
|
||||
*/
|
||||
public static function add($groupIDs) {
|
||||
$groupIDs = (array) $groupIDs;
|
||||
|
||||
foreach ($groupIDs as $groupID) {
|
||||
// first delete the current cache
|
||||
self::clearGroupContactCache($groupID);
|
||||
$params = array(array('group', 'IN', array($groupID), 0, 0));
|
||||
// the below call updates the cache table as a byproduct of the query
|
||||
CRM_Contact_BAO_Query::apiQuery($params, array('contact_id'), NULL, NULL, 0, 0, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store values into the group contact cache.
|
||||
*
|
||||
* @todo review use of INSERT IGNORE. This function appears to be slower that inserting
|
||||
* with a left join. Also, 200 at once seems too little.
|
||||
*
|
||||
* @param array $groupID
|
||||
* @param array $values
|
||||
*/
|
||||
public static function store($groupID, &$values) {
|
||||
$processed = FALSE;
|
||||
|
||||
// sort the values so we put group IDs in front and hence optimize
|
||||
// mysql storage (or so we think) CRM-9493
|
||||
sort($values);
|
||||
|
||||
// to avoid long strings, lets do BULK_INSERT_COUNT values at a time
|
||||
while (!empty($values)) {
|
||||
$processed = TRUE;
|
||||
$input = array_splice($values, 0, CRM_Core_DAO::BULK_INSERT_COUNT);
|
||||
$str = implode(',', $input);
|
||||
$sql = "INSERT IGNORE INTO civicrm_group_contact_cache (group_id,contact_id) VALUES $str;";
|
||||
CRM_Core_DAO::executeQuery($sql);
|
||||
}
|
||||
self::updateCacheTime($groupID, $processed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the cache_date.
|
||||
*
|
||||
* @param array $groupID
|
||||
* @param bool $processed
|
||||
* Whether the cache data was recently modified.
|
||||
*/
|
||||
public static function updateCacheTime($groupID, $processed) {
|
||||
// only update cache entry if we had any values
|
||||
if ($processed) {
|
||||
// also update the group with cache date information
|
||||
$now = date('YmdHis');
|
||||
$refresh = 'null';
|
||||
}
|
||||
else {
|
||||
$now = 'null';
|
||||
$refresh = 'null';
|
||||
}
|
||||
|
||||
$groupIDs = implode(',', $groupID);
|
||||
$sql = "
|
||||
UPDATE civicrm_group
|
||||
SET cache_date = $now, refresh_date = $refresh
|
||||
WHERE id IN ( $groupIDs )
|
||||
";
|
||||
CRM_Core_DAO::executeQuery($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated function - the best function to call is
|
||||
* CRM_Contact_BAO_Contact::updateContactCache at the moment, or api job.group_cache_flush
|
||||
* to really force a flush.
|
||||
*
|
||||
* Remove this function altogether by mid 2018.
|
||||
*
|
||||
* However, if updating code outside core to use this (or any BAO function) it is recommended that
|
||||
* you add an api call to lock in into our contract. Currently there is not really a supported
|
||||
* method for non core functions.
|
||||
*/
|
||||
public static function remove() {
|
||||
Civi::log()
|
||||
->warning('Deprecated code. This function should not be called without groupIDs. Extensions can use the api job.group_cache_flush for a hard flush or add an api option for soft flush', array('civi.tag' => 'deprecated'));
|
||||
CRM_Contact_BAO_GroupContactCache::opportunisticCacheFlush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to clear group contact cache and reset the corresponding
|
||||
* group's cache and refresh date
|
||||
*
|
||||
* @param int $groupID
|
||||
*
|
||||
*/
|
||||
public static function clearGroupContactCache($groupID) {
|
||||
$transaction = new CRM_Core_Transaction();
|
||||
$query = "
|
||||
DELETE g
|
||||
FROM civicrm_group_contact_cache g
|
||||
WHERE g.group_id = %1 ";
|
||||
|
||||
$update = "
|
||||
UPDATE civicrm_group g
|
||||
SET cache_date = null, refresh_date = null
|
||||
WHERE id = %1 ";
|
||||
|
||||
$params = array(
|
||||
1 => array($groupID, 'Integer'),
|
||||
);
|
||||
|
||||
CRM_Core_DAO::executeQuery($query, $params);
|
||||
// also update the cache_date for these groups
|
||||
CRM_Core_DAO::executeQuery($update, $params);
|
||||
unset(self::$_alreadyLoaded[$groupID]);
|
||||
|
||||
$transaction->commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the smart group cache tables.
|
||||
*
|
||||
* This involves clearing out any aged entries (based on the site timeout setting) and resetting the time outs.
|
||||
*
|
||||
* This function should be called via the opportunistic or deterministic cache refresh function to make the intent
|
||||
* clear.
|
||||
*/
|
||||
protected static function flushCaches() {
|
||||
try {
|
||||
$lock = self::getLockForRefresh();
|
||||
}
|
||||
catch (CRM_Core_Exception $e) {
|
||||
// Someone else is kindly doing the refresh for us right now.
|
||||
return;
|
||||
}
|
||||
$params = array(1 => array(self::getCacheInvalidDateTime(), 'String'));
|
||||
// @todo this is consistent with previous behaviour but as the first query could take several seconds the second
|
||||
// could become inaccurate. It seems to make more sense to fetch them first & delete from an array (which would
|
||||
// also reduce joins). If we do this we should also consider how best to iterate the groups. If we do them one at
|
||||
// a time we could call a hook, allowing people to manage the frequency on their groups, or possibly custom searches
|
||||
// might do that too. However, for 2000 groups that's 2000 iterations. If we do all once we potentially create a
|
||||
// slow query. It's worth noting the speed issue generally relates to the size of the group but if one slow group
|
||||
// is in a query with 500 fast ones all 500 get locked. One approach might be to calculate group size or the
|
||||
// number of groups & then process all at once or many query runs depending on what is found. Of course those
|
||||
// preliminary queries would need speed testing.
|
||||
CRM_Core_DAO::executeQuery(
|
||||
"
|
||||
DELETE gc
|
||||
FROM civicrm_group_contact_cache gc
|
||||
INNER JOIN civicrm_group g ON g.id = gc.group_id
|
||||
WHERE g.cache_date <= %1
|
||||
",
|
||||
$params
|
||||
);
|
||||
|
||||
// Clear these out without resetting them because we are not building caches here, only clearing them,
|
||||
// so the state is 'as if they had never been built'.
|
||||
CRM_Core_DAO::executeQuery(
|
||||
"
|
||||
UPDATE civicrm_group g
|
||||
SET cache_date = NULL,
|
||||
refresh_date = NULL
|
||||
WHERE g.cache_date <= %1
|
||||
",
|
||||
$params
|
||||
);
|
||||
$lock->release();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the refresh is already initiated.
|
||||
*
|
||||
* We have 2 imperfect methods for this:
|
||||
* 1) a static variable in the function. This works fine within a request
|
||||
* 2) a mysql lock. This works fine as long as CiviMail is not running, or if mysql is version 5.7+
|
||||
*
|
||||
* Where these 2 locks fail we get 2 processes running at the same time, but we have at least minimised that.
|
||||
*
|
||||
* @return \Civi\Core\Lock\LockInterface
|
||||
* @throws \CRM_Core_Exception
|
||||
*/
|
||||
protected static function getLockForRefresh() {
|
||||
if (!isset(Civi::$statics[__CLASS__]['is_refresh_init'])) {
|
||||
Civi::$statics[__CLASS__] = array('is_refresh_init' => FALSE);
|
||||
}
|
||||
|
||||
if (Civi::$statics[__CLASS__]['is_refresh_init']) {
|
||||
throw new CRM_Core_Exception('A refresh has already run in this process');
|
||||
}
|
||||
$lock = Civi::lockManager()->acquire('data.core.group.refresh');
|
||||
if ($lock->isAcquired()) {
|
||||
Civi::$statics[__CLASS__]['is_refresh_init'] = TRUE;
|
||||
return $lock;
|
||||
}
|
||||
throw new CRM_Core_Exception('Mysql lock unavailable');
|
||||
}
|
||||
|
||||
/**
|
||||
* Do an opportunistic cache refresh if the site is configured for these.
|
||||
*
|
||||
* Sites that do not run the smart group clearing cron job should refresh the
|
||||
* caches on demand. The user session will be forced to wait so it is less
|
||||
* ideal.
|
||||
*/
|
||||
public static function opportunisticCacheFlush() {
|
||||
if (Civi::settings()->get('smart_group_cache_refresh_mode') == 'opportunistic') {
|
||||
self::flushCaches();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do a forced cache refresh.
|
||||
*
|
||||
* This function is appropriate to be called by system jobs & non-user sessions.
|
||||
*/
|
||||
public static function deterministicCacheFlush() {
|
||||
if (self::smartGroupCacheTimeout() == 0) {
|
||||
CRM_Core_DAO::executeQuery("TRUNCATE civicrm_group_contact_cache");
|
||||
CRM_Core_DAO::executeQuery("
|
||||
UPDATE civicrm_group g
|
||||
SET cache_date = null, refresh_date = null");
|
||||
}
|
||||
else {
|
||||
self::flushCaches();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove one or more contacts from the smart group cache.
|
||||
*
|
||||
* @param int|array $cid
|
||||
* @param int $groupId
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if successful.
|
||||
*/
|
||||
public static function removeContact($cid, $groupId = NULL) {
|
||||
$cids = array();
|
||||
// sanitize input
|
||||
foreach ((array) $cid as $c) {
|
||||
$cids[] = CRM_Utils_Type::escape($c, 'Integer');
|
||||
}
|
||||
if ($cids) {
|
||||
$condition = count($cids) == 1 ? "= {$cids[0]}" : "IN (" . implode(',', $cids) . ")";
|
||||
if ($groupId) {
|
||||
$condition .= " AND group_id = " . CRM_Utils_Type::escape($groupId, 'Integer');
|
||||
}
|
||||
$sql = "DELETE FROM civicrm_group_contact_cache WHERE contact_id $condition";
|
||||
CRM_Core_DAO::executeQuery($sql);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the smart group cache for a saved search.
|
||||
*
|
||||
* @param object $group
|
||||
* The smart group that needs to be loaded.
|
||||
* @param bool $force
|
||||
* Should we force a search through.
|
||||
*/
|
||||
public static function load(&$group, $force = FALSE) {
|
||||
$groupID = $group->id;
|
||||
$savedSearchID = $group->saved_search_id;
|
||||
if (array_key_exists($groupID, self::$_alreadyLoaded) && !$force) {
|
||||
return;
|
||||
}
|
||||
|
||||
// grab a lock so other processes don't compete and do the same query
|
||||
$lock = Civi::lockManager()->acquire("data.core.group.{$groupID}");
|
||||
if (!$lock->isAcquired()) {
|
||||
// this can cause inconsistent results since we don't know if the other process
|
||||
// will fill up the cache before our calling routine needs it.
|
||||
// however this routine does not return the status either, so basically
|
||||
// its a "lets return and hope for the best"
|
||||
return;
|
||||
}
|
||||
|
||||
self::$_alreadyLoaded[$groupID] = 1;
|
||||
|
||||
// we now have the lock, but some other process could have actually done the work
|
||||
// before we got here, so before we do any work, lets ensure that work needs to be
|
||||
// done
|
||||
// we allow hidden groups here since we dont know if the caller wants to evaluate an
|
||||
// hidden group
|
||||
if (!$force && !self::shouldGroupBeRefreshed($groupID, TRUE)) {
|
||||
$lock->release();
|
||||
return;
|
||||
}
|
||||
|
||||
$sql = NULL;
|
||||
$idName = 'id';
|
||||
$customClass = NULL;
|
||||
if ($savedSearchID) {
|
||||
$ssParams = CRM_Contact_BAO_SavedSearch::getSearchParams($savedSearchID);
|
||||
|
||||
// rectify params to what proximity search expects if there is a value for prox_distance
|
||||
// CRM-7021
|
||||
if (!empty($ssParams)) {
|
||||
CRM_Contact_BAO_ProximityQuery::fixInputParams($ssParams);
|
||||
}
|
||||
|
||||
$returnProperties = array();
|
||||
if (CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_SavedSearch', $savedSearchID, 'mapping_id')) {
|
||||
$fv = CRM_Contact_BAO_SavedSearch::getFormValues($savedSearchID);
|
||||
$returnProperties = CRM_Core_BAO_Mapping::returnProperties($fv);
|
||||
}
|
||||
|
||||
if (isset($ssParams['customSearchID'])) {
|
||||
// if custom search
|
||||
|
||||
// we split it up and store custom class
|
||||
// so temp tables are not destroyed if they are used
|
||||
// hence customClass is defined above at top of function
|
||||
$customClass = CRM_Contact_BAO_SearchCustom::customClass($ssParams['customSearchID'], $savedSearchID);
|
||||
$searchSQL = $customClass->contactIDs();
|
||||
$searchSQL = str_replace('ORDER BY contact_a.id ASC', '', $searchSQL);
|
||||
if (!strstr($searchSQL, 'WHERE')) {
|
||||
$searchSQL .= " WHERE ( 1 ) ";
|
||||
}
|
||||
$idName = 'contact_id';
|
||||
}
|
||||
else {
|
||||
$formValues = CRM_Contact_BAO_SavedSearch::getFormValues($savedSearchID);
|
||||
// CRM-17075 using the formValues in this way imposes extra logic and complexity.
|
||||
// we have the where_clause and where tables stored in the saved_search table
|
||||
// and should use these rather than re-processing the form criteria (which over-works
|
||||
// the link between the form layer & the query layer too).
|
||||
// It's hard to think of when you would want to use anything other than return
|
||||
// properties = array('contact_id' => 1) here as the point would appear to be to
|
||||
// generate the list of contact ids in the group.
|
||||
// @todo review this to use values in saved_search table (preferably for 4.8).
|
||||
$query
|
||||
= new CRM_Contact_BAO_Query(
|
||||
$ssParams, $returnProperties, NULL,
|
||||
FALSE, FALSE, 1,
|
||||
TRUE, TRUE,
|
||||
FALSE,
|
||||
CRM_Utils_Array::value('display_relationship_type', $formValues),
|
||||
CRM_Utils_Array::value('operator', $formValues, 'AND')
|
||||
);
|
||||
$query->_useDistinct = FALSE;
|
||||
$query->_useGroupBy = FALSE;
|
||||
$searchSQL
|
||||
= $query->searchQuery(
|
||||
0, 0, NULL,
|
||||
FALSE, FALSE,
|
||||
FALSE, TRUE,
|
||||
TRUE,
|
||||
NULL, NULL, NULL,
|
||||
TRUE
|
||||
);
|
||||
}
|
||||
$groupID = CRM_Utils_Type::escape($groupID, 'Integer');
|
||||
$sql = $searchSQL . " AND contact_a.id NOT IN (
|
||||
SELECT contact_id FROM civicrm_group_contact
|
||||
WHERE civicrm_group_contact.status = 'Removed'
|
||||
AND civicrm_group_contact.group_id = $groupID ) ";
|
||||
}
|
||||
|
||||
if ($sql) {
|
||||
$sql = preg_replace("/^\s*SELECT/", "SELECT $groupID as group_id, ", $sql);
|
||||
}
|
||||
|
||||
// lets also store the records that are explicitly added to the group
|
||||
// this allows us to skip the group contact LEFT JOIN
|
||||
$sqlB = "
|
||||
SELECT $groupID as group_id, contact_id as $idName
|
||||
FROM civicrm_group_contact
|
||||
WHERE civicrm_group_contact.status = 'Added'
|
||||
AND civicrm_group_contact.group_id = $groupID ";
|
||||
|
||||
self::clearGroupContactCache($groupID);
|
||||
|
||||
$processed = FALSE;
|
||||
$tempTable = 'civicrm_temp_group_contact_cache' . rand(0, 2000);
|
||||
foreach (array($sql, $sqlB) as $selectSql) {
|
||||
if (!$selectSql) {
|
||||
continue;
|
||||
}
|
||||
$insertSql = "CREATE TEMPORARY TABLE $tempTable ($selectSql);";
|
||||
$processed = TRUE;
|
||||
CRM_Core_DAO::executeQuery($insertSql);
|
||||
CRM_Core_DAO::executeQuery(
|
||||
"INSERT IGNORE INTO civicrm_group_contact_cache (contact_id, group_id)
|
||||
SELECT DISTINCT $idName, group_id FROM $tempTable
|
||||
");
|
||||
CRM_Core_DAO::executeQuery(" DROP TEMPORARY TABLE $tempTable");
|
||||
}
|
||||
|
||||
self::updateCacheTime(array($groupID), $processed);
|
||||
|
||||
if ($group->children) {
|
||||
|
||||
//Store a list of contacts who are removed from the parent group
|
||||
$sql = "
|
||||
SELECT contact_id
|
||||
FROM civicrm_group_contact
|
||||
WHERE civicrm_group_contact.status = 'Removed'
|
||||
AND civicrm_group_contact.group_id = $groupID ";
|
||||
$dao = CRM_Core_DAO::executeQuery($sql);
|
||||
$removed_contacts = array();
|
||||
while ($dao->fetch()) {
|
||||
$removed_contacts[] = $dao->contact_id;
|
||||
}
|
||||
|
||||
$childrenIDs = explode(',', $group->children);
|
||||
foreach ($childrenIDs as $childID) {
|
||||
$contactIDs = CRM_Contact_BAO_Group::getMember($childID, FALSE);
|
||||
//Unset each contact that is removed from the parent group
|
||||
foreach ($removed_contacts as $removed_contact) {
|
||||
unset($contactIDs[$removed_contact]);
|
||||
}
|
||||
$values = array();
|
||||
foreach ($contactIDs as $contactID => $dontCare) {
|
||||
$values[] = "({$groupID},{$contactID})";
|
||||
}
|
||||
|
||||
self::store(array($groupID), $values);
|
||||
}
|
||||
}
|
||||
|
||||
$lock->release();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the smart group cache timeout in minutes.
|
||||
*
|
||||
* This checks if a timeout has been configured. If one has then smart groups should not
|
||||
* be refreshed more frequently than the time out. If a group was recently refreshed it should not
|
||||
* refresh again within that period.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function smartGroupCacheTimeout() {
|
||||
$config = CRM_Core_Config::singleton();
|
||||
|
||||
if (
|
||||
isset($config->smartGroupCacheTimeout) &&
|
||||
is_numeric($config->smartGroupCacheTimeout)
|
||||
) {
|
||||
return $config->smartGroupCacheTimeout;
|
||||
}
|
||||
|
||||
// Default to 5 minutes.
|
||||
return 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the smart groups that this contact belongs to.
|
||||
*
|
||||
* Note that this could potentially be a super slow function since
|
||||
* it ensure that all contact groups are loaded in the cache
|
||||
*
|
||||
* @param int $contactID
|
||||
* @param bool $showHidden
|
||||
* Hidden groups are shown only if this flag is set.
|
||||
*
|
||||
* @return array
|
||||
* an array of groups that this contact belongs to
|
||||
*/
|
||||
public static function contactGroup($contactID, $showHidden = FALSE) {
|
||||
if (empty($contactID)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (is_array($contactID)) {
|
||||
$contactIDs = $contactID;
|
||||
}
|
||||
else {
|
||||
$contactIDs = array($contactID);
|
||||
}
|
||||
|
||||
self::loadAll();
|
||||
|
||||
$hiddenClause = '';
|
||||
if (!$showHidden) {
|
||||
$hiddenClause = ' AND (g.is_hidden = 0 OR g.is_hidden IS NULL) ';
|
||||
}
|
||||
|
||||
$contactIDString = CRM_Core_DAO::escapeString(implode(', ', $contactIDs));
|
||||
$sql = "
|
||||
SELECT gc.group_id, gc.contact_id, g.title, g.children, g.description
|
||||
FROM civicrm_group_contact_cache gc
|
||||
INNER JOIN civicrm_group g ON g.id = gc.group_id
|
||||
WHERE gc.contact_id IN ($contactIDString)
|
||||
$hiddenClause
|
||||
ORDER BY gc.contact_id, g.children
|
||||
";
|
||||
|
||||
$dao = CRM_Core_DAO::executeQuery($sql);
|
||||
$contactGroup = array();
|
||||
$prevContactID = NULL;
|
||||
while ($dao->fetch()) {
|
||||
if (
|
||||
$prevContactID &&
|
||||
$prevContactID != $dao->contact_id
|
||||
) {
|
||||
$contactGroup[$prevContactID]['groupTitle'] = implode(', ', $contactGroup[$prevContactID]['groupTitle']);
|
||||
}
|
||||
$prevContactID = $dao->contact_id;
|
||||
if (!array_key_exists($dao->contact_id, $contactGroup)) {
|
||||
$contactGroup[$dao->contact_id]
|
||||
= array('group' => array(), 'groupTitle' => array());
|
||||
}
|
||||
|
||||
$contactGroup[$dao->contact_id]['group'][]
|
||||
= array(
|
||||
'id' => $dao->group_id,
|
||||
'title' => $dao->title,
|
||||
'description' => $dao->description,
|
||||
'children' => $dao->children,
|
||||
);
|
||||
$contactGroup[$dao->contact_id]['groupTitle'][] = $dao->title;
|
||||
}
|
||||
|
||||
if ($prevContactID) {
|
||||
$contactGroup[$prevContactID]['groupTitle'] = implode(', ', $contactGroup[$prevContactID]['groupTitle']);
|
||||
}
|
||||
|
||||
if ((!empty($contactGroup[$contactID]) && is_numeric($contactID))) {
|
||||
return $contactGroup[$contactID];
|
||||
}
|
||||
else {
|
||||
return $contactGroup;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the datetime from which the cache should be considered invalid.
|
||||
*
|
||||
* Ie if the smartgroup cache timeout is 5 minutes ago then the cache is invalid if it was
|
||||
* refreshed 6 minutes ago, but not if it was refreshed 4 minutes ago.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getCacheInvalidDateTime() {
|
||||
return date('YmdHis', strtotime("-" . self::smartGroupCacheTimeout() . " Minutes"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the date when the cache should be refreshed from.
|
||||
*
|
||||
* Ie. now + the offset & we will delete anything prior to then.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getRefreshDateTime() {
|
||||
return date('YmdHis', strtotime("+ " . self::smartGroupCacheTimeout() . " Minutes"));
|
||||
}
|
||||
|
||||
}
|
198
sites/all/modules/civicrm/CRM/Contact/BAO/GroupNesting.php
Normal file
198
sites/all/modules/civicrm/CRM/Contact/BAO/GroupNesting.php
Normal file
|
@ -0,0 +1,198 @@
|
|||
<?php
|
||||
/*
|
||||
+--------------------------------------------------------------------+
|
||||
| CiviCRM version 4.7 |
|
||||
+--------------------------------------------------------------------+
|
||||
| Copyright U.S. PIRG Education Fund (c) 2007 |
|
||||
| Licensed to CiviCRM under the Academic Free License version 3.0. |
|
||||
+--------------------------------------------------------------------+
|
||||
| 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 U.S. PIRG 2007
|
||||
*/
|
||||
class CRM_Contact_BAO_GroupNesting extends CRM_Contact_DAO_GroupNesting {
|
||||
|
||||
/**
|
||||
* Adds a new group nesting record.
|
||||
*
|
||||
* @param int $parentID
|
||||
* Id of the group to add the child to.
|
||||
* @param int $childID
|
||||
* Id of the new child group.
|
||||
*
|
||||
* @return \CRM_Contact_DAO_GroupNesting
|
||||
*/
|
||||
public static function add($parentID, $childID) {
|
||||
$dao = new CRM_Contact_DAO_GroupNesting();
|
||||
$dao->child_group_id = $childID;
|
||||
$dao->parent_group_id = $parentID;
|
||||
if (!$dao->find(TRUE)) {
|
||||
$dao->save();
|
||||
}
|
||||
return $dao;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a child group from it's parent.
|
||||
*
|
||||
* Does not delete child group, just the association between the two
|
||||
*
|
||||
* @param int $parentID
|
||||
* The id of the group to remove the child from.
|
||||
* @param int $childID
|
||||
* The id of the child group being removed.
|
||||
*/
|
||||
public static function remove($parentID, $childID) {
|
||||
$dao = new CRM_Contact_DAO_GroupNesting();
|
||||
$dao->child_group_id = $childID;
|
||||
$dao->parent_group_id = $parentID;
|
||||
if ($dao->find(TRUE)) {
|
||||
$dao->delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the association between parent and child is present.
|
||||
*
|
||||
* @param int $parentID
|
||||
* The parent id of the association.
|
||||
*
|
||||
* @param int $childID
|
||||
* The child id of the association.
|
||||
*
|
||||
* @return bool
|
||||
* True if association is found, false otherwise.
|
||||
*/
|
||||
public static function isParentChild($parentID, $childID) {
|
||||
$dao = new CRM_Contact_DAO_GroupNesting();
|
||||
$dao->child_group_id = $childID;
|
||||
$dao->parent_group_id = $parentID;
|
||||
if ($dao->find()) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether groupId has 1 or more parent groups.
|
||||
*
|
||||
* @param int $groupId
|
||||
* The id of the group to check for parent groups.
|
||||
*
|
||||
* @return bool
|
||||
* True if 1 or more parent groups are found, false otherwise.
|
||||
*/
|
||||
public static function hasParentGroups($groupId) {
|
||||
$dao = new CRM_Contact_DAO_GroupNesting();
|
||||
$query = "SELECT parent_group_id FROM civicrm_group_nesting WHERE child_group_id = $groupId LIMIT 1";
|
||||
$dao->query($query);
|
||||
if ($dao->fetch()) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of group ids of child groups of the specified group.
|
||||
*
|
||||
* @param array $groupIds
|
||||
* An array of valid group ids (passed by reference).
|
||||
*
|
||||
* @return array
|
||||
* List of groupIds that represent the requested group and its children
|
||||
*/
|
||||
public static function getChildGroupIds($groupIds) {
|
||||
if (!is_array($groupIds)) {
|
||||
$groupIds = array($groupIds);
|
||||
}
|
||||
$dao = new CRM_Contact_DAO_GroupNesting();
|
||||
$query = "SELECT child_group_id FROM civicrm_group_nesting WHERE parent_group_id IN (" . implode(',', $groupIds) . ")";
|
||||
$dao->query($query);
|
||||
$childGroupIds = array();
|
||||
while ($dao->fetch()) {
|
||||
$childGroupIds[] = $dao->child_group_id;
|
||||
}
|
||||
return $childGroupIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of group ids of parent groups of the specified group.
|
||||
*
|
||||
* @param array $groupIds
|
||||
* An array of valid group ids (passed by reference).
|
||||
*
|
||||
* @return array
|
||||
* List of groupIds that represent the requested group and its parents
|
||||
*/
|
||||
public static function getParentGroupIds($groupIds) {
|
||||
if (!is_array($groupIds)) {
|
||||
$groupIds = array($groupIds);
|
||||
}
|
||||
$dao = new CRM_Contact_DAO_GroupNesting();
|
||||
$query = "SELECT parent_group_id FROM civicrm_group_nesting WHERE child_group_id IN (" . implode(',', $groupIds) . ")";
|
||||
$dao->query($query);
|
||||
$parentGroupIds = array();
|
||||
while ($dao->fetch()) {
|
||||
$parentGroupIds[] = $dao->parent_group_id;
|
||||
}
|
||||
return $parentGroupIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of group ids of descendent groups of the specified group.
|
||||
*
|
||||
* @param array $groupIds
|
||||
* An array of valid group ids (passed by reference).
|
||||
*
|
||||
* @param bool $includeSelf
|
||||
*
|
||||
* @return array
|
||||
* List of groupIds that represent the requested group and its descendents
|
||||
*/
|
||||
public static function getDescendentGroupIds($groupIds, $includeSelf = TRUE) {
|
||||
if (!is_array($groupIds)) {
|
||||
$groupIds = array($groupIds);
|
||||
}
|
||||
$dao = new CRM_Contact_DAO_GroupNesting();
|
||||
$query = "SELECT child_group_id, parent_group_id FROM civicrm_group_nesting WHERE parent_group_id IN (" . implode(',', $groupIds) . ")";
|
||||
$dao->query($query);
|
||||
$tmpGroupIds = array();
|
||||
$childGroupIds = array();
|
||||
if ($includeSelf) {
|
||||
$childGroupIds = $groupIds;
|
||||
}
|
||||
while ($dao->fetch()) {
|
||||
// make sure we're not following any cyclical references
|
||||
if (!array_key_exists($dao->parent_group_id, $childGroupIds) && $dao->child_group_id != $groupIds[0]) {
|
||||
$tmpGroupIds[] = $dao->child_group_id;
|
||||
}
|
||||
}
|
||||
if (!empty($tmpGroupIds)) {
|
||||
$newChildGroupIds = self::getDescendentGroupIds($tmpGroupIds);
|
||||
$childGroupIds = array_merge($childGroupIds, $newChildGroupIds);
|
||||
}
|
||||
return $childGroupIds;
|
||||
}
|
||||
|
||||
}
|
268
sites/all/modules/civicrm/CRM/Contact/BAO/GroupNestingCache.php
Normal file
268
sites/all/modules/civicrm/CRM/Contact/BAO/GroupNestingCache.php
Normal file
|
@ -0,0 +1,268 @@
|
|||
<?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_GroupNestingCache {
|
||||
|
||||
/**
|
||||
* Update cache.
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
static public function update() {
|
||||
// lets build the tree in memory first
|
||||
|
||||
$sql = "
|
||||
SELECT n.child_group_id as child ,
|
||||
n.parent_group_id as parent
|
||||
FROM civicrm_group_nesting n,
|
||||
civicrm_group gc,
|
||||
civicrm_group gp
|
||||
WHERE n.child_group_id = gc.id
|
||||
AND n.parent_group_id = gp.id
|
||||
";
|
||||
|
||||
$dao = CRM_Core_DAO::executeQuery($sql);
|
||||
|
||||
$tree = array();
|
||||
while ($dao->fetch()) {
|
||||
if (!array_key_exists($dao->child, $tree)) {
|
||||
$tree[$dao->child] = array(
|
||||
'children' => array(),
|
||||
'parents' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
if (!array_key_exists($dao->parent, $tree)) {
|
||||
$tree[$dao->parent] = array(
|
||||
'children' => array(),
|
||||
'parents' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
$tree[$dao->child]['parents'][] = $dao->parent;
|
||||
$tree[$dao->parent]['children'][] = $dao->child;
|
||||
}
|
||||
|
||||
if (self::checkCyclicGraph($tree)) {
|
||||
CRM_Core_Error::fatal(ts("We detected a cycle which we can't handle. aborting"));
|
||||
}
|
||||
|
||||
// first reset the current cache entries
|
||||
$sql = "
|
||||
UPDATE civicrm_group
|
||||
SET parents = null,
|
||||
children = null
|
||||
";
|
||||
CRM_Core_DAO::executeQuery($sql);
|
||||
|
||||
$values = array();
|
||||
foreach (array_keys($tree) as $id) {
|
||||
$parents = implode(',', $tree[$id]['parents']);
|
||||
$children = implode(',', $tree[$id]['children']);
|
||||
$parents = $parents == NULL ? 'null' : "'$parents'";
|
||||
$children = $children == NULL ? 'null' : "'$children'";
|
||||
$sql = "
|
||||
UPDATE civicrm_group
|
||||
SET parents = $parents ,
|
||||
children = $children
|
||||
WHERE id = $id
|
||||
";
|
||||
CRM_Core_DAO::executeQuery($sql);
|
||||
}
|
||||
|
||||
// this tree stuff is quite useful, so lets store it in the cache
|
||||
CRM_Core_BAO_Cache::setItem($tree, 'contact groups', 'nestable tree hierarchy');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $tree
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function checkCyclicGraph(&$tree) {
|
||||
// lets keep this simple, we should probably use a graph algorithm here at some stage
|
||||
|
||||
// foreach group that has a parent or a child, ensure that
|
||||
// the ancestors and descendants dont intersect
|
||||
foreach ($tree as $id => $dontCare) {
|
||||
if (self::isCyclic($tree, $id)) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $tree
|
||||
* @param int $id
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isCyclic(&$tree, $id) {
|
||||
$parents = $children = array();
|
||||
self::getAll($parent, $tree, $id, 'parents');
|
||||
self::getAll($child, $tree, $id, 'children');
|
||||
|
||||
$one = array_intersect($parents, $children);
|
||||
$two = array_intersect($children, $parents);
|
||||
if (!empty($one) ||
|
||||
!empty($two)
|
||||
) {
|
||||
CRM_Core_Error::debug($id, $tree);
|
||||
CRM_Core_Error::debug($id, $one);
|
||||
CRM_Core_Error::debug($id, $two);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param $groups
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getPotentialCandidates($id, &$groups) {
|
||||
$tree = CRM_Core_BAO_Cache::getItem('contact groups', 'nestable tree hierarchy');
|
||||
|
||||
if ($tree === NULL) {
|
||||
self::update();
|
||||
$tree = CRM_Core_BAO_Cache::getItem('contact groups', 'nestable tree hierarchy');
|
||||
}
|
||||
|
||||
$potential = $groups;
|
||||
|
||||
// remove all descendants
|
||||
self::invalidate($potential, $tree, $id, 'children');
|
||||
|
||||
// remove all ancestors
|
||||
self::invalidate($potential, $tree, $id, 'parents');
|
||||
|
||||
return array_keys($potential);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $potential
|
||||
* @param $tree
|
||||
* @param int $id
|
||||
* @param $token
|
||||
*/
|
||||
public static function invalidate(&$potential, &$tree, $id, $token) {
|
||||
unset($potential[$id]);
|
||||
|
||||
if (!isset($tree[$id]) ||
|
||||
empty($tree[$id][$token])
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($tree[$id][$token] as $tokenID) {
|
||||
self::invalidate($potential, $tree, $tokenID, $token);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $all
|
||||
* @param $tree
|
||||
* @param int $id
|
||||
* @param $token
|
||||
*/
|
||||
public static function getAll(&$all, &$tree, $id, $token) {
|
||||
// if seen before, dont do anything
|
||||
if (isset($all[$id])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$all[$id] = 1;
|
||||
if (!isset($tree[$id]) ||
|
||||
empty($tree[$id][$token])
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($tree[$id][$token] as $tokenID) {
|
||||
self::getAll($all, $tree, $tokenID, $token);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function json() {
|
||||
$tree = CRM_Core_BAO_Cache::getItem('contact groups', 'nestable tree hierarchy');
|
||||
|
||||
if ($tree === NULL) {
|
||||
self::update();
|
||||
$tree = CRM_Core_BAO_Cache::getItem('contact groups', 'nestable tree hierarchy');
|
||||
}
|
||||
|
||||
// get all the groups
|
||||
$groups = CRM_Core_PseudoConstant::group();
|
||||
|
||||
foreach ($groups as $id => $name) {
|
||||
$string = "id:'$id', name:'$name'";
|
||||
if (isset($tree[$id])) {
|
||||
$children = array();
|
||||
if (!empty($tree[$id]['children'])) {
|
||||
foreach ($tree[$id]['children'] as $child) {
|
||||
$children[] = "{_reference:'$child'}";
|
||||
}
|
||||
$children = implode(',', $children);
|
||||
$string .= ", children:[$children]";
|
||||
if (empty($tree[$id]['parents'])) {
|
||||
$string .= ", type:'rootGroup'";
|
||||
}
|
||||
else {
|
||||
$string .= ", type:'middleGroup'";
|
||||
}
|
||||
}
|
||||
else {
|
||||
$string .= ", type:'leafGroup'";
|
||||
}
|
||||
}
|
||||
else {
|
||||
$string .= ", children:[], type:'rootGroup'";
|
||||
}
|
||||
$values[] = "{ $string }";
|
||||
}
|
||||
|
||||
$items = implode(",\n", $values);
|
||||
$json = "{
|
||||
identifier:'id',
|
||||
label:'name',
|
||||
items:[ $items ]
|
||||
}";
|
||||
return $json;
|
||||
}
|
||||
|
||||
}
|
153
sites/all/modules/civicrm/CRM/Contact/BAO/GroupOrganization.php
Normal file
153
sites/all/modules/civicrm/CRM/Contact/BAO/GroupOrganization.php
Normal file
|
@ -0,0 +1,153 @@
|
|||
<?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_GroupOrganization extends CRM_Contact_DAO_GroupOrganization {
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an associative array and creates a groupOrganization object.
|
||||
*
|
||||
* @param array $params
|
||||
* (reference ) an assoc array of name/value pairs.
|
||||
*
|
||||
* @return CRM_Contact_DAO_GroupOrganization
|
||||
*/
|
||||
public static function add(&$params) {
|
||||
$formattedValues = array();
|
||||
self::formatValues($params, $formattedValues);
|
||||
$dataExists = self::dataExists($formattedValues);
|
||||
if (!$dataExists) {
|
||||
return NULL;
|
||||
}
|
||||
$groupOrganization = new CRM_Contact_DAO_GroupOrganization();
|
||||
$groupOrganization->copyValues($formattedValues);
|
||||
// we have ensured we have group_id & organization_id so we can do a find knowing that
|
||||
// this can only find a matching record
|
||||
$groupOrganization->find(TRUE);
|
||||
$groupOrganization->save();
|
||||
return $groupOrganization;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the params.
|
||||
*
|
||||
* @param array $params
|
||||
* (reference ) an assoc array of name/value pairs.
|
||||
* @param array $formatedValues
|
||||
* (reference ) an assoc array of name/value pairs.
|
||||
*/
|
||||
public static function formatValues(&$params, &$formatedValues) {
|
||||
if (!empty($params['group_organization'])) {
|
||||
$formatedValues['id'] = $params['group_organization'];
|
||||
}
|
||||
|
||||
if (!empty($params['group_id'])) {
|
||||
$formatedValues['group_id'] = $params['group_id'];
|
||||
}
|
||||
|
||||
if (!empty($params['organization_id'])) {
|
||||
$formatedValues['organization_id'] = $params['organization_id'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there is data to create the object.
|
||||
*
|
||||
* @param array $params
|
||||
* (reference ) an assoc array of name/value pairs.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function dataExists($params) {
|
||||
// return if no data present
|
||||
if (!empty($params['organization_id']) && !empty($params['group_id'])) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $groupID
|
||||
* @param $defaults
|
||||
*/
|
||||
public static function retrieve($groupID, &$defaults) {
|
||||
$dao = new CRM_Contact_DAO_GroupOrganization();
|
||||
$dao->group_id = $groupID;
|
||||
if ($dao->find(TRUE)) {
|
||||
$defaults['group_organization'] = $dao->id;
|
||||
$defaults['organization_id'] = $dao->organization_id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to check group organization relationship exist.
|
||||
*
|
||||
* @param int $contactID
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function hasGroupAssociated($contactID) {
|
||||
$orgID = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_GroupOrganization',
|
||||
$contactID, 'group_id', 'organization_id'
|
||||
);
|
||||
if ($orgID) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete Group Organization.
|
||||
*
|
||||
* @param int $groupOrganizationID
|
||||
* Group organization id that needs to be deleted.
|
||||
*
|
||||
* @return int|null
|
||||
* no of deleted group organization on success, false otherwise
|
||||
*/
|
||||
public static function deleteGroupOrganization($groupOrganizationID) {
|
||||
$results = NULL;
|
||||
$groupOrganization = new CRM_Contact_DAO_GroupOrganization();
|
||||
$groupOrganization->id = $groupOrganizationID;
|
||||
|
||||
$results = $groupOrganization->delete();
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
}
|
72
sites/all/modules/civicrm/CRM/Contact/BAO/Household.php
Normal file
72
sites/all/modules/civicrm/CRM/Contact/BAO/Household.php
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?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_Household extends CRM_Contact_DAO_Contact {
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the household with primary contact id.
|
||||
*
|
||||
* @param int $primaryContactId
|
||||
* Null if deleting primary contact.
|
||||
* @param int $contactId
|
||||
* Contact id.
|
||||
*
|
||||
* @return Object
|
||||
* DAO object on success
|
||||
*/
|
||||
public static function updatePrimaryContact($primaryContactId, $contactId) {
|
||||
$queryString = "UPDATE civicrm_contact
|
||||
SET primary_contact_id = ";
|
||||
|
||||
$params = array();
|
||||
if ($primaryContactId) {
|
||||
$queryString .= '%1';
|
||||
$params[1] = array($primaryContactId, 'Integer');
|
||||
}
|
||||
else {
|
||||
$queryString .= "null";
|
||||
}
|
||||
|
||||
$queryString .= " WHERE id = %2";
|
||||
$params[2] = array($contactId, 'Integer');
|
||||
|
||||
return CRM_Core_DAO::executeQuery($queryString, $params);
|
||||
}
|
||||
|
||||
}
|
421
sites/all/modules/civicrm/CRM/Contact/BAO/Individual.php
Normal file
421
sites/all/modules/civicrm/CRM/Contact/BAO/Individual.php
Normal file
|
@ -0,0 +1,421 @@
|
|||
<?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 contains functions for individual contact type.
|
||||
*/
|
||||
class CRM_Contact_BAO_Individual extends CRM_Contact_DAO_Contact {
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Function is used to format the individual contact values.
|
||||
*
|
||||
* @param array $params
|
||||
* (reference ) an assoc array of name/value pairs.
|
||||
* @param CRM $contact
|
||||
* Contact object.
|
||||
*
|
||||
* @return CRM_Contact_BAO_Contact
|
||||
*/
|
||||
public static function format(&$params, &$contact) {
|
||||
if (!self::dataExists($params)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// "null" value for example is passed by dedupe merge in order to empty.
|
||||
// Display name computation shouldn't consider such values.
|
||||
foreach (array('first_name', 'middle_name', 'last_name', 'nick_name', 'formal_title', 'birth_date', 'deceased_date') as $displayField) {
|
||||
if (CRM_Utils_Array::value($displayField, $params) == "null") {
|
||||
$params[$displayField] = '';
|
||||
}
|
||||
}
|
||||
|
||||
$sortName = $displayName = '';
|
||||
$firstName = CRM_Utils_Array::value('first_name', $params, '');
|
||||
$middleName = CRM_Utils_Array::value('middle_name', $params, '');
|
||||
$lastName = CRM_Utils_Array::value('last_name', $params, '');
|
||||
$nickName = CRM_Utils_Array::value('nick_name', $params, '');
|
||||
$prefix_id = CRM_Utils_Array::value('prefix_id', $params, '');
|
||||
$suffix_id = CRM_Utils_Array::value('suffix_id', $params, '');
|
||||
$formalTitle = CRM_Utils_Array::value('formal_title', $params, '');
|
||||
|
||||
// get prefix and suffix names
|
||||
$prefix = $suffix = NULL;
|
||||
if ($prefix_id) {
|
||||
$params['individual_prefix'] = $prefix = CRM_Core_PseudoConstant::getLabel('CRM_Contact_DAO_Contact', 'prefix_id', $prefix_id);
|
||||
}
|
||||
if ($suffix_id) {
|
||||
$params['individual_suffix'] = $suffix = CRM_Core_PseudoConstant::getLabel('CRM_Contact_DAO_Contact', 'suffix_id', $suffix_id);
|
||||
}
|
||||
|
||||
$params['is_deceased'] = CRM_Utils_Array::value('is_deceased', $params, FALSE);
|
||||
|
||||
$individual = NULL;
|
||||
if ($contact->id) {
|
||||
$individual = new CRM_Contact_BAO_Contact();
|
||||
$individual->id = $contact->id;
|
||||
if ($individual->find(TRUE)) {
|
||||
|
||||
//lets allow to update single name field though preserveDBName
|
||||
//but if db having null value and params contain value, CRM-4330.
|
||||
$useDBNames = array();
|
||||
|
||||
foreach (array('last', 'middle', 'first', 'nick') as $name) {
|
||||
$dbName = "{$name}_name";
|
||||
$value = $individual->$dbName;
|
||||
|
||||
// the db has name values
|
||||
if ($value && !empty($params['preserveDBName'])) {
|
||||
$useDBNames[] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (array('prefix', 'suffix') as $name) {
|
||||
$dbName = "{$name}_id";
|
||||
$value = $individual->$dbName;
|
||||
if ($value && !empty($params['preserveDBName'])) {
|
||||
$useDBNames[] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
if ($individual->formal_title && !empty($params['preserveDBName'])) {
|
||||
$useDBNames[] = 'formal_title';
|
||||
}
|
||||
|
||||
// CRM-4430
|
||||
//1. preserve db name if want
|
||||
//2. lets get value from param if exists.
|
||||
//3. if not in params, lets get from db.
|
||||
|
||||
foreach (array('last', 'middle', 'first', 'nick') as $name) {
|
||||
$phpName = "{$name}Name";
|
||||
$dbName = "{$name}_name";
|
||||
$value = $individual->$dbName;
|
||||
if (in_array($name, $useDBNames)) {
|
||||
$params[$dbName] = $value;
|
||||
$contact->$dbName = $value;
|
||||
$$phpName = $value;
|
||||
}
|
||||
elseif (array_key_exists($dbName, $params)) {
|
||||
$$phpName = $params[$dbName];
|
||||
}
|
||||
elseif ($value) {
|
||||
$$phpName = $value;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (array('prefix', 'suffix') as $name) {
|
||||
$dbName = "{$name}_id";
|
||||
|
||||
$value = $individual->$dbName;
|
||||
if (in_array($name, $useDBNames)) {
|
||||
$params[$dbName] = $value;
|
||||
$contact->$dbName = $value;
|
||||
if ($value) {
|
||||
$$name = CRM_Core_PseudoConstant::getLabel('CRM_Contact_DAO_Contact', $dbName, $value);
|
||||
}
|
||||
else {
|
||||
$$name = NULL;
|
||||
}
|
||||
}
|
||||
elseif (array_key_exists($dbName, $params)) {
|
||||
// CRM-5278
|
||||
if (!empty($params[$dbName])) {
|
||||
$$name = CRM_Core_PseudoConstant::getLabel('CRM_Contact_DAO_Contact', $dbName, $params[$dbName]);
|
||||
}
|
||||
}
|
||||
elseif ($value) {
|
||||
$$name = CRM_Core_PseudoConstant::getLabel('CRM_Contact_DAO_Contact', $dbName, $value);
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array('formal_title', $useDBNames)) {
|
||||
$params['formal_title'] = $individual->formal_title;
|
||||
$contact->formal_title = $individual->formal_title;
|
||||
$formalTitle = $individual->formal_title;
|
||||
}
|
||||
elseif (array_key_exists('formal_title', $params)) {
|
||||
$formalTitle = $params['formal_title'];
|
||||
}
|
||||
elseif ($individual->formal_title) {
|
||||
$formalTitle = $individual->formal_title;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//first trim before further processing.
|
||||
foreach (array('lastName', 'firstName', 'middleName') as $fld) {
|
||||
$$fld = trim($$fld);
|
||||
}
|
||||
|
||||
if ($lastName || $firstName || $middleName) {
|
||||
// make sure we have values for all the name fields.
|
||||
$formatted = $params;
|
||||
$nameParams = array(
|
||||
'first_name' => $firstName,
|
||||
'middle_name' => $middleName,
|
||||
'last_name' => $lastName,
|
||||
'nick_name' => $nickName,
|
||||
'individual_suffix' => $suffix,
|
||||
'individual_prefix' => $prefix,
|
||||
'prefix_id' => $prefix_id,
|
||||
'suffix_id' => $suffix_id,
|
||||
'formal_title' => $formalTitle,
|
||||
);
|
||||
// make sure we have all the name fields.
|
||||
foreach ($nameParams as $name => $value) {
|
||||
if (empty($formatted[$name]) && $value) {
|
||||
$formatted[$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$tokens = array();
|
||||
CRM_Utils_Hook::tokens($tokens);
|
||||
$tokenFields = array();
|
||||
foreach ($tokens as $catTokens) {
|
||||
foreach ($catTokens as $token => $label) {
|
||||
$tokenFields[] = $token;
|
||||
}
|
||||
}
|
||||
|
||||
//build the sort name.
|
||||
$format = Civi::settings()->get('sort_name_format');
|
||||
$sortName = CRM_Utils_Address::format($formatted, $format,
|
||||
FALSE, FALSE, TRUE, $tokenFields
|
||||
);
|
||||
$sortName = trim($sortName);
|
||||
|
||||
//build the display name.
|
||||
$format = Civi::settings()->get('display_name_format');
|
||||
$displayName = CRM_Utils_Address::format($formatted, $format,
|
||||
FALSE, FALSE, TRUE, $tokenFields
|
||||
);
|
||||
$displayName = trim($displayName);
|
||||
}
|
||||
|
||||
//start further check for email.
|
||||
if (empty($sortName) || empty($displayName)) {
|
||||
$email = NULL;
|
||||
if (!empty($params['email']) &&
|
||||
is_array($params['email'])
|
||||
) {
|
||||
foreach ($params['email'] as $emailBlock) {
|
||||
if (isset($emailBlock['is_primary'])) {
|
||||
$email = $emailBlock['email'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$uniqId = CRM_Utils_Array::value('user_unique_id', $params);
|
||||
if (!$email && $contact->id) {
|
||||
$email = CRM_Contact_BAO_Contact::getPrimaryEmail($contact->id);
|
||||
}
|
||||
}
|
||||
|
||||
//now set the names.
|
||||
$names = array('displayName' => 'display_name', 'sortName' => 'sort_name');
|
||||
foreach ($names as $value => $name) {
|
||||
if (empty($$value)) {
|
||||
if ($email) {
|
||||
$$value = $email;
|
||||
}
|
||||
elseif ($uniqId) {
|
||||
$$value = $uniqId;
|
||||
}
|
||||
elseif (!empty($params[$name])) {
|
||||
$$value = $params[$name];
|
||||
}
|
||||
// If we have nothing else going on set sort_name to display_name.
|
||||
elseif ($displayName) {
|
||||
$$value = $displayName;
|
||||
}
|
||||
}
|
||||
//finally if we could not pass anything lets keep db.
|
||||
if (!empty($$value)) {
|
||||
$contact->$name = $$value;
|
||||
}
|
||||
}
|
||||
|
||||
$format = CRM_Utils_Date::getDateFormat('birth');
|
||||
if ($date = CRM_Utils_Array::value('birth_date', $params)) {
|
||||
if (in_array($format, array(
|
||||
'dd-mm',
|
||||
'mm/dd',
|
||||
))) {
|
||||
$separator = '/';
|
||||
if ($format == 'dd-mm') {
|
||||
$separator = '-';
|
||||
}
|
||||
$date = $date . $separator . '1902';
|
||||
}
|
||||
elseif (in_array($format, array(
|
||||
'yy-mm',
|
||||
))) {
|
||||
$date = $date . '-01';
|
||||
}
|
||||
elseif (in_array($format, array(
|
||||
'M yy',
|
||||
))) {
|
||||
$date = $date . '-01';
|
||||
}
|
||||
elseif (in_array($format, array(
|
||||
'yy',
|
||||
))) {
|
||||
$date = $date . '-01-01';
|
||||
}
|
||||
$contact->birth_date = CRM_Utils_Date::processDate($date);
|
||||
}
|
||||
elseif ($contact->birth_date) {
|
||||
$contact->birth_date = CRM_Utils_Date::isoToMysql($contact->birth_date);
|
||||
}
|
||||
|
||||
if ($date = CRM_Utils_Array::value('deceased_date', $params)) {
|
||||
if (in_array($format, array(
|
||||
'dd-mm',
|
||||
'mm/dd',
|
||||
))) {
|
||||
$separator = '/';
|
||||
if ($format == 'dd-mm') {
|
||||
$separator = '-';
|
||||
}
|
||||
$date = $date . $separator . '1902';
|
||||
}
|
||||
elseif (in_array($format, array(
|
||||
'yy-mm',
|
||||
))) {
|
||||
$date = $date . '-01';
|
||||
}
|
||||
elseif (in_array($format, array(
|
||||
'M yy',
|
||||
))) {
|
||||
$date = $date . '-01';
|
||||
}
|
||||
elseif (in_array($format, array(
|
||||
'yy',
|
||||
))) {
|
||||
$date = $date . '-01-01';
|
||||
}
|
||||
|
||||
$contact->deceased_date = CRM_Utils_Date::processDate($date);
|
||||
}
|
||||
elseif ($contact->deceased_date) {
|
||||
$contact->deceased_date = CRM_Utils_Date::isoToMysql($contact->deceased_date);
|
||||
}
|
||||
|
||||
if ($middle_name = CRM_Utils_Array::value('middle_name', $params)) {
|
||||
$contact->middle_name = $middle_name;
|
||||
}
|
||||
|
||||
return $contact;
|
||||
}
|
||||
|
||||
/**
|
||||
* Regenerates display_name for contacts with given prefixes/suffixes.
|
||||
*
|
||||
* @param array $ids
|
||||
* The array with the prefix/suffix id governing which contacts to regenerate.
|
||||
* @param int $action
|
||||
* The action describing whether prefix/suffix was UPDATED or DELETED.
|
||||
*/
|
||||
public static function updateDisplayNames(&$ids, $action) {
|
||||
// get the proper field name (prefix_id or suffix_id) and its value
|
||||
$fieldName = '';
|
||||
foreach ($ids as $key => $value) {
|
||||
switch ($key) {
|
||||
case 'individualPrefix':
|
||||
$fieldName = 'prefix_id';
|
||||
$fieldValue = $value;
|
||||
break 2;
|
||||
|
||||
case 'individualSuffix':
|
||||
$fieldName = 'suffix_id';
|
||||
$fieldValue = $value;
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
if ($fieldName == '') {
|
||||
return;
|
||||
}
|
||||
|
||||
// query for the affected individuals
|
||||
$fieldValue = CRM_Utils_Type::escape($fieldValue, 'Integer');
|
||||
$contact = new CRM_Contact_BAO_Contact();
|
||||
$contact->$fieldName = $fieldValue;
|
||||
$contact->find();
|
||||
|
||||
// iterate through the affected individuals and rebuild their display_names
|
||||
while ($contact->fetch()) {
|
||||
$contact = new CRM_Contact_BAO_Contact();
|
||||
$contact->id = $contact->contact_id;
|
||||
if ($action == CRM_Core_Action::DELETE) {
|
||||
$contact->$fieldName = 'NULL';
|
||||
$contact->save();
|
||||
}
|
||||
$contact->display_name = $contact->displayName();
|
||||
$contact->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates display name.
|
||||
*
|
||||
* @return string
|
||||
* the constructed display name
|
||||
*/
|
||||
public function displayName() {
|
||||
$prefix = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'prefix_id');
|
||||
$suffix = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'suffix_id');
|
||||
return str_replace(' ', ' ', trim($prefix[$this->prefix_id] . ' ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name . ' ' . $suffix[$this->suffix_id]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there is data to create the object.
|
||||
*
|
||||
* @param array $params
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function dataExists($params) {
|
||||
if ($params['contact_type'] == 'Individual') {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
398
sites/all/modules/civicrm/CRM/Contact/BAO/ProximityQuery.php
Normal file
398
sites/all/modules/civicrm/CRM/Contact/BAO/ProximityQuery.php
Normal file
|
@ -0,0 +1,398 @@
|
|||
<?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_ProximityQuery {
|
||||
|
||||
/**
|
||||
* Trigonometry for calculating geographical distances.
|
||||
*
|
||||
* Modification made in: CRM-13904
|
||||
* http://en.wikipedia.org/wiki/Great-circle_distance
|
||||
* http://www.movable-type.co.uk/scripts/latlong.html
|
||||
*
|
||||
* All function arguments and return values measure distances in metres
|
||||
* and angles in degrees. The ellipsoid model is from the WGS-84 datum.
|
||||
* Ka-Ping Yee, 2003-08-11
|
||||
* earth_radius_semimajor = 6378137.0;
|
||||
* earth_flattening = 1/298.257223563;
|
||||
* earth_radius_semiminor = $earth_radius_semimajor * (1 - $earth_flattening);
|
||||
* earth_eccentricity_sq = 2*$earth_flattening - pow($earth_flattening, 2);
|
||||
* This library is an implementation of UCB CS graduate student, Ka-Ping Yee (http://www.zesty.ca).
|
||||
* This version has been taken from Drupal's location module: http://drupal.org/project/location
|
||||
*/
|
||||
|
||||
static protected $_earthFlattening;
|
||||
static protected $_earthRadiusSemiMinor;
|
||||
static protected $_earthRadiusSemiMajor;
|
||||
static protected $_earthEccentricitySQ;
|
||||
|
||||
public static function initialize() {
|
||||
static $_initialized = FALSE;
|
||||
|
||||
if (!$_initialized) {
|
||||
$_initialized = TRUE;
|
||||
|
||||
self::$_earthFlattening = 1.0 / 298.257223563;
|
||||
self::$_earthRadiusSemiMajor = 6378137.0;
|
||||
self::$_earthRadiusSemiMinor = self::$_earthRadiusSemiMajor * (1.0 - self::$_earthFlattening);
|
||||
self::$_earthEccentricitySQ = 2 * self::$_earthFlattening - pow(self::$_earthFlattening, 2);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Latitudes in all of U. S.: from -7.2 (American Samoa) to 70.5 (Alaska).
|
||||
* Latitudes in continental U. S.: from 24.6 (Florida) to 49.0 (Washington).
|
||||
* Average latitude of all U. S. zipcodes: 37.9.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Estimate the Earth's radius at a given latitude.
|
||||
* Default to an approximate average radius for the United States.
|
||||
*
|
||||
* @param float $latitude
|
||||
* @return float
|
||||
*/
|
||||
public static function earthRadius($latitude) {
|
||||
$lat = deg2rad($latitude);
|
||||
|
||||
$x = cos($lat) / self::$_earthRadiusSemiMajor;
|
||||
$y = sin($lat) / self::$_earthRadiusSemiMinor;
|
||||
return 1.0 / sqrt($x * $x + $y * $y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert longitude and latitude to earth-centered earth-fixed coordinates.
|
||||
* X axis is 0 long, 0 lat; Y axis is 90 deg E; Z axis is north pole.
|
||||
*
|
||||
* @param float $longitude
|
||||
* @param float $latitude
|
||||
* @param float|int $height
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function earthXYZ($longitude, $latitude, $height = 0) {
|
||||
$long = deg2rad($longitude);
|
||||
$lat = deg2rad($latitude);
|
||||
|
||||
$cosLong = cos($long);
|
||||
$cosLat = cos($lat);
|
||||
$sinLong = sin($long);
|
||||
$sinLat = sin($lat);
|
||||
|
||||
$radius = self::$_earthRadiusSemiMajor / sqrt(1 - self::$_earthEccentricitySQ * $sinLat * $sinLat);
|
||||
|
||||
$x = ($radius + $height) * $cosLat * $cosLong;
|
||||
$y = ($radius + $height) * $cosLat * $sinLong;
|
||||
$z = ($radius * (1 - self::$_earthEccentricitySQ) + $height) * $sinLat;
|
||||
|
||||
return array($x, $y, $z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a given angle to earth-surface distance.
|
||||
*
|
||||
* @param float $angle
|
||||
* @param float $latitude
|
||||
* @return float
|
||||
*/
|
||||
public static function earthArcLength($angle, $latitude) {
|
||||
return deg2rad($angle) * self::earthRadius($latitude);
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimate the min and max longitudes within $distance of a given location.
|
||||
*
|
||||
* @param float $longitude
|
||||
* @param float $latitude
|
||||
* @param float $distance
|
||||
* @return array
|
||||
*/
|
||||
public static function earthLongitudeRange($longitude, $latitude, $distance) {
|
||||
$long = deg2rad($longitude);
|
||||
$lat = deg2rad($latitude);
|
||||
$radius = self::earthRadius($latitude);
|
||||
|
||||
$angle = $distance / $radius;
|
||||
$diff = asin(sin($angle) / cos($lat));
|
||||
$minLong = $long - $diff;
|
||||
$maxLong = $long + $diff;
|
||||
|
||||
if ($minLong < -pi()) {
|
||||
$minLong = $minLong + pi() * 2;
|
||||
}
|
||||
|
||||
if ($maxLong > pi()) {
|
||||
$maxLong = $maxLong - pi() * 2;
|
||||
}
|
||||
|
||||
return array(
|
||||
rad2deg($minLong),
|
||||
rad2deg($maxLong),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimate the min and max latitudes within $distance of a given location.
|
||||
*
|
||||
* @param float $longitude
|
||||
* @param float $latitude
|
||||
* @param float $distance
|
||||
* @return array
|
||||
*/
|
||||
public static function earthLatitudeRange($longitude, $latitude, $distance) {
|
||||
$long = deg2rad($longitude);
|
||||
$lat = deg2rad($latitude);
|
||||
$radius = self::earthRadius($latitude);
|
||||
|
||||
$angle = $distance / $radius;
|
||||
$minLat = $lat - $angle;
|
||||
$maxLat = $lat + $angle;
|
||||
$rightangle = pi() / 2.0;
|
||||
|
||||
// wrapped around the south pole
|
||||
if ($minLat < -$rightangle) {
|
||||
$overshoot = -$minLat - $rightangle;
|
||||
$minLat = -$rightangle + $overshoot;
|
||||
if ($minLat > $maxLat) {
|
||||
$maxLat = $minLat;
|
||||
}
|
||||
$minLat = -$rightangle;
|
||||
}
|
||||
|
||||
// wrapped around the north pole
|
||||
if ($maxLat > $rightangle) {
|
||||
$overshoot = $maxLat - $rightangle;
|
||||
$maxLat = $rightangle - $overshoot;
|
||||
if ($maxLat < $minLat) {
|
||||
$minLat = $maxLat;
|
||||
}
|
||||
$maxLat = $rightangle;
|
||||
}
|
||||
|
||||
return array(
|
||||
rad2deg($minLat),
|
||||
rad2deg($maxLat),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $latitude
|
||||
* @param float $longitude
|
||||
* @param float $distance
|
||||
* @param string $tablePrefix
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function where($latitude, $longitude, $distance, $tablePrefix = 'civicrm_address') {
|
||||
self::initialize();
|
||||
|
||||
$params = array();
|
||||
$clause = array();
|
||||
|
||||
list($minLongitude, $maxLongitude) = self::earthLongitudeRange($longitude, $latitude, $distance);
|
||||
list($minLatitude, $maxLatitude) = self::earthLatitudeRange($longitude, $latitude, $distance);
|
||||
|
||||
// DONT consider NAN values (which is returned by rad2deg php function)
|
||||
// for checking BETWEEN geo_code's criteria as it throws obvious 'NAN' field not found DB: Error
|
||||
$geoCodeWhere = array();
|
||||
if (!is_nan($minLatitude)) {
|
||||
$geoCodeWhere[] = "{$tablePrefix}.geo_code_1 >= $minLatitude ";
|
||||
}
|
||||
if (!is_nan($maxLatitude)) {
|
||||
$geoCodeWhere[] = "{$tablePrefix}.geo_code_1 <= $maxLatitude ";
|
||||
}
|
||||
if (!is_nan($minLongitude)) {
|
||||
$geoCodeWhere[] = "{$tablePrefix}.geo_code_2 >= $minLongitude ";
|
||||
}
|
||||
if (!is_nan($maxLongitude)) {
|
||||
$geoCodeWhere[] = "{$tablePrefix}.geo_code_2 <= $maxLongitude ";
|
||||
}
|
||||
$geoCodeWhereClause = implode(' AND ', $geoCodeWhere);
|
||||
|
||||
$where = "
|
||||
{$geoCodeWhereClause} AND
|
||||
ACOS(
|
||||
COS(RADIANS({$tablePrefix}.geo_code_1)) *
|
||||
COS(RADIANS($latitude)) *
|
||||
COS(RADIANS({$tablePrefix}.geo_code_2) - RADIANS($longitude)) +
|
||||
SIN(RADIANS({$tablePrefix}.geo_code_1)) *
|
||||
SIN(RADIANS($latitude))
|
||||
) * 6378137 <= $distance
|
||||
";
|
||||
return $where;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process form.
|
||||
*
|
||||
* @param CRM_Contact_BAO_Query $query
|
||||
* @param array $values
|
||||
*
|
||||
* @return null
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function process(&$query, &$values) {
|
||||
list($name, $op, $distance, $grouping, $wildcard) = $values;
|
||||
|
||||
// also get values array for all address related info
|
||||
$proximityVars = array(
|
||||
'street_address' => 1,
|
||||
'city' => 1,
|
||||
'postal_code' => 1,
|
||||
'state_province_id' => 0,
|
||||
'country_id' => 0,
|
||||
'state_province' => 0,
|
||||
'country' => 0,
|
||||
'distance_unit' => 0,
|
||||
);
|
||||
|
||||
$proximityAddress = array();
|
||||
$qill = array();
|
||||
foreach ($proximityVars as $var => $recordQill) {
|
||||
$proximityValues = $query->getWhereValues("prox_{$var}", $grouping);
|
||||
if (!empty($proximityValues) &&
|
||||
!empty($proximityValues[2])
|
||||
) {
|
||||
$proximityAddress[$var] = $proximityValues[2];
|
||||
if ($recordQill) {
|
||||
$qill[] = $proximityValues[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($proximityAddress)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (isset($proximityAddress['state_province_id'])) {
|
||||
$proximityAddress['state_province'] = CRM_Core_PseudoConstant::stateProvince($proximityAddress['state_province_id']);
|
||||
$qill[] = $proximityAddress['state_province'];
|
||||
}
|
||||
|
||||
$config = CRM_Core_Config::singleton();
|
||||
if (!isset($proximityAddress['country_id'])) {
|
||||
// get it from state if state is present
|
||||
if (isset($proximityAddress['state_province_id'])) {
|
||||
$proximityAddress['country_id'] = CRM_Core_PseudoConstant::countryIDForStateID($proximityAddress['state_province_id']);
|
||||
}
|
||||
elseif (isset($config->defaultContactCountry)) {
|
||||
$proximityAddress['country_id'] = $config->defaultContactCountry;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($proximityAddress['country_id'])) {
|
||||
$proximityAddress['country'] = CRM_Core_PseudoConstant::country($proximityAddress['country_id']);
|
||||
$qill[] = $proximityAddress['country'];
|
||||
}
|
||||
|
||||
if (
|
||||
isset($proximityAddress['distance_unit']) &&
|
||||
$proximityAddress['distance_unit'] == 'miles'
|
||||
) {
|
||||
$qillUnits = " {$distance} " . ts('miles');
|
||||
$distance = $distance * 1609.344;
|
||||
}
|
||||
else {
|
||||
$qillUnits = " {$distance} " . ts('km');
|
||||
$distance = $distance * 1000.00;
|
||||
}
|
||||
|
||||
$qill = ts('Proximity search to a distance of %1 from %2',
|
||||
array(
|
||||
1 => $qillUnits,
|
||||
2 => implode(', ', $qill),
|
||||
)
|
||||
);
|
||||
|
||||
$fnName = isset($config->geocodeMethod) ? $config->geocodeMethod : NULL;
|
||||
if (empty($fnName)) {
|
||||
CRM_Core_Error::fatal(ts('Proximity searching requires you to set a valid geocoding provider'));
|
||||
}
|
||||
|
||||
$query->_tables['civicrm_address'] = $query->_whereTables['civicrm_address'] = 1;
|
||||
|
||||
require_once str_replace('_', DIRECTORY_SEPARATOR, $fnName) . '.php';
|
||||
$fnName::format($proximityAddress);
|
||||
if (
|
||||
!is_numeric(CRM_Utils_Array::value('geo_code_1', $proximityAddress)) ||
|
||||
!is_numeric(CRM_Utils_Array::value('geo_code_2', $proximityAddress))
|
||||
) {
|
||||
// we are setting the where clause to 0 here, so we wont return anything
|
||||
$qill .= ': ' . ts('We could not geocode the destination address.');
|
||||
$query->_qill[$grouping][] = $qill;
|
||||
$query->_where[$grouping][] = ' (0) ';
|
||||
return NULL;
|
||||
}
|
||||
|
||||
$query->_qill[$grouping][] = $qill;
|
||||
$query->_where[$grouping][] = self::where(
|
||||
$proximityAddress['geo_code_1'],
|
||||
$proximityAddress['geo_code_2'],
|
||||
$distance
|
||||
);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $input
|
||||
* retun void
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public static function fixInputParams(&$input) {
|
||||
foreach ($input as $param) {
|
||||
if (CRM_Utils_Array::value('0', $param) == 'prox_distance') {
|
||||
// add prox_ prefix to these
|
||||
$param_alter = array('street_address', 'city', 'postal_code', 'state_province', 'country');
|
||||
|
||||
foreach ($input as $key => $_param) {
|
||||
if (in_array($_param[0], $param_alter)) {
|
||||
$input[$key][0] = 'prox_' . $_param[0];
|
||||
|
||||
// _id suffix where needed
|
||||
if ($_param[0] == 'country' || $_param[0] == 'state_province') {
|
||||
$input[$key][0] .= '_id';
|
||||
|
||||
// flatten state_province array
|
||||
if (is_array($input[$key][2])) {
|
||||
$input[$key][2] = $input[$key][2][0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
6494
sites/all/modules/civicrm/CRM/Contact/BAO/Query.php
Normal file
6494
sites/all/modules/civicrm/CRM/Contact/BAO/Query.php
Normal file
File diff suppressed because it is too large
Load diff
167
sites/all/modules/civicrm/CRM/Contact/BAO/Query/Hook.php
Normal file
167
sites/all/modules/civicrm/CRM/Contact/BAO/Query/Hook.php
Normal file
|
@ -0,0 +1,167 @@
|
|||
<?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
|
||||
*/
|
||||
|
||||
/**
|
||||
* Delegate query functions based on hook system.
|
||||
*/
|
||||
class CRM_Contact_BAO_Query_Hook {
|
||||
|
||||
/**
|
||||
* @var array of CRM_Contact_BAO_Query_Interface objects
|
||||
*/
|
||||
protected $_queryObjects = NULL;
|
||||
|
||||
/**
|
||||
* Singleton function used to manage this object.
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public static function singleton() {
|
||||
static $singleton = NULL;
|
||||
if (!$singleton) {
|
||||
$singleton = new CRM_Contact_BAO_Query_Hook();
|
||||
}
|
||||
return $singleton;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or build the list of search objects (via hook).
|
||||
*
|
||||
* @return array
|
||||
* Array of CRM_Contact_BAO_Query_Interface objects
|
||||
*/
|
||||
public function getSearchQueryObjects() {
|
||||
if ($this->_queryObjects === NULL) {
|
||||
$this->_queryObjects = array();
|
||||
CRM_Utils_Hook::queryObjects($this->_queryObjects, 'Contact');
|
||||
}
|
||||
return $this->_queryObjects;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function &getFields() {
|
||||
$extFields = array();
|
||||
foreach (self::getSearchQueryObjects() as $obj) {
|
||||
$flds = $obj->getFields();
|
||||
$extFields = array_merge($extFields, $flds);
|
||||
}
|
||||
return $extFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $apiEntities
|
||||
* @param $fieldOptions
|
||||
*/
|
||||
public function alterSearchBuilderOptions(&$apiEntities, &$fieldOptions) {
|
||||
foreach (self::getSearchQueryObjects() as $obj) {
|
||||
$obj->alterSearchBuilderOptions($apiEntities, $fieldOptions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter search query.
|
||||
*
|
||||
* @param string $query
|
||||
* @param string $fnName
|
||||
*/
|
||||
public function alterSearchQuery(&$query, $fnName) {
|
||||
foreach (self::getSearchQueryObjects() as $obj) {
|
||||
$obj->$fnName($query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fieldName
|
||||
* @param $mode
|
||||
* @param $side
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function buildSearchfrom($fieldName, $mode, $side) {
|
||||
$from = '';
|
||||
foreach (self::getSearchQueryObjects() as $obj) {
|
||||
$from .= $obj->from($fieldName, $mode, $side);
|
||||
}
|
||||
return $from;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $tables
|
||||
*/
|
||||
public function setTableDependency(&$tables) {
|
||||
foreach (self::getSearchQueryObjects() as $obj) {
|
||||
$obj->setTableDependency($tables);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $panes
|
||||
*/
|
||||
public function registerAdvancedSearchPane(&$panes) {
|
||||
foreach (self::getSearchQueryObjects() as $obj) {
|
||||
$obj->registerAdvancedSearchPane($panes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $panes
|
||||
*/
|
||||
public function getPanesMapper(&$panes) {
|
||||
foreach (self::getSearchQueryObjects() as $obj) {
|
||||
$obj->getPanesMapper($panes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CRM_Core_Form $form
|
||||
* @param $type
|
||||
*/
|
||||
public function buildAdvancedSearchPaneForm(&$form, $type) {
|
||||
foreach (self::getSearchQueryObjects() as $obj) {
|
||||
$obj->buildAdvancedSearchPaneForm($form, $type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $paneTemplatePathArray
|
||||
* @param $type
|
||||
*/
|
||||
public function setAdvancedSearchPaneTemplatePath(&$paneTemplatePathArray, $type) {
|
||||
foreach (self::getSearchQueryObjects() as $obj) {
|
||||
$obj->setAdvancedSearchPaneTemplatePath($paneTemplatePathArray, $type);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
124
sites/all/modules/civicrm/CRM/Contact/BAO/Query/Interface.php
Normal file
124
sites/all/modules/civicrm/CRM/Contact/BAO/Query/Interface.php
Normal file
|
@ -0,0 +1,124 @@
|
|||
<?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
|
||||
*/
|
||||
|
||||
/**
|
||||
* Abstract class for search BAO query objects
|
||||
*/
|
||||
abstract class CRM_Contact_BAO_Query_Interface {
|
||||
|
||||
abstract public function &getFields();
|
||||
|
||||
/**
|
||||
* @param string $fieldName
|
||||
* @param $mode
|
||||
* @param $side
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function from($fieldName, $mode, $side);
|
||||
|
||||
/**
|
||||
* @param $query
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function select(&$query) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $query
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function where(&$query) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $tables
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function setTableDependency(&$tables) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $panes
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function registerAdvancedSearchPane(&$panes) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CRM_Core_Form $form
|
||||
* @param $type
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function buildAdvancedSearchPaneForm(&$form, $type) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $paneTemplatePathArray
|
||||
* @param $type
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function setAdvancedSearchPaneTemplatePath(&$paneTemplatePathArray, $type) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe options for available for use in the search-builder.
|
||||
*
|
||||
* The search builder determines its options by examining the API metadata corresponding to each
|
||||
* search field. This approach assumes that each field has a unique-name (ie that the field's
|
||||
* unique-name in the API matches the unique-name in the search-builder).
|
||||
*
|
||||
* @param array $apiEntities
|
||||
* List of entities whose options should be automatically scanned using API metadata.
|
||||
* @param array $fieldOptions
|
||||
* Keys are field unique-names; values describe how to lookup the options.
|
||||
* For boolean options, use value "yesno". For pseudoconstants/FKs, use the name of an API entity
|
||||
* from which the metadata of the field may be queried. (Yes - that is a mouthful.)
|
||||
* @void
|
||||
*/
|
||||
public function alterSearchBuilderOptions(&$apiEntities, &$fieldOptions) {
|
||||
}
|
||||
|
||||
}
|
2152
sites/all/modules/civicrm/CRM/Contact/BAO/Relationship.php
Normal file
2152
sites/all/modules/civicrm/CRM/Contact/BAO/Relationship.php
Normal file
File diff suppressed because it is too large
Load diff
183
sites/all/modules/civicrm/CRM/Contact/BAO/RelationshipType.php
Normal file
183
sites/all/modules/civicrm/CRM/Contact/BAO/RelationshipType.php
Normal file
|
@ -0,0 +1,183 @@
|
|||
<?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_RelationshipType extends CRM_Contact_DAO_RelationshipType {
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 flattened values.
|
||||
*
|
||||
* @return CRM_Contact_BAO_RelationshipType
|
||||
*/
|
||||
public static function retrieve(&$params, &$defaults) {
|
||||
$relationshipType = new CRM_Contact_DAO_RelationshipType();
|
||||
$relationshipType->copyValues($params);
|
||||
if ($relationshipType->find(TRUE)) {
|
||||
CRM_Core_DAO::storeValues($relationshipType, $defaults);
|
||||
$relationshipType->free();
|
||||
return $relationshipType;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the is_active flag in the db.
|
||||
*
|
||||
* @param int $id
|
||||
* Id of the database record.
|
||||
* @param bool $is_active
|
||||
* Value we want to set the is_active field.
|
||||
*
|
||||
* @return Object
|
||||
* DAO object on success, null otherwise
|
||||
*/
|
||||
public static function setIsActive($id, $is_active) {
|
||||
return CRM_Core_DAO::setFieldValue('CRM_Contact_DAO_RelationshipType', $id, 'is_active', $is_active);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the relationship type in the db.
|
||||
*
|
||||
* @param array $params
|
||||
* (reference ) an assoc array of name/value pairs.
|
||||
* @param array $ids
|
||||
* The array that holds all the db ids.
|
||||
*
|
||||
* @return CRM_Contact_DAO_RelationshipType
|
||||
*/
|
||||
public static function add(&$params, &$ids) {
|
||||
//to change name, CRM-3336
|
||||
if (empty($params['label_a_b']) && !empty($params['name_a_b'])) {
|
||||
$params['label_a_b'] = $params['name_a_b'];
|
||||
}
|
||||
|
||||
if (empty($params['label_b_a']) && !empty($params['name_b_a'])) {
|
||||
$params['label_b_a'] = $params['name_b_a'];
|
||||
}
|
||||
|
||||
// set label to name if it's not set - but *only* for
|
||||
// ADD action. CRM-3336 as part from (CRM-3522)
|
||||
if (empty($ids['relationshipType'])) {
|
||||
if (empty($params['name_a_b']) && !empty($params['label_a_b'])) {
|
||||
$params['name_a_b'] = $params['label_a_b'];
|
||||
}
|
||||
if (empty($params['name_b_a']) && !empty($params['label_b_a'])) {
|
||||
$params['name_b_a'] = $params['label_b_a'];
|
||||
}
|
||||
}
|
||||
|
||||
// action is taken depending upon the mode
|
||||
$relationshipType = new CRM_Contact_DAO_RelationshipType();
|
||||
|
||||
$relationshipType->copyValues($params);
|
||||
|
||||
// if label B to A is blank, insert the value label A to B for it
|
||||
if (!strlen(trim($strName = CRM_Utils_Array::value('name_b_a', $params)))) {
|
||||
$relationshipType->name_b_a = CRM_Utils_Array::value('name_a_b', $params);
|
||||
}
|
||||
if (!strlen(trim($strName = CRM_Utils_Array::value('label_b_a', $params)))) {
|
||||
$relationshipType->label_b_a = CRM_Utils_Array::value('label_a_b', $params);
|
||||
}
|
||||
|
||||
$relationshipType->id = CRM_Utils_Array::value('relationshipType', $ids);
|
||||
|
||||
$result = $relationshipType->save();
|
||||
|
||||
CRM_Core_PseudoConstant::relationshipType('label', TRUE);
|
||||
CRM_Core_PseudoConstant::relationshipType('name', TRUE);
|
||||
CRM_Case_XMLProcessor::flushStaticCaches();
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete Relationship Types.
|
||||
*
|
||||
* @param int $relationshipTypeId
|
||||
*
|
||||
* @throws CRM_Core_Exception
|
||||
* @return mixed
|
||||
*/
|
||||
public static function del($relationshipTypeId) {
|
||||
// make sure relationshipTypeId is an integer
|
||||
// @todo review this as most delete functions rely on the api & form layer for this
|
||||
// or do a find first & throw error if no find
|
||||
if (!CRM_Utils_Rule::positiveInteger($relationshipTypeId)) {
|
||||
throw new CRM_Core_Exception(ts('Invalid relationship type'));
|
||||
}
|
||||
|
||||
//check dependencies
|
||||
|
||||
// delete all relationships
|
||||
$relationship = new CRM_Contact_DAO_Relationship();
|
||||
$relationship->relationship_type_id = $relationshipTypeId;
|
||||
$relationship->delete();
|
||||
|
||||
// remove this relationship type from membership types
|
||||
$mems = civicrm_api3('MembershipType', 'get', array(
|
||||
'relationship_type_id' => array('LIKE' => "%{$relationshipTypeId}%"),
|
||||
'return' => array('id', 'relationship_type_id', 'relationship_direction'),
|
||||
));
|
||||
foreach ($mems['values'] as $membershipTypeId => $membershipType) {
|
||||
$pos = array_search($relationshipTypeId, $membershipType['relationship_type_id']);
|
||||
// Api call may have returned false positives but currently the relationship_type_id uses
|
||||
// nonstandard serialization which makes anything more accurate impossible.
|
||||
if ($pos !== FALSE) {
|
||||
unset($membershipType['relationship_type_id'][$pos], $membershipType['relationship_direction'][$pos]);
|
||||
civicrm_api3('MembershipType', 'create', $membershipType);
|
||||
}
|
||||
}
|
||||
|
||||
//fixed for CRM-3323
|
||||
$mappingField = new CRM_Core_DAO_MappingField();
|
||||
$mappingField->relationship_type_id = $relationshipTypeId;
|
||||
$mappingField->find();
|
||||
while ($mappingField->fetch()) {
|
||||
$mappingField->delete();
|
||||
}
|
||||
|
||||
$relationshipType = new CRM_Contact_DAO_RelationshipType();
|
||||
$relationshipType->id = $relationshipTypeId;
|
||||
return $relationshipType->delete();
|
||||
}
|
||||
|
||||
}
|
496
sites/all/modules/civicrm/CRM/Contact/BAO/SavedSearch.php
Normal file
496
sites/all/modules/civicrm/CRM/Contact/BAO/SavedSearch.php
Normal file
|
@ -0,0 +1,496 @@
|
|||
<?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
|
||||
*/
|
||||
|
||||
/**
|
||||
* Business object for Saved searches.
|
||||
*/
|
||||
class CRM_Contact_BAO_SavedSearch extends CRM_Contact_DAO_SavedSearch {
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the db for all saved searches.
|
||||
*
|
||||
* @return array
|
||||
* contains the search name as value and and id as key
|
||||
*/
|
||||
public function getAll() {
|
||||
$savedSearch = new CRM_Contact_DAO_SavedSearch();
|
||||
$savedSearch->selectAdd();
|
||||
$savedSearch->selectAdd('id, name');
|
||||
$savedSearch->find();
|
||||
while ($savedSearch->fetch()) {
|
||||
$aSavedSearch[$savedSearch->id] = $savedSearch->name;
|
||||
}
|
||||
return $aSavedSearch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve DB object based on input parameters.
|
||||
*
|
||||
* It also stores all the retrieved values in the default array.
|
||||
*
|
||||
* @param array $params
|
||||
* (reference ) an assoc array of name/value pairs.
|
||||
* @param array $defaults
|
||||
* (reference ) an assoc array to hold the flattened values.
|
||||
*
|
||||
* @return CRM_Contact_BAO_SavedSearch
|
||||
*/
|
||||
public static function retrieve(&$params, &$defaults) {
|
||||
$savedSearch = new CRM_Contact_DAO_SavedSearch();
|
||||
$savedSearch->copyValues($params);
|
||||
if ($savedSearch->find(TRUE)) {
|
||||
CRM_Core_DAO::storeValues($savedSearch, $defaults);
|
||||
return $savedSearch;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an id, extract the formValues of the saved search.
|
||||
*
|
||||
* @param int $id
|
||||
* The id of the saved search.
|
||||
*
|
||||
* @return array
|
||||
* the values of the posted saved search used as default values in various Search Form
|
||||
*/
|
||||
public static function getFormValues($id) {
|
||||
$fv = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_SavedSearch', $id, 'form_values');
|
||||
$result = NULL;
|
||||
if ($fv) {
|
||||
// make sure u unserialize - since it's stored in serialized form
|
||||
$result = unserialize($fv);
|
||||
}
|
||||
|
||||
$specialFields = array('contact_type', 'group', 'contact_tags', 'member_membership_type_id', 'member_status_id');
|
||||
foreach ($result as $element => $value) {
|
||||
if (CRM_Contact_BAO_Query::isAlreadyProcessedForQueryFormat($value)) {
|
||||
$id = CRM_Utils_Array::value(0, $value);
|
||||
$value = CRM_Utils_Array::value(2, $value);
|
||||
if (is_array($value) && in_array(key($value), CRM_Core_DAO::acceptedSQLOperators(), TRUE)) {
|
||||
$op = key($value);
|
||||
$value = CRM_Utils_Array::value($op, $value);
|
||||
if (in_array($op, array('BETWEEN', '>=', '<='))) {
|
||||
self::decodeRelativeFields($result, $id, $op, $value);
|
||||
unset($result[$element]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (strpos($id, '_date_low') !== FALSE || strpos($id, '_date_high') !== FALSE) {
|
||||
$entityName = strstr($id, '_date', TRUE);
|
||||
if (!empty($result['relative_dates']) && array_key_exists($entityName, $result['relative_dates'])) {
|
||||
$result[$id] = NULL;
|
||||
$result["{$entityName}_date_relative"] = $result['relative_dates'][$entityName];
|
||||
}
|
||||
else {
|
||||
$result[$id] = $value;
|
||||
$result["{$entityName}_date_relative"] = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$result[$id] = $value;
|
||||
}
|
||||
unset($result[$element]);
|
||||
continue;
|
||||
}
|
||||
if (!empty($value) && is_array($value)) {
|
||||
if (in_array($element, $specialFields)) {
|
||||
// Remove the element to minimise support for legacy formats. It is stored in $value
|
||||
// so will be re-set with the right name.
|
||||
unset($result[$element]);
|
||||
$element = str_replace('member_membership_type_id', 'membership_type_id', $element);
|
||||
$element = str_replace('member_status_id', 'membership_status_id', $element);
|
||||
CRM_Contact_BAO_Query::legacyConvertFormValues($element, $value);
|
||||
$result[$element] = $value;
|
||||
}
|
||||
// As per the OK (Operator as Key) value format, value array may contain key
|
||||
// as an operator so to ensure the default is always set actual value
|
||||
elseif (in_array(key($value), CRM_Core_DAO::acceptedSQLOperators(), TRUE)) {
|
||||
$result[$element] = CRM_Utils_Array::value(key($value), $value);
|
||||
if (is_string($result[$element])) {
|
||||
$result[$element] = str_replace("%", '', $result[$element]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (substr($element, 0, 7) == 'custom_' &&
|
||||
(substr($element, -5, 5) == '_from' || substr($element, -3, 3) == '_to')
|
||||
) {
|
||||
// Ensure the _relative field is set if from or to are set to ensure custom date
|
||||
// fields with 'from' or 'to' values are displayed when the are set in the smart group
|
||||
// being loaded. (CRM-17116)
|
||||
if (!isset($result[CRM_Contact_BAO_Query::getCustomFieldName($element) . '_relative'])) {
|
||||
$result[CRM_Contact_BAO_Query::getCustomFieldName($element) . '_relative'] = 0;
|
||||
}
|
||||
}
|
||||
// check to see if we need to convert the old privacy array
|
||||
// CRM-9180
|
||||
if (!empty($result['privacy'])) {
|
||||
if (is_array($result['privacy'])) {
|
||||
$result['privacy_operator'] = 'AND';
|
||||
$result['privacy_toggle'] = 1;
|
||||
if (isset($result['privacy']['do_not_toggle'])) {
|
||||
if ($result['privacy']['do_not_toggle']) {
|
||||
$result['privacy_toggle'] = 2;
|
||||
}
|
||||
unset($result['privacy']['do_not_toggle']);
|
||||
}
|
||||
|
||||
$result['privacy_options'] = array();
|
||||
foreach ($result['privacy'] as $name => $val) {
|
||||
if ($val) {
|
||||
$result['privacy_options'][] = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($result['privacy']);
|
||||
}
|
||||
}
|
||||
|
||||
if ($customSearchClass = CRM_Utils_Array::value('customSearchClass', $result)) {
|
||||
// check if there is a special function - formatSavedSearchFields defined in the custom search form
|
||||
if (method_exists($customSearchClass, 'formatSavedSearchFields')) {
|
||||
$customSearchClass::formatSavedSearchFields($result);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get search parameters.
|
||||
*
|
||||
* @param int $id
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getSearchParams($id) {
|
||||
$fv = self::getFormValues($id);
|
||||
//check if the saved search has mapping id
|
||||
if (CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_SavedSearch', $id, 'mapping_id')) {
|
||||
return CRM_Core_BAO_Mapping::formattedFields($fv);
|
||||
}
|
||||
elseif (!empty($fv['customSearchID'])) {
|
||||
return $fv;
|
||||
}
|
||||
else {
|
||||
return CRM_Contact_BAO_Query::convertFormValues($fv);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the where clause for a saved search.
|
||||
*
|
||||
* @param int $id
|
||||
* Saved search id.
|
||||
* @param array $tables
|
||||
* (reference ) add the tables that are needed for the select clause.
|
||||
* @param array $whereTables
|
||||
* (reference ) add the tables that are needed for the where clause.
|
||||
*
|
||||
* @return string
|
||||
* the where clause for this saved search
|
||||
*/
|
||||
public static function whereClause($id, &$tables, &$whereTables) {
|
||||
$params = self::getSearchParams($id);
|
||||
if ($params) {
|
||||
if (!empty($params['customSearchID'])) {
|
||||
// this has not yet been implemented
|
||||
}
|
||||
else {
|
||||
return CRM_Contact_BAO_Query::getWhereClause($params, NULL, $tables, $whereTables);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contact IDS Sql (whatever that means!).
|
||||
*
|
||||
* @param int $id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function contactIDsSQL($id) {
|
||||
$params = self::getSearchParams($id);
|
||||
if ($params && !empty($params['customSearchID'])) {
|
||||
return CRM_Contact_BAO_SearchCustom::contactIDSQL(NULL, $id);
|
||||
}
|
||||
else {
|
||||
$tables = $whereTables = array('civicrm_contact' => 1);
|
||||
$where = CRM_Contact_BAO_SavedSearch::whereClause($id, $tables, $whereTables);
|
||||
if (!$where) {
|
||||
$where = '( 1 )';
|
||||
}
|
||||
$from = CRM_Contact_BAO_Query::fromClause($whereTables);
|
||||
return "
|
||||
SELECT contact_a.id
|
||||
$from
|
||||
WHERE $where";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get from where email (whatever that means!).
|
||||
*
|
||||
* @param int $id
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function fromWhereEmail($id) {
|
||||
$params = self::getSearchParams($id);
|
||||
|
||||
if ($params) {
|
||||
if (!empty($params['customSearchID'])) {
|
||||
return CRM_Contact_BAO_SearchCustom::fromWhereEmail(NULL, $id);
|
||||
}
|
||||
else {
|
||||
$tables = $whereTables = array('civicrm_contact' => 1, 'civicrm_email' => 1);
|
||||
$where = CRM_Contact_BAO_SavedSearch::whereClause($id, $tables, $whereTables);
|
||||
$from = CRM_Contact_BAO_Query::fromClause($whereTables);
|
||||
return array($from, $where);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// fix for CRM-7240
|
||||
$from = "
|
||||
FROM civicrm_contact contact_a
|
||||
LEFT JOIN civicrm_email ON (contact_a.id = civicrm_email.contact_id AND civicrm_email.is_primary = 1)
|
||||
";
|
||||
$where = " ( 1 ) ";
|
||||
$tables['civicrm_contact'] = $whereTables['civicrm_contact'] = 1;
|
||||
$tables['civicrm_email'] = $whereTables['civicrm_email'] = 1;
|
||||
return array($from, $where);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a saved search compute the clause and the tables and store it for future use.
|
||||
*/
|
||||
public function buildClause() {
|
||||
$fv = unserialize($this->form_values);
|
||||
|
||||
if ($this->mapping_id) {
|
||||
$params = CRM_Core_BAO_Mapping::formattedFields($fv);
|
||||
}
|
||||
else {
|
||||
$params = CRM_Contact_BAO_Query::convertFormValues($fv);
|
||||
}
|
||||
|
||||
if (!empty($params)) {
|
||||
$tables = $whereTables = array();
|
||||
$this->where_clause = CRM_Contact_BAO_Query::getWhereClause($params, NULL, $tables, $whereTables);
|
||||
if (!empty($tables)) {
|
||||
$this->select_tables = serialize($tables);
|
||||
}
|
||||
if (!empty($whereTables)) {
|
||||
$this->where_tables = serialize($whereTables);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the search.
|
||||
*
|
||||
* @param bool $hook
|
||||
*/
|
||||
public function save($hook = TRUE) {
|
||||
// first build the computed fields
|
||||
$this->buildClause();
|
||||
|
||||
parent::save($hook);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an id, get the name of the saved search.
|
||||
*
|
||||
* @param int $id
|
||||
* The id of the saved search.
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
* the name of the saved search
|
||||
*/
|
||||
public static function getName($id, $value = 'name') {
|
||||
$group = new CRM_Contact_DAO_Group();
|
||||
$group->saved_search_id = $id;
|
||||
if ($group->find(TRUE)) {
|
||||
return $group->$value;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a smart group from normalised values.
|
||||
*
|
||||
* @param array $params
|
||||
*
|
||||
* @return \CRM_Contact_DAO_SavedSearch
|
||||
*/
|
||||
public static function create(&$params) {
|
||||
$savedSearch = new CRM_Contact_DAO_SavedSearch();
|
||||
if (isset($params['formValues']) &&
|
||||
!empty($params['formValues'])
|
||||
) {
|
||||
$savedSearch->form_values = serialize($params['formValues']);
|
||||
}
|
||||
else {
|
||||
$savedSearch->form_values = NULL;
|
||||
}
|
||||
|
||||
$savedSearch->is_active = CRM_Utils_Array::value('is_active', $params, 1);
|
||||
$savedSearch->mapping_id = CRM_Utils_Array::value('mapping_id', $params, 'null');
|
||||
$savedSearch->custom_search_id = CRM_Utils_Array::value('custom_search_id', $params, 'null');
|
||||
$savedSearch->id = CRM_Utils_Array::value('id', $params, NULL);
|
||||
|
||||
$savedSearch->save();
|
||||
|
||||
return $savedSearch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign test value.
|
||||
*
|
||||
* @param string $fieldName
|
||||
* @param array $fieldDef
|
||||
* @param int $counter
|
||||
*/
|
||||
protected function assignTestValue($fieldName, &$fieldDef, $counter) {
|
||||
if ($fieldName == 'form_values') {
|
||||
// A dummy value for form_values.
|
||||
$this->{$fieldName} = serialize(
|
||||
array('sort_name' => "SortName{$counter}"));
|
||||
}
|
||||
else {
|
||||
parent::assignTestValues($fieldName, $fieldDef, $counter);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store relative dates in separate array format
|
||||
*
|
||||
* @param array $queryParams
|
||||
* @param array $formValues
|
||||
*/
|
||||
public static function saveRelativeDates(&$queryParams, $formValues) {
|
||||
$relativeDates = array('relative_dates' => array());
|
||||
foreach ($formValues as $id => $value) {
|
||||
if (preg_match('/_date_relative$/', $id) && !empty($value)) {
|
||||
$entityName = strstr($id, '_date', TRUE);
|
||||
$relativeDates['relative_dates'][$entityName] = $value;
|
||||
}
|
||||
}
|
||||
// merge with original queryParams if relative date value(s) found
|
||||
if (count($relativeDates['relative_dates'])) {
|
||||
$queryParams = array_merge($queryParams, $relativeDates);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store search variables in $queryParams which were skipped while processing query params,
|
||||
* precisely at CRM_Contact_BAO_Query::fixWhereValues(...). But these variable are required in
|
||||
* building smart group criteria otherwise it will cause issues like CRM-18585,CRM-19571
|
||||
*
|
||||
* @param array $queryParams
|
||||
* @param array $formValues
|
||||
*/
|
||||
public static function saveSkippedElement(&$queryParams, $formValues) {
|
||||
// these are elements which are skipped in a smart group criteria
|
||||
$specialElements = array(
|
||||
'operator',
|
||||
'component_mode',
|
||||
'display_relationship_type',
|
||||
);
|
||||
foreach ($specialElements as $element) {
|
||||
if (!empty($formValues[$element])) {
|
||||
$queryParams[] = array($element, '=', $formValues[$element], 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode relative custom fields (converted by CRM_Contact_BAO_Query->convertCustomRelativeFields(...))
|
||||
* into desired formValues
|
||||
*
|
||||
* @param array $formValues
|
||||
* @param string $fieldName
|
||||
* @param string $op
|
||||
* @param array|string|int $value
|
||||
*/
|
||||
public static function decodeRelativeFields(&$formValues, $fieldName, $op, $value) {
|
||||
// check if its a custom date field, if yes then 'searchDate' format the value
|
||||
$isCustomDateField = CRM_Contact_BAO_Query::isCustomDateField($fieldName);
|
||||
|
||||
// select date range as default
|
||||
if ($isCustomDateField) {
|
||||
$formValues[$fieldName . '_relative'] = 0;
|
||||
}
|
||||
switch ($op) {
|
||||
case 'BETWEEN':
|
||||
if ($isCustomDateField) {
|
||||
list($formValues[$fieldName . '_from'], $formValues[$fieldName . '_from_time']) = CRM_Utils_Date::setDateDefaults($value[0], 'searchDate');
|
||||
list($formValues[$fieldName . '_to'], $formValues[$fieldName . '_to_time']) = CRM_Utils_Date::setDateDefaults($value[1], 'searchDate');
|
||||
}
|
||||
else {
|
||||
list($formValues[$fieldName . '_from'], $formValues[$fieldName . '_to']) = $value;
|
||||
}
|
||||
break;
|
||||
|
||||
case '>=':
|
||||
if ($isCustomDateField) {
|
||||
list($formValues[$fieldName . '_from'], $formValues[$fieldName . '_from_time']) = CRM_Utils_Date::setDateDefaults($value, 'searchDate');
|
||||
}
|
||||
else {
|
||||
$formValues[$fieldName . '_from'] = $value;
|
||||
}
|
||||
break;
|
||||
|
||||
case '<=':
|
||||
if ($isCustomDateField) {
|
||||
list($formValues[$fieldName . '_to'], $formValues[$fieldName . '_to_time']) = CRM_Utils_Date::setDateDefaults($value, 'searchDate');
|
||||
}
|
||||
else {
|
||||
$formValues[$fieldName . '_to'] = $value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
166
sites/all/modules/civicrm/CRM/Contact/BAO/SearchCustom.php
Normal file
166
sites/all/modules/civicrm/CRM/Contact/BAO/SearchCustom.php
Normal file
|
@ -0,0 +1,166 @@
|
|||
<?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_SearchCustom {
|
||||
|
||||
/**
|
||||
* Get details.
|
||||
*
|
||||
* @param int $csID
|
||||
* @param int $ssID
|
||||
* @param int $gID
|
||||
*
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function details($csID, $ssID = NULL, $gID = NULL) {
|
||||
$error = array(NULL, NULL, NULL);
|
||||
|
||||
if (!$csID &&
|
||||
!$ssID &&
|
||||
!$gID
|
||||
) {
|
||||
return $error;
|
||||
}
|
||||
|
||||
$customSearchID = $csID;
|
||||
$formValues = array();
|
||||
if ($ssID || $gID) {
|
||||
if ($gID) {
|
||||
$ssID = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Group', $gID, 'saved_search_id');
|
||||
}
|
||||
|
||||
$formValues = CRM_Contact_BAO_SavedSearch::getFormValues($ssID);
|
||||
$customSearchID = CRM_Utils_Array::value('customSearchID',
|
||||
$formValues
|
||||
);
|
||||
}
|
||||
|
||||
if (!$customSearchID) {
|
||||
return $error;
|
||||
}
|
||||
|
||||
// check that the csid exists in the db along with the right file
|
||||
// and implements the right interface
|
||||
$customSearchClass = civicrm_api3('OptionValue', 'getvalue', array(
|
||||
'option_group_id' => 'custom_search',
|
||||
'return' => 'name',
|
||||
'value' => $customSearchID,
|
||||
));
|
||||
|
||||
$ext = CRM_Extension_System::singleton()->getMapper();
|
||||
|
||||
if (!$ext->isExtensionKey($customSearchClass)) {
|
||||
$customSearchFile = str_replace('_',
|
||||
DIRECTORY_SEPARATOR,
|
||||
$customSearchClass
|
||||
) . '.php';
|
||||
}
|
||||
else {
|
||||
$customSearchFile = $ext->keyToPath($customSearchClass);
|
||||
$customSearchClass = $ext->keyToClass($customSearchClass);
|
||||
}
|
||||
|
||||
$error = include_once $customSearchFile;
|
||||
if ($error == FALSE) {
|
||||
CRM_Core_Error::fatal('Custom search file: ' . $customSearchFile . ' does not exist. Please verify your custom search settings in CiviCRM administrative panel.');
|
||||
}
|
||||
|
||||
return array($customSearchID, $customSearchClass, $formValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $csID
|
||||
* @param int $ssID
|
||||
*
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function customClass($csID, $ssID) {
|
||||
list($customSearchID, $customSearchClass, $formValues) = self::details($csID, $ssID);
|
||||
|
||||
if (!$customSearchID) {
|
||||
CRM_Core_Error::fatal('Could not resolve custom search ID');
|
||||
}
|
||||
|
||||
// instantiate the new class
|
||||
$customClass = new $customSearchClass($formValues);
|
||||
|
||||
return $customClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $csID
|
||||
* @param int $ssID
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function contactIDSQL($csID, $ssID) {
|
||||
$customClass = self::customClass($csID, $ssID);
|
||||
return $customClass->contactIDs();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $args
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function &buildFormValues($args) {
|
||||
$args = trim($args);
|
||||
|
||||
$values = explode("\n", $args);
|
||||
$formValues = array();
|
||||
foreach ($values as $value) {
|
||||
list($n, $v) = CRM_Utils_System::explode('=', $value, 2);
|
||||
if (!empty($v)) {
|
||||
$formValues[$n] = $v;
|
||||
}
|
||||
}
|
||||
return $formValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $csID
|
||||
* @param int $ssID
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function fromWhereEmail($csID, $ssID) {
|
||||
$customClass = self::customClass($csID, $ssID);
|
||||
|
||||
$from = $customClass->from();
|
||||
$where = $customClass->where();
|
||||
|
||||
return array($from, $where);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
<?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
|
||||
*/
|
||||
|
||||
/**
|
||||
* BAO object for crm_email table.
|
||||
*/
|
||||
class CRM_Contact_BAO_SubscriptionHistory extends CRM_Contact_DAO_SubscriptionHistory {
|
||||
/**
|
||||
* Class constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new subscription history record.
|
||||
*
|
||||
* @param array $params
|
||||
* Values for the new history record.
|
||||
*
|
||||
* @return object
|
||||
* $history The new history object
|
||||
*/
|
||||
public static function &create(&$params) {
|
||||
$history = new CRM_Contact_BAO_SubscriptionHistory();
|
||||
$history->date = date('Ymd');
|
||||
$history->copyValues($params);
|
||||
$history->save();
|
||||
return $history;
|
||||
}
|
||||
|
||||
/**
|
||||
* Erase a contact's subscription history records.
|
||||
*
|
||||
* @param int $id
|
||||
* The contact id.
|
||||
*/
|
||||
public static function deleteContact($id) {
|
||||
$history = new CRM_Contact_BAO_SubscriptionHistory();
|
||||
$history->contact_id = $id;
|
||||
$history->delete();
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue