Tuesday, January 7, 2014

Programmatically provisioning Term Sets, Terms, and Managed Metadata columns in SharePoint 2013

In this post I explain how to provision Term Sets, Terms, and Managed Metadata columns in SharePoint 2013 using C#.

Caveats

  • This is for SharePoint 2013 full trust solutions where the Managed Metadata Service is provisioned
  • The Terms and Term Sets  are provisioned in the default site collection term store, but they could just as easily be provisioned in another term store with the appropriate assignment of privileges.
  • For the purposes of this post, I created a site scoped feature and provision all of the terms in a feature receiver. This is not meant to be an explanation of feature receivers so I don't cover a lot of the details of how to setup a feature receiver.


Provisioning SharePoint 2013 Term Sets in Code


In order to provision term sets in code, you first need to get a reference to the term store. In the example below, I use a feature receiver to provision the term set and associated terms. The feature is a site scoped feature and I use the default site collection term store. When activated on a site collection, it will run the code below to provision the terms.

public class MyFeatureReceiver : SPFeatureReceiver
{
    public override void FeatureActivated(SPFeatureReceiverProperties properties)
    {  
        //Get a reference to the current site
        var site = properties.Feature.Parent as SPSite;

        //Get the Default Site Collection Term Store
        var taxonomySession = new TaxonomySession(site);
        var termStore = taxonomySession.DefaultSiteCollectionTermStore;
        var termStoreGroup = termStore.GetSiteCollectionGroup(site);

        //Create Department Term Set with associate Terms
        var termSet = termStoreGroup.CreateTermSet("Department");
        termSet.CreateTerm("Finance", CultureInfo.CurrentCulture.LCID);
        termSet.CreateTerm("Accounting", CultureInfo.CurrentCulture.LCID);
        var humanResources = termSet.CreateTerm("Human Resources", CultureInfo.CurrentCulture.LCID);
        humanResources.CreateTerm("Recruiting", CultureInfo.CurrentCulture.LCID);
        humanResources.CreateTerm("Benefits", CultureInfo.CurrentCulture.LCID);
        humanResources.CreateTerm("Compensation", CultureInfo.CurrentCulture.LCID);
        humanResources.CreateTerm("Payroll", CultureInfo.CurrentCulture.LCID);
        termSet.CreateTerm("Operations", CultureInfo.CurrentCulture.LCID);

        termStore.CommitAll();
    }
}

Note that I assign the Human Resources term to a variable ("humanResources") because I need to create child terms. Once I create "humanResources", I can create child terms by using the CreateTerm method on the newly created "humanResources" variable.

This is what the site collection term store looks like once the solution has been deployed and the feature has been activated:


 

Provisioning a Managed Metadata Site Column

In order to provision a managed metadata site column that uses the Human Resources term as an anchor, I just add a little code below "termStore.CommitAll();". Here is the full feature receiver code with the added managed metadata site column linked to the new Human Resources term.

public class MyFeatureReceiver : SPFeatureReceiver
{
    public override void FeatureActivated(SPFeatureReceiverProperties properties)
    {  
        //Get a reference to the current site
        var site = properties.Feature.Parent as SPSite;

        //Get the Default Site Collection Term Store
        var taxonomySession = new TaxonomySession(site);
        var termStore = taxonomySession.DefaultSiteCollectionTermStore;
        var termStoreGroup = termStore.GetSiteCollectionGroup(site);

        //Create Department Term Set with associate Terms
        var termSet = termStoreGroup.CreateTermSet("Department");
        termSet.CreateTerm("Finance", CultureInfo.CurrentCulture.LCID);
        termSet.CreateTerm("Accounting", CultureInfo.CurrentCulture.LCID);
        var humanResources = termSet.CreateTerm("Human Resources", CultureInfo.CurrentCulture.LCID);
        humanResources.CreateTerm("Recruiting", CultureInfo.CurrentCulture.LCID);
        humanResources.CreateTerm("Benefits", CultureInfo.CurrentCulture.LCID);
        humanResources.CreateTerm("Compensation", CultureInfo.CurrentCulture.LCID);
        humanResources.CreateTerm("Payroll", CultureInfo.CurrentCulture.LCID);
        termSet.CreateTerm("Operations", CultureInfo.CurrentCulture.LCID);

        termStore.CommitAll();

        var web = site.RootWeb;
        if (!web.Fields.ContainsFieldWithStaticName("HumanResourcesDepartment"))
        {
             var humanResourcesDepartment = web.Fields.CreateNewField("TaxonomyFieldType", "HumanResourcesDepartment") as TaxonomyField;
             if (humanResourcesDepartment != null)
             {
                 humanResourcesDepartment.StaticName = "HumanResourcesDepartment";
                 humanResourcesDepartment.Title = "Human Resources Department";
                 humanResourcesDepartment.SspId = termStore.Id;
                 humanResourcesDepartment.TermSetId = termSet.Id
                 humanResourcesDepartment.TargetTemplate = string.Empty;
                 humanResourcesDepartment.AnchorId = humanResources.Id;
                 humanResourcesDepartment.Group = "Custom Columns";
                 humanResourcesDepartment.AllowMultipleValues = true;
                 humanResourcesDepartment.Open = false;
             }
             web.Fields.Add(humanResourcesDepartment);
             web.Update();
        }
    }
}

Note that since I have the Human Resources term saved in the humanResources variable, I am able to grab it's id to set the anchor point for my managed metadata column.

Here's what the site column looks like after the feature has been deployed and activated:





Notice how the Term Set Settings are anchored to the Human Resources term in the Department Term Set. Here's how the properties on the new managed metadata field should be set:
  • The SspId should be set to the id of the Term Store to use. In this case I happen to have only one Managed Metadata Service and one Term Store to use. In the code above, I just grab the id from my termStore variable.
  • The TermSetId should be set to the id of the Term Set to use. In this case, my Term Set is "Department". In the code above, I just grab the id from my termSet vaiable..
  • The AnchorId should be set to the id of the term that you would like to anchor on. In a lot of cases, this may be set to null if you want to anchor on the Term Set itself. However, in this case, I want to create a column that allows me to select a department within Human Resources. So, I am going to anchor on the "Human Resources" Term. In the code above, I just grab the id from my humanResources variable.

Enjoy!

Sunday, September 29, 2013

Simple jQuery Content Filter for Office 365 Public Website

I had the need recently to quickly create a content filter for the SPS Chicago session page. The page lists all the upcoming sessions in a paragraph format, something like this:

Title 1
Speaker 1
Session Abstract 1

Title 2
Speaker 2
Session Abstract 2

etc...

I wanted something simple at the top of the page that would allow users to filter the sessions based on some kind of key word.

So, I wrapped each of the sessions in a div with a class name of a descriptive term, something like this:

<div class="DemoDeveloper">
Title 1
Speaker 1
Session Abstract 1
</div>

Then I added check boxes to the top of page for each descriptive term, like this:


I wanted it so that when you checked a box, the applicable sessions were displayed. When the check box was unchecked, those sessions were hidden. So, I added the following jQuery to the page (for brevity I only show the developer check box functionality below):

        $(document).ready(function () {
            function DemoToggleView() {
                if (!$("#DemoDeveloperCheckbox").prop("checked")) {
                    $(".DemoDeveloper").hide();
                } else {
                    $(".DemoDeveloper").show();
                }
            };
            $("#DemoDeveloperCheckbox").click(DemoToggleView);
        });

The "DemoToggleView" is just a function that either hides or shows divs with a class of "DemoDeveloper" depending on whether the check box is checked or unchecked. The second part of the code attaches the function "DemoToggleView" to the click event of the checkbox.

Here's what it looks like in action. Click the checkbox to toggle the developer sessions. Note that "Title 1" is wrapped in a div class of "DemoDeveloper".

Developer    

Title 1
Speaker 1
Session Abstract 1


Title 2
Speaker 2
Session Abstract 2


As always, let me know what Think

[Edit] Matt Bramer refactored this into a much cleaner solution. Check it out here: 
http://mattbramer.blogspot.com/2013/10/simpler-jquery-content-filter.html?m=1 .

Volume Shadow Copy Service Error while Installing Search Service Application on SharePoint 2013

If you get the following error (see below) while installing the Search Service Application on SharePoint 2013, make sure that the "SharePoint Server Search 15" (OSearch15.exe) has full control privileges to the following registry key: HKLM\System\CurrentControlSet\Services\VSS\Diag

Error
Volume Shadow Copy Service error: Unexpected error calling routine RegOpenKeyExW(-2147483646,SYSTEM\CurrentControlSet\Services\VSS\Diag,...).  hr = 0x80070005, Access is denied.

Operation:
   Initializing Writer

Context:
   Writer Class Id: {0ff1ce15-0201-0000-0000-000000000000}
   Writer Name: OSearch15 VSS Writer
   Writer Instance Name: OSearch15 Replication Service
   Writer Instance ID: {51cfa6a9-0ee0-4650-928a-c52e42b8df3b}