Using Activities


The portal homepage logs a user into a user/group/role (i.e. their control models), and redirects them to one of '/a/{activity-slug}' or '/p/{activity-slug}'.

Control Authentication Checks

At this point, the participant/administrator and activity middleware are active. These are defined in the SDK.

Participant middleware checks our control credentials. These check that the given credentials are in the for logic group for the activity, and the resource needed for the activity (i.e. group if a group activity, role if a role activity etc) is present. If any of these checks fail, we throw an ActivityRequiresParticipant. At this point, the portal catches the exception and puts us on a page loaded through the LogIntoParticipant controller. This page loads, using the Audience Member API from the SDK, the things the user can be to access the given activity.

The reason for this is that, if the exception has been fired, we want to log in the user into something they can act as to be in the activity for logic group. Using the AudienceMember, we work out if the user can just be themselves, and if any groups or roles are in the logic group. We then load a page if so, which lists all the things we can be. This uses the same post request as the Portal Homepage to log us in and direct us to the activity.

log in to access activity ss

Of course, the same concept applies for the administrator middleware, which is applied if on the administrator side. This just directs to the LogIntoAdmin controller instead, through firing an ActivityRequiresAdmin exception. It also doesn’t check the activity resource type, just checks the user is in the admin logic group.

Activity Instances

The activity middleware is applied on the admin and participant sides. It injects the activity into the container. This means we can resolve the current activity from the container using the class name.

Additionally, it handles activity instances. Activity instances link together an activity and a resource. A resource is either a user, group or role, depending on what the activity is for. For example, if the activity is for a role, then when a user logged into that role does something in an activity, the activity instance ID is saved in the database, e.g. against an uploaded file. The activity instance contains a type, 'role', and an ID, of the role the user is logged into. Multiple different users can log into the same activity instance, for example if another user had the same role they would share the role activity instance. This essentially abstracts the user id, group id and/or role id to a single unified model with knowledge of the user/group/role structure.

When in an activity, we ensure an activity instance is loaded and retrievable. The SDK provides an API for setting and getting an Activity Instance, and middleware to automatically check if it’s loaded. The middleware that is ran is listed below

Log into an activity instance

First, if the request contains a query with the key aiid, the activity instance, then that activity instance will be logged into.

Check logged into activity instance

Throws a NotInActivityInstanceException if you are not logged into an activity instance. The portal defines an exception handler to handle this exception gracefully. It retrieves the activity being accessed, then uses the DefaultActivityInstanceGenerator, given by the SDK, to create or find an activity instance to log into. Most of the time, there will only be one activity instance. All the time, we can just find the first activity instance for their credentials and log into it.

Additionally, if the correct model is not available, we fire an ActivityRequiresUser / ActivityRequiresGroup / ActivityRequiresRole exception, which extends ActivityRequiresParticipant so behaves in the same way in terms of loading the page to log into the correct model.

Check Activity Instance is for the activity

At this point, we should have either been logged into the activity instance referenced with aiid, or the default activity instance. We now check the activity instance is linked to the activity we’re trying to load, and throw a NotInActivityInstanceException.

Inject the activity instance

FInally, we inject the activity instance to be referenced by its class name to be resolved from the container.

Page Layout

When clicking on an activity, once all the checks are complete, you are taken to the activity page. This shows each of the module instances within the activity, and gives you a brief overview of the activity.

activity ss


Title at the top of the page and browser title is dynamic. These are set to the title of the activity.


The sidebar shows activities you can access through your given credentials. For example, since we’re currently logged into our Swimming Club membership, the sidebar will show activities for which we are in the 'activity for logic' group. Clicking on them will take you to the participant page for the given activity (assuming we’re already on the participant side) or the admin side if we’re on the admin side.

In summary, it will take you to any activities you can access without changing your participant/admin status or your user/group/role logins.

Acting As

In the dropdown, unlike when we were on the homepage and saw 'Acting as nothing', we can now see 'Acting in your membership to Swimming Club'. This is because this represents the current credentials we’re in.

Activity Instance Selection

The SDK allows for three main types of activities, open, completable and multi-completable.

In an open activity, the concept of finishing doesn’t exist. This is useful for things like 'Group Management', because there is only ever one activity instance and no completions.

In a completable activity, there is only ever one activity instance so the activity can only be finished once. Therefore, this is useful for one-off tasks that cannot be repeated, but needs to be completed.

A multi-completable activity is similar to a completable activity, but the activity can be started over and over. This is useful for things like 'Expense Claim Submissions', since you can restart the service for each expense claim so the service only has to be designed for a single submission.

A multi-completable activity can have many activity instances for the same person. In reality, this means that one resource (i.e. the user/group/role the user is logged into, depending on who the activity is for) can have many totally disparate run throughs of the activity, allowing them to start it over as many times as they’d like.

To be able to get back to previous activity instances, the activity instance switcher shows all previous run throughs (with a name and description). Clicking 'New Run Through' will create a new activity instance, and clicking on an activity instance will log into that one. To do that, it simply reloads the page but passes the aiid parameter, which the LogIntoActivityInstance middleware in the 'activity' group logs into, meaning the page is reloaded from the point of view of the selected activity instance!

The DefaultActivityInstanceGenerator returns the first activity instance from the database to log into. This works for a case where there is a single Activity Instance, but also where there are multiple activity instances.

This should be changed to the most recent one, i.e. sort the activity instances by created_at descending


Having loaded the page, and selected an activity instance if necessary, we can take a look at the modules.

Modules have different states, decided by logic groups and completion information in the module instance set up. These are evaluated on load, so the frontend has the information necessary to render the page.

Modules are loaded through the Pages\ActivityController. As normal, they have a participant() and admin() function. Both are similar.

They both start by using the ResourceIdGenerator to get a resource ID and resource type to load. They also have access to the ActivityInstance and Activity through the container.

The participant function then returns the correct view with the following parameters

  • activity → The current activity with all the module instances

  • activityInstance → The activity instance logged into

  • activityInstances → All activity instances for the activity, resource type and resource ID

  • admin → false

  • evaluation → Information about the state of each of the modules.

The admin function is very similar, except it uses a different evaluation function and returns admin as true.

The evaluation is an SDK provided tool, and returns an array of completed Evaluation objects with the module instance ID as the key. The evaluation object is a simple data structure, holding the following parameters

  • visible:

    • Should the module instance be visible/shown on the page?

    • The module is still accessible directly at the URL, but this may change soon.

    • If admin, will always be true

    • Uses the visible logic group

  • mandatory

    • Is the module instance mandatory to complete?

    • If admin, will always be false

    • Will be false if the module instance is part of an open activity, since completion makes no sense in that context

    • Uses the mandatory logic group

  • active:

    • Can the module instance be accessed?

    • Always true if admin

    • Uses the active logic group

  • complete:

    • Has the module been completed?

    • If admin, will always be false

    • Will be false if the module instance is part of an open activity, since completion makes no sense in that context

    • Uses the completion condition.

The below image demonstrates the different states a module can take. Of course, the 'Not Visible' state is not visible, so is not included. All the buttons below are assumed to have the 'Visible' evaluation as true.

button types ss