This is the third post in a series of posts about the Records Center in MOSS. (Part One, Part Two)
Creating a custom router is relatively straight forward in MOSS...All you need to do is create a class which implements the IRouter interface and register the router with the records center.
The IRouter interface has only one method called OnSubmitFile.
class CustomRouting : IRouter {
public RouterResult OnSubmitFile(string recordSeries, string sourceUrl,
string userName, ref byte[] fileToSubmit,
ref RecordsRepositoryProperty[] properties,
ref SPList destination,
ref string resultDetails) {
return RouterResult.SuccessContinueProcessing; }}
The return value from this method is a choice between
RouterResult.SuccessContinueProcessing – The routing succeeded continue with the default processing. The normal processing continues. Use this if you wish to add/modify some properties or modify the document.RouterResult.RejectFile – Reject the file becuase it is invalid for some reason. You can set the resultDetails argument to inform the user of the problem.
RouterResult.SuccessCancelFurtherProcessing – The processing has succeeded and do not perform the normal processing. Use this if you actually store the document yourself.
The documentation appears to imply that by changing the destination paramter you can change the ultimate destination of the routing. In my experience this does not actually occur unless there is a problem storing the document in the original document library. If this happens it does get routed to the destination you specify, but it does not copy the metadata. I am not sure if this is a bug, or by design though.
However, you can use SuccessCancelFurtherProcessing and store your document in the location of your choice. This is what the sample will demonstrate.
The sample code below shows a custom router which will route records based on their content type to folders in the document library specified by the routing table entry. The customer router creates a folder using the same name as the content type and then stores documents in folders based on the date.
The class implements the IRouter interface and the OnSubmitFile method.
public RouterResult OnSubmitFile(string recordSeries, string sourceUrl,
string userName, ref byte[] fileToSubmit,
ref RecordsRepositoryProperty[] properties,
ref Microsoft.SharePoint.SPList destination,
ref string resultDetails)
{
try
{
string sContentType = GetContentType(properties);
if (string.IsNullOrEmpty(sContentType))
{
// Log the fact we didn't find it...but let the default processing continue
Trace.WriteLine("RecordsRouting Failed...ContentType not found");
return RouterResult.SuccessContinueProcessing;
}
HandleContentType(sContentType, sourceUrl, fileToSubmit, properties, destination);
return RouterResult.SuccessCancelFurtherProcessing;
}
catch (Exception ex)
{
resultDetails = "Failed to route record: " + ex.Message;
Trace.WriteLine(resultDetails);
return RouterResult.RejectFile;
}
}
The method attempts to get the ContentType from the file (this maybe missing if it was submitted from outside of SharePoint) and then calls HandleContentType.
HandleContentType does all the work of getting the new desination folder, creating the new filename, storing the document and copying the metadata.
private void HandleContentType(string contentType, string sourceUrl,
byte[] fileToSubmit,
RecordsRepositoryProperty[] properties,
SPList destination)
{
// Create a new filename using the date & time
string sFileName = Path.GetFileNameWithoutExtension(sourceUrl) + " (" + DateTime.Now.ToUniversalTime().ToString("yyMMddHHmmss") + ")" + Path.GetExtension(sourceUrl);
SPFolder oFolder = GetDestinationFolder(contentType, destination);
// Add the document
SPFile oFile = oFolder.Files.Add(sFileName, fileToSubmit);
SPListItem oItem = oFile.Item;
oItem["ContentTypeId"] = destination.ContentTypes[contentType].Id;
oItem.Update();
foreach (RecordsRepositoryProperty p in properties)
{
if (oItem.Fields.ContainsField(p.Name))
{
try
{
if (OkToCopyField(p.Name, oItem))
oItem[p.Name] = p.Value;
}
catch (Exception ex)
{
Trace.WriteLine(string.Format("Failed to copy field '{0}': {1}", p.Name, ex.Message));
}
}
}
oItem.Update();
}
There is also another class included in the project, FeatureReceiver. This class handles registering the router when the feature is activated and removing it again when it is deactivated. In order to make the custom router available you need to add the router to the RecordSeriesCollection of the Records Center site and so the feature should be activated within that site.
public class FeatureReceiver : SPFeatureReceiver
{
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
SPWeb oWeb = (SPWeb)properties.Feature.Parent;
RecordSeriesCollection oSeries = new RecordSeriesCollection(oWeb);
oSeries.AddRouter("TheKid Sample Router", "TheKid.RecordsCenter, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0a0c37d47e875d49", "TheKid.RecordsCenter.RecordsRouting");
}
public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
SPWeb oWeb = (SPWeb)properties.Feature.Parent;
RecordSeriesCollection oSeries = new RecordSeriesCollection(oWeb);
oSeries.RemoveRouter("TheKid Sample Router");
}
public override void FeatureInstalled(SPFeatureReceiverProperties properties)
{}
public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
{}
}
There are two downloads for this sample, one being the project with the code and the other being a WSP solution for you to deploy and try out the sample.
Download the Sample Project
Download the Sample WSP Solution
To install the wsp you will need to run the following command (I did try using the installer, but I couldn't get it to work for this solution).
stsadm -o addsolution -filename c:\thekid.recordscenter.wsp
stsadm -o deploysolution -name thekid.recordscenter.wsp -immediate -allowGacDeployment
You will then need to activate the sample in the 'Site Features' for the records center site.
Once it has been activated the router will be available to all routing entries. To try the router you can simply add it to the default unclassified routing entry. Edit the entry and change the Router entry at the bottom of the settings. If you cannot see this then the feature is either not activated for the records center or the activation failed in some way.
Press OK and now all new unclassified documents wil get routed using the new sample router.
This is just a sample and is not really production quality, but it should give you a good start in creating your own routers and getting the records center to store documents where you want them. You can lso read the records management Q&A here and more about records management here