Re-Submitting files with the Content Organizer (Submitting files through code)

About a week ago I wrote an article about the “Content Organizer” feature of SharePoint 2010.

The “Content Organizer” feature makes it possible to route all kind of different information within your SharePoint site. If you want to read more about this feature click here.

In certain scenarios you would like to be able to re-submit documents when certain metadata of that document is changed. Out of the box this isn’t possible this means you will have to create a custom solution.

What you can do is create a list settings page that you will have to register on document libraries.

With this page you can enable the re-submitting feature and select the fields that have to be changed if the file needs to be re-submitted.

If you enable the re-submitting feature on the page it will install an event receiver on the current list. This event receiver will run on the ItemUpdated event. In the ItemUpdated event you can then check which fields are changed and then send the document for re-routing.

I hear you asking how can I submit a file for routing. Let’s take a look at the ItemUpdated receiver I have written.

/// <summary>
/// An item is being updated.
/// </summary>
public override void ItemUpdated(SPItemEventProperties properties)
{
    base.ItemUpdated(properties);

    if (CheckForReRouteItem(properties))
    {
        if (ReRoutItem(properties))
        {
            properties.ListItem.Delete();
        }
    }
}

This code will be executed by the event Receiver. The method Check ForReRouteItem will check if certain fields were changed.

private bool CheckForReRouteItem(SPItemEventProperties properties)
{
    List<string> changesFields = GetField(properties);

    foreach (string field in changesFields)
    {
        if (properties.AfterProperties[field] != properties.BeforeProperties[field])
        {
            return true;
        }
    }

    return false;
}

In the above method the fields for which we have to check if they are changed are retrieved by another method called GetFields(properties). This method will retrieve the fields that are saved in the property bag by the list settings page.

If a field is changed the method will return true to indicate that the files needs to be submitted to the Content Organizer.

private bool ReRoutItem(SPItemEventProperties properties)
{
    Guid webId = properties.Web.ID;
    Guid siteId = properties.SiteId;
    bool retVal = false;

    SPSecurity.RunWithElevatedPrivileges(delegate()
    {
        using (SPSite site = new SPSite(siteId))
        {
            using (SPWeb web = site.OpenWeb(webId))
            {
                SPFile file = properties.ListItem.File;
                string fileType = properties.ListItem.ContentType.Name;
                SPFieldCollection fields = file.Item.Fields;
                List<RecordsRepositoryProperty> itemProperties = new List<RecordsRepositoryProperty>();

                foreach (SPField spField in fields)
                {
                    try
                    {
                        string fieldValue = file.Item[spField.Id] != null ? file.Item[spField.Id].ToString() : string.Empty;
                        string value = spField.GetFieldValue(fieldValue) as string;
                        RecordsRepositoryProperty property = new RecordsRepositoryProperty
                        {
                            Name = spField.Title,
                            Type = spField.TypeAsString,
                            Value = value
                        };
                        itemProperties.Add(property);
                    }
                    catch (ArgumentException) { }
                }

                string result = string.Empty;
                OfficialFileResult outcome = OfficialFileCore.SubmitFile(properties.Web, file.OpenBinary(), itemProperties.ToArray(), fileType, file.Web.Url + file.Url, out result);
                if (outcome == OfficialFileResult.Success)
                {
                    retVal = true;
                }
            }
        }
    });

    return retVal;
}

The above method performs the actual submitting of the file to the Content Organizer by calling OfficialFileCore.SubmitFile().

This method will return an OfficialFileResult to indicate whether the submitting was successful of not.

Important: With submitting the file we had a problem because it was giving us an outcome of NotFound every time we submitted the file. After debugging the SharePoint dlls we found out that Microsoft performs the following method EcmDocumentRoutingWeb.IsSubmitter(web)):

internal static bool IsSubmitter(SPWeb web)
{
    if (web == null)
    {
        return false;
    }
    bool flag = false;
    try
    {
        string str = web.Properties["_dlc_RepositoryUsersGroup"];
        if (!string.IsNullOrEmpty(str))
        {
            flag = web.IsCurrentUserMemberOfGroup(int.Parse(str, NumberFormatInfo.InvariantInfo));
        }
    }
    catch (FileNotFoundException exception)
    {
        ULS.SendTraceTag(0x636d7539, ULSCat.msoulscat_DLC_Routing, ULSTraceLevel.Medium, "Routing Engine: User does not have permissions to submit to this site. Exception: {0}", new object[] { exception.Message });
        flag = false;
    }
    return flag;
}

In this method Microsoft checks if the current user is in a specific group to allow the submission of the file. If you are not in this group Microsoft will return an outcome of NotFound.

I hope this post will help you guys develop cool SharePoint solutions.

5 Replies to “Re-Submitting files with the Content Organizer (Submitting files through code)”

  1. Hey Maik.
    We’ve set the SPEventReceiver to Synchronous, so it will not interfere with other actions.
    Unfortunately this gives an error on the Microsoft.SharePoint.WebControls.SaveButton.OnBubbleEvent as it tries to get the SPFile we’ve just deleted.
    I finally fixed this by modifying the HTTPContext.Response similar to the OnBubbleEvent.

    1. Hi Bas, could you post your fix to the OnBubbleEvent error?
      Also I had to use a different code to get it to move properly:

      private static void ReRouteItem(SPItemEventProperties properties)
      {
      SPSecurity.RunWithElevatedPrivileges(delegate
      {
      using (var site = new SPSite(properties.SiteId))
      using (var web = site.OpenWeb(properties.Web.ID))
      {
      var ecmWeb = new EcmDocumentRoutingWeb(web);
      var final = ecmWeb.Router.GetFinalRoutingDestinationFolderUrl(properties.ListItem);
      if (string.IsNullOrEmpty(final)) return;
      var targetFolder = web.GetFolder(final);
      if (targetFolder.ServerRelativeUrl == properties.ListItem.File.ParentFolder.ServerRelativeUrl)
      return;
      var newUrl = SPUrlUtility.CombineUrl(targetFolder.ServerRelativeUrl,
      Path.GetFileName(properties.ListItem.File.Url));
      using (new DisableItemEventFiring())
      {
      properties.ListItem.File.MoveTo(newUrl, SPMoveOperations.None, false);
      }
      }
      });
      }

  2. Hi Maik

    Thanks a lot for the code. It all seems to be working but the content organizer rule is not creating folders at the destination as defined in organizer rules. I have checked that the field that I am using to create a folder is getting populated correctly. Can you suggest what could be wrong?

    Thanks in anticipation.
    Rajat.

    1. Hi Rajat,

      We never got in the situation were the folders were not created. I will have to look deeper in the problem. Are you receiving any errors in the event viewer or ULS log.

  3. Thanks so much for this post (specifically the “important” section). It saved me a lot of time from debugging.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.