#75: Added menu manager, tests and a simple renderer.

pull/81/head
Halil İbrahim Kalkan 9 years ago
parent a39630674f
commit 8042e1f882

@ -1,4 +1,5 @@
using AbpDesk.EntityFrameworkCore;
using AbpDesk.Web.Mvc.Navigation;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
@ -8,6 +9,7 @@ using Volo.Abp;
using Volo.Abp.AspNetCore.Modularity;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Modularity;
using Volo.Abp.Ui.Navigation;
namespace AbpDesk.Web.Mvc
{
@ -23,7 +25,12 @@ namespace AbpDesk.Web.Mvc
var configuration = BuildConfiguration(hostingEnvironment);
AbpDeskDbConfigurer.Configure(services, configuration);
services.Configure<NavigationOptions>(options =>
{
options.MenuContributors.Add(new MainMenuContributor());
});
services.AddMvc();
services.AddAssemblyOf<AbpDeskWebMvcModule>();
}

@ -0,0 +1,26 @@
using System.Threading.Tasks;
using Volo.Abp.Ui.Navigation;
namespace AbpDesk.Web.Mvc.Navigation
{
public class MainMenuContributor : IMenuContributor
{
public Task ConfigureMenuAsync(MenuConfigurationContext context)
{
if (context.Menu.Name != StandardMenus.Main)
{
return Task.CompletedTask;
}
context.Menu.DisplayName = "Main Menu";
context.Menu.AddItem(
new ApplicationMenuItem("TicketManagement", "Ticket Management")
.AddItem(new ApplicationMenuItem("Administration.UserManagement", "User Management"))
.AddItem(new ApplicationMenuItem("Administration.RoleManagement", "Role Management"))
);
return Task.CompletedTask;
}
}
}

@ -0,0 +1,22 @@
@using System.Threading.Tasks
@model Volo.Abp.Ui.Navigation.ApplicationMenu
<h2>@Model.DisplayName</h2>
<ul>
@foreach (var menuItem in Model.Items)
{
<li>
<a href="@menuItem.Url">@menuItem.DisplayName</a>
@if (menuItem.Items.Any())
{
<ul>
@foreach (var childMenuItem in menuItem.Items)
{
<li>
<a href="@childMenuItem.Url">@childMenuItem.DisplayName</a>
</li>
}
</ul>
}
</li>
}
</ul>

@ -0,0 +1,24 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.Ui.Navigation;
namespace AbpDesk.Web.Mvc.Views.Shared.Components.HorizontalMenu
{
public class HorizontalMenuViewComponent : ViewComponent
{
private readonly IMenuManager _menuManager;
public HorizontalMenuViewComponent(IMenuManager menuManager)
{
//TODO: Create a INavigationAppService that can also be used remotely, instead of directly using IMenuManager!
_menuManager = menuManager;
}
public async Task<IViewComponentResult> InvokeAsync(string menuName = StandardMenus.Main)
{
var menu = await _menuManager.GetAsync(StandardMenus.Main);
return View(menu);
}
}
}

@ -1,4 +1,6 @@
@{
@using AbpDesk.Web.Mvc.Views.Shared.Components
@using AbpDesk.Web.Mvc.Views.Shared.Components.HorizontalMenu
@{
Layout = null;
}
@ -7,11 +9,13 @@
<html>
<head>
<title>title</title>
@RenderSection("styles", false)
</head>
<body>
@await Component.InvokeAsync(typeof(HorizontalMenuViewComponent))
@RenderBody()
</body>

@ -0,0 +1,63 @@
using System.Collections.Generic;
using JetBrains.Annotations;
namespace Volo.Abp.Ui.Navigation
{
public class ApplicationMenu : IHasMenuItems
{
/// <summary>
/// Unique name of the menu in the application.
/// </summary>
[NotNull]
public string Name { get; }
/// <summary>
/// Display name of the menu.
/// Default value is the <see cref="Name"/>.
/// </summary>
[NotNull]
public string DisplayName
{
get { return _displayName; }
set
{
Check.NotNullOrWhiteSpace(value, nameof(value));
_displayName = value;
}
}
private string _displayName;
/// <inheritdoc cref="IHasMenuItems.Items"/>
[NotNull]
public IList<ApplicationMenuItem> Items { get; } //TODO: Create a specialized collection (that can contain AddAfter for example)
/// <summary>
/// Can be used to store a custom object related to this menu.
/// </summary>
[CanBeNull]
public object CustomData { get; set; }
public ApplicationMenu(
[NotNull] string name,
string displayName = null)
{
Check.NotNullOrWhiteSpace(name, nameof(name));
Name = name;
DisplayName = displayName ?? Name;
Items = new List<ApplicationMenuItem>();
}
/// <summary>
/// Adds a <see cref="ApplicationMenuItem"/> to <see cref="Items"/>.
/// </summary>
/// <param name="menuItem"><see cref="ApplicationMenuItem"/> to be added</param>
/// <returns>This <see cref="ApplicationMenu"/> object</returns>
public ApplicationMenu AddItem([NotNull] ApplicationMenuItem menuItem)
{
Items.Add(menuItem);
return this;
}
}
}

@ -0,0 +1,108 @@
using System.Collections.Generic;
using JetBrains.Annotations;
using Volo.ExtensionMethods.Collections.Generic;
namespace Volo.Abp.Ui.Navigation
{
public class ApplicationMenuItem : IHasMenuItems
{
private string _displayName;
/// <summary>
/// Default <see cref="Order"/> value of a menu item.
/// </summary>
public const int DefaultOrder = 1000;
/// <summary>
/// Unique name of the menu in the application.
/// </summary>
[NotNull]
public string Name { get; }
/// <summary>
/// Display name of the menu item.
/// </summary>
[NotNull]
public string DisplayName
{
get { return _displayName; }
set
{
Check.NotNullOrWhiteSpace(value, nameof(value));
_displayName = value;
}
}
/// <summary>
/// The Display order of the menu.
/// Default value: 1000.
/// </summary>
public int Order { get; set; }
/// <summary>
/// The URL to navigate when this menu item is selected.
/// </summary>
[CanBeNull]
public string Url { get; set; }
/// <summary>
/// Icon of the menu item if exists.
/// </summary>
[CanBeNull]
public string Icon { get; set; }
/// <summary>
/// Returns true if this menu item has no child <see cref="Items"/>.
/// </summary>
public bool IsLeaf => Items.IsNullOrEmpty();
/// <summary>
/// Target of the menu item. Can be null, "_blank", "_self", "_parent", "_top" or a frame name for web applications.
/// </summary>
[CanBeNull]
public string Target { get; set; }
/// <inheritdoc cref="IHasMenuItems.Items"/>
[NotNull]
public IList<ApplicationMenuItem> Items { get; }
/// <summary>
/// Can be used to store a custom object related to this menu item. Optional.
/// </summary>
public object CustomData { get; set; }
public ApplicationMenuItem(
[NotNull] string name,
[NotNull] string displayName,
string url = null,
string icon = null,
int order = DefaultOrder,
object customData = null,
string target = null)
{
Check.NotNullOrWhiteSpace(name, nameof(name));
Check.NotNullOrWhiteSpace(displayName, nameof(displayName));
Name = name;
DisplayName = displayName;
Url = url;
Icon = icon;
Order = order;
CustomData = customData;
Target = target;
Items = new List<ApplicationMenuItem>();
}
/// <summary>
/// Adds a <see cref="ApplicationMenuItem"/> to <see cref="Items"/>.
/// </summary>
/// <param name="menuItem"><see cref="ApplicationMenuItem"/> to be added</param>
/// <returns>This <see cref="ApplicationMenuItem"/> object</returns>
public ApplicationMenuItem AddItem([NotNull] ApplicationMenuItem menuItem)
{
Items.Add(menuItem);
return this;
}
}
}

@ -0,0 +1,12 @@
using System.Collections.Generic;
namespace Volo.Abp.Ui.Navigation
{
public interface IHasMenuItems
{
/// <summary>
/// Menu items.
/// </summary>
IList<ApplicationMenuItem> Items { get; }
}
}

@ -0,0 +1,11 @@
using Volo.DependencyInjection;
namespace Volo.Abp.Ui.Navigation
{
public interface IMenuConfigurationContext : IServiceProviderAccessor
{
ApplicationMenu Menu { get; set; }
//TODO: Add Localization, Authorization components since they are most used components on menu creation!
}
}

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Volo.Abp.Ui.Navigation
{
public interface IMenuContributor
{
Task ConfigureMenuAsync(MenuConfigurationContext context);
}
}

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Volo.Abp.Ui.Navigation
{
public interface IMenuManager
{
Task<ApplicationMenu> GetAsync(string name);
}
}

@ -0,0 +1,17 @@
using System;
namespace Volo.Abp.Ui.Navigation
{
public class MenuConfigurationContext : IMenuConfigurationContext
{
public ApplicationMenu Menu { get; set; }
public IServiceProvider ServiceProvider { get; }
public MenuConfigurationContext(ApplicationMenu menu, IServiceProvider serviceProvider)
{
Menu = menu;
ServiceProvider = serviceProvider;
}
}
}

@ -0,0 +1,39 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.DependencyInjection;
namespace Volo.Abp.Ui.Navigation
{
public class MenuManager : IMenuManager, ITransientDependency
{
private readonly NavigationOptions _options;
private readonly IServiceProvider _serviceProvider;
public MenuManager(
IOptions<NavigationOptions> options,
IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
_options = options.Value;
}
public async Task<ApplicationMenu> GetAsync(string name)
{
var menu = new ApplicationMenu(name);
using (var scope = _serviceProvider.CreateScope())
{
var context = new MenuConfigurationContext(menu, scope.ServiceProvider);
foreach (var contributor in _options.MenuContributors)
{
await contributor.ConfigureMenuAsync(context);
}
}
return menu;
}
}
}

@ -0,0 +1,16 @@
using System.Collections.Generic;
using JetBrains.Annotations;
namespace Volo.Abp.Ui.Navigation
{
public class NavigationOptions
{
[NotNull]
public List<IMenuContributor> MenuContributors { get; }
public NavigationOptions()
{
MenuContributors = new List<IMenuContributor>();
}
}
}

@ -0,0 +1,8 @@
namespace Volo.Abp.Ui.Navigation
{
public static class StandardMenus
{
public const string Main = "Main";
public const string User = "User";
}
}

@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
namespace Volo.ExtensionMethods.Collections.Generic
{
@ -26,6 +28,22 @@ namespace Volo.ExtensionMethods.Collections.Generic
source.Insert(targetIndex, item);
}
[NotNull]
public static T GetOrAdd<T>([NotNull] this IList<T> source, Func<T, bool> selector, Func<T> factory)
{
Check.NotNull(source, nameof(source));
var item = source.FirstOrDefault(selector);
if (item == null)
{
item = factory();
source.Add(item);
}
return item;
}
/// <summary>
/// Sort a list by a topological sorting, which consider their dependencies.
/// </summary>

@ -1,14 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using Shouldly;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
using Volo.Abp.MultiTenancy.ConfigurationStore;
using Volo.ExtensionMethods.Collections.Generic;
using Xunit;
namespace Volo.Abp.Data.MultiTenancy

@ -0,0 +1,102 @@
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Volo.Abp.Modularity;
using Volo.Abp.TestBase;
using Volo.ExtensionMethods.Collections.Generic;
using Xunit;
namespace Volo.Abp.Ui.Navigation
{
public class MenuManager_Tests : AbpIntegratedTest<MenuManager_Tests.TestModule>
{
private readonly IMenuManager _menuManager;
public MenuManager_Tests()
{
_menuManager = ServiceProvider.GetRequiredService<IMenuManager>();
}
[Fact]
public async Task Should_Get_Menu()
{
var mainMenu = await _menuManager.GetAsync(StandardMenus.Main);
mainMenu.Name.ShouldBe(StandardMenus.Main);
mainMenu.DisplayName.ShouldBe("Main Menu");
mainMenu.Items.Count.ShouldBe(2);
mainMenu.Items[0].Name.ShouldBe("Dashboard");
mainMenu.Items[1].Name.ShouldBe("Administration");
mainMenu.Items[1].Items[0].Name.ShouldBe("Administration.UserManagement");
mainMenu.Items[1].Items[1].Name.ShouldBe("Administration.RoleManagement");
mainMenu.Items[1].Items[2].Name.ShouldBe("Administration.DashboardSettings");
}
public class TestModule : AbpModule
{
public override void ConfigureServices(IServiceCollection services)
{
services.Configure<NavigationOptions>(options =>
{
options.MenuContributors.Add(new TestMenuContributer1());
options.MenuContributors.Add(new TestMenuContributer2());
});
}
}
/* Adds menu items:
* - Administration
* - User Management
* - Role Management
*/
public class TestMenuContributer1 : IMenuContributor
{
public Task ConfigureMenuAsync(MenuConfigurationContext context)
{
if (context.Menu.Name != StandardMenus.Main)
{
return Task.CompletedTask;
}
context.Menu.DisplayName = "Main Menu";
var administration = context.Menu.Items.GetOrAdd(
m => m.Name == "Administration",
() => new ApplicationMenuItem("Administration", "Administration")
);
administration.AddItem(new ApplicationMenuItem("Administration.UserManagement", "User Management"));
administration.AddItem(new ApplicationMenuItem("Administration.RoleManagement", "Role Management"));
return Task.CompletedTask;
}
}
/* Adds menu items:
* - Dashboard
* - Administration
* - Dashboard Settings
*/
public class TestMenuContributer2 : IMenuContributor
{
public Task ConfigureMenuAsync(MenuConfigurationContext context)
{
if (context.Menu.Name != StandardMenus.Main)
{
return Task.CompletedTask;
}
context.Menu.Items.Insert(0, new ApplicationMenuItem("Dashboard", "Dashboard"));
var administration = context.Menu.Items.GetOrAdd(
m => m.Name == "Administration",
() => new ApplicationMenuItem("Administration", "Administration")
);
administration.AddItem(new ApplicationMenuItem("Administration.DashboardSettings", "Dashboard Settings"));
return Task.CompletedTask;
}
}
}
}
Loading…
Cancel
Save