$acls = CRM_ACL_BAO_Cache::build($contactID);
$aclKeys = array_keys($acls);
$aclKeys = implode(',', $aclKeys);
+ $select = "a.operation, a.object_id";
+ $hasPriorty = FALSE;
+ if (array_key_exists('priority', CRM_ACL_BAO_ACL::getSupportedFields())) {
+ $select .= ",a.priority";
+ $hasPriority = TRUE;
+ }
$query = "
-SELECT a.operation, a.object_id
+SELECT {$select}
FROM civicrm_acl_cache c, civicrm_acl a
WHERE c.acl_id = a.id
AND a.is_active = 1
AND a.object_table = %1
AND a.id IN ( $aclKeys )
AND a.deny = 1
-GROUP BY a.operation,a.object_id
-ORDER BY a.object_id
";
$params = [1 => [$tableName, 'String']];
$dao = CRM_Core_DAO::executeQuery($query, $params);
while ($dao->fetch()) {
if ($dao->object_id) {
if (self::matchType($type, $dao->operation)) {
- $ids[] = $dao->object_id;
+ if ($hasPriority) {
+ $permittedRules = CRM_Core_DAO::singleValueQuery("SELECT count(a.id)
+ FROM civicrm_acl a
+ INNER JOIN civicrm_acl_cache c ON c.acl_id = a.id
+ WHERE a.id IN ( $aclKeys )
+ AND a.is_active = 1
+ AND a.object_table = %1
+ AND a.deny = 0
+ AND a.priority > %2
+ AND a.object_id = %3", [
+ 1 => [$tableName, 'String'],
+ 2 => [(int) $dao->priority, 'Integer'],
+ 3 => [$dao->object_id, 'Integer'],
+ ]);
+ if (empty($permittedRules) && !in_array($dao->object_id, $ids, TRUE)) {
+ $ids[] = $dao->object_id;
+ }
+ }
+ else {
+ if (!in_array($dao->object_id, $ids, TRUE)) {
+ $ids[] = $dao->object_id;
+ }
+ }
}
}
}
*
* Generated from xml/schema/CRM/ACL/ACL.xml
* DO NOT EDIT. Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:f21ef3073d6247d130341cd182793ea6)
+ * (GenCodeChecksum:9d50ed80344474830f87df285dc6cbf2)
*/
/**
*/
public $is_active;
+ /**
+ * @var int|string
+ * (SQL type: int)
+ * Note that values will be retrieved from the database as a string.
+ */
+ public $priority;
+
/**
* Class constructor.
*/
],
'add' => '1.6',
],
+ 'priority' => [
+ 'name' => 'priority',
+ 'type' => CRM_Utils_Type::T_INT,
+ 'title' => ts('Priority'),
+ 'required' => TRUE,
+ 'usage' => [
+ 'import' => FALSE,
+ 'export' => FALSE,
+ 'duplicate_matching' => FALSE,
+ 'token' => FALSE,
+ ],
+ 'where' => 'civicrm_acl.priority',
+ 'default' => '0',
+ 'table_name' => 'civicrm_acl',
+ 'entity' => 'ACL',
+ 'bao' => 'CRM_ACL_BAO_ACL',
+ 'localizable' => 0,
+ 'html' => [
+ 'type' => 'Number',
+ ],
+ 'add' => '5.64',
+ ],
];
CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']);
}
'localizable' => FALSE,
'sig' => 'civicrm_acl::0::acl_id',
],
+ 'index_priority' => [
+ 'name' => 'index_priority',
+ 'field' => [
+ 0 => 'priority',
+ ],
+ 'localizable' => FALSE,
+ 'sig' => 'civicrm_acl::0::priority',
+ ],
];
return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices;
}
if ($this->_action & CRM_Core_Action::ADD) {
$defaults['object_type'] = 1;
+ $defaults['priority'] = 0;
}
$showHide = new CRM_Core_ShowHideBlocks();
0 => ts('Allow'),
1 => ts('Deny'),
]);
+ $this->add('number', 'priority', ts('Priority'));
$this->addFormRule(['CRM_ACL_Form_ACL', 'formRule']);
}
$acl[$dao->id]['object_id'] = $dao->object_id;
$acl[$dao->id]['is_active'] = $dao->is_active;
$acl[$dao->id]['deny'] = $dao->deny;
+ $acl[$dao->id]['priority'] = $dao->priority;
if ($acl[$dao->id]['entity_id']) {
$acl[$dao->id]['entity'] = $roles[$acl[$dao->id]['entity_id']] ?? NULL;
* The version number matching this function name
*/
public function upgrade_5_64_alpha1($rev): void {
+ $this->addTask('Add priority column onto ACL table', 'addColumn', 'civicrm_acl', 'priority', 'int NOT NULL DEFAULT 0');
$this->addTask(ts('Upgrade DB to %1: SQL', [1 => $rev]), 'runSql', $rev);
$this->addTask('Update post_URL/cancel_URL in logging tables', 'updateLogging');
}
<td class="label">{$form.deny.label}</td>
<td>{$form.deny.html}</td>
</tr>
+ <tr class="crm-acl-form-block-priority">
+ <td class="label">{$form.priority.label}</td>
+ <td>{$form.priority.label}<br />
+ <span class="description">{ts}Higher priority ACL rules will override lower priority rules{/ts}</span>
+ </td>
+ </tr>
</table>
<div id="id-group-acl">
<table class="form-layout-compressed">
<th>{ts}Description{/ts}</th>
<th>{ts}Enabled?{/ts}</th>
<th>{ts}Mode{/ts}</th>
+ <th>{ts}Priority{/ts}</th>
<th></th>
</tr>
</thead>
<td class="crm-acl-name crm-editable" data-field="name">{$row.name}</td>
<td class="crm-acl-is_active" id="row_{$aclID}_status">{if $row.is_active eq 1} {ts}Yes{/ts} {else} {ts}No{/ts} {/if}</td>
<td class="crm-acl-deny" id="row_{$aclID}_deny">{if $row.deny}{ts}Deny{/ts}{else}{ts}Allow{/ts}{/if}</td>
+ <td class="crm-acl-priority" id="row_{$aclID}_priority">{$row.priority}</td>
<td>{$row.action|replace:'xx':$aclID}</td>
</tr>
{/foreach}
Civi::$statics['CRM_ACL_BAO_ACL'] = [];
}
+ /**
+ * @throws \CRM_Core_Exception
+ */
+ public function testPriorityCustomGroupACL(): void {
+ // Create 2 multi-record custom entities and 2 regular custom fields
+ $customGroups = [];
+ foreach ([1, 2] as $i) {
+ $customGroups[$i] = CustomGroup::create(FALSE)
+ ->addValue('title', "priority_extra_group_$i")
+ ->addValue('extends', 'Contact')
+ ->addValue('is_multiple', FALSE)
+ ->addChain('field', CustomField::create()
+ ->addValue('label', "priority_extra_field_$i")
+ ->addValue('custom_group_id', '$id')
+ ->addValue('html_type', 'Text')
+ ->addValue('data_type', 'String')
+ )
+ ->execute()->single()['id'];
+ $this->callAPISuccess('Acl', 'create', [
+ 'name' => 'Deny everyone to access custom group ' . $customGroups[$i],
+ 'entity_table' => 'civicrm_acl_role',
+ 'entity_id' => 0,
+ 'operation' => 'Edit',
+ 'object_table' => 'civicrm_custom_group',
+ 'object_id' => $customGroups[$i],
+ 'deny' => 1,
+ ]);
+ }
+
+ $this->callAPISuccess('OptionValue', 'create', [
+ 'option_group_id' => 'acl_role',
+ 'label' => 'Test Priority ACL Role',
+ 'value' => 5,
+ 'is_active' => 1,
+ ]);
+ $aclGroup = $this->groupCreate();
+ ACLEntityRole::create(FALSE)->setValues([
+ 'acl_role_id' => 5,
+ 'entity_table' => 'civicrm_group',
+ 'entity_id' => $aclGroup,
+ 'is_active' => 1,
+ ])->execute();
+ $this->callAPISuccess('Acl', 'create', [
+ 'name' => 'Test Postive Priority ACL',
+ 'priority' => 1,
+ 'entity_table' => 'civicrm_acl_role',
+ 'entity_id' => 5,
+ 'operation' => 'Edit',
+ 'object_table' => 'civicrm_custom_group',
+ 'object_id' => $customGroups[2],
+ ]);
+ $userID = $this->createLoggedInUser();
+ CRM_Core_Config::singleton()->userPermissionClass->permissions = [
+ 'access CiviCRM',
+ 'view my contact',
+ ];
+ $this->callAPISuccess('GroupContact', 'create', [
+ 'contact_id' => $userID,
+ 'group_id' => $aclGroup,
+ 'status' => 'Added',
+ ]);
+ Civi::cache('metadata')->clear();
+ Civi::$statics['CRM_ACL_BAO_ACL'] = [];
+ $getFields = Contact::getFields()
+ ->addWhere('name', 'LIKE', 'priority_extra_group_%.priority_extra_field_%')
+ ->execute();
+ $this->assertCount(1, $getFields);
+
+ Civi::cache('metadata')->clear();
+ Civi::$statics['CRM_ACL_BAO_ACL'] = [];
+ }
+
public function aclGroupHookAllResults($action, $contactID, $tableName, &$allGroups, &$currentGroups) {
if ($tableName === $this->aclGroupHookType) {
$currentGroups = array_keys($allGroups);
<label>Enabled</label>
</html>
</field>
+ <field>
+ <name>priority</name>
+ <type>int</type>
+ <default>0</default>
+ <required>true</required>
+ <add>5.64</add>
+ <html>
+ <type>Number</type>
+ </html>
+ </field>
+ <index>
+ <name>index_priority</name>
+ <fieldName>priority</fieldName>
+ </index>
</table>