iPhone Detected, site running in minimal mode.
Home     Tags/Archives     Tweets     About Kevin

By request here is an image version of the mindmap that I used when presenting at last week's SPIN (SharePoint Users of Indiana​) meeting.  It had certainly been way too long since I've presented at my "home user group" (I think the last time was about 4 years ago! is that right?! wow).

This session was An Introduction to Electronic Content Management (yes the controversy between Electronic and Enterprise was built purposely into the title).  It is by far the least technical topic that I've presented on in quite a while, but as I've been doing quite a bit of work lately on ECM and Information Architecture Design the timing was great.  It will now go into my presentation rotation for potential future speaking gigs.

Anyway, if you click on the thumbnail below you'll get a large format picture version of the mindmap - sorry folks I don't really like powerpoint slide decks for these types of presentations.  What this means is that there is quite a bit of detail that I TALKED about during the presentation that isn't included here in the presentation artifact.  So if you didn't come to the meeting, make sure next time you do!  I supposed if enough people request it I might be persuaded to record a screencast.

Mindmap Thumbnail

Lastly, a special thank you goes out to Rob Bogue that provided some of the content for this presentation especially in the "History of Content Management" arm of the map.  Also, I don't remember if I mentioned it at the end of the meeting, but I highly value any feedback on my presentations from attendees.  If you have any you can either leave it below or my anonymous BetterMe inbox is always open at http://bit.ly/tokevin

[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 CreateGroup from Reflector(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…): 

  1. 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”.
  2. 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("/", "-");
  3. 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!)
  4. 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.
  5. Create all your term sets and populate them with default terms if desired.
  6. 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.​​​

I had a great time this weekend in Chicago at the SharePoint Saturday event held at McCormick Place.  It's always a blast to catch up with old friends and meet some new ones- and this being my first time presenting up in Chicago (other than some AIIM events) I did get to meet quite a few new folks.

The event organizers did an awesome job from the pre-communication leading up to the event (I think this was pulled together in just over a month), the speaker dinner, and at the event itself.  It was also a great new experience for me presenting in a "real" conference center / convention hall.

I had a great time in my session- the audience was great and it seemed pretty effortless.  Lots of good questions too.  So here are the resources as promised from the session on Social Computing with SharePoint 2010.  I've included the mindmap that was used during the presentation (in large pannable graphic format) as well as a slidedeck version, which may be easier for some to read-- just click on the appropriate thumbnail below.

 Mindmap Thumb

 SlideDeck Thumb

Also, since we didn't have detailed session evaluations, I'd love to hear any feedback from those that came!  Thanks Chicago-- I'll be back next week (next time for pleasure)!​


​I just spent a bunch of time working around a SharePoint bug related to manipulating the order that navigation items appear in a MOSS Publishing site on the TopNavigationBar.  Here I will spell out what you need to get to get various pieces of this to work- but first, let's define what we are trying to accomplish.

Say you have a site that has been created in some way.  It doesn't really matter if it was declaritively deployed from a Site Definition, restored from a backup, generated by a SharePoint Deployment Wizard CMP, or built up manually from code.  The thing is once it's done being created you need to modify from code various aspects of the top navigation bar including manually setting the order of the items.  Again, it doesn't matter where this code is running from, whether it be a PowerShell script, a command line application, or a suicide webpart placed on the site.  Ok, all that being said, *MY* specific requirements were to:

  • Change Top (global) Nav to Include Subwebs, Hide Pages, and support manual (non-automatic) ordering
  • Hide one of the subwebs
  • Reorder the other subwebs to a custom (ie. arbitrary) order
  • All changes must be done fully in code with no "manual human steps" required as it was part of an automated deployment script.
The good news is that to do the first two bullets, things pretty much work like they are supposed to, although finding exactly how to do some of that took a bit of work, so I'll reproduce them here.  Unfortunately for the third bullet there's a bug that gets in the way and so weird workarounds are required.

First of all, my code segments below will assume you have referenced Microsoft.SharePoint.dll and Microsoft.SharePoint.Publishing.dll and have the following usings at the top of your file:

   1:  using Microsoft.SharePoint;
   2:  using Microsoft.SharePoint.Publishing;
   3:  using Microsoft.SharePoint.Navigation;​
   4:  using System.Collections.Generic;

To set the different properties of the root web's navigation settings simply do the following:

   1:  using (var site = new SPSite(webUrl))
   2:  {
   3:      var rootPubweb = PublishingWeb.GetPublishingWeb(site.RootWeb);
   4:      rootPubweb.NavigationOrderingMethod = OrderingMethod.Manual;
   5:      rootPubweb.IncludeSubSitesInNavigation = true;
   6:      rootPubweb.IncludePagesInNavigation = false;
   7:      rootPubweb.Update();
   8:  }

Now, if you want to hide a particular subweb from the top navigation, you actually need to set the property of the subweb, not a property in the root web or it's navigation collections.  So, you would add the following (inside the SPSite using statement):

   1:  var subwebs = rootPubweb.GetPublishingWebs();
   2:  subwebs["subwebToHide"].IncludeInGlobalNavigation = false;
   3:  subwebs["subwebToHide"].Update();
   4:  rootPubweb.Update();

So far so good.  Now when it gets to actually specifying the order of the nodes, the documentation that you will find will direct you to get an SPNavigationNodeCollection from rootWebInstance.Navigation.TopNavigationBar.  If you iterate over the SPNavigationNode 's in this collection you can set their order by using the .MoveToFirst, .MoveToLast, and .Move methods.  Unfortunately on a newly build publishing site this collection will not contain any of your subwebs, only the top level pages (even though we specified to NOT include pages and to include subwebs).  In scouring the internet I found that others had indeed come across this bug as well.  What's odd is that if you use the SharePoint GUI and go to the root web's navigation settings (/_layouts/AreaNavigationSettings.aspx), select any subweb in the "Global Navigation" tree and click one of the Move buttons, then some magic code runs behind the scenes and the Navigation.TopNavigationBar collection will now be properly populated (and you won't need the following work around any more for that particular site).  However, as my requirement was to not require any manual steps I had to go on to develop the workaround below.

So I embarked on an effort to reproduce the "magic code" that initialized the navigation collection properly.  There was much trial and error involved while making incremental changes and then interogating object properties with PowerShell, etc...  In the end the successful strategy was to create the navigation nodes manually (in code).  The trickiest part though was getting them to be set as "internal" and not "external" links (this is a read-only property on each NavigationNode)-- it turned out the node's Url property had to be set just right for it to work.  Here is my workaround code:

   1:  using (var site = new SPSite(webUrl))
   2:  {
   3:      var rootPubweb = PublishingWeb.GetPublishingWeb(site.RootWeb);
   4:      var subwebs = rootPubweb.GetPublishingWebs();
   6:      //Build a temporary hash of the node titles (so we can use them instead of index numbers)
   7:      SPNavigationNodeCollection nodes = site.RootWeb.Navigation.TopNavigationBar;
   8:      Dictionary<string, SPNavigationNode> nodeHash = new Dictionary<string, SPNavigationNode>();
   9:      for (int x = 0; x < nodes.Count; x++)
  10:      {
  11:          nodeHash.Add(nodes[x].Title, nodes[x]);
  12:      }
  14:      //This block fixes the evil SharePoint defect ------------------------
  15:      foreach (PublishingWeb subweb in subwebs)
  16:      {
  17:          if (!nodeHash.ContainsKey(subweb.Title))
  18:          {
  19:              string relUrl = subweb.ParentPublishingWeb.Uri.MakeRelativeUri(subweb.Uri).ToString();
  20:              SPNavigationNode nn =
  21:                  Microsoft.SharePoint.Publishing.Navigation.SPNavigationSiteMapNode.CreateSPNavigationNode(
  22:                      subweb.Title, relUrl, NodeTypes.Area, site.RootWeb.Navigation.TopNavigationBar);
  23:              nn.Update();
  24:          }
  25:      }
  26:      site.RootWeb.Update();
  27:      //---------------------------------------------------------------------
  29:      //Rebuild the hash table since now we should have the correct nodes!
  30:      nodeHash = new Dictionary<string, SPNavigationNode>();
  31:      for (int x = 0; x < nodes.Count; x++)
  32:      {
  33:          nodeHash.Add(nodes[x].Title, nodes[x]);
  34:      }
  36:      string[] subwebTitlesInOrder = {"FirstNavItem", "SecondNavItem", "ThirdNavItem", "FourthNavItem"};
  38:      //Order the nodes in the proper order
  39:      SPNavigationNode currentNode;
  40:      SPNavigationNode previousNode=null;
  42:      for (int i = 0; i < subwebTitlesInOrder.Length; i++)
  43:      {
  44:          currentNode = nodeHash[subwebTitlesInOrder[i]];
  45:          if (i == 0)
  46:          {
  47:              currentNode.MoveToFirst(nodes);
  48:          }
  49:          else
  50:          {
  51:              currentNode.Move(nodes, previousNode);
  52:          }
  53:          previousNode = currentNode;
  54:      }
  55:      site.RootWeb.Update();
  56:  }
And that's it!

Wrapped up another DevLink today with my session this morning: Building a SharePoint 2010 Development Environment.  It was a full room and a great audience.  Thanks very much to all who made it in for the Saturday morning session (I know how rough that can be after Friday night shenanigans :)

Anyway, I've posted below a hires picture version of the mindmap used in the presentation as well as a PDF version of the basic installation walkthrough.  Below that is a list of all the places we talked about on the internet so that you have all the links in once place.  I'd love to hear any additional feedback from the folks that attended (since at least at the moment they appear to have misplaced the speaker evals from this morning-- I'm sure they will turn up though).

Click one of the two pictures below to download the presentation resources:



  << Older      [more posts]      Newer >>

RSS FeedBack to the HomepageMy Twitter Feed and More!Video Chat Now!


Hide Low Frequency Tags


Recent Posts