Update copyright date for 2020
[civicrm-core.git] / CRM / ACL / BAO / ACL.php
CommitLineData
6a488035 1<?php
6a488035
TO
2/*
3 +--------------------------------------------------------------------+
fee14197 4 | CiviCRM version 5 |
6a488035 5 +--------------------------------------------------------------------+
f299f7db 6 | Copyright CiviCRM LLC (c) 2004-2020 |
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
f299f7db 31 * @copyright CiviCRM LLC (c) 2004-2020
6a488035
TO
32 */
33
34/**
35 * Access Control List
36 */
37class CRM_ACL_BAO_ACL extends CRM_ACL_DAO_ACL {
db01bf2f 38 /**
39 * @var string
40 */
683bf891
SL
41 public static $_entityTable = NULL;
42 public static $_objectTable = NULL;
43 public static $_operation = NULL;
6a488035 44
683bf891 45 public static $_fieldKeys = NULL;
6a488035 46
28518c90 47 /**
3819f101 48 * Get ACL entity table.
49 *
28518c90
EM
50 * @return array|null
51 */
00be9182 52 public static function entityTable() {
6a488035 53 if (!self::$_entityTable) {
cf0d1c08 54 self::$_entityTable = [
6a488035
TO
55 'civicrm_contact' => ts('Contact'),
56 'civicrm_acl_role' => ts('ACL Role'),
cf0d1c08 57 ];
6a488035
TO
58 }
59 return self::$_entityTable;
60 }
61
28518c90
EM
62 /**
63 * @return array|null
64 */
00be9182 65 public static function objectTable() {
6a488035 66 if (!self::$_objectTable) {
cf0d1c08 67 self::$_objectTable = [
6a488035
TO
68 'civicrm_contact' => ts('Contact'),
69 'civicrm_group' => ts('Group'),
70 'civicrm_saved_search' => ts('Contact Group'),
6a488035 71 'civicrm_admin' => ts('Import'),
cf0d1c08 72 ];
6a488035
TO
73 }
74 return self::$_objectTable;
75 }
76
28518c90
EM
77 /**
78 * @return array|null
79 */
00be9182 80 public static function operation() {
6a488035 81 if (!self::$_operation) {
cf0d1c08 82 self::$_operation = [
6a488035
TO
83 'View' => ts('View'),
84 'Edit' => ts('Edit'),
85 'Create' => ts('Create'),
86 'Delete' => ts('Delete'),
87 'Search' => ts('Search'),
88 'All' => ts('All'),
cf0d1c08 89 ];
6a488035
TO
90 }
91 return self::$_operation;
92 }
93
94 /**
95 * Construct a WHERE clause to handle permissions to $object_*
96 *
d6951adc 97 * @deprecated
98 *
b758c7d5
TO
99 * @param array $tables
100 * Any tables that may be needed in the FROM.
101 * @param string $operation
102 * The operation being attempted.
103 * @param string $object_table
104 * The table of the object in question.
105 * @param int $object_id
106 * The ID of the object in question.
107 * @param int $acl_id
108 * If it's a grant/revoke operation, the ACL ID.
109 * @param bool $acl_role
110 * For grant operations, this flag determines if we're granting a single acl (false) or an entire group.
6a488035 111 *
a6c01b45
CW
112 * @return string
113 * The WHERE clause, or 0 on failure
6a488035 114 */
e6a83034
TO
115 public static function permissionClause(
116 &$tables, $operation,
6a488035
TO
117 $object_table = NULL, $object_id = NULL,
118 $acl_id = NULL, $acl_role = FALSE
119 ) {
d6951adc 120 CRM_Core_Error::deprecatedFunctionWarning('unknown - this is really old & not used in core');
28a04ea9 121 $dao = new CRM_ACL_DAO_ACL();
6a488035 122
cf0d1c08 123 $t = [
6a488035
TO
124 'ACL' => self::getTableName(),
125 'ACLRole' => 'civicrm_acl_role',
126 'ACLEntityRole' => CRM_ACL_DAO_EntityRole::getTableName(),
127 'Contact' => CRM_Contact_DAO_Contact::getTableName(),
128 'Group' => CRM_Contact_DAO_Group::getTableName(),
129 'GroupContact' => CRM_Contact_DAO_GroupContact::getTableName(),
cf0d1c08 130 ];
6a488035 131
3bdcd4ec 132 $contact_id = CRM_Core_Session::getLoggedInContactID();
6a488035
TO
133
134 $where = " {$t['ACL']}.operation = '" . CRM_Utils_Type::escape($operation, 'String') . "'";
135
136 /* Include clause if we're looking for a specific table/id permission */
137
6a488035
TO
138 if (!empty($object_table)) {
139 $where .= " AND ( {$t['ACL']}.object_table IS null
140 OR ({$t['ACL']}.object_table = '" . CRM_Utils_Type::escape($object_table, 'String') . "'";
141 if (!empty($object_id)) {
142 $where .= " AND ({$t['ACL']}.object_id IS null
143 OR {$t['ACL']}.object_id = " . CRM_Utils_Type::escape($object_id, 'Integer') . ')';
144 }
145 $where .= '))';
146 }
147
148 /* Include clause if we're granting an ACL or ACL Role */
149
6a488035
TO
150 if (!empty($acl_id)) {
151 $where .= " AND ({$t['ACL']}.acl_id IS null
152 OR {$t['ACL']}.acl_id = " . CRM_Utils_Type::escape($acl_id, 'Integer') . ')';
153 if ($acl_role) {
154 $where .= " AND {$t['ACL']}.acl_table = '{$t['ACLRole']}'";
155 }
156 else {
157 $where .= " AND {$t['ACL']}.acl_table = '{$t['ACL']}'";
158 }
159 }
160
cf0d1c08 161 $query = [];
6a488035
TO
162
163 /* Query for permissions granted to all contacts in the domain */
164
6a488035
TO
165 $query[] = "SELECT {$t['ACL']}.*, 0 as override
166 FROM {$t['ACL']}
167
168 WHERE {$t['ACL']}.entity_table = '{$t['Domain']}'
169 AND ($where)";
170
171 /* Query for permissions granted to all contacts through an ACL group */
172
6a488035
TO
173 $query[] = "SELECT {$t['ACL']}.*, 0 as override
174 FROM {$t['ACL']}
175
176 INNER JOIN {$t['ACLEntityRole']}
177 ON ({$t['ACL']}.entity_table = '{$t['ACLRole']}'
178 AND {$t['ACL']}.entity_id =
179 {$t['ACLEntityRole']}.acl_role_id)
180
181 INNER JOIN {$t['ACLRole']}
182 ON {$t['ACL']}.entity_id =
183 {$t['ACLRole']}.id
184
185 WHERE {$t['ACLEntityRole']}.entity_table =
186 '{$t['Domain']}'
187 AND {$t['ACLRole']}.is_active = 1
188 AND ($where)";
189
190 /* Query for permissions granted directly to the contact */
191
6a488035
TO
192 $query[] = "SELECT {$t['ACL']}.*, 1 as override
193 FROM {$t['ACL']}
194
195 INNER JOIN {$t['Contact']}
196 ON ({$t['ACL']}.entity_table = '{$t['Contact']}'
197 AND {$t['ACL']}.entity_id = {$t['Contact']}.id)
198
199 WHERE {$t['Contact']}.id = $contact_id
200 AND ($where)";
201
202 /* Query for permissions granted to the contact through an ACL group */
203
6a488035
TO
204 $query[] = "SELECT {$t['ACL']}.*, 1 as override
205 FROM {$t['ACL']}
206
207 INNER JOIN {$t['ACLEntityRole']}
208 ON ({$t['ACL']}.entity_table = '{$t['ACLRole']}'
209 AND {$t['ACL']}.entity_id =
210 {$t['ACLEntityRole']}.acl_role_id)
211
212 INNER JOIN {$t['ACLRole']}
213 ON {$t['ACL']}.entity_id = {$t['ACLRole']}.id
214
215 WHERE {$t['ACLEntityRole']}.entity_table =
216 '{$t['Contact']}'
217 AND {$t['ACLRole']}.is_active = 1
218 AND {$t['ACLEntityRole']}.entity_id = $contact_id
219 AND ($where)";
220
221 /* Query for permissions granted to the contact through a group */
222
6a488035
TO
223 $query[] = "SELECT {$t['ACL']}.*, 0 as override
224 FROM {$t['ACL']}
225
226 INNER JOIN {$t['GroupContact']}
227 ON ({$t['ACL']}.entity_table = '{$t['Group']}'
228 AND {$t['ACL']}.entity_id =
229 {$t['GroupContact']}.group_id)
230
231 WHERE ($where)
232 AND {$t['GroupContact']}.contact_id = $contact_id
233 AND {$t['GroupContact']}.status = 'Added')";
234
6a488035 235 /* Query for permissions granted through an ACL group to a Contact
e70a7fc0 236 * group */
6a488035 237
6a488035
TO
238 $query[] = "SELECT {$t['ACL']}.*, 0 as override
239 FROM {$t['ACL']}
240
241 INNER JOIN {$t['ACLEntityRole']}
242 ON ({$t['ACL']}.entity_table = '{$t['ACLRole']}'
243 AND {$t['ACL']}.entity_id =
244 {$t['ACLEntityRole']}.acl_role_id)
245
246 INNER JOIN {$t['ACLRole']}
247 ON {$t['ACL']}.entity_id = {$t['ACLRole']}.id
248
249 INNER JOIN {$t['GroupContact']}
250 ON ({$t['ACLEntityRole']}.entity_table =
251 '{$t['Group']}'
252 AND {$t['ACLEntityRole']}.entity_id =
253 {$t['GroupContact']}.group_id)
254
255 WHERE ($where)
256 AND {$t['ACLRole']}.is_active = 1
257 AND {$t['GroupContact']}.contact_id = $contact_id
258 AND {$t['GroupContact']}.status = 'Added'";
259
260 $union = '(' . implode(') UNION DISTINCT (', $query) . ')';
261
262 $dao->query($union);
263
cf0d1c08 264 $allow = [0];
265 $deny = [0];
266 $override = [];
6a488035
TO
267
268 while ($dao->fetch()) {
269 /* Instant bypass for the following cases:
e70a7fc0
TO
270 * 1) the rule governs all tables
271 * 2) the rule governs all objects in the table in question
272 * 3) the rule governs the specific object we want
273 */
6a488035 274
6a488035
TO
275 if (empty($dao->object_table) ||
276 ($dao->object_table == $object_table
277 && (empty($dao->object_id)
278 || $dao->object_id == $object_id
279 )
280 )
281 ) {
282 $clause = 1;
283 }
284 else {
285 /* Otherwise try to generate a clause for this rule */
286
6a488035
TO
287 $clause = self::getClause(
288 $dao->object_table, $dao->object_id, $tables
289 );
290
291 /* If the clause returned is null, then the rule is a blanket
e70a7fc0
TO
292 * (id is null) on a table other than the one we're interested
293 * in. So skip it. */
6a488035 294
6a488035
TO
295 if (empty($clause)) {
296 continue;
297 }
298 }
299
300 /* Now we figure out if this is an allow or deny rule, and possibly
e70a7fc0 301 * a contact-level override */
6a488035 302
6a488035
TO
303 if ($dao->deny) {
304 $deny[] = $clause;
305 }
306 else {
307 $allow[] = $clause;
308
309 if ($dao->override) {
310 $override[] = $clause;
311 }
312 }
313 }
314
315 $allows = '(' . implode(' OR ', $allow) . ')';
316 $denies = '(' . implode(' OR ', $deny) . ')';
317 if (!empty($override)) {
318 $denies = '(NOT (' . implode(' OR ', $override) . ") AND $denies)";
319 }
320
321 return "($allows AND NOT $denies)";
322 }
323
324 /**
325 * Given a table and id pair, return the filter clause
326 *
b758c7d5
TO
327 * @param string $table
328 * The table owning the object.
329 * @param int $id
330 * The ID of the object.
331 * @param array $tables
332 * Tables that will be needed in the FROM.
6a488035 333 *
72b3a70c
CW
334 * @return string|null
335 * WHERE-style clause to filter results,
16b10e64 336 * or null if $table or $id is null
6a488035
TO
337 */
338 public static function getClause($table, $id, &$tables) {
353ffa53
TO
339 $table = CRM_Utils_Type::escape($table, 'String');
340 $id = CRM_Utils_Type::escape($id, 'Integer');
cf0d1c08 341 $whereTables = [];
6a488035
TO
342
343 $ssTable = CRM_Contact_BAO_SavedSearch::getTableName();
344
345 if (empty($table)) {
346 return NULL;
347 }
348 elseif ($table == $ssTable) {
349 return CRM_Contact_BAO_SavedSearch::whereClause($id, $tables, $whereTables);
350 }
351 elseif (!empty($id)) {
352 $tables[$table] = TRUE;
353 return "$table.id = $id";
354 }
355 return NULL;
356 }
357
358 /**
359 * Construct an associative array of an ACL rule's properties
360 *
b758c7d5
TO
361 * @param string $format
362 * Sprintf format for array.
363 * @param bool $hideEmpty
364 * Only return elements that have a value set.
6a488035 365 *
a6c01b45
CW
366 * @return array
367 * Assoc. array of the ACL rule's properties
6a488035 368 */
100b0ec6 369 public function toArray($format = '%s', $hideEmpty = FALSE) {
cf0d1c08 370 $result = [];
6a488035
TO
371
372 if (!self::$_fieldKeys) {
373 $fields = CRM_ACL_DAO_ACL::fields();
374 self::$_fieldKeys = array_keys($fields);
375 }
376
377 foreach (self::$_fieldKeys as $field) {
378 $result[$field] = $this->$field;
379 }
380 return $result;
381 }
382
383 /**
384 * Retrieve ACLs for a contact or group. Note that including a contact id
385 * without a group id will return those ACL rules which are granted
386 * directly to the contact, but not those granted to the contact through
387 * any/all of his group memberships.
388 *
b758c7d5
TO
389 * @param int $contact_id
390 * ID of a contact to search for.
391 * @param int $group_id
392 * ID of a group to search for.
393 * @param bool $aclRoles
394 * Should we include ACL Roles.
6a488035 395 *
a6c01b45
CW
396 * @return array
397 * Array of assoc. arrays of ACL rules
6a488035
TO
398 */
399 public static function &getACLs($contact_id = NULL, $group_id = NULL, $aclRoles = FALSE) {
cf0d1c08 400 $results = [];
6a488035
TO
401
402 if (empty($contact_id)) {
403 return $results;
404 }
405
406 $contact_id = CRM_Utils_Type::escape($contact_id, 'Integer');
407 if ($group_id) {
408 $group_id = CRM_Utils_Type::escape($group_id, 'Integer');
409 }
410
411 $rule = new CRM_ACL_BAO_ACL();
412
353ffa53 413 $acl = self::getTableName();
6a488035 414 $contact = CRM_Contact_BAO_Contact::getTableName();
353ffa53
TO
415 $c2g = CRM_Contact_BAO_GroupContact::getTableName();
416 $group = CRM_Contact_BAO_Group::getTableName();
6a488035
TO
417
418 $query = " SELECT $acl.*
419 FROM $acl ";
420
421 if (!empty($group_id)) {
422 $query .= " INNER JOIN $c2g
423 ON $acl.entity_id = $c2g.group_id
424 WHERE $acl.entity_table = '$group'
425 AND $acl.is_active = 1
426 AND $c2g.group_id = $group_id";
427
428 if (!empty($contact_id)) {
429 $query .= " AND $c2g.contact_id = $contact_id
430 AND $c2g.status = 'Added'";
431 }
432 }
433 else {
434 if (!empty($contact_id)) {
435 $query .= " WHERE $acl.entity_table = '$contact'
436 AND $acl.entity_id = $contact_id";
437 }
438 }
439
440 $rule->query($query);
441
442 while ($rule->fetch()) {
443 $results[$rule->id] = $rule->toArray();
444 }
445
446 if ($aclRoles) {
447 $results += self::getACLRoles($contact_id, $group_id);
448 }
449
450 return $results;
451 }
452
453 /**
d2e5d2ce 454 * Get all of the ACLs through ACL groups.
6a488035 455 *
b758c7d5
TO
456 * @param int $contact_id
457 * ID of a contact to search for.
458 * @param int $group_id
459 * ID of a group to search for.
6a488035 460 *
a6c01b45
CW
461 * @return array
462 * Array of assoc. arrays of ACL rules
6a488035
TO
463 */
464 public static function &getACLRoles($contact_id = NULL, $group_id = NULL) {
465 $contact_id = CRM_Utils_Type::escape($contact_id, 'Integer');
466 if ($group_id) {
467 $group_id = CRM_Utils_Type::escape($group_id, 'Integer');
468 }
469
470 $rule = new CRM_ACL_BAO_ACL();
471
353ffa53
TO
472 $acl = self::getTableName();
473 $aclRole = 'civicrm_acl_role';
6a488035 474 $aclRoleJoin = CRM_ACL_DAO_EntityRole::getTableName();
353ffa53
TO
475 $contact = CRM_Contact_BAO_Contact::getTableName();
476 $c2g = CRM_Contact_BAO_GroupContact::getTableName();
477 $group = CRM_Contact_BAO_Group::getTableName();
6a488035
TO
478
479 $query = " SELECT $acl.*
480 FROM $acl
481 INNER JOIN civicrm_option_group og
482 ON og.name = 'acl_role'
483 INNER JOIN civicrm_option_value ov
484 ON $acl.entity_table = '$aclRole'
485 AND ov.option_group_id = og.id
486 AND $acl.entity_id = ov.value";
487
488 if (!empty($group_id)) {
489 $query .= " INNER JOIN $c2g
490 ON $acl.entity_id = $c2g.group_id
491 WHERE $acl.entity_table = '$group'
492 AND $acl.is_active = 1
493 AND $c2g.group_id = $group_id";
494
495 if (!empty($contact_id)) {
496 $query .= " AND $c2g.contact_id = $contact_id
497 AND $c2g.status = 'Added'";
498 }
499 }
500 else {
501 if (!empty($contact_id)) {
502 $query .= " WHERE $acl.entity_table = '$contact'
503 AND $acl.is_active = 1
504 AND $acl.entity_id = $contact_id";
505 }
506 }
507
cf0d1c08 508 $results = [];
6a488035
TO
509
510 $rule->query($query);
511
512 while ($rule->fetch()) {
a5611c8e 513 $results[$rule->id] = $rule->toArray();
6a488035
TO
514 }
515
516 return $results;
517 }
518
519 /**
d2e5d2ce 520 * Get all ACLs granted to a contact through all group memberships.
6a488035 521 *
b758c7d5
TO
522 * @param int $contact_id
523 * The contact's ID.
524 * @param bool $aclRoles
525 * Include ACL Roles?.
6a488035 526 *
a6c01b45
CW
527 * @return array
528 * Assoc array of ACL rules
6a488035
TO
529 */
530 public static function &getGroupACLs($contact_id, $aclRoles = FALSE) {
531 $contact_id = CRM_Utils_Type::escape($contact_id, 'Integer');
532
533 $rule = new CRM_ACL_BAO_ACL();
534
353ffa53
TO
535 $acl = self::getTableName();
536 $c2g = CRM_Contact_BAO_GroupContact::getTableName();
537 $group = CRM_Contact_BAO_Group::getTableName();
cf0d1c08 538 $results = [];
6a488035
TO
539
540 if ($contact_id) {
541 $query = "
542SELECT $acl.*
543 FROM $acl
544 INNER JOIN $c2g
545 ON $acl.entity_id = $c2g.group_id
546 WHERE $acl.entity_table = '$group'
547 AND $c2g.contact_id = $contact_id
548 AND $c2g.status = 'Added'";
549
550 $rule->query($query);
551
552 while ($rule->fetch()) {
79380078 553 $results[$rule->id] = $rule->toArray();
6a488035
TO
554 }
555 }
556
557 if ($aclRoles) {
558 $results += self::getGroupACLRoles($contact_id);
559 }
560
561 return $results;
562 }
563
564 /**
d2e5d2ce 565 * Get all of the ACLs for a contact through ACL groups owned by Contact.
6a488035
TO
566 * groups.
567 *
b758c7d5
TO
568 * @param int $contact_id
569 * ID of a contact to search for.
6a488035 570 *
a6c01b45
CW
571 * @return array
572 * Array of assoc. arrays of ACL rules
6a488035
TO
573 */
574 public static function &getGroupACLRoles($contact_id) {
575 $contact_id = CRM_Utils_Type::escape($contact_id, 'Integer');
576
577 $rule = new CRM_ACL_BAO_ACL();
578
579 $acl = self::getTableName();
580 $aclRole = 'civicrm_acl_role';
581
6a488035 582 $aclER = CRM_ACL_DAO_EntityRole::getTableName();
353ffa53 583 $c2g = CRM_Contact_BAO_GroupContact::getTableName();
6a488035
TO
584 $group = CRM_Contact_BAO_Group::getTableName();
585
586 $query = " SELECT $acl.*
587 FROM $acl
588 INNER JOIN civicrm_option_group og
589 ON og.name = 'acl_role'
590 INNER JOIN civicrm_option_value ov
591 ON $acl.entity_table = '$aclRole'
592 AND ov.option_group_id = og.id
593 AND $acl.entity_id = ov.value
594 AND ov.is_active = 1
595 INNER JOIN $aclER
596 ON $aclER.acl_role_id = $acl.entity_id
597 AND $aclER.is_active = 1
598 INNER JOIN $c2g
599 ON $aclER.entity_id = $c2g.group_id
600 AND $aclER.entity_table = 'civicrm_group'
601 WHERE $acl.entity_table = '$aclRole'
602 AND $acl.is_active = 1
603 AND $c2g.contact_id = $contact_id
604 AND $c2g.status = 'Added'";
605
cf0d1c08 606 $results = [];
6a488035
TO
607
608 $rule->query($query);
609
610 while ($rule->fetch()) {
39eb89f4 611 $results[$rule->id] = $rule->toArray();
6a488035
TO
612 }
613
614 // also get all acls for "Any Role" case
615 // and authenticated User Role if present
616 $roles = "0";
617 $session = CRM_Core_Session::singleton();
618 if ($session->get('ufID') > 0) {
619 $roles .= ",2";
620 }
621
622 $query = "
623SELECT $acl.*
624 FROM $acl
625 WHERE $acl.entity_id IN ( $roles )
626 AND $acl.entity_table = 'civicrm_acl_role'
627";
628
629 $rule->query($query);
630 while ($rule->fetch()) {
631 $results[$rule->id] = $rule->toArray();
632 }
633
634 return $results;
635 }
636
637 /**
638 * Get all ACLs owned by a given contact, including domain and group-level.
639 *
b758c7d5
TO
640 * @param int $contact_id
641 * The contact ID.
6a488035 642 *
a6c01b45
CW
643 * @return array
644 * Assoc array of ACL rules
6a488035
TO
645 */
646 public static function &getAllByContact($contact_id) {
cf0d1c08 647 $result = [];
6a488035
TO
648
649 /* First, the contact-specific ACLs, including ACL Roles */
650 $result += self::getACLs($contact_id, NULL, TRUE);
651
652 /* Then, all ACLs granted through group membership */
653 $result += self::getGroupACLs($contact_id, TRUE);
654
655 return $result;
656 }
657
28518c90 658 /**
c490a46a 659 * @param array $params
28518c90
EM
660 *
661 * @return CRM_ACL_DAO_ACL
662 */
00be9182 663 public static function create(&$params) {
6a488035
TO
664 $dao = new CRM_ACL_DAO_ACL();
665 $dao->copyValues($params);
666 $dao->save();
1fe97a01 667 return $dao;
6a488035
TO
668 }
669
28518c90 670 /**
c490a46a 671 * @param array $params
28518c90
EM
672 * @param $defaults
673 */
00be9182 674 public static function retrieve(&$params, &$defaults) {
6a488035
TO
675 CRM_Core_DAO::commonRetrieve('CRM_ACL_DAO_ACL', $params, $defaults);
676 }
677
678 /**
fe482240 679 * Update the is_active flag in the db.
6a488035 680 *
b758c7d5
TO
681 * @param int $id
682 * Id of the database record.
683 * @param bool $is_active
684 * Value we want to set the is_active field.
6a488035 685 *
8a4fede3 686 * @return bool
687 * true if we found and updated the object, else false
6a488035 688 */
00be9182 689 public static function setIsActive($id, $is_active) {
9cdf85c1 690 Civi::cache('fields')->flush();
5e601882
SL
691 // reset ACL and system caches.
692 CRM_Core_BAO_Cache::resetCaches();
6a488035
TO
693
694 return CRM_Core_DAO::setFieldValue('CRM_ACL_DAO_ACL', $id, 'is_active', $is_active);
695 }
696
28518c90
EM
697 /**
698 * @param $str
100fef9d 699 * @param int $contactID
28518c90
EM
700 *
701 * @return bool
702 */
00be9182 703 public static function check($str, $contactID) {
6a488035
TO
704
705 $acls = CRM_ACL_BAO_Cache::build($contactID);
706
707 $aclKeys = array_keys($acls);
708 $aclKeys = implode(',', $aclKeys);
709
710 if (empty($aclKeys)) {
711 return FALSE;
712 }
713
6a488035
TO
714 $query = "
715SELECT count( a.id )
716 FROM civicrm_acl_cache c, civicrm_acl a
717 WHERE c.acl_id = a.id
718 AND a.is_active = 1
719 AND a.object_table = %1
720 AND a.id IN ( $aclKeys )
721";
cf0d1c08 722 $params = [1 => [$str, 'String']];
6a488035
TO
723
724 $count = CRM_Core_DAO::singleValueQuery($query, $params);
725 return ($count) ? TRUE : FALSE;
726 }
727
28518c90
EM
728 /**
729 * @param $type
730 * @param $tables
731 * @param $whereTables
100fef9d 732 * @param int $contactID
28518c90
EM
733 *
734 * @return null|string
735 */
199761b4 736 public static function whereClause($type, &$tables, &$whereTables, $contactID = NULL) {
6a488035 737 $acls = CRM_ACL_BAO_Cache::build($contactID);
6a488035
TO
738
739 $whereClause = NULL;
cf0d1c08 740 $clauses = [];
6a488035
TO
741
742 if (!empty($acls)) {
743 $aclKeys = array_keys($acls);
744 $aclKeys = implode(',', $aclKeys);
745
746 $query = "
747SELECT a.operation, a.object_id
748 FROM civicrm_acl_cache c, civicrm_acl a
749 WHERE c.acl_id = a.id
750 AND a.is_active = 1
751 AND a.object_table = 'civicrm_saved_search'
752 AND a.id IN ( $aclKeys )
753ORDER BY a.object_id
754";
755
756 $dao = CRM_Core_DAO::executeQuery($query);
757
758 // do an or of all the where clauses u see
cf0d1c08 759 $ids = [];
6a488035
TO
760 while ($dao->fetch()) {
761 // make sure operation matches the type TODO
762 if (self::matchType($type, $dao->operation)) {
763 if (!$dao->object_id) {
cf0d1c08 764 $ids = [];
6a488035
TO
765 $whereClause = ' ( 1 ) ';
766 break;
767 }
768 $ids[] = $dao->object_id;
769 }
770 }
771
772 if (!empty($ids)) {
773 $ids = implode(',', $ids);
774 $query = "
775SELECT g.*
776 FROM civicrm_group g
777 WHERE g.id IN ( $ids )
778 AND g.is_active = 1
779";
353ffa53 780 $dao = CRM_Core_DAO::executeQuery($query);
1bcdee33 781 $groupIDs = [];
782 $groupContactCacheClause = FALSE;
6a488035 783 while ($dao->fetch()) {
1bcdee33 784 $groupIDs[] = $dao->id;
6a488035 785
1d902030 786 if (($dao->saved_search_id || $dao->children || $dao->parents)) {
787 if ($dao->cache_date == NULL) {
788 CRM_Contact_BAO_GroupContactCache::load($dao);
789 }
1bcdee33 790 $groupContactCacheClause = " UNION SELECT contact_id FROM civicrm_group_contact_cache WHERE group_id IN (" . implode(', ', $groupIDs) . ")";
6a488035 791 }
6a488035 792
6a488035
TO
793 }
794
1bcdee33 795 if ($groupIDs) {
796 $clauses[] = "(
797 `contact_a`.id IN (
798 SELECT contact_id FROM civicrm_group_contact WHERE group_id IN (" . implode(', ', $groupIDs) . ") AND status = 'Added'
799 $groupContactCacheClause
800 )
801 )";
6a488035
TO
802 }
803 }
804 }
805
806 if (!empty($clauses)) {
807 $whereClause = ' ( ' . implode(' OR ', $clauses) . ' ) ';
808 }
809
810 // call the hook to get additional whereClauses
811 CRM_Utils_Hook::aclWhereClause($type, $tables, $whereTables, $contactID, $whereClause);
812
199761b4 813 if (empty($whereClause)) {
6a488035
TO
814 $whereClause = ' ( 0 ) ';
815 }
816
817 return $whereClause;
818 }
819
28518c90 820 /**
c490a46a 821 * @param int $type
100fef9d 822 * @param int $contactID
28518c90
EM
823 * @param string $tableName
824 * @param null $allGroups
825 * @param null $includedGroups
826 *
827 * @return array
828 */
e6a83034
TO
829 public static function group(
830 $type,
100b0ec6
TO
831 $contactID = NULL,
832 $tableName = 'civicrm_saved_search',
833 $allGroups = NULL,
6a488035
TO
834 $includedGroups = NULL
835 ) {
e3ad0182 836 $userCacheKey = "{$contactID}_{$type}_{$tableName}_" . CRM_Core_Config::domainID() . '_' . md5(implode(',', array_merge((array) $allGroups, (array) $includedGroups)));
837 if (empty(Civi::$statics[__CLASS__]['permissioned_groups'])) {
cf0d1c08 838 Civi::$statics[__CLASS__]['permissioned_groups'] = [];
e3ad0182 839 }
840 if (!empty(Civi::$statics[__CLASS__]['permissioned_groups'][$userCacheKey])) {
841 return Civi::$statics[__CLASS__]['permissioned_groups'][$userCacheKey];
842 }
6a488035 843
8aace6af 844 if ($allGroups == NULL) {
887688b9 845 $allGroups = CRM_Contact_BAO_Contact::buildOptions('group_id', NULL, ['onlyActive' => FALSE]);
8aace6af
JG
846 }
847
6a488035
TO
848 $acls = CRM_ACL_BAO_Cache::build($contactID);
849
cf0d1c08 850 $ids = [];
6a488035
TO
851 if (!empty($acls)) {
852 $aclKeys = array_keys($acls);
853 $aclKeys = implode(',', $aclKeys);
854
77f080cb 855 $cacheKey = CRM_Utils_Cache::cleanKey("$tableName-$aclKeys");
557f8c17
ARW
856 $cache = CRM_Utils_Cache::singleton();
857 $ids = $cache->get($cacheKey);
858 if (!$ids) {
cf0d1c08 859 $ids = [];
557f8c17 860 $query = "
6a488035
TO
861SELECT a.operation, a.object_id
862 FROM civicrm_acl_cache c, civicrm_acl a
863 WHERE c.acl_id = a.id
864 AND a.is_active = 1
865 AND a.object_table = %1
866 AND a.id IN ( $aclKeys )
867GROUP BY a.operation,a.object_id
868ORDER BY a.object_id
869";
cf0d1c08 870 $params = [1 => [$tableName, 'String']];
557f8c17
ARW
871 $dao = CRM_Core_DAO::executeQuery($query, $params);
872 while ($dao->fetch()) {
873 if ($dao->object_id) {
874 if (self::matchType($type, $dao->operation)) {
875 $ids[] = $dao->object_id;
876 }
6a488035 877 }
557f8c17
ARW
878 else {
879 // this user has got the permission for all objects of this type
880 // check if the type matches
881 if (self::matchType($type, $dao->operation)) {
882 foreach ($allGroups as $id => $dontCare) {
883 $ids[] = $id;
884 }
6a488035 885 }
557f8c17 886 break;
6a488035 887 }
6a488035 888 }
557f8c17 889 $cache->set($cacheKey, $ids);
6a488035
TO
890 }
891 }
892
b1f4e637
RN
893 if (empty($ids) && !empty($includedGroups) &&
894 is_array($includedGroups)
895 ) {
896 $ids = $includedGroups;
897 }
e3ad0182 898 if ($contactID) {
54d93c06 899 $groupWhere = '';
900 if (!empty($allGroups)) {
901 $groupWhere = " AND id IN (" . implode(',', array_keys($allGroups)) . ")";
902 }
e3ad0182 903 // Contacts create hidden groups from search results. They should be able to retrieve their own.
904 $ownHiddenGroupsList = CRM_Core_DAO::singleValueQuery("
905 SELECT GROUP_CONCAT(id) FROM civicrm_group WHERE is_hidden =1 AND created_id = $contactID
54d93c06 906 $groupWhere
e3ad0182 907 ");
908 if ($ownHiddenGroupsList) {
909 $ownHiddenGroups = explode(',', $ownHiddenGroupsList);
54d93c06 910 $ids = array_merge((array) $ids, $ownHiddenGroups);
e3ad0182 911 }
912
913 }
b1f4e637 914
6a488035 915 CRM_Utils_Hook::aclGroup($type, $contactID, $tableName, $allGroups, $ids);
e3ad0182 916 Civi::$statics[__CLASS__]['permissioned_groups'][$userCacheKey] = $ids;
6a488035
TO
917 return $ids;
918 }
919
28518c90 920 /**
c490a46a 921 * @param int $type
28518c90
EM
922 * @param $operation
923 *
924 * @return bool
925 */
00be9182 926 public static function matchType($type, $operation) {
6a488035
TO
927 $typeCheck = FALSE;
928 switch ($operation) {
929 case 'All':
930 $typeCheck = TRUE;
931 break;
932
933 case 'View':
934 if ($type == CRM_ACL_API::VIEW) {
935 $typeCheck = TRUE;
936 }
937 break;
938
939 case 'Edit':
940 if ($type == CRM_ACL_API::VIEW || $type == CRM_ACL_API::EDIT) {
941 $typeCheck = TRUE;
942 }
943 break;
944
945 case 'Create':
946 if ($type == CRM_ACL_API::CREATE) {
947 $typeCheck = TRUE;
948 }
949 break;
950
951 case 'Delete':
952 if ($type == CRM_ACL_API::DELETE) {
953 $typeCheck = TRUE;
954 }
955 break;
956
957 case 'Search':
958 if ($type == CRM_ACL_API::SEARCH) {
959 $typeCheck = TRUE;
960 }
961 break;
962 }
963 return $typeCheck;
964 }
965
966 /**
d2e5d2ce 967 * Delete ACL records.
6a488035 968 *
b758c7d5
TO
969 * @param int $aclId
970 * ID of the ACL record to be deleted.
6a488035 971 *
6a488035 972 */
00be9182 973 public static function del($aclId) {
6a488035
TO
974 // delete all entries from the acl cache
975 CRM_ACL_BAO_Cache::resetCache();
976
977 $acl = new CRM_ACL_DAO_ACL();
978 $acl->id = $aclId;
979 $acl->delete();
980 }
96025800 981
6a488035 982}