I don't know if you can somehow replace the default MenuProvider to accomplish this, but if you don't mind a little hack, you could do it like this:
[MenuProvider]
public class CustomMenuProvider : IMenuProvider
{
public IEnumerable<MenuItem> GetMenuItems()
{
if (!PrincipalInfo.Current.RoleList.Contains("ReportsRole"))
{
var hideMenuHack = new UrlMenuItem(
"<span><style>.epi-navigation-global_cms_report { display:none !important }</style></span>",
"/global/cms/edit",
"javascript: return;"
);
return new MenuItem[] { hideMenuHack };
}
return new MenuItem[0];
}
}
I checked out the new menu and it doesn't use the same class names as before, so the CSS hack wouldn't work anyway.
I guess if you really need to fix it you could register a custom script in module.config, get all the menu items and hide the Reports item if the user doesn't have the required access (perhaps make a rest endpoint that the script can call to check it).
How about a less hacky method? 😀
You could implement a custom menu assembler and remove the report item as required:
[ServiceConfiguration(ServiceType = typeof(MenuAssembler), Lifecycle = ServiceInstanceScope.Singleton)]
public class CustomMenuAssembler : MenuAssembler
{
public CustomMenuAssembler(IMenuProvider[] menuProviders, IServiceLocator container) : base(menuProviders,
container)
{
}
public override IEnumerable<MenuItem> GetMenuItems(string parentPath, int relativeDepth)
{
var menuItems = base.GetMenuItems(parentPath, relativeDepth).ToList();
var reportItem =
menuItems.SingleOrDefault(x => x.Path.Equals("/global/cms/report", StringComparison.Ordinal));
if (reportItem == null)
{
return menuItems;
}
// Add user group check here
menuItems.Remove(reportItem);
return menuItems;
}
}
I didn't get this part to work in an Alloy site:
[ServiceConfiguration(ServiceType = typeof(MenuAssembler), Lifecycle = ServiceInstanceScope.Singleton)]
So I registered it like this:
[InitializableModule]
public class DependencyResolverInitialization : IConfigurableModule
{
public void ConfigureContainer(ServiceConfigurationContext context)
{
context.ConfigurationComplete += (o, e) =>
{
context.Services.AddSingleton<MenuAssembler, LimitedReportsMenuAssembler>();
};
}
}
Any suggestion why the attribute did not work?
Honestly, I like this better anyway.
At a guess, it's probably because under the hood Epi is using reflection to retrieve and register all the ServiceConfigurationAttributes. That means it would be getting registered twice and which ever gets registered last is going to be used, not sure what is deciding the order but your code solves the problem by ensuring the custom one is registered last.
That's my theory at least! 😉
Hi all,
I would add here that yes this fullfills the requirement of removing the menu item for reports if the logged in user is not in the defined group/role BUT if they know the Reports url they still can access the reports main view (default url: /EPiServer/CMS/Report/default.aspx, if UI url is not changed - but in all projects this ofcourse changed :D) and from there navigate to all the reports. If security audit is done - we should also limit the real access.
Simple base limitation would be in web.config to add a new location element:
<location path="EPiServer/CMS/Report">
<system.web>
<authorization>
<allow roles="ReportViewers"/>
<deny users="*"/>
</authorization>
</system.web>
</location>
Naturally one would use the 'role' one has used in the code limiting the visibility of the Reports menu item.
Note! All the Episerver default reports use the base path of 'ui-url-here/cms/report' so the above access restriction works for those (including the menu.aspx creating the left menu in the reports view) but any custom reports in other locations would need their own restriction location element.
I would like «Reports» in the global menu to be visible for members of a specific user group.
Other users should not see «Reports».
How can that be done?