Skip to content

Instantly share code, notes, and snippets.

@jstemerdink
Last active February 28, 2023 10:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jstemerdink/4bd655f7edd40954701f5f47254aae72 to your computer and use it in GitHub Desktop.
Save jstemerdink/4bd655f7edd40954701f5f47254aae72 to your computer and use it in GitHub Desktop.
Using the new EPiServer notification API

Using the new EPiServer notification API

An example of how you can use the new notification API within EPiServer to notify admins that are online that something has gone wrong.

Blogpost can be found here

Powered by ReSharper image

using System.Collections.Generic;
using System.Linq;
using System.Web.Security;
using EPiServer.ContentCollaboration.Notification;
using EPiServer.Notification;
using EPiServer.PlugIn;
using EPiServer.Scheduler;
using EPiServer.ServiceLocation;
[ScheduledPlugIn(DisplayName = "ScheduledNotificationTest")]
public class ScheduledNotificationTest : ScheduledJobBase
{
private bool stopSignaled;
public ScheduledNotificationTest()
{
this.IsStoppable = true;
}
protected Injected<INotifier> Notifier { get; set; }
protected Injected<INotificationProvider> NotificationProvider { get; set; }
protected Injected<INotififcation> Notififcation { get; set; }
protected Injected<IUserNotificationRepository> UserNotificationRepository { get; set; }
/// <summary>
/// Called when a user clicks on Stop for a manually started job, or when ASP.NET shuts down.
/// </summary>
public override void Stop()
{
this.stopSignaled = true;
}
/// <summary>
/// Called when a scheduled job executes
/// </summary>
/// <returns>A status message to be stored in the database log and visible from admin mode</returns>
public override string Execute()
{
//Call OnStatusChanged to periodically notify progress of job for manually started jobs
this.OnStatusChanged(string.Format("Starting execution of {0}", this.GetType()));
//For long running jobs periodically check if stop is signaled and if so stop execution
if (this.stopSignaled)
{
//this.SendMessage("Stop of job was called");
return "Stop of job was called";
}
this.SendMessage("Test notification sent.");
return "Completed";
}
private async void SendMessage(string message)
{
string[] usersInRole = Roles.GetUsersInRole("Administrators");
INotificationUser notificationSender = new NotificationUser("admin");
List<INotificationUser> notificationReceivers = (from sUser in usersInRole
select Membership.GetUser(sUser)
into user
where user != null
select new NotificationUser(user.UserName))
.Cast<INotificationUser>().ToList();
SlackNotifier notifier = new SlackNotifier();
await
notifier.PostNotificationAsync(
new NotificationMessage()
{
ChannelName = SlackFormatter.ChannelName,
Content = message,
Subject = "Scheduled job",
Recipients = notificationReceivers,
Sender = notificationSender,
TypeName = SlackMessageProvider.Name
}
);
await
this.Notifier.Service.PostNotificationAsync(
new NotificationMessage
{
ChannelName = SlackFormatter.ChannelName,
Content = message,
Subject = "Scheduled job",
Recipients = notificationReceivers,
Sender = notificationSender,
TypeName = SlackMessageProvider.Name
}
);
}
}
using System.Collections.Generic;
using EPiServer.Notification;
using EPiServer.ServiceLocation;
/// <summary>
/// Class SlackFormatter.
/// </summary>
/// <seealso cref="EPiServer.Notification.INotificationFormatter" />
/// <seealso cref="EPiServer.Notification.IUserNotificationFormatter" />
[ServiceConfiguration(typeof(IUserNotificationFormatter))]
[ServiceConfiguration(typeof(INotificationFormatter))]
public class SlackFormatter : INotificationFormatter, IUserNotificationFormatter
{
/// <summary>
/// The channel name
/// </summary>
public const string ChannelName = "epi.slack";
/// <summary>
/// The name of the formatter.
/// </summary>
/// <value>The name of the formatter.</value>
public string FormatterName
{
get
{
return "slackformatter";
}
}
/// <summary>
/// Specifies which channels the formatter supports.
/// </summary>
/// <value>The supported channel names.</value>
public IEnumerable<string> SupportedChannelNames
{
get
{
return new[] { ChannelName };
}
}
/// <summary>
/// Performs formatting of messages.
/// </summary>
/// <param name="notifications">Messages to format</param>
/// <param name="recipient">The receiver of the message</param>
/// <param name="format">The format to format to</param>
/// <param name="channelName">The message channel</param>
/// <returns>A list of formatted messages</returns>
/// <remarks>One use case for a formatter might be to combine several messages into one.</remarks>
public IEnumerable<FormatterNotificationMessage> FormatMessages(
IEnumerable<FormatterNotificationMessage> notifications,
string recipient,
NotificationFormat format,
string channelName)
{
// we do not want to change the messages, so we just return them as they are
return notifications;
}
/// <summary>
/// Formats the user message.
/// </summary>
/// <param name="notification">The notification.</param>
/// <returns>UserNotificationMessage.</returns>
public UserNotificationMessage FormatUserMessage(UserNotificationMessage notification)
{
if (notification != null)
{
notification.Content = "<div style=\"color:#f7542b\">" + notification.Content + "</div>";
}
return notification;
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using EPiServer.Notification;
using EPiServer.ServiceLocation;
using Slack.Webhooks;
/// <summary>
/// Class SlackMessageProvider.
/// </summary>
/// <seealso cref="EPiServer.Notification.INotificationProvider" />
/// <seealso cref="EPiServer.Notification.INotificationProviderStatus" />
[ServiceConfiguration(typeof(INotificationProvider))]
public class SlackMessageProvider : INotificationProvider, INotificationProviderStatus
{
/// <summary>
/// The name
/// </summary>
public const string Name = "Slack";
/// <summary>
/// The slack client
/// </summary>
private readonly SlackClient slackClient =
new SlackClient("https://your.webhook");
/// <summary>
/// Gets the name of the provider.
/// </summary>
/// <value>The name of the provider.</value>
public string ProviderName
{
get
{
return Name;
}
}
/// <summary>
/// Specifie the format the provider supports.
/// </summary>
/// <returns>Supported format.</returns>
public NotificationFormat GetProviderFormat()
{
return new NotificationFormat { MaxLength = null, SupportsHtml = false };
}
/// <summary>
/// Sends the formatted messages.
/// </summary>
/// <param name="messages">The messages to send.</param>
/// <param name="succeededAction">A success action that should be called for successfully sent messages.</param>
/// <param name="failedAction">A failure action that should be called when a message send operation fails.</param>
public void Send(
IEnumerable<ProviderNotificationMessage> messages,
Action<ProviderNotificationMessage> succeededAction,
Action<ProviderNotificationMessage, Exception> failedAction)
{
IEnumerable<ProviderNotificationMessage> notificationMessages =
messages as IList<ProviderNotificationMessage> ?? messages.ToList();
foreach (ProviderNotificationMessage message in notificationMessages)
{
try
{
SlackMessage slackMessage = new SlackMessage
{
Channel = "#yourchannel",
Text = message.Content,
IconEmoji = null,
Username = "EPi Notification",
Mrkdwn = false
};
this.slackClient.Post(slackMessage);
if (succeededAction != null)
{
succeededAction(message);
}
}
catch (Exception e)
{
// if the post fails call the failedAction
if (failedAction == null)
{
continue;
}
failedAction(message, e);
}
}
}
/// <summary>
/// Specifies whether or not the provider is disabled
/// </summary>
/// <value><c>true</c> if this instance is disabled; otherwise, <c>false</c>.</value>
public bool IsDisabled
{
get
{
return false;
}
}
/// <summary>
/// The reason that the provider is disabled
/// </summary>
/// <value>The disabled reason.</value>
public string DisabledReason
{
get
{
return "No slack";
}
}
}
using System;
using System.Threading.Tasks;
using EPiServer.Notification;
using Slack.Webhooks;
/// <summary>
/// Class SlackNotifier.
/// </summary>
/// <seealso cref="EPiServer.Notification.INotifier" />
/// <author>Jeroen Stemerdink</author>
public class SlackNotifier : INotifier
{
private readonly SlackClient slackClient =
new SlackClient("https://your.webhook");
/// <summary>
/// Sends a message. The delivery of the notification depends on user settings, which
/// <see cref="T:EPiServer.Notification.INotificationFormatter" /> and
/// <see cref="T:EPiServer.Notification.INotificationProvider" /> that are registered.
/// </summary>
/// <param name="notification">The message to send.</param>
public async Task PostNotificationAsync(NotificationMessage notification)
{
SlackMessage slackMessage = new SlackMessage
{
Channel = "#yourchannel",
Text = notification.Content,
IconEmoji = null,
Username = "EPi Notification",
Mrkdwn = false
};
await this.slackClient.PostAsync(slackMessage);
}
/// <summary>Occurs when a message is notified.</summary>
public event EventHandler<NotificationEventArgs> NotificationPosted;
/// <summary>
/// Occurs when a message is saved.
/// </summary>
public event EventHandler<NotificationEventArgs> NotificationSaved;
/// <summary>
/// Occurs when a message is filtered.
/// </summary>
public event EventHandler<NotificationEventArgs> NotificationFiltered;
/// <summary>
/// Handles the <see cref="E:NotificationPosted" /> event.
/// </summary>
/// <param name="e">The <see cref="NotificationEventArgs" /> instance containing the event data.</param>
protected virtual void OnNotificationPosted(NotificationEventArgs e)
{
EventHandler<NotificationEventArgs> handler = this.NotificationPosted;
if (handler != null)
{
handler(this, e);
}
}
/// <summary>
/// Handles the <see cref="E:NotificationSaved" /> event.
/// </summary>
/// <param name="e">The <see cref="NotificationEventArgs" /> instance containing the event data.</param>
protected virtual void OnNotificationSaved(NotificationEventArgs e)
{
EventHandler<NotificationEventArgs> handler = this.NotificationSaved;
if (handler != null)
{
handler(this, e);
}
}
/// <summary>
/// Handles the <see cref="E:NotificationFiltered" /> event.
/// </summary>
/// <param name="e">The <see cref="NotificationEventArgs" /> instance containing the event data.</param>
protected virtual void OnNotificationFiltered(NotificationEventArgs e)
{
EventHandler<NotificationEventArgs> handler = this.NotificationFiltered;
if (handler != null)
{
handler(this, e);
}
}
}
using EPiServer;
using EPiServer.Core;
using EPiServer.DataAbstraction;
using EPiServer.Framework;
using EPiServer.Framework.Initialization;
using EPiServer.Notification;
using EPiServer.Web.Routing;
/// <summary>
/// Create a module that get initialized after the CMS Module has been initialized
/// </summary>
[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class SlackNotifierInitialization : IInitializableModule
{
public void Initialize(InitializationEngine context)
{
INotificationPreferenceRegister preferencesRegister = context.Locate.Advanced.GetInstance<INotificationPreferenceRegister>();
// register the Slack NotificationProvider to handle all notifications created on the "slack" channel
preferencesRegister.RegisterDefaultPreference(
SlackFormatter.ChannelName,
SlackMessageProvider.Name,
s => s);
}
public void Uninitialize(InitializationEngine context)
{
// remove the event handler when the modules gets un initialized
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment