Wednesday, February 18, 2015

Deploying Managed Metadata Fields declaratively in SharePoint - Tips and Tricks

In this article, we will see how we can deploy Managed metadata fields in a declarative way in SharePoint 

A managed metadata column lets you control the information that people can enter into a column. Users select the terms or phrases that they enter in the column from a pre-defined set of managed terms.

The challenge comes when you want to provision your custom managed metadata navigation field in a declarative way, and adding this column to a content type or a list instance without losing all the powerful features that comes along using this field type.

Out of the Box, if you added a managed column to your list SharePoint performs some actions behind the scene:
  1. Creates a hidden note field attached to your column
  2. Attached your list to two event receivers to update the taxonomy hidden list .
  3. Fills in some properties related to the current web and taxonomy list id
  4. Creates a taxonomy search managed  property after a full crawl that could be used as a refiner or even as a category column if you are planning to use the Product Catalog model.
So there are a lot of articles explaining how to deploy Managed Metadata Fields declarative in SharePoint, but they are scattered :) , so I decided to aggregate all the tips and tricks to cover this task.

  • Taxonomy Field  declarative XML, no tricks here :)

<Field ID="{20A3C69E-FFFB-43F4-BBDF-2D22BAF0EB84}"
         Sortable="TRUE" />

  • SharePoint creates a hidden note field to be field with selected values while filling the taxonomy field, here comes the tricky part, while adding your custom field to the list you MUST add this hidden field along with it, The Display name should follow the following convention 'TaxonomyField_0' and the static name should follow the following convention 'TaxononmyFieldTaxHTField0', without following this conventions SharePoint search will not generate the crawled property for your custom column 'ows_taxId_CustomEventType', and for sure will not create the managed property 'owstaxidKataraEventType' 

<Field ID="{09F37A61-50FE-413E-941F-3BEE2A1B5BF8}"
         Hidden="TRUE" />
  • After declaring the schema of both field you should get them connected together, and should attach the created column to a specific Term Set and specific Term to choose from as a source, this could be achived by adding a feature event receiver using the following code snippet.

SPSite site = properties.Feature.Parent as SPSite;
Guid eventFieldId = new Guid("{20A3C69E-FFFB-43F4-BBDF-2D22BAF0EB84}");

if (site.RootWeb.Fields.Contains(eventFieldId))
 TaxonomySession session = new TaxonomySession(site);
 if (session.TermStores.Count != 0)
  var termStore = session.TermStores["Managed Metadata Serivce"];
  var group = termStore.Groups["YOUR GROUP NAME"];
  var termSet = group.TermSets["YOUR TERM SET NAME"];

  var eventTypeTerm = termSet.Terms["THE TERM NAME CONTAINING YOUR VALUE"];

  TaxonomyField eventField = site.RootWeb.Fields[eventFieldId] as TaxonomyField;
  //Attach the note field to the taxonomy field
  eventField.TextField = new Guid("{09F37A61-50FE-413E-941F-3BEE2A1B5BF8}"); 

  // Connect to MMS
  eventField.SspId = termSet.TermStore.Id;
  eventField.TermSetId = termSet.Id;
  eventField.TargetTemplate = string.Empty;
  eventField.AnchorId = eventTypeTerm.Id;
  eventField.LookupWebId = site.RootWeb.ID;
  if (eventField.TypeAsString == "TaxonomyFieldTypeMulti")
   ageGroupField.AllowMultipleValues = true;
  • In this stage we have our field provisioned and added to our site as a site column, the next step is to add it to a content type, the trick here is we must add two hidden fields 'TaxCatchAll' & 'TaxCatchAllLabel' fields,If it doesn’t, then you won’t get facets showing up correctly in faceted search. Note that not having the TaxCatchAll and TaxCatchAllLabel pair of columns in your list or library or content type can cause that - See more at:
  •  so our field references will be like the following 

<FieldRef ID="{20A3C69E-FFFB-43F4-BBDF-2D22BAF0EB84}" DisplayName="$Resources:FILENAME_Columns,EventType;" Required="TRUE" Name="CustomEventType" Filterable="TRUE" Sortable="TRUE" />
<FieldRef ID="{09F37A61-50FE-413E-941F-3BEE2A1B5BF8}" DisplayName="CustomEventType_0" Hidden="TRUE" Name="CustomEventTypeTaxHTField0" />
<FieldRef ID="{f3b0adf9-c1a2-4b02-920d-943fba4b3611}" DisplayName="Taxonomy Catch All Column" Required="FALSE" Hidden="TRUE" Name="TaxCatchAll" Sealed="TRUE" Sortable="FALSE" />
<FieldRef ID="{8f6b6dd8-9357-4019-8172-966fcd502ed2}" DisplayName="Taxonomy Catch All Column1" Required="FALSE" Hidden="TRUE" Name="TaxCatchAllLabel" ReadOnly="TRUE" Sealed="TRUE" Sortable="FALSE" />
  •  Now we come to the next trick, referencing those columns to a list template in the schema.xml file , the trick here is to declare the TaxHiddenList  'TaxCatchAll' & 'TaxCatchAllLabel' correctly to the the list schema, those fields are a lookup columns so they need the information list (source), following is the definetion, NOTE the List attribute. 

<Field Type="LookupMulti" DisplayName="Taxonomy Catch All Column" StaticName="TaxCatchAll" Name="TaxCatchAll" ID="{f3b0adf9-c1a2-4b02-920d-943fba4b3611}" ShowInViewForms="FALSE" List="Lists/TaxonomyHiddenList" Required="FALSE" Hidden="TRUE" CanToggleHidden="TRUE" ShowField="CatchAllData" SourceID="{1e46f7fe-3764-40b5-abd1-1746c716214b}" Mult="TRUE" Sortable="FALSE" AllowDeletion="TRUE" Sealed="TRUE" Version="2" />
<Field Type="LookupMulti" DisplayName="Taxonomy Catch All Column1" StaticName="TaxCatchAllLabel" Name="TaxCatchAllLabel" ID="{8f6b6dd8-9357-4019-8172-966fcd502ed2}" ShowInViewForms="FALSE" List="Lists/TaxonomyHiddenList" Required="FALSE" Hidden="TRUE" CanToggleHidden="TRUE" ShowField="CatchAllDataLabel" FieldRef="{F3B0ADF9-C1A2-4b02-920D-943FBA4B3611}" SourceID="{1e46f7fe-3764-40b5-abd1-1746c716214b}" ReadOnly="TRUE" Mult="TRUE" Sortable="FALSE" AllowDeletion="TRUE" Sealed="TRUE" Version="2" />

  • Finally, we need to attache the "TaxonomyItemSynchronousAddedEventReceiver" & "TaxonomyItemUpdatingEventReceiver" to update all the hidden fields.

  <Assembly>Microsoft.SharePoint.Taxonomy, Version=, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly>
  <Assembly>Microsoft.SharePoint.Taxonomy, Version=, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly>

Now you are all set to enjoy many many features and ideas that this colum type offers :)