Wednesday, 23 September 2015

Export a term set to 'Importable' CSV using JavaScript in SharePoint online

It is a well known fact that we can import term set in SharePoint using a CSV file. This is particularly helpful when you are working in multiple on-premise environments/multi-tenant environment and you want to replicate a deep and complex term hierarchy everywhere. This can be very tedious if done manually. Also, you may not always have the liberty to use server side code with SharePoint online to export the term set. Here, we'll explore a way to export a term set in CSV file using JavaScript only.

Note, the implementation does not work in IE (I have tested this in IE 9) because of the way we export data to CSV from client-side. But it works very well in Chrome. I did not have much time to explore the reason for this but I plan to do so in the near future :)

The format of CSV which is acceptable during import is provided to us as a sample file in SharePoint. To see the format, navigate to Term store management and click on the Taxonomy node.

The columns in the CSV are in the following order

"Term Set Name", "Term Set Description", "LCID", "Available for Tagging", "Term Description", "Level 1 Term", "Level 2 Term"......"Level 'n'  Term".

You can see from the test file to write each term, a proper hierarchy has to be maintained.

For testing purpose, I have created two term groups, one to Export a term set and one to import from CSV. The term set contains 'Regions'

get_pathOfTerm() : 

Before going into the code, I want to mention this method which has made writing the hierarchy of term set to CSV so easy !! When we use the "getAllTerms()" method on a term set, the terms are returned without its hierarchy details. So if we have to write the terms according to the hierarchy, we might have to take the longer route and iterate through each term and its children. This is where "get_pathOfTerm()" comes to our rescue. This method returns the hierarchy of a particular term, with each level separated by a semi-colon ';'. What this means is, referring to the above screenshot (term set hierarchy) if I get the path of the term "New York", the value returned is

North America;USA;New York

Now, we anyway need the values as comma separated since we are writing it to a CSV. Just replace the semi-colon with comma and you have the string for the entire hierarchy of a term you want to write.

I have developed a simple SharePoint hosted app to demo this. We will provide the GUID of a term set to our app which then exports that particular term set. To get GUID of a term set, click on the term set that you want to export in Term Management and in the right pane you'll find the unique identifier at the bottom.

Import this into a term set.

Here is the full code of the functionality

I found the method to export data to CSV using JavaScript on Raymond Camden's blog.

Hope this helps !

Tuesday, 1 September 2015

Index web property bag using JavaScript object model-AngularJS in SharePoint online

SharePoint 2013 added a new capability to index the property bags. Indexing property bag through the server side object model code is pretty easy. The WEB object has a property called "IndexedPropertyKey" which is a collection of all the web properties that should be indexed. Here is a reference article . However, doing this through client object model is tricky because we do not have access to IndexedPropertyKey through CSOM. Vesa Juvonen has made our life easy by documenting a work-around to do this through CSOM.

Basically the way this works is - all the properties that need to be indexed are encoded and stored in a "vti_indexedpropertykeys" property. In a scenario where multiple properties need to be indexed, the encoded values are separated by a PIPE "|" character. In this post we are going to index the web properties using JavaScript object model on a SharePoint online site. We will develop a SharePoint hosted app to add and index a property. Here is how our application will look like.

Lets go ahead and add a couple of new properties to be indexed.

The checkbox facilitates the user to choose whether the new property should be indexed or not. As we have chosen to index the properties , the vti_indexedpropertykeys is created in the web properties with the encoded values of property keys separated by PIPE character

Once the incremental crawl completes, the web properties that we just added are indexed.

Lets go through the code

The AngularJS code has a controller "SPWebPropertiesController" and an AngularJS service "WebPropertiesService" which contains function to fetch and add new web properties. The function $scope.GetWebProperties uses AngularJS defer and on success, it populates the keys and values of the properties. It also checks whether the key "vti_indexedpropertykeys" exists and sets the flag $scope.vtiIndexedPropertyKeysExists accordingly.

All the AngularJS and Javascript code below is a part of single JavaScript file. It is separated here in this post and GIT for the ease of understanding.


FetchWebProperties function:

This function gets all the properties from the property bag and returns it to the controller where the key and values are populated in $scope.WebProperties=[];

AddNewProperty function: 

- This function adds a new property to the property bag using the set_item(Property_Key, Property_Value) function. If you want to modify the value of a property, use "webProps.set_item(Existing_Property_Key, New_Value)".
- If we have chosen to index the property using the checkbox, we must get the encoded value of the property key. This is done using the EncodePropertyKey function.
- Checks whether the key "vti_indexedpropertykeys" exists, using the flag that we have set in GetWebProperties function of the controller. If it exists, it adds the encoded property key.
- If the "vti_indexedpropertykeys" is not present, it creates this new key and then adds the encoded property key that has to be indexed.

EncodePropertyKey function : 

This function converts the property key to an encoded string. Initially I observed the bytes array in C# console application and Javascript conversion and noticed that the '0's from JS conversion were missing. So I added those (line 5 in the below function)

WebPropertiesService code :