EcmDocumentRouter Class

The EcmDocumentRouter class represents a content organizer that routes documents submitted to a Web site.

Inheritance Hierarchy

System.Object
  Microsoft.Office.RecordsManagement.RecordsRepository.EcmDocumentRouter

Namespace:  Microsoft.Office.RecordsManagement.RecordsRepository
Assembly:  Microsoft.Office.Policy (in Microsoft.Office.Policy.dll)

Syntax

'Declaration
Public Class EcmDocumentRouter
'Usage
Dim instance As EcmDocumentRouter
public class EcmDocumentRouter

Remarks

The content organizer feature enables content stewards to configure their sites so that there is a single point of content creation and submission and so that content is automatically saved to the appropriate location based on the metadata of the document. Use the EcmDocumentRouterRule to specify the final location for documents based on their content type and conditions. The content organizer for a specified site uses the RoutingRuleCollection property to store all of the rules defined for a EcmDocumentRoutingWeb object.

Initialize a new instance of the EcmDocumentRouter class by using the Router property of an EcmDocumentRoutingWeb. The EcmDocumentRouter class also provides an API (SaveFileToFinalLocation(EcmDocumentRoutingWeb, SPFolder, Stream, String, String, Hashtable, SPUser, Boolean, String)) to override the pre-configured location and save the document to a different location.

The following code includes a sample custom router that inspects the content of a text file that is being saved for sensitive information, and a class that you can use to reorganize documents on a site based on new content organizer rules.

Examples

using System;
using System.Collections;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using Microsoft.SharePoint;
using RecordsRepositoryProperty = Microsoft.SharePoint.RecordsRepositoryProperty;
using EcmDocumentRoutingWeb = Microsoft.Office.RecordsManagement.RecordsRepository.EcmDocumentRoutingWeb;
using EcmDocumentRouter = Microsoft.Office.RecordsManagement.RecordsRepository.EcmDocumentRouter;
using ICustomRouter = Microsoft.Office.RecordsManagement.RecordsRepository.ICustomRouter;
using CustomRouterResult = Microsoft.Office.RecordsManagement.RecordsRepository.CustomRouterResult;

namespace Microsoft.SDK.SharePointServer.Samples
{
    public class SampleDocumentRouter : ICustomRouter
    {
        /// <summary>
        /// A sample custom router which inspects the content of a text file being saved for sensitive information
        /// If sensitive information is found, then the information is masked and saved by the custom router.          
        /// </summary>
        /// <param name="contentOrganizerWeb">The Content Organizer that invoked the custom router.</param>
        /// <param name="recordSeries">Content type of the file being organized</param>
        /// <param name="userName">The name of the user who created the file. Value can be empty if the user no longer exists.</param>
        /// <param name="fileContent">Content of the file being saved.</param>
        /// <param name="properties">Metadata for the file being saved.</param>
        /// <param name="finalFolder">The final location that the content organizer determined for this file.</param>
        /// <param name="resultDetails">Any details that the custom router wants to furnish for logging purposes.</param>
        /// <returns>Whether the content organizer should continue to save the file in the designated location</returns>
        CustomRouterResult ICustomRouter.OnSubmitFile(
            EcmDocumentRoutingWeb contentOrganizerWeb,
            string recordSeries, // Content type name
            string userName,
            Stream fileContent,
            RecordsRepositoryProperty[] properties,
            SPFolder finalFolder,
            ref string resultDetails)
        {
            if (contentOrganizerWeb == null)
                throw new ArgumentNullException("contentOrganizerWeb");
            // We should have a Content Organizer enabled web           
            if (!contentOrganizerWeb.IsRoutingEnabled)
                throw new ArgumentException("Invalid content organizer.");

            // TODO: Replace these variable values and input parameters with your own values.
            //Change Domain\LoginName with the login name of a site user creating this file.
            const string submitterLoginName = "Domain\\LoginName";
            // Change MyFileName to the required file name. This will be used if this custom router needs to save the processed file to the final location.
            string modifiedFileName = "MyFileName.txt";
            
            // Read incoming content into a string so that we can look for ssn. 
            // Do not close the stream that was passed in.
            string fileContentString = string.Empty;
            StreamReader sr = new StreamReader(fileContent);
            {
                fileContentString = sr.ReadToEnd();
            }
            // regular expression to match social security numbers in file content.
            Regex socialSecurityRegex = new Regex("([0-9]){3}-([0-9]){2}-([0-9]){4}");
            MatchCollection matches = socialSecurityRegex.Matches(fileContentString);
            if (matches.Count <= 0)
            {
                // return a string which will be logged by the content organizer.
                resultDetails = "File was inspected and no sensitive data was found.";
                return CustomRouterResult.SuccessContinueProcessing;
            }
            else
            {
                string submittingUserName = userName;
                if (string.IsNullOrEmpty(userName))
                {
                    // LoginName of the user creating the file
                    submittingUserName = submitterLoginName;
                }
                // We want to fix up the file content and save the file ourselves
                using (SPSite site = new SPSite(contentOrganizerWeb.DropOffZoneUrl))
                {
                    SPWeb web = site.OpenWeb();
                    // User creating the file
                    SPUser submittingUser = web.SiteUsers[submittingUserName];
                    string fileName = modifiedFileName;
                    // Create a Hashtable of properties which forms the metadata for the file
                    Hashtable fileProperties = EcmDocumentRouter.GetHashtableForRecordsRepositoryProperties(properties, recordSeries);
                    // Hide sensitive content in the file stream.
                    fileContentString = socialSecurityRegex.Replace(fileContentString, "***-**-****");
                    byte[] modifiedByteStream = Encoding.UTF8.GetBytes(fileContentString);
                    SPFolder newFinalFolder = finalFolder;
                    // Alternately, if modifying properties of the document, the following method can be used to find re-evaluate the content organizer rules for the document
                    EcmDocumentRouter contentOrganizer = contentOrganizerWeb.Router;
                    if (ModifyProperties(properties))
                    {
                        newFinalFolder = contentOrganizer.GetFinalRoutingDestinationFolder(properties, recordSeries);
                    }

                    // Modify content (and/or properties) as required and then save the modified content ourselves.
                    using (MemoryStream finalStm = new MemoryStream(modifiedByteStream))
                    {
                        finalStm.Write(modifiedByteStream, 0, modifiedByteStream.Length);
                        // Save the file here since we need to modify the file.
                        EcmDocumentRouter.SaveFileToFinalLocation(contentOrganizerWeb,
                            newFinalFolder,
                            finalStm,
                            fileName,
                            "",
                            fileProperties,
                            submittingUser,
                            true /*override versioning settings on the content organizer and create a new file*/, "");
                    }
                    
                    resultDetails = "File was inspected and sensitive data was found. File has been saved with a custom name.";
                    return CustomRouterResult.SuccessCancelFurtherProcessing;
                }
            }
        }

        private static bool ModifyProperties(RecordsRepositoryProperty[] properties)
        {
            if(properties == null)
                throw new ArgumentNullException("properties");

            // TODO: Replace these variable values and input parameters with your own values.
            // Name of the field to be modified.
            string guidPropertyToModify = SPBuiltInFieldId.Title.ToString();
            // A valid value for the field being modified
            const string modifiedPropertyValue = "Modified by custom router";
            foreach (RecordsRepositoryProperty fileProperty in properties)
            {
                if (fileProperty.Name == guidPropertyToModify)
                {
                    fileProperty.Value = modifiedPropertyValue;
                    return true;
                }
            }
            return false;
        }
    }

}

using System;
using System.Text;
using System.IO;
using Microsoft.SharePoint;
using SPUrlUtility = Microsoft.SharePoint.Utilities.SPUrlUtility;
using EcmDocumentRoutingWeb = Microsoft.Office.RecordsManagement.RecordsRepository.EcmDocumentRoutingWeb;
using EcmDocumentRouter = Microsoft.Office.RecordsManagement.RecordsRepository.EcmDocumentRouter;

namespace Microsoft.SDK.SharePointServer.Samples
{
    /// <summary>
    /// Use this class to re-organize documents within the site based on new content organizer rules.    
    /// </summary>    
    /// <remarks>Assumes that documents are present in a content-organizer enabled web site.</remarks>
    /// <see cref="SPFile.MoveTo()"/>
    /// <see cref="Microsoft.SDK.SharePointServer.Samples.ContentOrganizerCodeSample"/>
    public class ReOrganizeDocuments
    {
        /// <summary>
        /// Moves the input file to a location as specified by the content organizer rules
        /// </summary>
        /// <param name="sourceFile">File to be reorganized</param>
        /// <returns>The new file object if the input file could be organized, otherwise the input file object.</returns>
        public SPFile ReorganizeFile(SPFile sourceFile)
        {
            if (sourceFile == null)
            {
                throw new ArgumentNullException("sourceFile");
            }
            SPListItem sourceItem = sourceFile.Item;
            if (sourceItem == null)
            {
                throw new ArgumentException("Only files in document libraries can be organized");
            }
            // Caller must close this after the lifetime of the file object.
            SPWeb parentWeb = sourceItem.Web;
            EcmDocumentRoutingWeb contentOrganizerWeb = new EcmDocumentRoutingWeb(parentWeb);
            if (!contentOrganizerWeb.IsRoutingEnabled)
            {
                // There is no infrastructure for organizing this file.
                return sourceFile;
            }

            // Find the final location for this file based on its metadata
            string finalFolderUrl = contentOrganizerWeb.Router.GetFinalRoutingDestinationFolderUrl(sourceItem);
            if (!string.IsNullOrEmpty(finalFolderUrl))
            {
                // Absolute url of a new location has been found for this file.                
                SPFolder targetFolder = parentWeb.GetFolder(finalFolderUrl);
                // Find the new path for this file
                string newFileUrl = SPUrlUtility.CombineUrl(targetFolder.ServerRelativeUrl, Path.GetFileName(sourceFile.Url));
                // If a file exists with the same name, append a new version
                sourceFile.MoveTo(newFileUrl, SPMoveOperations.Overwrite, false /*requireWebFilePermission*/);
                return parentWeb.GetFile(newFileUrl);
            }
            // Routing the file was unsuccessful. Most likely reasons:
            // 1. There were no rules configured for the properties (metadata) on this file
            // 2. File did not have a value for all required metadata
            // 3. Rule that matched this file required the file to be sent to an external location.
            return sourceFile;
        }
    }
}

Thread Safety

Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

See Also

Reference

EcmDocumentRouter Members

Microsoft.Office.RecordsManagement.RecordsRepository Namespace

EcmDocumentRoutingWeb

Router

RoutingRuleCollection

RouteFileToFinalDestination(SPListItem, String, Boolean)

SaveFileToFinalLocation(EcmDocumentRoutingWeb, SPFolder, Stream, String, String, Hashtable, SPUser, Boolean, String)

EcmDocumentRouterRule

EcmDocumentRouterRuleCollection