Article Notifications

The Notification Service is made up of two parts the notification type and the notification distribution type. The INotificationType defines your notification and allows you to setup the name, permissions and message. When a notification event is triggered then the INotificationDistributionType is responsible for sending the notification via a particular medium. Out of the box Verint Community provides two distribution types, the email and socket (toast) distribution types.

Why Should I Create a Notification Type?

New notification types are useful when you want to notify community members about specific events in the community, either events that occur with core functionality or because of new functionality added to the platform.

Why Should I Create a Notification Distribution Type?

Besides sending a notification via email or toast notification a new distribution type can send a notification through another medium. For example you could setup a notification distribution using a text messaging platform. Defining a distribution type is out of scope for this document.

Creating an INotificationType

To add support for sending notifications through Verint Community the INotificationType plugin must be implemented. The INotificationType interface extends IPlugin and the following assemblies must also be referenced in your plugin Telligent.Evolution.Components.dll, Telligent.Evolution.Api and Telligent.Evolution.Core.

When creating a NotificationType plugin use an event handler to create a notification. For instance, if a user comments on content an event handler can be created for the event in the plugin's Initialize method. Then the notification can be sent by using the controller.

private void CommentEvents_AfterCreate(CommentAfterCreateEventArgs e)
{
    if (e == null
        || e.Content == null
        || e.User == null
        || !e.IsApproved
        || !e.Content.CreatedByUserId.HasValue
        || e.CommentTypeId.GetValueOrDefault(Guid.Empty) != Guid.Empty)
    {
        return;
    }

    var comment = Apis.Get<IComments>().Get(e.CommentId);
    if (comment != null && comment.IsApproved)
    {
        AddNotifications(e.Content.ContentId, e.Content.ContentTypeId, e.CommentId, e.Content.CreatedByUserId.Value, e.UserId);
    }
}

When SetController is called the plugin's instance of the notification controller is passed to the INotificationType. This is a crucial part of the notification plugin. If you do not define a private INotificationController seen here as _notificationController you cannot send notifications.

public void SetController(INotificationController controller)
{
    _notificationController = controller;
}

In the AddNotifications method the INotificationController is used. When the CreateUpdate method is called one of two processes will occur. If a notification matching the ContentId and ContentTypeId exists it will modify the notification accordingly. If the notification does not exist, it will create one.

Alternatively there is also a Delete method in the INotificationController, this can be used if a BeforeDelete event handler is implemented.

It is also possible to add ExtendedAttibutes if you need to pass other information when rendering the message. In this example we define the content id and type to be used later in the GetTargetUrl method and save the comment id as an ExtendedAttribute so it can later be used in GetMessage.

private void AddNotifications(Guid contentId, Guid contentTypeId, Guid commentId, int contentAuthor, int actorId)
{
    var comment = Apis.Get<IComments>().Get(commentId);
    if (comment == null) { return; }

    var attributes = new List<IExtendedAttribute>
    {
        new ExtendedAttribute {Key = "TargetCommentId", Value = comment.CommentId.ToString("N")}
    };

    _notificationController.CreateUpdate(new NotificationCreateUpdateOptions
    {
        ContentId = contentId,
        ContentTypeId = contentTypeId,
        LastUpdate = DateTime.UtcNow,
        UserId = contentAuthor,
        ActorIdToAdd = actorId,
        ExtendedAttributes = attributes
    });
}

 Begin by choosing a name and description for the notification type, this is different than the plugin name. It will be displayed in the user's settings page under the Notification's tab.

public string NotificationTypeName
{
    get { return "Notification Sample"; }
}

public string NotificationTypeDescription
{
    get { return "Sends a sample notification."; }
}

The NotificationTypeId is the unique ID for the notification type.

public Guid NotificationTypeId
{
    get { return new Guid("3C0542FD-5AB0-4386-911C-86CCF530BAEE"); }
}

To tell the system if the notification data can and should be cached (for 5 seconds) the IsCacheable property is used. The other cache property, VaryCacheByUser, tells the system if the notification data should be cached according to each user.

public bool IsCacheable
{
    get { return true; }
}

public bool VaryCacheByUser
{
    get { return true; }
}

It is necessary to define if a user can delete a notification. This value tells the system if the user attempting to delete the notification is allowed to do so. In this sample, deleting is allowed only by the recipient which is defined by the UserId in the notification.

public bool CanDeleteNotification(Guid notificationId, int userId)
{
    var notification = PublicApi.Notifications.Get(notificationId);
    return notification != null && notification.UserId == userId;
}

The GetMessage returns a string value of the message being sent to the user. Here the ExtendedAttributes can be accessed, the information was passed in from the create notification in CommentEvents_AfterCreate.

public string GetMessage(Guid notificationId, string target)
{
    var notification = Apis.Get<INotifications>().Get(notificationId);
    if (notification == null || notification.ExtendedAttributes == null) { return null; }

    var commentIdAttribute = notification.ExtendedAttributes.FirstOrDefault(ea => ea.Key == "TargetCommentId");
    if (commentIdAttribute == null) { return null; }

    var commentId = Guid.Parse(commentIdAttribute.Value);
    var comment = Apis.Get<IComments>().Get(commentId);
    if (comment == null) { return null; }

    var user = Apis.Get<IUsers>().Get(new UsersGetOptions { Id = notification.UserId });
    if (user == null) { return null; }

    return string.Format("{0}, added a comment \"{1}\"", user.DisplayName, comment.Body());
}

The GetTargetUrl will contain the URL where the content is located and returns a string value of the location. In this sample the comment contains the content Id and type that it is associated with.

public string GetTargetUrl(Guid notificationId)
{
    var notification = Apis.Get<INotifications>().Get(notificationId);
    return notification != null && notification.Content != null ? notification.Content.Url : null;
}

Here is the complete sample.

using System;
using System.Collections.Generic;
using System.Linq;
using Telligent.Evolution.Extensibility;
using Telligent.Evolution.Extensibility.Api.Entities.Version1;
using Telligent.Evolution.Extensibility.Api.Version1;
using Telligent.Evolution.Extensibility.Content.Version1;

namespace Samples
{
    public class MyNotifications : INotificationType
    {
        private INotificationController _notificationController;

        #region IPlugin

        public string Name
        {
            get { return "My Notifications Plugin"; }
        }

        public string Description
        {
            get { return "This plugin will demo how the INotificationType plugin works"; }
        }

        public void Initialize()
        {
            Apis.Get<IComments>().Events.AfterCreate += CommentEvents_AfterCreate;
        }
        
        #endregion
        
        #region INotificationType
        
        public string NotificationTypeName
        {
            get { return "Notification Sample"; }
        }

        public string NotificationTypeDescription
        {
            get { return "Sends a sample notification."; }
        }

        public string NotificationTypeCategory
        {
            get { return "Samples"; }
        }

        public Guid NotificationTypeId
        {
            get { return new Guid("3C0542FD-5AB0-4386-911C-86CCF530BAEE"); }
        }

        public bool IsCacheable
        {
            get { return true; }
        }

        public bool VaryCacheByUser
        {
            get { return true; }
        }

        public bool CanDeleteNotification(Guid notificationId, int userId)
        {
            var notification = Apis.Get<INotifications>().Get(notificationId);
            return notification != null && notification.UserId == userId;
        }

        public string GetMessage(Guid notificationId, string target)
        {
            var notification = Apis.Get<INotifications>().Get(notificationId);
            if (notification == null || notification.ExtendedAttributes == null) { return null; }

            var commentIdAttribute = notification.ExtendedAttributes.FirstOrDefault(ea => ea.Key == "TargetCommentId");
            if (commentIdAttribute == null) { return null; }

            var commentId = Guid.Parse(commentIdAttribute.Value);
            var comment = Apis.Get<IComments>().Get(commentId);
            if (comment == null) { return null; }

            var user = Apis.Get<IUsers>().Get(new UsersGetOptions { Id = notification.UserId });
            if (user == null) { return null; }

            return string.Format("The following comment was added by {0}: \"{1}\"", user.DisplayName, comment.Body());
        }
        
        public string GetTargetUrl(Guid notificationId)
        {
            var notification = Apis.Get<INotifications>().Get(notificationId);
            return notification != null && notification.Content != null ? notification.Content.Url : null;
        }

        public void SetController(INotificationController controller)
        {
            _notificationController = controller;
        }

        #endregion

        private void CommentEvents_AfterCreate(CommentAfterCreateEventArgs e)
        {
            if (e == null
                || e.Content == null
                || e.User == null
                || !e.IsApproved
                || !e.Content.CreatedByUserId.HasValue
                || e.CommentTypeId.GetValueOrDefault(Guid.Empty) != Guid.Empty)
            {
                return;
            }

            var comment = Apis.Get<IComments>().Get(e.CommentId);
            if (comment != null && comment.IsApproved)
            {
                AddNotifications(e.Content.ContentId, e.Content.ContentTypeId, e.CommentId, e.Content.CreatedByUserId.Value, e.UserId);
            }
        }

        private void AddNotifications(Guid contentId, Guid contentTypeId, Guid commentId, int contentAuthor, int actorId)
        {
            var comment = Apis.Get<IComments>().Get(commentId);
            if (comment == null) { return; }

            var attributes = new List<IExtendedAttribute>
            {
                new ExtendedAttribute {Key = "TargetCommentId", Value = comment.CommentId.ToString("N")}
            };

            _notificationController.CreateUpdate(new NotificationCreateUpdateOptions
            {
                ContentId = contentId,
                ContentTypeId = contentTypeId,
                LastUpdate = DateTime.UtcNow,
                UserId = contentAuthor,
                ActorIdToAdd = actorId,
                ExtendedAttributes = attributes
            });
        }

    }
}

In this sample the notification is displayed when a comment is created. To enable the plugin, in the administration panel, navigate to the Notifications menu and find the "My Notifications Plugin" check Enabled and click Save. Create a comment and notice the toast notification.

This is a sample of the toast notification.