Just when I was getting the hang of building better and better meganav controls from scratch on all my SharePoint 2010 projects, 2013 came out and brought me to the light with term-driven pages and managed navigation. This is a great feature, and a perfect example of the graceful product evolution I've been noticing in SharePoint over the years:
In version n, "it," whatever "it" is, doesn't exist; we build it from scratch and blog about it.
In version (n+1) the tools are there; we still need a lot of code or XML to get it to work.
In version while (m >= (n+2)) it's built in; we only have to tweak, implement, or configure it a bit to fit our needs.
A lot of SharePoint components (with heavy integration with the corresponding Visual Studio release) have followed this path on their journey toward out-of-the-box-dom:
- 2003: Forget about it
- 2007: DDF files and WSP Builder
- 2010: Integrated Visual Studio deployments and feature/solution upgrades
Visual Web Parts
- 2007: SmartPart (host a user control in a web part)
- 2010: Visual web part (with designer code that welds a user control to the web part with code behind for each)
- 2013: Just a user control (finally)
- 2007: This is funny: SharePoint 2007 mobile browsers and devices; at least now we have master pages...
- 2010: Big updates for cross-browser support and not-horrible SmartPhone experiences, which opened the floodgates for a deluge of CSS files bloated with many thousands of lines of styles.
- 2013: Device Channels
- 2007: AJAX
- 2010: JSOM
- 2013: REST
"Beyond AD" Authentication Paradigms
- 2007: Custom authentication providers and extended web applications
- 2010: Claims
- 2013: OAuth
As you can see, SharePoint has been steadily growing in two directions: up, alongside emerging development paradigms (and, to an extent, fads) and out, deepening from community contributions, feature requests, and bug fixes. And I think this is a beautiful thing; it keeps me reminded that Microsoft is in the trenches with us. As we keep pushing the limits of what SharePoint can do, they keep responding with new limits.
One area that really hasn't gotten a lot of love in SharePoint is navigation. In the 2003 days, we had the quick launch. 2007 brought us the publishing navigation screen and manageable breadcrumbs. But into the 2010 release, we were either building our aforementioned meganav controls, or simply branding the "global" and "left" navigation divs and calling it a day; not much had changed in the better part of a decade.
But with the 2013 release, we got a new toy to play with: managed navigation. Like the meganav example, this is something that I've built from scratch time and time again: using the naturally-hierarchical nature of terms to model my site's navigation menus. It's another great example of using taxonomy to abstract the physical layout of the site.
But now, it's all out of the box, which is something our customers will love. However, it goes way beyond simply rendering anchor tags based off a term set. With managed navigation, we can take normal publishing pages to the next level with friendly urls, navigational visibility, associated hover text and image for generated hyperlinks, and a dictionary of custom properties (which is great jumping off point from out of the box features into custom functionality).
Speaking of customizations, the CSOM API for term-driven pages is what I'd like to get into for this post. Microsoft.SharePoint.Client.Taxonomy is new in the 2013 release, and has given us our first programmatic taste of client side metadata...and it's delicious. In fact, compared to other SharePoint client APIs, this one closely mirrors it's server side counterpart, making it even easier to use.
However, that said, the implementation of term-driven pages, although fully supported in CSOM, is bit clunky. The classes in the client taxonomy namespace that I find myself using the most all have a navigational counterpart: for every Term, TermCollection, and TermSet there exists a NavigationTerm, NavigationTermCollection, and NavigationTermSet.
Now what's clunky is how you get at these classes. If you were to gander at your term sets via clicking on "Term store management" in your site collection settings, (navigation taxonomy only lives at the site collection level, not in any Managed Metadata service application in Central Administration) you'll notice that some appear grayed out, while others appear as you'd expect.
Digging deeper into one of the navigation term sets, you can see its terms appear to act as standard ones do; they simply has more tabs of metadata. In addition to the custom properties, we see SEO and other navigational options in these screens. The nav term set itself also has more going than its conventional counterpart, most notably configurations for term-driven pages.
What's interesting is, after looking at the CSOM taxonomy API, seeing how closely the navigation taxonomy implementation mirrors this same concept: start with a normal term or term set, and then add in more meta (meta-meta?) data. To demonstrate this, let's consider a usage of the static method that gives us a reference to a NavigationTermSet: NavigationTermSet.GetAsResolvedByWeb.
Code Listing 1
- public static NavigationTermSet GetNavigationTermSet(this ClientContext context, TermSet termSet)
- //get term set
- NavigationTermSet termset = NavigationTermSet.GetAsResolvedByWeb(
- return context.LoadNavigationTermSet(termset);
GetNavigationTermSet extends ClientContext, and basically "casts" a standard term set into its navigational form. What this implies is that you can use whatever method you'd like to acquire a TermSet, and then route it through this puppy (if it indeed is a nav term set behind the scenes) to get at all the extra metadata. In fact, the method still seems to work all the same even if the termSet parameter is blatantly not of the navigable persuasion.
This got me thinking. Is there really such a thing as a NavigationTerm or NavigationTermSet? If you can literally (and I never use literally figuratively) "resolve" a nav term set from a standard one, then perhaps they share more than a few common chromosomes? Although some quick F12-ing revealed that NavigationTermSet doesn't inherit from TermSet polymorphicly, I was still convinced that something was up.
It was actually a major flaw in some provisioning logic that put my mind at ease regarding this existential navigation crises. A botched deployment had accidentally provisioned standard term sets and terms, when they were supposed to be navigational. I was tasked with fixing this scenario. Still contemplating if a NavigationTermSet was the chicken or the egg with respect to a Termset, I took another spin through the API for a way to not only cast between the two (as in Code Listing #1) but actually potentially convert one to the other.
And that's where I found it: IsNavigationTermSet. This innocent little flag on NavigationTermSet is far more powerful than it would seem. From the description of this property on the page the above link references, this is how we can tell if the term set is used for navigation. To quote: Gets or sets a value that indicates whether the underlying term set is intended to be used for taxonomy navigation.
Intended to be used for taxonomy navigation, huh? That explains it. It's not a different object, just a different representation of a termset with enhanced usages. This is sort of how contravariance works in .NET: a NavigationTerm is simply a more specific expression of a Term. So maybe it's not that NavigationTermSets don't really exist; TermSets might be the actual apparitions...
...Okay, probably not. Notice in the links above that all I could find on the Navigation[Whatever] objects were cryptic TechNet spec docs, while the "standard" taxonomy classes have normal MSDN articles. To me, this is a pretty big indicator that our navigation types are the outliers. Combined that with the fact that they live in the MS.SP.Client.Publishing namespace and not MS.SP.Client.Taxonomy, I can further assume that their main use case is not metadata.
However it works, let me close with some code. The method below, ConvertTermSetToNavigation, does exactly that: it wraps a conventional term set (and all its child terms) in a navigational blanket. This is the logic I used to fix the aforementioned botched deployment; I don't see much use in other contexts. But what I find interesting is the interchangeability of the types, and how digging into the implementation of the API led me to a place of greater understanding of the underlying concepts needed to master client side taxonomy customization. Have fun!
Code Listing 2
- public static void ConvertTermSetToNavigation(this ClientContext context, TermSet termSet)
- //get nav term set
- NavigationTermSet navTermSet = context.GetNavigationTermSet(termSet);
- navTermSet.IsNavigationTermSet = true;