Integrating web.sitemap feature in WCSF

Topics: Web Client Software Factory, UIP Application Block discussion, User Forum
Jul 16, 2007 at 4:14 AM
Dear All,
I have designed a solution for WCSF to work with web.sitemap files under every business module. Please have look at the Use Case and the approach.
Consider the following Use Case:
Using WSCF modular based approach we need to define the following business modules under the root folder
DevelopmentWebSite
Employee
Machine
Manager
Reports

We have a Main page that has a MainMenu that needs to be populated via Web.SiteMap. The following are the top nodes of the Main Menu.

Home Asset Supervise Reports Section

Leaf nodes under these nodes are populated from the different modules and pages contained under them

Home --- Personal Info Employee\Personal.aspx
Employee Rating Employee\Rating.aspx
Asset Info Machine\Information.aspx

Asset --- Machine Track Record Machine\Maintainence.aspx
Machine Configuration Machine\Configuration.aspx
Machine User Info Employee\UserDetails.aspx
Machine Parts Reports\MachineDetails.aspx

Supervise --- Manager Info Manager\ManagerInfo.aspx
Team Info Employee\SupervisorTimings.aspx
Machine Section Info Machine\MachineInSection.aspx
Production Info Reports\UnitsofWork.aspx

Reports Section --- New/Edit Report Templates Reports\NewReportTemplate.aspx
Execute Report Reports\RunReport.aspx
Save Report Tempate Reports\SaveTempate.aspx
Export Report Reports\ExportTo.aspx

A sitemap can have only one root node. So we need to have a virtual root node and have the Main Menu top nodes under the root node. In WSCF every module has an ModuleInitializer class that has a function <RegisterSiteMapInformation> that uses the <siteMapBuilderService> to add nodes to the sitemap. This is done via code. We need to achieve the same functionality using Web.SiteMap and that too under parent node not defined in the current module. <siteMapBuilderService> needs a function that takes ParentNodeInfo as a string rather than a SitMapNodeInfo.
So we need to have Web.SiteMap under every module so that the SiteMap can be modified outside the code. It is also required to place nodes under specific parent nodes defined under the root or in some other module.
Approach:
1. Define a Web.Sitmap under the root and every business module folder (it also has web.config already defined).
Add an attribute <MenuSection> for a node a and assign it a key. f.e.x.
<siteMapNode url="" title="" description="">
<siteMapNode url="~/Machine/Maintainence.aspx" title="Machine track record" description="Machine track record" ResourceKey="MachineMaintainenceSite" MenuSection="Asset">
<siteMapNode url="~/Machine/MaintainceLevel2.aspx" title="Level 2 Maintenance record" description="Level 2 Maintenance record" ResourceKey="Level2MaintainenceSite"/>
</siteMapNode>
<siteMapNode url="~/Machine/Configuration.aspx" title="Machine Configuration" description="Machine Configuration" ResourceKey="MachineMaintainenceConfigSite" MenuSection="Asset" />

2. The root folder already has a ModuleInitializer class defined f.e.x. Shell. Write the following code in the load function
IModuleEnumerator ModuleEnumerator = moduleContainer.Parent.Services.Get<IModuleEnumerator>(true);
BuildModuleSiteMap(ModuleEnumerator);
3. BuildModuleSiteMap function loops through each Module and finds Web.SiteMap and adds node via SiteMapBuilder service
private void BuildModuleSiteMap(IModuleEnumerator ModuleEnumerator)
{
string filename = "/web.sitemap";
foreach (IModuleInfo moduleInfo in ModuleEnumerator.EnumerateModules())
{
if (!String.IsNullOrEmpty(moduleInfo.VirtualPath))
{
string path = moduleInfo.VirtualPath;
string dirfile = HttpContext.Current.Server.MapPath(path + filename);
if (System.IO.File.Exists(dirfile))
{
XmlSiteMapProvider oProvider = new XmlSiteMapProvider();
NameValueCollection providerAttributes = new NameValueCollection(1);
providerAttributes.Add("siteMapFile", path + filename);
// Initialize the provider with a provider name and file name.
oProvider.Initialize("StandardMapProvider", providerAttributes);
// Call the BuildSiteMap to load the site map information into memory.
oProvider.BuildSiteMap();
AddSiteNodes(oProvider.RootNode, null);
}

}
}
}

private void AddSiteNodes(SiteMapNode oNode, SiteMapNodeInfo oParentNode)
{
string nodeKey;
foreach (SiteMapNode childNodesEnumerator in oNode.ChildNodes)
{
NameValueCollection nodeAttributes = new NameValueCollection(4);
nodeAttributes.Add("MenuSection", childNodesEnumerator"MenuSection");
nodeAttributes.Add("ID", childNodesEnumerator"ID");
nodeAttributes.Add("ImageURL", childNodesEnumerator"ImageURL");
nodeAttributes.Add("TargetFrame", childNodesEnumerator"TargetFrame");
nodeKey = (childNodesEnumerator"Key" != null) ? childNodesEnumerator"Key" : childNodesEnumerator"ResourceKey";
SiteMapNodeInfo moduleNode = new SiteMapNodeInfo(nodeKey, childNodesEnumerator.Url, childNodesEnumerator.Title, childNodesEnumerator.Description, childNodesEnumerator.Roles, nodeAttributes, null, childNodesEnumerator"ResourceKey");
if (oParentNode != null)
_siteMapBuilderService.AddNode(moduleNode, oParentNode);
else
{
if (childNodesEnumerator"MenuSection" != null)
_siteMapBuilderService.AddNodeTo(moduleNode, childNodesEnumerator"MenuSection");
else
_siteMapBuilderService.AddNode(moduleNode);
}
if (childNodesEnumerator.HasChildNodes)
AddSiteNodes(childNodesEnumerator, moduleNode);
}
}

4. Add a function <AddNodeTo> in SiteMapBuilderService that takes ParentNode key to add a node to the parent.
public void AddNodeTo(SiteMapNodeInfo node, string parent)
{
if (_keyIndex.ContainsKey(parent))
{
AddNode(node, _keyIndexparent, int.MaxValue);
}
else
{
throw new System.NullReferenceException(string.Format("Parent Node <{0}> does not exist in collection", parent));
}
}

I would like to publish this solution in CodeProject for a wider audience. Please let me know if you have any suggestions to improve this solution. Thanks.

Many regards
Sunil
Developer
Jul 16, 2007 at 6:12 PM
Hi Sunil, great work. You should consider to contribute with this to the WCSF Contrib project. Take a look to the sign up process if you are interested.

Ezequiel Jadib
http://staff.southworks.net/blogs/ejadib
Jul 17, 2007 at 3:25 AM
Thanks for the feedback...I am interested in enrolling to WCSF Contrib project...will do it now...thanks...
Aug 7, 2008 at 3:22 PM

I think this is a great solution and should be added to the WCSF in the next build so that we don't have to extend the CompositeWeb source every time there is a new version. I think managing nodes in the Web.sitemap file is a better base rather than updating the ModuleInitializer class.

Thank you.