Joshua Folkerts
Feb 16, 2019
  4798
(4 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

Marcus B
Marcus B Feb 17, 2019 09:38 PM

Looks good. There are been a few questions on this recently on the forums. I hope that this helps them

Allan Thraen
Allan Thraen Feb 18, 2019 09:57 AM

Funny, I remember building some like this to help a client shortly after I joined Episerver in 2007 :-) So the demand is not new. Thanks for the write-up, Josh - looks good!

Janaka Fernando
Janaka Fernando Feb 18, 2019 06:56 PM

Good usage of extending the IUserNotificationRepository here!

Another option to complement this could be creating a custom Task called "Expiring Pages" similar to what Linus has demonstrated on this post - https://niteco.com/blogs/plugging-into-the-tasks-list/ 

Please login to comment.
Latest blogs
Copy Optimizely SaaS CMS Settings to ENV Format Via Bookmarklet

Do you work with multiple Optimizely SaaS CMS instances? Use a bookmarklet to automatically copy them to your clipboard, ready to paste into your e...

Daniel Isaacs | Dec 22, 2024 | Syndicated blog

Increase timeout for long running SQL queries using SQL addon

Learn how to increase the timeout for long running SQL queries using the SQL addon.

Tomas Hensrud Gulla | Dec 20, 2024 | Syndicated blog

Overriding the help text for the Name property in Optimizely CMS

I recently received a question about how to override the Help text for the built-in Name property in Optimizely CMS, so I decided to document my...

Tomas Hensrud Gulla | Dec 20, 2024 | Syndicated blog

Resize Images on the Fly with Optimizely DXP's New CDN Feature

With the latest release, you can now resize images on demand using the Content Delivery Network (CDN). This means no more storing multiple versions...

Satata Satez | Dec 19, 2024