First commit

This commit is contained in:
Theodotos Andreou 2018-01-14 13:10:16 +00:00
commit c6e2478c40
13918 changed files with 2303184 additions and 0 deletions

File diff suppressed because it is too large Load diff

View 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;
}
}

View 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']);
}
}

View 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;
}
}

File diff suppressed because it is too large Load diff

View 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')));
}
}

View file

@ -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 {
}

File diff suppressed because it is too large Load diff

View 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;
}
}

View 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"));
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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);
}
}

View 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;
}
}

View 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;
}
}
}
}

File diff suppressed because it is too large Load diff

View 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);
}
}
}

View 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) {
}
}

File diff suppressed because it is too large Load diff

View 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();
}
}

View 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;
}
}
}

View 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);
}
}

View file

@ -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();
}
}