Extension:Echo/Creating a new notification type

Notifications system allows logged in users to receive alerts and notices about their interactions with connected wikis. In Wikimedia projects, this means wikis in the cluster, but the Notifications system can also be used by third-parties - either in a single wiki or a series of connected wikis to utilize the cross-wiki notification system

The system allows extension developers the ability to add new notification types that users can receive under certain circumstances. This document will outline the way to create such notification, explain best practices and formulate the known pitfalls to avoid.

Introduction to how notifications work
Notifications are, very generally, made of two concepts - the Event and the PresentationModel. The event stores details of the triggered event and the presentation model defines the way it is presented in the user interface.

EchoEvent
EchoEvent class defines general events, collects information about the related page, user and wiki and inserts them into the database. Events are general, and one even may define multiple views. For example, mentioning 4 people will create a single event, that will then be referenced (and 'trigger' a notification) for the four mentioned users. All of their individual notifications, however, will reference the same event.

EchoPresentationModel
Every type of notifications requires a presentation model, a way to define what the notification will display, where it will link to, and what secondary actions is will present. The front-end of the notifications system (the notifications popup and the Special:Notifications page) read that information to produce a proper display of notification.

The presentation of a notification type is always based on the base definition in EchoEventPresentationModel. That class defines the base behavior of all notifications, and each child notification must extend it, and then adjust the details to its needs.

How events work
If you are creating a new notification type, you should first create an event. The logical process is: ''Note: This is a slight generalization of the process for the sake of clarity. Explaining how things work in practice with database table rows (per user, wiki, cross-wiki, etc) is outside the scope of this tutorial.''
 * 1) When relevant, your code creates the event using
 * 2) As part of the definition of the event you should detail which users receive a notification from this event. This is defined by the parameter   (For an example of this, see Flow's 'user-locators' method used to locate users who are following a title)
 * 3) The event is stored in the database, and as notifications for the relevant users
 * 4) When users request their notification list from the API, the system looks for the relevant presentation model and creates the data - title, primary link, secondary links, relevant user, etc.
 * 5) The front-end uses the structured data to present the notification.

Event definition
Events contain the following definitions that are stored in  in the hook The role of user-locators and user-filters is important to make sure we don't produce double-notification from a single event to users. For example, if a new topic is created in Flow, we want to notify all those who follow the board - but we want to ignore users who were mentioned, because 'mention' will produce its own event.

Note: When you create new events, try to make sure you filter out potential colliding events for your notified users, otherwise, they will receive two notifications for the same event.

Event creation details
When we create and trigger the event, we need to supply details as well:

Presentation model definition
Presentation models are meant to be very flexible, so to allow the creation of notification types with specific displays. Unlike event definition and creation, the presentation model is a class on its own, and requires extending it and making the methods specific to the notification case. These are the definitions of the base  class:

Important notes

 * is an incredibly important method in this class, because on top of affecting whether the notification is displayed, it is also part of the test the Notification system uses to moderate notifications. For example, if a notification was created about a new mention for a user, but the revision that triggered this was then deleted or suppressed, the notification should vanish as well. The system will check if the notification can be displayed (using, among other things, 'canRender') - if canRender in this case checks that the revision exists, it will return 'false' (because the revision was deleted) which will make sure the notification is flagged for deletion.  Not defining canRender correctly can result in errors, as the notification will try to call for details on a missing title or revision, so please take care to define it if it is needed.
 * must return an array. If your notification only has secondary links in certain cases, please make sure that all other cases return an array and not 'false' or 'null'; unlike  that returns   for empty value,   must return an array.

Structure of secondary links
The secondary links information allows notifications to specify sub-links to actions that are extending the main primary link for the notification. For example, a notification about some revision edit can have its primary link point to the revision diff page, but also have secondary links pointing for the user page of the user who made the change, or a link to the view the page that was changed.

Secondary links also allow API-directed actions ('dynamic' actions) for actions like 'stop watching a page' or others that require the front-end to request an AJAX request to the API directly from the notification.

The general structure of secondary link: Secondary links also allow for dynamic actions, but those require some registration and further definition that is outside the scope of this tutorial.

Walkthrough: Creating new notification type
Let's say we have an extension that keeps a list of users who are interested in new titles created with specific words in them. The extension allows users to add and remove themselves from a watch-list for a certain list of words in a title, and listens to the hook for creating new pages on the wiki to identify relevant pages. When a relevant page is created, the extension code will create an event with the relevant details, and notify the users that are on the list for that combination of words.

This section will demonstrate how to create this new notification type:
 * 1) Define a function to identify relevant users to notify
 * 2) Create an event
 * 3) Create a presentation model

Defining the event
We need to make sure our event is defined in the system. This is done by listening to the hook  and defining the event definition: You can see another example of this in the Thank extension.

Identify relevant users to notify
Notification system will try to notify relevant users, but it can't guess who those are. The code that creates the event needs to supply that information. To do that, we should create a function that is then inserted into the definition of the new event.

In this case, we will want to go over the list of users and add them all:

Adding code to trigger the event
Now that we have a way to identify who we want to notify, we need to actually trigger this event. Our hypothetical extension listens to page creation hook and then checks to see if the new title has words that fit the list. However way the extension code does this, once it identified a title that fits a list, it will create an event and notify the relevant users.

The code below skips the actual operation of listening to new page creation and checking the words, and jumps straight into defining the new event: You can see an example of filtering out mentioned users in Flow's event for flow-new-topic.