Publish Delayed Page Versions and outputcache

Hi! I've tried to figure out how to setup EPiServer (4.51) to flush the outputcache for pages that have a start publish date somewhere in the middle of a cache policy time out interval (EPnCachePolicyTimeout). And further more how to achieve this for pages set to be published with the delayed publish function. As I haven't found any settings nor documentation describing the sought behaviour I've started to think about how to achieve this on my own by overriding the SetCachePolicy method. However, I'd have to figure out some sort of time table for what's about to become published. I see two main scenarios here: figure this out every time the SetCachePolicy method is called by using PropertyCriteriaCollection, PropertyCriteria and FindPagesWithCriteria or figure it out on application startup and store it in an application wide (static) variable (or whenever the page cache is rebuilt) complemented with wiring to the Save, Publish, ... events to keep the time table up to date. I guess the second scenario would require a lot less resources. So setting the cache would involve two steps: 1. Figure out a time table (done in either the SetCachePolicy method or in some application wide fashion) 2. Use this time table to set the next timeout (done in the SetCachePolicy method) Now the questions: Have I missed something? Do I have to handle this situation by myself? Any other suggestions? If any of the above solutions have to be implemented: How do I find out the date and time of a delayed publish? Is there an event to wire to when delayed publish is set for a page? or is there any other way to find out when a delayed publish is about to occur? Is there an event to wire to when the page cache has been rebuilt? Regards, Anders
Feb 08, 2008 15:06
This is how I solved it: In Global.asax.cs I added a private, a property, init code and code to maintain a correct time table: private static Classes.PublishTimeTable _publishTimeTable; public static Classes.PublishTimeTable PublishTimeTable { get { return _publishTimeTable; } } In Application_Start I added init code _publishTimeTable = new Solution.Classes.PublishTimeTable(Global.EPConfig.CachePolicyTimeout); In an eventhandler for SavingPage I added the following lines if(e.Page.WorkPageID == 0) { _publishTimeTable.AddPublishDate(e.Page.StartPublish); } else { _publishTimeTable.AddDelayedPublishDate(e.Page.StartPublish); } _publishTimeTable.AddPublishDate(e.Page.StopPublish); The I created two classes to do the time table work for me. PublishTimeTable.cs /// /// Summary description for PublisTimeTable. /// public class PublishTimeTable { private DateTime MAXDATE = DateTime.MaxValue; //.Parse("9999-12-31 23:59:59"); private TimeCollection _timeCollection; private int _cacheTimeout = 0; private bool _isActive = false; public TimeCollection PublishOccasions { get { return _timeCollection; } } public bool IsActive { get { return _isActive; } } public PublishTimeTable(int cacheTimeout) { _cacheTimeout = cacheTimeout; _timeCollection = new TimeCollection(); BuildPublishTimeTable(); } public DateTime GetNextTimeout() { DateTime now = DateTime.Now; if(_isActive) { return CheckPublishOccasions(now); } else { return now.AddSeconds(_cacheTimeout); } } public bool AddPublishDate(DateTime publishDate) { if(DateTime.Now.CompareTo(publishDate) < 1 && publishDate.CompareTo(MAXDATE) < 0) { _timeCollection.Add(publishDate); _isActive = true; return true; } else { return false; } } public bool AddDelayedPublishDate(DateTime publishDate) { if(AddPublishDate(publishDate)) { AddPublishDate(publishDate.AddMinutes(-1)); AddPublishDate(publishDate.AddMinutes(1)); return true; } else { return false; } } private DateTime CheckPublishOccasions(DateTime now) { DateTime nextTimeout = now.AddSeconds(_cacheTimeout); int count = _timeCollection.Count; for(int index = count - 1; index >= 0; index--) { if(_timeCollection[index].CompareTo(now) < 1) { try { _timeCollection.RemoveAt(index); } catch {} } else if(_timeCollection[index].CompareTo(nextTimeout) < 1) { nextTimeout = _timeCollection[index]; } } _isActive = _timeCollection.Count > 0; return nextTimeout; } private void BuildPublishTimeTable() { // Future start publish EPiServer.PropertyCriteria upCommingPublish = new EPiServer.PropertyCriteria(); upCommingPublish.Condition = EPiServer.Filters.CompareCondition.GreaterThan; upCommingPublish.Type = EPiServer.Core.PropertyDataType.Date; upCommingPublish.Name = "PageStartPublish"; upCommingPublish.Value = DateTime.Now.ToString(); upCommingPublish.Required = true; AddPagePublishDates(upCommingPublish); // Future stop publish upCommingPublish.Name = "PageStopPublish"; AddPagePublishDates(upCommingPublish); } private void AddPagePublishDates(EPiServer.Core.PageDataCollection pages) { for(int index = 0; index < pages.Count; index++) { if(!_timeCollection.Contains(pages[index].StartPublish)) { _timeCollection.Add(pages[index].StartPublish); _isActive = true; } } } private void AddPagePublishDates(EPiServer.PropertyCriteria upCommingPublish) { EPiServer.PropertyCriteriaCollection publishCriteria = new EPiServer.PropertyCriteriaCollection(); publishCriteria.Add(upCommingPublish); AddPagePublishDates(EPiServer.Global.EPDataFactory.FindPagesWithCriteria(Global.EPConfig.StartPage, publishCriteria, EPiServer.Security.AccessControlList.NoAccess)); } public override string ToString() { StringBuilder toString = new StringBuilder(base.ToString()); foreach(DateTime occasion in _timeCollection) { toString.AppendFormat("{0}{1}", Environment.NewLine, occasion); } return toString.ToString(); } } and a time collection TimeCollection.cs /// /// Summary description for TimeCollection. /// public class TimeCollection : CollectionBase { public TimeCollection() { } public DateTime this[int index] { get { return (DateTime)List[index]; } set { List[index] = value; } } public int Add(DateTime value) { return List.Add(value); } public int IndexOf(DateTime value) { return List.IndexOf(value); } public void Insert(int index, DateTime value) { List.Insert(index, value); } public void Remove(DateTime value) { List.Remove(value); } public void RemoveAt(int index) { List.RemoveAt(index); } public bool Contains(DateTime value) { // If value is not of type DateTime, this will return false. return List.Contains(value); } }
Feb 20, 2008 22:29
Well, forgot to tell you about one thing. In a base class that all my pages inherit from I added an override for SetCachePolicy protected override void SetCachePolicy() { base.SetCachePolicy(); if(Global.PublishTimeTable.IsActive) { base.Response.Cache.SetExpires(Global.PublishTimeTable.GetNextTimeout()); } } It seems to work fine, but I'm having some trouble with time-sync, so delayed publish only works if I start the job manually in Admin.
Feb 20, 2008 22:34
Well! I have actually made some new discoveries in the process of testing my new solution. 1. Delayed publish actually works without intervention. 2. ... but this is only true as long as the ASP.NET worker process running the EPiServer instance isn't restarted. When it is restarted something strange happens. It seems as the page cache is partly updated. In editmode you see the old version when viewing a page, but the new version when editing the same page and the version list states that the latest version is published (which is the one seen when editing). Thus I have made som minor adjustments to the code above. The method AddDelayedPublishDate is removed and the code block if(e.Page.WorkPageID == 0) { _publishTimeTable.AddPublishDate(e.Page.StartPublish); } else { _publishTimeTable.AddDelayedPublishDate(e.Page.StartPublish); } _publishTimeTable.AddPublishDate(e.Page.StopPublish); is replaced by _publishTimeTable.AddPublishDate(e.Page.StartPublish); _publishTimeTable.AddPublishDate(e.Page.StopPublish);
Feb 22, 2008 14:31
I've discovered that Delayed publish seems to work even though ASP.Net worker process is restarted. My previous test results were distorted by the fact that the development machine I was using had insufficient resources for the amount of data used. This in combination with test parameters that were to narrow lead to the wrong conclusion about delayed publishing.
Feb 28, 2008 13:59
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.