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
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 | |
} | |
} |