Here is the Dynamic sitemap class modified from its original state. I put this in the Business logic namespace under Utils. Notice that the stuff that is relevant for a nopCommerce category menu is in the AddDynamicNodes() method:
/*
* DynamicSiteMapProvider
*
* Created by Simon Harriyott http://harriyott.com
* More details at http://tinyurl.com/2t4olk
*
* DynamicSiteMapProvider is a class that adds new
* items to a site map at runtime. The filename of
* the existing xml sitemap is set in the Web.Config
* file. This file is loaded, and extra nodes can
* be added to the xml (by you) before the new xml is
* converted into a site map node structure, and passed
* back to the calling object.
*
* Feel free to use and modify this class for personal,
* non-profit or commercial use, without charge. I would
* appreciate you leaving this comment here. I'd also
* appreciate a link to my blog from your website, but
* I'll leave that up to you.
*
*/
#region Using
using System;
using System.Web;
using System.Web.UI;
using System.Xml;
using NopSolutions.NopCommerce.BusinessLogic.Categories;
using NopSolutions.NopCommerce.BusinessLogic.Products;
using NopSolutions.NopCommerce.BusinessLogic.SEO;
#endregion
namespace NopSolutions.NopCommerce.BusinessLogic.Utils {
/// <summary>
/// Adds new items to an existing xml site map at runtime.
/// Modify this class to add your own items.
/// </summary>
public class DynamicSiteMapProvider : StaticSiteMapProvider {
public DynamicSiteMapProvider()
: base() {
}
private String _siteMapFileName;
private SiteMapNode _rootNode = null;
private XmlNamespaceManager _NamespaceManager;
public XmlNamespaceManager NamespaceManager {
get { return _NamespaceManager; }
}
// Return the root node of the current site map.
public override SiteMapNode RootNode {
get {
return BuildSiteMap();
}
}
/// <summary>
/// Pull out the filename of the site map xml.
/// </summary>
/// <param name="name"></param>
/// <param name="attributes"></param>
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection attributes) {
base.Initialize(name, attributes);
_siteMapFileName = attributes["siteMapFile"];
}
private const String SiteMapNodeName = "siteMapNode";
public override SiteMapNode BuildSiteMap() {
lock (this) {
if (null == _rootNode) {
Clear();
// Load the sitemap's xml from the file.
XmlDocument siteMapXml = LoadSiteMapXml();
_NamespaceManager = new XmlNamespaceManager(siteMapXml.NameTable);
_NamespaceManager.AddNamespace("map", siteMapXml.DocumentElement.NamespaceURI);
// Create the first site map item from the top node in the xml.
XmlElement rootElement =
(XmlElement)siteMapXml.GetElementsByTagName(
SiteMapNodeName)[0];
// This is the key method - add the dynamic nodes to the xml
AddDynamicNodes(rootElement);
// Now build up the site map structure from the xml
GenerateSiteMapNodes(rootElement);
}
}
return _rootNode;
}
/// <summary>
/// Open the site map file as an xml document.
/// </summary>
/// <returns>The contents of the site map file.</returns>
private XmlDocument LoadSiteMapXml() {
XmlDocument siteMapXml = new XmlDocument();
siteMapXml.Load(AppDomain.CurrentDomain.BaseDirectory + _siteMapFileName);
return siteMapXml;
}
/// <summary>
/// Creates the site map nodes from the root of
/// the xml document.
/// </summary>
/// <param name="rootElement">The top-level sitemap element from the XmlDocument loaded with the site map xml.</param>
private void GenerateSiteMapNodes(XmlElement rootElement) {
_rootNode = GetSiteMapNodeFromElement(rootElement);
AddNode(_rootNode);
CreateChildNodes(rootElement, _rootNode);
}
/// <summary>
/// Recursive method! This finds all the site map elements
/// under the current element, and creates a SiteMapNode for
/// them. On each of these, it calls this method again to
/// create it's new children, and so on.
/// </summary>
/// <param name="parentElement">The xml element to iterate through.</param>
/// <param name="parentNode">The site map node to add the new children to.</param>
private void CreateChildNodes(XmlElement parentElement, SiteMapNode parentNode) {
foreach (XmlNode xmlElement in parentElement.ChildNodes) {
if (xmlElement.Name == SiteMapNodeName) {
SiteMapNode childNode = GetSiteMapNodeFromElement((XmlElement)xmlElement);
AddNode(childNode, parentNode);
CreateChildNodes((XmlElement)xmlElement, childNode);
}
}
}
/// <summary>
/// The key method. You can add your own code in here
/// to add xml nodes to the structure, from a
/// database, file on disk, or just from code.
/// To keep the example short, I'm just adding from code.
/// </summary>
/// <param name="rootElement"></param>
private void AddDynamicNodes(XmlElement rootElement) {
//This is the place to start adding dynamic content.
//Get an instance of the top level menu item ... in this case "Shop"
//and pass it into the BuildCategorySubMen method
XmlElement shopElement = GetElementByTitle(rootElement, "Shop");
if (shopElement != null) {
BuildCategorySubMenu(shopElement);
}
}
private static XmlElement AddDynamicChildElement(XmlElement parentElement, String url, String title, String description) {
// Create new element from the parameters
XmlElement childElement = parentElement.OwnerDocument.CreateElement(SiteMapNodeName);
childElement.SetAttribute("url", url);
childElement.SetAttribute("title", title);
childElement.SetAttribute("description", description);
// Add it to the parent
parentElement.AppendChild(childElement);
return childElement;
}
private SiteMapNode GetSiteMapNodeFromElement(XmlElement rootElement) {
SiteMapNode newSiteMapNode;
String url = rootElement.GetAttribute("url");
String title = rootElement.GetAttribute("title");
String description = rootElement.GetAttribute("description");
// The key needs to be unique, so hash the url and title.
newSiteMapNode = new SiteMapNode(this,
(url + title).GetHashCode().ToString(), url, title, description);
return newSiteMapNode;
}
protected void BuildCategorySubMenu(XmlElement topItem) {
CategoryCollection breadcrumbs = CategoryManager.GetAllCategories(0);
foreach (Category category in breadcrumbs) {
if (CategoryManager.GetBreadCrumb(category.CategoryID).Count > 0) {
XmlElement newTop = AddDynamicChildElement(topItem, string.Format("~/Category.aspx?CategoryID={0}", category.CategoryID), category.Name, category.Description);
BuildSubMenu(CategoryManager.GetBreadCrumb(category.CategoryID), category.CategoryID, category, 0, newTop);
}
else {
AddDynamicChildElement(topItem, string.Format("~/Category.aspx?CategoryID={0}",category.CategoryID), category.Name, category.Description);
}
}
}
protected void BuildSubMenu(CategoryCollection breadCrumb, int rootCategoryID,
Category currentCategory1, int level, XmlElement topItem) {
foreach (Category category in CategoryManager.GetAllCategories(rootCategoryID)) {
if (breadCrumb.Count > 1) {
XmlElement newTop = AddDynamicChildElement(topItem, string.Format("~/Category.aspx?CategoryID={0}", category.CategoryID), category.Name, category.Description);
BuildSubMenu(CategoryManager.GetBreadCrumb(category.CategoryID), category.CategoryID, category, 0, newTop);
}
else {
AddDynamicChildElement(topItem, string.Format("~/Category.aspx?CategoryID={0}", category.CategoryID), category.Name, category.Description);
}
}
}
private XmlElement GetElementByUrl(XmlElement rootElement, string url) {
return rootElement.SelectSingleNode(String.Format("//map:{0}[@url='{1}']", SiteMapNodeName, url), NamespaceManager) as XmlElement;
}
//This could break if there are more than one elements with the same title
//URL is a key for this node so it wont break
private XmlElement GetElementByTitle(XmlElement rootElement, string title) {
return rootElement.SelectSingleNode(String.Format("//map:{0}[@title='{1}']", SiteMapNodeName, title), NamespaceManager) as XmlElement;
}
protected override SiteMapNode GetRootNodeCore() {
return RootNode;
}
// Empty out the existing items.
protected override void Clear() {
lock (this) {
_rootNode = null;
base.Clear();
}
}
}
}