[UPDATE: At some point Microsoft has changed the CreateSiteCollectionGroup method of the TermStore object so that it is now public and you can use that directly in your code rather than follow the method that this article suggests. For additional information see http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.taxonomy.termstore.getsitecollectiongroup.aspx]
I’ve been playing around lately with term stores, both the central global variety as well as the private (aka “local”) site collection type. This secondary type is very useful if you want to limit access to the term sets or allow the term set’s contents to diverge from site collection to site collection. Why would you want to do this? Well, of course the primary power of a term store is to be able to globally manage a set of metadata for your organization, but these more localized term store’s give you more control over the use and management of the term sets, but still let you use all the cool new things SharePoint 2010 has to offer such as metadata navigation, faceted-search, etc…
So knowing all this, you can imagine my surprise when I learned that site collection "term stores" are actually just a special group in an MMS (Managed Metadata Service) term store. You are probably already familiar with Term Store Groups as they for the top level in the hierarchy within in a term store. What you may not know is that there are two types of these groups, "regular" and "site collection" (well there's also a third type, "system"). The site collection groups have a property that defines which site collections have access to the group (it’s an ACL that just stores a collection of Guids that are the SPSite.ID’s). The easiest way to create one of these is to create a new site column within your site, choose the field type of "managed metadata", then change the term set selection to "customize this term set". This action will both create the special term group in your MMS term store as well as your new term set.
Super, that’s what I want, so let’s package this up as a feature so that I can staple it to a site definition. Wait a minute! Not so fast. You can’t create site collection term store groups in code. Seriously? Yes, after checking this out with multiple sources as well checking things out for myself with reflector I found that the only public method on the TermStore object is CreateGroup(string groupName) which only creates a normal group (note: there is another private overloaded method of CreateGroup that takes a GroupType as a parameter… and there are other internal methods that call this, but there are no public paths to any of these). Also, once a group is created you cannot change its type.
Well, that’s a big bummer, but not to be deterred I have come up with a workaround. It’s not the perfect solution to this problem, but it does appear to get the job done. Also, if you were going to have publishing enabled on the site, then it’s really no extra work. The biggest downside to this is that I believe you will need to have the full version of Office Server (not just foundation) to pull off this work around.
So it goes like this- in code (feature receiver, powershell script, or whatever…):
- Activate FeatureID: f6924d36-2fa8-4f0b-b16d-06b7250180fa. This is the Publishing Infrastructure feature. One of the side effects of this feature is that it creates a site collection term group in the DefaultSiteCollectionTermStore and adds a term set called “Wiki Categories”.
- Look up the created term group by name (you'll just have to iterate over the collection as there's no method for doing this -- look for one with the name equal to the defaultName below:
string defaultName = "Site Collection - " + (site.Url.Replace("http://", "")).Replace("/", "-");
- Doublecheck that the current site’s ID is in the group's SiteCollectionAccessIds property. If not, it’s ok, you can hi-jack it using the .AddSiteCollectionAccess(Guid ID) and .RemoveSiteCollectionAccess(Guid ID) methods. I found that the publishing infrastructure feature only creates the new term set if one doesn’t already exist with that default name. Since it doesn’t clean itself up when you delete the termset you can sometimes find yourself in this state. This does open up another way to “exploit” this workaround though, because you can actually create a dummy site somewhere else and hi-jack it’s site collection term group (you can always rename the term group to whatever you want later… they key is now you have a group that is the correct type!)
- Rename the group if desired. Also grab the term group’s ID and stash it in your site’s property bag so that later you can fetch it directly using TermStore.GetGroup(Guid id) rather than having to iterate over the groups collection.
- Create all your term sets and populate them with default terms if desired.
- Optionally you can now deactivate the publishing infrastructure feature if you don’t need it for the site. You will need to clean up a few things if you want to stay neat including: items added to Style Library, some lists (Content and Structure Reports, Reusable Content, and Workflow Tasks), and the Wiki Categories term set from the site collection term group.
Ideally, Microsoft will make a fix in the future that allows us to create a site collection term store group without resorting to all this, but until then, we can exploit the above trick. I will actually post some code in the next few weeks that essentially does all of the above by creating a “shadow site collection”, hi-jacking it’s site collection term group, and deleting the shadow site collection all encapsulated in a single CreateSiteCollectionTermGroup method call.