Monday, September 24, 2007

EPiServer Code: Send a warning email when a page is about to expire

As you might imagine I find it difficult to keep my hands of the brand new v.5. So naturally I've been searching out excuses to try out coding some small samples against the API.
Here's a feature that I've heard requested from several intranet customers already - An automatic email that informs the owner of a page that it's about to expire.
"What a great idea" I thought first time I heard it - One of the major problems with most intranet is outdated information so I can easily imagine companies having a policy that all pages on their intranet should have an expiration date - and I can just as easily imagine the need for owners to change that expiration date, if the information is still relevant hence producing the need for a warning email.
First off I could see several approaches to making this in EPiServer:
  1. Hook into the right DataFactory event from the global.asax and send a mail whenever a page is expiring
  2. Use the v5 support for workflow foundation and make a workflow that performs a SendEmail activity when a page is moved to the archive
  3. Set up a scheduled task to check for pages about to expire
Although I found option 2 very encharming due to the use of workflows, I decided to make an implementation of option 3 - since this was the only approach that would send out a warning email before the page actually expired (and any damage was done).

I made a new C# Code library project, added references to the relevant EPiServer dlls (and log4net to enable logging) and wrote this code:



using System;
using System.Collections.Generic;
using System.Text;
using EPiServer.PlugIn;
using EPiServer;
using EPiServer.Core;
using EPiServer.Filters;
using EPiServer.Security;
using EPiServer.Personalization;
using EPiServer.Configuration;
using System.Net.Mail;

namespace Allan.EPiModules
{
[ScheduledPlugIn(DisplayName = "Page Expiry Warning")]
public class ExpiryWarningJob
{
private static log4net.ILog _log;

static ExpiryWarningJob()
{
_log = log4net.LogManager.GetLogger(typeof(ExpiryWarningJob));
}

public static string Execute(){
int num = 0;
//Find pages that will expire in a day
PropertyCriteria criteria = new PropertyCriteria();
criteria.Name = "PageStopPublish";
criteria.Value = DateTime.Now.AddDays(1).ToString();
criteria.Type = PropertyDataType.Date;
criteria.Required = true;
criteria.Condition = CompareCondition.LessThan;
//...but hasn't already expired
PropertyCriteria criteria2 = new PropertyCriteria();
criteria2.Name = "PageStopPublish";
criteria2.Value = DateTime.Now.ToString();
criteria2.Type = PropertyDataType.Date;
criteria2.Required = true;
criteria2.Condition = CompareCondition.GreaterThan;
PropertyCriteriaCollection criterias = new PropertyCriteriaCollection();
criterias.Add(criteria);
criterias.Add(criteria2);
foreach (PageData data in DataFactory.Instance.FindPagesWithCriteria(
PageReference.RootPage, criterias,
null, LanguageSelector.MasterLanguage(), AccessLevel.NoAccess)
)
{
SendMail(data);
num++;

}
return string.Format("{0} expiry emails sent", num.ToString());

}


private static void SendMail(PageData p)
{
//Identify user profile.
//Consider using the ChangedBy instead of CreatedBy.
EPiServerProfile esp = EPiServerProfile.Get(p.CreatedBy);
if (esp.Email != null)
{
try
{
//Build a new mail message
MailMessage message = new MailMessage("expire@" + Settings.Instance.SiteUrl.Host, esp.Email);
message.Subject = "Page \"" + p.PageName + "\" is about to expire";
message.Headers.Add("X-Mailer", "EPiServer CMS");
message.Headers.Add("Content-Base", Settings.Instance.SiteUrl.GetLeftPart(UriPartial.Authority));
message.Body = "The page <A href=\""
+ Settings.Instance.SiteUrl.GetLeftPart(UriPartial.Authority)
+ p.StaticLinkURL + "\">"
+ p.PageName
+ "</A> will expire on "
+ p.StopPublish.ToShortDateString();
message.IsBodyHtml = true;
message.BodyEncoding = Encoding.UTF8;
SmtpClient smtp = new SmtpClient();
//Make sure the web.config sets up the SMTP Client.
smtp.Send(message);
_log.Info("Expiry warning sent to: " + esp.Email);
}
catch (Exception e)
{
_log.Error("Failed to send expiry warning", e);
}
}
else _log.Warn("Unable to send expiry warning to " +
esp.DisplayName +
" - no known email address");
}
}
}

The dll should be placed in the EPiServers "bin" folder and then it will automatically be loaded. The ScheduledPlugin attribute will make it appear as a scheduled task in Admin mode. Here you should probably set it to run once a day - perhaps in the early hours of the morning will be best.
You also need to make sure the web.config is setup to the SMTP server.

The code is a pretty simple sample that will find the pages that are about to expire (the following day) and then send a mail to the creator of each page that it's about to expire.

The sample was made in less than a day and of course it still could use a lot of work to be really nice. Ideas for improvements:
  • Group expiration mails so that each user won't be bombarded with several mails every day
  • Consider how long time before people should be warned that the pages are about to expire - is 1 day time enough?
  • Consider if it's really the creator that should get the mail - perhaps the last person to have updated the page would be the right one?
  • Build functionality together with tasks - so instead of emails a task to check the page should be created.
Enjoy!

New Job!

Just a quick personal update: I've decided to join the great team at EPiServer, starting october 1st as a Technical Architect. I'll still be based in Copenhagen, Denmark - but my primary work-area will still be within development (which for the most part is in Stockholm) so I guess I'll once again get to try working virtually with people all over the world.
I'm looking very much forward to joining the EPiServer family and I hope that I'll be able to contribute to make the EPiServer CMS an even better product (although it's already quite good).

...And just to answer an obvious question: Yes, this means that there might be more CMS and EPiServer related posts on my blog in the future - but I'll still try to find time between changing my sons diapers and developing CMS features to post some more unrelated crazy code :-)

Thursday, September 13, 2007

An oldie but a goldie

Just came across this old story that pretty much explains why so many software companies have trouble surviving after moving from pioneers to "real software company".
It's quite a laugh.

Thursday, September 6, 2007

Google Earth Rules!

A couple of days ago I read that Google Earth had introduced a new awesome feature and I hurried to download the latest version.
No, I'm not talking about the wannabe astronomical function where you can browse the sky as well as the earth - thats cute, but no were near as cool as their new "secret" Flight Simulator.

What a great idea to build in a flight-sim in Google earth! It's awesome to cruise around in 10.000 ft in a makebelieve airplane seeing the ground as it really looks.
All we need now to make it even more realistic is some clouds, radio-chatter, more 3d buildings and a bunch of hot stewardesses!

"Ladies & Gentlemen, Captain Allan has turned on the Fasten Seat Belt sign, so please buggle up and turn off any electronic devices"