CRM-12645 fix the code that calls the links function to not whomp it.
[civicrm-core.git] / CRM / Contact / BAO / Contact / Permission.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
7e9e8871 4 | CiviCRM version 4.7 |
6a488035 5 +--------------------------------------------------------------------+
fa938177 6 | Copyright CiviCRM LLC (c) 2004-2016 |
6a488035
TO
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
d25dd0ee 26 */
6a488035
TO
27
28/**
29 *
30 * @package CRM
fa938177 31 * @copyright CiviCRM LLC (c) 2004-2016
6a488035
TO
32 */
33class CRM_Contact_BAO_Contact_Permission {
34
dddf4bf6 35 /**
e4541c56 36 * Check which of the given contact IDs the logged in user
340be2e7 37 * has permissions for the operation type according to:
38 * - general permissions (e.g. 'edit all contacts')
39 * - deletion status (unless you have 'access deleted contacts')
40 * - ACL
41 * - permissions inherited through relationships (also second degree if enabled)
dddf4bf6 42 *
43 * @param array $contact_ids
44 * Contact IDs.
340be2e7 45 * @param int $type the type of operation (view|edit)
dddf4bf6 46 *
47 * @see CRM_Contact_BAO_Contact_Permission::allow
48 *
49 * @return array
50 * list of contact IDs the logged in user has the given permission for
51 */
52 public static function allowList($contact_ids, $type = CRM_Core_Permission::VIEW) {
53 $result_set = array();
54 if (empty($contact_ids)) {
55 // empty contact lists would cause trouble in the SQL. And be pointless.
56 return $result_set;
57 }
58
59 // make sure the the general permissions are given
e4541c56 60 if (CRM_Core_Permission::check('edit all contacts')
19f13a7c 61 || $type == CRM_Core_Permission::VIEW && CRM_Core_Permission::check('view all contacts')
dddf4bf6 62 ) {
67df1408 63
dddf4bf6 64 // if the general permission is there, all good
67df1408 65 if (CRM_Core_Permission::check('access deleted contacts')) {
e8a0f9e0 66 // if user can access deleted contacts -> fine
67df1408 67 return $contact_ids;
e4541c56 68 }
69 else {
67df1408 70 // if the user CANNOT access deleted contacts, these need to be filtered
340be2e7 71 $contact_id_list = implode(',', $contact_ids);
67df1408 72 $filter_query = "SELECT DISTINCT(id) FROM civicrm_contact WHERE id IN ($contact_id_list) AND is_deleted = 0";
73 $query = CRM_Core_DAO::executeQuery($filter_query);
74 while ($query->fetch()) {
75 $result_set[(int) $query->id] = TRUE;
76 }
77 return array_keys($result_set);
78 }
dddf4bf6 79 }
80
81 // get logged in user
340be2e7 82 $contactID = CRM_Core_Session::getLoggedInContactID();
dddf4bf6 83 if (empty($contactID)) {
67df1408 84 return array();
dddf4bf6 85 }
86
87 // make sure the cache is filled
88 self::cache($contactID, $type);
89
90 // compile query
dddf4bf6 91 $operation = ($type == CRM_Core_Permission::VIEW) ? 'View' : 'Edit';
92
93 // add clause for deleted contacts, if the user doesn't have the permission to access them
163bfad3 94 $LEFT_JOIN_DELETED = $AND_CAN_ACCESS_DELETED = '';
dddf4bf6 95 if (!CRM_Core_Permission::check('access deleted contacts')) {
67df1408 96 $LEFT_JOIN_DELETED = "LEFT JOIN civicrm_contact ON civicrm_contact.id = contact_id";
e4541c56 97 $AND_CAN_ACCESS_DELETED = "AND civicrm_contact.is_deleted = 0";
dddf4bf6 98 }
99
100 // RUN the query
340be2e7 101 $contact_id_list = implode(',', $contact_ids);
dddf4bf6 102 $query = "
103SELECT contact_id
104 FROM civicrm_acl_contact_cache
105 {$LEFT_JOIN_DELETED}
106WHERE contact_id IN ({$contact_id_list})
107 AND user_id = {$contactID}
108 AND operation = '{$operation}'
109 {$AND_CAN_ACCESS_DELETED}";
110 $result = CRM_Core_DAO::executeQuery($query);
111 while ($result->fetch()) {
67df1408 112 $result_set[(int) $result->contact_id] = TRUE;
dddf4bf6 113 }
114
e4541c56 115 // if some have been rejected, double check for permissions inherited by relationship
dddf4bf6 116 if (count($result_set) < count($contact_ids)) {
67df1408 117 $rejected_contacts = array_diff_key($contact_ids, $result_set);
98445ac5 118 // @todo consider storing these to the acl cache for next time, since we have fetched.
dddf4bf6 119 $allowed_by_relationship = self::relationshipList($rejected_contacts);
67df1408 120 foreach ($allowed_by_relationship as $contact_id) {
121 $result_set[(int) $contact_id] = TRUE;
122 }
dddf4bf6 123 }
124
67df1408 125 return array_keys($result_set);
dddf4bf6 126 }
127
6a488035 128 /**
fe482240 129 * Check if the logged in user has permissions for the operation type.
6a488035 130 *
77c5b619
TO
131 * @param int $id
132 * Contact id.
77b97be7 133 * @param int|string $type the type of operation (view|edit)
6a488035 134 *
acb1052e 135 * @return bool
a6c01b45 136 * true if the user has permission, false otherwise
6a488035 137 */
00be9182 138 public static function allow($id, $type = CRM_Core_Permission::VIEW) {
9a41e168 139 // get logged in user
340be2e7 140 $contactID = CRM_Core_Session::getLoggedInContactID();
9a41e168 141
2b8d25f0 142 // first: check if contact is trying to view own contact
e4541c56 143 if ($type == CRM_Core_Permission::VIEW && CRM_Core_Permission::check('view my contact')
144 || $type == CRM_Core_Permission::EDIT && CRM_Core_Permission::check('edit my contact')
2b8d25f0 145 ) {
146 return TRUE;
147 }
6a488035
TO
148
149 # FIXME: push this somewhere below, to not give this permission so many rights
150 $isDeleted = (bool) CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $id, 'is_deleted');
151 if (CRM_Core_Permission::check('access deleted contacts') && $isDeleted) {
152 return TRUE;
153 }
154
155 // short circuit for admin rights here so we avoid unneeeded queries
156 // some duplication of code, but we skip 3-5 queries
157 if (CRM_Core_Permission::check('edit all contacts') ||
158 ($type == CRM_ACL_API::VIEW && CRM_Core_Permission::check('view all contacts'))
159 ) {
160 return TRUE;
161 }
162
9a41e168 163 // check permission based on relationship, CRM-2963
6a488035
TO
164 if (self::relationship($id)) {
165 return TRUE;
166 }
167
9a41e168 168 // check permission based on ACL
169 $tables = array();
170 $whereTables = array();
6a488035 171
9a41e168 172 $permission = CRM_ACL_API::whereClause($type, $tables, $whereTables);
6a488035
TO
173 $from = CRM_Contact_BAO_Query::fromClause($whereTables);
174
175 $query = "
176SELECT count(DISTINCT contact_a.id)
177 $from
178WHERE contact_a.id = %1 AND $permission";
179 $params = array(1 => array($id, 'Integer'));
180
181 return (CRM_Core_DAO::singleValueQuery($query, $params) > 0) ? TRUE : FALSE;
182 }
183
184 /**
fe482240 185 * Fill the acl contact cache for this contact id if empty.
6a488035 186 *
c490a46a 187 * @param int $userID
dd244018 188 * @param int|string $type the type of operation (view|edit)
77c5b619
TO
189 * @param bool $force
190 * Should we force a recompute.
6a488035 191 */
00be9182 192 public static function cache($userID, $type = CRM_Core_Permission::VIEW, $force = FALSE) {
340be2e7 193 // FIXME: maybe find a better way of keeping track of this. @eileen pointed out
194 // that somebody might flush the cache away from under our feet,
195 // but the altenative would be a SQL call every time this is called,
196 // and a complete rebuild if the result was an empty set...
e4541c56 197 static $_processed = array(
198 CRM_Core_Permission::VIEW => array(),
199 CRM_Core_Permission::EDIT => array());
6a488035 200
c0e87307 201 if ($type == CRM_Core_Permission::VIEW) {
6a488035
TO
202 $operationClause = " operation IN ( 'Edit', 'View' ) ";
203 $operation = 'View';
204 }
205 else {
206 $operationClause = " operation = 'Edit' ";
207 $operation = 'Edit';
208 }
209
210 if (!$force) {
c0e87307 211 // skip if already calculated
212 if (!empty($_processed[$type][$userID])) {
6a488035
TO
213 return;
214 }
215
216 // run a query to see if the cache is filled
217 $sql = "
218SELECT count(id)
219FROM civicrm_acl_contact_cache
220WHERE user_id = %1
221AND $operationClause
222";
223 $params = array(1 => array($userID, 'Integer'));
224 $count = CRM_Core_DAO::singleValueQuery($sql, $params);
225 if ($count > 0) {
c0e87307 226 $_processed[$type][$userID] = 1;
6a488035
TO
227 return;
228 }
229 }
230
231 $tables = array();
232 $whereTables = array();
233
234 $permission = CRM_ACL_API::whereClause($type, $tables, $whereTables, $userID);
235
236 $from = CRM_Contact_BAO_Query::fromClause($whereTables);
237
9a41e168 238 // FIXME: don't use 'ON DUPLICATE KEY UPDATE'
6a488035
TO
239 CRM_Core_DAO::executeQuery("
240INSERT INTO civicrm_acl_contact_cache ( user_id, contact_id, operation )
241SELECT $userID as user_id, contact_a.id as contact_id, '$operation' as operation
242 $from
243WHERE $permission
244GROUP BY contact_a.id
245ON DUPLICATE KEY UPDATE
246 user_id=VALUES(user_id),
247 contact_id=VALUES(contact_id),
248 operation=VALUES(operation)"
249 );
c0e87307 250 $_processed[$type][$userID] = 1;
6a488035
TO
251 }
252
253 /**
fe482240 254 * Check if there are any contacts in cache table.
6a488035 255 *
da6b46f4 256 * @param int|string $type the type of operation (view|edit)
77c5b619
TO
257 * @param int $contactID
258 * Contact id.
6a488035 259 *
acb1052e 260 * @return bool
6a488035 261 */
acb1052e 262 public static function hasContactsInCache(
51ccfbbe 263 $type = CRM_Core_Permission::VIEW,
6a488035
TO
264 $contactID = NULL
265 ) {
266 if (!$contactID) {
340be2e7 267 $contactID = CRM_Core_Session::getLoggedInContactID();
6a488035
TO
268 }
269
270 if ($type = CRM_Core_Permission::VIEW) {
271 $operationClause = " operation IN ( 'Edit', 'View' ) ";
272 $operation = 'View';
273 }
274 else {
275 $operationClause = " operation = 'Edit' ";
276 $operation = 'Edit';
277 }
278
279 // fill cache
280 self::cache($contactID);
281
282 $sql = "
283SELECT id
284FROM civicrm_acl_contact_cache
285WHERE user_id = %1
286AND $operationClause LIMIT 1";
287
288 $params = array(1 => array($contactID, 'Integer'));
289 return (bool) CRM_Core_DAO::singleValueQuery($sql, $params);
290 }
291
86538308
EM
292 /**
293 * @param string $contactAlias
86538308
EM
294 *
295 * @return array
296 */
188fd46b 297 public static function cacheClause($contactAlias = 'contact_a') {
6a488035
TO
298 if (CRM_Core_Permission::check('view all contacts') ||
299 CRM_Core_Permission::check('edit all contacts')
300 ) {
301 if (is_array($contactAlias)) {
302 $wheres = array();
303 foreach ($contactAlias as $alias) {
304 // CRM-6181
305 $wheres[] = "$alias.is_deleted = 0";
306 }
307 return array(NULL, '(' . implode(' AND ', $wheres) . ')');
308 }
309 else {
310 // CRM-6181
311 return array(NULL, "$contactAlias.is_deleted = 0");
312 }
313 }
314
188fd46b 315 $contactID = (int) CRM_Core_Session::getLoggedInContactID();
6a488035
TO
316 self::cache($contactID);
317
318 if (is_array($contactAlias) && !empty($contactAlias)) {
319 //More than one contact alias
320 $clauses = array();
321 foreach ($contactAlias as $k => $alias) {
322 $clauses[] = " INNER JOIN civicrm_acl_contact_cache aclContactCache_{$k} ON {$alias}.id = aclContactCache_{$k}.contact_id AND aclContactCache_{$k}.user_id = $contactID ";
323 }
324
325 $fromClause = implode(" ", $clauses);
326 $whereClase = NULL;
327 }
328 else {
329 $fromClause = " INNER JOIN civicrm_acl_contact_cache aclContactCache ON {$contactAlias}.id = aclContactCache.contact_id ";
b49db103 330 $whereClase = " aclContactCache.user_id = $contactID AND $contactAlias.is_deleted = 0";
6a488035
TO
331 }
332
333 return array($fromClause, $whereClase);
334 }
335
188fd46b 336 /**
0b80f0b4 337 * Generate acl subquery that can be placed in the WHERE clause of a query or the ON clause of a JOIN
188fd46b 338 *
0b80f0b4 339 * @return string|null
188fd46b 340 */
b53bcc5d 341 public static function cacheSubquery() {
188fd46b
CW
342 if (!CRM_Core_Permission::check(array(array('view all contacts', 'edit all contacts')))) {
343 $contactID = (int) CRM_Core_Session::getLoggedInContactID();
344 self::cache($contactID);
0b80f0b4 345 return "IN (SELECT contact_id FROM civicrm_acl_contact_cache WHERE user_id = $contactID)";
188fd46b 346 }
0b80f0b4 347 return NULL;
188fd46b
CW
348 }
349
6a488035 350 /**
fe482240 351 * Get the permission base on its relationship.
6a488035 352 *
77c5b619
TO
353 * @param int $selectedContactID
354 * Contact id of selected contact.
355 * @param int $contactID
356 * Contact id of the current contact.
6a488035 357 *
a6c01b45
CW
358 * @return bool
359 * true if logged in user has permission to view
c490a46a 360 * selected contact record else false
340be2e7 361 *
362 * @deprecated should be replaced by a ::relationshipList(array($selectedContactID)) call
6a488035 363 */
00be9182 364 public static function relationship($selectedContactID, $contactID = NULL) {
6a488035 365 if (!$contactID) {
340be2e7 366 $contactID = CRM_Core_Session::getLoggedInContactID();
6a488035
TO
367 if (!$contactID) {
368 return FALSE;
369 }
370 }
a93664c8 371 if ($contactID == $selectedContactID &&
75b35151 372 (CRM_Core_Permission::check('edit my contact'))
a93664c8 373 ) {
6a488035
TO
374 return TRUE;
375 }
376 else {
340be2e7 377 // FIXME: secondDegRelPermissions should be a setting
378 $config = CRM_Core_Config::singleton();
d5f1ee75
DG
379 if ($config->secondDegRelPermissions) {
380 $query = "
381SELECT firstdeg.id
382FROM civicrm_relationship firstdeg
383LEFT JOIN civicrm_relationship seconddegaa
384 on firstdeg.contact_id_a = seconddegaa.contact_id_b
385 and seconddegaa.is_permission_b_a = 1
386 and firstdeg.is_permission_b_a = 1
387 and seconddegaa.is_active = 1
388LEFT JOIN civicrm_relationship seconddegab
389 on firstdeg.contact_id_a = seconddegab.contact_id_a
390 and seconddegab.is_permission_a_b = 1
391 and firstdeg.is_permission_b_a = 1
392 and seconddegab.is_active = 1
393LEFT JOIN civicrm_relationship seconddegba
394 on firstdeg.contact_id_b = seconddegba.contact_id_b
395 and seconddegba.is_permission_b_a = 1
396 and firstdeg.is_permission_a_b = 1
397 and seconddegba.is_active = 1
398LEFT JOIN civicrm_relationship seconddegbb
399 on firstdeg.contact_id_b = seconddegbb.contact_id_a
400 and seconddegbb.is_permission_a_b = 1
401 and firstdeg.is_permission_a_b = 1
402 and seconddegbb.is_active = 1
2efcf0c2 403WHERE
d5f1ee75 404 (
2efcf0c2 405 ( firstdeg.contact_id_a = %1 AND firstdeg.contact_id_b = %2 AND firstdeg.is_permission_a_b = 1 )
d5f1ee75 406 OR ( firstdeg.contact_id_a = %2 AND firstdeg.contact_id_b = %1 AND firstdeg.is_permission_b_a = 1 )
2efcf0c2 407 OR (
d5f1ee75 408 firstdeg.contact_id_a = %1 AND seconddegba.contact_id_a = %2
2efcf0c2 409 AND (seconddegba.contact_id_a NOT IN (SELECT id FROM civicrm_contact WHERE is_deleted = 1))
d5f1ee75 410 )
2efcf0c2 411 OR (
d5f1ee75 412 firstdeg.contact_id_a = %1 AND seconddegbb.contact_id_b = %2
2efcf0c2 413 AND (seconddegbb.contact_id_b NOT IN (SELECT id FROM civicrm_contact WHERE is_deleted = 1))
d5f1ee75 414 )
2efcf0c2 415 OR (
d5f1ee75 416 firstdeg.contact_id_b = %1 AND seconddegab.contact_id_b = %2
2efcf0c2 417 AND (seconddegab.contact_id_b NOT IN (SELECT id FROM civicrm_contact WHERE is_deleted = 1))
d5f1ee75 418 )
2efcf0c2 419 OR (
420 firstdeg.contact_id_b = %1 AND seconddegaa.contact_id_a = %2 AND (seconddegaa.contact_id_a NOT IN (SELECT id FROM civicrm_contact WHERE is_deleted = 1))
d5f1ee75 421 )
2efcf0c2 422 )
423 AND (firstdeg.contact_id_a NOT IN (SELECT id FROM civicrm_contact WHERE is_deleted = 1))
d5f1ee75
DG
424 AND (firstdeg.contact_id_b NOT IN (SELECT id FROM civicrm_contact WHERE is_deleted = 1))
425 AND ( firstdeg.is_active = 1)
426 ";
427 }
428 else {
429 $query = "
6a488035
TO
430SELECT id
431FROM civicrm_relationship
432WHERE (( contact_id_a = %1 AND contact_id_b = %2 AND is_permission_a_b = 1 ) OR
433 ( contact_id_a = %2 AND contact_id_b = %1 AND is_permission_b_a = 1 )) AND
434 (contact_id_a NOT IN (SELECT id FROM civicrm_contact WHERE is_deleted = 1)) AND
435 (contact_id_b NOT IN (SELECT id FROM civicrm_contact WHERE is_deleted = 1))
436 AND ( civicrm_relationship.is_active = 1 )
437";
d5f1ee75 438 }
51ccfbbe 439 $params = array(
353ffa53 440 1 => array($contactID, 'Integer'),
6a488035
TO
441 2 => array($selectedContactID, 'Integer'),
442 );
443 return CRM_Core_DAO::singleValueQuery($query, $params);
444 }
445 }
446
447
dddf4bf6 448 /**
449 * Filter a list of contact_ids by the ones that the
450 * currently active user as a permissioned relationship with
451 *
452 * @param array $contact_ids
453 * List of contact IDs to be filtered
454 *
455 * @return array
456 * List of contact IDs that the user has permissions for
457 */
458 public static function relationshipList($contact_ids) {
459 $result_set = array();
e4541c56 460
dddf4bf6 461 // no processing empty lists (avoid SQL errors as well)
462 if (empty($contact_ids)) {
67df1408 463 return array();
dddf4bf6 464 }
465
466 // get the currently logged in user
340be2e7 467 $contactID = CRM_Core_Session::getLoggedInContactID();
dddf4bf6 468 if (empty($contactID)) {
67df1408 469 return array();
dddf4bf6 470 }
471
472 // compile a list of queries (later to UNION)
473 $queries = array();
474 $contact_id_list = implode(',', $contact_ids);
475
67df1408 476 // add a select statement for each direection
dddf4bf6 477 $directions = array(array('from' => 'a', 'to' => 'b'), array('from' => 'b', 'to' => 'a'));
67df1408 478
479 // NORMAL/SINGLE DEGREE RELATIONSHIPS
dddf4bf6 480 foreach ($directions as $direction) {
481 $user_id_column = "contact_id_{$direction['from']}";
482 $contact_id_column = "contact_id_{$direction['to']}";
483
484 // add clause for deleted contacts, if the user doesn't have the permission to access them
67df1408 485 $LEFT_JOIN_DELETED = $AND_CAN_ACCESS_DELETED = '';
dddf4bf6 486 if (!CRM_Core_Permission::check('access deleted contacts')) {
67df1408 487 $LEFT_JOIN_DELETED = "LEFT JOIN civicrm_contact ON civicrm_contact.id = {$contact_id_column} ";
e4541c56 488 $AND_CAN_ACCESS_DELETED = "AND civicrm_contact.is_deleted = 0";
dddf4bf6 489 }
490
491 $queries[] = "
340be2e7 492SELECT civicrm_relationship.{$contact_id_column} AS contact_id
dddf4bf6 493 FROM civicrm_relationship
494 {$LEFT_JOIN_DELETED}
495 WHERE civicrm_relationship.{$user_id_column} = {$contactID}
496 AND civicrm_relationship.{$contact_id_column} IN ({$contact_id_list})
497 AND civicrm_relationship.is_active = 1
498 AND civicrm_relationship.is_permission_{$direction['from']}_{$direction['to']} = 1
499 $AND_CAN_ACCESS_DELETED";
e4541c56 500 }
dddf4bf6 501
340be2e7 502 // FIXME: secondDegRelPermissions should be a setting
503 $config = CRM_Core_Config::singleton();
dddf4bf6 504 if ($config->secondDegRelPermissions) {
340be2e7 505 foreach ($directions as $first_direction) {
506 foreach ($directions as $second_direction) {
507 // add clause for deleted contacts, if the user doesn't have the permission to access them
508 $LEFT_JOIN_DELETED = $AND_CAN_ACCESS_DELETED = '';
509 if (!CRM_Core_Permission::check('access deleted contacts')) {
510 $LEFT_JOIN_DELETED = "LEFT JOIN civicrm_contact first_degree_contact ON first_degree_contact.id = second_degree_relationship.contact_id_{$second_direction['from']}\n";
511 $LEFT_JOIN_DELETED .= "LEFT JOIN civicrm_contact second_degree_contact ON second_degree_contact.id = second_degree_relationship.contact_id_{$second_direction['to']} ";
512 $AND_CAN_ACCESS_DELETED = "AND first_degree_contact.is_deleted = 0\n";
513 $AND_CAN_ACCESS_DELETED .= "AND second_degree_contact.is_deleted = 0 ";
dddf4bf6 514 }
340be2e7 515
516 $queries[] = "
517SELECT second_degree_relationship.contact_id_{$second_direction['to']} AS contact_id
518 FROM civicrm_relationship first_degree_relationship
519 LEFT JOIN civicrm_relationship second_degree_relationship ON first_degree_relationship.contact_id_{$first_direction['to']} = second_degree_relationship.contact_id_{$first_direction['from']}
520 {$LEFT_JOIN_DELETED}
521 WHERE first_degree_relationship.contact_id_{$first_direction['from']} = {$contactID}
522 AND second_degree_relationship.contact_id_{$second_direction['to']} IN ({$contact_id_list})
523 AND first_degree_relationship.is_active = 1
524 AND first_degree_relationship.is_permission_{$first_direction['from']}_{$first_direction['to']} = 1
525 AND second_degree_relationship.is_active = 1
526 AND second_degree_relationship.is_permission_{$second_direction['from']}_{$second_direction['to']} = 1
527 $AND_CAN_ACCESS_DELETED";
dddf4bf6 528 }
529 }
530 }
531
532 // finally UNION the queries and call
340be2e7 533 $query = "(" . implode(")\nUNION DISTINCT (", $queries) . ")";
dddf4bf6 534 $result = CRM_Core_DAO::executeQuery($query);
535 while ($result->fetch()) {
67df1408 536 $result_set[(int) $result->contact_id] = TRUE;
dddf4bf6 537 }
67df1408 538 return array_keys($result_set);
dddf4bf6 539 }
540
541
86538308 542 /**
100fef9d 543 * @param int $contactID
c490a46a 544 * @param CRM_Core_Form $form
86538308
EM
545 * @param bool $redirect
546 *
547 * @return bool
548 */
00be9182 549 public static function validateOnlyChecksum($contactID, &$form, $redirect = TRUE) {
6a488035
TO
550 // check if this is of the format cs=XXX
551 if (!CRM_Contact_BAO_Contact_Utils::validChecksum($contactID,
353ffa53
TO
552 CRM_Utils_Request::retrieve('cs', 'String', $form, FALSE)
553 )
554 ) {
6a488035
TO
555 if ($redirect) {
556 // also set a message in the UF framework
557 $message = ts('You do not have permission to edit this contact record. Contact the site administrator if you need assistance.');
558 CRM_Utils_System::setUFMessage($message);
559
560 $config = CRM_Core_Config::singleton();
561 CRM_Core_Error::statusBounce($message,
562 $config->userFrameworkBaseURL
563 );
564 // does not come here, we redirect in the above statement
565 }
566 return FALSE;
567 }
568
a9a1ea2c 569 // set appropriate AUTH source
e8f14831 570 self::initChecksumAuthSrc(TRUE, $form);
a9a1ea2c 571
6a488035
TO
572 // so here the contact is posing as $contactID, lets set the logging contact ID variable
573 // CRM-8965
574 CRM_Core_DAO::executeQuery('SET @civicrm_user_id = %1',
575 array(1 => array($contactID, 'Integer'))
576 );
77b97be7 577
6a488035
TO
578 return TRUE;
579 }
580
86538308
EM
581 /**
582 * @param bool $checkSumValidationResult
583 * @param null $form
584 */
00be9182 585 public static function initChecksumAuthSrc($checkSumValidationResult = FALSE, $form = NULL) {
a9a1ea2c 586 $session = CRM_Core_Session::singleton();
e8f14831 587 if ($checkSumValidationResult && $form && CRM_Utils_Request::retrieve('cs', 'String', $form, FALSE)) {
a9a1ea2c
DS
588 // if result is already validated, and url has cs, set the flag.
589 $session->set('authSrc', CRM_Core_Permission::AUTH_SRC_CHECKSUM);
0db6c3e1 590 }
4c9b6178 591 elseif (($session->get('authSrc') & CRM_Core_Permission::AUTH_SRC_CHECKSUM) == CRM_Core_Permission::AUTH_SRC_CHECKSUM) {
77b97be7 592 // if checksum wasn't present in REQUEST OR checksum result validated as FALSE,
a9a1ea2c
DS
593 // and flag was already set exactly as AUTH_SRC_CHECKSUM, unset it.
594 $session->set('authSrc', CRM_Core_Permission::AUTH_SRC_UNKNOWN);
595 }
596 }
597
86538308 598 /**
100fef9d 599 * @param int $contactID
c490a46a 600 * @param CRM_Core_Form $form
86538308
EM
601 * @param bool $redirect
602 *
603 * @return bool
604 */
00be9182 605 public static function validateChecksumContact($contactID, &$form, $redirect = TRUE) {
6a488035
TO
606 if (!self::allow($contactID, CRM_Core_Permission::EDIT)) {
607 // check if this is of the format cs=XXX
608 return self::validateOnlyChecksum($contactID, $form, $redirect);
609 }
610 return TRUE;
611 }
96025800 612
6a488035 613}