I'm trying to create a sitemap for my MOSS publishing site, i've got two approaches but seem to be stuck with both.

My first approach is to use the PortalSiteMapProvider, which is already created and nicely cached...

PublishingWeb rootWeb = PublishingWeb.GetPublishingWeb(SPContext.Current.Site.RootWeb);

//Get the URL of the default page in the web
string defaultPageUrl = rootWeb.DefaultPage.ServerRelativeUrl;

PortalListItemSiteMapNode webNode = (PortalListItemSiteMapNode)PortalSiteMapProvider.CurrentNavSiteMapProviderNoEncode.FindSiteMapNode(defaultPageUrl);

HttpContext.Current.Response.Output.WriteLine("Top Level: " + webNode.Title.ToString() + "<br />");

//iterate through each one of the pages and subsites
foreach (SiteMapNode smnTopLevelItem in webNode.ParentNode.ChildNodes)

    HttpContext.Current.Response.Output.WriteLine(smnTopLevelItem.Title.ToString() + "<br />");

    //if the current sitemap has children, create a submenu for it
    if (smnTopLevelItem.HasChildNodes)
     foreach (SiteMapNode smnChildItem in smnTopLevelItem.ChildNodes)
      HttpContext.Current.Response.Output.WriteLine(smnChildItem.Title.ToString() + "<br />");


but this seems to return everything in the site collection (e.g. lists, surverys). I only want to show the Sharepoint webs.

My other approach was to use this piece of code..

SPSite siteCollection = new SPSite("");
SPWebCollection sites = siteCollection.AllWebs;
foreach (SPWeb site in sites)
    Console.WriteLine(site.Title.ToString() + " " + site.ServerRelativeUrl.ToString());

Which is perfect, apart from the problem of returning all the webs in a flat list.

Ideally I want to be able to add indentation to show child webs.

+4  A: 

Generally it's a bad idea to use the object model for recursion. It's extremely slow and resource-intensive to do this. PortalSiteMapProvider is pre-cached for you and can tear through an entire site structure in milliseconds.

Regarding your question about SPSite.AllWebs, that property does return a flat list of all webs. That's what it's for. If you want a list of only the immediate child webs, use the SPSite.RootWeb.Webs property. Recurse over each SPWeb in the .Webs property, and call their .Webs property in turn to get a tree-view.

Also, when dealing with the object model, make sure to DISPOSE EVERY WEB AND SITE. This will cause epic bad problems if you don't. This includes disposing every web in the .Webs collection, even if you haven't touched it.


To get the PortalSiteMapProvider to return only webs, set its IncludePages property to false.

Rex M
ok cool, i'd like to use PortalSiteMapProvider but i'm unsure how to restrict the output to webs only.
+2  A: 

Have you tried checking the type of every node in the foreach loop using the PortalSiteMapNode.Type property and displaying only nodes of type NodeTypes.Area?

Waldek Mastykarz - MOSS MVP
Excellent answer!

thanks for the replys everyone, this is what I came up with

        public ListSiteMap()
        PortalSiteMapProvider portalProvider1 = PortalSiteMapProvider.WebSiteMapProvider;
        portalProvider1.DynamicChildLimit = 0;
        portalProvider1.EncodeOutput = true;

        SPWeb web = SPContext.Current.Site.RootWeb;

        PortalSiteMapNode webNode = (PortalSiteMapNode)portalProvider1.FindSiteMapNode(web.ServerRelativeUrl);

        if (webNode == null || webNode.Type != NodeTypes.Area) return;

        Console.WriteLine(webNode.Title.ToString() + " - " + webNode.Description.ToString());

        // get the child nodes (sub sites)

    private void ProcessSubWeb(PortalSiteMapNode webNode)
        foreach (PortalSiteMapNode childNode in webNode.ChildNodes)
            Console.WriteLine(childNode.Title.ToString() + " - " + childNode.Description.ToString());

            //if the current web has children, call method again
            if (childNode.HasChildNodes)

I found these articles helped



I'm about to do the same thing (i.e. get quicklaunch in MOSS site to show sites and subsites only.

I get the PortalSiteMap but you seem to be rendering the results to a Console applicaton as opposed to a .sitemap xml.

Could you please explain?

I just wanted to see the output to verify it was correct. One thing I haven't accounted for in the code is hidden items - you may want to check for these when creating your xml file.