Pages

Tuesday, August 6, 2013

Change MS Office file properties Programmatically (Colligo issues with iPad – SharePoint)

“Colligo briefcase” is a product used for synching/accessing SharePoint documents using iPad. All works well, as long as we use Out-of-the-box libraries. I recently came across a portal which showed a completely different display in iPad. The document Name looks completely different from its web view. After some research we realized that the problem occurs whenever there is a custom implementation (with custom I mean it has Custom List definition, which was used to create a Library instance). But how does the document name change? Or from where is this name picked from?

The answer for this question is: it picks up the document’s “Title” property to display the name in iPad. I am not sure, why Colligo shows it differently for different definition of libraries. I have no idea what makes Colligo decide to take the title property for the display.

Document’s Title property can be viewed by clicking on File Tab (or right click on the document and click on properties , visit Details tab to view the properties):

How do we solve this problem. Below are 2 options which maybe used until Colligo releases an upgrade version with resolution to this problem.
  1. Do not create List definition if we do not really need it. Instead create library instances from the default Library template (we can do this programmatically as well).
  2. Create an event handler to update the “Title” property of the document after upload. We can have this event handler associated with content type so that we need not add it to every single library. Associating and dissociating of the event handler can be done on feature activation and deactivation respectively. This would enable us to remove the event handler without having to deploy the code if Colligo comes up with a solution for this problem.
Below is the code block (console application) to be used to update the “Title” and “Subject”

static void Main(string[] args)
        {
             SPSite site = new SPSite(http://YourSiteURL;);
            SPWeb web = site.OpenWeb();
            string filePath = “path of the file to be updated”;
            SPFile file = web.GetFile(filePath);
            string itemTitle = file.Title;
            Stream streamItemDoc = file.OpenBinaryStream();
            Package packageDoc = Package.Open(streamItemDoc, FileMode.Open, FileAccess.ReadWrite);
            packageDoc.PackageProperties.Title = file.Name.Substring(0, file.Name.LastIndexOf(‘.’));
            packageDoc.PackageProperties.Subject = “Sample Subject”;
             PackagePartCollection packageCollection = packageDoc.GetParts();
            PackagePart part = packageCollection.FirstOrDefault();
            Uri uriProps = part.Uri;
             XmlDocument xmlDocPP = null;
            PackagePart curPP = packageDoc.GetPart(uriProps);
            using (Stream streamProps = curPP.GetStream())
            {
                xmlDocPP = new XmlDocument();
                xmlDocPP.Load(streamProps);
            }
             xmlDocPP.Save(packageDoc.GetPart(uriProps).GetStream(FileMode.Create, FileAccess.Write));
            packageDoc.Flush();
            file.SaveBinary(streamItemDoc);
            file.Properties["vti_title"] = itemTitle;
            file.Update();
         }

Points to remember:
  1. Whenever, we update the package Title property it updates the List Item Title as well and therefore in the penultimate line, the title is updated back to what it was originally.
  2. Windows.Base is the reference required to be added (maybe required only for client projects such as Console Application, Windows Application)

Wednesday, July 17, 2013

Programmatically assinging nested Terms to “Taxonomy” column – Managed Metadata

According to me, Managed Metadata is one of the greatest additions to SharePoint. There are many advantages of using Managed Metadata Service and needless to mention the flexibility and the ease of maintenance it provides to maintain enterprise data. However, these details are out of scope as far as this blog is concerned.

Creating Taxonomy columns which uses Managed Metadata Service to bind the data through UI is straight forward. However, to do it programmatically would require considering few factors. There are many blogs which speak about this, but the one which stands out highlighting the details (as far as I am considered) is here:
http://www.sharepointconfig.com/2011/03/the-complete-guide-to-provisioning-sharepoint-2010-managed-metadata-fields/

This article speaks about creating a taxonomy column and binding the TermSet for that created column.

So far so good, but now I want to bind the taxonomy columns to terms or nested terms and not the termset. This is something I could not find over web and therefore this blog. Consider the example where:
  • “Group Name” is “Vehicles”;
    • “TermSet” are “Cars”, “Trucks” etc
      • “Terms” are “Honda”, “Toyota” etc
        • “Terms inside Honda” are “Civic”, “Accord”, “CRV” etc
        • “Terms inside Toyota” are “Camry”, “Corolla”, “Prius” etc
Now I want to create a taxonomy column which would allow me to select only “Honda” make “Cars”. Here I have nested terms and I cannot bind the column to termset “Cars” or “Trucks” as I want users to select only “Honda” vehicles for one list and another list which would have only “Toyota” vehicles selectable for a different Taxonomy field.

Solution: Below is the code block which would do the expected (observe code assignment for “AnchorId” which was assigned to Guid.Empty for TermSet in the link shared earlier)

public static void AssignTermToTaxonomyField(SPSite site, Guid fieldId, string termGroup, string termSetName, string termName)
{
if (site == null)
{
throw new ArgumentNullException(“site”);
}
if (site.RootWeb.Fields.Contains(fieldId))
{
TaxonomySession session = new TaxonomySession(site);
if (session.DefaultKeywordsTermStore != null)
{
// get the default metadata service application   
TermStore termStore = session.DefaultKeywordsTermStore;
Group group = (from g in termStore.Groups where g.Name.ToLower() == termGroup.ToLower() select g).FirstOrDefault();
if (group != null && !(string.IsNullOrEmpty(termName)))
{
TermSet termSet = group.TermSets[termSetName];
TaxonomyField field = site.RootWeb.Fields[fieldId] as TaxonomyField;
foreach (Term term in termSet.Terms)
{
if (term.Name.ToLower() == termName.ToLower())
{
  // connect the field to the specified term  
field.SspId = term.TermStore.Id;
field.TermSetId = termSet.Id;
field.TargetTemplate = string.Empty;
field.AnchorId = term.Id;
field.Update();
break;
}
}
}
}
else
{
throw new ArgumentException(string.Format(“DefaultKeywordsTermStore not found in site {0}”, site.Url));
}
}
else
{
throw new ArgumentException(string.Format(“Field {0} not found in site {1}”, fieldId, site.Url), “fieldId”);
}
}

So the points to remember are:
  • Assign Site Column’s “SspId” to Term’s TermStoreId (Ex: Honda or Toyota)
  • Assign Site Column’s “TermSetId” to Term’s TermSetId (nested terms would also be part of the same termsets only)  (Ex: Cars)
  • **Assign Site Column’s “AnchorId” to the actual nested term’s Id which we want to map (Ex: Honda or Toyota)
Hope this helps and Thank you for visiting my blog!