From 834989057c8ce4e16d590e977733f08c6aa5bc03 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Mon, 17 Jan 2022 21:41:32 -0500 Subject: [PATCH] SearchKit - Support conditional links Advanced feature to conditionally show/hide links in a links/buttons/menu column, based on user permissions or row values. --- .../SearchDisplay/AbstractRunAction.php | 27 +++++++++ ext/search_kit/Civi/Search/Admin.php | 16 +++++- .../crmSearchAdminLinkGroup.component.js | 57 +++++++++++++++++-- .../crmSearchAdminLinkGroup.html | 12 ++++ .../crmSearchAdminTokenSelect.component.js | 8 +-- 5 files changed, 107 insertions(+), 13 deletions(-) diff --git a/ext/search_kit/Civi/Api4/Action/SearchDisplay/AbstractRunAction.php b/ext/search_kit/Civi/Api4/Action/SearchDisplay/AbstractRunAction.php index f6957086d0..3e6450edde 100644 --- a/ext/search_kit/Civi/Api4/Action/SearchDisplay/AbstractRunAction.php +++ b/ext/search_kit/Civi/Api4/Action/SearchDisplay/AbstractRunAction.php @@ -373,6 +373,9 @@ abstract class AbstractRunAction extends \Civi\Api4\Generic\AbstractAction { $out['text'] = $this->replaceTokens($column['text'], $data, 'view'); } foreach ($column['links'] as $item) { + if (!$this->checkLinkCondition($item, $data)) { + continue; + } $path = $this->replaceTokens($this->getLinkPath($item, $data), $data, 'url'); if ($path) { $link = [ @@ -390,6 +393,30 @@ abstract class AbstractRunAction extends \Civi\Api4\Generic\AbstractAction { return $out; } + /** + * Check if a link should be shown based on its conditions. + * + * Given a link, check if it is set to be displayed conditionally. + * If so, evaluate the condition, else return TRUE. + * + * @param array $item + * @param array $data + * @return bool + */ + private 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 !== '='; + } + return TRUE; + } + return ArrayQueryActionTrait::filterCompare($data, $item['condition']); + } + /** * @param array $link * @param array $data diff --git a/ext/search_kit/Civi/Search/Admin.php b/ext/search_kit/Civi/Search/Admin.php index 2fb9d9f497..e634639411 100644 --- a/ext/search_kit/Civi/Search/Admin.php +++ b/ext/search_kit/Civi/Search/Admin.php @@ -30,11 +30,12 @@ class Admin { public static function getAdminSettings():array { $schema = self::getSchema(); $extensions = \CRM_Extension_System::singleton()->getMapper(); - return [ + $data = [ 'schema' => self::addImplicitFKFields($schema), 'joins' => self::getJoins($schema), 'pseudoFields' => AbstractRunAction::getPseudoFields(), 'operators' => \CRM_Utils_Array::makeNonAssociative(self::getOperators()), + 'permissions' => [], 'functions' => self::getSqlFunctions(), 'displayTypes' => Display::getDisplayTypes(['id', 'name', 'label', 'description', 'icon']), 'styles' => \CRM_Utils_Array::makeNonAssociative(self::getStyles()), @@ -49,6 +50,19 @@ class Admin { ->addWhere('used_for', 'CONTAINS', 'civicrm_saved_search') ->execute(), ]; + $perms = \Civi\Api4\Permission::get() + ->addWhere('group', 'IN', ['civicrm', 'cms']) + ->addWhere('is_active', '=', 1) + ->setOrderBy(['title' => 'ASC']) + ->execute(); + foreach ($perms as $perm) { + $data['permissions'][] = [ + 'id' => $perm['name'], + 'text' => $perm['title'], + 'description' => $perm['description'] ?? NULL, + ]; + } + return $data; } /** diff --git a/ext/search_kit/ang/crmSearchAdmin/crmSearchAdminLinkGroup.component.js b/ext/search_kit/ang/crmSearchAdmin/crmSearchAdminLinkGroup.component.js index 6623f2933d..524eb5f6b5 100644 --- a/ext/search_kit/ang/crmSearchAdmin/crmSearchAdminLinkGroup.component.js +++ b/ext/search_kit/ang/crmSearchAdmin/crmSearchAdminLinkGroup.component.js @@ -8,11 +8,26 @@ apiParams: '<', links: '<' }, + require: { + crmSearchAdmin: '^crmSearchAdmin' + }, templateUrl: '~/crmSearchAdmin/crmSearchAdminLinkGroup.html', controller: function ($scope, $element, $timeout, searchMeta) { var ts = $scope.ts = CRM.ts('org.civicrm.search_kit'), ctrl = this, - linkProps = ['path', 'entity', 'action', 'join', 'target', 'icon', 'text', 'style']; + linkProps = ['path', 'entity', 'action', 'join', 'target', 'icon', 'text', 'style', 'condition']; + + var permissionOperators = [ + {key: '=', value: ts('Has')}, + {key: '!=', value: ts('Lacks')} + ]; + + this.getOperators = function(clause) { + if (clause[0] === 'check user permission') { + return permissionOperators; + } + return CRM.crmSearchAdmin.operators; + }; this.styles = CRM.crmSearchAdmin.styles; @@ -20,6 +35,26 @@ return _.findWhere(this.styles, {key: item.style}); }; + this.getField = searchMeta.getField; + + this.fields = function() { + var selectFields = ctrl.crmSearchAdmin.getSelectFields(); + var permissionField = [{ + text: ts('Current User Permission'), + id: 'check user permission', + description: ts('Check permission of logged-in user') + }]; + return {results: permissionField.concat(selectFields)}; + }; + + this.onChangeCondition = function(item) { + if (item.condition[0]) { + item.condition[1] = '='; + } else { + item.condition = []; + } + }; + this.sortableOptions = { containment: 'tbody', direction: 'vertical', @@ -32,23 +67,31 @@ } }; + this.permissions = CRM.crmSearchAdmin.permissions; + $scope.pickIcon = function(index) { searchMeta.pickIcon().then(function(icon) { ctrl.group[index].icon = icon; }); }; + function setDefaults(item, newValue) { + _.each(linkProps, function(prop) { + item[prop] = newValue[prop] || (prop === 'condition' ? [] : ''); + }); + } + this.addItem = function(item) { - ctrl.group.push(_.pick(item, linkProps)); + var newItem = _.pick(item, linkProps); + setDefaults(newItem, newItem); + ctrl.group.push(newItem); }; this.onChangeLink = function(item, newValue) { if (newValue.path === 'civicrm/') { newValue = JSON.parse(this.default); } - _.each(linkProps, function(prop) { - item[prop] = newValue[prop] || ''; - }); + setDefaults(item, newValue); }; this.serialize = JSON.stringify; @@ -58,11 +101,15 @@ style: 'default', text: ts('Link'), icon: 'fa-external-link', + condition: [], path: 'civicrm/' }); var defaultLinks = _.filter(ctrl.links, function(link) { return !link.join; }); + _.each(ctrl.group, function(item) { + setDefaults(item, item); + }); if (!ctrl.group.length) { if (defaultLinks.length) { _.each(defaultLinks, ctrl.addItem); diff --git a/ext/search_kit/ang/crmSearchAdmin/crmSearchAdminLinkGroup.html b/ext/search_kit/ang/crmSearchAdmin/crmSearchAdminLinkGroup.html index fc67712617..86af85b404 100644 --- a/ext/search_kit/ang/crmSearchAdmin/crmSearchAdminLinkGroup.html +++ b/ext/search_kit/ang/crmSearchAdmin/crmSearchAdminLinkGroup.html @@ -6,6 +6,7 @@ {{:: ts('Open') }} {{:: ts('Text') }} {{:: ts('Link') }} + {{:: ts('Show if') }} {{:: ts('Style') }} @@ -34,6 +35,17 @@ + + + +
+ +
+
+ +
+