I recently had a customer who wanted a simple web part that just listed the subsites of a site – something like what you see in SharePoint’s “View All Site Content” page, but for a couple of levels, rather than just one. Obviously, this would be quite simple to do directly by traversing the SPWeb object and it’s children, but it struck me that there must be a better, standard way – ideally one that uses a bit of caching or optimisation.
I found myself looking at the PortalSiteMapProvider for the first time. I’ve written my own NavigationProviders before now, and I’ve used some of the out of the box ones, but the PortalSiteMapProvider is an object I’ve never really used, or become familiar with. Here’s what I found…
I added a new Navigation Provider in my Web.Config. None of the standard, out of the box configurations were quite as I wanted:
<add name="AWBTestNav" description="" type="Microsoft.SharePoint.Publishing.Navigation.PortalSiteMapProvider, Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" NavigationType="Current" EncodeOutput="true" IncludePages="Never" IncludeSubSites="Always" IncludeHeadings="false" IncludeAuthoredLinks="false" />;
This navigation provider, for the current site, never shows pages, headings or authored links, and always shows subsites.
Then I added a new SiteMapDataSource and Menu to my page:
<asp:SiteMapDataSource SiteMapProvider="AWBTestNav" ShowStartingNode="false" id="AWBTestSiteMap" runat="server"/> <SharePoint:AspMenu id="AWBNavMenu" DataSourceId="AWBTestSiteMap" runat="server" Orientation="Vertical" StaticDisplayLevels="2" ItemWrap="true" MaximumDynamicDisplayLevels="0" StaticSubMenuIndent="0" SkipLinkText="" > ... (Left out the Level styles) ... </SharePoint:AspMenu>
You’ll notice that we’ve got 2 levels being displayed in the menu. And Presto:
Of course, we could interact with the provider directly. I wrote some code to traverse a few levels:
//... PortalSiteMapProvider psmp = PortalSiteMapProvider.WebSiteMapProvider ; SiteMapNode rootNode = psmp.CurrentNode; // output the node here PortalSiteMapNode psmn = rootNode as PortalSiteMapNode; SiteMapNodeCollection nodes = psmp.GetChildNodes(psmn, NodeTypes.Area, NodeTypes.Area, NodeOrder.LastModifiedDate, false); foreach (SiteMapNode n in nodes) { Recurse(psmp, n, 1); } //.... private void Recurse(PortalSiteMapProvider psmp, SiteMapNode node, int level) { PortalSiteMapNode psmn = node as PortalSiteMapNode; // output the node here // Arbitrary limit of 2 levels below root. if (level < 3) { SiteMapNodeCollection nodes = psmp.GetChildNodes(psmn, NodeTypes.Area, NodeTypes.Area,NodeOrder.LastModifiedDate,false); foreach (SiteMapNode n in nodes) { Recurse(psmp, n, level+1); } } }
This code gets me the root node, and 2 levels of subnodes, via the object model. And it does it with sorting and caching, which is neat.