SharePoint 2010 Content Organizer - Client Object Model

by Scosby Wednesday, March 16, 2011

Introduction

SharePoint 2010 introduced the Content Organizer to provide a more formal routing infrastructure for site content. Based on a list of configured rules, content is routed to a library based on conditions such as column values or content type. Read more about the Content Organizer on MSDN. This post assumes the user has installed the SharePoint 2010 Client Object Model redistributable.

Contents

Using the Content Organizer

One particular feature of the Content Organizer is to redirect users to the Drop Off library. This setting is configured in the Content Organizer Settings link located under Site Administration on the Site Settings page. (See figure 1)

Figure 1 – Content Organizer Redirect Setting

Enabling this setting will send all content for a library that has a configured rule to the drop off library. In other words, if users upload a document to the library it will be sent to the drop off library instead. This only applies to libraries that are listed as “target libraries” in a Content Organizer Rule. (See figure 2).

Figure 2 – Content Organizer Rule configured to a target library

Once a new item has been routed to the drop off library, either a manager or a timer job will move the item to its final location once the rule’s conditions have been met. The final location is the target library defined by the rule. A daily timer job, conspicuously called “Content Organizer Processing” (see figure 3), is created for each web application with a content organizer enabled site. This job will evaluate items in the drop off library against the rule’s conditions. The item must satisfy all the rule’s conditions in order to be moved to the target library. Unless a manager submits content before the job is run, any content matching the rule’s conditions will not be routed to the target library (final location) until the job has run. It is possible to run the job on demand, or have a manager submit the item in the drop off library, to move items immediately.

Figure 3 - Content Organizer Timer Job

Problem

When using the SharePoint Client Object model, there is no implementation provided for determining content organizer rules. Additionally, uploading with the client object model ignores the redirect users setting and bypasses the content organizer, ignoring any rules defined for a target library.  By design, when redirecting users to the drop off library, the content organizer restricts a library’s available content types for new documents or uploads to those defined as content organizer rules specifying that library as a target. If no content organizer rules exist, then the library will behave as it does without the content organizer.

Solution

Despite the lack of built in support with the client object model for the content organizer, it is possible to discover the rules and honor the redirect setting. The solution is to mimic the content organizer redirect behavior by releasing to the drop off library instead of uploading to the target library. This post will demonstrate how to retrieve the list of enabled content organizer rules using a CAML query. If there is a rule for the release content type and the redirect users setting is enabled, then files should go to the drop off library instead of the target library.

Designing the Solution

The solution has three high-level steps to perform.

Checking the Content Organizer

Using the client object model, it is possible for the user to determine if a Microsoft.SharePoint.Client.Web (Web) is using the content organizer. The field client_routerenforcerouting should be checked for a bool indicating if the Web is redirecting users to the drop off library.

The user should not simply check for this property on the Web and assume all libraries have rules, however. SharePoint requires the user to define content organizer rules to control the routing of content. Thus, the user must retrieve the content organizer rules and evaluate them against the intended content type for an upload. If a rule matches the content type, then the content should be sent to the drop off library instead of the target library, again, only if client_routerenforcerouting is true.

If a Web does not enforce the content organizer redirect, it is safe to upload directly to any library. Thus, retrieving rules for a Web without the content organizer redirect would not be necessary. This method assumes the user has loaded the Web.AllProperties property on the Web context as follows, else an exception is thrown: context.Load(context.Web, x => x.AllProperties)

Code Sample – checking for content organizer rules


        private static bool RequiresContentOrganizer(Web web)

        {

            if (!web.IsObjectPropertyInstantiated("AllProperties"))

            {

                throw new InvalidOperationException("Web.AllProperties is not initialized!");

            }

 

            string fieldName = "client_routerenforcerouting";

 

            Dictionary<string, object> fieldValues = web.AllProperties.FieldValues;

 

            if (fieldValues.ContainsKey(fieldName))

            {

                object value = fieldValues[fieldName];

 

                if (value != null)

                {

                    bool result = false;

 

                    if (bool.TryParse((string)value, out result))

                    {

                        return result;

                    }

                    else

                    {

                        throw new InvalidOperationException("Unexpected field value in Web properties for content organizer redirect setting!");

                    }

                }

            }

 

            return false;

        }

Query the Content Organizer Rules

The content organizer rules are created in a site list called “Routing Rules”.  This special list has default views which group the rules. The user can choose to display the rules grouped by Content Type or Target Library. Either grouping will provide a collapsible section so the user can easily navigate complex sets of routing rules.

In order for the client object model to release to the drop off library, instead of the target library, it is necessary to construct a CAML query against the routing rules list. The query will return the view fields defined in the special “Rule” content type and allow the user to inspect the routing rule from the client object model.

Querying the rules list can be split into two parts:

·         Find the list.

·         Query the list.

When using the SharePoint client object model, it is possible for the user to easily construct a simple “All Items” CAML query. The user can specify a row limit and a collection of fields to include in the search results. This approach reduces the amount of XML manipulation the user must perform to build the CAML query.

Code Sample – Part One: Find the List

The routing rules list URL should be consistent across Language packs; you can check the RoutingRuleList_ListFolder key in the dlccore resx file located in the SharePoint %14 Hive%\Resources directory. This means that regardless of your site’s language, you can rely on SharePoint naming the routing rules list URL as “…/RoutingRules”.

        string webUrl = context.Web.ServerRelativeUrl;

 

        //Handle root sites & sub-sites differently

        string routingRulesUrl = webUrl.Equals("/", StringComparison.Ordinal) ? "/RoutingRules" : webUrl + "/RoutingRules";

 

        List routingRules = null;

 

        ListCollection lists = context.Web.Lists;

 

        IQueryable<List> queryObjects = lists.Include(list => list.RootFolder).Where(list => list.RootFolder.ServerRelativeUrl == routingRulesUrl);

 

        IEnumerable<List> filteredLists = context.LoadQuery(queryObjects);

 

        context.ExecuteQuery();

 

        routingRules = filteredLists.FirstOrDefault();

 

        if (routingRules == null)

        {

            throw new InvalidOperationException("Could not locate the routing rules list!");

        }

Code Sample – Part Two: Query the List

The query should filter the list results to include only the active (based on the RoutingEnabled field) content organizer rules. The user should notice how the CAML query is restricted to a row limit of 100 and defines a custom set of view fields in the static CamlQuery.CreateAllItemsQuery method. This method generates a basic CAML query, which is then parsed with LINQ  to XML and modified to include the query element.

        private CamlQuery GetContentOrganizerRulesCaml()

        {

            string[] viewFields = new string[]

               {

                   "RoutingConditions",

                   "RoutingContentTypeInternal",

                   "RoutingPriority",

                   "RoutingRuleName",

                   "RoutingTargetFolder",

                   "RoutingTargetLibrary",

                   "RoutingTargetPath"

               };

 

            //view...

            CamlQuery caml = CamlQuery.CreateAllItemsQuery(100, viewFields);

 

            XElement view = XElement.Parse(caml.ViewXml);

 

            //query...

            XElement routingEnabled = new XElement("Eq",

                new XElement("FieldRef", new XAttribute("Name", "RoutingEnabled")),

                new XElement("Value", new XAttribute("Type", "YesNo"), "1"));

 

            XElement query = new XElement("Query", new XElement("Where", routingEnabled));

 

            //Add query element to view element

            view.FirstNode.AddBeforeSelf(query);

 

            caml.ViewXml = view.ToString();

 

            return caml;

        }

 Evaluate the Rules and Conditions

After the user gets the Content Organizer rules it is important to match the release content type id with any of the rules. Should there be a match, the user must release to the drop off library instead of the content type’s library. If no rules match the upload’s content type, then the content can be sent to the content type’s library.

Code Sample – Evaluate Rules and Send Content

This method demonstrates how the user can parse the search results from the Rules List. The “RoutingContentTypeInternal” field needs to be split, in order to determine the rule’s content type and content type ID. If a rule matches, then the user can determine where to correctly send the content.

        private static void EvaluateRules(ListItemCollection items)

        {

            string yourContentTypeId = "0x01010B"; //replace with your upload content type ID.

 

            ListItem rule = null;

 

            foreach (ListItem item in items)

            {

                string contentType = null;

 

                string contentTypeId = null;

 

                if (item.FieldValues.ContainsKey("RoutingContentTypeInternal"))

                {

                    object value = item.FieldValues["RoutingContentTypeInternal"] ?? string.Empty;

 

                    string[] values = value.ToString().Split(new char[] { '|' }, StringSplitOptions.None);

 

                    if (values.Length == 2)

                    {

                        contentType = values[1];

 

                        contentTypeId = values[0];

                    }

                }

 

                if (yourContentTypeId == contentTypeId)

                {

                    rule = item;

 

                    break;

                }

            }

 

            if (rule != null)

            {

                //send to drop off library...

            }

            else

            {

                //send to content type library...

            }

        }

Summary

This post explained the difficulty of using the client object model to release content to a Web with the content organizer enabled (redirecting users to drop off library) and determining which libraries have been impacted by the content organizer rules. This post explained several code snippets from the attached sample class file, so the user can better understand how to implement and use the object model.

Scosby Content Organizer.zip (1.73 kb)

Tags: , ,

IT | Programming