Building a Real-Time Notification System in MediaWiki Using Extension Hooks and WebSockets



This content originally appeared on DEV Community and was authored by HexShift

Introduction

MediaWiki provides an excellent foundation for collaborative publishing, but it lacks native real-time functionality. Editors must manually refresh pages to check for changes, and administrators are not immediately notified of critical edits or vandalism. This article demonstrates how to build a real-time notification system for MediaWiki using extension hooks, a message broker like Redis, and a WebSocket server. The implementation remains modular and decoupled, enabling flexible integration and scaling.

Why Real-Time Notifications Matter in MediaWiki

In collaborative environments such as knowledge bases, wiki farms, or moderated documentation portals, visibility into changes as they occur is critical. While MediaWiki includes the Echo extension for notifications, it does not provide real-time push functionality. Instead, Echo uses a polling model or relies on email, which introduces latency and reduces responsiveness.

By integrating a backend message queue with WebSocket delivery, we can push immediate updates to connected users when changes happen on the wiki.

System Overview

This solution consists of three major components:

  • A MediaWiki extension that listens to hooks like PageContentSaveComplete and emits update events.
  • A message broker such as Redis, which serves as a relay point.
  • A WebSocket server that listens for messages and pushes them to clients in real time.

This design pattern decouples the wiki from the real-time delivery layer, improving maintainability and performance.

Creating the MediaWiki Extension

Begin by creating a new extension folder in your MediaWiki installation, for example RealTimeNotify. Inside this folder, create an extension.json file to define metadata, classes, and hooks.

Set the type to extension, and under Hooks, declare the hook handler for PageContentSaveComplete. In AutoloadClasses, link your PHP class such as RealTimeNotifyHooks to a path like includes/RealTimeNotifyHooks.php.

In your hook class, define a static method called onPageContentSaveComplete. This hook provides access to the edited WikiPage, the User, and other contextual information. Capture the page title using $wikiPage->getTitle()->getPrefixedText() and get the editor’s username with $user->getName().

Creating the JSON Message Payload

To push an update to the broker, convert the event into a JSON object with keys such as page_title, user_name, timestamp, and summary. Use the PHP json_encode() function to serialize the array.

For example:

$data = [ 'page_title' => $title, 'user_name' => $username, 'timestamp' => wfTimestamp( TS_ISO_8601, time() ), 'summary' => $summary ];

Then:

$message = json_encode($data);

This message will be forwarded to the broker.

Connecting to Redis

MediaWiki does not ship with built-in support for Redis publishing, so install a PHP Redis client such as phpredis or predis/predis. Once installed, use the Redis client inside your hook:

$redis = new Redis();

$redis->connect('127.0.0.1', 6379);

$redis->publish('mediawiki:notifications', $message);

This line will publish the edit event to a channel named mediawiki:notifications, which external subscribers can listen to.

If you prefer reliability and delivery acknowledgment, RabbitMQ is a suitable alternative, using the PHP php-amqplib/php-amqplib library.

WebSocket Server with Node.js

On the subscriber side, run a WebSocket server that connects to Redis and broadcasts messages. One of the simplest implementations uses Node.js and the ws and ioredis modules.

Set up a WebSocket server on port 8080 and connect to the Redis channel. When a message is published to mediawiki:notifications, the server reads it and sends it to all connected clients.

In the handler, iterate over all clients in wss.clients, and for each one that is open, call client.send(message).

This approach allows the WebSocket server to act as a translator between Redis events and browser-based JavaScript listeners.

JavaScript Frontend Integration

Create a JavaScript file in your extension, such as modules/ext.realtimenotify.js. This script should be loaded through ResourceLoader.

Inside the script, open a WebSocket connection to the server:

const socket = new WebSocket('ws://yourdomain.com:8080');

Add a listener:

socket.onmessage = function (event) { const data = JSON.parse(event.data); mw.notify("Page edited: " + data.page_title + " by " + data.user_name, { autoHide: true }); };

This displays a popup notification to the user when a change is pushed from the backend.

In extension.json, register this file under ResourceModules and ensure it loads conditionally based on user preferences.

Adding a User Preference

Not all users may want real-time notifications. You can make it configurable using the GetPreferences hook.

Inside your extension class, implement onGetPreferences. Add a boolean toggle named enable_realtime_notify.

Then, in BeforePageDisplay, check the user’s preference:

if ( $user->getOption( 'enable_realtime_notify' ) ) { $out->addModules( 'ext.realtimenotify' ); }

This ensures that only users who have opted in receive notifications and frontend load.

Logging and Debugging

To assist with troubleshooting, add a custom log group in LocalSettings.php:

$wgDebugLogGroups['realtimenotify'] = '/var/log/mediawiki/realtimenotify.log';

Then use wfDebugLog in your hook:

wfDebugLog('realtimenotify', "Notification sent for " . $data['page_title']);

To debug Redis, use the command-line client: redis-cli SUBSCRIBE mediawiki:notifications

This confirms whether messages are published as expected.

Authentication and Security

When exposing a WebSocket server, always consider access control. For private wikis, you may want to authenticate WebSocket connections via short-lived tokens or signed URLs.

A simple token can be generated server-side and passed via a query parameter:

const socket = new WebSocket('ws://yourdomain.com:8080?token=xyz');

The WebSocket server should validate the token against the MediaWiki session or via a custom API call.

Avoid sending private user information in payloads. Sanitize summaries and usernames before broadcasting. For additional protection, consider using HTTPS with a reverse proxy that terminates TLS and performs access control.

Scaling the Architecture

For high-traffic wikis, you may need to scale components independently:

  • Use Redis Cluster or Sentinel for high availability
  • Run multiple WebSocket server instances and load balance them with Nginx or HAProxy
  • Persist messages temporarily in queues if no clients are connected
  • Integrate analytics or logging services to monitor delivery latency

RabbitMQ with durable queues and dead-lettering provides more reliability than Redis pub/sub. Choose the broker based on your tolerance for message loss and operational complexity.

Limitations

This system does not guarantee delivery unless a reliable queue system is used. Redis pub/sub drops messages if no subscriber is listening. The frontend implementation uses the browser’s native WebSocket interface and may not reconnect automatically on failure unless explicitly coded.

Also note that browser notifications via mw.notify are transient and not stored. For persistent alerts, you would need to integrate this system with the Echo extension or a database-backed log.

Conclusion

Real-time notifications can greatly improve the responsiveness and interactivity of your MediaWiki installation. By combining core extension hooks with Redis, WebSockets, and simple JavaScript, you can enable instant feedback for editors and admins alike. This system is especially useful in dynamic or moderated wikis where knowing about changes the moment they happen is essential.

If you want to go deeper into advanced MediaWiki extension development — including job queues, caching strategies, hook orchestration, and deployment tooling — check out Mastering MediaWiki Extensions: Beyond the Manual. This expert-level guide explains how to push MediaWiki beyond its defaults and build scalable, powerful functionality that goes far beyond what the manual provides.


This content originally appeared on DEV Community and was authored by HexShift