Advanced feature to conditionally show/hide links in a links/buttons/menu column,
based on user permissions or row values.
$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 = [
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
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()),
->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;
}
/**
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;
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',
}
};
+ 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;
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);
<th>{{:: ts('Open') }}</th>
<th>{{:: ts('Text') }}</th>
<th>{{:: ts('Link') }}</th>
+ <th>{{:: ts('Show if') }}</th>
<th>{{:: ts('Style') }}</th>
<th class="crm-search-admin-icon-col"></th>
</tr>
<td class="form-inline">
<crm-search-admin-link-select api-entity="$ctrl.apiEntity" api-params="$ctrl.apiParams" link="item" links="$ctrl.links" on-change="$ctrl.onChangeLink(item, newLink)"></crm-search-admin-link-select>
</td>
+ <td class="form-inline">
+ <input ng-model="item.condition[0]" crm-ui-select="{placeholder: item.action ? ts('Allowed') : ts('Always'), data: $ctrl.fields}" ng-change="$ctrl.onChangeCondition(item)">
+ <select ng-if="item.condition[0]" class="form-control api4-operator" ng-model="item.condition[1]" ng-options="o.key as o.value for o in $ctrl.getOperators(item.condition)">
+ </select>
+ <div class="form-group" ng-if="item.condition[0] === 'check user permission'">
+ <input class="form-control" crm-ui-select="{data: $ctrl.permissions}" ng-model="item.condition[2]">
+ </div>
+ <div class="form-group" ng-if="item.condition[0] && item.condition[0] !== 'check user permission' && item.condition[1].indexOf('IS ') !== 0">
+ <crm-search-input ng-model="item.condition[2]" field="$ctrl.getField(item.condition[0])" option-key="'name'" op="item.condition[1]" class="form-group"></crm-search-input>
+ </div>
+ </td>
<td>
<div class="btn-group">
<button type="button" style="min-width: 85px" class="btn btn-{{ item.style }} dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
this.getTokens = function() {
var allFields = ctrl.admin.getAllFields(ctrl.suffix || '', ['Field', 'Custom', 'Extra', 'Pseudo']);
- _.eachRight(ctrl.admin.savedSearch.api_params.select, function(fieldName) {
- allFields.unshift({
- id: _.last(fieldName.split(' AS ')),
- text: searchMeta.getDefaultLabel(fieldName)
- });
- });
return {
- results: allFields
+ results: ctrl.admin.getSelectFields().concat(allFields)
};
};