Try our conversational search powered by Generative AI!

Joshua Folkerts
Feb 16, 2019
  4323
(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
Why C# Developers Should Embrace Node.js

Explore why C# developers should embrace Node.js especially with Optimizely's SaaS CMS on the horizon. Understand the shift towards agile web...

Andy Blyth | May 2, 2024 | Syndicated blog

Is Optimizely CMS PaaS the Preferred Choice?

As always, it depends. With it's comprehensive and proven support for complex business needs across various deployment scenarios, it fits very well...

Andy Blyth | May 2, 2024 | Syndicated blog

Adding market segment for Customized Commerce 14

Since v.14 of commerce, the old solution  for adding market segment to the url is not working anymore due to techinal changes of .NET Core. There i...

Oskar Zetterberg | May 2, 2024

Blazor components in Optimizely CMS admin/edit interface

Lab: Integrating Blazor Components into Various Aspects of Optimizely CMS admin/edit interface

Ove Lartelius | May 2, 2024 | Syndicated blog

Anonymous Tracking Across Devices with Optimizely ODP

An article by Lead Integration Developer, Daniel Copping In this article, I’ll describe how you can use the Optimizely Data Platform (ODP) to...

Daniel Copping | Apr 30, 2024 | Syndicated blog

Optimizely Forms - How to add extra data automatically into submission

Some words about Optimizely Forms Optimizely Forms is a built-in add-on by Optimizely development team that enables to create forms dynamically via...

Binh Nguyen Thi | Apr 29, 2024