4

Some permissions allow actions that may not be desirable to have as a search action that can be done en masse. e.g. I may want contacts to merge records but not delete records en masse on a search display. Other actions don't have a specific permission tied to them that have no other way of being controlled.

Goal: Control what available search actions show in core for a given user based on a given permission that exists in Civi.

How do you go about restricting Search Kit actions at a more granular level (for each action?). Or is this something better to work into core?

We have done this for Advanced Search and all other entity searches in legacy search screens. Relevant snippet below done from a custom Wordpress MU plugin according to a org specific custom permissions structure:

add_filter( 'civicrm_searchTasks', 'advanced_search_actions_control_callback', 10, 4 );

//Remove some available actions on search pages for all users if login user does not have 'access_civicrm_advanced_search_actions' capability
function advanced_search_actions_control_callback( $objectType, &$tasks ) {

    $original_tasks = $tasks;
    $titles = array_column($tasks, 'title');
    $to_remove = array();

    if ( $objectType = array('contact','event','activities','membership')) {
        $capability = 'access_civicrm_advanced_search_actions';

        $actions_remove = array('Add relationship - to Household',
                    'Delete permanently',
                    'Delete contacts',
                    'Group - create smart group',
                    'Tag - add to activities',
                    'Tag - remove from activities',
                    'Tag - remove from activities',
                    'Add relationship - to Individual',
                    'Add relationship - to Organization',
                    'Communication preferences - alter',
                    'Email - unhold addresses',
                    'Group - create smart group',
                    'Register participants for event',
                    'Restore contacts from trash',
                    'Tag - add to contacts',
                    'Tag - remove from contacts',
                    'Update multiple contacts',
                    'Update multiple memberships'
                    );

        foreach($tasks as $key => $value) {

            if ( in_array( $value['title'], $actions_remove ) ) {
                //$to_remove[] = $key;
                unset($tasks[$key]);
            }
        }

        if(current_user_can( $capability) ) {
            $tasks = $original_tasks;
        }
    }
}

Looking at https://docs.civicrm.org/dev/en/latest/searchkit/tasks/#angular-based-tasks the Search Kit hook is civicrm_searchKitTasks so I replicated the function above and used but is not working. Obviously, using Angular is a major difference so more changes probably are needed. It would be ideal to not have to permission search actions independently of one other in this searching transtion time. I had hoped the permissioning we've done in advanced search legacy actions would apply over.

 add_filter( 'civicrm_searchKitTasks', 'searchkit_actions_control_callback', 10, 4 );
    
    //Remove some available actions on search pages for all users if login user does not have 'access_civicrm_advanced_search_actions' capability
    function searchkit_actions_control_callback( $objectType, &$tasks ) {
    
        $original_tasks = $tasks;
        $titles = array_column($tasks, 'title');
        $to_remove = array();
    
        if ( $objectType = array('contact','event','activities','membership')) {
            $capability = 'access_civicrm_advanced_search_actions';
    
            $actions_remove = array('Add relationship - to Household',
                        'Delete permanently',
                        'Delete contacts',
                        'Group - create smart group',
                        'Tag - add to activities',
                        'Tag - remove from activities',
                        'Tag - remove from activities',
                        'Add relationship - to Individual',
                        'Add relationship - to Organization',
                        'Communication preferences - alter',
                        'Email - unhold addresses',
                        'Group - create smart group',
                        'Register participants for event',
                        'Restore contacts from trash',
                        'Tag - add to contacts',
                        'Tag - remove from contacts',
                        'Update multiple contacts',
                        'Update multiple memberships'
                        );

            foreach($tasks as $key => $value) {
    
                if ( in_array( $value['title'], $actions_remove ) ) {
                    //$to_remove[] = $key;
                    unset($tasks[$key]);
                }
            }
    
            if(current_user_can( $capability) ) {
                $tasks = $original_tasks;
            }
        }
    }
4
  • 1
    For me, at the UX level, I would like being able to define in the same SearchKit construction whether to show all the (permissioned) actions or a selection of them. Commented Apr 26, 2022 at 15:04
  • 1
    Yeah, that is one route (on each Search Display) that would be in the enhancement department. I probably still would like a global method so I didn't have to remove the same actions each time. That will get old. I still like to show some actions based on permission as well as the display is likely used by a variety of user levels, not all or nothing for the display.
    – Andy Burns
    Commented Apr 26, 2022 at 15:08
  • Maybe enabling the possibility to define sets of actions (at SearchKit settings level). Then you can choose on each Search Display (1) the default actions, (2) a custom selection of actions OR (3) a predefined set of actions. Commented Apr 26, 2022 at 15:24
  • Just to add that if this happened we could probably reduce the overall permission required to use Search Kit, too. At the moment it's 'Administer CiviCRM data', which is higher than I want to give my users (and higher than is needed for Advanced Search). I figure it's probably because of the issues raised here. Commented Apr 26, 2022 at 16:23

3 Answers 3

3

The hook will work to do what you want, but you should inspect the $tasks variable to see what it looks like. The array is keyed by entity type so your code needs to be updated accordingly.

But as noted in the comments, a better solution would be to add this ability to the UI when configuring a display. It wouldn't be difficult, would someone like to sponsor a few hours of Core Team work to do so?

2
3

As of CiviCRM v5.60 this is now doable in the UI!

Selecting only certain tasks:

enter image description here

Results in limited choices:

enter image description here

See https://github.com/civicrm/civicrm-core/pull/25521

1
  • So pity Tasks are available for Table DIsplay only! I've tried to honestly borrow Table Display code and almost succeed: it works in SearchKit but gives "Error: [$injector:unpr] errors.angularjs.org/1.8.2/$injector/…" in Form Builder. Since I know nothing about Angular there is a very small chance for me to crack it.
    – yurg
    Commented Mar 1, 2023 at 22:39
1

Via a WP MU Plugin, relevant code snippet:

// Remove some available Search Kit actions on search pages for all users if login user does not have 'access_civicrm_search_actions' capability

add_filter( 'civicrm_searchKitTasks', 'search_kit_actions_control_callback', 100, 3 );


        function search_kit_actions_control_callback( &$tasks, $checkPermissions, $userId ) {
        
            $original_tasks = $tasks;
        
            foreach ($tasks as $entity => $entityTasks) {
                // Uncomment this to see a list of tasks and keys
                //\Civi::log()->debug('entityTasks: ' . $entity . ': ' . print_r($entityTasks,TRUE));
        
                if (!in_array($entity, ['Contact', 'Event', 'Activity', 'Membership'])) {
                    continue;
                }
                $capability = 'access_civicrm_search_actions';
        
                $actions_remove = [
        
                    'delete', // Delete Contacts
                    'tag', // Tag - Add/Remove Tags
                    'update', // Update
                ];
                if ($entity === 'Activity') {
                    $actions_remove[] = 'tag';
                }
        
                foreach ($entityTasks as $key => $value) {
        
                    if (in_array($key, $actions_remove)) {
                        unset($tasks[$entity][$key]);
                    }
                }
        
                if(current_user_can( $capability) ) {
                    $tasks = $original_tasks;
                }
            }
        }
   

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.