Sue Hernandez's SharePoint Blog

SharePoint and Related Stuff

SharePoint 2010 Remote Metadata – Client Object Model and WS

Today I was playing with an Add-In to have an attachment go from Microsoft Outlook straight to SharePoint into a Document Library, but they have to specify the Metadata, and one of the metadata fields is of type “Managed Metadata”.  So I had to figure out how to remotely get the choices for the metadata fields – i.e. the terms in the term set that apply only to that column.

So it turns out that you can get the SchemaXml property off of a field in a List and that will return a lot of information to you, like what some of the key ID’s are.  Below is an example where I’m getting key information to be able to dynamically display a control for the metadata field in question:

ClientContext spCtx = new ClientContext(webURL);
Web remoteWeb = spCtx.Web;

List docLib = remoteWeb.Lists.GetByTitle(listName);
spCtx.Load(docLib);
spCtx.Load(docLib.Fields);
spCtx.ExecuteQuery();
Control lastControl = null;
foreach (Field field in docLib.Fields)
{
 int top = 5;
 if (lastControl != null)
 {
  top = lastControl.Top + lastControl.Height + 5;
 }
 if (!field.Hidden && !field.ReadOnlyField)
 {
  if (field.InternalName != "FileLeafRef" &&
   field.InternalName != "ContentType" &&
   field.Title != "Title")
  {
   // Title will be same as File Leaf Ref (Name)
   Label newLbl = new Label();
   newLbl.AutoSize = false;
   newLbl.Width = 200;
   newLbl.Top = top;
   newLbl.Left = 5;
   newLbl.Text = field.Title;
   pnlControls.Controls.Add(newLbl);
   switch (field.TypeDisplayName)
   {
    case "Managed Metadata":
     string xml = field.SchemaXml;
     XmlDocument xDoc = new XmlDocument();
     xDoc.LoadXml(xml);
     string termSetID = string.Empty;
     string anchorID = string.Empty;
     string sspID = string.Empty;
     XmlNode termSetIDNode = xDoc.SelectSingleNode("//Property/Value[../Name = 'TermSetId']");
     if (termSetIDNode != null)
     {
      termSetID = termSetIDNode.InnerText;
     }
     XmlNode anchorIDNode = xDoc.SelectSingleNode("//Property/Value[../Name = 'AnchorId']");
     if (anchorIDNode != null)
     {
      anchorID = anchorIDNode.InnerText;
     }
     XmlNode sspIDNode = xDoc.SelectSingleNode("//Property/Value[../Name = 'SspId']");
     if (sspIDNode != null)
     {
      sspID = sspIDNode.InnerText;
     }
     ManagedMetadataField mmdFld = new ManagedMetadataField(webURL, sspID, termSetID, anchorID);
     mmdFld.Left = 210;
     mmdFld.Top = top;
     mmdFld.Tag = field.InternalName;
     lastControl = mmdFld;
     pnlControls.Controls.Add(mmdFld);
     break;
    default:
     TextBox slineOText = new TextBox();
     slineOText.Width = 264;
     slineOText.Tag = field.InternalName;
     slineOText.Left = 210;
     slineOText.Top = top;
     lastControl = slineOText;
     pnlControls.Controls.Add(slineOText);
     break;
   }
  }
 }
}

Then, in the user control for the Managed Metadata Field type, I call on the TaxonomyClientService web service to get the terms in the term set.  The code is below, however I don’t know what the significance of the <timestamp> piece is yet.  Also, I don’t know if those @a9’s and @a45’s and such actually change names depending on ??

List<Term> relevantTerms = new List<Term>();
if (!string.IsNullOrEmpty(termSetID))
{
 taxonomyWS.Taxonomywebservice ws = new taxonomyWS.Taxonomywebservice();
 ws.Url = webURL + "/_vti_bin/TaxonomyClientService.asmx";
 // TODO
 ws.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
 string oldtimestamp = "<timeStamp>633992461437070000</timeStamp>";
 string clientVersion = "<version>3</version>";
 string termStoreIds = "<termStoreId>" + sspID + "</termStoreId>";
 string termSetIds = "<termSetId>" + termSetID + "</termSetId>";
 int lcidEnglish = System.Globalization.CultureInfo.GetCultureInfo("en-US").LCID;
 string timeStamp = string.Empty;
 string termSet = ws.GetTermSets(termStoreIds, termSetIds, lcidEnglish, oldtimestamp, clientVersion, out timeStamp);
 XmlDocument xDoc = new XmlDocument();
 xDoc.LoadXml(termSet);
 XmlNodeList relevantTermNodes = null;
 if (!string.IsNullOrEmpty(anchorID) && anchorID != "00000000-0000-0000-0000-000000000000")
 {
  XmlNode anchorNode = xDoc.SelectSingleNode("//T[@a9 = '" + anchorID + "']");
  if (anchorNode != null)
  {
   relevantTermNodes = xDoc.SelectNodes("//T[contains(TMS/TM/@a45,'" + anchorID + "')]");
  }
 }
 
 if(relevantTermNodes == null)
 {
  relevantTermNodes = xDoc.SelectNodes("//T");
 }
 foreach (XmlNode node in relevantTermNodes)
 {
  string id = string.Empty;
  string name = string.Empty;
  string path = string.Empty;
  XmlNode idNode = node.SelectSingleNode("@a9");
  if (idNode != null)
  {
   id = idNode.InnerText;
  }
  XmlNode pathNode = node.SelectSingleNode("TMS/TM/@a40");
  if (pathNode != null)
  {
   path = pathNode.InnerText;
  }
  XmlNodeList nameNodes = node.SelectNodes("LS/TL/@a32");
  bool first = true;
  List<string> extraNames = new List<string>();
  foreach (XmlNode nameNode in nameNodes)
  {
   if (nameNode != null)
   {
    if (first)
    {
     name = nameNode.InnerText;
    }
    else
    {
     extraNames.Add(nameNode.InnerText);
    }
   }
   first = false;
  }
  if (!string.IsNullOrEmpty(id) &&
   !string.IsNullOrEmpty(name))
  {
   Term newTerm = new Term(id, name, path);
   if (extraNames.Count > 0)
   {
    newTerm.AlternateTerms = extraNames;
   }
   relevantTerms.Add(newTerm);
  }
 }
}
relevantTerms.Sort();
return relevantTerms;

Note the use here of a custom class called Term and the fact that you can have Alternate Terms.

Finally, when I go to return the final selected value, you have to get the WssID from the hidden list called TaxonomyHiddenList.

public string GetSelectedValue()
{
 if(!string.IsNullOrEmpty(txtTermValue.Text))
 {
  foreach (Term term in terms)
  {
   if (term.TermName.ToLower() == txtTermValue.Text.ToLower())
   {
    // Found the right term
    return GetWssIDByTermID(term.TermID) + ";#" + term.TermName + "|" + term.TermID;
   }
  }
 }
 return null;
}
private string GetWssIDByTermID(string termID)
{
 int result = -1;
 try
 {
  ClientContext spCtx = new ClientContext(webURL);
  Web web = spCtx.Web;
  List hiddenList = web.Lists.GetByTitle("TaxonomyHiddenList");
  CamlQuery query = new CamlQuery();
  query.ViewXml = "<View><Query><Where><Eq><FieldRef Name='IdForTerm' /><Value Type='Text'>" + termID + "</Value></Eq></Where></Query></View>";
  ListItemCollection items = hiddenList.GetItems(query);
  spCtx.ExecuteQuery();
  if (items != null)
  {
   spCtx.Load(items);
   spCtx.ExecuteQuery();
   if (items.Count > 0)
   {
    result = Convert.ToInt32(items[0]["ID"]);
   }
  }
 }
 catch
 {
  result = -1;
 }
 return result.ToString();
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: