Monday, 21 January 2013

Using Powershell to import a Managed Metadata termset from XML file

This post follows on from Part 1

Importing a termset from XML file 

Our XML file will have the following structure:
   1: <xml version="1.0">

   2: <TermStore Name="ABC" GUID="xxxx-xxxx-xxxx-xxxx">

   3:     <TermSetGroup Name="DEF" GUID="xxxx-xxxx-xxxx-xxxx">

   4:         <TermSet Name="GHI" GUID="xxxx-xxxx-xxxx-xxxx">

   5:             <Term>

   6:             <GUID>xxxx-xxxx-xxxx-xxxx</GUID>

   7:             <Name>JKL</Name>

   8:             <Description>blah</Description>

   9:             <IsAvailableForTagging>False</IsAvailableForTagging>

  10:                 <ChildTerms>

  11:                     <ChildTerm>

  12:                     <GUID>xxxx-xxxx-xxxx-xxxx</GUID>

  13:                     <Name>JKL Child</Name>

  14:                     <Description>blah</Description>

  15:                     <IsAvailableForTagging>True</IsAvailableForTagging>

  16:                     </ChildTerm>

  17:                 </ChildTerms>

  18:             </Term>

  19:         </TermSet>

  20:     </TermSetGroup>

  21: </TermStore>

First of all add the SharePoint Shell into the script:


   2: Add-PSSnapin “Microsoft.SharePoint.Powershell” –ErrorAction SilentlyContinue


Then create a SharePoint Taxonomy session, and open the termstore on the target site collection :

   1: $siteUrl = “http://mySharePointSite/”

   2: $session = new-object Microsoft.SharePoint.Taxonomy.TaxonomySession($siteUrl)

   3: $Termstore = $session.TermStores[0]

Next open the XML file for reading using the Get-Content command


   1: $DocLoc =“C:\myXML.xml”

   2: [XML]$XMLTermset = get-content$DocLoc

Now you can loop round each of the nodes in the XML document, first of the groups (folder in termstore manager). Determine if it is present already in the termstore and add if necessary:

   1: foreach($XMLGroup in $XMLTermset.TermStore.TermSetGroup)

   2: {

   3:  #Loop round each TermSetGroup node in the XML Document

   4:  if ($TermStore.Groups[$XMLGroup.Name]–eq $null)

   5:  #Test to see if the group exists in the termstore

   6:    {

   7:     $Group = $TermStore.CreateGroup($XMLGroup.Name)

   8:     #If the group doesn’t exist create it using the creategroup method 

   9:    }

  10:  else 

  11:    {

  12:     $Group = $TermStore.Groups[$XMLGroup.Name)

  13:     #Else set the variable group

  14:    }

Whilst in the group loop we can setup the termsets within that group. Again test to see if the termset exists and create it if not.

N.B. The method for creating a termset is similar to that of a group however this time we also specify the GUID for the termset. The reason for this is, if you create a Visual Studio SharePoint package with a Managed Metadata column (either by hand writing it or extracting from a WSP) part of the definition is the termset GUID, ergo when you create the termset it needs to have the same GUID as in the SharePoint solution.  

   1: foreach ($XMLTermset in $XMLGroup.Termset)

   2: {

   3:     if ($Group.Termset[$XMLTermset.GUID] -eq $null)

   4:     #Test to see if the termset exists

   5:     {

   6:         $TermsetGUID = [guid]$XMLTermset.GUID

   7:         $Termset =  $Group.CreateTermSet($XMLTermset.Name, $TermsetGUID)

   8: `       #If the termset does not exist create a GUID object with the GUID in the xml node then create the termset using the createTermSet method, passing the GUID and the name of the termset

   9:     }

  10:     else 

  11:     {

  12:         $TermSet = $Group.TermSets[$XMLTermset.GUID]

  13:         #If the termset already exists set the variable termset

  14:     }

Whilst in the termsets loop we can setup the Terms within that termset. Again test to see if the term exists and create it if not.

N.B. Again we specify the new items (in this case the term) GUID. This is for the same reason as above and also items which use the Managed Metadata column only store the GUID of the term, therefore the GUID’s need to match. Other properties can be set at the same time, the example below sets whether the term is available for tagging.

   1: foreach ($XMLTerm in $XMLTermset.Term)

   2: {

   3:     if ($TermSet.Terms[$XMLTerm.GUID] -eq $null)

   4:     #Test to see if the term exists    

   5:     {

   6:         $TermGUID = [guid]$XMLTerm.GUID

   7:         $Term = $TermSet.CreateTerm($XMLTerm.Name, 1033,$TermGUID)

   8:         #Create term, specifying the Locale (1033)

   9:         $Term.SetDescription($XMLTerm.Description, 1033)

  10:         #Set the terms description using the locale         


  12:         if($XMLTerm.IsAvailableForTagging -eq "False")

  13:         {

  14:             $Term.IsAvailableForTagging = $false

  15:             #If the term is not available for tagging

  16:         }

  17:     else

  18:     {

  19:         $Term = $Termset.Terms[$XMLTerm.GUID]

  20:         #If the term exists set the variable term

  21:     }

Do the same for child terms whilst in the terms loop.

   1: foreach ($XMLChildTerm in $XMLTerm.ChildTerms.ChildTerm)

   2: {

   3:     if($XMLTern.ChildTerms -ne $null)

   4:     {

   5:         if ($Term.Terms[$XMLChildTerm.GUID] -eq $null)

   6:         #Test to see if the term exists    

   7:         {

   8:             $ChildTermGUID = [guid]$XMLChildTerm.GUID

   9:             $ChildTerm = $Term.CreateTerm($XMLChildTerm.Name, 1033,$ChildTermGUID)

  10:             #Create term, specifying the Locale (1033)

  11:             $ChildTerm.SetDescription($XMLChildTerm.Description, 1033)

  12:             #Set the terms description using the locale         


  14:             if($XMLChildTerm.IsAvailableForTagging -eq "False")

  15:             {

  16:                 $ChildTerm.IsAvailableForTagging = $false

  17:                 #If the term is not available for tagging

  18:             }

  19:         else

  20:         {

  21:             $ChildTerm = $Termset.Terms[$XMLTerm.GUID]

  22:             #If the term exists set the variable term

  23:         }

  24:     }

Next close all the loops and commit the changes to the termstore (if you do not do this no changes will be made).

   1: $TermStore.CommitAll();

Finally SharePoint stores a copy of the termstore in a hidden list called “TaxonomyHiddenList” in the site. In order to update the termstore you need to tell SharePoint to resync the hidden list:

   1: $session.DefaultKeywordsTermStore.ReSyncHiddenList();

However this may not always work, if you are restore a content database onto a site then the hidden list also comes across (as you would aspect). Take a look at the hidden list’s field (if needs be look in SharePoint designer):

  • Title

  • IdForTermStore

  • IdForTerm

  • IdForTermSet

  • Term

  • Path

  • CatchAllData

  • CatchAllDataLabel

  • Term1033 (or Locale)

  • Path1033

The offending item here is the IdForTermStore, this stores the Term Store ID which is unique to the farm and cannot be set through Powershell nor can it be omitted in the Visual Studio field definition. So what you need to do is write a piece of script to get all the items in this hidden list and update the IdForTermStore with the ID of the TermStore.Id, after resync the list and all items which use the term store should now have valid up to date content.

No comments:

Post a Comment