Sometimes we need to overwrite the existing navigation provider in SharePoint and we can create/write our own navigation provider to get the data from SharePoint list. This article will help you to implement your own custom navigation provider.
Final Output:
To create custom navigation provider requires following steps
Step 1: create custom site columns, custom content types and then create custom list that used these content types to allow users to easily build hierarchies for navigation provider.
Step 2: Replace the following code in the master page
Step 3:Create new class which should be inherited from "PortalSiteMapProvider" class.
using System;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Text;
usingSystem.Configuration;
usingSystem.Collections;
usingSystem.Web;
usingSystem.Web.Caching;
usingMicrosoft.SharePoint;
usingMicrosoft.SharePoint.Security;
usingMicrosoft.SharePoint.Publishing;
usingMicrosoft.SharePoint.Publishing.Navigation;
usingMicrosoft.SharePoint.Administration;
usingSystem.Security.Permissions;
usingSystem.Security;
namespaceTopNavProvider
{
//Assign the neccessary security permissions. TODO - Check the permissions required.
[AspNetHostingPermissionAttribute(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
[SharePointPermissionAttribute(SecurityAction.LinkDemand, ObjectModel = true)]
[AspNetHostingPermissionAttribute(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
[SharePointPermissionAttribute(SecurityAction.InheritanceDemand, ObjectModel = true)]
//This inherits from the PortalSiteMapProvider class in MOSS, just because it provides some of the functions I need.
//You could just as easily write one for WSS.
public classTopNavProvider: PortalSiteMapProvider
{
//Create the in memory objects for storage and fast retreival
protectedSiteMapNodeCollectiongSiteMapNodeColl;
protectedArrayListgChildParentRelationship;
//These are only the top level nodes that will show in the top nav
protectedArrayListgTopLevelNodes;
privatePortalSiteMapNodegRootNode = null;
public override void Initialize(string pName, System.Collections.Specialized.NameValueCollectionpConfig)
{
// Verify that config isn't null
if (pConfig == null)
throw new ArgumentNullException("config is null");
// Assign the provider a default name if it doesn't have one
if (String.IsNullOrEmpty(pName))
pName = "TopNavProvider";
// Add a default "description" attribute to config if the
// attribute doesn’t exist or is empty
if (string.IsNullOrEmpty(pConfig["description"]))
{
pConfig.Remove("description");
pConfig.Add("description", "top navigation provider");
}
base.Initialize(pName, pConfig);
gChildParentRelationship = new ArrayList();
gTopLevelNodes = new ArrayList();
//Build the site map in memory
LoadTopNavigationFromList();
}
protected virtual void LoadTopNavigationFromList()
{
//Make sure to build the structure in memory only once
lock (this)
{
if (gRootNode != null)
{
return;
}
else
{
//Initialiaze for the first time
SPSite _rootSite = null;
SPWeb _rootWeb = null;
SPList _topnavList = null;
try
{
//Clear the top level nodes and the relationships
gTopLevelNodes.Clear();
gChildParentRelationship.Clear();
//instantiate sites and lists for now. This setting assumes that the list being
//read from for the global top navigation is in the root web of the site collection listed in web.config.
_rootSite = SPContext.Current.Site;
_rootWeb = _rootSite.RootWeb;
_topnavList = _rootWeb.Lists[ConfigurationManager.AppSettings["TopNavigationListName"]];
//Build the root node
//Note: Any top level site of any site collection is assigned to be the rootNode here, not neccessarily the
//top level site of the main site collection
gRootNode = (PortalSiteMapNode)this.RootNode;
//We need to pass the PortalSiteMapNode constructor a PortalWebSiteMapNode object, so here it is
//Note: This is the root node of 1 site collection, but the navigation will be shown in all site collections.
PortalWebSiteMapNode _pwsmn = gRootNode as PortalWebSiteMapNode;
if (_pwsmn != null)
{
//Get the current folder to start. The navigation heirarchy can start at that folder.
SPFolder _currentFolder = _topnavList.RootFolder.SubFolders[ConfigurationManager.AppSettings["NavigationListStartFolderName"]];
//Build the nodes
BuildListNodes(_rootWeb, _currentFolder, _pwsmn, null, true);
}
}
catch (Exception ex)
{
//There was a problem opening the site or the list.
;
}
}
}
}
protected virtual void BuildListNodes(SPWebpCurrentWeb, SPFolderpFolder, PortalWebSiteMapNodepPrtlWebSiteMapNode, PortalSiteMapNodepParentSiteMapNode, boolpRootLevel)
{
// Get the collection of items from this folder
SPQuery _qry = new SPQuery();
_qry.Folder = pFolder;
SortedList _orderedNodes = new SortedList();
int _counter = 100; //for sorting items
try
{
//Browse through the items in the folder and create PortalSiteMapNodes
SPListItemCollection _ic = pCurrentWeb.Lists[pFolder.ParentListId].GetItems(_qry);
foreach (SPListItem _subitem in _ic)
{
//Change the nodeTypes to Authored link for leaf nodes so that the GetChildNodes method is not called for those nodes.
NodeTypes _ntypes = NodeTypes.AuthoredLink;
PortalSiteMapNode _psmn = null;
Boolean _flag = false;
try
{
//Create a PortalSiteMapNode
if (_subitem.Folder != null)
_ntypes = NodeTypes.Default;
ConfigurationManager.AppSettings.Set("Current User", pCurrentWeb.CurrentUser.Name);
SPGroupCollection _gCol = pCurrentWeb.CurrentUser.Groups;
foreach (SPGroupgrp in _gCol)
{
string[] groupName = _subitem["User Group"].ToString().Split('#');
foreach (string grpName in groupName)
{
if ((grp.Name.Equals(grpName.Split(';')[0])) && (_flag == false))
{
_flag = true;
}
}
}
if (_flag == true)
{
_psmn = new PortalSiteMapNode(pPrtlWebSiteMapNode, _subitem.ID.ToString(), _ntypes,
_subitem.GetFormattedValue(ConfigurationManager.AppSettings["UrlLink"]), _subitem.Title,
_subitem.GetFormattedValue(ConfigurationManager.AppSettings["UrlDescription"]));
string _IsNewWindow = Convert.ToString(_subitem.GetFormattedValue(ConfigurationManager.AppSettings["NewWindow"]));
SiteMapNode _node = _psmn as SiteMapNode;
if (_IsNewWindow == "Yes")
{
_node["target"] = "_blank";
}
//Order the nodes
if (_subitem.Folder != null)
{
int _order = Convert.ToInt32(_subitem.GetFormattedValue(ConfigurationManager.AppSettings["FolderItemOrder"]));
_orderedNodes.Add(_order, _psmn);
}
else
{
int _order = Convert.ToInt32(_subitem.GetFormattedValue(ConfigurationManager.AppSettings["ItemOrder"]));
_orderedNodes.Add(_order, _psmn);
}
}
}
catch (Exception ex)
{
//This will happen if 2 items are assigned the same order. Push one item to the last order.
_orderedNodes.Add(_counter++, _psmn);
//No need to process
}
//if this is a folder, fetch and build the heirarchy under this folder
if (_subitem.Folder != null)
BuildListNodes(pCurrentWeb, _subitem.Folder, pPrtlWebSiteMapNode, _psmn, false);
}
//Copy nodes in the right order
foreach (object portalSiteMapNode in _orderedNodes.Values)
{
//Add the node to the different collections
if (pRootLevel)
gTopLevelNodes.Add(portalSiteMapNode);
//If the parent node is not null, add the parent and the child relationship
if (pParentSiteMapNode != null)
gChildParentRelationship.Add(new DictionaryEntry(pParentSiteMapNode.Key, portalSiteMapNode));
}
}
catch (Exception ex)
{
;
}
}
public override SiteMapNodeCollectionGetChildNodes(System.Web.SiteMapNodepNode)
{
returnComposeNodes(pNode);
}
public virtual SiteMapNodeCollectionComposeNodes(System.Web.SiteMapNodepNode)
{
//The SiteMapNodeCollection which represents the children of this node
SiteMapNodeCollection _children = new SiteMapNodeCollection();
try
{
//If an absolute rootnode, then add the top level children which are the same for every site collection
if (pNode == pNode.RootNode)
{
SPSite _rootSite = SPContext.Current.Site;
SPWeb _rootWeb = _rootSite.RootWeb;
//Serve it from cache if possible.
object _topNodes = HttpRuntime.Cache["TopNavRootNodes"];
lock (this)
{
{
gRootNode = null;
LoadTopNavigationFromList();
}
//Else generate the top level nodes from memory. This must be done regardless of option 1 above
for (int i = 0; i lessthan gTopLevelNodes.Count; i++)
{
_children.Add(gTopLevelNodes[i] as PortalSiteMapNode);
}
//Add them to the cache
HttpRuntime.Cache["TopNavRootNodes"] = _children;
}
}
else
//Else this is a subnode, get only the children of that subnode
{
string _nodeKey = pNode.Key;
lock (this)
{
{
gRootNode = null;
LoadTopNavigationFromList();
}
//Else iterate through the nodes and find the children of this node
for (int i = 0; i lessthan gChildParentRelationship.Count; i++)
{
string _nKey = ((DictionaryEntry)gChildParentRelationship[i]).Key as string;
//if this is a child
if (_nodeKey == _nKey)
{
//Get the child from the arraylist
PortalSiteMapNode _child = (PortalSiteMapNode)(((DictionaryEntry)gChildParentRelationship[i]).Value);
if (_child != null)
{
_children.Add(_child as PortalSiteMapNode);
}
else
{
throw new Exception("ArrayLists not in sync.");
}
}
}
//Add the children to the cache
HttpRuntime.Cache["TopNavRootNodes" + _nodeKey] = _children;
}
}
}
catch (Exception ex)
{
//return empty site node collection
return new SiteMapNodeCollection(); // No need to process
}
return _children;
}
}
}
Step 4: Add the entry of the class in web config file of the site
1. Add the following entry under the sitemap -> provider tag
<siteMap defaultProvider="CurrentNavigation" enabled="true">
<providers>
<add name="TopNavProvider" description="Custom provider" type="CustomNavProvider.TopNavProvider, CustomNavProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=11d7cf682" NavigationType="Current" EncodeOutput="true" />
</providers>
</sitemap>
2. Add the following entry under app setting tag
<add key="TopNavigationListName" value="TopNavList"/>
<add key="CurrentLeftNavigationListName" value="CurrentLeftNavList"/>
<add key="StaticLeftNavigationListName" value="StaticNavList"/>
<add key="NavigationListStartFolderName" value="CoreList"/>
<add key="UrlLink" value="Url Link"/>
<add key="UrlDescription" value="Url Description"/>
<add key="NewWindow" value="Open New Window"/>
<add key="UrlAudience" value="Url Audience"/>
<add key="ItemOrder" value="Item Order"/>
<add key="FolderItemOrder" value="Folder Item Order"/>
Final Output:
To create custom navigation provider requires following steps
Step 1: create custom site columns, custom content types and then create custom list that used these content types to allow users to easily build hierarchies for navigation provider.
Step 2: Replace the following code in the master page
Step 3:Create new class which should be inherited from "PortalSiteMapProvider" class.
using System;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Text;
usingSystem.Configuration;
usingSystem.Collections;
usingSystem.Web;
usingSystem.Web.Caching;
usingMicrosoft.SharePoint;
usingMicrosoft.SharePoint.Security;
usingMicrosoft.SharePoint.Publishing;
usingMicrosoft.SharePoint.Publishing.Navigation;
usingMicrosoft.SharePoint.Administration;
usingSystem.Security.Permissions;
usingSystem.Security;
namespaceTopNavProvider
{
//Assign the neccessary security permissions. TODO - Check the permissions required.
[AspNetHostingPermissionAttribute(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
[SharePointPermissionAttribute(SecurityAction.LinkDemand, ObjectModel = true)]
[AspNetHostingPermissionAttribute(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
[SharePointPermissionAttribute(SecurityAction.InheritanceDemand, ObjectModel = true)]
//This inherits from the PortalSiteMapProvider class in MOSS, just because it provides some of the functions I need.
//You could just as easily write one for WSS.
public classTopNavProvider: PortalSiteMapProvider
{
//Create the in memory objects for storage and fast retreival
protectedSiteMapNodeCollectiongSiteMapNodeColl;
protectedArrayListgChildParentRelationship;
//These are only the top level nodes that will show in the top nav
protectedArrayListgTopLevelNodes;
privatePortalSiteMapNodegRootNode = null;
public override void Initialize(string pName, System.Collections.Specialized.NameValueCollectionpConfig)
{
// Verify that config isn't null
if (pConfig == null)
throw new ArgumentNullException("config is null");
// Assign the provider a default name if it doesn't have one
if (String.IsNullOrEmpty(pName))
pName = "TopNavProvider";
// Add a default "description" attribute to config if the
// attribute doesn’t exist or is empty
if (string.IsNullOrEmpty(pConfig["description"]))
{
pConfig.Remove("description");
pConfig.Add("description", "top navigation provider");
}
base.Initialize(pName, pConfig);
gChildParentRelationship = new ArrayList();
gTopLevelNodes = new ArrayList();
//Build the site map in memory
LoadTopNavigationFromList();
}
protected virtual void LoadTopNavigationFromList()
{
//Make sure to build the structure in memory only once
lock (this)
{
if (gRootNode != null)
{
return;
}
else
{
//Initialiaze for the first time
SPSite _rootSite = null;
SPWeb _rootWeb = null;
SPList _topnavList = null;
try
{
//Clear the top level nodes and the relationships
gTopLevelNodes.Clear();
gChildParentRelationship.Clear();
//instantiate sites and lists for now. This setting assumes that the list being
//read from for the global top navigation is in the root web of the site collection listed in web.config.
_rootSite = SPContext.Current.Site;
_rootWeb = _rootSite.RootWeb;
_topnavList = _rootWeb.Lists[ConfigurationManager.AppSettings["TopNavigationListName"]];
//Build the root node
//Note: Any top level site of any site collection is assigned to be the rootNode here, not neccessarily the
//top level site of the main site collection
gRootNode = (PortalSiteMapNode)this.RootNode;
//We need to pass the PortalSiteMapNode constructor a PortalWebSiteMapNode object, so here it is
//Note: This is the root node of 1 site collection, but the navigation will be shown in all site collections.
PortalWebSiteMapNode _pwsmn = gRootNode as PortalWebSiteMapNode;
if (_pwsmn != null)
{
//Get the current folder to start. The navigation heirarchy can start at that folder.
SPFolder _currentFolder = _topnavList.RootFolder.SubFolders[ConfigurationManager.AppSettings["NavigationListStartFolderName"]];
//Build the nodes
BuildListNodes(_rootWeb, _currentFolder, _pwsmn, null, true);
}
}
catch (Exception ex)
{
//There was a problem opening the site or the list.
;
}
}
}
}
protected virtual void BuildListNodes(SPWebpCurrentWeb, SPFolderpFolder, PortalWebSiteMapNodepPrtlWebSiteMapNode, PortalSiteMapNodepParentSiteMapNode, boolpRootLevel)
{
// Get the collection of items from this folder
SPQuery _qry = new SPQuery();
_qry.Folder = pFolder;
SortedList _orderedNodes = new SortedList();
int _counter = 100; //for sorting items
try
{
//Browse through the items in the folder and create PortalSiteMapNodes
SPListItemCollection _ic = pCurrentWeb.Lists[pFolder.ParentListId].GetItems(_qry);
foreach (SPListItem _subitem in _ic)
{
//Change the nodeTypes to Authored link for leaf nodes so that the GetChildNodes method is not called for those nodes.
NodeTypes _ntypes = NodeTypes.AuthoredLink;
PortalSiteMapNode _psmn = null;
Boolean _flag = false;
try
{
//Create a PortalSiteMapNode
if (_subitem.Folder != null)
_ntypes = NodeTypes.Default;
ConfigurationManager.AppSettings.Set("Current User", pCurrentWeb.CurrentUser.Name);
SPGroupCollection _gCol = pCurrentWeb.CurrentUser.Groups;
foreach (SPGroupgrp in _gCol)
{
string[] groupName = _subitem["User Group"].ToString().Split('#');
foreach (string grpName in groupName)
{
if ((grp.Name.Equals(grpName.Split(';')[0])) && (_flag == false))
{
_flag = true;
}
}
}
if (_flag == true)
{
_psmn = new PortalSiteMapNode(pPrtlWebSiteMapNode, _subitem.ID.ToString(), _ntypes,
_subitem.GetFormattedValue(ConfigurationManager.AppSettings["UrlLink"]), _subitem.Title,
_subitem.GetFormattedValue(ConfigurationManager.AppSettings["UrlDescription"]));
string _IsNewWindow = Convert.ToString(_subitem.GetFormattedValue(ConfigurationManager.AppSettings["NewWindow"]));
SiteMapNode _node = _psmn as SiteMapNode;
if (_IsNewWindow == "Yes")
{
_node["target"] = "_blank";
}
//Order the nodes
if (_subitem.Folder != null)
{
int _order = Convert.ToInt32(_subitem.GetFormattedValue(ConfigurationManager.AppSettings["FolderItemOrder"]));
_orderedNodes.Add(_order, _psmn);
}
else
{
int _order = Convert.ToInt32(_subitem.GetFormattedValue(ConfigurationManager.AppSettings["ItemOrder"]));
_orderedNodes.Add(_order, _psmn);
}
}
}
catch (Exception ex)
{
//This will happen if 2 items are assigned the same order. Push one item to the last order.
_orderedNodes.Add(_counter++, _psmn);
//No need to process
}
//if this is a folder, fetch and build the heirarchy under this folder
if (_subitem.Folder != null)
BuildListNodes(pCurrentWeb, _subitem.Folder, pPrtlWebSiteMapNode, _psmn, false);
}
//Copy nodes in the right order
foreach (object portalSiteMapNode in _orderedNodes.Values)
{
//Add the node to the different collections
if (pRootLevel)
gTopLevelNodes.Add(portalSiteMapNode);
//If the parent node is not null, add the parent and the child relationship
if (pParentSiteMapNode != null)
gChildParentRelationship.Add(new DictionaryEntry(pParentSiteMapNode.Key, portalSiteMapNode));
}
}
catch (Exception ex)
{
;
}
}
public override SiteMapNodeCollectionGetChildNodes(System.Web.SiteMapNodepNode)
{
returnComposeNodes(pNode);
}
public virtual SiteMapNodeCollectionComposeNodes(System.Web.SiteMapNodepNode)
{
//The SiteMapNodeCollection which represents the children of this node
SiteMapNodeCollection _children = new SiteMapNodeCollection();
try
{
//If an absolute rootnode, then add the top level children which are the same for every site collection
if (pNode == pNode.RootNode)
{
SPSite _rootSite = SPContext.Current.Site;
SPWeb _rootWeb = _rootSite.RootWeb;
//Serve it from cache if possible.
object _topNodes = HttpRuntime.Cache["TopNavRootNodes"];
lock (this)
{
{
gRootNode = null;
LoadTopNavigationFromList();
}
//Else generate the top level nodes from memory. This must be done regardless of option 1 above
for (int i = 0; i lessthan gTopLevelNodes.Count; i++)
{
_children.Add(gTopLevelNodes[i] as PortalSiteMapNode);
}
//Add them to the cache
HttpRuntime.Cache["TopNavRootNodes"] = _children;
}
}
else
//Else this is a subnode, get only the children of that subnode
{
string _nodeKey = pNode.Key;
lock (this)
{
{
gRootNode = null;
LoadTopNavigationFromList();
}
//Else iterate through the nodes and find the children of this node
for (int i = 0; i lessthan gChildParentRelationship.Count; i++)
{
string _nKey = ((DictionaryEntry)gChildParentRelationship[i]).Key as string;
//if this is a child
if (_nodeKey == _nKey)
{
//Get the child from the arraylist
PortalSiteMapNode _child = (PortalSiteMapNode)(((DictionaryEntry)gChildParentRelationship[i]).Value);
if (_child != null)
{
_children.Add(_child as PortalSiteMapNode);
}
else
{
throw new Exception("ArrayLists not in sync.");
}
}
}
//Add the children to the cache
HttpRuntime.Cache["TopNavRootNodes" + _nodeKey] = _children;
}
}
}
catch (Exception ex)
{
//return empty site node collection
return new SiteMapNodeCollection(); // No need to process
}
return _children;
}
}
}
Step 4: Add the entry of the class in web config file of the site
1. Add the following entry under the sitemap -> provider tag
<siteMap defaultProvider="CurrentNavigation" enabled="true">
<providers>
<add name="TopNavProvider" description="Custom provider" type="CustomNavProvider.TopNavProvider, CustomNavProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=11d7cf682" NavigationType="Current" EncodeOutput="true" />
</providers>
</sitemap>
2. Add the following entry under app setting tag
<add key="TopNavigationListName" value="TopNavList"/>
<add key="CurrentLeftNavigationListName" value="CurrentLeftNavList"/>
<add key="StaticLeftNavigationListName" value="StaticNavList"/>
<add key="NavigationListStartFolderName" value="CoreList"/>
<add key="UrlLink" value="Url Link"/>
<add key="UrlDescription" value="Url Description"/>
<add key="NewWindow" value="Open New Window"/>
<add key="UrlAudience" value="Url Audience"/>
<add key="ItemOrder" value="Item Order"/>
<add key="FolderItemOrder" value="Folder Item Order"/>