Time released content in ASP.NET
While working on the PDC2008 website, we had several time-critical updates. There were some announcements that needed to go live on the website at specific times to coincide with other marketing, there were updates to the list of of software being given to attendees that needed to go live right after the keynotes in which they were announced, etc. While some of the site ran on RSS feeds, on some pages we needed the flexibility of static HTML and CSS. While there were plenty of times where I made that sort of deployment by hitting upload in Filezilla at just the right time, there were other times where that wasn’t possible.
The first time that hit was in August, several months before the conference, when we needed a site update to go live while I was scheduled to be in the middle of a flight. Clearly, a technological solution was needed.
Extending The ASP:Placeholder Control
Yes, all you ASP.NET MVC hipsters can wander off now – this is an ASP.NET Webforms solution, since the site initially went live when ASP.NET MVC was at Preview 3 (5 months before hitting Beta). But – wait – please leave a comment on how you’d approach this with ASP.NET MVC, would you?
The ASP.NET Placeholder control is a simple control that renders no markup of its own, it just holds content. It’s great for holding blocks of content which is either displayed or hidden, among other things. So in this case, I inherited from Placeholder and created TimedContentPlaceholder, adding a Start and End property. That let me do this kind of thing:
<PDC:TimedContentPlaceholder runat="server" Start="10/28/2008 10:30 AM"> <% /* Phase 3 (to go live Monday, October 28 – following Keynote 2) : Please add as a block at the top of the page under a heading reading, "BITS UPDATE 02" */ %> <h2>BITS UPDATE 02</h2> <b>Windows 7 Ultimate ISO</b></h3> <p> Burn your own Windows 7 Ultimate DVD using these 32bit & 64bit ISO files.</p> <PDC:TimedContentPlaceholder>
So that content needed to be prestaged, but shouldn’t go live until the specified date and time. Here’s the control which handled that:
public class TimedContentPlaceholder : System.Web.UI.WebControls.PlaceHolder { public DateTime? Start { get; set; } public DateTime? End { get; set; } public Uri Redirect { get; set; } public override bool Visible { get { return base.Visible && isInTimeRange(); } set { base.Visible = value; } } protected override void OnInit(EventArgs e) { if (Redirect != null) if (isInTimeRange()) Page.Response.Redirect(Redirect.AbsoluteUri); } private bool isInTimeRange() { if (Start != null && Utility.GetCurrentDateTime() < Start) return false; if (End != null && Utility.GetCurrentDateTime() >= End) return false; return true; } }
Redirection
Most of that’s pretty straightforward, but there are a few extra things there. First of all, there’s the Redirect property. That’s useful when you’ve got a basic page in place which will later redirect to an application or even another server later. For instance, we used that to redirect to the server hosting the session information on the cutover date using something like this:
<PDC:TimedContentPlaceholder runat="server" Redirect="https://sessions.microsoftpdc.com/" Start="8/15/2008 12:00 AM" />
Time Machine
The second thing is a bigger deal – we weren’t just using DateTime.Now in our time checks, we called out to a function. There were two reasons for that – the simple reason was to provide a central place to handle the timezone conversion between the conference location and the server location. But the main reason was to allow specifying a test time, which allowed us to see how the site would look at any time by passing in querystring arguments. Here’s how Utility.GetCurrentDateTime() worked:
public static DateTime GetCurrentDateTime() { //Compensate for server being on the east coast if (IsProduction()) return DateTime.Now.Subtract(new TimeSpan(3, 0, 0)); DateTime testDate; if (HttpContext.Current.Session["date"] == null) return DateTime.Now; if (DateTime.TryParse(HttpContext.Current.Session["date"].ToString(), out testDate)) return testDate; return DateTime.Now; }
There was separate code in the masterpage which took a querystring variable and set a session variable with the test date. We also pulled that session variable in Silverlight elements as well, so we could step through the site evolution as the conference approached, each keynote completed, and the conference ended.
Not rocket science, but it worked for us. How would you have solved this?