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.
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:
<Condition Column='column' Operator='operator' Value='value'>
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:
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
$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
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.