Setting Custom Expiration Programmatically

A cool feature in SharePoint 2010 that I’ve been working with for a little while is custom expiration.  SharePoint 2010 gives a few neat options for extending the out-of-box expiration engine to allow custom expiration date calculations and processing actions.

There are a number of good articles out there on how to set custom date calculation formulas and processing actions, however, the challenge I faced was once you have your custom expiration formula and action created, how does one go about setting it programmatically?

The following table has links to some good articles on how to create your own custom expiration formulas and custom actions. Although all of these articles show examples using SharePoint 2007, the same code and APIs apply just the same in SharePoint 2010.

Custom expiration formula https://blah.winsmarts.com/2008-10-Authoring_custom_expiration_policies_and_actions_in_SharePoint_2007.aspx
Custom expiration formula https://msdn.microsoft.com/en-us/library/cc453774(v=office.12).aspx
Custom expiration action https://www.tonstegeman.com/Blog/Lists/Posts/Post.aspx?List=70640fe5%2D28d9%2D464f%2Db1c9%2D91e07c8f7e47&ID=25

For some background, there are two main options for setting expiration in SharePoint 2010. The first is the traditional way that has been around since 2007 that includes attaching an information policy on a content type. The second way is a new way in 2010 that allows the user to set an information policy directly on a list that will override any polices set on content types through was is called in the UI “Library & Folders” level expiration.

In my scenario, I need to set both a custom expiration formula and custom action on a newly created list and only on that list regardless of the content types used in that list. Therefore I need to use the second approach above, applying the information policy directly on the list level.

The following screen shot shows what the admin UI would look like after setting the policy:

image

What I found was the key to doing this programmatically is a class named “ListPolicySettings” this class is located in the following DLL and namespace:

Assembly Microsoft.Office.Policy.dll
Assembly Location <14 Hive>\ISAPI\
Namespace Namespace: Microsoft.Office.RecordsManagement.InformationPolicy

The following is a code sample of this class in action:

Code Snippet

  1. SPList targetList = targetWeb.Lists[targetListId];
  2.  
  3. ListPolicySettings retentionPolicy = new ListPolicySettings(targetList);
  4.  
  5. string retentionXml2 = "<Schedules nextStageId=\"3\">" +
  6.     "<Schedule type=\"Default\">" +
  7.     "<stages>" +
  8.     "<data stageId=\"1\" stageDeleted=\"true\" />" +
  9.     "<data stageId=\"2\" recur=\"true\" offset=\"1\" unit=\"days\">" +
  10.     "<formula id=\"CustomExpiration.CustomExpirationFormula\" />" +
  11.     "<action type=\"action\" id=\"CustomExpiration.CustomExpirationAction\" />" +
  12.     "</data>" +
  13.     "</stages>" +
  14.     "</Schedule>" +
  15.     "</Schedules>";
  16.  
  17. retentionPolicy.UseListPolicy = true;
  18. retentionPolicy.SetRetentionSchedule(retentionXml2, "Test from ER");
  19. retentionPolicy.Update();

In this example above I am setting a retention stage with a custom expiration formula and custom expiration action that also reoccurs with an interval of 1 day.

The key method above is the “SetRetentionSchedule()” method off the ListPolicySettings class. It takes two parameters, an SPList (pretty straight forward) but the second is the RetentionXML block – this is where it got a little tricky.  I could not find much documentation on this class or method, so in order to figure out the XML schema I used a combination of reflector and also used the following code sample to write a little console app to just snag the XML from an existing list where I set the retention schedule information via the user interface.

Code Snippet

  1. SPList targetList = site.RootWeb.Lists[listName];
  2.  
  3. ListPolicySettings retentionPolicy = new ListPolicySettings(targetList);
  4.  
  5. string currentPolicyXml = retentionPolicy.GetRetentionSchedule("/DL1");

By setting the retention schedule information a few different times with different combinations and seeing how the XML changed, I got a feel for how the XML schema was constructed.

Here one more example from my exploratory session on this:

Code Snippet

  1. string retentionXml = "<Schedules nextStageId=\"2\">" +
  2.                 "<Schedule type=\"Default\">" +
  3.                 "<stages>" +
  4.                 "<data stageId=\"1\">" +
  5.                 "<formula id=\"Microsoft.Office.RecordsManagement.PolicyFeatures.Expiration.Formula.BuiltIn\">" +
  6.                 "<number>18</number>" +
  7.                 "<property>Modified</property>" +
  8.                 "<propertyId>28cf69c5-fa48-462a-b5cd-27b6f9d2bd5f</propertyId>" +
  9.                 "<period>years</period>" +
  10.                 "</formula>" +
  11.                 "<action type=\"action\" id=\"Microsoft.Office.RecordsManagement.PolicyFeatures.Expiration.Action.Delete\" />" +
  12.                 "</data>" +
  13.                 "</stages>" +
  14.                 "</Schedule>" +
  15.                 "</Schedules>";

In this example above I am using the OOB expiration formula and using the Modified Date column as the trigger date plus 18 years.  For the action, I am using the OOB permanent delete action.

I hope that with the examples above, this is enough to help you out if you are in the same scenario I was in in trying to figure how to apply a policy directly on a list programmatically!