Notifications API

Last modified by Admin on 2024/10/01 00:39

bellHandle notifications
TypeJAR
CategoryAPI
Developed by

XWiki Development Team

Rating
LicenseGNU Lesser General Public License 2.1
Bundled With

XWiki Standard

Installable with the Extension Manager

Description

Module that get the notifications to display to a user and handle the notification statuses.

Script Service

This module introduces a script service available like this:

$services.notifications

See the code with the available methods.

Tutorial: How to send notifications

Principle

In XWiki, a notification is simply an event sent by some application, stored by the Event Stream Module and then displayed in the notifications menu.

Recordable Event

In order to indicate that the event you send through the Observation Manager must be stored by the Event Stream, the event must implement the RecordableEvent interface.

Example: 

package org.xwiki.platform.blog.events;

import org.xwiki.eventstream.RecordableEvent;

/**
 * Event sent when a blog post has been published.
 *
 * @version $Id: c89002f510111a9e9162394fa35fc811d50eb482 $
 * @since 9.2RC1
 */

public class BlogPostPublishedEvent implements RecordableEvent
{
   @Override
   public boolean matches(Object otherEvent)
   {
       return otherEvent instanceof BlogPostPublishedEvent;
   }
}

Note: you can also create and send an "Untyped Event" without coding anything with some special XObjects.

Targetable Event

If you want that only some users or some groups receive the notification (by default it is everybody), your event need to implement the TargetableEvent interface. Then, you can specify a list of targets which are the ids of the users or the groups.
Note that if you don't specify a list of targets, your event will be considered as a standard RecordableEvent so it will be sent to all users watching for this event.

XWiki <12.3  only users could be targeted with Targetable events, the support of group has been added in XWiki 12.3RC1.

XWiki 12.8+  those events are not concerned anymore by the inclusive notification filters. It means that for those events, even if the user is not actively watching the location from were the event is sent, he will receive the notifications. However, if the user created an exclusive filter, this filter will be honoured and the notification won't be received.

Recordable Event Converter

You can customize the way an event is stored by the Event Stream by implementing your own RecordableEventConverter.

The role of this component is to convert an event from the Observation Module to an event from the Event Stream Module, that the event stream can store. If you want to store custom parameters, you need to create your own converter.

By default, the DefaultRecordableEventConverter is used.

Before 12.6 or if you use the hibernate event store, the parameters you put in an event from a custom converter are not stored anymore (see XWIKI-7520). A workaround available since XWiki 12.1RC1, is to use a custom LegacyEventConverter to use the parameters1 to 5 from LegacyEvent, in order to store some specific information.

Recordable Event Descriptor

In order to be see the notification, a user must enable the notification for the event type you have created. To be listed in the user notification settings, you must provide a RecordableEventDescriptor, which is a component that give some description about your event type to the users.

Example:

package org.xwiki.platform.blog.events;

import javax.inject.Named;
import javax.inject.Singleton;

import org.xwiki.component.annotation.Component;
import org.xwiki.eventstream.RecordableEventDescriptor;

/**
 * Descriptor for the event {@link BlogPostPublishedEvent}.
 *
 * @version $Id: 6f28643529fd7f1bb4477afa7c9117f75cc2b84c $
 * @since 9.2RC1
 */

@Component
@Singleton
@Named("BlogPostPublishedEvent")
public class BlogPostPublishedEventDescriptor implements RecordableEventDescriptor
{
   @Override
   public String getEventType()
   {
       return BlogPostPublishedEvent.class.getCanonicalName();
   }

   @Override
   public String getApplicationName()
   {
       return "blog.applicationName";
   }

   @Override
   public String getDescription()
   {
       return "blog.events.blogpostpublished.description";
   }

   @Override
   public String getApplicationIcon()
   {
       return "rss";
   }
}

Notification Displayer

By default, all notifications are displayed using the notification/default.vm template.

## Default renderer for any kind of notification.
## Each application should create its own.
## The variable "$event" contains the event to display.
## See: org.xwiki.notifications.CompositeEvent
#template('notification/macros.vm')
#set ($app  = "$!event.events.get(0).application")
#set ($icon = 'bell')
#if ($app == 'xwiki')
 #set ($app = $services.localization.render('core.events.appName'))
 #set ($icon = 'file')
#end
#displayNotificationEvent($icon, $app, $event)

You can override this template for your event type by creating a template called notification/{canonical name of your event class}.vm

Example: notification/org.xwiki.platform.blog.events.BlogPostPublishedEventDescriptor.vm.

Note that before XWiki 12.2 it wasn't possible to inject resources with the Skin Extension Plugin in those templates.

An other way to provide a custom displayer is to create a component that implement the NotificationDisplayer role.

Example:

/**
 * Implement a {@link NotificationDisplayer} for the event type
 * {@link org.xwiki.platform.blog.events.BlogPostPublishedEvent}.
 *
 * @version $Id: ebf9735007f36e701c2ed07420bc972b56da7982 $
 * @since 9.2RC1
 */

@Component
@Singleton
@Named(BlogNotificationDisplayer.EVENT_TYPE)
public class BlogNotificationDisplayer implements NotificationDisplayer
{
   /**
     * Name of the event type that this displayer handle.
     */

   public static final String EVENT_TYPE = "org.xwiki.platform.blog.events.BlogPostPublishedEvent";

   private static final List<String> EVENTS = Arrays.asList(EVENT_TYPE);

   @Override
   public Block renderNotification(CompositeEvent eventNotification) throws NotificationException
   {
       // custom code here
   }

   @Override
   public List<String> getSupportedEvents()
   {
       return EVENTS;
   }
}

Note that the renderNotification method takes a CompositeEvent: this is because the events are not rendered alone but by group of similar events. This allows for example to display only one notification for all likes on a page.

Example of code to send a notification

observationManager.notify(new BlogPostPublishedEvent(), "org.xwiki.platform:xwiki-platform-blog-api", document);

Example of code that send a notification when a blog post is saved

package org.xwiki.platform.blog.internal;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import org.xwiki.bridge.event.DocumentCreatedEvent;
import org.xwiki.bridge.event.DocumentUpdatedEvent;
import org.xwiki.component.annotation.Component;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.model.reference.SpaceReference;
import org.xwiki.observation.AbstractEventListener;
import org.xwiki.observation.ObservationManager;
import org.xwiki.observation.event.Event;
import org.xwiki.platform.blog.events.BlogPostPublishedEvent;

import com.xpn.xwiki.doc.XWikiDocument;
import com.xpn.xwiki.objects.BaseObject;

/**
 * Send the {@link BlogPostPublishedEvent} when a blog post is published.
 *
 * @version $Id: 6a3f33b7e415f7d967865024087d755448db2dc9 $
 * @since 9.2RC1
 */

@Component
@Singleton
@Named(BlogDocumentSavedListener.NAME)
public class BlogDocumentSavedListener extends AbstractEventListener
{
   /**
     * Name of the listener.
     */

   public static final String NAME = "Blog Document Saved Listener";

   @Inject
   private ObservationManager observationManager;

   /**
     * Construct a BlogDocumentSavedListener.
     */

   public BlogDocumentSavedListener()
   {
       super(NAME, new DocumentCreatedEvent(), new DocumentUpdatedEvent());
   }

   @Override
   public void onEvent(Event event, Object source, Object data)
   {
        XWikiDocument document = (XWikiDocument) source;

       // Send a BlogPostPublishedEvent if the blog post is published but was not before
       final DocumentReference blogPostClass = new DocumentReference("BlogPostClass", new SpaceReference("Blog",
                document.getDocumentReference().getWikiReference()));

        BaseObject blogPost         = document.getXObject(blogPostClass);
        BaseObject previousBlogPost = document.getOriginalDocument().getXObject(blogPostClass);

       if (isPublished(blogPost) && !isPublished(previousBlogPost)) {
            observationManager.notify(new BlogPostPublishedEvent(), "org.xwiki.platform:xwiki-platform-blog-api",
                    document);
       }
   }

   private boolean isPublished(BaseObject blogPost)
   {
       return blogPost != null && blogPost.getIntValue("published") == 1 && blogPost.getIntValue("hidden") == 0;
   }
}

Notification Email Renderer

By default, all notifications are displayed using the notification/email/default.html.vm and notification/email/default.plain.vm templates.

You can override these templates for your event type by creating templates called notification/email/{canonical name of your event class}.html.vm and notification/email/{canonical name of your event class}.plain.vm, respectively for emails clients that handle HTML and emails clients that do not.

An other way to provide a custom email renderer is to create a component that implement the NotificationEmailRenderer role.

@Component
@Singleton
@Named("canonical name of your event class")
public class CustomNotificationEmailRenderer implements NotificationEmailRenderer
{
 @Override
 public String renderHTML(CompositeEvent compositeEvent) throws NotificationException
 {
   return "a string containing HTML code for email clients that handle HTML";
 }

 @Override
 public String renderPlainText(CompositeEvent compositeEvent) throws NotificationException
 {
   return "a plain text string for email clients that do not handle HTML";
 }

 @Override
 public String generateEmailSubject(CompositeEvent compositeEvent) throws NotificationException
 {
   // not used yet
   return "a plain text string for the email's subject, when a notification is sent on real-time";
 }
}

Tutorial: How to create your own notification filter

This part is still unstable and not heavily tested.

On the Java-side, you can implement your own component for the role org.xwiki.notifications.NotificationFilter.

On the wiki-side, you must also create an UI Extension with the Extension Point ID "notification.profile.filter" and the name should be the name of the event you want to filter.

Create custom notification templates

Since XWiki 9.6RC1, an administrator can create custom notifications templates directly through wiki pages. This is done by using the NotificationDisplayerClass XObject.

In order to demonstrate how this system works, we will use this very XObject to define a custom template for "update" notifications.

The NotificationDisplayerClass XObject takes two properties : 

  • Event Type : The type of event that should be overridden
  • Notification Template : The notification template that should be used instead of the default one

NotificationDisplayerClassXObjectExample.png

Currently, the Event type property of the NotificationDisplayerClass can be found in the descriptors of the events for which you want to override the notification template. For example, the event type of the event DocumentUpdatedEvent is defined in the DocumentUpdatedEventDescriptor class as "update".

With the object described above, here is the result in the notification center (the two images represent how the notifications are displayed before and after defining the XObject).

For emails

Since XWiki 9.11.1, you can do the same for email notifications.

You need to use the class NotificationEmailRendererClass which takes four properties : 

  • Event Type : The type of event that should be overridden
  • HTML Template : The template used for the HTML version of the email
  • Plain text Template : The template used for the plain text version of the email
  • Email Subject Template : The template used to generate the email subject (not used yet)

NotificationEmailRendererClassXObjectExample.png

Create your own Notification Filter

Developers can create their own Notification Filter. For that, they need to create their own implementation of NotificationFilter

Prerequisites & Installation Instructions

We recommend using the Extension Manager to install this extension (Make sure that the text "Installable with the Extension Manager" is displayed at the top right location on this page to know if this extension can be installed with the Extension Manager).

You can also use the manual method which involves dropping the JAR file and all its dependencies into the WEB-INF/lib folder and restarting XWiki.

Dependencies

Dependencies for this extension (org.xwiki.platform:xwiki-platform-notifications-api 16.8.0):

Get Connected