Implementing a custom SiteMapProvider 

Thursday, January 26, 2006 2:50:36 PM
Rate this Content 0 Votes

For mojoPortal I needed to implement a custom SiteMapProvider to use the new ASP.NET 2.0 Menu and treeview controls and the built in SiteMapPath control which provides breadcrumbs. Since mojoPortal supports multiple database platforms (MS SQL, MySQL, PostgreSQL, and more recently SQLite thanks to Joseph Hill) I needed a solution that would work with all of them.

One approach would be to make a MSSqlSiteMapProvider (there is already one out there  you can use), a MySqlSiteMapProvider, a PostgreSqlSiteMapProvider and so on.  But since I already have a SiteSettings business object which has a PageSettings collection it made sense to implement my mojoSiteMapProvider using these objects which are already abstracted above the data services facade. For anyone else who might already have some business objects reperesenting the page hierarchy of their site I thought I would post this example:

using System;
using System.IO;
using System.Web;
using System.Data.SqlClient;
using System.Collections;
using System.Collections.Specialized;
using System.Configuration;
using System.Web.Configuration;
using System.Collections.Generic;
using System.Configuration.Provider;
using System.Security.Permissions;
using System.Data.Common;
using System.Data;
using System.Web.Caching;
using mojoPortal.Business;

namespace mojoPortal.Web
{
    /// <summary>
    /// Author:             Joe Audette
    /// Created:            1/21/2006
    /// Last Modified:    1/23/2006
    ///
    /// </summary>
    public class mojoSiteMapProvider : StaticSiteMapProvider
    {

        private const string cacheDependencyName = "siteMapCacheDependency";
        private readonly object objLock = new object();
        private SiteMapNode rootNode;
        private Dictionary<int, SiteMapNode> nodes = new Dictionary<int, SiteMapNode>(16);
        protected SiteSettings siteSettings;
       
        public override void Initialize(string name, NameValueCollection config)
        {
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }

            if (String.IsNullOrEmpty(name))
            {
                name = "mojoSiteMapProvider";
            }

            if (string.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", "mojoPortal site map provider");
            }

            base.Initialize(name, config);
   
        }

        public override SiteMapNode BuildSiteMap()
        {

            if (rootNode != null)
            {
                return rootNode;
            }

            lock (objLock)
            {
                siteSettings = SiteSettings.GetCurrent();

                if (siteSettings != null)
                {
                    rootNode = CreateSiteMapNode((PageSettings)siteSettings.Pages[0], -1);

                    for (int i = 0; i < siteSettings.Pages.Count; i++)
                    {
                        PageSettings page = (PageSettings)siteSettings.Pages[i];
                        AddNode(CreateSiteMapNode(page, i), GetParentNode(page));
                      
                    }
                }
            }

            CacheDependency cacheDependency = new CacheDependency(
                SiteUtils.GetPathToSiteMapCacheDependencyFile());

            HttpRuntime.Cache.Insert(
                cacheDependencyName,
                new object(),
                cacheDependency,
                Cache.NoAbsoluteExpiration,
                Cache.NoSlidingExpiration,
                CacheItemPriority.Normal,
               new CacheItemRemovedCallback(OnSiteMapChanged));

            return rootNode;
        }


        private SiteMapNode CreateSiteMapNode(PageSettings page, int pageIndex)
        {
           
            string[] rolelist = null;
            if (!String.IsNullOrEmpty(page.AuthorizedRoles))
            {
                rolelist = page.AuthorizedRoles.Split(new char[] { ',', ';' }, 512);
            }

            string pageUrl;
            if (page.UseUrl)
            {
                pageUrl = page.Url;
            }
            else
            {
                if (pageIndex > -1)
                {
                    pageUrl =  "~/Default.aspx?pageindex=" + pageIndex.ToString()
                        + "&pageid=" + page.PageID.ToString();
                }
                else
                {
                    pageUrl =  "~/Default.aspx?pageid=" + page.PageID.ToString();
                }
            }

            SiteMapNode node = new SiteMapNode(
                this,
                page.PageID.ToString(),
                pageUrl,
                page.PageName,
                page.PageName,
                rolelist, null, null, null);

           
            if(!this.nodes.ContainsKey(page.PageID))
            {
                nodes.Add(page.PageID, node);
            }
  
            return node;
        }

        private SiteMapNode GetParentNode(PageSettings page)
        {
            if (page.ParentID <= -1)
            {
                return this.rootNode;

            }

            return nodes[page.ParentID];

        }


        protected override SiteMapNode GetRootNodeCore()
        {
             BuildSiteMap();
             return rootNode; 
        }

        protected override void Clear()
        {
            lock (objLock)
            {
                this.rootNode = null;
                this.nodes.Clear();
                base.Clear();
            }
        }


        public void OnSiteMapChanged(string key, object item, CacheItemRemovedReason reason)
        {
            Clear();

        }

    }
}

Share This Using Popular Bookmarking Services

re: Implementing a custom SiteMapProvider

Tuesday, January 31, 2006 6:39:21 PM Joe
I had to modify this to add the child nodes recursively. If you'd like to see the updated code let me know or just get the 2.1 branch of mojoportal form svn.

Joe

re: Implementing a custom SiteMapProvider

Wednesday, January 31, 2007 10:54:01 AM Rik

I'd love to have that modification with the recursive loading if you could post it. 

Thanks in advance, Rik


re: Implementing a custom SiteMapProvider

Tuesday, February 06, 2007 5:43:44 PM Joe Audette
Hi,

Sorry it took so long to respond, been getting more email than I can
process lately.

You can view the source of my latest custom SiteMapProvider here:
https://forgesvn1.novell.com/viewsvn/mojoportal/branches/2.x/Web/Components/mojoSiteMapProvider.cs?view=markup

Note that I also implemented a custom SiteMapNode
https://forgesvn1.novell.com/viewsvn/mojoportal/branches/2.x/Web/Components/mojoSiteMapNode.cs?view=markup

Hope it helps,

Joe

Comments are closed on this post.
Donate Money to support the mojoPortal Project. View Joe Audette's profile on LinkedIn View Joe Audette's profile on The Guild of Accessible Web Designers site