Take the community feedback survey now.

Joshua Folkerts
Feb 16, 2019
  72
(0 votes)

EPiServer Notification For Expiring Pages

A client asked if they could recieve some form of notification that pages they have created are about to expire 7 days before it actually expired.   I asked the client if they wanted an email of the list of the pages that are about to be expired and their response was, we get so many emails a day, that it might get lost in the list.  So the next best thing is for me to use the notification service that will send a notification to the user in the cms in edit mode.  I know episerver uses the the notification service to tell us when their is a new version of tinymce, etc, but i wanted to use it to inform the user that these x number of pages are about to expire.  

With the thought of using the notication servier, I went perusing around the documentation to find some information on how to tie into the notification service.  After reading the documentation, I came up with a scheduled job that will grab all the pages in the site tree and check the stop publish date to see if it is about to expire.  When the page is about to expire, i add it to the notification queue to notifiy the editor that this page is about to expire.

There are many ways and services that allow you to do this exact thing but i wanted to keep it simple and allow the admin to control how often to check the expiriy of the the page.  My goal is to keep is self contained regardless what cloud service or on premise hosting enviroment you chose.  

So to give an overview on what I did to accomplish this, 

  1. Create a scheduled job that will parse the pages for expiring pages
  2. Implement the IUserNotificationRepository to create new notifications to send
  3. Send the notification to the user based on pages expirey

This is the scheduled job i used to send the expiration of the pages to the user.

using Blend.Episerver;
using EPiServer.Core;
using EPiServer.Editor;
using EPiServer.Notification;
using EPiServer.PlugIn;
using EPiServer.ServiceLocation;
using EPiServer.Web;
using EPiServer.Web.Routing;
using Nito.AsyncEx;
using System;
using System.Collections.Generic;
using System.Linq;

namespace EPiServerSamples.ScheduledJobs
{
    [ScheduledPlugIn(DisplayName = "Expiring Pages Notification")]
    public class ExpiringPagesNotification : Blend.Episerver.ScheduledJobs.BlendJobBase
    {
        bool _stopSignaled;

        readonly INotifier notifier;

        readonly IUserNotificationRepository userNotificationService;

        const string ExpiryChannelName = "epi.PageExpiry";

        public ExpiringPagesNotification()
        {
            IsStoppable = true;
            var locator = ServiceLocator.Current;
            notifier = locator.GetInstance<INotifier>();
            this.userNotificationService = locator.GetInstance<IUserNotificationRepository>();
        }

        /// <summary>
        /// Called when a user clicks on Stop for a manually started job, or when ASP.NET shuts down.
        /// </summary>
        public override void Stop()
        {
            _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
            OnStatusChanged(string.Format("Starting execution of {0}", this.GetType()));

            var pages = this.GetExpiringPages();
            this.SetCounter("Expiring Pages", pages.Count());

            foreach (var page in pages)
            {
                var category = new Uri(UrlResolver.Current.GetUrl(page.ContentLink));
                var notifications = AsyncContext.Run(() => this.userNotificationService.GetUserNotificationsAsync(new UserNotificationsQuery()
                {
                    ChannelName = ExpiryChannelName,
                    Category = category
                }, 0, 20));

                if (!notifications.PagedResult.HasValue())
                {
                    AsyncContext.Run(() => notifier.PostNotificationAsync(new NotificationMessage()
                    {
                        ChannelName = ExpiryChannelName,
                        Content = $"{page.Name}(<a href=\"{ PageEditing.GetEditUrl(page.ContentLink)}\">{page.ContentLink}</a>) is about to Expire on {page.StopPublish.Value.ToShortDateString()} - {page.StopPublish.Value.ToShortTimeString()}",
                        Subject = "Expiration of Page Upcoming",
                        Recipients = new[] { new NotificationUser(page.CreatedBy) },
                        Sender = new NotificationUser("System"),
                        TypeName = "PageExpiring",
                        Category = new Uri(UrlResolver.Current.GetUrl(page.ContentLink))
                    }));
                    this.Increment("Messages Sent");
                }

            //For long running jobs periodically check if stop is signaled and if so stop execution
            if (_stopSignaled)
            {
                return "Stop of job was called";
            }
            }

            return this.CounterReport();
        }

        private IEnumerable<PageData> GetExpiringPages() =>
            this.contentLoader.GetItems(this.contentLoader.GetDescendents(SiteDefinition.Current.StartPage), new LoaderOptions())
            .Where(x => x is IVersionable && ((IVersionable)x).StopPublish.GetValueOrDefault(DateTime.MaxValue) < DateTime.Now.AddDays(8))
            .OfType<PageData>();
    }
}

So to recap on the job here.  

I first get a list of all pages that are expiring in the next 7 days.  The method "GetExpiringPages" just returns a list of pages that are about to be expired and that is the list of pages I look through.  I check to see if the notification has already been sent for this current page.  If the notification hasn't been sent, then i create a new postnotification to the creator of the page and send them a message.   

I hope this helps someone who might need a sample of using epi notifications.

Feb 16, 2019

Comments

Please login to comment.
Latest blogs
A day in the life of an Optimizely OMVP - Opticon London 2025

This installment of a day in the life of an Optimizely OMVP gives an in-depth coverage of my trip down to London to attend Opticon London 2025 held...

Graham Carr | Oct 2, 2025

Optimizely Web Experimentation Using Real-Time Segments: A Step-by-Step Guide

  Introduction Personalization has become de facto standard for any digital channel to improve the user's engagement KPI’s.  Personalization uses...

Ratish | Oct 1, 2025 |

Trigger DXP Warmup Locally to Catch Bugs & Performance Issues Early

Here’s our documentation on warmup in DXP : 🔗 https://docs.developers.optimizely.com/digital-experience-platform/docs/warming-up-sites What I didn...

dada | Sep 29, 2025

Creating Opal Tools for Stott Robots Handler

This summer, the Netcel Development team and I took part in Optimizely’s Opal Hackathon. The challenge from Optimizely was to extend Opal’s abiliti...

Mark Stott | Sep 28, 2025

Integrating Commerce Search v3 (Vertex AI) with Optimizely Configured Commerce

Introduction This blog provides a technical guide for integrating Commerce Search v3, which leverages Google Cloud's Vertex AI Search, into an...

Vaibhav | Sep 27, 2025

A day in the life of an Optimizely MVP - Opti Graph Extensions add-on v1.0.0 released

I am pleased to announce that the official v1.0.0 of the Opti Graph Extensions add-on has now been released and is generally available. Refer to my...

Graham Carr | Sep 25, 2025