Scheduled user actions

The user data component includes a mechanism to schedule particular actions to be performed, based off filters. For example, "delete users who have been suspended for 7 years".

This can be combined with user data purging on suspend/delete in order to meet GDPR obligations in a more automated fashion.


This is implemented in totara_userdata. It is not designed to be extended in plugins.

There are 3 main parts: actions, filters, and the execution job.

Actions are changes in the system to perform. For example, suspending a user. They implement the action_contract interface, and take a single user on which to perform the action.

Filters are restrictions on what users to match. For example, being a member of X audience. Filters likewise implement a filter_contract interface, and accept a user_repository on to which they apply the filter. See the code for the exact structure of the interfaces.

The execution job is a scheduled task that takes each rule, finds all users that match the filters, and runs the action specified in the rule for each.

Note that with the current implementation, this means that if an action does not result in the user no longer matching the filter, the action will run again the next time the scheduled task runs.

Introducing new actions and filters

Adding a new action can be done by adding a new class and adding it to totara_useraction\action\factory. It will then be picked up and become an option in the UI.

The filters are fixed – there is a separate column on the rule table for each filter, and the filters are individually implemented in the UI, so adding a new filter is a bit more involved. The steps are roughly:

  • Define the filter class, implementing filter_contract
  • Add a column to the totara_useraction_scheduled_rule table to store the filter
  • Update the entity class to include the column
  • Update the scheduled_rule model class to pass the data through via create(), update() and a filter_x property/get_filter_x() method
  • Update the GraphQL queries, mutations, and resolvers to include the new filter
  • Implement the frontend – add the filter to the edit screen, and the details panel on the list view