Skip to content

Commit

Permalink
SearchKit Toolbar - Fix conditionals, add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
colemanw committed Sep 20, 2023
1 parent 48cd44b commit d812911
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,10 @@ private function checkLinkAccess(array $link, array $data, int $index = 0): bool
if (!$actionName) {
return FALSE;
}
if ($actionName === 'create') {
// No record to check for this action and getPermittedLinkAction says it's allowed; we're good.
return TRUE;
}
$idField = CoreUtil::getIdFieldName($link['entity']);
$idKey = $this->getIdKeyName($link['entity']);
$id = $data[$link['prefix'] . $idKey] ?? NULL;
Expand All @@ -566,6 +570,8 @@ private function checkLinkAccess(array $link, array $data, int $index = 0): bool
$apiRequest = Request::create($link['entity'], $actionName, ['version' => 4]);
return CoreUtil::checkAccessRecord($apiRequest, $values);
}
// No id so cannot possibly update or delete record
return FALSE;
}
return TRUE;
}
Expand All @@ -589,6 +595,11 @@ private function getPermittedLinkAction(string $entityName, string $actionName):
'allowed' => civicrm_api4($entityName, 'getActions', ['checkPermissions' => TRUE])->column('name'),
];
}
// Map CRM_Core_Action names to API action names :/
$map = [
'add' => 'create',
];
$actionName = $map[$actionName] ?? $actionName;
// Action exists and is permitted
if (in_array($actionName, $this->entityActions[$entityName]['allowed'], TRUE)) {
return $actionName;
Expand Down Expand Up @@ -616,16 +627,22 @@ private function getPermittedLinkAction(string $entityName, string $actionName):
* @param array $data
* @return bool
*/
private function checkLinkCondition(array $item, array $data): bool {
protected function checkLinkCondition(array $item, array $data): bool {
if (empty($item['condition'][0]) || empty($item['condition'][1])) {
return TRUE;
}
$op = $item['condition'][1];
if ($item['condition'][0] === 'check user permission') {
if (!empty($item['condition'][2]) && !\CRM_Core_Permission::check($item['condition'][2])) {
return $op !== '=';
// No permission == open access
if (empty($item['condition'][2])) {
return TRUE;
}
return TRUE;
$permissions = (array) $item['condition'][2];
if ($op === 'CONTAINS') {
// Place conditions in OR array for CONTAINS operator
$permissions = [$permissions];
}
return \CRM_Core_Permission::check($permissions) == ($op !== '!=');
}
// Convert the conditional value of 'current_domain' into an actual value that filterCompare can work with
if ($item['condition'][2] === 'current_domain') {
Expand Down
3 changes: 3 additions & 0 deletions ext/search_kit/Civi/Api4/Action/SearchDisplay/Run.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ private function formatToolbar(): array {
$settings['toolbar'][] = $settings['addButton'] + ['style' => 'primary', 'target' => 'crm-popup'];
}
foreach ($settings['toolbar'] ?? [] as $button) {
if (!$this->checkLinkCondition($button, $data)) {
continue;
}
$button = $this->formatLink($button, $data);
if ($button) {
$toolbar[] = $button;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@
linkProps = ['path', 'task', 'entity', 'action', 'join', 'target', 'icon', 'text', 'style', 'condition'];

ctrl.permissionOperators = [
{key: '=', value: ts('Has')},
{key: '!=', value: ts('Lacks')}
{key: 'CONTAINS', value: ts('Includes')},
{key: '=', value: ts('Has All')},
{key: '!=', value: ts('Lacks All')}
];

this.styles = CRM.crmSearchAdmin.styles;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
<input ng-model="item.condition[0]" crm-ui-select="{placeholder: item.action ? ts('Allowed') : ts('Always'), data: $ctrl.fields}" ng-change="$ctrl.onChangeCondition(item)">
<div class="form-group" ng-if="item.condition[0] === 'check user permission'">
<select class="form-control api4-operator" ng-model="item.condition[1]" ng-options="o.key as o.value for o in $ctrl.permissionOperators"></select>
<input class="form-control" crm-ui-select="{data: $ctrl.permissions}" ng-model="item.condition[2]">
<input class="form-control" crm-ui-select="{data: $ctrl.permissions, multiple: true}" ng-model="item.condition[2]" ng-list>
</div>
<crm-search-condition class="form-group"
ng-if="item.condition[0] && item.condition[0] !== 'check user permission'"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1906,7 +1906,6 @@ public function testKeyIsReturned(): void {

public function testRunWithToolbar(): void {
$params = [
'checkPermissions' => FALSE,
'return' => 'page:1',
'savedSearch' => [
'api_entity' => 'Contact',
Expand Down Expand Up @@ -1944,6 +1943,15 @@ public function testRunWithToolbar(): void {
],
'filters' => ['contact_type' => 'Individual'],
];
// No 'add contacts' permission == no "Add contacts" button
\CRM_Core_Config::singleton()->userPermissionClass->permissions = [
'access CiviCRM',
'administer search_kit',
];
$result = civicrm_api4('SearchDisplay', 'run', $params);
$this->assertCount(0, $result->toolbar);
// With 'add contacts' permission the button will be shown
\CRM_Core_Config::singleton()->userPermissionClass->permissions[] = 'add contacts';
$result = civicrm_api4('SearchDisplay', 'run', $params);
$this->assertCount(1, $result->toolbar);
$button = $result->toolbar[0];
Expand Down Expand Up @@ -1978,6 +1986,80 @@ public function testRunWithToolbar(): void {
$this->assertTrue($button['autoOpen']);
}

public static function toolbarLinkPermissions(): array {
$sets = [];
$sets[] = [
'CONTAINS',
['access CiviCRM', 'administer CiviCRM'],
['access CiviCRM'],
TRUE,
];
$sets[] = [
'=',
['access CiviCRM', 'administer CiviCRM'],
['access CiviCRM'],
FALSE,
];
$sets[] = [
'!=',
['access CiviCRM', 'administer CiviCRM'],
['access CiviCRM'],
TRUE,
];
$sets[] = [
'CONTAINS',
['access CiviCRM', 'administer CiviCRM'],
[],
FALSE,
];
$sets[] = [
'=',
[],
[],
TRUE,
];
return $sets;
}

/**
* @dataProvider toolbarLinkPermissions
*/
public function testToolbarLinksPermissionOperators($linkOperator, $linkPerms, $userPerms, $shouldBeVisible): void {
$params = [
'return' => 'page:1',
'savedSearch' => [
'api_entity' => 'Contact',
'api_params' => [
'version' => 4,
'select' => ['first_name', 'contact_type'],
],
],
'display' => [
'type' => 'table',
'label' => '',
'settings' => [
'actions' => TRUE,
'pager' => [],
'toolbar' => [
[
'path' => 'civicrm/test',
'text' => 'Test',
'condition' => [
'check user permission',
$linkOperator,
$linkPerms,
],
],
],
'columns' => [],
],
],
];
\CRM_Core_Config::singleton()->userPermissionClass->permissions = array_merge(['administer search_kit'], $userPerms);
$result = civicrm_api4('SearchDisplay', 'run', $params);
$this->assertCount((int) $shouldBeVisible, $result->toolbar);
}

public function testRunWithEntityFile(): void {
$cid = $this->createTestRecord('Contact')['id'];
$notes = $this->saveTestRecords('Note', [
Expand Down

0 comments on commit d812911

Please sign in to comment.