Adding Custom Workflow Notifications to Kentico
Kentico Workflows are a great way to ensure content is entered and approved before being published to your production site. Built-in notifications can inform approvers that a page is awaiting approval and inform editors when their content has been approved or rejected. While these notifications work well for many projects, developers can customize the content publishing process to fit nearly any business need. In this article, I’ll show you how to extend the notification process to inform other approvers when a document is approved by a member of the group.
Out of the box, Kentico does a good job at automating communications to all parties of your content publishing team. Whether it’s the editor that is making the change or the group of approvers that ensure the content is correct, notifications can be configured to send emails at all steps of the process. Over the years, I have often needed to customize this module to get a little more functionality. A common requirement was to notify other members of a group when someone approves a page in the process. So how do we accomplish this? Global Event Handlers to the rescue!
For this demo, I’m working with everyone’s favorite Dancing Goat site. For the users, I created two groups and a couple of users.
Note that I have abbreviated the names below for the rest of the article.
Content Editors – This will be the group that is updating the content.
Workflow Approvers – This will be the group that will approve content updates.
- Approver 1 (A1)
- Approver 2 (A2)
I created a simple workflow with a single Approve step.
For the permissions, I added Workflow Approvers as the only role that moves the page past the Approve step.
With that in place, I was ready to add some customizations for my scenario.
Adding a Custom User Setting
The next step was to add a flag to the User class to let the system know if the user wants to receive the notifications. In the Membership/Classes/Users/Fields section, I created a new field to hold my setting.
In my demo I added a filed ot the Users class in the Membership module. This is a system class that is customizable, however, it is usually better to add your custom field to the User Settings class. This seperates them from the system table and allows you to categorize multiple fields easily.
Adding the Global Event Handler
The next step was to add my custom code. I created a CMSModuleLoader partial class and added my global event handler for the WorkflowEvents.Approve.After event. Each time a page is approved, this event will fire, and I can determine if a notification should be sent.
public override void Init()
// Assigns custom handlers to events
WorkflowEvents.Approve.After += Approve_After;
In this Approve_After event, I added my logic. This consisted of a couple of steps:
Get the previous workflow step/Check the WorkflowStepType
Because the Approve_After fires when a document is approved or published, I needed to look at the previous step to see where the document was coming from. I only wanted to handle when the document was being approved from one step to the next.
//Get the previous step of the workflow
WorkflowStepInfo wsi = e.PreviousStep;
if (wsi != null)
//Make sure it was an approval (standard) step
if (wsi.StepType == WorkflowStepTypeEnum.Standard)
Get All of the Other Users that Can Approve the Previous Step
For my new notifications, I want to let all of the other approvers know that the current user has approved this step. For this functionality, I leveraged the GetUsersWhoCanApprove function, with a WHERE condition to filter out the current user.
// Get all of the other approvers for the current workflow step
var approvers = WorkflowStepInfoProvider.GetUsersWhoCanApprove(wsi, null, SiteContext.CurrentSiteID, "UserID <> " + CMSActionContext.CurrentUser.UserID, null, 0, "Email, FullName, Username, UserWorkflowEmailNotification");
if (approvers != null)
//Loop through the approvers
foreach (var approver in approvers)
Check If the User Should Get Notifications
Now that I had my collection of “other” approvers, I needed to ensure they wanted to receive notifications. For this, I checked the custom flag I created above.
// Check if user should be notified upon approval
if (ValidationHelper.GetBoolean(approver["UserWorkflowEmailNotification"], false))
// Send an Email notification
SendWorkflowNotification(approver.Email, approver.FullName, CMSActionContext.CurrentUser.FullName, e.Document.DocumentName, URLHelper.GetAbsoluteUrl(e.Document.GetPreviewLink(approver.UserName)), "APPROVED");
Add the Email Notification
After ensuring the user wanted to receive the notifications, I added a new method to send the email. Using the SendEmailWithTemplateText method, I populated all of the values recipient information.
// Get the site name
string siteName = null;
SiteInfo si = SiteInfoProvider.GetSiteInfo(SiteContext.CurrentSiteID);
if (si != null)
siteName = si.SiteName;
EmailTemplateInfo eti = EmailTemplateProvider.GetEmailTemplate("WorkflowNotificationEmailTemplate", SiteContext.CurrentSiteName);
MacroResolver mcr = MacroResolver.GetInstance();
// Create message object
EmailMessage message = new EmailMessage();
// Get sender from settings
message.EmailFormat = EmailFormatEnum.Both;
message.From = eti.TemplateFrom;
// Do not send the e-mail if there is no sender specified
if (message.From != "")
// Initialize message
message.Recipients = strRecipientEmail;
message.Subject = eti.TemplateSubject;
// Send email via Email engine API
EmailSender.SendEmailWithTemplateText(SiteContext.CurrentSiteName, message, eti, mcr, true);
Note that I used a macro resolver to send dynamic values to my template. This allowed me to customize the message for each recipient.
Create the Email Notification Template
The last piece of setup was to create an email template to format the notifications. Under Email Templates, I created a new template and formatted with the dynamic values.
With all of the configuration and code in place, it was time to test! Seeing how it was a demo environment, I had to simulate a little functionality. To do this, I opened two browsers and signed into one as E1 and the other as A1. The idea will be when E1 makes a change and submits it, Kentico will send an email notification to A1 and A2 letting them know the page is awaiting approval (because both users are in the Workflow Approvers role). After A1 approves the change, the site will generate an email using the new template for A2, informing them that A1 made the change. Finally, the site will send a notification to E1 informing them the page has been published (because that is the last step of the workflow).
Here is a visual representation of the process, in case all of that didn’t make sense.
To test, E1 updates a page and submits it for approval.
Kentico generated the Awaiting approval emails for the Workflow Approvers group (which contains A1 and A2).
A1 then goes in and approves the change
At this point, my global event handler fired and the system found all of the other approvers in the Workflow Approvers group and generated a dynamic email for each of them (in this case one email for A2 is generated).
An email was also sent to the E1 to inform them the page was published.
With this modification, content approvers will now be notified any time another user approves a page in the process. This will prevent them from having to log in into the site unnecessarily, saving lots of time for Angry Birds, Facebook, or whatever site they are wasting their time on that day. Additionally, the new notification contains a preview link for the page. This allows them to jump right to the content and see the update without having to log in and view it.
Hopefully, this blog will show you how you can easily extend the Workflow system to generate and deliver the notifications that make sense for your site. By overriding global events, you can customize how the system will handle any scenario and ensure your content is updated quickly and efficiently. Good luck!
Get the code
This blog is intended for information purposes only and provides an example of one of the many ways to accomplish the described task. Always consult Kentico Documentation for the best practices and additional examples that may be more effective in your specific situation.