The content you're reading is getting on in years
This post is on the older side and its content may be out of date.
Be sure to visit our blogs homepage for our latest news, updates and information.
The navigation is one of the most important parts of a site, which aims at providing topnotch user experience. It is important what pages will be shown to the user and to which he can navigate to. That is why, in this blog post will be described how the Sitefinity navigation could be customized in order to show different nodes depending on the user role, on the pages role permissions or to present all nodes. This could be done by extending the SitefinitySiteMap, which the navigation uses to get the nodes to be shown.
We should simply create a class and inherit from the SitefinitySiteMap. Then register our site map in the web.config of the application, in the siteMap section:
<
add
name
=
"CustomSitefinitySiteMap"
type
=
"SitefinityWebApp.CustomSitefinitySiteMap, SitefinityWebApp"
rootNode
=
"F669D9A7-009D-4D83-DDAA-000000000002"
/>
The rootNode is the application root node id - SiteInitializer.CurrentFrontendRootNodeId or the root_Id of all pages on first level in the sf_page_node table. You can also get it by browsing the pages section and take a look the request executed: /Sitefinity/Services/Pages/PagesService.svc/?root=f669d9a7-009d-4d83-ddaa-000000000002&...
By default, the nodes will be filtered based on the user roles and permissions and nodes that they do not have access to, will not be displayed in the navigation. We can easily skip this, and show all nodes. When the user navigates to a page that they cannot view, they will be either prompted to login or will be shown a message that they do not possess the rights to view the page content. The method that needs to be overridden is IsAccessibleToUser, which is called for all nodes and returns a boolean indicator. To show all nodes just return true:
public
override
bool
IsAccessibleToUser(HttpContext context, SiteMapNode node)
{
return
true
;
}
NOTE: for newer versions refer to this KB article:
http://knowledgebase.progress.com/articles/Article/Extend-Sitefinity-SiteMap
This could be done through the navigation widget's advanced settings. Set the SiteMapProviderName to your custom SitefinitySiteMap provider, for instance:
We can filter the nodes in the provider based on whether or not they are protected. We can get a collection of those nodes which are protected by the role permission:
public
List<SiteMapNode> ProtectedNodes {
get
;
set
; }
private
Role role;
public
Role Role
{
get
{
if
(role ==
null
)
{
role = RoleManager.GetManager(
"AppRoles"
).GetRoles()
.Where(a => a.Name ==
"BackendUsers"
).SingleOrDefault();
}
return
role;
}
}
public
override
bool
IsAccessibleToUser(HttpContext context, SiteMapNode node)
{
var isProtected = node.Roles.Cast<Guid>().Any(p => Role.Id == p);
if
(isProtected)
{
var pageSiteNode = node
as
PageSiteNode;
if
(pageSiteNode !=
null
)
{
if
(pageSiteNode.DeniedRoles !=
null
&& pageSiteNode.DeniedRoles.Count > 0)
{
isProtected = pageSiteNode.DeniedRoles.Any(p => Role.Id == p);
}
}
if
(isProtected)
{
if
(ProtectedNodes ==
null
)
{
ProtectedNodes =
new
List<SiteMapNode>();
}
ProtectedNodes.Add(node);
}
}
return
true
;
}
Using a MVC widget we will display the navigation, getting the nodes from our custom SitefinitySiteMap provider and style the protected nodes in the navigation. We will use recursion in order to both convert the nodes to a model and show the hierarchy in the view. Getting the nodes and building the navigation models hierarchy:
public
List<PageNodeModel> nav =
new
List<PageNodeModel>();
public
List<SiteMapNode> protectedNodes =
new
List<SiteMapNode>();
/// <summary>
/// This is the default Action.
/// </summary>
public
ActionResult Index()
{
SiteMapProvider mapPro = SitefinitySiteMap.GetSiteMapProvider(
"CustomSitefinitySiteMap"
);
var rootNode = mapPro.FindSiteMapNodeFromKey(SiteInitializer.CurrentFrontendRootNodeId.ToString());
var nodes = mapPro.GetChildNodes(rootNode);
protectedNodes = (mapPro
as
CustomSitefinitySiteMap).ProtectedNodes;
var model =
new
ProtectedNavigationModel();
BuildNavigation(nodes);
model.Nav = nav;
return
View(
"Default"
, model);
}
public
void
BuildNavigation(SiteMapNodeCollection nodes)
{
var rootNodes =
new
List<SiteMapNode>();
foreach
(SiteMapNode node
in
nodes)
{
if
(node.ParentNode.Key.ToLowerInvariant() == SiteInitializer.CurrentFrontendRootNodeId.ToString().ToLowerInvariant())
{
var pageSiteNode = node
as
PageSiteNode;
if
(pageSiteNode !=
null
&& pageSiteNode.ShowInNavigation && pageSiteNode.Visible ==
true
)
{
PageNodeModel model = PageNodeModel.ToModel(node);
model.IsProtected = protectedNodes.Contains(node);
nav.Add(model);
rootNodes.Add(node);
}
}
}
if
(nav.Count > 0)
{
for
(
int
i = 0; i < nav.Count; i++)
{
AddChildren(nav[i], rootNodes[i]);
}
}
}
private
void
AddChildren(PageNodeModel parent, SiteMapNode parentNode)
{
foreach
(SiteMapNode item
in
parentNode.ChildNodes)
{
var pageSiteNode = item
as
PageSiteNode;
if
(pageSiteNode !=
null
&& pageSiteNode.ShowInNavigation && pageSiteNode.Visible ==
true
)
{
PageNodeModel itemModel = PageNodeModel.ToModel(item);
itemModel.IsProtected = protectedNodes.Contains(item);
parent.ChildNodes.Add(itemModel);
AddChildren(itemModel, item);
}
}
}
In the view we define and use a helper:
@helper NavigationView(List<
SitefinityWebApp.Mvc.Models.PageNodeModel
> nodes)
{
foreach (var item in nodes)
{
<
li
>
@if (item.ChildNodes.Count > 0)
{
if (item.IsProtected)
{
<
span
class
=
"protected"
><
a
href
=
"@Url.Content(item.Url)"
>@item.Title</
a
></
span
>
}
else
{
<
span
><
a
href
=
"@Url.Content(item.Url)"
>@item.Title</
a
></
span
>
}
<
ul
>
@NavigationView(item.ChildNodes)
</
ul
>
}
else
{
if (item.IsProtected)
{
<
span
class
=
"protected"
><
a
href
=
"@Url.Content(item.Url)"
>@item.Title</
a
></
span
>
}
else
{
<
span
><
a
href
=
"@Url.Content(item.Url)"
>@item.Title</
a
></
span
>
}
}
</
li
>
}
}
The result we build and style with kendo:
The blog post files could be download from here: SitefinitySiteMap and navigation.
Video of the sample MVC navigation and the protected pages:
Nikola Zagorchev is a Tech Support Engineer at Telerik. He joined the Sitefinity Support team in March 2014.
Let our experts teach you how to use Sitefinity's best-in-class features to deliver compelling digital experiences.
Learn MoreSubscribe to get all the news, info and tutorials you need to build better business apps and sites
Progress collects the Personal Information set out in our Privacy Policy and the Supplemental Privacy notice for residents of California and other US States and uses it for the purposes stated in that policy.
You can also ask us not to share your Personal Information to third parties here: Do Not Sell or Share My Info
We see that you have already chosen to receive marketing materials from us. If you wish to change this at any time you may do so by clicking here.
Thank you for your continued interest in Progress. Based on either your previous activity on our websites or our ongoing relationship, we will keep you updated on our products, solutions, services, company news and events. If you decide that you want to be removed from our mailing lists at any time, you can change your contact preferences by clicking here.