I was talking to a client who was having difficulty developing a navigation control using the methods described in most of the documentation out there, which explains creating a custom Site Map Provider which inherits from the PortalSiteMapProvider (PSMP)object. There was just no way they were going to get exactly what they were looking for in relation to what their high-priced 3rd-party designer delivered.
After taking some time to do some exploration, and being a big proponent of web server controls in the MOSS context, I knew this was the way they needed to go. I found a great post by Chris Richard on the MS ECM Team Blog that explained how the PortalSiteMapProvider deals with performance issues found often in costly navigation rendering routines. This post also explained how to use the PSMP object in custom code - something that caught my eye.
There are a few instances of the PSMP that are defined in the MOSS web.config that can be accessed through the PSM object:
PortalSiteMapProvider.CombinedNavSiteMapProvider: attached to the global or top nav menus
PortalSiteMapProvider.CurrentNavSiteMapProvider: attached to the current, or "Quick Launch" nav menu
PortalSiteMapProvider.CurrentNavSiteMapProviderNoEncode: attached to the breadcrumb nav
PortalSiteMapProvider.GlobalNavSiteMapProvider: Get the map for the global nav
PortalSiteMapProvider.WebSiteMapProvider: enumerates Web sites (SPWebs) in the site collection
Essentially, all of these object are set with the current SPContext, so they are already security-trimmed and ready to use as-is for the user currently browsing the site. The special one for me here is the CurrentNavSiteMapProviderNoEncode, which includes the pages, not just the webs. There is a limitation which Chris mentions in that this provider doesn't return the default page in a web, rather just a reference to the web itself, but that's ok - he explains a workaround to this if you're interested...
Essentially, my client's problem is that they have a left-side tree navigation, kind of. There's some interesting display logic that, like I said earlier, just doesn't work with the out-of-box implementation. Without going into what they really needed to accomplish, I figure it would be good to explain how to use the object to answer some of the common questions in a custom navigation scenario.
1) Where Am I Now?
2) Where's That Page?
Luckily, the CurrentNavSiteMapProviderNoEncode has a method that makes finding stuff pretty easy. FindSiteMapNode(). This method has three overloads, all which return a SiteMapNode object.
The first overload takes an HttpContext as a parameter and is great for finding where you're at now. The second overload takes a URL and can be used for finding any SiteMapNode given the URL provided. The third takes both a URL and an SPWeb object as context.
Here's some examples on how you use this along with the Publishing object model:
//In a server control, find the SiteMapNode for the current HttpContext
SiteMapNode siteNode = PortalSiteMapProvider.CurrentNavSiteMapProviderNoEncode.FindSiteMapNode(Context);
/// <summary>
/// Find SiteMapNode for PublishingWeb object
/// </summary>
/// <param name="web">PublishingWeb object to retrieve in PortalSiteMapProvider</param>
/// <returns>SiteMapNode object representing given PublishingWeb object</returns>
protected SiteMapNode GetSiteNodeForPubWeb(PublishingWeb web)
{
string url = web.Url + "/" + web.DefaultPage.Url;
SiteMapNode siteNode = PortalSiteMapProvider.CurrentNavSiteMapProviderNoEncode.FindSiteMapNode(url);
return siteNode;
}
The SiteMapNode object contains everything needed to walk around in the hierarchy of the PSMP, and should allow you conquer any custom display logic any tricky designer can throw at you...
Update: I added a follow-up post to this one with some Sample Code for a Custom C# MOSS Navigation Using PortalSiteMapProvider