There is the deprecated navigation system that is based on xml - known as Dynamicweb.Frontend.NavigationProviders.NavigationProvider - they support products in navigation.
The new navigation system that is based on razor templates and viewmodels - known as Dynamicweb.Frontend.Navigation.NavigationTreeNodeProvider - they do not support products in navigation.
This change is by design. As we saw navigations with 100.000+ products in the navigation leading to death and destruction...
If you need products in the navigation based on the razor model, you have to implement your self - either using the API in the razor template or by adding your own NavigationTreeNodeProvider that can also return products.
Performance can easily go wrong - so take that into consideration. Looping all groups and their products can be super expensive to do on all requests.
Our GroupNavigationTreeNodeProvider - you can copy and extend that to return products as well.
using System;
using System.Collections.Generic;
using System.Linq;
using Dynamicweb.Configuration;
using Dynamicweb.Content;
using Dynamicweb.Ecommerce.Extensibility.Controls;
using Dynamicweb.Ecommerce.Products;
using Dynamicweb.Environment.Helpers;
using Dynamicweb.Extensibility.AddIns;
using Dynamicweb.Frontend.Navigation;
namespace Dynamicweb.Ecommerce.Frontend.Navigation
{
[AddInOrder(2)]
public class GroupNavigationTreeNodeProvider : NavigationTreeNodeProvider
{
/// <summary>
/// Gets the group navigation nodes for a given parent based on the provided navigation context and navigation settings.
/// </summary>
/// <param name="context">The navigation context used to generate navigation tree nodes.</param>
/// <param name="settings">The navigation settings used to generate navigation tree nodes.</param>
/// <param name="parent">The parent navigation tree node.</param>
/// <returns>A colllection of group navigation tree nodes.</returns>
public override IEnumerable<NavigationTreeNode> GetNodes(NavigationContext context, NavigationSettings settings, NavigationTreeNode parent)
{
int rootPageId = parent.PageId;
if (rootPageId <= 0)
{
return Enumerable.Empty<NavigationTreeNode>();
}
var page = Dynamicweb.Content.Services.Pages.GetPage(rootPageId);
var groups = GetGroups(parent, page);
IEnumerable<NavigationTreeNode> nodes;
if (groups.Any())
{
int productPageId = LinkHelper.GetInternalPageId(page.NavigationSettings.ProductPage);
nodes = groups.Select(group => CreateGroupNavigationTreeNode(page, group, (parent.Level == 0 ? page.Level : parent.Level) + 1, productPageId));
}
else
{
nodes = Enumerable.Empty<NavigationTreeNode>();
}
return nodes;
}
/// <summary>
/// Initialize a navigation context.
/// </summary>
/// <param name="context">The navigation context used to generate navigation tree nodes.</param>
public override void InitializeContext(NavigationContext context)
{
string groupId = context.QueryString["GroupID"];
if (!string.IsNullOrEmpty(groupId))
{
var groupPath = GetPrimaryPath(groupId, Common.Context.LanguageID);
foreach (var currentGroupId in groupPath)
{
groupId = currentGroupId;
context.Path.Add($"Page_{context.PageId}::group_{groupId}");
}
}
}
private static List<string> GetPrimaryPath(string groupId, string languageId)
{
var result = new List<string>();
while (!string.IsNullOrEmpty(groupId))
{
result.Add(groupId);
var group = Services.ProductGroups.GetGroup(groupId, languageId);
groupId = group?.GetPrimaryParentGroupId() ?? group?.GetParentGroups().FirstOrDefault()?.Id;
}
result.Reverse();
return result;
}
/// <summary>
/// Gets the groups for a given navigation context and navigation settings.
/// </summary>
/// <param name="parent">The parent navigation tree node.</param>
/// <param name="page">The node page.</param>
/// <returns>A coollection of groups.</returns>
private static IEnumerable<Group> GetGroups(NavigationTreeNode parent, Page page)
{
IEnumerable<Group> groups;
var ecomSettings = page.NavigationSettings;
if (ecomSettings is null || !ecomSettings.UseEcomGroups)
{
return Enumerable.Empty<Group>();
}
if (parent is GroupNavigationTreeNode parentGroup)
{
if (ecomSettings.MaxLevels == 0 || parent.Level - page.Level < ecomSettings.MaxLevels)
{
groups = GetSubgroups(parentGroup);
}
else
{
groups = Enumerable.Empty<Group>();
}
}
else
{
groups = GetGroupsBySettings(ecomSettings);
}
groups = FilterGroups(groups);
return groups;
}
private static IEnumerable<Group> FilterGroups(IEnumerable<Group> groups)
{
bool useAssortments = SystemConfiguration.Instance.GetBoolean("/Globalsettings/Ecom/Assortments/UseAssortments");
if (useAssortments)
{
var user = Security.UserManagement.UserContext.Current.User;
var assortmentIds = user is not null ? Services.Assortments.GetAssortmentIdsByUser(user, true) : Services.Assortments.GetAllowAnonymousUsersAssortmentIds();
var hasAccessToGroup = new Func<Group, bool>(group =>
{
foreach (var assortmentId in assortmentIds)
{
var assortment = Services.Assortments.GetAssortmentById(assortmentId);
if (assortment is not null && assortment.GroupHierarchy.Contains(group.Id))
return true;
}
return false;
});
groups = groups.Where(g => g.NavigationShowInMenu && hasAccessToGroup(g));
}
else
{
groups = groups.Where(g => g.NavigationShowInMenu == true);
}
return groups;
}
private static IEnumerable<Group> GetSubgroups(GroupNavigationTreeNode parent)
{
return Services.ProductGroups.GetSubgroups(parent.Group);
}
private static IEnumerable<Group> GetGroupsBySettings(PageNavigationSettings ecomSettings)
{
IEnumerable<Group> topLevelGroups;
string languageId = Common.Context.LanguageID;
switch (ecomSettings.ParentType)
{
case EcommerceNavigationParentType.Groups:
{
var groupHandler = new ProductsAndGroupsHandler(ecomSettings.Groups);
if (groupHandler.Type == HandlerConfigurationType.All)
{
var shop = Services.Shops.GetShop(string.IsNullOrEmpty(groupHandler.ShopSelected) ? ecomSettings.ShopID : groupHandler.ShopSelected);
if (shop is null)
{
topLevelGroups = Enumerable.Empty<Group>();
}
else
{
topLevelGroups = shop.GetTopLevelGroups(languageId);
}
}
else
{
topLevelGroups = groupHandler.GroupsSelected;
}
break;
}
case EcommerceNavigationParentType.Shop:
{
var shop = Services.Shops.GetShop(ecomSettings.ShopID);
if (shop is null)
{
topLevelGroups = Enumerable.Empty<Group>();
}
else
{
topLevelGroups = shop.GetTopLevelGroups(languageId);
}
break;
}
default:
{
topLevelGroups = Enumerable.Empty<Group>();
break;
}
}
return topLevelGroups;
}
private static GroupNavigationTreeNode CreateGroupNavigationTreeNode(Page page, Group group, int level, int productPageId)
{
var node = new GroupNavigationTreeNode(page, group, level, productPageId);
return node;
}
}
}