Try our conversational search powered by Generative AI!

Joshua Folkerts
Feb 16, 2019
  4375
(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
Build a headless blog with Astro and Optimizely SaaS CMS

I’m a big fan of using the right tool for the right job. I’m also a big fan of Astro , for the right use case. Let's explore Astro to see what it's...

Jacob Pretorius | May 28, 2024

Microsoft announces Natural language to SQL

Finally, Microsoft launches "Natural language to SQL," after it has been available for several months in Optimizely CMS!

Tomas Hensrud Gulla | May 23, 2024 | Syndicated blog

Five easy ways to start personalizing your content right now

If you clicked on this article, you already know that getting the right message to the right person at the right time helps drive conversions and...

Kara Andersen | May 23, 2024

ExtendedCms.TinyMceEnhancements – serwer side webp support

Today I will introduce another small feature of TinyMceEnhancements plugin. The functionality is used to automatically detect whether a browser...

Grzegorz Wiecheć | May 22, 2024 | Syndicated blog