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 &amp; 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?

5 Comments

  • That's actually a pretty elegant solution, as long as the clocks on all Web Servers are in Sync.

    The only change that I would make (but I am European) is to handle all Dates in UTC, so that no "Compensation for East Coast Servers" is required, but then again, UTC is only 1 or 2 hours off my local time, which makes it easier to handle than if you're US-based.

  • Of course, if your content was database driven and had a publish and expire date for each entry, you could deploy the content months in advance (like we do for the Barkus PetParade each year in Soulard).

  • Well, I don't use ASP.NET MVC so I can't say how I use that to do this, but I do use Castle Monorail, which is rather close.

    In Monorail, it would be rather simple to write a ViewComponent to do everything --- except the redirect. But, this is just wrong. But doing this (with the ViewComponent or with the WebControl), you are mixing Content with Presentation.

    The value of the time controled text should be set in the Controller/CodeBehind.

  • Love the time machine part. It's *so* getting translated to my stuff.

  • Your solution worked for you and is pretty light-weight, which is really all the matters.

    I have previously implemented a content management system which did time released content another way.

    It consisted of a content user control which had a content "key" property. Before the page was rendered the control would request content for the "key" from a content manager.

    The content manager would read and cache a heap of XML content items. Each content item had a "key", HTML content and heaps other properties including publish start and end dates.

    It was the content managers responsibility to return the correct content for any given "key" at any given time.

    In our case the content manager was implemented as a WCF service so content could be requested by both Java Struts and ASP.NET sites that had to be supported. (Additional Java code was obviously required for the Struts app to get content from the WCF service)

    This worked really well for us as it meant time released content could be created without changing code and it worked across two server platforms (Struts also being an MVC framework).

    I imagine some blog engines would probably also support some sort of time released content if you posted content with the publish date in the future.

Comments have been disabled for this content.