Service Messages Feature Description

Use Case

Mastodon instances like mastodon.art provide their community rules via a mandatory dialog that has to be accepted before sign up. Similarly Forgejo instances could benefit from a dedicated “Service Messages” Mechanism that allows organizations to prominently show their guidelines to users on certain occasions in the login flow. The requirement here is that agreement to the guidelines happens after sign up. Also this feature is a requirement for compliance within regulated entities. Here, regulators require that certain legal information reach the user and the user decision to accept the info needs to be recorded. Furthermore, it may also be useful to show certain information to the user that don’t need to be explicitly accepted - this can easily be realized within the same development scope.

For example:

  • Community Guidelines could be shown to users once on first sign in and after they have been updated with a prominent pop up
    • Acceptance of these guidelines may be a condition to continue
  • Users could be informed about degraded performance or an upcoming maintenance window or given alerts about usage quota with a small banner

See uctkm002-admin-sets-service for an exemplary flow

Requirements

  • uctkm002-1: When a user logs in for the first time, an “Service Message” should be shown to the user.
  • uctkm002-2: When the Service Message is changed, it should be shown again to all users on their next login.
  • uctkm002-3: Due to compliance, it is necessary to document that the user has seen and agreed to the message presented. To continue interacting with forgejo an “Accept” Button needs to be clicked.
  • uctkm002-4: When the “Accept” Button named in uctkm002-3 is clicked, the timestamp of the consent action must be saved in the database.
  • (Not needed currently) uctkm002-5: If more than one text needs to be shown to the user, the admin can create and manage these texts in Forgejo and set options for them individually.
  • uctkm002-6: A “Service Message” can be configured so that it either requires interaction to proceed (it can be blocking the user flow) or is shown passively and can be discarded by the user at will.

Feature Description

A Service Message is a notification to all users that can only be set by an instance admin. There will be two kinds of Service Messages: “modal” and “banner”.

  • “Banner”
    • A banner is dismissable
    • A banner integrates with the UI to not disrupt the user workflow
    • It does not stop the user navigating the site
    • Its aim is to present secondary information like outage or down time info
  • “Modal”
    • A “Modal” is visually prominent and stops the user in their workflow so interaction is mandatory
    • To continue, the user must press the Confirm button
    • On confirming the Modal, the confirmation time stamp will be saved along with the user id in the service_message struct
    • On denying the Modal, the user is logged out
    • The aim of the Modal is to make sure information is read and acknowledged and the acknowledgment is recorded

The management UI will provide typed text input fields (e.g. one field for modal, one for banner) with markdown and preview capability. The field provides a markdown editor and preview capability. Saving the field will immediately show the service message to any logged in user (also the admin) after their next request to the UI. With this behaviour we can also reach users who might be logged in for a longer period of time.

Technical Diagrams

The connection between templates and routers/web is described here uctkm002-frontend-backend-connection.

An initial technical view of how a confirmation may be saved: uctkm002-saving-confirmation.

ADR

Web Endpoints for Management

To avoid conflicts with the existing structure the service message was implemented using:

  • A dedicated web endpoint admin/service_message for the management of the feature is implemented
  • A dedicated template admin/service_message and a dedicated form is used

POST /admin/service_message/ # Create, Update POST /admin/service_message/delete_modal # Delete GET /admin/service_message/ # Show current service message

Logic for showing the service message

Uctkm002-1&2 have in common, that the user has not accepted an existing modal service message yet. Leveraging that fact, we just need to check the following:

  • Did the user accept the service message yet?
  • Is the service message timestamp of the user older than the timestamp of the current service message?

If any of these questions are answered yes, the service message is shown.

Distinction by type

Considering the requirements and feature description, it becomes apparent that probably only two types of Service Messages will exist (and be shown at the same time).

So for now it makes sense to have typed input fields in the admin management ui. Those fields will then send a form POST to {{AppSubUrl}}/admin/service_message?sm_type="modal || banner". This saves us creating and managing options while being clearer to the admin as well. These modal and banner will be the distinguishing types in the model as well. If more Service Messages need to be shown, the process could include adding types like banner_secondary.

Confirm button and saving a timestamp to database

The confirm button triggers a POST action against {{AppSubUrl}}/user/{username}/confirm?sm_type=sm.type. See uctkm002-saving-confirmation for the full flow.

It makes sense to save the confirmation in the user model for the following reasons:

  1. Saving the confirmation per user in ServiceMessage would require potentially hundreds of thousands of requests to the DB to a single table.
  2. Cleanup Jobs would need to be done to also delete users from the ServiceMessage table
  3. RAM usage will increase if we always have a huge list of users loaded when a ServiceMessage is loaded from DB
  4. The user/{username} endpoint requires an authenticated request, so we can make sure the user is already logged in.
  5. The User Model is already loaded and interacted upon when a user is logged in

So we’ll save the ServiceMessageConfirmation in the User: type ServiceMessageConfirmations map[string][1]timeutil.TimeStamp

  • string will be a valid ServiceMessage type
  • [0] will be the Timestamp of the User Confirmation Interaction

This allows us to compare timestamps of updates and user confirmations and request or maybe re-request confirmation if necessary.

Not passing the whole service message struct

We’ll only use the needed struct fields not the whole struct. The main reason for that is that text rendering happens on the server side. The rendered text needs to be passed to the context as well - so needlessly passing the struct holding the unrendered text as well as the rendered HTML is a waste of resources.