Configuring Content Organizer Rules with PowerShell

As you probably know from my previous posts, I have been configuring a wide-scale document management solution using Content Organizer feature of SharePoint. The idea is to use managed metadata to tag the document with information about the business unit and region it originates from (in my case Region and Section metadata columns) and let SharePoint classify it to the correct site and document library. I wrote about how to expose the cross-site content organizer hubs a few months ago.

As I had many regions and many sections to configure, I had to manage to build the whole hierarchy with a PowerShell script instead of doing the work by hand. In this post I will share with you the things I learnt by doing so.

Anatomy of the Content Organizer Rules

When you click the Content Organizer Rules in Site Settings, it will show you the contents of a hidden list called "Content Organizer Rules". You can see it adding /RoutingRules to the site URL.

image

Each rule is a list item with several fields of importance:

  • RoutingEnabled: this column should be set to 0 or 1 in order to disable or enable the rule.
  • RoutingPriority: a number from 1 to 10. 1 is the highest priority and 10 is the lowest one. A rule with higher priority will run BEFORE any rules with lower priority.
  • RoutingRuleName: a string with the rule name.
  • RoutingContentType: if the rule applies to a specific content type, here you should put the content type name (not the ID).
  • RoutingContentTypeInternal: this is the content type ID for the content type specified in the RoutingContentType field concatenated with the content type name using the pipe ‘|’ character as separator
  • RoutingRuleExternal: if the content organizer rule should route to another site, this field should be set to 1. Set it to 0 if the routing is done in the same site as the rule.
  • RoutingTargetLibrary: the destination library if the document is routed in the same site.
  • RoutingTargetFolder: the destination folder if the folder classification is used.
  • RoutingTargetPath: if you are routing to another site, this should be the name of the content organizer source (set in Central Administration).
  • RoutingConditions: this is an XML string with the routing condition. The XML syntax will be explained later.

In order to programatically create these rules, SharePoint server object model exposes a class named EcmDocumentRouterRule. This class simply surfaces the underlying list columns as class properties.

In order to create a new rule, just instantiate the EcmDocumentRouterRule passing the SharePoint site for the rule in the constructor (the SPWeb object). Populate the properties and call the Update() method on the rule. Job done!

Rule Condition XML

The most complex part of the creating the rule in code is how to correctly construct the condition XML. It should be in the form of:

<Conditions>
  <Condition Column=’column’ Operator=’operator’ Value=’value’>
  </Condition>

</Conditions>

All the children nodes of Conditions are evaluated together (i.e. it’s an AND of all of them). Each condition specifies the Column that is evaluated, the operator and the value to compare against.

The column is comprised of several pieces: field GUID|field internal name|field display name.  (in my case it was ‘5bc078e1-bcf6-4475-aadf-2b567726c696|Region|Region’)

The available operators are:

  • IsEqual
  • IsNotEqual
  • GreaterThan
  • LessThan
  • GreaterThanOrEqual
  • LessThanOrEqual
  • BeginsWith
  • NotBeginsWith
  • EndsWith
  • NotEndsWith
  • Contains
  • NotContains
  • EqualsOrIsAChildOf
  • NotEqualsOrIsAChildOf
  • IsEmpty
  • IsNotEmpty
  • ContainsAny
  • ContainsAnyOrChildOf
  • ContainsAllOrChildOf

The value is the literal value to compare against. If you are comparing against managed metadata columns, the value specified as ’16;#Austria|1953bfe9-95d0-4ec8-8b9f-7a58169a9a53Region’. The left part is the underlying lookup value for the managed metadata field and the right part is the Term ID for the selected term.

Note: all the managed metadata columns in SharePoint are implemented as lookup columns to a site-collection root site hidden list. Every time a new managed metadata value is added to a list item, SharePoint adds a new entry to this hidden list. The consequence of this implementation is that you must get the lookup value IDs to make a CAML query against managed metadata using TaxonomyField.GetWssIdsOfTerm method.

Putting it all together

So, let’s say that we have the text value of the managed metadata column and we want to create a content organizer rule that will route the document with the content type "My Content Type" to a different site when the value of that column matches our text value or its children (in my case the column name and the term set name is Region and its value is ‘Spain’). How should we construct our PowerShell script to do it?

First of all, we have to retrieve the Term ID that corresponds to the taxonomy node for the term set "Region" with the value of "Spain". In the $web variable we have the SPWeb object for the site we want to create the rule for.

$regionValue = ‘Spain’
$ts = Get-SPTaxonomySession -Site $web.Site
$tstore = $ts.TermStores[0]
$tgroup = $tstore.Groups["Group Name"]
$tset = $tgroup.TermSets["Region"]
$term = $tset.GetTerms($regionValue, $true)
$termValueGuid = $term.Id

Now we have the term ID that corresponds to ‘Spain’ entry in the ‘Region’ term set in the ‘Group Name’ term set group. Now we need to construct the full literal value of the managed metadata column (the lookup part and the GUID part). In order to do so, we use the TaxonomyFieldValue.PopulateFromLabelGuidPair method. It consists of the text value (‘Spain’) and its Guid separated by the pipe character (‘|’).

$docLib = $web.Lists["Document Library Name"]
$regionField = [Microsoft.SharePoint.Taxonomy.TaxonomyField]$docLib.Fields["Region"]
[Microsoft.SharePoint.Taxonomy.TaxonomyFieldValue]$taxonomyFieldValue = New-Object Microsoft.SharePoint.Taxonomy.TaxonomyFieldValue($regionField)   
$taxonomyFieldValue.PopulateFromLabelGuidPair([Microsoft.SharePoint.Taxonomy.TermSet]::NormalizeName($region) + "|" + $termValueGuid)

Now we can create the content organizer rule. Remember that the column name for the condition XML is the field Guid, static name and display name. In this case we route the document to another content organizer called ‘Spain’, that’s why the rule is marked as External. The value for the taxonomy field is retrieved using the ValidatedString property of the TaxonomyFieldValue object.

[Microsoft.Office.RecordsManagement.RecordsRepository.EcmDocumentRouterRule]$rule = New-Object Microsoft.Office.RecordsManagement.RecordsRepository.EcmDocumentRouterRule($web)
$rule.ConditionsString = "<Conditions><Condition Column=’" + $regionField.Id + "|Region|Region’ Operator=’EqualsOrIsAChildOf’ Value=’" + $taxonomyFieldValue.ValidatedString +  "’></Condition></Conditions>"
$rule.Name = $region + " rule"
$rule.ContentTypeString = $web.AvailableContentTypes["My Content Type"]
$rule.RouteToExternalLocation = $true
$rule.Priority = "5"
$rule.TargetPath = $regionValue
$rule.Enabled = $true
$rule.Update()

 

Summary

I hope that this code snippet can save you some time if you create a lot of content organizer rules and want to avoid doing it by hand.

Building a Document Routing Hierarchy with SharePoint Subsites and Content Organizer

In this occasion I have been exploring the possibility of an auto-organizing document hierarchy in SharePoint 2010, made with Content Organizer functionality. As you may recall, the Content Organizer allows the documents to be routed according to rules that use metadata to decide where the document should go. This greatly enhances the usability of a document repository in SharePoint, as the end users don’t have to know where exactly should the document be uploaded. By leveraging the content organizer rules, we automate the exact logistic organization of the documents and significantly lower the possibility of incorrect classification.

Content Organizer out-of-the-box

The straightforward Content Organizer works great when you cope with different document libraries on a single site. You get one “Drop Off Library” where you should upload the documents to. Once uploaded there, the documents will be routed to the right document library and optionally inside a specific folder.

The user interface for dropping a document in Content Organizer notifies you that the document will be routed:

image

As you can see in the Content Organizer Rule editor, we only get local site lists and libraries as the destination option:

image

What happens when you have a hierarchy that spans multiple subsites that all share the same base content type but are strictly separated in different subsites for security reasons? Well, in this case you have to tweak the Content Organizer a bit to accommodate the subsites.

Routing Documents to a different site

In order to allow a content organizer to route a document to a different site, you have to create a “Send To” connection in the Central Administration. Go to “General Application Settings”, then choose “Configure send to connections” in “External Service Connection” section. In this page you will have to add the absolute path of the content organizer service of the site that you wish to route the document to. The URL is always the same:

  •  Site URL followed by /_vti_bin/officialfile.asmx

In this example, there is a subsite called “Global” and the “send to” connection" called Global is created. Please remember that the Send To Connections configuration is stored for each web application, so make sure that you are changing it for the right web application.

image

Once you have the Send To connection registered in Central Administration, you have to change two things in the site that you wish to be the entry point to the system. Go to “Site Settings”, “Content Organizer Settings” and make sure that the checkbox “Sending to Another Site” is set.

SNAGHTML1c7e8ab7

Now you can go to “Site Settings”, “Content Organizer Rules” of the site and create a rule that can target another site.

image

There is one limitation to this approach: you can target a different site but you can’t target a specific document library on that site. The document will be routed to the Content Organizer on that site and its rules will be enforced. So, in order to overcome this limitation you have to add a rule on the destination site that will route the document into a specific document library.

As it begins to become a little tricky to explain in words, I’ll draw a quick diagram to explain how my system works:

image

I add the routing rules to the Root Site that will send the newly uploaded document to the correct site, according to a Type column (in my case it’s a Managed Metadata column but it could be any type of column that can be compared to). When the document arrives to the Content Organizer on the destination site, I put two simple rules there:

  • If the Type of the document is the correct one, I move the document to the corresponding document library
  • If the Type of the document is not the correct one, I route the document again to the Root Site

The purpose of this loop is to minimize the number of rules for the correct classification. If the user uploads a Sales document on a HR site, I’d have to write the rule that moves it to the Sales site. By keeping all the routing logic for the different site at the Root Site level, I just have to send the document to the root site in order to get classified correctly.

Note: this setup can cause infinite loop if you mess with the rules and conditions, so please double-check them.

PowerShell to the Rescue

So, we have seen how to organize a multiple-site hierarchy with the Content Organizer feature. I admit that the only boring thing in the whole process is the act of building the “Send To” connections by hand. I have created a tiny PowerShell script that will do that for you. It will parse the given web application URL, iterate over all the sites in the site collections and then will add the sites with active Content Organizer to the “Send To” connections.

image