Skip to content

Commit

Permalink
Rename ConfigureDynamicMenu to ConfigureAbpDynamicMenu.
Browse files Browse the repository at this point in the history
Added a host demo data seeder.
Fix a bug of url override.
Restrict a menu item to have 2 levels of submenu items.
Support deleting parent menu items.
Update README.md.
  • Loading branch information
gdlcf88 committed Oct 11, 2021
1 parent 198895b commit a39e711
Show file tree
Hide file tree
Showing 21 changed files with 176 additions and 24 deletions.
2 changes: 1 addition & 1 deletion common.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<LangVersion>latest</LangVersion>
<Version>0.1.0</Version>
<Version>0.2.0</Version>
<NoWarn>$(NoWarn);CS1591;CS0436</NoWarn>
<AbpProjectType>module</AbpProjectType>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
Expand Down
41 changes: 41 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,43 @@
# Abp.DynamicMenu

[![ABP version](https://img.shields.io/badge/dynamic/xml?style=flat-square&color=yellow&label=abp&query=%2F%2FProject%2FPropertyGroup%2FAbpVersion&url=https%3A%2F%2Fraw.githubusercontent.com%2FEasyAbp%2FAbp.DynamicMenu%2Fmaster%2FDirectory.Build.props)](https://abp.io)
[![NuGet](https://img.shields.io/nuget/v/EasyAbp.Abp.DynamicMenu.Domain.Shared.svg?style=flat-square)](https://www.nuget.org/packages/EasyAbp.Abp.DynamicMenu.Domain.Shared)
[![NuGet Download](https://img.shields.io/nuget/dt/EasyAbp.Abp.DynamicMenu.Domain.Shared.svg?style=flat-square)](https://www.nuget.org/packages/EasyAbp.Abp.DynamicMenu.Domain.Shared)
[![GitHub stars](https://img.shields.io/github/stars/EasyAbp/Abp.DynamicMenu?style=social)](https://www.github.com/EasyAbp/Abp.DynamicMenu)

An abp module that dynamically creates menu items for ABP UI projects in runtime.

## Online Demo

We have launched an online demo for this module: [https://dynamicmenu.samples.easyabp.io](https://dynamicmenu.samples.easyabp.io)

![demo.gif](/docs/images/demo.gif)

## Installation

1. Install the following NuGet packages. ([see how](https://github.com/EasyAbp/EasyAbpGuide/blob/master/docs/How-To.md#add-nuget-packages))

* EasyAbp.Abp.DynamicMenu.Application
* EasyAbp.Abp.DynamicMenu.Application.Contracts
* EasyAbp.Abp.DynamicMenu.Domain
* EasyAbp.Abp.DynamicMenu.Domain.Shared
* EasyAbp.Abp.DynamicMenu.EntityFrameworkCore
* EasyAbp.Abp.DynamicMenu.HttpApi
* EasyAbp.Abp.DynamicMenu.HttpApi.Client
* EasyAbp.Abp.DynamicMenu.Web

1. Add `DependsOn(typeof(AbpDynamicMenuXxxModule))` attribute to configure the module dependencies. ([see how](https://github.com/EasyAbp/EasyAbpGuide/blob/master/docs/How-To.md#add-module-dependencies))

1. Add `builder.ConfigureAbpDynamicMenu();` to the `OnModelCreating()` method in **MyProjectMigrationsDbContext.cs**.

1. Add EF Core migrations and update your database. See: [ABP document](https://docs.abp.io/en/abp/latest/Tutorials/Part-1?UI=MVC&DB=EF#add-database-migration).

## Usage

1. Create a dynamic menu item on the management page.

2. Refresh the page and you can see the menu item you just created.

## Road map

- [ ] More customizable options for menu items.
Binary file added docs/images/demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,8 @@
namespace EasyAbp.Abp.DynamicMenu.Blazor.Server.Host
{
[DependsOn(
typeof(DynamicMenuEntityFrameworkCoreModule),
typeof(DynamicMenuApplicationModule),
typeof(DynamicMenuHttpApiModule),
typeof(DynamicMenuHostSharedModule),
typeof(AbpAspNetCoreMvcUiBasicThemeModule),
typeof(AbpAutofacModule),
typeof(AbpSwashbuckleModule),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\EasyAbp.Abp.DynamicMenu.Application\EasyAbp.Abp.DynamicMenu.Application.csproj" />
<PackageReference Include="Volo.Abp.EntityFrameworkCore.SqlServer" Version="$(AbpVersion)" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic" Version="$(AbpVersion)" />
<PackageReference Include="Volo.Abp.Autofac" Version="$(AbpVersion)" />
Expand All @@ -46,7 +45,6 @@
<PackageReference Include="Volo.Abp.PermissionManagement.Application" Version="$(AbpVersion)" />
<PackageReference Include="Volo.Abp.PermissionManagement.EntityFrameworkCore" Version="$(AbpVersion)" />
<ProjectReference Include="..\..\src\EasyAbp.Abp.DynamicMenu.Blazor.Server\EasyAbp.Abp.DynamicMenu.Blazor.Server.csproj" />
<ProjectReference Include="..\..\src\EasyAbp.Abp.DynamicMenu.EntityFrameworkCore\EasyAbp.Abp.DynamicMenu.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\src\EasyAbp.Abp.DynamicMenu.HttpApi\EasyAbp.Abp.DynamicMenu.HttpApi.csproj" />
<ProjectReference Include="..\EasyAbp.Abp.DynamicMenu.Host.Shared\EasyAbp.Abp.DynamicMenu.Host.Shared.csproj" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
modelBuilder.ConfigureIdentity();
modelBuilder.ConfigureFeatureManagement();
modelBuilder.ConfigureTenantManagement();
modelBuilder.ConfigureDynamicMenu();
modelBuilder.ConfigureAbpDynamicMenu();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using EasyAbp.Abp.DynamicMenu.MenuItems;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Uow;

namespace EasyAbp.Abp.DynamicMenu
{
public class DemoDataSeedContributor : IDataSeedContributor, ITransientDependency
{
private readonly IMenuItemRepository _menuItemRepository;

public DemoDataSeedContributor(IMenuItemRepository menuItemRepository)
{
_menuItemRepository = menuItemRepository;
}

[UnitOfWork]
public async Task SeedAsync(DataSeedContext context)
{
if (await _menuItemRepository.FindAsync(x => x.Name == "DemoMenu") != null)
{
return;
}

var subItems = new List<MenuItem>
{
new("DemoMenu", "ChangePassword", "Change password", "~/Account/Manage", null, null, null, null,
DynamicMenuConsts.DefaultLResourceTypeName, DynamicMenuConsts.DefaultLResourceTypeAssemblyName,
null)
};

var demoMenu = new MenuItem(null, "DemoMenu", "Demo menu", null, null, null, null, null,
DynamicMenuConsts.DefaultLResourceTypeName, DynamicMenuConsts.DefaultLResourceTypeAssemblyName, subItems);

await _menuItemRepository.InsertAsync(demoMenu, true);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using EasyAbp.Abp.DynamicMenu.EntityFrameworkCore;
using Volo.Abp.Modularity;

namespace EasyAbp.Abp.DynamicMenu
{
[DependsOn(
typeof(DynamicMenuEntityFrameworkCoreModule),
typeof(DynamicMenuApplicationModule)
)]
public class DynamicMenuHostSharedModule : AbpModule
{

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@
<Import Project="..\..\common.props" />

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFramework>netstandard2.1</TargetFramework>
<RootNamespace>EasyAbp.Abp.DynamicMenu</RootNamespace>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\EasyAbp.Abp.DynamicMenu.Application\EasyAbp.Abp.DynamicMenu.Application.csproj" />
<ProjectReference Include="..\..\src\EasyAbp.Abp.DynamicMenu.EntityFrameworkCore\EasyAbp.Abp.DynamicMenu.EntityFrameworkCore.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ namespace EasyAbp.Abp.DynamicMenu
{
[DependsOn(
typeof(DynamicMenuWebModule),
typeof(DynamicMenuApplicationModule),
typeof(DynamicMenuEntityFrameworkCoreModule),
typeof(DynamicMenuHostSharedModule),
typeof(AbpAuditLoggingEntityFrameworkCoreModule),
typeof(AbpAutofacModule),
typeof(AbpAccountWebModule),
Expand All @@ -65,7 +64,7 @@ namespace EasyAbp.Abp.DynamicMenu
typeof(AbpAspNetCoreMvcUiBasicThemeModule),
typeof(AbpAspNetCoreSerilogModule),
typeof(AbpSwashbuckleModule)
)]
)]
public class DynamicMenuWebUnifiedModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\EasyAbp.Abp.DynamicMenu.Application\EasyAbp.Abp.DynamicMenu.Application.csproj" />
<ProjectReference Include="..\..\src\EasyAbp.Abp.DynamicMenu.EntityFrameworkCore\EasyAbp.Abp.DynamicMenu.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\src\EasyAbp.Abp.DynamicMenu.Web\EasyAbp.Abp.DynamicMenu.Web.csproj" />
<ProjectReference Include="..\EasyAbp.Abp.DynamicMenu.Host.Shared\EasyAbp.Abp.DynamicMenu.Host.Shared.csproj" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
modelBuilder.ConfigureIdentity();
modelBuilder.ConfigureFeatureManagement();
modelBuilder.ConfigureTenantManagement();
modelBuilder.ConfigureDynamicMenu();
modelBuilder.ConfigureAbpDynamicMenu();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Volo.Abp;

namespace EasyAbp.Abp.DynamicMenu.MenuItems
{
public sealed class ExceededMenuLevelLimitException : BusinessException
{
public ExceededMenuLevelLimitException(int maxLevel)
: base("EasyAbp.Abp.DynamicMenu:ExceededMenuLevelLimit")
{
Data["MaxLevel"] = maxLevel;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EasyAbp.Abp.DynamicMenu.Permissions;
Expand Down Expand Up @@ -40,7 +42,7 @@ protected override async Task<MenuItem> GetEntityByIdAsync(MenuItemKey id)
{
// TODO: AbpHelper generated
return await AsyncExecuter.FirstOrDefaultAsync(
_repository.Where(e =>
(await _repository.WithDetailsAsync()).Where(e =>
e.Name == id.Name
)
);
Expand Down Expand Up @@ -82,17 +84,54 @@ public override async Task<MenuItemDto> UpdateAsync(MenuItemKey id, UpdateMenuIt
return await MapToGetOutputDtoAsync(entity);
}

public override async Task DeleteAsync(MenuItemKey id)
{
await CheckDeletePolicyAsync();

var menuItem = await GetEntityByIdAsync(id);

await RecursiveDeleteAsync(menuItem);

await CurrentUnitOfWork.SaveChangesAsync();
}

protected virtual async Task RecursiveDeleteAsync(MenuItem menuItem)
{
if (!menuItem.MenuItems.IsNullOrEmpty())
{
foreach (var subItem in menuItem.MenuItems)
{
await RecursiveDeleteAsync(subItem);
}
}

await _repository.DeleteAsync(menuItem);
}

protected async Task CheckParentNameAsync([CanBeNull] string parentName)
{
if (parentName == null)
{
return;
}

if (await _repository.FindAsync(x => x.Name == parentName) == null)
var parent = await _repository.FindAsync(x => x.Name == parentName);

if (parent == null)
{
throw new NonexistentParentMenuItemException(parentName);
}

// Maximum menu item level: 3
if (!parent.ParentName.IsNullOrEmpty())
{
var grandparent = await _repository.GetAsync(x => x.Name == parent.ParentName);

if (grandparent.ParentName != null)
{
throw new ExceededMenuLevelLimitException(3);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ protected virtual async Task AddDynamicMenuItemsAsync(IHasMenuItems parent, IEnu
continue;
}

child.Url = menuItem.UrlMvc ?? menuItem.Url;
child.Url = menuItem.UrlBlazor ?? menuItem.Url;
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"SubMenuItems": "Sub-items",
"MenuItemDeletionConfirmationMessage": "Are you sure to delete the menu item {0}?",
"SuccessfullyDeleted": "Successfully deleted",
"EasyAbp.Abp.DynamicMenu:NonexistentParentMenuItem": "Nonexistent parent menu item: {ParentName}"
"EasyAbp.Abp.DynamicMenu:NonexistentParentMenuItem": "Nonexistent parent menu item: {ParentName}",
"EasyAbp.Abp.DynamicMenu:ExceededMenuLevelLimit": "Exceeded the maximum menu item level: {MaxLevel}"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"SubMenuItems": "子菜单项",
"MenuItemDeletionConfirmationMessage": "确认删除菜单项 {0}?",
"SuccessfullyDeleted": "删除成功",
"EasyAbp.Abp.DynamicMenu:NonexistentParentMenuItem": "不存在的父级菜单项:{ParentName}"
"EasyAbp.Abp.DynamicMenu:NonexistentParentMenuItem": "不存在的父级菜单项:{ParentName}",
"EasyAbp.Abp.DynamicMenu:ExceededMenuLevelLimit": "菜单项最大支持 {MaxLevel} 级"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"SubMenuItems": "子菜單項",
"MenuItemDeletionConfirmationMessage": "確認刪除菜單項 {0}?",
"SuccessfullyDeleted": "刪除成功",
"EasyAbp.Abp.DynamicMenu:NonexistentParentMenuItem": "不存在的父級菜單項:{ParentName}"
"EasyAbp.Abp.DynamicMenu:NonexistentParentMenuItem": "不存在的父級菜單項:{ParentName}",
"EasyAbp.Abp.DynamicMenu:ExceededMenuLevelLimit": "菜單項最大支持 {MaxLevel} 級"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);

builder.ConfigureDynamicMenu();
builder.ConfigureAbpDynamicMenu();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace EasyAbp.Abp.DynamicMenu.EntityFrameworkCore
{
public static class DynamicMenuDbContextModelCreatingExtensions
{
public static void ConfigureDynamicMenu(
public static void ConfigureAbpDynamicMenu(
this ModelBuilder builder,
Action<DynamicMenuModelBuilderConfigurationOptions> optionsAction = null)
{
Expand Down Expand Up @@ -45,14 +45,16 @@ public static void ConfigureDynamicMenu(
builder.Entity<MenuItem>(b =>
{
b.ToTable(options.TablePrefix + "MenuItems", options.Schema);
b.ConfigureByConvention();
b.ConfigureByConvention();

/* Configure more properties here */

b.HasKey(e => new
{
e.Name,
});

/* Configure more properties here */
b.HasMany(x => x.MenuItems).WithOne();
});
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ public static IQueryable<MenuItem> IncludeDetails(this IQueryable<MenuItem> quer
}

return queryable
.Include(x => x.MenuItems);
.Include(x => x.MenuItems)
.ThenInclude(x => x.MenuItems);
}
}
}

0 comments on commit a39e711

Please sign in to comment.