Answers.Body: <p>Via a WP MU Plugin, relevant code snippet:</p>
  <pre><code>// 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( &amp;$tasks, $checkPermissions, $userId ) {
          
              $original_tasks = $tasks;
          
              foreach ($tasks as $entity =&gt; $entityTasks) {
                  // Uncomment this to see a list of tasks and keys
                  //\Civi::log()-&gt;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 =&gt; $value) {
          
                      if (in_array($key, $actions_remove)) {
                          unset($tasks[$entity][$key]);
                      }
                  }
          
                  if(current_user_can( $capability) ) {
                      $tasks = $original_tasks;
                  }
              }
          }
     
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-07-28T20:43:30.680Z
Answers.LastActivityDate: 2022-07-28T21:09:52.197Z
Answers.LastEditDate: 2022-07-28T21:09:52.197Z
Answers.UserDisplayName: Andy Burns
Answers.Body: <p>As of CiviCRM v5.60 this is now doable in the UI!</p>
  <p><strong>Selecting only certain tasks:</strong></p>
  <p><a href="https://i.sstatic.net/WDcGp.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/WDcGp.png" alt="enter image description here" /></a></p>
  <p><strong>Results in limited choices:</strong></p>
  <p><a href="https://i.sstatic.net/5yl3T.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/5yl3T.png" alt="enter image description here" /></a></p>
  <p>See <a href="https://github.com/civicrm/civicrm-core/pull/25521" rel="nofollow noreferrer">https://github.com/civicrm/civicrm-core/pull/25521</a></p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2023-03-01T22:39:01.870Z
Answers.Text: 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] http://errors.angularjs.org/1.8.2/$injector/unpr?p0=searchDisplayTasksTraitProvider%20%3C-%20searchDisplayTasksTrait" in Form Builder. Since I know nothing about Angular there is a very small chance for me to crack it.
Answers.UserDisplayName: yurg
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2023-03-01T21:52:50.360Z
Answers.LastActivityDate: 2023-03-01T21:52:50.360Z
Answers.UserDisplayName: Coleman
Answers.Body: <p>The hook will work to do what you want, but you should inspect the <code>$tasks</code> variable to see what it looks like. The array is keyed by entity type so your code needs to be updated accordingly.</p>
  <p>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?</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-04-26T22:46:51.463Z
Answers.Text: Message sent in Mattermost
Answers.UserDisplayName: Andy Burns
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2023-02-07T09:18:20.153Z
Answers.Text: Added an issue on GitLab for this: https://lab.civicrm.org/dev/core/-/issues/4118
Answers.UserDisplayName: Andreas
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-04-26T21:05:08.030Z
Answers.LastActivityDate: 2022-04-26T21:05:08.030Z
Answers.UserDisplayName: Coleman
Body: <p>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.</p>
  <p><strong>Goal: Control what available search actions show in core for a given user based on a given permission that exists in Civi.</strong></p>
  <p>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?</p>
  <p>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:</p>
  <pre><code>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, &amp;$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 =&gt; $value) {
  
              if ( in_array( $value['title'], $actions_remove ) ) {
                  //$to_remove[] = $key;
                  unset($tasks[$key]);
              }
          }
  
          if(current_user_can( $capability) ) {
              $tasks = $original_tasks;
          }
      }
  }
  </code></pre>
  <p>Looking at <a href="https://docs.civicrm.org/dev/en/latest/searchkit/tasks/#angular-based-tasks" rel="nofollow noreferrer">https://docs.civicrm.org/dev/en/latest/searchkit/tasks/#angular-based-tasks</a> the Search Kit hook is <code>civicrm_searchKitTasks</code> 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.</p>
  <pre><code> 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, &amp;$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 =&gt; $value) {
      
                  if ( in_array( $value['title'], $actions_remove ) ) {
                      //$to_remove[] = $key;
                      unset($tasks[$key]);
                  }
              }
      
              if(current_user_can( $capability) ) {
                  $tasks = $original_tasks;
              }
          }
      }
  </code></pre>
  
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2022-04-26T15:04:42.870Z
Comments.Text: 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.
Comments.UserDisplayName: Francesc Bassas i Bullich
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2022-04-26T15:08:46.860Z
Comments.Text: 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.
Comments.UserDisplayName: Andy Burns
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2022-04-26T15:24:21.237Z
Comments.Text: 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.
Comments.UserDisplayName: Francesc Bassas i Bullich
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2022-04-26T16:23:01.760Z
Comments.Text: 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.
Comments.UserDisplayName: Andrew West
Tags: <hooks><searchkit><actions>
Title: Controlling Available Search Kit Actions at a Granular Level
ThreadText: # Controlling Available Search Kit Actions at a Granular Level
  
  (Asked by: username_0 on 2022-04-26)
  
  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;
              }
          }
      }
  
  **Tags:** hooks, searchkit, actions
  
  > **Comments:**
  > **username_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.  
  > **username_0**: 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.  
  > **username_1**: 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.  
  > **username_2**: 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.  
  
  ---
  
  ## 3 answers
  
  ### Answer by username_0 on 2022-07-28. Score: 1 | Accepted answer
  
  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;
                  }
              }
          }
  
  
  ---
  
  ### Answer by username_3 on 2023-03-01. Score: 3
  
  As of CiviCRM v5.60 this is now doable in the UI!
  
  **Selecting only certain tasks:**
  
  [![enter image description here](https://i.sstatic.net/WDcGp.png)](https://i.sstatic.net/WDcGp.png)
  
  **Results in limited choices:**
  
  [![enter image description here](https://i.sstatic.net/5yl3T.png)](https://i.sstatic.net/5yl3T.png)
  
  See <https://github.com/civicrm/civicrm-core/pull/25521>
  
  > **Comments:**
  > **username_4**: 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] http://errors.angularjs.org/1.8.2/$injector/unpr?p0=searchDisplayTasksTraitProvider%20%3C-%20searchDisplayTasksTrait" in Form Builder. Since I know nothing about Angular there is a very small chance for me to crack it.  
  
  ---
  
  ### Answer by username_3 on 2022-04-26. Score: 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?
  
  > **Comments:**
  > **username_0**: Message sent in Mattermost  
  > **username_5**: Added an issue on GitLab for this: https://lab.civicrm.org/dev/core/-/issues/4118  
  
  ---
  
  