Extension:TimedNotify
TimedNotify Release status: stable |
|
---|---|
Implementation | Hook |
Description | Provides a time-based notification system for Echo |
Author(s) | Xxmarijnw (Wikibase Solutions) |
Latest version | 1.1.0 (2023-01-23) |
MediaWiki | 1.35+ |
PHP | 7.4+ |
Database changes | Yes |
License | GNU General Public License 2.0 or later |
Download | GitHub:
<translate> Note:</translate> |
|
|
Quarterly downloads | Lua error in Module:Extension at line 172: bad argument #1 to 'inNamespace' (unrecognized namespace name 'skin'). |
Public wikis using | Lua error in Module:Extension at line 172: bad argument #1 to 'inNamespace' (unrecognized namespace name 'skin'). |
TimedNotify provides a time-based notification system for Echo. Time-based notifications are notifications that are not explicitly triggered by an action, but are instead sent out (periodically) when certain criteria are met. The canonical example of time-based notifications are reminders.
Configuration
The following configuration options are available:
$wgTimedNotifyRunRate
(default:0.05
) - determines the run rate of the notifier$wgTimedNotifyRunDeferred
(default:true
) - whether to run the notifications in a deferred update$wgTimedNotifyPushedNotificationRetentionDays
(default:60
) - the number of days to retain pushed notifications$wgTimedNotifyDisabledNotifiers
(default:[]
) - which notifiers to disable
$wgTimedNotifyRunRate
This configuration parameter determines how often to run the notifications. Calculating which notifications to send out and to whom is expensive. The notifications are therefore only calculated approximately once every 1/$wgTimedNotifyRunRate
requests. Thus, if you set your run rate to 0.01
, the probability of running the notifications is 1 in 100 for every request. Setting this to a lower number will increase performance, as the notifications have to be calculated less often, but it will decrease the timeliness of the notifications. That is, notifications may arrive later than expected. It is recommended to keep this value between 0.01
(1/100
) and 0.5
(1/2
), depending on the size of your wiki. For smaller wiki's, this value should be greater than for large wiki's.
If you don't want notifications to run on web requests at all, you can set the run rate to 0.0
. In this case maintenance/runNotifications.php
should be run periodically.
It should be a number between 0
inclusive and 1
inclusive. The default value is 0.05
(1/20
).
$wgTimedNotifyRunDeferred
This configuration parameter determines whether to run the notifications in a deferred update. If this parameter is set to true
, notifications are calculated after a response has already been sent to the browser. This calculation will therefore not impact the load time of the wiki. Unless you have a specific reason to set this to false
, you generally should not have to change this.
It should be a boolean. The default value is true
.
$wgTimedNotifyPushedNotificationRetentionDays
This configuration parameter specifies the number of days to remember pushed notifications. In order to prevent duplicate notifications for the same event, TimedNotify keeps track for which events it has already sent out notifications in a database table. To prevent this table from growing extremely large, old notifications are occasionally purged from the table. This configuration options specifies the minimum age in days before a notification is purged from this table.
It should be an integer. The default value is 60
.
$wgTimedNotifyDisabledNotifiers
This configuration parameter specifies which notifiers to disable. It should be an array of booleans, where the key is the name of the notifier, and the value is true
to disable the notifier. By default, the array is empty and all notifiers are enabled.
It should be an array. The default value is []
.
Adding a new notification type
Adding a new time-based notification type is relatively easy and quite similar to adding a new notification type to Echo directly.
Introduction to how time-based notifications work
Time-based notifications are notifications that are not explicitly triggered by an action. This means that the calculation to determine which time-based notifications need to be sent out needs to happen periodically. How often this happens is based on $wgTimedNotifyRunRate
, which is explained above.
Since the notifications are (re-)calculated periodically, we need to make sure we do not push the same notification twice. To solve this, a notification can be annotated with a unique ID. The extension guarantees that a notification is only pushed if no notification with the same ID has been pushed before.
For example, suppose we want to create a notification that reminds a user that their moderation status is to expire in less than 30 days. The condition for this "event" is true for 30 days, but the notification should only be sent out once. Therefore, we push a notification annotated with the ID 2022-08-12-30d
, where 2022-08-12
is the expiration date of the user's moderation status. The expiration date will not change (and if it does, the notification should be sent out again when applicable), and therefore the notification will only be sent out once.
Notification definition
Each notifier must be defined in a class. This class must extend the base TimedNotify\Notifier
class, and may implement the following methods:
Method | Description |
---|---|
getName() |
The name of the notifier (should be unique). |
getPresentationModel() |
The class name of the presentation model. This corresponds directly to presentation models in Echo. |
getNotifications() |
An array of notifications that should be sent (see below for the format). |
getIcons() |
Additional icons to define. |
static getNotificationUsers(EchoEvent) |
The users that should be notified for the given EchoEvent. |
static getFilteredUsers(EchoEvent) |
The list of users that should not be notified for the given EchoEvent. |
Notification format
A notification should be an array with the following keys:
Key | Type | Description | |
---|---|---|---|
id |
string | A unique identifier for this notification (will automatically be scoped to the notifier). The notification will only be emitted if a notification with this key has not already been emitted. If this value is omitted, the notification will be emitted unconditionally. | <translate> Optional</translate> |
data |
array | Additional data to add to the notification. | <translate> Optional</translate> |
Notification registration
Notification registration happens in the method that corresponds to the TimedNotifyGetNotifierClasses
hook. This hook includes a single variable, &$notifierClasses
and contains a list of classes that extend Notifier
. You must add your new notifiers through this hook. For example:
public function onTimedNotifyGetNotifierClasses( array &$notifierClasses ): void {
$notifierClasses[] = MyCoolNotifier::class;
}
Walkthrough: Creating a new notification type
Suppose we want to create an extension that notifies the user whenever their moderation status is about to expire. This section will demonstrate how to build such an extension using TimedNotify. This section will omit some details of creating an extension, and will only focus on the parts that interact with TimedNotify.
Creating the class
We start by creating a new class that extends TimedNotify\Notifier
:
class ModerationStatusReminder extends Notifier {
public function getName(): string {
// This function can return basically anything, as long as it is unique
return "ModerationStatusReminder";
}
public function getPresentationModel(): string {
// This function should return the class name of the presentation model
// to use for this notification. This corresponds directly to the presentation
// model used by Echo. For details on how to create a presentation model,
// please refer to the Echo documentation:
// https://www.mediawiki.org/wiki/Extension:Echo/Creating_a_new_notification_type
return ModerationStatusReminderPresentationModel::class;
}
public function getNotifications(): array {
$config = MediaWikiServices::getInstance()->getMainConfig();
// Get a timestamp $wgModerationStatusReminderDays into the future
$future = time() + 60 * 60 * 24 * $config->get( 'ModerationStatusReminderDays' );
// Retrieve all the users that have an expiry time between now and $future
// (for this walkthrough, we will assume 'getSoonToExpire' builds an
// appropriate query and returns its result as an stdClass.
$result = $this->getSoonToExpire( $future );
// We now iterate over all users that have a moderation status that is
// soon to expire and create notifications for each user
$notifications = [];
foreach ( $result as $row ) {
$expirationTime = $row->ug_expiry;
$user = $row->ug_user;
// We build a unique ID for the notification
$notificationId = $user . '-' . $expirationTime;
$notifications[] = [
'id' => $notificationId,
'data' => [
// Include the user so we know who to notify
'user' => $user,
// Include the expiration time for visual purposes
'expirationTime' => $expirationTime
]
];
}
// Finally, we return the list of notifications we have constructed
return $notifications;
}
public static function getNotificationUsers( EchoEvent $event ): array {
// Since we already included which user to notify in the event, we
// can just return that user. For many other notification types,
// it is more convenient to calculate who to notify in this function
// directly, since many users may be notified for the same event.
return [User::newFromId( $event->getExtraParam( 'user' ) )];
}
}
Next, we implement the TimedNotifyGetNotifierClasses
to register our notifier:
public function onTimedNotifyGetNotifierClasses( array &$notifierClasses ): void {
$notifierClasses[] = ModerationStatusReminder::class;
}
Installation
- <translate> <tvar name=1>Download</tvar> and place the file(s) in a directory called <tvar name=name>
TimedNotify
</tvar> in your <tvar name=ext>extensions/
</tvar> folder.</translate> - <translate> Add the following code at the bottom of your <tvar name=1>LocalSettings.php </tvar> file:</translate>
wfLoadExtension( 'TimedNotify' );
- <translate> Run the [[<tvar name=update>Special:MyLanguage/Manual:Update.php</tvar>|update script]] which will automatically create the necessary database tables that this extension needs.</translate>
- File:OOjs UI icon check-constructive.svg <translate> Done</translate> – <translate> Navigate to <tvar name=special>Special:Version</tvar> on your wiki to verify that the extension is successfully installed.</translate>
- Pages with script errors
- Pages with broken file links
- Stable extensions
- Extensions without an image
- Hook extensions
- Extensions without a compatibility policy
- Extensions with manual MediaWiki version
- GPL licensed extensions
- Extensions in GitHub version control
- BeforeCreateEchoEvent extensions
- BeforeInitialize extensions
- LoadExtensionSchemaUpdates extensions
- All extensions
- Extensions not in ExtensionJson